Notifications
Clear all

[Closed] CgTalk Maxscript Challenge 002: "Split it up"

CgTalk Maxscript Challenge 002: “Split it up”

DESCRIPTION: Create an object splitter that has options to randomly or evenly split an object into seperate polys.

INTERMEDIATE SCRIPTERS: Add dimension to the chunks. Create new polys from the split ones.
ADVANCED SCRIPTERS: Split the object up into more pieces than there are polys in the original object, and add dimension to the pieces. If you have time, throw in an explosion or physics effect.

RULES:  

[ul]
[li]Must be code only. No pre-setup scenes.[/li][li]Code from scratch. Try not to use pre-exisitng functions or plugins.[/li][li]Show your script references, if any (eg. Looking at another script to assist you).[/li][li]You are encouraged to ask for help where needed, but try to do it on your own. The maxscript reference is an invaluable resource.[/li][li]Post your final script inside [/li]“`
tags (located on your posting toolbar).
[li]Post all code into THIS thread. [/li][/ul]
NOTES: I believe this type of script has been written before, but I wanted to give beginners a chance to start working with editPoly/Mesh functions. The advanced ones are for people who want more of a challenge.

45 Replies

I’m not sure if this is what the script is supposed to do…but I wrote this today anyways

Basically…it takes apart a mesh similar to the way the “explode” spacewarp does (ie…detached individual polys, or groups of polys)…only this one can also extrudes faces for chunkiness

Problems (I’m still new to the scripting game so there are a few)

-extruded faces extude outwards (not inwards) due to issues with the face normals I was having
-there’s no option to split the mesh apart “evenly” or “randomly” as in the challenge guidelines…it simply breaks it apart into groups of faces (the number of faces in each group can be specified in a spinner in the UI)
-I couldn’t figure out a way to create backfacing for the detached polys…so I just used the “capholes” function to ensure everything is 2-sided/capped…therefore if your chunks are set to be really thick…the backface mesh can look weird (not necessarily a bad thing though, just very unprecise)

As reference, I used the maxscript helpfiles that come with max.


   utility splitter "Object Splitter"
   (
       rollout splitroll "Object Splitter" width:162 height:52
       (
           local amount1 = bezier_float()
           global chunk1 = bezier_float()
           button split "Split Selected Object" pos:[12,10] width:140 height:26
           spinner amount "Extrude amount: " range: [.01,100,1] controller:amount1 
           spinner chunk "Chunk size: " type:#integer range: [1,100,1] controller:chunk1
           
           on split pressed do
           (
               sel = selection[1]
               if sel != undefined then
               (
                   namecount = 0
                   obj1 = sel
                   convertto obj1 editable_poly
                   faces = polyop.getnumfaces obj1
                   print ((faces as string) +  " faces in " + (sel as string))
                   print chunk1.value
                   for t in 1 to faces by (chunk1.value as integer) do
                   (
                       namecount += 1
                       name1 = ("explode_obj" + (namecount as string)) 
                       if t+(chunk1.value-1) >= faces then
                       (
                           face_array = #{t..faces}
                       )
                       else
                       (
                           face_array = #{t..(t+(chunk1.value-1))}
                       )
                       polyop.detachfaces obj1 face_array delete:false asnode:true name:name1 node:[0,0,0]
                       name2 = $*explode_obj*[namecount]    
                       print name2
                       
                       polyop.extrudefaces name2 #all amount1.value
                       polyop.capholesbyface name2 #all
                       name2.pivot = name2.center
                       
                       
                   )
                   
                   delete obj1
               )
               else print "No selection"
               
           )
   
       )
   on splitter open do
   (
       addrollout splitroll
       chunk1.value = 1
       amount1.value = 1
   )
   )
   

Go easy on it …it’s only the 3rd script I’ve written. Btw…Thanks for these challenges erilaz…they’re great practice!

Hello,

I’ve taken different interpretation, and split the object into evenly divided chunks based on number of division in the x, y, z, axis (I know this has been done before). It’s good Eploy practice for me.
The code could be optimize a lot, removing repeated code using functions, and also limiting the capping to search though faces that actually need capping. It could also be turned into a macroscript, but I didn’t really see the point.
I’ve included features like predicting the number of objects and faces that will be created, and a progress bar sine the process is quite slow.
Haven’t really had much time to spend on this one, and probably spent to much anyway, about 3hrs plus a bt of messing around the other night when I was thinking about how I would do it (i was thinking more about explode then).

Nice work ivanisavich, and thanks again to erilaz.

One question: how can I post my code so I don’t lose the formatting (in this case the tabs), if I paste from MXS I get double spacing.

oh, and I’ve also attached a function I use regulary (since footools left us) to explode polys into elements, works on an EMesh

J.


It looks like I’ve had problems with uploading one line of code to the webboard, so I’ve changed some variable names and uploaded the code again.



--------------------------------------------------------------------------------
--	CGTalk Challenge 2 - Divide Object
--	version 1.0
--	max version 6, 7
--	written by Joshua Newman
--	[www.joshuanewman.net]( http://www.joshuanewman.net/) 
--	written 22/06/05
--	last updated 22/06/05
--	copyright 2005
--------------------------------------------------------------------------------
--
-- divides an editable poly into any (ok up to 100 each axis) objects
 
global obj, pgttl
rollout CGTalk_divider "Parameters"
(
-- Functions
fn objfilter obj=classof obj==Editable_Poly
-- Rollout items
pickbutton pobj "Pick Editable Poly!" width:240 filter:objfilter
group "Segments"
(
spinner xd "X objects:" type:#integer range:[1,100,2]
spinner yd "Y objects:" type:#integer range:[1,100,2]
spinner zd "Z objects:" type:#integer range:[1,100,2]
label objnum "objects:8" align:#right
label fcenum "faces:0" align:#right
)
checkbox obox "original to boxmode" checked:false across:2
checkbox ohde "hide original" checked:true offset:[28,0]
 
button go "Divide" width:240 height:50 enabled:false
 
progressbar prgrss "" color:red
 
label l1 "\xa9 2005 Joshua Newman" across:2 offset:[-8,0]
hyperlink hl "[www.joshuanewman.net]( http://www.joshuanewman.net/ )" address:"[www.joshuanewman.net]( http://www.joshuanewman.net/ )" offset:[4,0]
-- rollout functions
fn updatedisplay c=
(
pgttl=(zd.value*yd.value*xd.value)
objnum.text=("Objects:"+pgttl as string)
if obj!=undefined then 
(
fcenum.text=("faces:"+(obj.numfaces*pgttl) as string)
)
)
-- rollout events
on xd changed c do updatedisplay c
on yd changed c do updatedisplay c
on zd changed c do updatedisplay c
 
on pobj picked o do
(
obj=o
pobj.text=obj.name
go.enabled=true
)
 
on CGTalk_divider open do
(
-- this routine scans the current selection to look for a likely source object, and if it finds one, it places it in the settings
for s in selection do 	 
(
if classof s==Editable_Poly then 
(
	pobj.text=s.name
	obj=s
	print s
	go.enabled=true
	fcenum.text=("faces:"+(obj.numfaces*8) as string)
	exit
)
)
)
-- the start of the prcess
on go pressed do
(
-- get the parameter values into variables
xdiv=xd.value
ydiv=yd.value
zdiv=zd.value
 
-- the objects vital statistics
size=obj.max-obj.min
zval=size.z*1./zdiv
yval=size.y*1./ydiv
xval=size.x*1./xdiv
 
-- check the hide and boxmode settings and act as appropriate
if obox.state then obj.boxmode=true	-- make the origianl object display in boxmode
if ohde.state then hide obj	 -- hide the original object
 
-- some variables
zobj=#()
yobj=#()
xobj=#()
-- progressbar
pgvlu=0
 
-- Z slice!
 
for i=1 to zdiv do
(
local tmp=copy obj
polyop.slice tmp tmp.faces (ray [obj.pos.x,obj.pos.y,obj.min.z+zval*(i-1)] [0,0,1])	 -- slice bottom segment
polyop.slice tmp tmp.faces (ray [obj.pos.x,obj.pos.y,obj.min.z+zval*i] [0,0,1])	 -- slice top segment
fces=for i in tmp.faces collect i.index				-- collect the faces into an array
dlfcs=#()
for f in fces do if (polyop.getfacecenter tmp f).z<(obj.min.z+(zval*(i-1))) then append dlfcs f	 -- collect the faces below the slice
for f in fces do if (polyop.getfacecenter tmp f).z>(obj.min.z+(zval*i)) then append dlfcs f	 -- collect the faces above the slice
if dlfcs.count>0 then polyop.deletefaces tmp (dlfcs as bitarray) delisoverts:true	 -- if the list isn't emply, delete the faces
polyop.capholesbyface tmp (tmp.faces as bitarray)			 -- cap holes! - this is using all faces! could be costly on large polys
append zobj tmp
pgvlu+=1			 -- increase the progress
prgrss.value=100./pgttl*pgvlu		-- update the progressbar
)
-- Y slice
for z in zobj do
(
for i=1 to ydiv do
(
	local tmp=copy z
	polyop.slice tmp tmp.faces (ray [obj.pos.x,obj.min.y+yval*(i-1),obj.pos.z] [0,1,0]) 	 -- slice bottom segment
	polyop.slice tmp tmp.faces (ray [obj.pos.x,obj.min.y+yval*i,obj.pos.z] [0,1,0])	 -- slice top segment
	fces=for i in tmp.faces collect i.index			 -- collect the faces into an array
	dlfcs=#()
	for f in fces do if (polyop.getfacecenter tmp f).y<(obj.min.y+(yval*(i-1))) then append dlfcs f -- collect the faces below the slice
	for f in fces do if (polyop.getfacecenter tmp f).y>(obj.min.y+(yval*i)) then append dlfcs f -- collect the faces above the slice
	if dlfcs.count>0 then polyop.deletefaces tmp (dlfcs as bitarray) delisoverts:true -- if the list isn't emply, delete the faces
	polyop.capholesbyface tmp (tmp.faces as bitarray)		 -- cap holes! - this is using all faces! could be costly on large polys
	append yobj tmp
	pgvlu+=1			 -- increase the progress
	prgrss.value=100./pgttl*pgvlu		-- update the progressbar
)
)
-- X slice
for y in yobj do
(
for i=1 to xdiv do
(
	local tmp=copy y
	polyop.slice tmp tmp.faces (ray [obj.min.x+xval*(i-1),obj.pos.y,obj.pos.z] [1,0,0]) 	 -- slice bottom segment
	polyop.slice tmp tmp.faces (ray [obj.min.x+xval*i,obj.pos.y,obj.pos.z] [1,0,0])	 -- slice top segment
	fces=for i in tmp.faces collect i.index			 -- collect the faces into an array
	dlfcs=#()
	for f in fces do if (polyop.getfacecenter tmp f).x<(obj.min.x+(xval*(i-1))) then append dlfcs f -- collect the faces below the slice
	for f in fces do if (polyop.getfacecenter tmp f).x>(obj.min.x+(xval*i)) then append dlfcs f -- collect the faces above the slice
	if dlfcs.count>0 then polyop.deletefaces tmp (dlfcs as bitarray) delisoverts:true -- if the list isn't emply, delete the faces
	polyop.capholesbyface tmp (tmp.faces as bitarray)		 -- cap holes! - this is using all faces! could be costly on large polys
	append xobj tmp
	pgvlu+=1			 -- increase the progress
	prgrss.value=100./pgttl*pgvlu		-- update the progressbar
)
)
-- remove the temporary slices 
delete zobj
delete yobj
prgrss.value=0 -- reset progress bar
select xobj	-- select new objects
)
)
newroll=newrolloutfloater "CGTalk - Challenge 2 - Divider." 260 302 750 80
addrollout CGTalk_divider newroll
 

sorry, attachment:

J.

Wow…Fantastic work j_man! Your script worked great! A couple of minor errors popped up…but no big deal it still did what it needed to do.

As for mine…I updated it a bit…still no where near the quality of yours ;)…but now it’s a bit more intuitive…and the faces extrude inward instead of outward.

http://www.theonlyt.com/Main/Scripts/splitter.ms

Guys, fantastic work so far! I’m still nutting out a decent method myself!

Can you please keep to the rule of posting your code in

 tags, rather than an attachment?  It makes it easier for people to see the code at face value, and then copy/paste it if they want to run it.

Sorry Erilaz…I figured wasting more forum space with my updated code would be pointless since not too many things were changed.

1 Reply
(@erilaz)
Joined: 11 months ago

Posts: 0

Not a problem. In fact, it would be preferable for you to repost your code and highlight what you’ve changed. The changes are the important part. The challenges are here to encourage thinking about new ways of using maxscript, and since this is all text you can fill the thread as much as you like (within reason, of course!)

K cool


 global amount1 = bezier_float() --threw these 2 initializations up here to avoid errors
 global chunk1 = bezier_float()
 
 rollout splitroll "Object Splitter" width:162 height:52
 (
 			
 		button split "Split Selected Object" pos:[25,10] width:140 height:26
 		spinner amount "Extrude amount: " range: [.01,100,1] controller:amount1 
 		spinner chunk "Chunk size: " type:#integer range: [1,100,1] controller:chunk1
 		button del "Delete ALL chunks" --new button was added for housecleaning :)
 		on split pressed do
 		(
 			sel = selection[1]
 			
 			if sel != undefined then
 			(
 				namecount = 0
 				obj1 = sel
 				
 				convertto obj1 editable_poly
 				faces = polyop.getnumfaces obj1
 		    	print ((faces as string) +  " faces in " + (sel as string))
 		    	if chunk1.value >= faces then chunk1.value = (faces-1)
 				for t in 1 to faces by (chunk1.value as integer) do
 				(
 					namecount += 1
 		    		name1 = ("temp_obj" + (namecount as string)) --to avoid duplication problems later on, chunks that are being split in this loop are given a temporary name while the loop is in progress
 		    		if t+(chunk1.value) > faces then
 					(
 		    			face_array = #{t..faces}
 						
 					)
 					else
 					(
 		    			face_array = #{t..(t+(chunk1.value-1))}
 						
 					)
 		    		polyop.detachfaces obj1 face_array delete:false asnode:true name:name1 node:[0,0,0]
 		    		name2 = $*temp_obj*[namecount]	
 					
 					count = polyop.getnumfaces name2
 					
 					
 					for i in 1 to count do
 					(
 						
 		    			verts_array = polyop.getfaceverts name2 i --different method of capping holes in this update...still some issues when chunks are made from a group of 3 faces or more...but whatever :P    				    
 		    		    polyop.capholesbyvert name2 verts_array
 						
 					)
 		    		count2 = polyop.getnumfaces name2		    	   
 		    		polyop.extrudefaces name2 #{(count+1)..count2} (amount1.value) --faces are now extruded properly					
 		    		name2.pivot = name2.center		    		   
 				)
 				select $*temp_obj*
 				namecount = 0
 				for j in selection do
 				(
 					namecount += 1
 		    		j.name = "explode_obj" + (namecount as string) --this loop renames the temp objects to their proper names
 					
 				)
 				deselect $
 				delete obj1
 			)
 			else print "No selection"			
 		)
 		on del pressed do
 		(
 			try
 			(
 				delete $explode_obj* --deletes all chunks (if they are present)
 			)
 			catch ()
 		)
 		on splitroll open do
 		(
 			amount1.value = 1
 			chunk1.value = 1	
 		)
 )
 splitter=newrolloutfloater "Object Splitter" 200 143 650 300 --splitter script is now a floater instead of utility. Thanks for the idea on that one j_man!
 addrollout splitroll splitter
 

Hey Josh,

I get this error when trying to change the X spinner:
– Error occurred in updateDisplay()
– Frame:
– xdivision: undefined
– S: undefined
– c: 3
– called in xdivisions.changed()
– Frame:
– c: 3
>> MAXScript Rollout Handler Exception: – Type error: Call needs function or class, got: undefined <<

And this one if I don’t touch the defaults and just hit “Divide”:
– Error occurred in i loop
– Frame:
– tmp: $Editable_Poly:Plane02 @ [1523.483398,-0.000044,1002.226563]
– i: 1
– dlfcs: #(11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25)
– fces: #(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, …)
– called in go.pressed()
– Frame:
– xdiv: 2
– yobj: #()
– yval: 7.35278e-006
– pgvlu: 1
– zval: 168.212
– zobj: #($Editable_Poly:Plane02 @ [1523.483398,-0.000044,1002.226563])
– zdiv: 2
– xobj: #()
– size: [1160.66,1.47056e-005,336.424]
– ydiv: 2
– xval: 580.331
>> MAXScript Rollout Handler Exception: – Incompatible types: 100.0, and undefined <<

It looks very well organized though.

Okay, I’m jumping in to this one. The script is fairly simple right now, but I plan on beefing it up as I find the time. This could be very handy for effects work.

 How to work it:
 - Run the script.
 - Select an editable poly object with lots of polys.
  • Crank up the “Crack Factor”, the higher the number, the bigger the chunks. Beware that if your object has tons of polys and the crack factor is kept too low, it could take a while to process.

    • Set the “Thickness” value to something other than 0 to add an edge to the chunks.
    • Hit “Do It!” to crack up the object.

    I’ll be adding more options and trying to optimize it later, but this is a good first pass.

fn growSelectionByRandomEdge obj faceNum numTimes = (
    	local faceArr = #(faceNum)
    	for t = 1 to numTimes do (
    		local edgeArr = (polyOp.getEdgesUsingFace obj faceArr) as array
    		n = edgeArr.count / 2
    		for j = n to 1 by -1 do (
    			ranNum = random 1 n
    			deleteItem edgeArr ranNum
    			)
    		tempArr = (polyOp.getFacesUsingEdge obj edgeArr) as array
    		for j = 1 to tempArr.count do (append faceArr tempArr[j])
    		)
    	return faceArr as array
    	)
    
    rollout crackMeUpRlt "Crack Me Up" (
    	button doItBtn "DO IT!" width:125 height:30
        spinner crackFactorSpnr "Crack Factor:" range:[1,1000000,10] type:#integer offset:[0,10] width:110 align:#right
    	spinner thicknessSpner "Thickness:" range:[0,1000000,10] width:100 align:#right
    	
    	on doItBtn pressed do (
    		obj = selection[1]
    		if obj != undefined and classof obj == Editable_Poly then (
    			crackFactor = crackFactorSpnr.value
    			crackObjArr = #()
    			numFacesStart = getNumFaces obj
    			setWaitCursor()
    			with undo "CrackMeUp" on (
 				for i = numFacesStart to 1 by -1 do (
 					numFaces = getNumFaces obj
 					if numFaces == 0 then exit
 		 	detachArr = growSelectionByRandomEdge obj numFaces crackFactor
 		 		polyOp.detachFaces obj detachArr asNode:true name:(uniqueName obj.name)
				    objArr = objects as array
        		    	append crackObjArr objArr[objArr.count]
    					)
    				delete obj
    				for i in crackObjArr do (
 		 	i.wirecolor = [(random 0 255),(random 0 255),(random 0 255)]
 					i.pivot = i.center
        		    	if thicknessSpner.value != 0 then (
        		    		pushMod = Shell()
        		    		addModifier i pushMod
 		 		pushMod.innerAmount = thicknessSpner.value
 						)
 					collapseStack i
    					)
    				) -- end undo
    			setArrowCursor()
    			) else ( -- end if classof obj
 			messagebox "Selected object needs to be a collapsed Editable Poly."
    			)
    		) -- end doItBtn pressed
    	) -- end rollout
    
    createDialog crackMeUpRlt width:150 pos:[40,80]
Page 1 / 5