Script adapted: Poser import for morph targets

Hi,
since about 2 months, I’m using Poser as well.

With the presupplied blender 2.42a wavefront obj import, the vertex ordering might change,
which is kills the ability to use blender to create custom morphs for Poser.
(The reordering was done for some performance optimizations, I think, and don’t hurt
for most applications.)

In some previous post, cambo pointed to some script in a wikipedia page,
which performed a very simple .obj import (you don’t need materials or normals
for morph targets), without reordering, suitable for morph target import / export.
This script was based on NMesh and did one object at a time.

I extended the script to import multiple objects (so you can export the
whole figure from Poser in one single step and import it into blender).
A first test was successful, but sloooow. According to API docs, I converted
it from NMesh to Mesh then and got a speedup of 10.

It is not yet extensively tested, but imports the Poser 7 Sydney figure (100 K vertices)
in around 5 seconds now.

I attach it in case you’re interested. Maybe if I find some time, I add a nice wrapper to it
so that it shows up in the import menu. Currently it must be started from a text window.

Best regards,
Michael


import Blender

def import_obj(path):
	Blender.Window.WaitCursor(1)

	# parse the file
	file = open(path, 'r')

	scn = Blender.Scene.GetCurrent()
	for o in scn.getChildren():
		o.sel = 0

	name = 'dummy'
	object_pending = 0
	verts_in_object = 0
	linenr = 0
	v_off = 0
	facelist = []
	vertexlist = []

	for line in file.readlines():
		linenr = linenr + 1
		words = line.split()
		if len(words) == 0 or words[0].startswith('#'):
			pass
		elif words[0] == 'g' or words[0] == 'o':
			# new group or object (we treat it the same)
			print 'new group in line %i' % linenr
			object_pending = 1
			name = words[1];
			
		elif words[0] == 'v':
			if object_pending == 1:
				# save the previous object
				mesh.verts.extend(vertexlist)
				mesh.faces.extend(facelist)
				ob = Blender.Object.New('Mesh', name)
				ob.link(mesh)
				scn.link(ob)
				ob.sel= 1
				ob.Layers = scn.Layers
				v_off += verts_in_object
				verts_in_object = 0
				object_pending = 0
				facelist = []
				vertexlist = []

			# is this vertex the first one in the object?
			if verts_in_object == 0:
				# create a new Mesh
				mesh = Blender.Mesh.New()
				print 'creating a new mesh in line %i' % linenr
				
			vertexlist.append([float(words[1]), float(words[2]), float(words[3])])
			verts_in_object = verts_in_object + 1

		elif words[0] == 'f':
			faceVertList = []
			for faceIdx in words[1:]:
				# Poser uses vertex / texture vertex / normal vertex
				faceIdx = faceIdx.split('/')[0]
				faceVertList.append(int(faceIdx)-1 - v_off)
			facelist.append(faceVertList)
	
	if object_pending == 1:
		# save the previous object
		mesh.verts.extend(vertexlist)
		mesh.faces.extend(facelist)
		ob = Blender.Object.New('Mesh', name)
		ob.link(mesh)
		scn.link(ob)
		ob.sel= 1
		ob.Layers = scn.Layers

	Blender.Window.WaitCursor(0)
	Blender.Window.RedrawAll()

Blender.Window.FileSelector(import_obj, 'Import')

P.S.: The corresponding (unmodified) export script (for the selected object only)
attached for your convenience as well:


import Blender

def write_obj(filepath):
        out = file(filepath, 'w')
        scn = Blender.Scene.GetCurrent()
        object = scn.getActiveObject()
        mesh = object.getData()
        
        for vert in mesh.verts:
                out.write( 'v %f %f %f
' % (vert.co.x, vert.co.y, vert.co.z) )
        
        for face in mesh.faces:
                out.write('f')
                
                for vert in face.v:
                        out.write( ' %i' % (vert.index + 1) )
                out.write('
')
        out.close()

Blender.Window.FileSelector(write_obj, "Export")

Does it deals with the scale difference between poser and blender? will be cool to have the script auto fix the rotX -90 thing and also change size, lets say 10 times (you can zoom in yeah, but clipping makes it annoying.
Of course that would need a counterpart to undone all this on exporting.
Testing…

Mariano, a quick fix for clipping (which you probably already know) is to use the Orthogrphic camera. That fixed a lot of headaches when I found that out. X10 is a good number though, you’re right.

Michael314, I suppose it would be difficult to drop the ordered verts code into the existing OBJ script? Would be nice to have everything in one place.

What would be really great is the ability to import a Poser character rigged and ready to roll with morph targets in place.:smiley: Who knows. Maybe one of these days.

@Marty_D

Yeah I know there are workarounds… it was just a matter of “elegance” or something. When you export, lets say a head, to add a new expression morph, it comes up into blender as a tiny little invisible stone… lost somewhere in the grid.

Will be nice to have that head rotated and centered with a scale that look natural on Blender’s grid size (it can be change I know but the standard grid size).

About the poser import… yeah thay would be great… I think poser4 is cheap enought and it is truly great to do Cinematic Previs for Movies… I have done Matrix like fights test so easily… Its like playing with real-life action figures. Thou I agree that the keyframing tools are way too limited.

@Michael314

If you are short of time… would you mind I add this features to the scripts? and some wrapper for the menus like “Import Poser Morph Target” (and an export on of course)

Hi,
if you like, please go ahead and improve the script! I might not do it in the next two weeks, as I’m travelling from Saturday onwards.

Best regards,
Michael

Btw, in DAZ, you can define the scaling upon export. In Poser it’s fixed.
Above script currently does not do the 90 degree rotation.

Great thanks… Ill post 'em here when done.

In Importer replace this line:


vertexlist.append([float(words[1]), float(words[2]), float(words[3])])

with this one:


vertexlist.append([float(words[1])*10, -float(words[3])*10, float(words[2])*10])

That will rotate mesh for -90 degrees and resize for 10 times

In Exporter replace this line:


out.write( 'v %f %f %f
' % (vert.co.x, vert.co.y, vert.co.z) )

with this one:


out.write( 'v %f %f %f
' % (vert.co.x/10, vert.co.z/10, -vert.co.y/10) )

This will be the first import/export script made by a whole Team of developers :smiley:
Seriously now … Thanks for the code :slight_smile:

I have also adjusted that wiki script for my Importer/Exporter. But I’m working on something else, a real morph importer. I want to be able to import morphs from pz2 or cr2 file (they are almost the same thing) But the main problem is how poser handles figures and morphs. I have studied that and I have learned not only that poser reorders groups in the process of figure creation (loading) but it also reorders vertices and that’s a real hard nut to crack. Poser is actually adding duplicated vertices in place of seems that are connecting actors. I believe that Poser needs them because of the morphing system it uses. Morphs are actually only a differences, every delta is only a vector that must be added to corresponding vertex. And if some morph spreads on more connected actors, I think that those duplicates are used to track down already moved vertices. But I don’t know how to filter them out to get correct vertex order. And that drives me crazy :frowning: