Discussion:
[Maya-Python] blendShape Data
Enrico Losavio
2015-01-27 22:57:03 UTC
Permalink
Hey everyone!

I'm trying to improve the workflow here in my studio, and I'm fighting
against blendShape nodes.
I want to access the target data after the mesh objects are deleted from
the scene, but apparently Maya APIs don't allow me to do that.
It seems strange that such an important node doesn't have a good API
support, but as stated in this
<http://nccastaff.bournemouth.ac.uk/jmacey/RobTheBloke/www/research/maya/mfnblendshape.htm> article
there's a bug (since at least ten years...)

I noticed that as long as the mesh object is connected to the blendShape
node, the node is holding position data (thus, moving all the vertices to
fit the exact shape of the target). But when targets are deleted, the BS
node holds only the deltas (ie the difference between the target mesh and
the original mesh at the moment of deletion).

So basically, when I use the MFnBlendShapeDeformer.getTargets() method,
Maya either gives me the target object (when it is connected, from which I
can retrieve vertices position using MFnMesh.getPoints()) or returns Null
(when there's no object connected)

I mean.. we're talking about freaking blendShapes! There must be a way to
just query the data I'm looking for without flipping out!

Do you have any advice on how to work this problem around?

Thanks a lot
--
You received this message because you are subscribed to the Google Groups "Python Programming for Autodesk Maya" group.
To unsubscribe from this group and stop receiving emails from it, send an email to python_inside_maya+***@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/python_inside_maya/9d2c5cde-d915-47cf-92ec-61962d44410c%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
Enrico Losavio
2015-01-28 12:44:01 UTC
Permalink
Quick update..

I'm trying to solve the problem remaining in the APIs context. Which means
that I don't want to enable the blend targets one at a time and take the
output mesh object.
Now I'm trying to access the data using MPlugs, but I'm probably querying
the wrong attribute.
The thing is that usually the target shape is connected to the
inputGeomTarget, but when it is deleted that attribute, sadly, remains
empty. This means that the offset data I'm looking for is stored somewhere
else in the node, and my goal is to catch this data as -i guess- an
MPointArray or MVectorArray object.

So.. does anyone knows how a blendShape node works internally? Or what
inputPointsTarget and inputComponentsTarget attributes are for?

Thanks again!

Enrico
--
You received this message because you are subscribed to the Google Groups "Python Programming for Autodesk Maya" group.
To unsubscribe from this group and stop receiving emails from it, send an email to python_inside_maya+***@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/python_inside_maya/8673dee4-8f9c-4b4e-b2aa-3d3b85e47423%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
3***@gmail.com
2015-01-29 04:14:47 UTC
Permalink
I don't have maya in front of me, and I'm certainly no expert. But if you can get the deltas from the BS node, then why not just multiply your base shape point position by the deltas? I suppose it's slightly more overhead for you, but maya storing the deltas instead of point positions on the BS node makes sense to me.

Kev
--
You received this message because you are subscribed to the Google Groups "Python Programming for Autodesk Maya" group.
To unsubscribe from this group and stop receiving emails from it, send an email to python_inside_maya+***@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/python_inside_maya/d7fb5179-dc63-4028-bbbe-49d8c8b9bd5f%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
3***@gmail.com
2015-01-29 04:35:41 UTC
Permalink
sorry, I just re-read your posts. I wasn't much help, lol.
--
You received this message because you are subscribed to the Google Groups "Python Programming for Autodesk Maya" group.
To unsubscribe from this group and stop receiving emails from it, send an email to python_inside_maya+***@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/python_inside_maya/c343f430-95bd-4dda-ad4a-840f2b762d00%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
Joe Weidenbach
2015-01-29 05:05:13 UTC
Permalink
I, also, am no expert, but here's what I do know from writing a few
custom deformers.

The dependency graph is made of dependency nodes that are connected to
each other. When a node is used for input, it's data is cached
internally in the deformer node. This is functionality that's built
into MPxDeformer, and comes basically for free if you use the deform()
method to handle your deformations. That node (in this case a mesh)
only updates that cache data if it's output plug is marked dirty. If
the connection is broken, Maya just holds onto that cache data and keeps
using it as though the node was there. This is by design, and is meant
to enhance performance by only running calculations if data has
changed. I haven't worked with MPxDeformer enough to know if you get
access to that cache from inside (that is, if you wrote a custom
blendshape node and accessed the data internally), but I'm almost
positive that it's completely closed off from outside.

What you could do, conceivably, if you need to reconstruct the original
shapes is to iterate through the targets list in the blendshape node,
setting the weights to 1 one at a time, and then duplicating the mesh or
reading the point data you need.

Maybe that helps?

Joe
Post by 3***@gmail.com
sorry, I just re-read your posts. I wasn't much help, lol.
---
This email is free from viruses and malware because avast! Antivirus protection is active.
http://www.avast.com
--
You received this message because you are subscribed to the Google Groups "Python Programming for Autodesk Maya" group.
To unsubscribe from this group and stop receiving emails from it, send an email to python_inside_maya+***@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/python_inside_maya/54C9BF89.70001%40gmail.com.
For more options, visit https://groups.google.com/d/optout.
Enrico Losavio
2015-01-29 14:11:14 UTC
Permalink
This post might be inappropriate. Click to display it.
r***@gmail.com
2016-12-07 17:58:04 UTC
Permalink
Post by Enrico Losavio
So.. thanks everyone for the replies.
I managed to acced the data I was looking for. It was just a bit hidden and not-so-easy to read.
Since I haven't found a solution for this problem on the internet yet, i post mine here.
Given that (as written in the blendShape node documentation)
inputGeomTarget ... stores the The target geometry input ... (type: geometry)
inputPointsTarget ... stores the Delta values for points in a target geometry ... (type: pointArray)
inputComponentsTarget ... stores the Component list for points in a target geometry ... (type: componentList)
and that maya APIs dont have any convenient method to access this data, I decided to used MPlugs to reach these attributes.
If the shape target object is still connected to the blend shape node, you can simply use the MFnBlendShapeDeformer.getTargets() method
If the shape target object has been deleted or disconnected, then the inputGeomTarget attribute will be empty, and you will have to find a way to combine the two remaining attributes.
The code below retrieve this data (in this case just for the first target in the bsNode) and prints it out. I guess that, properly combined together, this data gives you a lot of control over what's happening inside the node, especially when dealing with complex rigs!
Hope this helps!
ps. let me know if you have any comment or suggestion...
Enrico
import maya.cmds as cmds
import maya.OpenMaya as OM
# gets the blendShape node MObject
bsNodeName = 'blendShape1'
bsNodeObj = OM.MObject()
sel = OM.MSelectionList()
sel.add(bsNodeName, 0)
sel.getDependNode(0, bsNodeObj)
# gets the plug for the inputTargetItem[] compound attribute and ...
dgFn = OM.MFnDependencyNode(bsNodeObj)
plug = dgFn.findPlug('inputTarget').elementByPhysicalIndex(0).child(0).elementByPhysicalIndex(0).child(0).elementByPhysicalIndex(0)
# if connected, retrieves the input target object and queries his points
    inputGeomTarget = plug.child(0).asMObject()
    targetPoints = OM.MPointArray()
    fnMesh = OM.MFnMesh(inputGeomTarget)
    fnMesh.getPoints(targetPoints)
# if not connected, retrieves the deltas and the affected component list
    inputPointsTarget = plug.child(1).asMObject()
    inputComponentsTarget = plug.child(2).asMObject()
    
    # to read the offset data, I had to use a MFnPointArrayData
    
    fnPoints = OM.MFnPointArrayData(inputPointsTarget)
    targetPoints = OM.MPointArray()
    fnPoints.copyTo(targetPoints)
    
    # read the component list data was the trickiest part, since I had to use MFnSingleIndexedComponent to extract (finally), an MIntArray
    # with the indices of the affected vertices
    
    componentList = OM.MFnComponentListData(inputComponentsTarget)[0]
    fnIndex = OM.MFnSingleIndexedComponent(componentList)
    targetIndices = OM.MIntArray()
    fnIndex.getElements(targetIndices)
    
            print "vertex %d has offset " %targetIndices[i], targetPoints[i].x,targetPoints[i].y, targetPoints[i].z
I was wondering why you are not iterating over OM.MFnComponentListData(inputComponentsTarget) but just take the first element with componentList = OM.MFnComponentListData(inputComponentsTarget)[0]

For some reason in my case inputComponents is a collection of lists.
--
You received this message because you are subscribed to the Google Groups "Python Programming for Autodesk Maya" group.
To unsubscribe from this group and stop receiving emails from it, send an email to python_inside_maya+***@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/python_inside_maya/8c64a036-639c-4fd7-8246-a5d5816648e6%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
Enrico Losavio
2018-10-27 21:45:26 UTC
Permalink
(two and a half years later)

I was wondering the same! 😅

I haven't touched this code in a long time. When I finally got it to work I
decided not to change, as there are a couple of things that I still don't
fully understand and I don't wanna break it.
This is the typical case of "It works!... But why?!"

Today I was just looking back into it, cause I was investigating a small
issue (which is actually specific to one file only, so maybe just a quirk
of maya) where inputComponentsTarget and inputPointsTarget have different
lengths, but the blendShape node still works fine... For most of the poses,
the index list

I was very surprised, cause a delta can't really be applied without knowing
which vertex to affect. Maybe the indices are stored somewhere else? Maybe
they're cached?


Now... I have also written some code to actually *write* my custom deltas
in the blendShape node. Here is an excerpt.

index_list = OM.MIntArray() # indices to be written
deltas_list = OM.MPointArray() # deltas to be written (maya wants points
instead of vectors) ¯\_(ツ)_/¯

