Delta mush in Splice

Because all the cool kids are into it, here’s an (early, unoptimised, naive, fairly gnarly) implementation of Delta Mush in Fabric Splice. It’s a regular splice node, not a deformer (still sorting that out).

deltaMush
It takes you from the world’s worst skinning job (on the left), to something that is slightly less awful (on the right).

Tangent space matrix code from Roy Nieterau, relax operator from Phil Taylor.

Update: 9/9/2014 – smoothPos now weights points by distance – end effect is you need fewer smooth iterations.
Update 11/9/2014 – Deltas are now simple vectors. Smooth function now better and faster thanks to exceedingly kind help from Roy Nieterau

require Math;
require Geometry;


operator smoothPos<<<index>>>(Vec3 inPositions[], io Vec3 outPositions[], PolygonMesh mesh) {
 
  Vec3 result = inPositions[ index ];
 
  LocalL16UInt32Array surroundingPoints;
  mesh.getPointSurroundingPoints( index, false, surroundingPoints );
  UInt32 nbNei = surroundingPoints.size();
 
  if( nbNei ) {
 
    Scalar neiSumDistance = 0;
    Vec3 delta;
   
    Scalar neiDistance;
    Vec3 toNeighbour;
    for( UInt32 i = 0; i < nbNei; ++i ) {
      UInt32 neiPt = surroundingPoints.get(i);
     
      toNeighbour = inPositions[ neiPt ] - result; // vector to neighbour
      neiDistance = toNeighbour.length();
     
      delta += toNeighbour * neiDistance; // weight by distance
      neiSumDistance += neiDistance;
    }
   
    delta /= (neiSumDistance * nbNei); // divide by sum of all neighbour distances (weighted) and amount of neighbours (average: mean)
    result += delta;
  }
 
  outPositions[index] = result;
}



operator relax_operator(in Scalar factor, in Integer iterations, io PolygonMesh mesh0) {

  GeometryAttributes attributes = mesh0.getAttributes();
  Ref<Vec3Attribute> positionsAttr = attributes.getPositions();
  Vec3 positions1[] = positionsAttr.values;
  Vec3 positions2[] = positions1.clone();

  for(Index i = 0; i < iterations; i++){
    smoothPos<<<mesh0.pointCount()>>>(positions1, positions2, mesh0);
    if(i < iterations-1){
      swap(positions1, positions2);
      positionsAttr.values = positions1;
    }
  }
  positionsAttr.values = positions2;
  //positionsAttr.incrementVersion();
  
  mesh0.recomputePointNormals();
  Ref<Vec3Attribute> normalsAttr = mesh0.getNormals();
  normalsAttr.incrementVersion();

  
  mesh0.recomputeTangents();

}


function Mat44 tangentSpaceMatrix(PolygonMesh mesh, Index point)
{
  //Roy Nieterau

  GeometryAttributes attributes = mesh.getAttributes();
  Ref<Vec4Attribute> tangentsAttr = attributes.getAttribute("tangents", Vec4Attribute);
  Vec4 tangents[] = tangentsAttr.values;

  Vec3 pos = mesh.getPointPosition(point);
  
   Vec3 normal = mesh.getPointNormal(point);
    Vec3 tangent = tangents[point].toVec3();
    Vec3 binormal = normal.cross(tangent);

      
    Mat44 mat(tangent.x, binormal.x, normal.x, pos.x,
              tangent.y, binormal.y, normal.y, pos.y,
              tangent.z, binormal.z, normal.z, pos.z,
              0       , 0        , 0         , 1    );
         
    return mat;
   
}




operator initialOffest<<<index>>>(in PolygonMesh refMesh, in PolygonMesh refMeshsmooth, 
            io PolygonMesh outMesh, io Vec3 delta[]) {
              
  Vec3 srcPos = refMesh.getPointPosition(index);
  Vec3 dstPos = refMeshsmooth.getPointPosition(index);

  Vec3 d = srcPos - dstPos;
  Mat44 srcMat = tangentSpaceMatrix(refMeshsmooth, index);
  srcMat.setTranslation(Vec3(0,0,0));

  delta[index] = srcMat.inverse() * d;
  
}

operator deformTask<<<index>>>(in PolygonMesh inMesh, io PolygonMesh outMesh,  io Vec3 delta[], in Scalar factor) {

  Vec3 oPos = inMesh.getPointPosition(index);
  Vec3 pos = outMesh.getPointPosition(index);

  Mat44 mat = tangentSpaceMatrix(outMesh, index);

  Vec3 newVec =   mat * delta[index];
  
  outMesh.setPointPosition(index, oPos.linearInterpolate(newVec, factor));

}



