# RoadKillPlugin # Initial code generated by XSI SDK Wizard # Executed Tue Mar 27 16:22:44 UTC+0100 2007 by kim # import win32com.client from win32com.client import constants import os import struct import tempfile import time null = None false = 0 true = 1 def XSILoadPlugin( in_reg ): #{{{ in_reg.Author = "Kim Aldis" in_reg.Name = "RoadKillPlugin" in_reg.Email = "" in_reg.URL = "" in_reg.Major = 1 in_reg.Minor = 0 in_reg.RegisterCommand("RoadKill","RoadKill") in_reg.RegisterMenu(constants.siMenuTbGetPropertyTextureProjectionID,"RoadKill_Menu",false,false) #RegistrationInsertionPoint - do not remove this line if ( Application.Interactive ) : oPrefs = Application.Preferences.Categories("RoadKill") if ( not oPrefs ) : Application.LogMessage( "Installing RoadKill Prefs" ) oCus = Application.ActiveSceneRoot.AddCustomProperty( "tmp" ) oCus.AddParameter2("RoadKillLocation", constants.siString,"C:\\program files\\RoadKill\\RoadKill1_1.exe", 0,0,0,0, false, false, "Roadkill Path" ) Application.InstallCustomPreferences( oCus, "RoadKill" ) return true def XSIUnloadPlugin( in_reg ): strPluginName = in_reg.Name return true #}}} ########################## Command Entry Points ############################# def RoadKill_Init( in_ctxt ): #{{{ oCmd = in_ctxt.Source oCmd.Description = "" oCmd.ReturnValue = true oArgs = oCmd.Arguments oArgs.Add("inMesh",constants.siArgumentInput) oArgs.Add("inEdgeList",constants.siArgumentInput) oArgs.Add("doABF",constants.siArgumentInput) oArgs.Add("doFillHoles",constants.siArgumentInput) oArgs.Add("doLive",constants.siArgumentInput) return true #}}} def RoadKill_Execute( inMesh, inEdgeList, doABF, doFillHoles, doLive ): #{{{ oEdgeSel = false oObject = false # do our best to accomadate input arguments and/or selections # if no inMesh then try to get object and edges as selection. # if ( inMesh is None ) : # assume something selected if ( Application.Selection.count == 0 ) : Error( "No Mesh selected or supplied as argument" ) return false oSel = Application.Selection(0) if ( oSel.type == "edgeSubComponent" ): oObject = oSel.Subcomponent.Parent3DObject oEdgeSel = oSel else: oObject = oSel oEdgeSel = false else : oObject = inMesh # if we haven't made it here with an edgelist then try for the input # arg if ( oEdgeSel is None ) : oEdgeSel = inEdgeList # and finally, if we still have no mes, DEATH!!! # no edgelist we can live with if ( not oObject ) : Error( "No mesh selected" ) return false # set up a temp output for obj and edge file sObjpath = Unique( "obj" ) sEdgepath = Unique( "edg" ) #sObjpath = "c:/temp/pee.obj" Application.LogMessage( "OBJ: " + sObjpath ) oPrefs = Application.Preferences.Categories( "Roadkill" ); if ( not oPrefs ): Error( "Can't find Preferences for Roadkill" ) return false sPath = oPrefs.RoadKillLocation.value; sRoadkillPath = "start /WAIT \"Roadkill\" \"" + sPath + "\" " # export the obj # Application.ObjExport( sObjpath, "", "", "", "", "", "", "", "", "", 0, 0, 0, "", 0) #exports with normals and uvs!! #objWrite( oObject, sObjpath ) sEdgeList = WriteEdgeList( oObject, oEdgeSel, sEdgepath ) # and system it # sArgs = "" if ( doABF is None or doFillHoles is None or doLive is None ) : #build a ppg. oProp = Application.ActiveSceneRoot.properties( "RoadKill" ) if ( not oProp ) : oProp = Application.ActiveSceneRoot.AddProperty( "CustomProperty", false, "RoadKill" ) odoABF = oProp.AddParameter2( "doABF", constants.siBool, true, 0,0,0,0, false, false, "Use ABF unwrap (Off = use LCSM)" ); odoFillHoles = oProp.AddParameter2( "doFillHoles", constants.siBool, false, 0,0,0,0, false, false, "Fill Holes" ); odoLive = oProp.AddParameter2( "doLive", constants.siBool, false, 0,0,0,0, false, false, "Use Roadkill Interactively" ); #oDelete = oProp.AddParameter2( "Delete", constants.siBool, true, 0,0,0,0, false, false, "Delete temporary model" ); #oCopy = oProp.AddParameter2( "Copy", constants.siBool, true, 0,0,0,0, false, false, "Copy UVs" ); #oUnWrap = oProp.AddParameter2( "UnWrap", constants.siBool, true, 0,0,0,0, false, false, "Do Unwrap (call RK)" ); odoABF = oProp.Parameters( "doABF" ); odoFillHoles = oProp.Parameters( "doFillHoles" ); odoLive = oProp.Parameters( "doLive" ); bFail = Application.InspectObj( oProp, false, "Roadkill; uvunwrapping", constants.siModal, false ) if ( bFail ): Warn( "User Cancelled" ) return false doABF = odoABF.value; doLive = odoLive.value; doFillHoles = odoFillHoles.value if ( doABF ): sArgs = sArgs + ",-abf" else: sArgs = sArgs + ",-lscm" if ( doFillHoles ): sArgs = sArgs + ",-fillholes" else: sArgs = sArgs + ",-nofillholes" if ( doLive ): sArgs = sArgs + ",-live" else: sArgs = sArgs + ",-notlive" #if oUnWrap.value : sCommand = sRoadkillPath + " \"" + sObjpath + "," + sEdgepath + sArgs + "\"" Application.LogMessage( sCommand ); os.system( sCommand ) # read back the obj and pull out it's new UV data. oNew = Application.ObjImport( sObjpath, 1, 0, 1, 1, 0, 1)(0) os.remove( sObjpath ) # ToDo: Waiting on a fix for the copy point thing. CopyUVs( oNew, oObject ) Application.DeleteObj( oNew ) FixUVs( oObject ) #Application.lmFixUVs( oObject + ".polymsh.cls.sample.clslist.Sample.Texture_Projection" ) if ( sEdgeList ): os.remove( sEdgepath ) Application.SelectObj( oNew ); return true #}}} def RoadKill_Menu_Init( in_ctxt ): #{{{ oMenu = in_ctxt.Source oMenu.AddCommandItem("RoadKill","RoadKill") return true #}}} ####################################### suppoort def Error( s ): #{{{ Application.LogMessage( "(RoadKill): %s" % (s), constants.siError ) #}}} def Warn( s ): #{{{ Application.LogMessage( "(RoadKill): %s" % (s), constants.siWarning ) #}}} def WriteEdgeList( oObject, oEdgeSel, sPath ): #{{{ if ( not oEdgeSel ) : Warn( "No edges have been selected selected for this unwrap. This may not work" ) return nPointsInObject = oObject.ActivePrimitive.Geometry.Points.count # Populate it with selected edges oEdges = oEdgeSel.Subcomponent.ComponentCollection nEdges = oEdges.count fp = open( sPath, mode='wb' ) data = struct.pack( 'l', nEdges ) fp.write( data ) for oEdge in oEdges: iLead = oEdge.points(0).index iTrail = oEdge.points(1).index fp.write( str( iLead ) ) data = struct.pack( 'x' ) fp.write( data ) fp.write( str( iTrail ) ) data = struct.pack( 'x' ) fp.write( data ) fp.close( ) #}}} # copy uv set from oSrc to oDest. # This assumes there's only one UV set coming back from # an obj import. def CopyUVs( oSrc, oDest ): #{{{ # first off, look for the uvs on the source obj oSampleSrc = false oClusters = oSrc.ActivePrimitive.geometry.clusters; for oCls in oClusters : if ( oCls.type == constants.siSampledPointCluster ): oSampleSrc = oCls break if ( not oSampleSrc ) : Error( "No UVs on source object " + oSrc.Name ) return false # look for a sample cluster on the destination and create one if it's not there. # oSampleDest = false oClusters = oDest.ActivePrimitive.geometry.clusters; for oCls in oClusters : if ( oCls.type == constants.siSampledPointCluster ): oSampleDest = oCls if ( not oSampleDest ): #using SICreateCluster because although it's not docced and I've no idea what that last argument is # I really can't be arsed to bugger about trying to resolve the name string which is the only thing # that comes back from Addcluster method. Grrh! # oSampleDest = Application.SICreateCluster( constants.siSampledPointCluster, "Texture_Coordinates_AUTO", oDest, 1+4 )(0) #and now we should be good to just do a copy paste. #source is a read OBJ, it should only have one UV set. I think. # oSrcUV = oSampleSrc.localproperties(0); Application.CopyPaste( oSrcUV, "", oSampleDest ) #}}} def Unique( s ): return "%s/%d.%s" % ( os.environ.get( "TEMP" ), int(time.time()), s ) def objWrite( oObj, sPath ): #{{{ fp = open( sPath, mode='w' ) fp.write( "# Wavefront OBJ Export by Kim Aldis\n" ) fp.write( "o %s\n\n" % (oObj.name) ) fp.write( "g %s\n\n" % (oObj.name) ) oGeom = oObj.ActivePrimitive.Geometry nPoints = oGeom.Points.count fp.write( "# begin %d Vertices\n" %( nPoints ) ) for i in range( nPoints ) : oPos = oGeom .Points(i).Position #Application.LogMessage( oPos.x ) fp.write( "v %f %f %f\n" % ( oPos.x, oPos.y, oPos.z ) ) nFaces = oGeom.Polygons.count fp.write( "\n#Normals\n" ) for i in range( nFaces ): oFace = oGeom.Polygons(i) nNodes = oFace.Nodes.count for j in range( nNodes ) : oNode = oFace.Nodes(j) fp.write( "vn %f %f %f\n" % ( oNode.Normal.x, oNode.Normal.y, oNode.Normal.z ) ) ## no need for uvs, RK will take care of this. fp.write( "\n#Faces\n" ) iNormal = 1 for i in range( nFaces ): oFacet = oGeom.Facets(i) oPoints = oFacet.Points.count fp.write( "f " ) for j in range( oPoints ) : oPoint = oFacet.Points(j) fp.write( " %d//%d " % ( oPoint.index + 1, iNormal ) ) iNormal = iNormal + 1 fp.write( "\n" ) #}}} # re-order UVs after copying. # thanks to Frederic Valluer for this. # def FixUVs( oObj ) : oClusters = oObj.ActivePrimitive.geometry.clusters; # Look for our sample cluster for i in range( oClusters.count ) : oCls = oClusters(i) if ( oCls.type == constants.siSampledPointCluster ): oCluster = oCls break # the UVspace we're interested in will be the # most recently added, the last in the list. # As long as you keep your fingers crossed. # oUVspace = oCluster.LocalProperties(oCluster.LocalProperties.count-1) aSamples = [] aFixed = [] # build the lut oFacets = oObj.ActivePrimitive.Geometry.Facets for oFacet in oFacets : for oSample in oFacet.Samples : aSamples.append( oSample.Index ) aElements = oUVspace.Elements.Array aIndices = oCluster.Elements.Array aFixed.append( [] ) aFixed.append( [] ) aFixed.append( [] ) ## there HAS to be a better way to do this, surely? for i in range( len( aIndices ) ): aFixed[0].append( 1 ) aFixed[1].append( 1 ) aFixed[2].append( 1 ) # and rebuild the uvs for i in range( len( aIndices ) ): idx = aSamples[aIndices[i]] aFixed[0][idx] = aElements[0][i] aFixed[1][idx] = aElements[1][i] aFixed[2][idx] = aElements[2][i] oUVspace.Elements.Array = aFixed