####################################################################################
### values for index_list and deltas_list are assigned outside this code
snippet ###
####################################################################################

# initializes function sets, needed to create the MObjects and set their
data
dg_component_fn = OM.MFnComponentListData()
dg_component_data = dg_component_fn.create()
singleComponent_fn = OM.MFnSingleIndexedComponent()
singleComponent_data = singleComponent_fn.create(OM.MFn.kMeshVertComponent)
singleComponent_fn.addElements(index_list)

# write the index data in the inputComponentsTarget plug
if not singleComponent_data.isNull():
dg_component_fn.add(singleComponent_data)
inputComponentsTarget_plug.setMObject(dg_component_data)

print dg_component_fn.length() # <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
this line prints 1 (in my test index_list and deltas_list had 11476
elements each)

# writes the delta data in the inputPointsTarget plug
dg_pointArray_fn = OM.MFnPointArrayData()
dg_pointArray_data = dg_pointArray_fn.create(deltas_list)
if not dg_pointArray_data.isNull():
inputPointsTarget_plug.setMObject(dg_pointArray_data)

print dg_pointArray_fn.length() # <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
this line prints 11476 (correct!)



It has always worked fine in the last few years, and it still works
correctly also with this file.
However, even after re-writing the blendShape targets in the node, the
indices still don't have the right length.
*With very few exceptions*,the length of the indices in
inputComponentsTarget is a very small value, between 1 and 5.