operator deltaMush(in PolygonMesh inMesh, io PolygonMesh outMesh, in PolygonMesh refMesh, 
                   io PolygonMesh refMeshSmooth, in Integer smoothIterations, in Boolean reset,
                   io Boolean init,
                   in Scalar factor, io Vec3 delta[]) {
  
 
  UInt64 start = getCurrentTicks();
  // validation
  if (smoothIterations < 1) {
    outMesh = inMesh.clone();
    outMesh.recomputePointNormals();
    setError("need some iterations");
    return;
  }
  
  
  if (reset == true) {
    init = false;
  }
  
  
  outMesh = inMesh.clone();
  outMesh.recomputePointNormals();
  if (factor > 0) {
    relax_operator(1.0, smoothIterations, outMesh);


    if(init == false) {
      delta.resize(outMesh.pointCount());
      refMeshSmooth = refMesh.clone();
      relax_operator(1.0, smoothIterations, refMeshSmooth);

      initialOffest<<<refMeshSmooth.pointCount()>>>(refMesh, refMeshSmooth, outMesh, delta);
    
      init = true;
    }


    deformTask<<<outMesh.pointCount()>>>(inMesh, outMesh, delta, Math_clamp(factor, 0, 1));
    outMesh.recomputePointNormals();
  
  }
  //outMesh = refMeshSmooth;
  
  
  UInt64 end = getCurrentTicks();
  //report("Elapsed time: " + getSecondsBetweenTicks(start, end) + " seconds");
}

Local timewarps for animated objects

Sure, scene timewarp is cool, but what if you want to just timewarp a few objects (say, your tracked camera)??

Select the objects that you want to timewarp, and run this script. Any animation curves on your selection will have a new animCurve node plugged into their “time” input, that you can tweak to timewarp your animation.


def makeWarpCurve():
    timeWarpNode = cmds.createNode('animCurveTT', name='timeWarper1')
    startTime= cmds.playbackOptions( q=True, min=True)
    endTime= cmds.playbackOptions( q=True, max=True)
    
    cmds.setAttr(timeWarpNode + '.preInfinity', 1)
    cmds.setAttr(timeWarpNode + '.postInfinity', 1)

    cmds.addAttr(timeWarpNode, ln='isTimeWarp',  at='bool')

    cmds.setKeyframe(timeWarpNode, time=(startTime,), insert=True)
    cmds.setKeyframe(timeWarpNode, time=(endTime,), insert=True)
    cmds.keyframe(timeWarpNode, index=(0,), absolute=True, valueChange=startTime)
    cmds.keyframe(timeWarpNode, index=(1,), absolute=True, valueChange=endTime)
    cmds.keyTangent(timeWarpNode, index=(0,), itt='linear', ott='linear')
    cmds.keyTangent(timeWarpNode, index=(1,), itt='linear', ott='linear')
    
    return timeWarpNode


def applyTimeWarp(objects, warpCurve=None):
    if not warpCurve:
        warpCurve = makeWarpCurve()
    else:
        if not cmds.objExists(warpCurve):
            print 'Cannot find warpCurve: "%s"' % warpCurve
            return
        
    for obj in objects:
        print obj
        for conn in cmds.listConnections( obj, d=False, s=True, type='animCurve'):
            print '\t', conn
            cmds.connectAttr(warpCurve + '.output', conn + '.input',  force=True)
    
    return warpCurve

def selectAllWarpNodes():
    wn = getAllWarpNodes()
    if wn:
        cmds.select(wn)
    else:
        print 'No warp nodes found'
    

def getAllWarpNodes():
    warpNodes = cmds.ls('*.isTimeWarp', recursive=True, objectsOnly=True)
    return warpNodes
    
    
def warpSelected():
    sel  = cmds.ls(sl=True)
    if sel:
        warpCurve = applyTimeWarp(sel)
        cmds.select(warpCurve)

warpSelected()

You can have multiple timewarps in a scene. Just select each object (or group of objects) separately and make new timewarpers.

I have no idea how it interacts with scene timewarp. Probably the universe will implode on itself.

the “getAllWarpNodes()” function will return all timewarper nodes in the scene. Or do a “selectAllWarpNodes()” to select them all.

Nodes missing from the hypershade

If you create a shader node via scripting without specifying “asShader=True” (there may be gui ways of getting the same effect), you may find that said nodes don’t appear in the top hypershade tabs. Maddening.

