Ditch the bones and make an object hierarchy.
Since each flap of your test file is a separate object would suggest can be done without the extra hassle of creating an armature.
For simplicity the face with the most folds is selected as the parent. Its origin is arbitrary and has been kept as the center of geometry.
Now for each flap connected to this, move its origin to the middle of the fold edge and parent to it.
And so on. In example the top flap is parented to the left flap which is parented to the bottom flap.
Make sure the rotation is applied on all the flaps.
Angle axis Rotation.
One of the rotation types available in blender is axis angle. ie how much to rotate about a particular axis, which is ideal in this case, since the fold determines the axis.
For example the front and bottom faces fold on the X axis. To keep the angle of fold direction desired positive, the -X axis is chosen for front flap.
Custom Rotation for a tilted aircraft flap
Driving the result.
Since for simple example, flat is angle 0 and folded is angle 90. Added a simple custom property "prop" to the bottom which goes from 0 (flat) to 90 degrees (pi / 2) folded and driven each child flap with it.
and here is the end result.
Same for armatures.
Slapped this together, thought I'd add it here. Can make a single mesh where each edge between two flaps is exactly aligned down the axis of the hinge.
Makes an armature and puts in hinge bones with head in closest point on of edge to face center, and tail pointing towards the face center. This will ensure that the bone always hinges on the X axis (1, 0, 0). With the mesh in XY plane the bone z axis matches the global z axis (or face normals)
All the hinge bones rotation modes are set to axis angle.
Each hinge bone is given a name with the edge index appended.
If a temp mesh is made as such, the location of the face center will become the bone tip. The actual objects can be parented to tip, or constraints used.
Added vertex groups to each face of the object.
Select the base face of the folding structure and run script.
import bpy
from mathutils import Matrix, Vector
from mathutils.geometry import intersect_point_line
import bmesh
context = bpy.context
def hinge(armob, ob, pb, face):
arm = armob.data
# add a bone from edge center to face center
face.select_set(True)
edges = [(l, f) for l in face.loops for f in l.edge.link_faces if not f.select]
for l, f in edges:
e = l.edge
b = arm.edit_bones.new(f"Hinge_{f.index}")
b.parent = pb
pt, dist = intersect_point_line(
f.calc_center_median(),
e.verts[1].co,
e.verts[0].co,
)
b.head = pt
b.tail = f.calc_center_median()
vg = (
ob.vertex_groups.get(b.name) or
ob.vertex_groups.new(name=b.name)
)
vg.add([v.index for v in f.verts], 1, 'ADD')
hinge(armob, ob, b, f)
def make_armature(ob, face_index):
me = ob.data
bm = bmesh.from_edit_mesh(me).copy()
face = bm.faces.active
bpy.ops.object.mode_set(mode='OBJECT')
bpy.ops.object.armature_add()
arm = context.object
mw = (
ob.parent.matrix_world.copy() if ob.parent
else ob.matrix_world.copy()
)
ob.matrix_parent_inverse.identity()
#ob.matrix_world.identity()
ob.parent = arm
ob.matrix_parent_inverse = ob.matrix_world.inverted()
ob.vertex_groups.clear()
arm.matrix_world = ob.matrix_world
bpy.ops.object.mode_set(mode='EDIT')
b = arm.data.edit_bones[0]
b.tail += face.calc_center_median()
b.head += face.calc_center_median()
hinge(arm, ob, b, face)
bpy.ops.object.mode_set(mode='OBJECT')
for pb in arm.pose.bones[1:]:
pb.rotation_mode = 'AXIS_ANGLE'
pb.rotation_axis_angle = (0, 1, 0, 0)
ob.modifiers.clear()
mod = ob.modifiers.new(
"Hinger",
type='ARMATURE',
)
mod.object = arm
bpy.ops.object.mode_set(mode='POSE')
make_armature(context.object, 0)