Here is an excerpt of the code to read the deltas from the blendShape node.

inputPointsTarget_data = inputPointsTarget_plug.asMDataHandle().data()
inputComponentsTarget_data =
inputComponentsTarget_plug.asMDataHandle().data()

# to read the deltas, use a MFnPointArrayData
targetPoints = OM.MPointArray()
fnPoints = OM.MFnPointArrayData(inputPointsTarget_data)
fnPoints.copyTo(targetPoints)

# use MFnSingleIndexedComponent to extract an MIntArray with the indices
dg_component_fn = OM.MFnComponentListData(inputComponentsTarget_data)[0]
# <<<<<<<<<<<< why does this need to be only the first element?
if dg_component_fn.isNull():
continue
singleComponent_fn = OM.MFnSingleIndexedComponent(dg_component_fn)
targetIndices = OM.MIntArray()
singleComponent_fn.getElements(targetIndices)

print "indices: ", targetIndices.length(), "points: ",
targetPoints.length() # <<<<<<<<<<<< prints "indices: 1 points: 11476"



To be honest I am still a bit confused by some bits of this code. I *think *I
understand the combined use of MFnComponentListData and
MFnSingleIndexedComponent, but I'm not so familiar with these classes and
the documentation is pretty scarce. Also google wasn't very helpful, as
brought me back to this very thread that I started long time ago...

Since I noticed this issue on one file only, I don't know if you'll be able
to replicate the same scenario.
However, maybe you can notice any anomaly in the way I'm handling this data
to put it in / take it out the plug.

Can anyone shed some light on what's going on here?
Any help would be greatly appreciated!

Thanks,
Enrico
--
You received this message because you are subscribed to the Google Groups "Python Programming for Autodesk Maya" group.
To unsubscribe from this group and stop receiving emails from it, send an email to python_inside_maya+***@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/python_inside_maya/3ed84bae-bd1b-470e-b701-3c145c50f235%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
3***@gmail.com
2015-01-29 17:34:49 UTC
Permalink
good stuff, Nice to see you got things working. That's quite the plug to access, lol.

