[Closed] Attach node to patch
As title suggests, I’m trying to figure out a decent workaround to attaching a specific node to the current patch object. patchOps.startAttach only lets you specify the node you are attaching to, and goes into pick mode.
The only solution I have so far, would be to get all of the positions of each vertex handle and its tangent type, set the patch steps to 0, convert to edit poly, attach node, convert to patch, reset the handle positions, and their tangent types, and set the patch steps back to what they were previously.
I haven’t tried this yet, I was curious if anyone had a more elegant workaround. My goal is to make a script that would attach selected patch objects together and I think my solution would be very slow on even an average number of objects.
Any help would be appreciated, thanks!
it’s very easy with c++/SDK and so complicated with MXS.
so the basic idea is to click in viewport in place where the attached node is using postmessage.
after that using change event of the patch exit the attaching mode.
here is a sample:
global PickSupport
fn CreateMessagesAssembly =
(
source = "using System;
"
source += "using System.Runtime.InteropServices;
"
source += "class PickSupport
"
source += "{
"
source += " [DllImport(\"user32.dll\", EntryPoint=\"GetWindowRect\")]
"
source += " static extern bool GetWindowRect(IntPtr hWnd, out POS rect);
"
source += " public struct POS
"
source += " {
"
source += " public int Left;
"
source += " public int Top;
"
source += " public int Right;
"
source += " public int Bottom;
"
source += " }
"
source += " public int[] GetWindowPosAndSize(Int32 hWnd)
"
source += " {
"
source += " POS rect;
"
source += " if ( GetWindowRect((IntPtr)hWnd, out rect) )
"
source += " {
"
source += " return new int[] { rect.Left, rect.Top, rect.Right - rect.Left, rect.Bottom - rect.Top };
"
source += " }
"
source += " return null;
"
source += " }
"
source += " [DllImport(\"user32.dll\")]
"
source += " public static extern int PostMessage(Int32 hWnd, int wMsg, int wParam, int lParam);
"
source += "}
"
csharpProvider = dotnetobject "Microsoft.CSharp.CSharpCodeProvider"
compilerParams = dotnetobject "System.CodeDom.Compiler.CompilerParameters"
compilerParams.ReferencedAssemblies.AddRange #("System.dll")
compilerParams.GenerateInMemory = on
compilerResults = csharpProvider.CompileAssemblyFromSource compilerParams #(source)
PickSupport = compilerResults.CompiledAssembly.CreateInstance "PickSupport"
)
CreateMessagesAssembly()
fn makeParam LoWord HiWord =
(
bit.or (bit.shift HiWord 16) (bit.and LoWord 0xFFFF)
)
fn findActiveLabel =
(
fn windowpos hwnd =
(
local p = PickSupport.GetWindowPosAndSize hwnd
#([p[1],p[2]],[p[3],p[4]])
)
hwnd = for c in (windows.getChildrenHWND #max) where c[4] == "ViewPanel" do exit with c[1]
d3ds = #()
lbls = #()
for c in (windows.getChildrenHWND hwnd parent:hwnd) do case c[4] of
(
"Label": append lbls c[1]
"D3DWindow": append d3ds c[1]
)
p = windowpos d3ds[viewport.activeViewport]
for lb in lbls where (windowpos lb)[1] == p[1] do exit with lb
)
fn pickPatchAttach node = if iskindof (patch = modpanel.getcurrentobject()) Editable_Patch do
(
bt = (windows.getchildhwnd #max "Attach")[1]
toolmode.CommandMode = #select
uiaccessor.pressbutton bt
when parameters patch change id:#custom_patch_pick patch do
(
deleteAllChangeHandlers id:#custom_patch_pick
toolmode.CommandMode = #select
)
hwnd = findActiveLabel()
mesh = snapshotasmesh node
pp = getvert mesh 1
gw.settransform (matrix3 1)
pv = gw.TransPoint pp
ps = mouse.screenpos - mouse.pos + pv
WM_LBUTTONDOWN = 0x201
WM_LBUTTONUP = 0x202
xy = makeParam (pv.x as integer) (pv.y as integer)
PickSupport.postmessage hwnd WM_LBUTTONDOWN 0 xy
PickSupport.postmessage hwnd WM_LBUTTONUP 0 xy
)
/* simple scene and attach in action */
(
viewport.resetAllViews()
delete objects
max create mode
target = Quadpatch name:"target" pos:[-10,0,0] length:10 lengthsegs:4 width:10 widthsegs:4 isselected:on
convertto target Editable_Patch
source = Quadpatch name:"source" pos:[10,0,0] length:10 lengthsegs:2 width:10 widthsegs:2
max modify mode
pickPatchAttach source
)
and multi-attach version
with using nodePreDelete callback
global PickSupport
fn CreateMessagesAssembly =
(
source = "using System;
"
source += "using System.Runtime.InteropServices;
"
source += "class PickSupport
"
source += "{
"
source += " [DllImport(\"user32.dll\", EntryPoint=\"GetWindowRect\")]
"
source += " static extern bool GetWindowRect(IntPtr hWnd, out POS rect);
"
source += " public struct POS
"
source += " {
"
source += " public int Left;
"
source += " public int Top;
"
source += " public int Right;
"
source += " public int Bottom;
"
source += " }
"
source += " public int[] GetWindowPosAndSize(Int32 hWnd)
"
source += " {
"
source += " POS rect;
"
source += " if ( GetWindowRect((IntPtr)hWnd, out rect) )
"
source += " {
"
source += " return new int[] { rect.Left, rect.Top, rect.Right - rect.Left, rect.Bottom - rect.Top };
"
source += " }
"
source += " return null;
"
source += " }
"
source += " [DllImport(\"user32.dll\")]
"
source += " public static extern int PostMessage(Int32 hWnd, int wMsg, int wParam, int lParam);
"
source += "}
"
csharpProvider = dotnetobject "Microsoft.CSharp.CSharpCodeProvider"
compilerParams = dotnetobject "System.CodeDom.Compiler.CompilerParameters"
compilerParams.ReferencedAssemblies.AddRange #("System.dll")
compilerParams.GenerateInMemory = on
compilerResults = csharpProvider.CompileAssemblyFromSource compilerParams #(source)
PickSupport = compilerResults.CompiledAssembly.CreateInstance "PickSupport"
)
CreateMessagesAssembly()
fn makeParam LoWord HiWord =
(
bit.or (bit.shift HiWord 16) (bit.and LoWord 0xFFFF)
)
fn findActiveLabel =
(
fn windowpos hwnd =
(
local p = PickSupport.GetWindowPosAndSize hwnd
#([p[1],p[2]],[p[3],p[4]])
)
hwnd = for c in (windows.getChildrenHWND #max) where c[4] == "ViewPanel" do exit with c[1]
d3ds = #()
lbls = #()
for c in (windows.getChildrenHWND hwnd parent:hwnd) do case c[4] of
(
"Label": append lbls c[1]
"D3DWindow": append d3ds c[1]
)
p = windowpos d3ds[viewport.activeViewport]
for lb in lbls where (windowpos lb)[1] == p[1] do exit with lb
)
fn nodePreDelete handle: =
(
if handle == unsupplied or handle == gethandlebyanim (callbacks.notificationparam()) do
(
callbacks.removescripts id:#custom_patch_pick
toolmode.CommandMode = #select
)
)
fn pickPatchAttach nodes = if iskindof (patch = modpanel.getcurrentobject()) Editable_Patch do
(
if isvalidnode nodes do nodes = #(nodes)
nodes = for node in nodes where canconvertto node Editable_Patch collect node
if nodes.count > 0 do
(
bt = (windows.getchildhwnd #max "Attach")[1]
toolmode.CommandMode = #select
uiaccessor.pressbutton bt
callbacks.addscript #nodePreDelete ("nodePreDelete handle:" + (gethandlebyanim nodes[nodes.count]) as string) id:#custom_patch_pick
hwnd = findActiveLabel()
WM_LBUTTONDOWN = 0x201
WM_LBUTTONUP = 0x202
for node in nodes do
(
mesh = snapshotasmesh node
pp = getvert mesh 1
gw.settransform (matrix3 1)
pv = gw.TransPoint pp
ps = mouse.screenpos - mouse.pos + pv
xy = makeParam (pv.x as integer) (pv.y as integer)
PickSupport.postmessage hwnd WM_LBUTTONDOWN 0 xy
PickSupport.postmessage hwnd WM_LBUTTONUP 0 xy
)
)
)
callbacks.removescripts id:#custom_patch_pick
/* simple scene and attach in action */
with redraw off
(
viewport.resetAllViews()
delete objects
max create mode
target = Quadpatch name:"target" pos:[-10,0,0] length:10 lengthsegs:4 width:10 widthsegs:4 isselected:on
convertto target Editable_Patch
sources = for k=1 to 4 collect (Quadpatch pos:[10*k,0,0] length:8 lengthsegs:2 width:8 widthsegs:2)
max modify mode
pickPatchAttach sources
)
Whoa you weren’t lying, that does look complicated! Thanks I will have to give that a try… Does this method work with off screen objects? Or does the picked nodes have to be visible in the viewport?
I tried doing a test of my idea just seeing if everything would work after going to edit poly and back –
fn ResetPatchVectorPositions Obj:$ =
(
PosArray = #()
VertTypesArray = for i = 1 to (patch.getNumVerts $) collect patch.getVertType $ i
Steps = getPatchSteps Obj
setPatchSteps Obj 0
for i = 1 to (patch.getNumVerts Obj) do
(
local VertVecs = (patch.getVertVecs Obj i as array)
append PosArray (for j = 1 to VertVecs.count collect (patch.getVec Obj VertVecs[j]))
)
modPanel.addModToSelection (Turn_to_Poly ()) ; macros.run "Modifier Stack" "Convert_to_Poly"
----------------------------------------------------------------------------------------------- attach here
modPanel.addModToSelection (Turn_to_Patch ()) ; macros.run "Modifier Stack" "Convert_to_Patch"
for i = 1 to (patch.getNumVerts Obj) do
(
local VertVecs = (patch.getVertVecs Obj i as array)
if (PosArray[i]) != undefined do (for j = 1 to VertVecs.count do (if (PosArray[i][j]) != undefined do (patch.setVec Obj VertVecs[j] PosArray[i][j])))
)
setPatchSteps Obj Steps
for i = 1 to (patch.getNumVerts $) do (if (VertTypesArray[i]) != undefined do patch.changeVertType $ i (VertTypesArray[i]))
)
It seems to work ok, without any noticeable differences after conversion, but I believe I will have to offset the node’s vertex list by the node’s total vertex count, after its attached, in order to access them properly.
you are right… the method needs several thing to do.
#1 we have to do it without view flickering (i showed how to disable view redraw)
#2 we have to insure the attached node’s visibility and not overlapping by other node for picking
so… the plan is:
store viewport state
store max scene state
disable redraw max
hide everything except the attached node
set view to be ready to pick for sure
do attach
use any event to restore views and scene state back
enable redraw and redraw the max
Sorry for not getting back sooner, had to get some real sleep Ok so I tried evaluating your single attach version in an empty scene in max 2013 x64 and the listener spit out some errors –
OK
CreateMessagesAssembly()
dotNetObject:PickSupport
makeParam()
findActiveLabel()
pickPatchAttach()
-- Error occurred in windowpos(); filename: ; position: 1739; line: 51
-- Frame:
-- p: undefined
-- hwnd: undefined
-- called in findActiveLabel(); filename: ; position: 2080; line: 62
-- Frame:
-- windowpos: windowpos()
-- d3ds: #()
-- lbls: #(264734P, 264736P, 264738P, 264740P)
-- p: undefined
-- hwnd: 264742P
-- called in pickPatchAttach(); filename: ; position: 2542; line: 78
-- Frame:
-- bt: 34802382P
-- 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: 3288; line: 104
-- Frame:
-- target: $target
-- source: $source
-- Runtime error: No method found which matched argument list
And the multi version looks like it has similar problems, which I have no clue what they are –
OK
CreateMessagesAssembly()
dotNetObject:PickSupport
makeParam()
findActiveLabel()
nodePreDelete()
pickPatchAttach()
OK
-- 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: 2898; line: 84
-- Frame:
-- bt: 25366036P
-- nodes: #($QuadPatch001, $QuadPatch002, $QuadPatch003, $QuadPatch004)
-- WM_LBUTTONDOWN: undefined
-- WM_LBUTTONUP: undefined
-- patch: Editable Patch
-- hwnd: undefined
-- called in anonymous codeblock; filename: ; position: 3779; line: 117
-- Frame:
-- target: $target
-- sources: #($QuadPatch001, $QuadPatch002, $QuadPatch003, $QuadPatch004)
-- Runtime error: No method found which matched argument list
they changed something in 2013. something is in the ViewPanel…
i don’t have max 2013 so could you show me the print of:
(
hwnds = for c in (windows.getChildrenHWND #max) where c[4] == "ViewPanel" collect
(
format ">> %
" c
c
)
if hwnds[1] != undefined then for c in (windows.getChildrenHWND hwnds[1][1]) do format " %
" c else print "no found"
)
Sure, btw I have Quickmaximize and Outliner 2.0 scripts installed, if it makes a difference, maximized I get –
>> #(264742P, 15010910P, 15010910P, "ViewPanel", "", 0P, 15010910P, 15010910P)
#(264740P, 264742P, 264742P, "Label", "Perspective", 0P, 15010910P, 15010910P)
#(264734P, 264742P, 264742P, "Label", "Top", 0P, 15010910P, 15010910P)
#(264736P, 264742P, 264742P, "Label", "Left", 0P, 15010910P, 15010910P)
#(264738P, 264742P, 264742P, "Label", "Front", 0P, 15010910P, 15010910P)
OK
Unmaxized, with the four default views I get –
>> #(264742P, 15010910P, 15010910P, "ViewPanel" , "", 0P, 15010910P, 15010910P)
#(264734P, 264742P, 264742P, "Label", "Perspective", 0P, 15010910P, 15010910P)
#(264736P, 264742P, 264742P, "Label", "Left", 0P, 15010910P, 15010910P)
#(264738P, 264742P, 264742P, "Label", "Front", 0P, 15010910P, 15010910P)
#(264740P, 264742P, 264742P, "Label", "Top", 0P, 15010910P, 15010910P)
#(6886352P, 264742P, 264742P, "VptSplitterBar", "", 0P, 15010910P, 15010910P)
#(26677438P, 264742P, 264742P, "VptSplitterBar", "", 0P, 15010910P, 15010910P)
OK
Also if it matters, I believe I get the same errors, with perspective maximized or not maximized, and the view gets unmaximized on error.
it’s easier for 2013… could you select other than perspective view and run the same code?
here is the 2013 version:
global PickSupport
fn CreateMessagesAssembly =
(
source = "using System;
"
source += "using System.Runtime.InteropServices;
"
source += "class PickSupport
"
source += "{
"
source += " [DllImport(\"user32.dll\")]
"
source += " public static extern int PostMessage(Int32 hWnd, int wMsg, int wParam, int lParam);
"
source += "}
"
csharpProvider = dotnetobject "Microsoft.CSharp.CSharpCodeProvider"
compilerParams = dotnetobject "System.CodeDom.Compiler.CompilerParameters"
compilerParams.ReferencedAssemblies.AddRange #("System.dll")
compilerParams.GenerateInMemory = on
compilerResults = csharpProvider.CompileAssemblyFromSource compilerParams #(source)
PickSupport = compilerResults.CompiledAssembly.CreateInstance "PickSupport"
)
CreateMessagesAssembly()
fn makeParam LoWord HiWord =
(
bit.or (bit.shift HiWord 16) (bit.and LoWord 0xFFFF)
)
fn findActiveLabel =
(
local hwnd
hwnd = for c in (windows.getChildrenHWND #max) where c[4] == "ViewPanel" do exit with c[1]
for c in (windows.getChildrenHWND hwnd parent:hwnd) where c[4] == "Label" do exit with c[1]
)
fn nodePreDelete handle: =
(
if handle == unsupplied or handle == gethandlebyanim (callbacks.notificationparam()) do
(
callbacks.removescripts id:#custom_patch_pick
toolmode.CommandMode = #select
)
)
fn pickPatchAttach nodes = if iskindof (patch = modpanel.getcurrentobject()) Editable_Patch do
(
if isvalidnode nodes do nodes = #(nodes)
nodes = for node in nodes where canconvertto node Editable_Patch collect node
if nodes.count > 0 do
(
bt = (windows.getchildhwnd #max "Attach")[1]
toolmode.CommandMode = #select
uiaccessor.pressbutton bt
callbacks.addscript #nodePreDelete ("nodePreDelete handle:" + (gethandlebyanim nodes[nodes.count]) as string) id:#custom_patch_pick
hwnd = findActiveLabel()
WM_LBUTTONDOWN = 0x201
WM_LBUTTONUP = 0x202
for node in nodes do
(
mesh = snapshotasmesh node
pp = getvert mesh 1
gw.settransform (matrix3 1)
pv = gw.TransPoint pp
ps = mouse.screenpos - mouse.pos + pv
xy = makeParam (pv.x as integer) (pv.y as integer)
PickSupport.postmessage hwnd WM_LBUTTONDOWN 0 xy
PickSupport.postmessage hwnd WM_LBUTTONUP 0 xy
)
)
)
callbacks.removescripts id:#custom_patch_pick
/* simple scene and attach in action */
with redraw off
(
viewport.resetAllViews()
delete objects
max create mode
target = Quadpatch name:"target" pos:[-10,0,0] length:10 lengthsegs:4 width:10 widthsegs:4 isselected:on
convertto target Editable_Patch
sources = for k=1 to 4 collect (Quadpatch pos:[10*k,0,0] length:8 lengthsegs:2 width:8 widthsegs:2)
max modify mode
pickPatchAttach sources
)