Can / How do I export SubD morph targets?



  • Hi,

    @Biscuits has said that some of the morphs for PE are made in subd L2. Is this ability only available via GoZ to ZBrush as I can't seem to export a body part if it is subdivided to L2 (or L1 for that matter) via export obj the file ends up being 0 bytes.


  • Poser Ambassadors

    Yes, I make morphs with Zbrush.

    Making HD Morphs:

    • Set the SubD Level of your figure to 1 or 2.

    If the option is greyed out...

    • Select your Figure

    • Menu > Figure > Skinning Method > Poser Unimesh

    • Select your figure or prop and set the Subdivision Levels to 1

    • Scale the figure to 300% (so in Zbrush your brushsize ratio is better).

    • GOZ and make your morph...choose the bake option.

    • GOZ back.

    • Set the scale back.

    Good chance this doesn’t work without GOZ on partial body parts though.



  • @Biscuits That's what I thought / feared :(

    There is no way I can buy ZBrush, at $800 for a single license it is just too expensive for a hobby. Interesting what you say about scaling though. Whenever I work on a prop or morph in Blender I scale the model 100x (remembering to apply loc rot and scale afterwards), otherwise things like shrink / fatten just go far too fast / far. I have my export options saved with a scale of 0.01 (or they come in to Poser VERY big :)

    It's these sort of things that really wind me up about Poser, if you can export to ZBrush, why not to obj so us paupers can play too


  • Poser Ambassadors

    Did you try exporting the obj and then importing in your regular modeller, subd there and export the whole fig.
    Then try to load on the subd figure in poser.

    Just a guess.

    If it doesn’t work as a single bodypart, it might work as a FBM, even if you only change the lips.

    Or start to subd in poser for the complete figure, export that out as an obj and import in your modeller...and morph from there.


  • Poser Ambassadors

    @amethystpendant

    Ok, 2 ways to get there.

    a) Make your morph on the un-subd figure in Blender. => (One always has a rough idea how the "final" should look, right? So do the "rough" work in Blender.
    Load your morph target as you usually do and set to 1.000. (This procedure ONLY works properly with a morph value set to 1.000)
    Set you figure to the Subd level required.
    Click on the little arrow behind your morph dial and select "Edit".
    Now fine tune your morph using the morph brush at the required SubD level.

    b) Project the SubD morph.

    I explain this "projection procedure" in my second video on YouTube when I talk about the HD morphs.
    The correct export and import procedures for morph creations between poser and Blender](

    )

    [And here the video about the HD morph projection procedure] (

    )

    First part of this second video is a FBM and the second part from 5min30sec onwards is about the HD morph projection procedure.
    Best regards, Tony



  • Thanks all. I'll definitely try and export the full body. I really don't get along with the morph brush so I doubt I'll try your approach Tony, but at least other people will know it's an option


  • Poser Ambassadors

    @amethystpendant
    NEVER export a full body.
    Poser will break the mesh down into unwelded vertex groups. => Video 1 and 4.
    Always load the original mesh in Blender as per Video 1.



  • @vilters Yes I know, but the original obj is not sub divided and the chances of the subdivision creating exactly the same verts in Blender is unlikely, guess I'll just have be jealous of those with ZBrush :(



  • @vilters Tony, it seems to me as though, given a Python script which can both parse and create Wavefront OBJ files (done), it ought to be possible (unless the original mesh is degenerate, with duplicated or undifferentiable vertices) to derive a mapping from mesh vertex index to body part delta index, and vice versa, with the understanding that at body part weld seams there will be, at most, two separate body part vertices which map to the same original mesh vertex.

    I have yet to delve into whether the subdivided mesh vertices are accessible to Poser Python, or exactly how to derive which subd-generated vertices reside on weld boundaries



  • @anomalaus said in Can / How do I export SubD morph targets?:

    @vilters Tony, it seems to me as though, given a Python script which can both parse and create Wavefront OBJ files (done), it ought to be possible (unless the original mesh is degenerate, with duplicated or undifferentiable vertices) to derive a mapping from mesh vertex index to body part delta index, and vice versa, with the understanding that at body part weld seams there will be, at most, two separate body part vertices which map to the same original mesh vertex.

    I have yet to delve into whether the subdivided mesh vertices are accessible to Poser Python, or exactly how to derive which subd-generated vertices reside on weld boundaries

    My guess would be that Subd is not accessible to Poser Python...


  • Poser Ambassadors

    My tests are from awhile ago but :

    • Poser unwelds its vertex groups at obj file loading (right from the load/start).

    • Blender does NOT unweld.

    • Poser doubles ALL vertex at ALL welds and this includes the SubD vertex.

    • Blender just does the SubD without vertex doubling.

    • Poser AND Blender use the same SubD.
      If you "forget" the double vertex Poser creates, both apps do exactly the same.

    That is why the HD Morph projection that I demo in Video 2 and 3 works as good as it does. => "Forgetting" the doubles for a moment, all other SubD vertex are in the same positions in Poser and Blender.

    The HD morph transfer / projection that I demo is "One to One".



  • @anomalaus @amethystpendant I did not test it, but actor.SubdivGeometry() is on page 129 of PoserPython Methods.
    Get the subdivided geometry for the actor. The returned geometry object
    can then be queried for vertex, set, or polygon information.



  • @phdubrov Ooh now that sounds interesting maybe I can write my own export script.



  • @anomalaus I don't suppose you already have code available that creates an obj file do you? If so any chance of sharing (that wasn't what I meant by writing my own, i.e. ask someone to do it. I've got the verts and the faces I just haven't used python to write to a file before.) And I'm not sure what sections need outputting as I don't think I need the tex verts as I will only be morphing the re-exporting, but the material names night be useful (for say hiding the lashes)



  • Okay so far I have this, probably not very elegant but I can import the obj fle into blender

    	import poser
    	scene=poser.Scene()
    	act=scene.CurrentActor()
    	geom=act.Geometry()
    	verts=geom.Vertices()
    	polys=geom.Polygons()
    	fname="E:\\Poser Pro Content\\" +act.Name()+".obj"
    	outfile=open(fname,"w")
    	for x in range(geom.NumVertices()):
    		v=verts[x]
    		outfile.write( "v "+str(v.X())+" "+str(v.Y())+" "+str(v.Z())+"\n")          
    		matname=""
    	for x in range(geom.NumPolygons()):
    		f=polys[x]
    		s=f.Start()
    		i=0
    		pstuff=[]
    		outstr="f "
    		if f.MaterialName() != matname:
    			outfile.write( "usemtl "+f.MaterialName()+"\n")
    			matname = f.MaterialName()
    		while i < f.NumVertices():
    			outstr=outstr+str(geom.Sets()[s + i]+1)+" "
    			i = i + 1
    		outfile.write(outstr+"\n")
    	outfile.close()
    

    Have I missed anything, is there anything I could improve (other than the no-no of hard coding the path) before I look at the SubD version?

    [Edit] Except the SubdivGeometry only works on Props / figures!!!!!



  • @amethystpendant here's a relevant extract of what I developed while exploring the possibility of natively importing DSON files into Poser without resorting to the unsupported DSON Importer. I have not tested this extract directly, but it worked in situ in another python script. Let me know if you run into any undefined constants (I found the most obvious ones and added their initialisation at the top)

    # Extract of code to create a grouped object in the scene, including creation and loading of a wavefront obj file
    # to bypass Poser Pro 11.1.0.34764 currently failing to support Python API creation of new polygon groups.
    # Original code (c) 2017 Geoff Hicks (an0malaus/GeoffIX/gwhicks) from LoadDSON.py
    # Extract released 2018-04-08 for public use
    
    debug = False
    verbose = False
    DSScaleFactor = 0.00381493 # 1 cm = 0.00381493 PNU, where 1 PNU = 103.2 inches
    objExt = 'obj'
    mtlExt = 'mtl'
    
    import poser
    from numpy import oldnumeric
    
    scene = poser.Scene()
    
    def CreateGroupedObject( scene, objName, vertices, polygons, \
    											uvs=[], normals=[], groups=[], materials=[], polyVertUVs=[], remove=True):
    	"""
    	Given the geometry mesh, polygon groups and materials, create an object in the Poser scene.
    	If there are no groups, the object can be created directly in Poser Pro 11.0.7.33999, however, that version of 
    	Python API does not allow creation of new vertex groups, so specification of which polygon vertices belong to which 
    	vertex groups cannot occur, so when more than a single group exists, a Wavefront OBJ file containing the grouping 
    	information must be written to a temporary location and imported into the scene, or, alternatively, written as 
    	custom geometry in a prop or figure file and loaded from the library.
    	NOTE: Added collection of weld groups, and group vertex sets.
    	NOTE: Wavefront OBJ format does not support spaces in filenames.
    	
    	scene		: The poser scene instance
    	objName		: The internal name of the geometry object to be created in the scene.
    	vertices	: A list of lists of 3 floats, giving the x, y and z coordinates of the mesh vertices.
    	polygons	: A list of lists of 5 or 6 ints: groupIdx, matGroupIdx, vertIdx1, vertIdx2, vertIdx3, [ vertIdx4 ]
    	uvs			: An optional list of lists of 2 floats, giving the u and v coordinates of the texture vertices.
    	normals		: An optional list of lists of 3 floats, giving the i, j and k normal vectors of the mesh vertices.
    	groups		: An optional list of group names representing body parts
    	materials	: An optional list of material names
    	polyVertUVs	: An optional list of lists of 3 ints: [ polygon index, vertex index, UV index ]. Defines vertices 
    				: which have different UVs in adjacent polygon facets.
    	remove		: An optional boolean (defaults to True) indicating whether to remove the obj and mtl files after the
    				: prop they define is loaded into the Poser scene. (The obj is currently excluded from removal)
    	Returns		: The newly created prop actor, and welds: a group keyed dict of lists of weld target groups
    	"""
    	global DSScaleFactor, objExt, mtlExt
    	
    	numVerts = len( vertices )
    	numTexVerts = len( uvs )
    	numNormals = len( normals )
    	numFacets = len( polygons )
    	numGroups = len( groups )
    	numMaterials = len( materials )
    	numRegions = 0 # WTH?
    	# Create a dict to lookup polygon vertex UVindices for vertices which have a different UV in different polygons
    	polyVertUV = {}
    	for pvu in polyVertUVs: # Each entry consists of [ polygon index, vertex index, UV index ]
    		polyVertUV[ tuple( pvu[ 0:2 ] ) ] = pvu[ 2 ] # Lookup with polyVertUV[ pIdx, vIdx ]
    	if numGroups > 0: # Create and import a wavefront OBJ file and material library file from temporary storage.
    		# Determine the object and material file paths in the temporary location
    		tmpDir = poser.TempLocation()
    		objFile = os.path.join( tmpDir, '.'.join( ( urllib.pathname2url( objName ), objExt ) ) )
    		mtlFileName = '.'.join( ( urllib.pathname2url( objName ), mtlExt ) )
    		mtlFile = os.path.join( tmpDir, mtlFileName )
    		
    		# Save the Wavefront OBJ file
    		with open( objFile, mode='wt' ) as pFile:
    			# Write the file header comment
    			pFile.write( '# File\t{}\n'.format( objFile ) )
    			pFile.write( '# File created by LoadDSON.py\n' )
    			pFile.write( '# NumVerts/NumTVerts/NumVNormals/NumFacets\t{}/{}/{}/{}\n'.format( numVerts, numTexVerts, numNormals, numFacets ) )
    			pFile.write( '# NumGroups/NumMaterials/NumRegions\t{}/{}/{}\n'.format( numGroups, numMaterials, numRegions ) )
    			#pFile.write( '# x/y/colour/ppu\t{}/{}/{}/{}\n'.format( uPixels, vPixels, colourIndex, pixelsPerUnit ) )
    			pFile.write( '\n' )
    		
    			# Refer to the material library file
    			pFile.write( 'mtllib {}\n'.format( mtlFileName ) )
    			pFile.write( '\n' )
    			
    			vGroups = [] # List of sets. Each per-vertex list entry is a set of the group indices that vertex is in
    			weldGroups = set() # Set of frozensets. Each frozenset contains a unique pair of group indices to be welded
    			# Save the vertices (xyz)
    			for v in xrange( numVerts ):
    				pFile.write( 'v {:11.9f} {:11.9f} {:11.9f}\n'.format( vertices[ v ][ 0 ] * DSScaleFactor, \
    											vertices[ v ][ 1 ] * DSScaleFactor, vertices[ v ][ 2 ] * DSScaleFactor ) )
    				vGroups.append( set() ) # Don't know vertex groups yet
    			pFile.write( '\n' )
    		
    			# Save the texture vertices (uv), if any
    			if numTexVerts > 0:
    				for vt in xrange( numTexVerts ):
    					# NOTE: The first UV parameter in DSON files appears to allow an integer component greater than 1.0, this may
    					# indicate that the UV coordinates are indexing a different UV map image, indexed by the integer portion of the
    					# U coordinate.
    					pFile.write( 'vt {:11.9f} {:11.9f}\n'.format( uvs[ vt ][ 0 ], uvs[ vt ][ 1 ] ) )
    				pFile.write( '\n' )
    			# Save the vertex normals, if any
    			if numNormals > 0:
    				for vn in xrange( numNormals ):
    					pFile.write( 'vn {:11.9f} {:11.9f} {:11.9f}\n'.format( normals[ vn ][ 0 ], normals[ vn ][ 1 ], normals[ vn ][ 2 ] ) )
    				pFile.write( '\n' )
    			
    			lastMtl = None
    			lastGroups = set() # Empty set for speedy comparison of lists of groups
    			for f in xrange( numFacets ):
    				# Specify which groups the subsequent facets belong to
    				#fGroups = set( GetPolyGroups( polygons, f ) ) # Cast the list of group names to a set
    				fGroups = set( [ polygons[ f ][ 0 ] ] ) # Cast the group index to a set (DSON encodes only one group per facet )
    				if fGroups != lastGroups:
    					groupNames = ''
    					for group in fGroups:
    						try:
    							groupNames += ' {}'.format( groups[ group ] )
    						except KeyError as ex:
    							print 'KeyError group {} not in groups {}'.format( group, groups )
    							raise KeyError
    					pFile.write( 'g{}\n'.format( groupNames ) )
    					lastGroups = fGroups
    				# Specify which material the subsequent facets will use
    				#mtl = GetPolyMatGroup( polygons, f )
    				mtl = polygons[ f ][ 1 ]
    				if mtl != lastMtl:
    					pFile.write( 'usemtl {}\n'.format( materials[ mtl ] ) )
    					lastMtl = mtl
    				# List the facets and their vertices [ / [ uvs ] [ / normals ] ] ...
    				poly = polygons[ f ][ 2: ] # Discard group and matGroup indices
    				fVerts = ''
    				for index in xrange( len( poly ) ):
    					v = poly[ index ]
    					for group in fGroups:
    						vGroups[ v ].add( group ) # Record the groups this vertex belongs to
    						if len( vGroups[ v ] ) > 1: # This vertex in multiple groups, so add those groups to weldGroups
    							weldGroups.add( frozenset( vGroups[ v ] ) ) # Assuming length of vGroups[v] never > 2
    							# otherwise we should add every possible combination of 2 groups n C 2.
    					if numTexVerts > 0:
    						if ( f, v ) in polyVertUV: # Alternate UV for this vertex in this facet
    							vt = polyVertUV[ f, v ]
    						else: # Default UV for this vertex in this facet
    							vt = v
    					if numNormals > 0:
    						vn = 0 # However the hell DSON specifies vertex normal indices TO DO
    						if numTexVerts > 0:
    							# f v1/vt1/vn1 v2/vt2/vn2 v3/vt3/vn3 ...
    							fVerts += ' {}/{}/{}'.format( v + 1, vt + 1, vn + 1 )
    						else:
    							# f v1//vn1 v2//vn2 v3//vn3 ...
    							fVerts += ' {}//{}'.format( v + 1, vn + 1 )
    					else: # No normals
    						if numTexVerts > 0:
    							# f v1/vt1 v2/vt2 v3/vt3 ...
    							fVerts += ' {}/{}'.format( v + 1, vt + 1 )
    						else: # No uvs or normals
    							# f v1 v2 v3 ...
    							fVerts += ' {}'.format( v + 1 )
    				pFile.write( 'f{}\n'.format( fVerts ) )
    		
    		# Convert weldGroups to weldList, which is a per-group list of sets of weld targets
    		welds = set()
    		print '{} welds:'.format( len( weldGroups ) )
    		for weld in weldGroups:
    			items = list( weld )
    			welds.add( frozenset( [ groups[ items[ 0 ] ], groups[ items[ 1 ] ] ] ) ) # Convert indices to group names
    			if debug or verbose:
    				print  '	weld    {}:1  \n			 {}:1'.format( groups[ items[ 0 ] ], groups[ items[ 1 ] ] )
    		# Save the material library file NOTE: This is a dummy, the textures will be applied after loading.
    		with open( mtlFile, mode='wt' ) as pFile:
    			# Material name. Can be any length but cannot contain spaces. Underscores are allowed.
    			pFile.write( 'newmtl {}\n'.format( 'Preview' ) )
    			# 
    			pFile.write( 'Ns {}\n'.format( '50' ) )
    			pFile.write( 'Ka {}\n'.format( '0 0 0' ) )
    			pFile.write( 'Kd {}\n'.format( '1 1 1' ) )
    			pFile.write( 'Ks {}\n'.format( '0.0489127 0.0754532 0.142184' ) )
    			pFile.write( 'map_Kd {}\n'.format( 'reusage-texture-metal-steel-plate-a-little-dirty-and-without-rivets.png' ) )
    			pFile.write( 'd {}\n'.format( '1' ) )
    		# Import the Wavefront Object to the Poser scene
    		imEx = scene.ImExporter()
    		imOpt = imEx.ImportOptions( objExt, None )
    		try: # Overcome bugs in Poser Pro 11.0.7.33999 Python API constants
    			imOpt[ poser.kImOptCodeCENTERED ] = 0 # This is Centred (9001)
    			imOpt[ poser.kImOptCodePLACEONFLOOR ] = 0 # This is Place on floor (9002)
    			imOpt[ poser.kImOptCodePERCENTFIGSIZE ] = 0#100.0 # Use 0 to ignore scaling
    			imOpt[ poser.kImOptCodeSCALEABSOLUTE ] = 0.0 # This is offset X (9004)
    			imOpt[ poser.kImOptCodeOFFSETX ] = 0.0 # This is offset Y (9004)
    			imOpt[ poser.kImOptCodeOFFSETY ] = 0.0 # This is offset Z (9004)
    			imOpt[ poser.kImOptCodeOFFSETZ ] = 0 # This is Weld (9005)
    			imOpt[ poser.kImOptCodeWELDIDENTICALVERTS ] = 0 # This is Normals Consistent (9006)
    			imOpt[ poser.kImOptCodeMAKEPOLYNORMSCONSISTENT ] = 0 # This is Flip Normals (9007)
    			imOpt[ poser.kImOptCodeFLIPNORMS ] = 0 # This is Flip U (9008)
    			imOpt[ poser.kImOptCodeFLIPUTEXTCOORDS ] = 0 # This is Flip V (9009)
    			imOpt[ poser.kImOptCodeFLIPVTEXTCOORDS ] = 0 # This does nothing
    		except AttributeError: # Poser Pro 2014 has no kImOptCodeSCALEABSOLUTE attribute and no API constant bug
    			imOpt[ poser.kImOptCodeCENTERED ] = 0
    			imOpt[ poser.kImOptCodePLACEONFLOOR ] = 0
    			imOpt[ poser.kImOptCodePERCENTFIGSIZE ] = 0 # Use 0 to ignore scaling
    			imOpt[ poser.kImOptCodeOFFSETX ] = 0.0
    			imOpt[ poser.kImOptCodeOFFSETY ] = 0.0
    			imOpt[ poser.kImOptCodeOFFSETZ ] = 0.0
    			imOpt[ poser.kImOptCodeWELDIDENTICALVERTS ] = 0
    			imOpt[ poser.kImOptCodeMAKEPOLYNORMSCONSISTENT ] = 0
    			imOpt[ poser.kImOptCodeFLIPNORMS ] = 0
    			imOpt[ poser.kImOptCodeFLIPUTEXTCOORDS ] = 0
    			imOpt[ poser.kImOptCodeFLIPVTEXTCOORDS ] = 0
    		if debug or verbose:
    			print 'Import Options', imOpt
    		imEx.Import( objExt, None, objFile, imOpt )
    		newProp = scene.Actor( urllib.pathname2url( objName ) )
    		# Remove the temporary Wavefront OBJ and material library files
    		if remove: # We actually never want to remove the obj file as it has the correct grouping for the figure
    			#os.remove( objFile )
    			os.remove( mtlFile )
    		return newProp, welds
    	else: # We can currently create ungrouped geometries directly in Poser, so go ahead.
    		numSets = 0
    		numTSets = 0
    		numTris = 0
    		numQuads = 0
    		numOther = 0
    		for polyIndex in xrange( numFacets ): # What a painful thing to have to do, to calculate numSets!
    			polyVerts = len( polygons[ polyIndex ] ) - 2 # DUF polylist values poly vertex lists have 2 extra ints
    			numSets += polyVerts
    			numTSets += polyVerts
    			if polyVerts == 3:
    				numTris += 1
    			elif polyVerts == 4:
    				numQuads += 1
    			else:
    				numOther += 1
    		if debug:
    			print '{} polygons consist of {} tris, {} quads and {} others.'.format( numFacets, numTris, numQuads, numOther )
    			print 'Comprising {} sets.'.format( numSets )
    		#"""
    		meshVerts = oldnumeric.zeros( ( numVerts, 3 ), oldnumeric.Float )
    		meshSets = oldnumeric.zeros( numSets, oldnumeric.Int )
    		meshPolys = oldnumeric.zeros( ( numFacets, 2 ), oldnumeric.Int )
    		meshTVerts = oldnumeric.zeros( ( numTexVerts, 2 ), oldnumeric.Float )
    		meshTSets = oldnumeric.zeros( numTSets, oldnumeric.Int )
    		meshTPolys = oldnumeric.zeros( ( numFacets, 2 ), oldnumeric.Int )
    		meshgeom = poser.NewGeometry()
    		# Fill out vertex array
    		for vertIndex in xrange( numVerts ):
    			for axisIndex in xrange( 3 ): # 0, 1, 2 for x, y, z
    				meshVerts[ vertIndex ][ axisIndex ] = vertices[ vertIndex ][ axisIndex ] * DSScaleFactor # cm to PNU
    		# Fill out texture vertex array
    		for vertIndex in xrange( numTexVerts ):
    			for axisIndex in xrange( 2 ): # 0, 1 for u, v
    				meshTVerts[ vertIndex ][ axisIndex ] = uvs[ vertIndex ][ axisIndex ]
    		# Fill out poly, sets, tpoly and tsets arrays
    		setIndex = 0
    		for polyIndex in xrange( numFacets ):
    			polyLen = len( polygons[ polyIndex ] )
    			polyVerts = polyLen - 2 # DUF polylist vertex lists have 2 extra ints.
    			meshPolys[ polyIndex ][ 0 ] = setIndex # Start Index
    			meshPolys[ polyIndex ][ 1 ] = polyVerts # Length
    			meshTPolys[ polyIndex ][ 0 ] = setIndex # Start Index
    			meshTPolys[ polyIndex ][ 1 ] = polyVerts # Length
    			for sidx, vertIndex in zip( xrange( setIndex, setIndex + polyVerts ), xrange( 2, polyLen ) ):
    				meshSets[ sidx ] = polygons[ polyIndex ][ vertIndex ]
    				if ( polyIndex, vertIndex ) in polyVertUV: # Alternate UV for this vertex in this polygon
    					meshTSets[ sidx ] = polyVertUV[ polyIndex, vertIndex ]
    				else: # Default UV for this vertex in this polygon
    					meshTSets[ sidx ] = polygons[ polyIndex ][ vertIndex ]
    			setIndex += polyVerts
    		if debug:
    			print 'meshPolys[0] = {} ({})'.format( meshPolys[ 0 ], meshPolys.shape[0] )
    			print 'meshSets[{}:{}] = {} ({})'.format( meshPolys[ 0 ][ 0 ], meshPolys[ 0 ][ 1 ], meshSets[ meshPolys[ 0 ][ 0 ] : meshPolys[ 0 ][ 1 ] ], meshSets.shape[0] )
    			print 'meshVerts[{}] = {} ({})'.format( meshSets[ meshPolys[ 0 ][ 0 ] ], meshVerts[ meshSets[ meshPolys[ 0 ][ 0 ] ] ], meshVerts.shape[0] )
    			print meshSets[0], meshSets[1], meshSets[2]
    			if numTexVerts > 0:
    				print 'meshTPolys[0] = {} ({})'.format( meshTPolys[ 0 ], meshTPolys.shape[0] )
    				print 'meshTSets[{}:{}] = {} ({})'.format( meshTPolys[ 0 ][ 0 ], meshTPolys[ 0 ][ 1 ], meshTSets[ meshTPolys[ 0 ][ 0 ] : meshTPolys[ 0 ][ 1 ] ], meshTSets.shape[0] )
    				print 'meshVerts[{}] = {} ({})'.format( meshTSets[ meshTPolys[ 0 ][ 0 ] ], meshTVerts[ meshTSets[ meshTPolys[ 0 ][ 0 ] ] ], meshTVerts.shape[0] )
    				print meshTSets[0], meshTSets[1], meshTSets[2]
    		if numTexVerts > 0:
    			meshgeom.AddGeneralMesh( meshPolys, meshSets, meshVerts, meshTPolys, meshTSets, meshTVerts )
    		else:
    			meshgeom.AddGeneralMesh( meshPolys, meshSets, meshVerts )
    		if False:
    			meshgeom.Weld()
    		newProp = scene.CreatePropFromGeom( meshgeom, objName )
    		return newProp, None
    
    

    For the curious, "n C 2" represents "n Choose 2" from the Binomial Coefficient, where choice order is irrelevant. n should be subscripted and the 2 should be superscripted, but Markdown and Python don't support that.

    Hopefully your browser will let you copy the code from this post.

    [EDIT] I have previously posted in other threads about the discrepancy between the ImExporter's parameter constants and what the actual parameters do. To preclude discussion, the constant names are WRONG, and the #comments are what I discovered was actually parsed by the Python API.



  • @anomalaus Thank you, that should keep me more than busy for a while :) Though the fact that I still can't output, say just the head with subd is annoying



  • @amethystpendant I am trying to imagine a scenario where the inverse (-1) of a magnet deforming all vertices to the origin (base coordinates at the origin and scale at 100%, magnet scale set to exactly zero after overriding any minimum limit, and zone with a constant 100% falloff profile) would reveal the subd vertex coordinates as a spawned morph target, and thus expose them via parsing a subsequently saved character file. I have code which can parse a cr2 or pz3 file and (mostly) create the referenced figures with their morphs.

    I admit that I recently had a problem spawning a morph on a prop I had forgotten was already subdivided, so it requires some care to ensure that there are absolutely no inherited transforms applied to the figure.



  • @anomalaus Actually it looks like you can export the subd if the actor in question is subd'd separately to the figure. Will let you know how I get on


Log in to reply
 

Looks like your connection to Graphics Forum was lost, please wait while we try to reconnect.