It'd be interesting to see, after you get the base shape point positions and multiply them by the deltas to get each shapes pp, if iterating through the shapes, turning them on one by one and getting the pp would be any faster.
--
You received this message because you are subscribed to the Google Groups "Python Programming for Autodesk Maya" group.
To unsubscribe from this group and stop receiving emails from it, send an email to python_inside_maya+***@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/python_inside_maya/5bce5752-8593-47ab-a8df-ecf4b6311479%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
Marcus Ottosson
2015-01-29 22:28:50 UTC
Permalink
Why remove the shapes to begin with? Honest question, as I typically leave
shapes connected or referenced. Is it a scene-size issue?
--
*Marcus Ottosson*
***@gmail.com
--
You received this message because you are subscribed to the Google Groups "Python Programming for Autodesk Maya" group.
To unsubscribe from this group and stop receiving emails from it, send an email to python_inside_maya+***@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/python_inside_maya/CAFRtmOATn%2BYnhUAchVGFnB%3DOSkCNu6Az6Otge1-rQK%2BXpc40NA%40mail.gmail.com.
For more options, visit https://groups.google.com/d/optout.
Enrico Losavio
2015-02-02 11:54:14 UTC
Permalink
Yeah, well. I'm not sure whether it's faster, but it's definitely more
elegant, since you don't have to leave maya APIs.
I guess you could then rewrite the script in C++ as a command plugin to
make it really really fast and efficient.
Still, having access to these deltas allows you to work with a wider range
of data and opens up to new possibilities!

And well, deleting BS targets helps keeping the scene lighter (and possibly
faster).
We are now working on a rig with several tens of targets, each one of which
has something like one million polygons... Deleting the shapes freed up
about 700mb!
--
You received this message because you are subscribed to the Google Groups "Python Programming for Autodesk Maya" group.
To unsubscribe from this group and stop receiving emails from it, send an email to python_inside_maya+***@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/python_inside_maya/f8172350-b8d9-453b-89ec-7ae23e883692%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
Marcus Ottosson
2015-02-02 12:38:37 UTC
Permalink
(and possibly faster)

Unless the shapes are also being manipulated during playback, I would be
surprised if this were true, considering that the input would be deemed
not-dirty regardless of anything being plugged into it.

Still, having access to these deltas allows you to work with a wider range
of data and opens up to new possibilities!

On the contrary, I would say.

By removing the shapes to begin with you limit yourself greatly. Does a
blendshape node even cache enough of the mesh to fully re-produce it? I’d
imagine it only bothers to store pointpositions, but I could be mistaken.

We are now working on a rig with several tens of targets, each one of which
has something like one million polygons
 Deleting the shapes freed up about
700mb!
​

That’s a good reason, I’ve done the same in similar situations. What I
found less destructive however was to reference the shapes - they can be
for example Maya scenefiles, plain obj or alembic - that make the scenes
even lighter as the blendShape node wouldn’t need to keep track of deltas
either.

Out of curiosity, are you really rebuilding tens of shapes at millions of
polygons each using Python?
​
--
You received this message because you are subscribed to the Google Groups "Python Programming for Autodesk Maya" group.
To unsubscribe from this group and stop receiving emails from it, send an email to python_inside_maya+***@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/python_inside_maya/CAFRtmOBDgF%2BPhp7fJUoL4wS0yaCsKR1WZ0MLUYiiBNBkavkUfg%40mail.gmail.com.
For more options, visit https://groups.google.com/d/optout.
Enrico Losavio
2015-03-08 19:56:38 UTC
Permalink
Well, when targets are deleted Maya is not storing points position anymore,
but only deltas of the affected vertices. That is: from now on, every
change applied to the origShape (as long as vertex count and indexing
remain untouched) will not be overridden by the blendShape node, but just
added up. This makes the workflow incredibly smooth, since you don't need
to remodel all the BS at this point. If you need to correct any of them,
you just have to trigger it on, duplicate the mesh. sculpt the edits and
plug it back into the bs node (and delete it ;) )

Plus, using my script i've noticed that often Maya (probably because of
some floating-point, double-precision data related issue) saves a lot of
junk deltas, whose value is approximable to zero. So I wrote another script
to get rid of deltas with a length smaller than a certain threshold, and by
doing that the file shrank by 100 mb more. Furthermore, the overall number
of deltas removed is something around four millions, which definitely means
a lot less computation for Maya.

So at the end of the day, my file is at least 800 mb smaller, the scene is
faster, and i have the power to change my base mesh whenever i want without
bothering too much about reshaping the targets.

And lastly, yes! I was trying to rebuild tens of shapes at millions of
polygons each using Python. It is not the fastest way, but I wanted to be
able to do it using the APIs only. Let's say it is part of a self-imposed
learning plan!

This is my approach to the question, because i need this level of freedom.
Referencing the targets is also another great way to keep you scene light,
but I prefer to avoid referencing since i believe it is weak and dangerous.
I know it is necessary when animating, but yet i would try to reduce it as
much as possible (thus having no references before the rigging file)!

Enrico
--
You received this message because you are subscribed to the Google Groups "Python Programming for Autodesk Maya" group.
To unsubscribe from this group and stop receiving emails from it, send an email to python_inside_maya+***@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/python_inside_maya/9e727008-02bf-4658-b69f-c2daddf01109%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
Loading...