예제 #1
0
    def make_armature(self, parent_node, armature):
        gltf_node = {
            'name': armature.name,
            'children': [],
        }

        gltf_joints = {}

        for bone_name, bone in armature.data.bones.items():
            bone_matrix = get_bone_matrix(bone, armature)

            gltf_joint = {
                'name': bone_name,
                'children': [],
                'rotation': quat_to_list(bone_matrix.to_quaternion()),
                'scale': list(bone_matrix.to_scale()),
                'translation': list(bone_matrix.to_translation()),
            }

            if bone.parent:
                self._add_child(gltf_joints[bone.parent.name], gltf_joint)
            else:
                self._add_child(gltf_node, gltf_joint)

            gltf_joints[bone_name] = gltf_joint

        self._setup_node(gltf_node, armature)
        self._add_child(parent_node, gltf_node)

        return gltf_node
예제 #2
0
    def make_armature(self, parent_node, armature):
        channel = self._buffer.add_channel({
            'componentType': spec.TYPE_FLOAT,
            'type': 'MAT4',
            'extras': {
                'reference': 'inverseBindMatrices',
            },
        })

        gltf_armature = {
            'name': armature.name,
            'children': [],
        }
        gltf_skin = {
            'name': armature.name,
            'joints': [],
            'inverseBindMatrices': channel['bufferView'],
        }
        self._root['skins'].append(gltf_skin)

        if self._pose_freeze:
            set_active_object(armature)

            # disconnect bones
            with Mode('EDIT'):
                for bone_name, bone in armature.data.edit_bones.items():
                    bone.use_connect = False

            # reset bones rotation
            with Mode('EDIT'):
                for bone_name, bone in armature.data.edit_bones.items():
                    bone.roll = 0
                    bone.length = 10

                    # pos_matrix = mathutils.Matrix.Translation(bone.matrix.to_translation())
                    # rot_matrix = bone.matrix.to_quaternion().to_matrix()
                    # bone.transform(pos_matrix.inverted())  # reset origin
                    # bone.transform(rot_matrix.inverted())  # reset rotation
                    # bone.transform(pos_matrix)  # restore origin

                    bone.tail = bone.head + mathutils.Vector(
                        (0, bone.length, 0))
                    bone.roll = 0

        # create joint nodes
        gltf_joints = {}
        for bone_name, bone in armature.data.bones.items():
            bone_matrix = self._transform(get_bone_matrix(bone, armature))
            if self._pose_freeze:
                bone_matrix = self._freeze(bone_matrix)

            gltf_joint = {
                'name': bone_name,
                'children': [],
                'rotation': quat_to_list(bone_matrix.to_quaternion()),
                'scale': list(bone_matrix.to_scale()),
                'translation': list(bone_matrix.to_translation()),
                # 'matrix': matrix_to_list(bone_matrix),
            }
            gltf_joints[bone_name] = gltf_joint

        # add joints to skin
        for bone_name in sorted(gltf_joints.keys()):
            bone = armature.data.bones[bone_name]
            if bone.parent:  # attach joint to parent joint
                self._add_child(gltf_joints[bone.parent.name],
                                gltf_joints[bone.name])
            else:  # attach joint to armature
                # gltf_skin['skeleton'] = len(self._root['nodes']) - 1
                self._add_child(gltf_armature, gltf_joints[bone.name])

            ib_matrix = self._transform(get_inverse_bind_matrix(
                bone, armature))
            if self._pose_freeze:
                ib_matrix = self._freeze(ib_matrix)

            self._buffer.write(gltf_skin['inverseBindMatrices'],
                               *matrix_to_list(ib_matrix))
            gltf_skin['joints'].append(len(self._root['nodes']) - 1)

        self._setup_node(gltf_armature, armature)
        self._add_child(parent_node, gltf_armature)

        # no meshes or animation only
        if (not list(filter(is_object_visible, armature.children))
                or self._export_type == 'animation'):
            gltf_child_node = {
                'name': '{}_EMPTY'.format(armature.name),
                'skin': len(self._root['skins']) - 1,
            }
            self._add_child(gltf_armature, gltf_child_node)

        return gltf_armature
