Home Technical Talk

Roadkill, 3DSMax 2009 - 2010 script updated

UPDATE: Just found a bug in 2010 where the edges selected in max weren't the edges being cut by Roadkill. I've updated the script on the site so if you were having weird edge selection issues under max 2010 that should fix it.


Hi Guys,

Ages back I wrote a script to bridge the gap between max and roadkill. I've updated it since then and improved the work flow and thought I'd make it available as I noticed people were still using it.

Homepage:
https://sites.google.com/site/copypastepixel/roadkill-maxscript

Feed back would be great so if you have any problems or suggestions let me know :)

Cheers,
copypastepixel

Replies

  • Nysuatro
    Thanks man. Looks like your are also on this forum, hehe
  • mLichy
  • renderhjs
    Offline / Send Message
    renderhjs sublime tool
    Could you elaborate a bit on the binary hack? I noticed that you change some ini settings of the (OBJ?) exporter and change some binaries to make things work.

    Will test the script later, looks interesting
  • copypastepixel
    renderhjs wrote: »
    Could you elaborate a bit on the binary hack? I noticed that you change some ini settings of the (OBJ?) exporter and change some binaries to make things work.

    Will test the script later, looks interesting

    Yeah that's a bit of a weird one. RK needs it's OBJ's to be exported and imported a certain way. I didn't want the user to have to manually configure the OBJ exporter every time they use RK so I need access to those settings.

    The settings are held in a nice INI file in 2010 but in 2009 there's no way of altering the obj exporter/importer settings via script. My workaround was to setup the OBJ plugin once, read in the state of it's setting files and then overwrite them as needed.

    It works but it is a massive hack....

    -copypastepixel
  • renderhjs
    Offline / Send Message
    renderhjs sublime tool
    @copypastepixel:
    I am right digging through your code and simplify it for my needs which is just splitting along selected UV edges and just unwrap associated shells.
    Your hacks are a very valuable input and I hope I get things working the way I want it. I will post more and the finished script once done.

    Anyway I just wanted to thank you for the great script - its a good source to learn from and build upon.
  • copypastepixel
    @renderhjs:
    Hope it helps :) I spent ages on the edge selection stuff. Most of it trying to use the unwrap mod's pelt seam selection to mark up the edges. Had no luck though as I never found a way to convert from the pelt seam selection to a mesh edge selection.

    Look forward to seeing your script. Will you keep the process entirely in max or use roadkill to unwrap?
  • renderhjs
    Offline / Send Message
    renderhjs sublime tool
    yeah I fixed that edge selection through the UVunwrap editor half way through, though there is a bug in Max2010 that prevents me from using either of the 2:
    unwrap5.getSelectedGeomEdges()
    unwrap6.getSelectedGeomEdgesByNode ...
    
    See also:
    http://area.autodesk.com/forum/autodesk-3ds-max/maxscript/maxscript-function-uvgetselectedgeomedges-broken-/page-last/

    But here is a snippet that will select all open Edges from your UV editor:
    function getOpenEdges obj uv=(--subfunction
    	--get a list of all Shells filled with the Bitarary selections for each shell
    	uv.selectFaces #{1..uv.unwrap.numberPolygons()};
    	uv.facetoedgeselect();--all edges are selected
    	local allEdgesSelection = uv.unwrap2.getSelectedEdges();
    	local edgeElemArray = #();
    	for ed in allEdgesSelection do (
    		edgeElemArray[ ed ] = 0;
    	)
    	local elem = #();
    	with redraw off;
    	for ed in allEdgesSelection do (
    		if edgeElemArray[ ed ] == 0 then (
    			uv.unwrap2.selectEdges  #{ ed };
    			uv.unwrap2.selectElement();
    			local elemEdges = uv.unwrap2.getSelectedEdges() as array;
    			if elemEdges.count > 2 then (-- Ignore elements with less than 3 UV vertices.
    				append elem (uv.unwrap2.getSelectedEdges());
    				for i in elemEdges do (
    					edgeElemArray[ i ] = elem.count; -- Mark these vertices with their element number in vertElemArray.
    				)
    			)
    		)
    	)
    	--now get all the open Edges for all shells (elem = shell)
    	local openEdgesSelection = #{};--the final open Edges selection
    	for e in elem do(
    		local selections = #();
    		local selectionsNum = #();
    		local maxNum = 0;
    		for ed in e do(
    			uv.unwrap2.selectEdges  #{ ed };
    			uv.unwrap2.openEdgeSelect();
    			if ((uv.getselectededges()).numberset > 1)then(
    				local sel = ((uv.unwrap2.getSelectedEdges()) - #{ ed });--substract the original edge, kinda a bug in the openEdgeSelect method
    				openEdgesSelection+= sel;--add it to the bitarray (only not yet switched bits will be drawn)
    			)
    		)	 
    	)
    	uv.unwrap2.selectEdges openEdgesSelection;
    	--uv.unwrap5.getSelectedGeomEdges()
    	local geoOpenUVEdges = uv.unwrap5.getSelectedGeomEdges();
    	print("oGeoEdges "+geoOpenUVEdges as string);
    )
    
    
    
    
    
    function fn_39__unwrap_with_roadkill =(
    	clearListener();
    	if (selection.count == 1)then(--at least an object selected
    		local obj = selection[1]; 
    		local uv = modPanel.getCurrentObject();
    		--classOf obj == Editable_mesh and 
    		if (classOf obj.baseObject == Editable_Poly and classof(uv) == Unwrap_UVW)then(
    			local version = (maxVersion())[1];
    			if (version >= 11000) then( --at least max 2009, otherwise no support
    				getOpenEdges obj uv;
    			)
    		)
    	)
    )
    fn_39__unwrap_with_roadkill();
    

    To test it, make sure you have a poly object (no mesh) and add a UVWunwrap modifier, and finally open the UV editor in Max for that poly. After that simply execute the script and it should select all open Edges.

    At first I tested it using a Mesh as a base object but it would select triangle edges within quads or nGon faces which is wrong but I think its more because of the nature of the Mesh object which is just way more limited.


    So my goal from here on is:
    1. Create a snapshot of the Poly you are currently editing in the UVunwrap editor, using the snapShot method
    2. transfer the openEdge bitarray to the mesh (the Vertex Order of a Mesh should be the same as poly so it should be no problem to transfer it).
    3. Export it using your code base and send it to the roadkill command line thingy
    4. Get it back from roadkill read it out into the still open UVeditor modifier.

    At some point I want to add a filter that only reads back changed UV shells that are associated with your selections (edges or faces in the UV editor). That way one could just flatten a selected shell without affecting all other shells one could have layouted already before.

    Look forward to seeing your script. Will you keep the process entirely in max or use roadkill to unwrap?
    I want to remain in Max and just use roadkill as a under the hood tool to unwrap very specific parts that are marked in the 3dsmax unwrap editor.
    Reasons for that are:
    • I don't like the navigation of Roadkill
    • don't like to switch applications all the time
    • like to keep it all in one place
    Should I succeed I want to integrate into TexTools:
    http://www.renderhjs.net/textools/
    So that I have ABF and LSCM at my fingertips in Max without the need of installing plugins for max.
  • CompanionCube
    Offline / Send Message
    CompanionCube polycounter lvl 12
    it would be amazing if you intergrated this into textools renderhjs!
  • renderhjs
    Offline / Send Message
    renderhjs sublime tool
    hey copypastepixel can you have a look at this code? - I think I have it working so far except it can't find at them moment the RoadKill URL even though the URL is correct on my computer.

    The problem orginates from:
    fn killroad_exportObj obj= (
    	select #(obj);
    	exportFile OBJFilePath #noPrompt selectedOnly:true;
    	commandstring = (" "" + OBJFilePath + "," + EdgeFilePath + ",-abf" + ",-nofillholes" + ",-notlive"+""")
    	local val = DOSCommand (Roadkillapp+commandstring);--Launch RoadKill
    	if val == 1 then(
    		print("Warning: The script was unable to locate the RoadKill executable! No UV's importedn");
    	)
    )
    
    I tested the URL's by executing them in the Run command panel in Windows and they are all valid but for some reason it returns 1 and I guess it means that there is some error with the parameters and or Roadkill URL.


    My script so far is:
    --RoadKill unwrap
    OBJFilePath =  GetDir #export + "roadkillOBJ.obj"
    RoadKillApp = "F:Projects 2010TexTools 3.20roadKillRoadKill 1_1aRoadKill1_1.exe"
    EdgeFilePath = GetDir #export + "edgList.edg";
    
    fn killroad_OverwriteGWObjConfig =(
    	local version = (maxVersion())[1];
    	if version == 11000 then( -- max 2009 only
    		local inputCfgPath = ((getDir #plugcfg) + ""+"gw_objimp.cfg")
    		local outputCfgPath =  ((getDir #plugcfg) + ""+"gw_objexp.cfg")
    		fnWriteBinary inputCfgPath #(5, 0, 0, 0, 39, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, -128, 63, 2, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 11, 0, 0, 0, 0, 0, 0, 0)
    		fnWriteBinary outputCfgPath #(16, 0, 0, 0, 86, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -128, 63, 0, 0, 1, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 46, 47, 109, 97, 112, 115, 47, 0, 1, 0, 0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 3, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 60, 78, 79, 78, 69, 62)
    	)else if version >= 12000 then( -- max 2010
    		local iniPath_exportSettings = objExp.getIniName()
    		setINISetting iniPath_exportSettings "Geometry" "FlipZyAxis" "0"
    		setINISetting iniPath_exportSettings "Geometry" "Shapes" "0"
    		setINISetting iniPath_exportSettings "Geometry" "ExportHiddenObjects" "0"
    		setINISetting iniPath_exportSettings "Geometry" "FaceType" "0"
    		setINISetting iniPath_exportSettings "Geometry" "TextureCoords" "0"
    		setINISetting iniPath_exportSettings "Geometry" "Normals" "0"
    		setINISetting iniPath_exportSettings "Geometry" "SmoothingGroups" "0"
    		setINISetting iniPath_exportSettings "Geometry" "ObjScale" "1.000000"
    		
    		setINISetting iniPath_exportSettings "Output" "RelativeIndex" "0"
    		setINISetting iniPath_exportSettings "Output" "Target" "0"
    		setINISetting iniPath_exportSettings "Output" "Precision" "4"
    		
    		local iniPath_importSettings = objImp.getIniName()
    		setINISetting iniPath_importSettings "General" "ResetScene" "0"
    		
    		setINISetting iniPath_importSettings "Objects" "SingleMesh" "1"
    		setINISetting iniPath_importSettings "Objects" "Retriangulate" "0"
    		setINISetting iniPath_importSettings "Objects" "AsEditablePoly" "0"
    
    		setINISetting iniPath_importSettings "Geometry" "TextureCoords" "1"
    	)
    )
    
    
    
    fn killroad_exportObj obj= (
    	select #(obj);
    	exportFile OBJFilePath #noPrompt selectedOnly:true;
    		
    	commandstring = (" "" + OBJFilePath + "," + EdgeFilePath + ",-abf" + ",-nofillholes" + ",-notlive"+""")
    	local val = DOSCommand (Roadkillapp+commandstring);--Launch RoadKill
    	if val == 1 then(
    		print("Warning: The script was unable to locate the RoadKill executable! No UV's importedn");
    	)
    )
    
    fn killroad_importObj = (
    	importFile OBJFilePath #noPrompt;
    
    	if IsValidNode  then(--just load for now the OBJ, to see if it works
    		print("success importing!!!");
    	)else(
    		messageBox "Node: 'roadKillObj' not found. No UV's importednThe script expected to find an imported node named 'default'. Check that 'single' is selected in the OBJ importer settings."
    	)
    )
    
    
    
    
    function killroad_saveOpenEdges obj uv=(
    	uv.unwrap2.setTVSubObjectMode 2;
    	--get a list of all Shells filled with the Bitarary selections for each shell
    	uv.selectFaces #{1..uv.unwrap.numberPolygons()};
    	uv.facetoedgeselect();--all edges are selected
    	local allEdgesSelection = uv.unwrap2.getSelectedEdges();
    	local edgeElemArray = #();
    	for ed in allEdgesSelection do (
    		edgeElemArray[ ed ] = 0;
    	)
    	local elem = #();
    	with redraw off;
    	for ed in allEdgesSelection do (
    		if edgeElemArray[ ed ] == 0 then (
    			uv.unwrap2.selectEdges  #{ ed };
    			uv.unwrap2.selectElement();
    			local elemEdges = uv.unwrap2.getSelectedEdges() as array;
    			if elemEdges.count > 2 then (-- Ignore elements with less than 3 UV vertices.
    				append elem (uv.unwrap2.getSelectedEdges());
    				for i in elemEdges do (
    					edgeElemArray[ i ] = elem.count; -- Mark these vertices with their element number in vertElemArray.
    				)
    			)
    		)
    	)
    
    	--now get all the open Edges for all shells (elem = shell)
    	local openEdgesSelection = #{};--the final open Edges selection
    	for e in elem do(
    		local selections = #();
    		local selectionsNum = #();
    		local maxNum = 0;
    		for ed in e do(
    			uv.unwrap2.selectEdges  #{ ed };
    			uv.unwrap2.openEdgeSelect();
    			if ((uv.getselectededges()).numberset > 1)then(
    				local sel = ((uv.unwrap2.getSelectedEdges()) - #{ ed });--substract the original edge, kinda a bug in the openEdgeSelect method
    				openEdgesSelection+= sel;--add it to the bitarray (only not yet switched bits will be drawn)
    			)
    		)	 
    	)
    	uv.unwrap2.selectEdges openEdgesSelection;
    
    	if (openEdgesSelection.numberset > 0)then(
    		local binFile = fOpen EdgeFilePath "wb";--write binary permission
    		WriteShort binFile openEdgesSelection.count #unsigned
    		WriteShort binFile 0 #signed -- seperator.
    		uv.unwrap2.setTVSubObjectMode 1;--otherwise Geo Verts are not recognized
    		for e in openEdgesSelection do(
    			uv.unwrap2.selectEdges  #{ e };
    			uv.edgeToVertSelect();
    			local vertList = uv.unwrap5.getSelectedGeomVerts();
    			if (vertList.numberSet == 2)then(
    				for v in vertList do(
    					writeString binFile ((v-1) as string)
    				)
    			)else(
    				exit;--error, some messy faces somewhere, where 1 edge connect with more than 2 geo verts
    			)
    		)
    		fClose binFile	
    		print("edges saved : "+openEdgesSelection.numberset as string+"x");
    	)
    )
    
    
    function fn_39__unwrap_with_roadkill =(
    	local version = (maxVersion())[1];
    	if (version >= 11000) then( --at least max 2009, otherwise no support
    		if (selection.count == 1)then(--at least an object selected
    			local obj = selection[1]; 
    			local uv = modPanel.getCurrentObject();
    			--classOf obj == Editable_mesh and 
    			if (classOf obj.baseObject == Editable_Poly and classof(uv) == Unwrap_UVW)then(
    
    				killroad_OverwriteGWObjConfig();
    				killroad_saveOpenEdges obj uv;
    				killroad_exportObj	obj;
    				killroad_importObj();
    					
    				print("Done.");
    			)
    		)
    	)		
    )
    clearListener();
    fn_39__unwrap_with_roadkill();--start the RoadKill OpenEdge auto Unwrap test
    


    Just change the URL of the RoadKill exe @ the 2nd line:
    RoadKillApp = "F:Projects 2010TexTools 3.20roadKillRoadKill 1_1aRoadKill1_1.exe"
    
    Then select a Poly Object and add a UVunwrap modifier (no need to open the UV editor) and execute the script.
    The root function that triggers everything is at the bottom, the rest is somewhat the same as your script except it writes a edge selection file from the UVunwrap modifier.

    Oh and I removed the popup warning, so there is just a trace warning if the URL couldn't be found.
  • copypastepixel
    @renderhjs:
    That all sounds possible. The only issue you may come across using my code is that it's all designed to work with edit mesh, not edit poly. Although it uses an edit poly mod to make edge selections, once that's done the export and import of data is all based around edit mesh triangulation. From memory I had some trouble getting an edit poly workflow working.

    It was while back now but I recall being unable to get an edit poly edge selection to match up with the .OBJ mesh that roadkill imported. I'm positive it's achievable though, I just left the problem alone after I got it all working with edit meshes.

    Being able to selectively unwrap shell sections from the unwrap interface is a really neat idea though, it would be great if you can make it happen!
  • copypastepixel
    @renderhjs:
    Just took a quick look and it looks like the export object and edge list paths aren't right.

    OBJFilePath = GetDir #export + "roadkillOBJ.obj"

    evaluates to

    "C: \Documents and Settings\User\My Documents\3dsMax\exportroadkillOBJ.obj"

    it should be

    OBJFilePath = GetDir #export + "\\roadkillOBJ.obj"

    which evaluates to

    "C: \Documents and Settings\User\My Documents\3dsMax\export\roadkillOBJ.obj"

    With that fixed the rest works fine :)
  • renderhjs
    Offline / Send Message
    renderhjs sublime tool
    you are right and then again wrong but got me on the right track :)

    its the board here that deletes the double backslash in the code tags. Its in my code - but in the end it seemed to be something related to my URL, so I just copied it directly to my c:\ drive without any spaces in the url or filename - and it works.

    But I am getting still some odd results:
    roadkill_script_01.gif
    I assume this is perhaps because the edge list is not working the way I hoped?, I will do some other tests later with some more primitive shapes to analyze whats going on.
    Also your non-flipping setting in the OBJ exporter turns out to be wrong with my 3dsMax 2010 so I enabled it and it imports back at the original position.

    My script so far, without beeing crippled by this board:
    http://renderhjs.net/bbs/polycount/roadKill/fn_39__unwrap_with_roadkill_003.ms
  • copypastepixel
    Just tried your script on a teapot and it seemed to work fine. I built a default teapot, converted to e-poly, created some shells with UV unwraps quick planar map, it came back fine.

    I also tried a few simple characters and they worked fine as well so I'm not sure what's going on there. Also, my meshes weren't coming back triangulated either.

    It's possible there's an obj setting that's not being set correctly on your PC, something I overlooked when overwriting the settings. Maybe have a hunt around the OBJ plugin settings and see if that helps?
  • MoP
    Offline / Send Message
    MoP polycounter lvl 18
    renderhjs: Are you using "snapshot" (or "snapshotAsMesh"? can't remember if that's a command or not)... I noticed a while back when doing my normalize UV script, that sometimes meshes would change topology after a snapshot. It was very strange. I had a test case lying around somewhere, I'll see if I can dig it up. Basically after snapshotting, 2 edges magically disappeared from the mesh, so you couldn't rely on the initial object being identical to the snapshot object. This was in Max 2008.
  • copypastepixel
    renderhjs: I think I know why your code worked for me but not you. My bad, turns out my script wasn't correctly setting the obj plugin before export ( in max 2010). The OBJ exporter has an option to optimise models but this seems to mess with the edge order. I'd manually disabled optimisation in the exporter on my PC hence it worked here but not anywhere else!

    Anyways, the script is updated so see if you have any more luck there :)
  • renderhjs
    Offline / Send Message
    renderhjs sublime tool
    holy mother of jesus, I got it working :D
    I am extremely pleased, will try to find a way this weekend to implement it to TexTools with a easy hook in way to add roadkill.

    I noticed though that your newest version has the ini switch of:
    setINISetting iniPath_exportSettings "Geometry" "TextureCoords" "0"
    
    which should be a 1 for true, because otherwise it wont work ;)

    I will update a trimmed down and optimized version of my script sometime this weekend so it works with any object selected. Which means that
    1. it will automatically add a new UVunwrap modifier if needed
    2. collect all the open edges + selected UV edges if any as the seam array
    3. export using your technique, have it be unwrapped by RoadKill, wait and import it back
    4. copy the UV channel of that imported poly (yes I got poly objects working, no forced triangles) and paste it to the original selected object using the paste UV modifier.
    There is still some flickering and some DOS prompt pop up going on (command line of RoadKill) but in the end I think this will be a super awesome script to just unwrap the way it takes hours otherwise with the crappy pelt stuff in max.

    thanks again for your help and input, much appreciated
  • r_fletch_r
    Offline / Send Message
    r_fletch_r polycounter lvl 9
    MoP wrote: »
    renderhjs: Are you using "snapshot" (or "snapshotAsMesh"? can't remember if that's a command or not)... I noticed a while back when doing my normalize UV script, that sometimes meshes would change topology after a snapshot.

    Its the conversion from editable poly to editable mesh i think. the channel tools dont work at all between the 2 :(

    If the mesh is 100% tris this isnt an issue. Quads and Ngons seem to make shit of the face indexes.
  • copypastepixel
    Top stuff! Can't wait to see it in action.

    I'll be damned... you're right about that INI UV setting. I didn't realise roadkill was able to work with/around existing UV's...

    EDIT: Wait, no, just checked again and RK doesn't pay attention to the incoming UV's so not exporting UV's shouldn't be an issue :)
  • renderhjs
    Offline / Send Message
    renderhjs sublime tool
    r_fletch_r wrote: »
    Its the conversion from editable poly to editable mesh i think. the channel tools dont work at all between the 2 :(

    If the mesh is 100% tris this isnt an issue. Quads and Ngons seem to make shit of the face indexes.
    no worries I got it working with editable poly objects so it works with any model.
    that way I can also just swap UVs with the imported roadkill one all in one modifier.
  • ironbearxl
    Offline / Send Message
    ironbearxl polycounter lvl 18
    Since Roadkill comes from Blender code, I'm not sure if the GPL would allow using it as a script inside a commercial application. Just a heads up. :)
  • renderhjs
    Offline / Send Message
    renderhjs sublime tool
    I am not intending doing anything commercial , nor do I plan distributing it with Textools. People will have to download it themselves and point towards the exe - thats at least my plan with textools.
  • ironbearxl
    Offline / Send Message
    ironbearxl polycounter lvl 18
    Ah ok, all good then renderhjs.
  • r_fletch_r
    Offline / Send Message
    r_fletch_r polycounter lvl 9
    renderhjs wrote: »
    no worries I got it working with editable poly objects so it works with any model.
    that way I can also just swap UVs with the imported roadkill one all in one modifier.

    how are you importing the obj file as an editable poly?
    :poly142:
    Edit: decided to look at my code again, had a hissy fit and wrote my own export code
    and a function to rip the UVs from obj files and inject them into editable poly objects.
    If this is of interest i can post the files.
  • zweek
    Offline / Send Message
    zweek polycounter lvl 18
    awesome renderhjs! looking forward to your latest update. this + textools = kickass
  • zweek
    Offline / Send Message
    zweek polycounter lvl 18
    renderhjs, when I run your script with open edges (by selecting edges and splitting them) it works much better than if I create UV seams within the Unwrap UVW modifier, not sure why...

    thanks again for giving these tools for us to use. Textools has really sped up my workflow.
    -z
  • renderhjs
    Offline / Send Message
    renderhjs sublime tool
    weird I haven't even updated the script here ._o but if it works for you. I hope to find some time today to wrap it all up.
Sign In or Register to comment.