Notifications
Clear all

[Closed] Attach node to patch

Ok, but do you mean the short code or the original attach code? The short code still looks the same ran in front view, I could be wrong though –

Unmaximized –

>> #(264742P, 15010910P, 15010910P, "ViewPanel", "", 0P, 15010910P, 15010910P)
 	#(264734P, 264742P, 264742P, "Label", "Perspective", 0P, 15010910P, 15010910P)
 	#(264 736P, 264742P, 264742P, "Label", "Left", 0P, 15010910P, 15010910P)
 	#(264738P, 264742P, 264742P, "Label", "Front", 0P, 15010910P, 15010910P)
 	#(264740P, 264742P, 264742P, "Label", "Top", 0P, 15010910P, 15010910P)
 	#(23729000P, 264742P, 264742P, "VptSplitterBar", "", 0P, 15010910P, 15010910P)
 	#(57805734P, 264742P, 264742P, "VptSplitterBar", "", 0P, 15010910P, 15010910P)
 OK

Maximized –

>> #(264742P, 15010910P, 15010910P, "ViewPanel", "", 0P, 15010910P, 15010910P)
 	#(264740P, 264742P, 264742P, "Label", "Front", 0P, 15010910P, 15010910P)
 	#(264734P, 264742P, 264742P, "Label", "Perspective", 0P, 15010910P, 15010910P)
 	#(264736P, 264742P, 264742P, "Label", "Left", 0P, 15010910P, 15010910P)
 	#(264738P, 264742P, 264742P, "Label", "Top", 0P, 15010910P, 15010910P)
 OK

The attach code ran in front view yields –

OK
 CreateMessagesAssembly()
 dotNetObject:PickSupport
 makeParam()
 findActiveLabel()
 pickPatchAttach()
 -- Error occurred in windowpos(); filename: ; position: 1737; line: 50
 --  Frame:
 --   p: undefined
 --   hwnd: undefined
 --   called in findActiveLabel(); filename: ; position: 2078; line: 61
 --  Frame:
 --   windowpos: windowpos()
 --   d3ds: #()
 --   lbls: #(264734P, 264736P, 264738P, 264740P)
 --   p: undefined
 --   hwnd: 264742P
 --   called in pickPatchAttach(); filename: ; position: 2540; line: 77
 --  Frame:
 --   bt: 44174336P
 --   pp: undefined
 --   ps: undefined
 --   XY: undefined
 --   node: $source
 --   WM_LBUTTONDOWN: undefined
 --   WM_LBUTTONUP: undefined
 --   pv: undefined
 --   patch: Editable Patch
 --   hwnd: undefined
 --   mesh: undefined
 --   called in anonymous codeblock; filename: ; position: 3286; line: 103
 --  Frame:
 --   target: $target
 --   source: $source
 -- Runtime error: No method found which matched argument list
 

Nice! It does work, I’ll have to figure out out to integrate that with my other poly, mesh, and spline attachment function libraries. When I release them I’ll give you credit, thanks! I’m almost done with my version, when I’m finished I’ll post that as well. I’ll have to compare the evaluation times on these. Is there a method of getting precise evaluation times in maxscript, other than just using a stop watch? Maybe when a block is done, or by checking if max is responding or not would be a good way for this?

1 Reply
(@denist)
Joined: 11 months ago

Posts: 0

you can do the same to attach splines. it works almost for everything that needs picking a node in the viewport.

timestemp() before and after.

That’s interesting to know, although I just used addAndWeld for attaching selected splines together. I’ll have to try that timestamp function.

Who knows what and where Autodesk changes things under the hood right now… My main beef is with older things like edit mesh and patch. I think that if you are going to keep older things in max for whatever legacy reasons, then UPDATE THEM! Or at least give better access to them in maxscript… I don’t really use edit mesh for modeling, but patches still can have their uses from time to time. I think it would be really nice to have the same ribbon tools for patches as well, which is why I’m trying to create a lot of them myself, but it definitely seems like a challenge. Anyways that’s my rant for today.