예제 #3
0
    def _setup_node(self, node, obj=None, can_merge=False):
        if obj is None:
            return

        armature = get_armature(obj)
        obj_matrix = self._transform(get_object_matrix(obj, armature=armature))

        # get custom object properties
        obj_props = get_object_properties(obj)

        if not can_merge and not armature:
            if self._geom_scale == 1:
                node.update({
                    'rotation':
                    quat_to_list(obj_matrix.to_quaternion()),
                    'scale':
                    list(obj_matrix.to_scale()),
                    'translation':
                    list(obj_matrix.to_translation()),
                    # 'matrix': matrix_to_list(obj_matrix),
                })
            else:
                x, y, z = list(obj_matrix.to_translation())
                x *= self._geom_scale
                y *= self._geom_scale
                z *= self._geom_scale
                node.update({
                    'rotation':
                    quat_to_list(obj_matrix.to_quaternion()),
                    'scale':
                    list(obj_matrix.to_scale()),
                    'translation': [x, y, z],
                })

        # setup collisions
        if not can_merge and is_collision(
                obj) and obj_props.get('type') != 'Portal':
            collision = {}
            node['extensions'] = {
                'BLENDER_physics': collision,
            }

            # collision shape
            shape = {
                'shapeType':
                obj.rigid_body.collision_shape,
                'boundingBox': [
                    obj.dimensions[i] / obj_matrix.to_scale()[i] *
                    self._geom_scale for i in range(3)
                ],
            }
            if obj.rigid_body.collision_shape == 'MESH':
                shape['mesh'] = node.pop('mesh')

            collision['collisionShapes'] = [shape]
            collision['static'] = obj.rigid_body.type == 'PASSIVE'

            # don't actually collide (ghost)
            if (not obj.collision or not obj.collision.use):
                collision['intangible'] = True

            if self._set_origin:
                obj.select_set(state=True)
                set_active_object(obj)
                x1, y1, z1 = obj.location
                # set origin to the center of bounds
                bpy.ops.object.origin_set(type='ORIGIN_GEOMETRY',
                                          center='BOUNDS')
                x2, y2, z2 = obj.location
                if 'extras' not in node:
                    node['extras'] = {}
                node['extras']['origin'] = [x2 - x1, y2 - y1, z2 - z1]

        # setup custom properties with tags
        if (obj_props or can_merge) and 'extras' not in node:
            node['extras'] = {}

        for k, v in obj_props.items():
            if node['extras'].get(k):  # tag exists
                tag = node['extras'].get(k)

            if type(v) in (tuple, list, dict):
                tag = json.dumps(v)
            else:
                tag = '{}'.format(v)

            node['extras'][k] = tag

        # if can_merge and 'type' not in obj_props:
        if can_merge:
            node['extras']['type'] = 'Merged'
예제 #4
0
    def _setup_node(self, node, obj=None, can_merge=False):
        if obj is None:
            return

        armature = get_armature(obj)
        obj_matrix = get_object_matrix(obj, armature=armature)

        # get custom object properties
        obj_props = get_object_properties(obj)

        if not can_merge and not armature:
            if self._geom_scale == 1:
                node.update({
                    'rotation': quat_to_list(obj_matrix.to_quaternion()),
                    'scale': list(obj_matrix.to_scale()),
                    'translation': list(obj_matrix.to_translation()),
                })
            else:
                x, y, z = list(obj_matrix.to_translation())
                x *= self._geom_scale
                y *= self._geom_scale
                z *= self._geom_scale
                node.update({
                    'rotation': quat_to_list(obj_matrix.to_quaternion()),
                    'scale': list(obj_matrix.to_scale()),
                    'translation': [x, y, z],
                })

        # setup collisions
        if not can_merge and is_collision(obj) and obj_props.get('type') != 'Portal':
            collision = {}
            node['extensions'] = {
                'BLENDER_physics': collision,
            }

            # collision shape
            shape = {
                'shapeType': obj.rigid_body.collision_shape,
                'boundingBox': [
                    obj.dimensions[i] / obj_matrix.to_scale()[i]
                    for i in range(3)
                ],
            }
            if obj.rigid_body.collision_shape == 'MESH':
                shape['mesh'] = node.pop('mesh')

            collision['collisionShapes'] = [shape]
            collision['static'] = obj.rigid_body.type == 'PASSIVE'

            # don't actually collide (ghost)
            if (not obj.collision or not obj.collision.use):
                collision['intangible'] = True

        # setup custom properties with tags
        if (obj_props or can_merge) and 'extras' not in node:
            node['extras'] = {}

        for k, v in obj_props.items():
            if node['extras'].get(k):  # tag exists
                tag = node['extras'].get(k)

            if type(v) in (tuple, list, dict):
                tag = json.dumps(v)
            else:
                tag = '{}'.format(v)

            node['extras'][k] = tag

        if can_merge and 'type' not in obj_props:
            node['extras']['type'] = 'Merged'
