OBJ flow exporter

This tool allows to export and import a mesh as a flow of OBJ files, driven by an expression, written in python.
It is based on the standard Maya exporter plugin.

Watch the preview here:

It is a bit raw, but it does its work pretty well. We used it in production for managing a metaball lava flow and it worked very fast.

Installation

Copy the .py file into your Maya scripts folder;

Import the script;

Call these functions:

displayExportUI(): for the exporting tool;

displayImportUI(): for the importing tool.

After the job is done, you can tweak the animation of your OBJ flow just by editing the expression created by the script.

You can download it here: bakeToObj.py.zip

Feel free to edit and improve the code and I’d like to keep updated for any improvement.

Please note that once you import an obj flow, you have effectively separate meshes, that means you can’t for example use motion blur, since at each frame the visible mesh changes.

Have fun!

Function createFollicle()

This function I wrote in python is very useful to create Follicles on surfaces and polygons, without the need of creating Hairs and then deleting the Hair System and the curves just to keep the follicles.
Plus, of course, is a good chance to code something and to have a function you can use in your scripts.

How does it works..

The function requires as parameters a list of 3 elements, named pos for a point position in 3d space and a nurbs surface OR a poly surface.

The follicle is located on the polygon/surface by finding the closest point on it from the given position.

I have included a second function, named createFollicles(), where you can pass an array of 3d points instead just one point. It will create multiple follicles.

Here’s a quick example..

I create a torus polygon, select some vertices, get their positions and create follicles on the vertices..


torus = cmds.polyTorus()
cmds.select(torus[0]+".vtx[10]", torus[0]+".vtx[20]", torus[0]+".vtx[30]")
sel = cmds.ls(sl=1, fl=1)

for obj in sel:
	pos = cmds.pointPosition(obj, w=1)
	follicle = createFollicle(pos, poly_surface = torus[0])
	print follicle

Here’s the function code:

import maya.cmds as cmds
import maya.OpenMaya as OpenMaya

def createFollicle (pos=[0, 0, 0], nurbs_surface=None, poly_surface=None):
	
	if (nurbs_surface==None and poly_surface==None):
		OpenMaya.displayError("Function createFollicle() needs a nurbs surface or poly surface")
		return

	transform_node = cmds.createNode("transform")
	cmds.setAttr((transform_node +".tx"), pos[0])
	cmds.setAttr((transform_node +".ty"), pos[1])
	cmds.setAttr((transform_node +".tz"), pos[2])
	
	#make vector product nodes to get correct rotation of the transform node
	vector_product = cmds.createNode("vectorProduct")
	cmds.setAttr((vector_product+".operation"), 4)
	cmds.connectAttr( (transform_node+".worldMatrix"), (vector_product+".matrix"), f=1)
	cmds.connectAttr( (transform_node+".rotatePivot"), (vector_product+".input1"), f=1)

	#connect the correct position to a closest point on surface node created
	if nurbs_surface:
		closest_position = cmds.createNode("closestPointOnSurface", n=(transform_node+"_CPOS"))
		cmds.connectAttr( (nurbs_surface+".ws"), (closest_position+".is"), f=1)
		cmds.connectAttr( (vector_product+".output"), (closest_position+".inPosition"), f=1)
	
	if poly_surface:
		closest_position = cmds.createNode("closestPointOnMesh", n=(transform_node+"_CPOS"))
		cmds.connectAttr( (poly_surface+".outMesh"), (closest_position+".im"), f=1)
		cmds.connectAttr( (vector_product+".output"), (closest_position+".inPosition"), f=1)

	#create a follicle node and connect it
	follicle_transform = cmds.createNode("transform", n=(transform_node+"follicle"))
	follicle = cmds.createNode("follicle", n=(transform_node+"follicleShape"), p=follicle_transform)
	cmds.connectAttr((follicle+".outTranslate"), (follicle_transform+".translate"), f=1)
	cmds.connectAttr((follicle+".outRotate"), (follicle_transform+".rotate"), f=1)
	if nurbs_surface:
		cmds.connectAttr((nurbs_surface+".local"), (follicle+".is"), f=1)
		cmds.connectAttr((nurbs_surface+".worldMatrix[0]"), (follicle+".inputWorldMatrix"), f=1)
	if poly_surface:
		cmds.connectAttr((poly_surface+".outMesh"), (follicle+".inm"), f=1)
		cmds.connectAttr((poly_surface+".worldMatrix[0]"), (follicle+".inputWorldMatrix"), f=1)

	cmds.setAttr((follicle+".parameterU"), cmds.getAttr (closest_position+".parameterU"))
	cmds.setAttr((follicle+".parameterV"), cmds.getAttr (closest_position+".parameterV"))

	#return strings
	cmds.delete(transform_node)
	return [follicle_transform, follicle, closest_position]
	
def createFollicles  (follicle_positions=[[0,0,0]], nurbs_surface=None, poly_surface=None):

	out_follicles=list()

	if (nurbs_surface==None and poly_surface==None):
		OpenMaya.displayError("Function createFollicles() needs a nurbs surface or poly surface")
		return
	 	
	for pos in follicle_positions:
		lst = createFollicle(pos, nurbs_surface, poly_surface)
		out_follicles.append(lst)
	return out_follicles

Function resolveName()

When you call this function, it gives you back a unique name in the Maya scene. Very useful when creating objects programmatically and you want to keep unique names in your scene (Essential when dealing with maya scripts).

If the string passed as parameter is not assigned yet, then the function returns the string itself, else it will return the string plus “_#”.

Python code:


def resolveName(n):
	name = n
	if cmds.objExists(n):
		i = 1
		while cmds.objExists(str(n) + "_" + str(i)):
			i+=1
		name = str(n) + "_" + str(i)
		print "Warning: Object named " + n + " already exists. Used " + name + " instead."
	return name

MEL code:

proc string resolveName(string $n){
	string $name = $n;
	if (`objExists $n`){
		int $i = 1;
		while (`objExists ($n + "_" + string($i))`){
			$i+=1;
		}
		$name = $n + "_" + string($i);
		print ("Warning: Object named " + $n + " already exists. Used " + $name + " instead.");
	}
	return $name;
}

Examples:

Suppose you want to create a series of objects using the same name. By calling the function before the name you don’t have to worry about naming conflicts:


import maya.cmds as cmds

num_spheres = 10
sphere_list = list()

for i in range(numspheres):
     new_sphere = cmds.sphere(n=resolveName("mySphere"))
     sphere_list.append(new_sphere)

This will create the spheres mySphere, mySphere_1, mySphere_2 and so on.
If you recall the function again, the numbering will start from the first available name.