Okay so I finally finished my version, which works decently. I haven’t recorded the times on this, and I’ve only tried it on a few objects. I figured it was worth a try because it seems like your function is dependent on the version, and if autodesk moves things around for 2014, then your function ends up getting broken. And I don’t know anything really about dot net to constantly update it for my other attachment scripts. I would have to say that the set of scripts is for max 2013 only, which I think would be a major drawback.

Anyways, here’s the function –

fn AttachPatch Obj:$ AttachNode:undefined = 
   (
   	if (Obj != undefined and AttachNode != undefined) then
   	(
   		if (classof Obj != Editable_Patch) then 
   		(
   			if (classof Obj == Editable_Poly or classof Obj == PolyMeshObject or classof Obj == Editable_Mesh) then 
   			(
   				select Obj ; modPanel.addModToSelection (Turn_to_Patch ()) ; macros.run "Modifier Stack" "Convert_to_Patch" -- Convert to patch, retain quads
   			) else macros.run "Modifier Stack" "Convert_to_Patch" -- Convert to patch
   		) -- Convert current object to patch if its not already a patch 
   		if (classof AttachNode != Editable_Patch) then 
   		(
   			if (classof AttachNode == Editable_Poly or classof AttachNode == PolyMeshObject or classof AttachNode == Editable_Mesh) then 
   			(
   				select AttachNode ; modPanel.addModToSelection (Turn_to_Patch ()) ; macros.run "Modifier Stack" "Convert_to_Patch" -- Convert to patch, retain quads
   			) else macros.run "Modifier Stack" "Convert_to_Patch" -- Convert to patch
   		) -- Convert attached object to patch if its not already a patch 
   		Loops2Sided1 = #{} -- Closed loops with only 2 edges
   		Loops2Sided2 = #{} -- Closed loops with only 2 edges
   		for i = 1 to (patch.getNumEdges Obj) do
   		(
   			local Edges1 = ((patch.getVertEdges Obj ((patch.getEdgeVert1 Obj i) + 1)) as bitarray) -- Get edges from first vertex
   			local Edges2 = ((patch.getVertEdges Obj ((patch.getEdgeVert2 Obj i) + 1)) as bitarray) -- Get edges from second vertex
   			if ((Edges1 * Edges2).numberset == 2) then Loops2Sided1 += (Edges1 * Edges2) -- Get both edges
   		) -- Get 2-sided closed loops on current object
   		for i = 1 to (patch.getNumEdges AttachNode) do
   		(
   			local Edges1 = ((patch.getVertEdges AttachNode ((patch.getEdgeVert1 AttachNode i) + 1)) as bitarray) -- Get edges from first vertex
   			local Edges2 = ((patch.getVertEdges AttachNode ((patch.getEdgeVert2 AttachNode i) + 1)) as bitarray) -- Get edges from second vertex
   			if ((Edges1 * Edges2).numberset == 2) then Loops2Sided2 += (Edges1 * Edges2) -- Get both edges
   		) -- Get 2-sided closed loops on attached object
   		patch.deletePatchParts Obj Loops2Sided1 #{} -- Delete 2-sided loops on current object to avoid vertex count mismatch
   		patch.deletePatchParts AttachNode Loops2Sided2 #{} -- Delete 2-sided loops on attached object to avoid vertex count mismatch
   		AllVerts1 = #() -- Initial vertex array
   		AllVecPositions1 = #() -- Initial position array
   		AllVertTypes1 = #() -- Initial handle array
   		AllVerts2 = #() -- Initial vertex array
   		AllVecPositions2 = #() -- Initial position array
   		AllVertTypes2 = #() -- Initial handle array
   		if ((patch.getNumVerts Obj) >= 3) then
   		(
   			AllVerts1 = #{1..(patch.getNumVerts Obj)} as array -- Get all vertices
   			AllVecPositions1 = (for i = 1 to AllVerts1.count collect (for j = 1 to (patch.getVertVecs Obj i).count collect (patch.getVec Obj (patch.getVertVecs Obj i)[j]))) as array -- Get 3d array of handle positions of current object
   			AllVertTypes1 = (for i = 1 to AllVerts1.count collect patch.getVertType Obj i) as array -- Get array of vertex tangent types of current object
   		) -- If there is at least three vertices in the current object i.e. a face exists
   		if ((patch.getNumVerts AttachNode) >= 3) then
   		(
   			AllVerts2 = #{1..(patch.getNumVerts AttachNode)} as array -- Get all vertices
   			AllVecPositions2 = (for i = 1 to AllVerts2.count collect (for j = 1 to (patch.getVertVecs AttachNode i).count collect (patch.getVec AttachNode (patch.getVertVecs AttachNode i)[j]))) as array -- Get 3d array of handle positions of attached node
   			AllVertTypes2 = (for i = 1 to AllVerts2.count collect patch.getVertType AttachNode i) as array -- Get array of vertex tangent types of attached node
   		) -- If there is at least three vertices in the attached object i.e. a face exists
   		AllVerts3 = AllVerts1 + AllVerts2 -- Add vertice selections
   		AllVecPositions3 = AllVecPositions1 + AllVecPositions2 -- Add positions together
   		AllVertTypes3 = AllVertTypes1 + AllVertTypes2 -- Add vert types together
   		Steps = getPatchSteps Obj -- Get patch steps of original object
   		ShowInterior = patch.getShowInterior Obj
   		setPatchSteps Obj 0 -- Set patch steps of current object to 0
   		setPatchSteps AttachNode 0 -- Set patch steps of attached node to 0
   		for i = 1 to AllVerts2.count do AllVerts2[i] += AllVerts1.count -- Offset vertice indexes
   		select Obj ; macros.run "Modifier Stack" "Convert_to_Poly" -- Convert to editable poly, using turn to poly mod in order to retain quads
   		select AttachNode ; macros.run "Modifier Stack" "Convert_to_Poly" -- Convert to editable poly, using turn to poly mod in order to retain quads
   		Obj.deleteIsoVerts() -- Delete isolated vertices
   		polyop.attach Obj AttachNode -- Attach node to current object
   		select Obj -- Select original object
   		modPanel.addModToSelection (Turn_to_Patch ()) ; macros.run "Modifier Stack" "Convert_to_Patch" -- Convert to editable patch, using turn to patch mod in order to retain quads
   		patch.update Obj -- Update patch
   		if ((patch.getNumVerts Obj) >= 3 and AllVecPositions3.count == (patch.getNumVerts Obj) and AllVerts3.count == (patch.getNumVerts Obj)) then 
   		(
   			for i = 1 to (patch.getNumVerts Obj) do 
   			(
   				if (AllVecPositions3[i] != undefined and AllVecPositions3[i].count == (patch.getVertVecs Obj i).count) then
   				(
   					for j = 1 to (patch.getVertVecs Obj i).count do 
   					(
   						if (AllVecPositions3[i][j] != undefined) then patch.setVec Obj (patch.getVertVecs Obj i)[j] AllVecPositions3[i][j] -- Return handles to what they were previously
   					) -- Loop through handles
   				) -- If vertices match and vert's handle numbers match
   			) -- Loop through vertices
   		) -- If vertex counts match
   		if ((patch.getNumVerts Obj) >= 3 and AllVertTypes3.count == (patch.getNumVerts Obj)) then (for i = 1 to (patch.getNumVerts Obj) do (if (AllVertTypes3[i]) != undefined do patch.changeVertType Obj i (AllVertTypes3[i]))) -- Return vertex tangent types to what they were previously
   		setPatchSteps Obj Steps -- Set patch steps to what they were previously
   		patch.setShowInterior Obj ShowInterior
   		patch.update Obj -- Update patch
   		return Obj
   	) -- If objects exist
   )

A couple of things to note, I had to make it so that the function deletes edges that make up a 2 sided loop i.e. edges that form 2 sided cylinders. So when you convert a teapot to patch and run the function using the teapot as the attach node, the handle and spout end up getting deleted. It was something that I couldn’t figure out a way around. This is because the geometry gets weird when you end up with double sided faces in edit poly. Also, you end up with a higher vertex count afterwards, since you can’t weld edges together with double sided faces. So if you use this function try to avoid those cases. Another thing I noticed is that sometimes, if you run the function and undo, then the patch steps on the source object end up staying at 0.

This is the single node version, I also made a function to loop through a selection of objects –

fn AttachSelectedPatch Sel:(selection as array) = 
   (
   	if (Sel != undefined) then
   	(
   		Object1 = Sel[1] -- Get first object in selection
   		ObjectSel = for i = 1 to Sel.count where (Sel[i] != Object1) collect Sel[i] -- Get selected objects excluding the first one
   		for i = 1 to ObjectSel.count do AttachPatch Obj:Object1 AttachNode:ObjectSel[i] -- Attach objects
   		patch.update Object1 ; CenterPivot Object1 ; WorldAlignPivot Object1 ; select Object1 -- Select object, update it, and fix its transforms
   		return Object1 -- Get object
   	) -- If objects exist
   )