There’s no need to nuke the nodes and start again – they can be fixed in place. There’s a number of hidden sets that the hypershade tabs rely upon, so it’s trivial to reconnect them. Code below – select the offending nodes, and run this.

import maya.cmds as cmds

def fixShadingNodes(inNode):
    target = None
    
    nodeType = cmds.nodeType(inNode)
    if cmds.getClassification(nodeType, satisfies="shader"):
        target = 'defaultShaderList1.shaders'
    elif cmds.getClassification(nodeType, satisfies="texture"):
        target = 'defaultTextureList1.textures'
    elif cmds.getClassification(nodeType, satisfies="utility"):
        target = 'defaultRenderUtilityList1.utilities'
    
    if target:
        cmds.connectAttr(sel + '.message', target, na=True, force=True)

for sel in cmds.ls(sl=True):
    fixShadingNodes(sel)
    

Or apply it to all instances of a node type

for x in cmds.ls(type='aiImage'):
    fixShadingNodes(x)

Fabric splice – push op

Okay, let’s get this party started. Fabric Splice is freakin’ awesome and any studio can get 50 licenses for free.

Here’s the simplest possible deformer that i can possibly think of – the good old “push” operator from softimage. It moves vertices along the point normal.

require Math;
require Geometry;

operator doPushOp<<<index>>>(in Scalar amplitude, io PolygonMesh mesh0){
  Vec3 pos = mesh0.getPointPosition(index);
  Vec3 n  = mesh0.getPointNormal(index);
  
  Vec3 newPos = pos + (n * amplitude);
  mesh0.setPointPosition(index, newPos);
  
}

operator pushop(in Scalar amplitude, io PolygonMesh meshes[]) {
  for (Size i=0; i<meshes.size(); i++) {
    meshes[i].recomputePointNormals();
    doPushOp<<<meshes[i].pointCount()>>>(amplitude, meshes[i]);
    }

}

Reloading python modules in a package

def reloadModules(moduleStr):
    import sys
    for key, value in sys.modules.items():
        if moduleStr in key:
            if value:
                print 'reloading:', key
                del(sys.modules[key])

                
reloadModules('blahblah')

From keyframe animation to deform animation in Maya

Usually point caching systems will only deal with objects that are driven by deformation (eg, nCache) – hence the name, point caching. Other bigger, badder systems (eg, alembic) can cope with both transforms and deformations. However, what if you have a transform driven object that you desperately need to export to a point cache?
Read more

Localise matLib in Softimage

Quick script for nAngus – this will import the material library from a referenced model and change the object assignments from the referenced to the local. Saves importing the refModel completely.

'''
localiseMatLib

Usage: select a referenced matLib. A (local) duplicate will be created and 
objects assigned to these new materials

'''

app = Application

print '##########################'
shaderColl = {}
sel = app.Selection
for x in sel:
	if x.Type == 'library_source':
		# is locked by refmodel?
		if x.lockType == 3:
			matLibName = x.FullName
			print 'Matlib:', matLibName
			for mem in x.items:
				print mem.name
				usedBy = mem.UsedBy
				
				itemList = []
				for obj in usedBy:
					itemList.append(obj.fullName)

				shaderColl[str(mem.name)] = itemList
			print 'shaderColl', shaderColl
			
			# copy the material library
			newMatLib = app.Duplicate(x)
			print newMatLib
			
			# assign objects to the new matlib
			for mem in x.items:
				objectList = shaderColl[str(mem.name)]
				newMatName = mem.FullName.replace(matLibName, str(newMatLib))
				print 'newMatName', newMatName
				for obj in objectList:
					print newMatName, obj
					Application.SIAssignMaterial( obj, newMatName)
		else:
			print 'Pick a matlib that is referenced'

ICE instances into Maya

There was a follow up to an old discussion on the Softimage mailing list concerning transferring particle instance orders from Soft into Maya. I thought I’d document what I said earlier.
Read more

Duplicating Shave setups

Shave and a Haircut… interesting bit of kit.

I’ve been needing to duplicate hair setups onto meshes that aren’t world aligned (they have different animation caches on them, otherwise the same mesh), which means that Shave’s built in duplicating tools won’t necessarily work. For some reason, trying to script the duplication and plugging in a new mesh wasn’t working, until i decided to call a scene refresh. That fixed it! The old refresh trick…

Read more

Pose driver

In rigging land, and I’m making a rotation driver from built-in maya nodes. This usually drives some front of chain blendshapes (use Chad Vernon’s awesome cvShapeInverter tool) to do corrective blends. This is similar in concept to the Comet poseDeformer.
Read more

Return top