Copying Parent-Relative Positions from one Object to Another in Blender
Well, the title of this post sure is a mouthful... but try as I might, I just couldn't come up with a shorter way to phrase what I've been doing and am going to write about here! I've been working on a proxy model for the Blood Imp monster from Gloomhaven: Jaws of the Lion. There has been a terrible heat wave in California though with rolling brown-outs in my area due to the increased draw on the electric grid caused by everyone's AC units. Given that we've all been suffering from power shortages, it just doesn't seem responsible to fire up the printer and put more stress on the system. Also, because this model is only a head and 4 limbs and reposing it isn't as much work as it would be for a normal humanoid creation, I figured that I could make a few alternative poses for it while I wait out this heat wave to begin test printing!
Well, the Fang array was working great, but I put the Empty objects in a different collection so that I wouldn't accidentally select them and mess things up while working on other stuff. When I copied the model onto my "ForPrinting" collection, I forgot to copy the Empties, which meant that my backup copy's teeth were still being defined by the Empties that I was going to be moving around for the new pose (in my Workspace collection). After finishing the first draft of the new pose, I wanted to compare it to the original... and was unhappy to find a flying aura of fangs around my monster!
D'oh! Now I've got a bit of a problem on my hand. I need a new set of Empties for this pose, and I need to get their exact position/rotation relative to this pose's fang objects to match the Empties' positions/rotations relative to the new pose's fang objects (ugh, yep, still difficult to verbalize what I'm trying to say here...). While it's simple enough to copy the Workspace Empties over to this Pose Collection, getting them in the precisely right spots to get the teeth to line back up with the gums would be incredibly difficult by hand. I could always remove all of the parenting, then look at the differences between the fang's XYZ position and rotation data and the empty's XYZ position and rotation data, then do the math based on the Pose Collection's fang's XYZ position and rotation data to figure out the new positions that I need for the empties... but that process is sufficiently complicated that I'm not even 100% sure that I just explained it accurately, must less that I could go through it without making all sorts of silly math and logic errors. Also... this isn't the first time that I've gotten myself into a situation like this, so I figured that it was time to come up with a real solution.
Well, a bunch of google searches and experimentation yielded results (I mean, why else would I be writing about it here, right?)! The answer was to use Blender's Python console, which is a bit intimidating, but is ultimately not too difficult to use (at least, for simple things like this). Just as Blender is an object oriented modelling tool, its command line console is similarly object oriented. That means that each model object (for example, a fang) has a whole bunch of properties that can be accessed at the command line. After a bunch of research, I realized that the .mesh_local property contained the exact data that I needed in this situation.
An object's .mesh_local property expressed its location, rotation, and scale relative to its parent object. So, that means that the solution to my problem is actually really simple. I need to find the mesh_local value for the Workspace Empties (which are working great with the Workspace Fangs), and I need to put that value onto the Pose Empties (which are all kinds of messed up in regards to the Pose Fangs). In my case, the Pose instances of the objects are all named with a .001 suffix (since they're copies of the Workspace instances). That means that the console command to copy the Workspace instance's values over the Pose instance's values is: bpy.data.objects['Empty.Fangs.Lower.001'].matrix_local = bpy.data.objects['Empty.Fangs.Lower'].matrix_local
So, there you have it! If you ever need to copy the relative position between an object and its parent over to another object and its parent, it turns out there's a really easy way to do it! When it comes right down to it, I think that it's probably even easier to do it than it is to describe what it is that you're doing!