It will probably be really slow for a large selection of objects because it loops through the selection and runs the attach function for each node. I plan on trying to make this method faster by just having one function, which is a multi-node version which somehow appends the position and vertex type arrays from each object, and just attaches them in edit poly and reapplies them after going back to patch.

If you know of any ways of making this faster and more efficient, please let me know.

Success! I was able to make one function to attach multiple patches. Just as I thought I got the first object, and looped through the rest of the selection. Got the first objects positions and vert types, and appended each object’s positions and types to those arrays. then I attached those same objects in edit poly, then converted to patch and reapplied those positions. I fixed some problems I missed and made it a little more efficient.

fn AttachObjectsPatch AttachNodes:(selection as array) AttachName:(uniquename ((selection as array)[1].name)) = 
         (
         	if (AttachNodes != undefined and AttachNodes.count != 0) then
         	(
         		Object1 = AttachNodes[1] -- Get first object in selection
         		ObjectSel = for i = 1 to AttachNodes.count where (AttachNodes[i] != Object1) collect AttachNodes[i] -- Get selected objects excluding the first one
         		AllVecPositions = #() -- Initial position array
         		AllVertTypes = #() -- Initial handle array
         		select Object1 -- Select first object
         		if (classof Object1 != Editable_Patch) then 
         		(
         			if (classof Object1 == Editable_Poly or classof Object1 == PolyMeshObject or classof Object1 == Editable_Mesh) then 
         			(
         				modPanel.addModToSelection (Turn_to_Patch ()) ; macros.run "Modifier Stack" "Convert_to_Patch" -- Convert to patch, retain quads
         			) else macros.run "Modifier Stack" "Convert_to_Patch" -- Convert to patch
         		) -- Convert first object to patch if its not already a patch 
         		Steps = getPatchSteps Object1 -- Get patch steps of first object
         		ShowInterior = patch.getShowInterior Object1 -- Get show interior edges
         		Loops2Sided = #{} -- Closed loops with only 2 edges
         		for i = 1 to (patch.getNumEdges Object1) do
         		(
         			local Edges1 = ((patch.getVertEdges Object1 ((patch.getEdgeVert1 Object1 i) + 1)) as bitarray) -- Get edges from first vertex
         			local Edges2 = ((patch.getVertEdges Object1 ((patch.getEdgeVert2 Object1 i) + 1)) as bitarray) -- Get edges from second vertex
         			if ((Edges1 * Edges2).numberset == 2) then Loops2Sided += #{((patch.getEdgeVert1 Object1 i) + 1), ((patch.getEdgeVert2 Object1 i) + 1)} -- Get both edges
         		) -- Get 2-sided closed loops on first object
         		patch.deletePatchParts Object1 Loops2Sided #{} -- Delete 2-sided loops on first object to avoid vertex count mismatch
         		setPatchSteps Object1 0 -- Set patch steps of first object to 0
         		AllVertsNum = (patch.getNumVerts Object1) -- Get vertex number of first object
         		if (AllVertsNum >= 3) then
         		(
         			AllVecPositions = (for i = 1 to AllVertsNum collect (for j = 1 to (patch.getVertVecs Object1 i).count collect (patch.getVec Object1 (patch.getVertVecs Object1 i)[j]))) as array -- Get 3d array of handle positions of first object
         			AllVertTypes = (for i = 1 to AllVertsNum collect patch.getVertType Object1 i) as array -- Get array of vertex tangent types of first object
         		) -- If there is at least three vertices in the first object i.e. a face exists
         		macros.run "Modifier Stack" "Convert_to_Poly" -- Convert to editable poly, using turn to poly mod in order to retain quads
         		for i = 1 to ObjectSel.count do
         		(
         			if (classof ObjectSel[i] != Editable_Patch) then 
         			(
         				select ObjectSel[i] -- Select current object
         				if (classof ObjectSel[i] == Editable_Poly or classof ObjectSel[i] == PolyMeshObject or classof ObjectSel[i] == Editable_Mesh) then 
         				(
         					modPanel.addModToSelection (Turn_to_Patch ()) ; macros.run "Modifier Stack" "Convert_to_Patch" -- Convert to patch, retain quads
         				) else macros.run "Modifier Stack" "Convert_to_Patch" -- Convert to patch
         			) -- Convert current object to patch if its not already a patch 
         			local Loops2Sided1 = #{} -- Closed loops with only 2 edges
         			local AllVertsNum1 = (patch.getNumVerts ObjectSel[i]) -- Get vertex number of current object
         			for j = 1 to (patch.getNumEdges ObjectSel[i]) do
         			(
         				local Edges1 = ((patch.getVertEdges ObjectSel[i] ((patch.getEdgeVert1 ObjectSel[i] j) + 1)) as bitarray) -- Get edges from first vertex
         				local Edges2 = ((patch.getVertEdges ObjectSel[i] ((patch.getEdgeVert2 ObjectSel[i] j) + 1)) as bitarray) -- Get edges from second vertex
         				if ((Edges1 * Edges2).numberset == 2) then Loops2Sided1 += #{((patch.getEdgeVert1 ObjectSel[i] i) + 1), ((patch.getEdgeVert2 ObjectSel[i] i) + 1)} -- Get both edges
         			) -- Get 2-sided closed loops on current object
         			patch.deletePatchParts ObjectSel[i] Loops2Sided1 #{} -- Delete 2-sided loops on attached object to avoid vertex count mismatch
         			if (AllVertsNum1 >= 3) then
         			(
         				local AllVecPositions1 = (for j = 1 to AllVertsNum1 collect (for k = 1 to (patch.getVertVecs ObjectSel[i] j).count collect (patch.getVec ObjectSel[i] (patch.getVertVecs ObjectSel[i] j)[k]))) as array -- Get 3d array of handle positions of current node
         				local AllVertTypes1 = (for j = 1 to AllVertsNum1 collect patch.getVertType ObjectSel[i] j) as array -- Get array of vertex tangent types of current node
         				AllVertsNum += AllVertsNum1 -- Add vertice selections
         				AllVecPositions += AllVecPositions1 -- Add positions together
         				AllVertTypes += AllVertTypes1 -- Add vert types together
         			) -- If there is at least three vertices in the current object i.e. a face exists
         			setPatchSteps ObjectSel[i] 0 -- Set patch steps of current node to 0
         		) -- Loop through object selection
         		select Object1 ; macros.run "Modifier Stack" "Convert_to_Poly" -- Select first object and convert to poly
         		for i = 1 to ObjectSel.count do polyop.attach Object1 ObjectSel[i] -- Attach objects to first object
         		Object1.deleteIsoVerts() -- Delete isolated vertices
         		modPanel.addModToSelection (Turn_to_Patch ()) ; macros.run "Modifier Stack" "Convert_to_Patch" -- Convert to editable patch, using turn to patch mod in order to retain quads
         		patch.update Object1 -- Update patch
         		if ((patch.getNumVerts Object1) >= 3 and AllVecPositions.count == (patch.getNumVerts Object1) and AllVertsNum == (patch.getNumVerts Object1)) then 
         		(
         			for i = 1 to (patch.getNumVerts Object1) do 
         			(
         				if (AllVecPositions[i] != undefined and AllVecPositions[i].count == (patch.getVertVecs Object1 i).count) then
         				(
         					for j = 1 to (patch.getVertVecs Object1 i).count do 
         					(
         						if (AllVecPositions[i][j] != undefined) then patch.setVec Object1 (patch.getVertVecs Object1 i)[j] AllVecPositions[i][j] -- Return handles to what they were previously
         					) -- Loop through handles
         				) -- If vertices match and vert's handle numbers match
         			) -- Loop through vertices
         		) -- If vertex counts match
         		if ((patch.getNumVerts Object1) >= 3 and AllVertTypes.count == (patch.getNumVerts Object1)) then (for i = 1 to (patch.getNumVerts Object1) do (if (AllVertTypes[i]) != undefined do patch.changeVertType Object1 i (AllVertTypes[i]))) -- Return vertex tangent types to what they were previously
         		setPatchSteps Object1 Steps -- Set patch steps to what they were previously
         		patch.setShowInterior Object1 ShowInterior -- Set show interior edges to what it was previously
         		patch.update Object1 -- Update patch
         		CenterPivot Object1 ; WorldAlignPivot Object1 -- Fix object's transforms
         		Object1.name = AttachName -- Update patch and fix its transforms
         		return Object1 --Get node
         	) -- If objects exist
         )
     Its a little slower than I would've wanted, but its not too bad.  I did timestamp tests using my first method and my second method and also your pick method.  Here are the results on my machine attaching 100 spheres -
     
     Method 1 - Single attach, getting and resetting vertex handles, function ran on each object looping through selection.
     Method 1 = 68.3 seconds
     Method 2 - Multiple attach, getting and resetting vertex handles, appending arrays.
     Method 2 = 10.6 seconds
     Method 3 - Multiple attach, picking objects in viewport.
     Method 3 = 6.8 seconds
     
     Although the pick method is the fastest, I did notice a problem.  After a running this on large numbers of selections -