예제 #5
0
    def make_action(self, node, armature):
        gltf_armature = self.make_armature(node, armature)
        self._setup_node(gltf_armature, armature)
        self._add_child(self._root, gltf_armature)

        gltf_skin = self._make_skin(armature, armature)
        gltf_node = {
            'name': 'ARMATURE',
            'children': [],
            'skin': len(self._root['skins']) - 1,
        }
        # self._setup_node(gltf_node, armature)
        self._add_child(gltf_armature, gltf_node)

        # <-- animation
        gltf_animation = {
            'name': self._action or 'GLTF_ANIMATION',
            'channels': [],
            'samplers': [],
        }

        # time or animation frame
        channel = self._buffer.add_channel({
            'componentType': spec.TYPE_FLOAT,
            'type': 'SCALAR',
            'extra': {
                'reference': 'input',
            },
        })
        input_id = channel['bufferView']

        # setup bones
        gltf_channels = {}
        gltf_samplers = []
        for bone_name, bone in armature.data.bones.items():
            gltf_joint = None
            for i, child in enumerate(self._root['nodes']):
                if child['name'] == bone_name:
                    gltf_joint = i
                    break

            gltf_target = {}
            if gltf_joint is not None:
                gltf_target['node'] = gltf_joint

            for path in ('rotation', 'scale', 'translation'):
                gltf_samplers.append(self._make_sampler(path, input_id))

                gltf_channel = {
                    'sampler': len(gltf_samplers) - 1,
                    'target': copy.copy(gltf_target),
                }
                gltf_channel['target']['path'] = path
                gltf_channels['{}/{}'.format(bone_name, path)] = gltf_channel

        gltf_animation['channels'] = list(gltf_channels.values())
        gltf_animation['samplers'] = gltf_samplers

        # set animation data
        frame_start = bpy.context.scene.frame_start
        frame_end = bpy.context.scene.frame_end
        if self._action:
            if self._action in bpy.data.actions:
                action = bpy.data.actions[self._action]
                frame_start, frame_end = action.frame_range

        frame = float(frame_start)
        frame_int = None
        input_ = 0
        while frame <= frame_end:
            # switch frame
            if frame_int != math.floor(frame):
                frame_int = math.floor(frame)
                bpy.context.scene.frame_current = frame_int
                bpy.context.scene.frame_set(frame_int)

            if isinstance(self._speed_scale, collections.Callable):
                speed_scale = self._speed_scale(frame_int)
            else:
                speed_scale = self._speed_scale

            # switch subframe
            if speed_scale != 1:
                bpy.context.scene.frame_subframe = frame - frame_int

            # write bone matrices
            for bone_name, bone in armature.pose.bones.items():
                bone_matrix = get_bone_matrix(bone, armature)

                rotation = quat_to_list(bone_matrix.to_quaternion())
                scale = list(bone_matrix.to_scale())
                translation = list(bone_matrix.to_translation())

                gltf_channel = gltf_channels['{}/{}'.format(
                    bone_name, 'rotation')]
                gltf_sampler = gltf_samplers[gltf_channel['sampler']]
                self._buffer.write(gltf_sampler['output'], *rotation)

                gltf_channel = gltf_channels['{}/{}'.format(
                    bone_name, 'scale')]
                gltf_sampler = gltf_samplers[gltf_channel['sampler']]
                self._buffer.write(gltf_sampler['output'], *scale)

                gltf_channel = gltf_channels['{}/{}'.format(
                    bone_name, 'translation')]
                gltf_sampler = gltf_samplers[gltf_channel['sampler']]
                self._buffer.write(gltf_sampler['output'], *translation)

                self._buffer.write(gltf_sampler['input'], input_)

            # advance to the next frame
            frame += speed_scale
            input_ += 1

        # animation -->

        self._root['animations'].append(gltf_animation)