Notifications
Clear all

[Closed] Unifying Spline Direction

Hello, I’m a newbie in MAXScript

I need some help with my current situation: I want to generate spline from selected edges of Editable Poly object. Heres what I did:

SCENARIO 1

  1. Select the edges I wanted to convert to spline
  2. Use polyOp.getEdgeSelection to get the selected edges
  3. Use polyOp.createShape to create spline

The above codes does exactly the same as when we click button “Create Shape From Selection” from EPoly rollout panel

…and the result is:

The problem is that there’s always 1 spline that has reversed direction compared to the others (notice the yellow point that represents the index 1 of each spline knot). Also the spline number is also in un-orderly fashion (5-4-3-1-2) instead of (5-4-3-2-1)

This can be simply fixed manually by selecting the reversed spline and and click the Reverse Button or use reverse <spline> in MAXScript. However I have trouble in finding the algorithm to detect which spline is reversed from the others.

After some digging, I then create another scenario. Instead of using EPoly, I used EMesh.

SCENARIO 2

  1. Select the edges I wanted to convert to spline (still in EPoly)
  2. Convert to / add modifier EMesh (found it preserves the selected edges, yay)
  3. Click “Create Shape From Edges” button in the EMesh rollout panel

Here’s the result when I did it manually:

It fixed the spline and the knots order. However when I used meshOps.createShapeFromEdges in MAXScript which supposed to do the same as above, I got error.

Here’s my code:

obj = selection
convertToMesh obj
meshOps.createShapeFromEdges obj

Here’s the error:

-- Error occurred in anonymous codeblock; filename: MY_MAXSCRIPT_FILE_URL; position: 68; line: 3
-- Known system exception
-- ########################################################################
-- Address: 0x116fbc8; nCode: 0x00000000C0000005
-- Desc: EXCEPTION_ACCESS_VIOLATION The thread tried to read from or write to a virtual address for which it does not have the appropriate access.
--       Write of Address: 0x000000000116FBC8
-- ########################################################################
-- MAXScript callstack:
--	thread data: threadID:4792
--	------------------------------------------------------
--	[stack level: 0]
--	In top-level
-- ########################################################################
-- C++ callstack:
-- (MAXScrpt): (filename not available): UndoBaseObject::`vftable'
-- (MAXScrpt): (filename not available): MeshValue::intern
-- (MAXScrpt): (filename not available): MeshValue::intern
-- (MAXScrpt): (filename not available): Primitive::apply
-- (MAXScrpt): (filename not available): StructDef::apply
-- (MAXScrpt): (filename not available): CodeTree::eval
-- (MAXScrpt): (filename not available): SourceFileWrapper::eval
-- (MAXScrpt): (filename not available): Listener::set_style
-- (USER32): (filename not available): EnableSessionForMMCSS
-- (USER32): (filename not available): EnableSessionForMMCSS
-- (USER32): (filename not available): EnableSessionForMMCSS
-- (USER32): (filename not available): DispatchMessageW
-- (USER32): (filename not available): CallWindowProcW
-- (UIControls): (filename not available): InitializeCustomUI
-- (UIControls): (filename not available): (function-name not available)
-- (USER32): (filename not available): DispatchMessageW
-- (USER32): (filename not available): DispatchMessageW
-- (USER32): (filename not available): CreateMenu
-- (3dsmax): (filename not available): XMLAnimTreeEntry::GetUnique
-- ((module-name not available)): (filename not available): (function-name not available)
-- ((module-name not available)): (filename not available): (function-name not available)
-- ((module-name not available)): (filename not available): (function-name not available)
-- ((module-name not available)): (filename not available): (function-name not available)
-- (VCRUNTIME140): (filename not available): (function-name not available)
-- ((module-name not available)): (filename not available): (function-name not available)
-- (clr): (filename not available): LegacyNGenTryEnumerateFusionCache
-- ((module-name not available)): (filename not available): (function-name not available)
-- ((module-name not available)): (filename not available): (function-name not available)
-- ((module-name not available)): (filename not available): (function-name not available)
-- ((module-name not available)): (filename not available): (function-name not available)
-- ((module-name not available)): (filename not available): (function-name not available)
-- ((module-name not available)): (filename not available): (function-name not available)
-- ((module-name not available)): (filename not available): (function-name not available)
-- (3dsmax): (filename not available): UtilGfx::kFONT_WIDTH
-- ((module-name not available)): (filename not available): (function-name not available)
-- ((module-name not available)): (filename not available): (function-name not available)
-- ((module-name not available)): (filename not available): (function-name not available)
-- ########################################################################

Also I heard that meshOps.createShapeFromEdges will show a dialog which I do not want. I prefer to do it silently like polyOp.createShape does.

I’m opened to any suggestion or workaround for this problem.
Thanks in advance!

4 Replies

If you just stick to your first way and just want to test direction of splines, I’d:

1/ calculate the vectors of the splines by getting the positions of vert 1 and vert 2 of each spline and taking pos1 from pos2

2/ you could compare the angles of each of the splines against the first one and if the result is more than 90 then you know you need to flip the spline. You can get the angle between 2 vectors with this:

(acos(dot (normalize vector1) (normalize vector2)))

There might be better answers, but this is how I’d do it.

Cg.

meshOps.createShapeFromEdges works only if the node is opened in modifier panel, edit mesh modifier selected, and edge selection level set.

is your scene meets all these conditions run the code below:

fn createShapeFromMeshEdges = 
(
	if iskindof (modi = modpanel.getcurrentobject()) Edit_Mesh do
	(
		DialogMonitorOPS.unRegisterNotification id:#createShapeWatcher

		fn notification = 
		(
			hwnd = DialogMonitorOPS.GetWindowHandle()
			if (UIAccessor.isWindow hwnd) and (UIAccessor.GetWindowText hwnd) == "Create Shape" do
			(
				UIAccessor.PressButtonByName hwnd "OK"
			)
			off
		)

		local enabled = DialogMonitorOPS.enabled
		DialogMonitorOPS.RegisterNotification notification id:#createShapeWatcher
		DialogMonitorOPS.enabled = on
		
		meshOps.createShapeFromEdges modi
		DialogMonitorOPS.unRegisterNotification id:#createShapeWatcher
		DialogMonitorOPS.enabled = enabled
		ok
	)
)
(
	createShapeFromMeshEdges()
)


but I would use the poly level and reorder shapes the way I need…

Thanks @3rd Dimentia and @Denis T, I’m a little bit occupied now and will surely try your solutions once I’m free.

@3rd Dimentia, I’m afraid that it won’t work when the spline is twisted more than 360 degree and below 450 degree. I haven’t tried your solutions but I’ll update the result here once I try that

@Denis T, is there any specific reason why you’d prefer the poly level?

Briefly read your solutions, I’m thinking about adding an Edit Mesh modifier and selecting the Edge level by script above my Editable poly (with already assigned edge selection). Since when I did that manually, the EMesh actually preserves the edge selection from my EPoly. Then I’d run your solution and delete the EMesh mod once I got the spline. So it will leave my object back to the EPoly state.