with redraw off
         (
         	viewport.resetAllViews() 
         	ObjectSel1 = (selection as array)
         	target = ObjectSel1[1]
         	sources = for i = 1 to ObjectSel1.count where (ObjectSel1[i] != target) collect ObjectSel1[i]
         	convertto target Editable_Patch
         	select target
         	max modify mode
         	pickPatchAttach sources
         )
     It looks like its missing about a third of the objects. -

 [[img] http://img694.imageshack.us/img694/7474/patchattachingerror.jpg[/img]]( http://img694.imageshack.us/img694/7474/patchattachingerror.jpg[/img]](  g"/> /)

I tried then, reselected the attached patches then selecting the rest and trying to evaluate again. I did this over and over a bunch of times. It attached a couple of spheres, but eventually stopped attaching anything…Not sure what’s going on, but maybe this method isn’t so good for a large selection…

1 Reply
(@ian31r)
Joined: 11 months ago

Posts: 0

Yeah…that is way over my head, much more then your picking method. I have very limited c++ knowledge. I’ve written some basic mental ray shaders in c++, but not much beyond that. Do you have to compile anything with that? How do you call that in a function? I tried evaluating it and the listener spits out –

-- Error occurred in anonymous codeblock; filename: ; position: 35; line: 1
-- Syntax error: at ,, expected <factor>
--  In line: def_visible_primitive(patchAttach, "

Yeah I probably could just use convertto for converting to poly, but I need to use the Turn_to_Patch modifier before conversion to convert a mesh with quads to a patch with quads, otherwise using convertto node Editable_Patch converts the whole model to tri patches. Is there a conversion function that I’m missing, which keeps quads?

I was thinking though, maybe you could use a hybrid of our two methods by finding which nodes have closed 2-sided edge loops, and using the your method for those, and using my method for the rest. Or using mine, if the amount of nodes used, is over say, 20.

you don’t need all these stack operations. just simply use


if iskindof node Editable_Patch then node else convertto node Editable_Patch
if iskindof node Editable_Poly then node else convertto node Editable_Poly

convertto returns undefined if it can’t convert to the specified class.

also i don’t see any reason to select every processing node.

1 Reply
(@ian31r)
Joined: 11 months ago

Posts: 0

Yeah that’s true, I’m going back in my multi-attach function, and make it so that the only object that’s selected, is the first node, which everything gets attached to. I switched to using –

addModifier node (Turn_to_Patch ()) ; convertto node Editable_Patch

to convert to patch, in order to not have to select anything, and keep quad faces. This will speed the function up a lot, thanks denis.

it seams like your attach doesn’t work right. try this setup:


delete objects
target = Quadpatch name:"target" pos:[-10,0,0] length:10 lengthsegs:4 width:10 widthsegs:4
convertto target Editable_Patch
source = Quadpatch pos:[0,0,0] length:8 lengthsegs:2 width:8 widthsegs:2
convertto source Editable_Patch
patch.setvert source 5 [0,0,4]
patch.update source

-- AttachPatch obj:target attachnode:source

1 Reply
(@ian31r)
Joined: 11 months ago

Posts: 0

Use this function instead –

fn AttachObjectsPatch AttachNodes:(selection as array) AttachName:(uniquename ((selection as array)[1].name)) = 
 (
 	if (AttachNodes != undefined and AttachNodes.count != 0) then
 	(
 		Object1 = AttachNodes[1] -- Get first object in selection
 		ObjectSel = for i = 1 to AttachNodes.count where (AttachNodes[i] != Object1) collect AttachNodes[i] -- Get selected objects excluding the first one
 		AllVecPositions = #() -- Initial position array
 		AllVertTypes = #() -- Initial handle array
 		select Object1 -- Select first object
 		if (classof Object1 == Editable_Poly or classof Object1 == PolyMeshObject or classof Object1 == Editable_Mesh) then addModifier Object1 (Turn_to_Patch ()) -- Use turn to patch mod to keep quads
 		convertto Object1 Editable_Patch -- Convert to patch
 		Steps = getPatchSteps Object1 -- Get patch steps of first object
 		ShowInterior = patch.getShowInterior Object1 -- Get show interior edges
 		Loops2Sided = #{} -- Closed loops with only 2 edges
 		for i = 1 to (patch.getNumEdges Object1) do
 		(
 			local Edges1 = ((patch.getVertEdges Object1 ((patch.getEdgeVert1 Object1 i) + 1)) as bitarray) -- Get edges from first vertex
 			local Edges2 = ((patch.getVertEdges Object1 ((patch.getEdgeVert2 Object1 i) + 1)) as bitarray) -- Get edges from second vertex
 			if ((Edges1 * Edges2).numberset == 2) then Loops2Sided += #{((patch.getEdgeVert1 Object1 i) + 1), ((patch.getEdgeVert2 Object1 i) + 1)} -- Get both edges
 		) -- Get 2-sided closed loops on first object
 		patch.deletePatchParts Object1 Loops2Sided #{} -- Delete 2-sided loops on first object to avoid vertex count mismatch
 		setPatchSteps Object1 0 -- Set patch steps of first object to 0
 		AllVertsNum = (patch.getNumVerts Object1) -- Get vertex number of first object
 		if (AllVertsNum >= 3) then
 		(
 			AllVecPositions = (for i = 1 to AllVertsNum collect (for j = 1 to (patch.getVertVecs Object1 i).count collect (patch.getVec Object1 (patch.getVertVecs Object1 i)[j]))) as array -- Get 3d array of handle positions of first object
 			AllVertTypes = (for i = 1 to AllVertsNum collect patch.getVertType Object1 i) as array -- Get array of vertex tangent types of first object
 		) -- If there is at least three vertices in the first object i.e. a face exists
 		convertto Object1 Editable_Poly ; update Object1 -- Convert to poly and update
 		for i = 1 to ObjectSel.count do
 		(
 			if (classof ObjectSel[i] == Editable_Poly or classof ObjectSel[i] == PolyMeshObject or classof ObjectSel[i] == Editable_Mesh) then addModifier ObjectSel[i] (Turn_to_Patch ()) -- Use turn to patch mod to keep quads
 			convertto ObjectSel[i] Editable_Patch -- Convert to patch
 			local Loops2Sided1 = #{} -- Closed loops with only 2 edges
 			local AllVertsNum1 = (patch.getNumVerts ObjectSel[i]) -- Get vertex number of current object
 			for j = 1 to (patch.getNumEdges ObjectSel[i]) do
 			(
 				local Edges1 = ((patch.getVertEdges ObjectSel[i] ((patch.getEdgeVert1 ObjectSel[i] j) + 1)) as bitarray) -- Get edges from first vertex
 				local Edges2 = ((patch.getVertEdges ObjectSel[i] ((patch.getEdgeVert2 ObjectSel[i] j) + 1)) as bitarray) -- Get edges from second vertex
 				if ((Edges1 * Edges2).numberset == 2) then Loops2Sided1 += #{((patch.getEdgeVert1 ObjectSel[i] i) + 1), ((patch.getEdgeVert2 ObjectSel[i] i) + 1)} -- Get both edges
 			) -- Get 2-sided closed loops on current object
 			patch.deletePatchParts ObjectSel[i] Loops2Sided1 #{} -- Delete 2-sided loops on attached object to avoid vertex count mismatch
 			if (AllVertsNum1 >= 3) then
 			(
 				local AllVecPositions1 = (for j = 1 to AllVertsNum1 collect (for k = 1 to (patch.getVertVecs ObjectSel[i] j).count collect (patch.getVec ObjectSel[i] (patch.getVertVecs ObjectSel[i] j)[k]))) as array -- Get 3d array of handle positions of current node
 				local AllVertTypes1 = (for j = 1 to AllVertsNum1 collect patch.getVertType ObjectSel[i] j) as array -- Get array of vertex tangent types of current node
 				AllVertsNum += AllVertsNum1 -- Add vertice selections
 				AllVecPositions += AllVecPositions1 -- Add positions together
 				AllVertTypes += AllVertTypes1 -- Add vert types together
 			) -- If there is at least three vertices in the current object i.e. a face exists
 			setPatchSteps ObjectSel[i] 0 -- Set patch steps of current node to 0
 		) -- Loop through object selection
 		for i = 1 to ObjectSel.count do polyop.attach Object1 ObjectSel[i] -- Attach objects to first object
 		Object1.deleteIsoVerts() -- Delete isolated vertices
 		addModifier Object1 (Turn_to_Patch ()) ; convertto Object1 Editable_Patch -- Use turn to patch mod to keep quads and convert to patch
 		patch.update Object1 -- Update patch
 		if ((patch.getNumVerts Object1) >= 3 and AllVecPositions.count == (patch.getNumVerts Object1) and AllVertsNum == (patch.getNumVerts Object1)) then 
 		(
 			for i = 1 to (patch.getNumVerts Object1) do 
 			(
 				if (AllVecPositions[i] != undefined and AllVecPositions[i].count == (patch.getVertVecs Object1 i).count) then
 				(
 					for j = 1 to (patch.getVertVecs Object1 i).count do 
 					(
 						if (AllVecPositions[i][j] != undefined) then patch.setVec Object1 (patch.getVertVecs Object1 i)[j] AllVecPositions[i][j] -- Return handles to what they were previously
 					) -- Loop through handles
 				) -- If vertices match and vert's handle numbers match
 			) -- Loop through vertices
 		) -- If vertex counts match
 		if ((patch.getNumVerts Object1) >= 3 and AllVertTypes.count == (patch.getNumVerts Object1)) then (for i = 1 to (patch.getNumVerts Object1) do (if (AllVertTypes[i]) != undefined do patch.changeVertType Object1 i (AllVertTypes[i]))) -- Return vertex tangent types to what they were previously
 		setPatchSteps Object1 Steps -- Set patch steps to what they were previously
 		patch.setShowInterior Object1 ShowInterior -- Set show interior edges to what it was previously
 		patch.update Object1 -- Update patch
 		CenterPivot Object1 ; WorldAlignPivot Object1 -- Fix object's transforms
 		Object1.name = AttachName -- Update patch and fix its transforms
 		return Object1 --Get node
 	) -- If objects exist
 )

It seems to work fine on the two quad patches. The only patches it doesn’t work good with is geometry with 2 sides, like the handle on a teapot –

btw. the c++ attach function attached 100 quad patches for 0.7 sec. you function after some optimization does the same for 30 secs.

I have very limited c++ knowledge. I’ve written some basic mental ray shaders in c++, but not much beyond that. Do you have to compile anything with that? How do you call that in a function? I tried evaluating it and the listener spits out

you would need to create a dlx plugin to extend mxs using ms visual studio (version used depends on max version you need to compile for). The most difficult part is setting up the initial project for the build. Once it’s created the process is mostly painless, but it’s definitely worth it. (thisis quite old now but mostly still valid if you are interested in taking it further).

Page 2 / 3