Exemple #1
0
def __gather_indices(blender_primitive, blender_mesh, modifiers,
                     export_settings):
    indices = blender_primitive['indices']

    new_indices = []

    # Reverse order of indices so game will render the proper front face
    for i in range(0, len(indices), 3):
        new_indices.append(indices[i + 2])
        new_indices.append(indices[i + 1])
        new_indices.append(indices[i + 0])

    indices = new_indices

    # NOTE: Values used by some graphics APIs as "primitive restart" values are disallowed.
    # Specifically, the values 65535 (in UINT16) and 4294967295 (in UINT32) cannot be used as indices.
    # https://github.com/KhronosGroup/glTF/issues/1142
    # https://github.com/KhronosGroup/glTF/pull/1476/files
    # Also, UINT8 mode is not supported:
    # https://github.com/KhronosGroup/glTF/issues/1471
    max_index = max(indices)
    assert max_index < 65535

    # if max_index >= 65530:

    # MSFS is expecting an unsigned short
    component_type = gltf2_io_constants.ComponentType.UnsignedShort
    # if max_index < 65535:
    #     component_type = gltf2_io_constants.ComponentType.UnsignedShort
    # elif max_index < 4294967295:
    #     component_type = gltf2_io_constants.ComponentType.UnsignedInt
    # else:
    #     print_console('ERROR', 'A mesh contains too many vertices (' + str(max_index) + ') and needs to be split before export.')
    #     return None

    element_type = gltf2_io_constants.DataType.Scalar
    return gltf2_io.Accessor(buffer_view=indices,
                             byte_offset=None,
                             component_type=component_type,
                             count=len(indices),
                             extensions=None,
                             extras=None,
                             max=None,
                             min=None,
                             name=None,
                             normalized=None,
                             sparse=None,
                             type=element_type)
Exemple #2
0
def gather_accessor(buffer_view: gltf2_io_binary_data.BinaryData,
                    component_type: gltf2_io_constants.ComponentType, count,
                    max, min, type: gltf2_io_constants.DataType,
                    export_settings) -> gltf2_io.Accessor:
    return gltf2_io.Accessor(buffer_view=buffer_view,
                             byte_offset=None,
                             component_type=component_type,
                             count=count,
                             extensions=None,
                             extras=None,
                             max=list(max) if max is not None else None,
                             min=list(min) if min is not None else None,
                             name=None,
                             normalized=None,
                             sparse=None,
                             type=type)
Exemple #3
0
def __gather_inverse_bind_matrices(blender_object, export_settings):
    inverse_matrices = []

    axis_basis_change = mathutils.Matrix.Identity(4)
    if export_settings[gltf2_blender_export_keys.YUP]:
        axis_basis_change = mathutils.Matrix(
            ((1.0, 0.0, 0.0, 0.0), (0.0, 0.0, 1.0, 0.0), (0.0, -1.0, 0.0, 0.0),
             (0.0, 0.0, 0.0, 1.0)))

    # # artificial torso, as needed by glTF
    # inverse_bind_matrix = blender_object.matrix_world.inverted() * axis_basis_change.inverted()
    # for column in range(0, 4):
    #     for row in range(0, 4):
    #         inverse_matrices.append(inverse_bind_matrix[row][column])

    #
    for blender_bone in blender_object.pose.bones:
        inverse_bind_matrix = gltf2_blender_math.multiply(
            axis_basis_change, blender_bone.bone.matrix_local)
        bind_shape_matrix = gltf2_blender_math.multiply(
            gltf2_blender_math.multiply(
                axis_basis_change, blender_object.matrix_world.inverted()),
            axis_basis_change.inverted())

        inverse_bind_matrix = gltf2_blender_math.multiply(
            inverse_bind_matrix.inverted(), bind_shape_matrix)
        for column in range(0, 4):
            for row in range(0, 4):
                inverse_matrices.append(inverse_bind_matrix[row][column])

    binary_data = gltf2_io_binary_data.BinaryData.from_list(
        inverse_matrices, gltf2_io_constants.ComponentType.Float)
    return gltf2_io.Accessor(
        buffer_view=binary_data,
        byte_offset=None,
        component_type=gltf2_io_constants.ComponentType.Float,
        count=len(inverse_matrices) //
        gltf2_io_constants.DataType.num_elements(
            gltf2_io_constants.DataType.Mat4),
        extensions=None,
        extras=None,
        max=None,
        min=None,
        name=None,
        normalized=None,
        sparse=None,
        type=gltf2_io_constants.DataType.Mat4)
Exemple #4
0
def __gather_position(blender_primitive, export_settings):
    position = blender_primitive["attributes"]["POSITION"]
    componentType = gltf2_io_constants.ComponentType.Float
    return {
        "POSITION": gltf2_io.Accessor(
            buffer_view=gltf2_io_binary_data.BinaryData.from_list(position, componentType),
            byte_offset=None,
            component_type=componentType,
            count=len(position) // gltf2_io_constants.DataType.num_elements(gltf2_io_constants.DataType.Vec3),
            extensions=None,
            extras=None,
            max=gltf2_blender_utils.max_components(position, gltf2_io_constants.DataType.Vec3),
            min=gltf2_blender_utils.min_components(position, gltf2_io_constants.DataType.Vec3),
            name=None,
            normalized=None,
            sparse=None,
            type=gltf2_io_constants.DataType.Vec3
        )
    }
Exemple #5
0
def __gather_normal(blender_primitive, export_settings):
    if export_settings['gltf_normals']:
        normal = blender_primitive["attributes"]['NORMAL']
        return {
            "NORMAL": gltf2_io.Accessor(
                buffer_view=gltf2_io_binary_data.BinaryData.from_list(normal, gltf2_io_constants.ComponentType.Float),
                byte_offset=None,
                component_type=gltf2_io_constants.ComponentType.Float,
                count=len(normal) // gltf2_io_constants.DataType.num_elements(gltf2_io_constants.DataType.Vec3),
                extensions=None,
                extras=None,
                max=None,
                min=None,
                name=None,
                normalized=None,
                sparse=None,
                type=gltf2_io_constants.DataType.Vec3
            )
        }
    return {}
def __get_keyframe_accessor(frame_start, frame_end, frame_step):
    # Gets an accessor for a range of keyframes. Used for sampler.input.
    fps = bpy.context.scene.render.fps
    keyframes = [
        frame / fps for frame in range(frame_start, frame_end, frame_step)
    ]
    keyframe_data = array.array('f', keyframes).tobytes()
    return gltf2_io.Accessor(
        buffer_view=gltf2_io_binary_data.BinaryData(keyframe_data),
        component_type=gltf2_io_constants.ComponentType.Float,
        type=gltf2_io_constants.DataType.Scalar,
        count=len(keyframes),
        min=[keyframes[0]],
        max=[keyframes[-1]],
        byte_offset=None,
        extensions=None,
        extras=None,
        name='accessorAnimationInput',
        normalized=None,
        sparse=None,
    )
Exemple #7
0
def __gather_input(action_group: bpy.types.ActionGroup,
                   blender_object: bpy.types.Object,
                   export_settings) -> gltf2_io.Accessor:
    """Gather the key time codes"""
    keyframes = __gather_keyframes(action_group, export_settings)
    times = [k.seconds for k in keyframes]

    return gltf2_io.Accessor(
        buffer_view=gltf2_io_binary_data.BinaryData.from_list(
            times, gltf2_io_constants.ComponentType.Float),
        byte_offset=None,
        component_type=gltf2_io_constants.ComponentType.Float,
        count=len(times),
        extensions=None,
        extras=None,
        max=[max(times)],
        min=[min(times)],
        name=None,
        normalized=None,
        sparse=None,
        type=gltf2_io_constants.DataType.Scalar)
Exemple #8
0
def __gather_tangent(blender_primitive, export_settings):
    if export_settings['gltf_tangents']:
        if blender_primitive["attributes"].get('TANGENT') is not None:
            tangent = blender_primitive["attributes"]['TANGENT']
            return {
                "TANGENT": gltf2_io.Accessor(
                    buffer_view=gltf2_io_binary_data.BinaryData.from_list(tangent, gltf2_io_constants.ComponentType.Float),
                    byte_offset=None,
                    component_type=gltf2_io_constants.ComponentType.Float,
                    count=len(tangent) // gltf2_io_constants.DataType.num_elements(gltf2_io_constants.DataType.Vec4),
                    extensions=None,
                    extras=None,
                    max=None,
                    min=None,
                    name=None,
                    normalized=None,
                    sparse=None,
                    type=gltf2_io_constants.DataType.Vec4
                )
            }

    return {}
Exemple #9
0
def __gather_indices(blender_primitive, blender_mesh, modifiers,
                     export_settings):
    indices = blender_primitive['indices']

    # NOTE: Values used by some graphics APIs as "primitive restart" values are disallowed.
    # Specifically, the values 65535 (in UINT16) and 4294967295 (in UINT32) cannot be used as indices.
    # https://github.com/KhronosGroup/glTF/issues/1142
    # https://github.com/KhronosGroup/glTF/pull/1476/files
    # Also, UINT8 mode is not supported:
    # https://github.com/KhronosGroup/glTF/issues/1471
    max_index = max(indices)
    if max_index < 65535:
        component_type = gltf2_io_constants.ComponentType.UnsignedShort
    elif max_index < 4294967295:
        component_type = gltf2_io_constants.ComponentType.UnsignedInt
    else:
        print_console(
            'ERROR', 'A mesh contains too many vertices (' + str(max_index) +
            ') and needs to be split before export.')
        return None

    element_type = gltf2_io_constants.DataType.Scalar
    binary_data = gltf2_io_binary_data.BinaryData.from_list(
        indices, component_type)
    return gltf2_io.Accessor(
        buffer_view=binary_data,
        byte_offset=None,
        component_type=component_type,
        count=len(indices) //
        gltf2_io_constants.DataType.num_elements(element_type),
        extensions=None,
        extras=None,
        max=None,
        min=None,
        name=None,
        normalized=None,
        sparse=None,
        type=element_type)
def __encode_output_accessor(values, path):
    # Encodes a list of T, R, or S (Vector/Quaternion) values to an accessor.
    vals = [x for val in values for x in val]
    vals_data = array.array('f', vals).tobytes()
    name = {
        'translation': 'accessorAnimationPositions',
        'rotation': 'accessorAnimationRotations',
        'scale': 'accessorAnimationScales'
    }.get(path)
    return gltf2_io.Accessor(
        buffer_view=gltf2_io_binary_data.BinaryData(vals_data),
        component_type=gltf2_io_constants.ComponentType.Float,
        type=gltf2_io_constants.DataType.vec_type_from_num(len(values[0])),
        count=len(values),
        min=None,
        max=None,
        byte_offset=None,
        extensions=None,
        extras=None,
        name=name,
        normalized=None,
        sparse=None,
    )
Exemple #11
0
def array_to_accessor(array,
                      component_type,
                      data_type,
                      include_max_and_min=False):
    dtype = gltf2_io_constants.ComponentType.to_numpy_dtype(component_type)
    num_elems = gltf2_io_constants.DataType.num_elements(data_type)

    if type(array) is not np.ndarray:
        array = np.array(array, dtype=dtype)
        array = array.reshape(len(array) // num_elems, num_elems)

    if num_elems == 1 and len(array.shape) == 1:
        array = array.reshape(len(array), 1)

    assert array.dtype == dtype
    assert array.shape[1] == num_elems

    amax = None
    amin = None
    if include_max_and_min:
        amax = np.amax(array, axis=0).tolist()
        amin = np.amin(array, axis=0).tolist()

    return gltf2_io.Accessor(
        buffer_view=gltf2_io_binary_data.BinaryData(array.tobytes()),
        byte_offset=None,
        component_type=component_type,
        count=len(array),
        extensions=None,
        extras=None,
        max=amax,
        min=amin,
        name=None,
        normalized=None,
        sparse=None,
        type=data_type,
    )
def __gather_indices(blender_primitive, export_settings):
    indices = blender_primitive['indices']

    max_index = max(indices)
    if max_index < (1 << 8):
        component_type = gltf2_io_constants.ComponentType.UnsignedByte
    elif max_index < (1 << 16):
        component_type = gltf2_io_constants.ComponentType.UnsignedShort
    elif max_index < (1 << 32):
        component_type = gltf2_io_constants.ComponentType.UnsignedInt
    else:
        gltf2_io_debug.print_console('ERROR',
                                     'Invalid max_index: ' + str(max_index))
        return None

    if export_settings['gltf_force_indices']:
        component_type = gltf2_io_constants.ComponentType.from_legacy_define(
            export_settings['gltf_indices'])

    element_type = gltf2_io_constants.DataType.Scalar
    binary_data = gltf2_io_binary_data.BinaryData.from_list(
        indices, component_type)
    return gltf2_io.Accessor(
        buffer_view=binary_data,
        byte_offset=None,
        component_type=component_type,
        count=len(indices) //
        gltf2_io_constants.DataType.num_elements(element_type),
        extensions=None,
        extras=None,
        max=None,
        min=None,
        name=None,
        normalized=None,
        sparse=None,
        type=element_type)
Exemple #13
0
def __gather_texcoord(blender_primitive, export_settings):
    attributes = {}
    if export_settings['gltf_texcoords']:
        texcoord_index = 0
        texcoord_id = 'TEXCOORD_' + str(texcoord_index)
        while blender_primitive["attributes"].get(texcoord_id) is not None:
            texcoord = blender_primitive["attributes"][texcoord_id]
            attributes[texcoord_id] = gltf2_io.Accessor(
                buffer_view=gltf2_io_binary_data.BinaryData.from_list(texcoord, gltf2_io_constants.ComponentType.Float),
                byte_offset=None,
                component_type=gltf2_io_constants.ComponentType.Float,
                count=len(texcoord) // gltf2_io_constants.DataType.num_elements(gltf2_io_constants.DataType.Vec2),
                extensions=None,
                extras=None,
                max=None,
                min=None,
                name=None,
                normalized=None,
                sparse=None,
                type=gltf2_io_constants.DataType.Vec2
            )
            texcoord_index += 1
            texcoord_id = 'TEXCOORD_' + str(texcoord_index)
    return attributes
Exemple #14
0
def __gather_colors(blender_primitive, export_settings):
    attributes = {}
    if export_settings['gltf_colors']:
        color_index = 0
        color_id = 'COLOR_' + str(color_index)
        while blender_primitive["attributes"].get(color_id) is not None:
            internal_color = blender_primitive["attributes"][color_id]
            attributes[color_id] = gltf2_io.Accessor(
                buffer_view=gltf2_io_binary_data.BinaryData.from_list(internal_color, gltf2_io_constants.ComponentType.Float),
                byte_offset=None,
                component_type=gltf2_io_constants.ComponentType.Float,
                count=len(internal_color) // gltf2_io_constants.DataType.num_elements(gltf2_io_constants.DataType.Vec4),
                extensions=None,
                extras=None,
                max=None,
                min=None,
                name=None,
                normalized=None,
                sparse=None,
                type=gltf2_io_constants.DataType.Vec4
            )
            color_index += 1
            color_id = 'COLOR_' + str(color_index)
    return attributes
Exemple #15
0
def __gather_output(channels: typing.Tuple[bpy.types.FCurve], parent_inverse,
                    blender_object_if_armature: typing.Optional[
                        bpy.types.Object],
                    export_settings) -> gltf2_io.Accessor:
    """Gather the data of the keyframes."""
    keyframes = gltf2_blender_gather_animation_sampler_keyframes.gather_keyframes(
        channels, export_settings)

    target_datapath = channels[0].data_path

    transform = parent_inverse

    is_yup = export_settings[gltf2_blender_export_keys.YUP]

    object_path = get_target_object_path(target_datapath)
    is_armature_animation = blender_object_if_armature is not None and object_path != ""
    if is_armature_animation:
        bone = blender_object_if_armature.path_resolve(object_path)
        if isinstance(bone, bpy.types.PoseBone):
            if bone.parent is not None:
                parent_transform = bone.parent.bone.matrix_local
                transform = gltf2_blender_math.multiply(
                    transform, parent_transform.inverted())
                # if not is_yup:
                #     transform = gltf2_blender_math.multiply(transform, gltf2_blender_math.to_zup())
            else:
                # only apply the y-up conversion to root bones, as child bones already are in the y-up space
                if is_yup:
                    transform = gltf2_blender_math.multiply(
                        transform, gltf2_blender_math.to_yup())
            local_transform = bone.bone.matrix_local
            transform = gltf2_blender_math.multiply(transform, local_transform)

    values = []
    for keyframe in keyframes:
        # Transform the data and extract
        value = gltf2_blender_math.transform(keyframe.value, target_datapath,
                                             transform)
        if is_yup and not is_armature_animation:
            value = gltf2_blender_math.swizzle_yup(value, target_datapath)
        keyframe_value = gltf2_blender_math.mathutils_to_gltf(value)
        if keyframe.in_tangent is not None:
            in_tangent = gltf2_blender_math.transform(keyframe.in_tangent,
                                                      target_datapath,
                                                      transform)
            if is_yup and blender_object_if_armature is None:
                in_tangent = gltf2_blender_math.swizzle_yup(
                    in_tangent, target_datapath)
            keyframe_value = gltf2_blender_math.mathutils_to_gltf(
                in_tangent) + keyframe_value
        if keyframe.out_tangent is not None:
            out_tangent = gltf2_blender_math.transform(keyframe.out_tangent,
                                                       target_datapath,
                                                       transform)
            if is_yup and blender_object_if_armature is None:
                out_tangent = gltf2_blender_math.swizzle_yup(
                    out_tangent, target_datapath)
            keyframe_value = keyframe_value + gltf2_blender_math.mathutils_to_gltf(
                out_tangent)
        values += keyframe_value

    component_type = gltf2_io_constants.ComponentType.Float
    if get_target_property_name(target_datapath) == "value":
        # channels with 'weight' targets must have scalar accessors
        data_type = gltf2_io_constants.DataType.Scalar
    else:
        data_type = gltf2_io_constants.DataType.vec_type_from_num(
            len(keyframes[0].value))

    return gltf2_io.Accessor(
        buffer_view=gltf2_io_binary_data.BinaryData.from_list(
            values, component_type),
        byte_offset=None,
        component_type=component_type,
        count=len(values) //
        gltf2_io_constants.DataType.num_elements(data_type),
        extensions=None,
        extras=None,
        max=None,
        min=None,
        name=None,
        normalized=None,
        sparse=None,
        type=data_type)
Exemple #16
0
def __gather_skins(blender_primitive, export_settings, asobo_vertex_type):
    attributes = {}
    if export_settings[gltf2_blender_export_keys.SKINS]:
        bone_set_index = 0
        joint_id = 'JOINTS_' + str(bone_set_index)
        weight_id = 'WEIGHTS_' + str(bone_set_index)
        while blender_primitive["attributes"].get(
                joint_id) and blender_primitive["attributes"].get(weight_id):
            if bone_set_index >= 1:
                if not export_settings['gltf_all_vertex_influences']:
                    gltf2_io_debug.print_console(
                        "WARNING",
                        "There are more than 4 joint vertex influences."
                        "The 4 with highest weight will be used (and normalized)."
                    )
                    break

            # joints
            internal_joint = blender_primitive["attributes"][joint_id]
            joint = gltf2_io.Accessor(
                buffer_view=internal_joint,
                byte_offset=None,
                component_type=gltf2_io_constants.ComponentType.UnsignedShort,
                count=len(internal_joint) //
                gltf2_io_constants.DataType.num_elements(
                    gltf2_io_constants.DataType.Vec4),
                extensions=None,
                extras=None,
                max=None,
                min=None,
                name=None,
                normalized=None,
                sparse=None,
                type=gltf2_io_constants.DataType.Vec4)
            attributes[joint_id] = joint

            # weights
            internal_weight = blender_primitive["attributes"][weight_id]
            # normalize first 4 weights, when not exporting all influences, except BLEND1 since it's already technically normalized
            if not export_settings[
                    'gltf_all_vertex_influences'] and not asobo_vertex_type == 'BLEND1':
                for idx in range(0, len(internal_weight), 4):
                    weight_slice = internal_weight[idx:idx + 4]
                    total = sum(weight_slice)
                    if total > 0:
                        factor = 1.0 / total
                        internal_weight[idx:idx + 4] = [
                            w * factor for w in weight_slice
                        ]
            weight_data_type = gltf2_io_constants.DataType.Scalar if asobo_vertex_type == 'BLEND1' else gltf2_io_constants.DataType.Vec4
            component_type = gltf2_io_constants.ComponentType.Float if asobo_vertex_type == 'BLEND1' else gltf2_io_constants.ComponentType.UnsignedShort
            weight = gltf2_io.Accessor(
                buffer_view=internal_weight if asobo_vertex_type == 'BLEND1'
                else [round(x * 65535.0) for x in internal_weight],
                byte_offset=None,
                component_type=component_type,
                count=len(internal_weight) //
                gltf2_io_constants.DataType.num_elements(weight_data_type),
                extensions=None,
                extras=None,
                max=None,
                min=None,
                name=None,
                normalized=True
                if component_type == 5121 or component_type == 5123 else
                None,  # only normalized for unsigned bytes and unsigned shorts
                sparse=None,
                type=weight_data_type)
            attributes[weight_id] = weight

            bone_set_index += 1
            joint_id = 'JOINTS_' + str(bone_set_index)
            weight_id = 'WEIGHTS_' + str(bone_set_index)
    return attributes
Exemple #17
0
def __gather_targets(blender_primitive, blender_mesh, modifiers,
                     export_settings):
    if export_settings[MORPH]:
        targets = []
        if blender_mesh.shape_keys is not None:
            morph_index = 0
            for blender_shape_key in blender_mesh.shape_keys.key_blocks:
                if blender_shape_key != blender_shape_key.relative_key:

                    target_position_id = 'MORPH_POSITION_' + str(morph_index)
                    target_normal_id = 'MORPH_NORMAL_' + str(morph_index)
                    target_tangent_id = 'MORPH_TANGENT_' + str(morph_index)

                    if blender_primitive["attributes"].get(target_position_id):
                        target = {}
                        internal_target_position = blender_primitive[
                            "attributes"][target_position_id]
                        binary_data = gltf2_io_binary_data.BinaryData.from_list(
                            internal_target_position,
                            gltf2_io_constants.ComponentType.Float)
                        target["POSITION"] = gltf2_io.Accessor(
                            buffer_view=binary_data,
                            byte_offset=None,
                            component_type=gltf2_io_constants.ComponentType.
                            Float,
                            count=len(internal_target_position) //
                            gltf2_io_constants.DataType.num_elements(
                                gltf2_io_constants.DataType.Vec3),
                            extensions=None,
                            extras=None,
                            max=gltf2_blender_utils.max_components(
                                internal_target_position,
                                gltf2_io_constants.DataType.Vec3),
                            min=gltf2_blender_utils.min_components(
                                internal_target_position,
                                gltf2_io_constants.DataType.Vec3),
                            name=None,
                            normalized=None,
                            sparse=None,
                            type=gltf2_io_constants.DataType.Vec3)

                        if export_settings[NORMALS] \
                                and export_settings[MORPH_NORMAL] \
                                and blender_primitive["attributes"].get(target_normal_id):

                            internal_target_normal = blender_primitive[
                                "attributes"][target_normal_id]
                            binary_data = gltf2_io_binary_data.BinaryData.from_list(
                                internal_target_normal,
                                gltf2_io_constants.ComponentType.Float,
                            )
                            target['NORMAL'] = gltf2_io.Accessor(
                                buffer_view=binary_data,
                                byte_offset=None,
                                component_type=gltf2_io_constants.
                                ComponentType.Float,
                                count=len(internal_target_normal) //
                                gltf2_io_constants.DataType.num_elements(
                                    gltf2_io_constants.DataType.Vec3),
                                extensions=None,
                                extras=None,
                                max=None,
                                min=None,
                                name=None,
                                normalized=None,
                                sparse=None,
                                type=gltf2_io_constants.DataType.Vec3)

                        if export_settings[TANGENTS] \
                                and export_settings[MORPH_TANGENT] \
                                and blender_primitive["attributes"].get(target_tangent_id):
                            internal_target_tangent = blender_primitive[
                                "attributes"][target_tangent_id]
                            binary_data = gltf2_io_binary_data.BinaryData.from_list(
                                internal_target_tangent,
                                gltf2_io_constants.ComponentType.Float,
                            )
                            target['TANGENT'] = gltf2_io.Accessor(
                                buffer_view=binary_data,
                                byte_offset=None,
                                component_type=gltf2_io_constants.
                                ComponentType.Float,
                                count=len(internal_target_tangent) //
                                gltf2_io_constants.DataType.num_elements(
                                    gltf2_io_constants.DataType.Vec3),
                                extensions=None,
                                extras=None,
                                max=None,
                                min=None,
                                name=None,
                                normalized=None,
                                sparse=None,
                                type=gltf2_io_constants.DataType.Vec3)
                        targets.append(target)
                        morph_index += 1
        return targets
    return None
def __gather_skins(blender_primitive, export_settings):
    attributes = {}
    if export_settings[gltf2_blender_export_keys.SKINS]:
        bone_set_index = 0
        joint_id = 'JOINTS_' + str(bone_set_index)
        weight_id = 'WEIGHTS_' + str(bone_set_index)
        while blender_primitive["attributes"].get(
                joint_id) and blender_primitive["attributes"].get(weight_id):
            if bone_set_index >= 1:
                if not export_settings['gltf_all_vertex_influences']:
                    gltf2_io_debug.print_console(
                        "WARNING",
                        "There are more than 4 joint vertex influences."
                        "The 4 with highest weight will be used (and normalized)."
                    )
                    break

            # joints
            internal_joint = blender_primitive["attributes"][joint_id]
            joint = gltf2_io.Accessor(
                buffer_view=gltf2_io_binary_data.BinaryData.from_list(
                    internal_joint,
                    gltf2_io_constants.ComponentType.UnsignedShort),
                byte_offset=None,
                component_type=gltf2_io_constants.ComponentType.UnsignedShort,
                count=len(internal_joint) //
                gltf2_io_constants.DataType.num_elements(
                    gltf2_io_constants.DataType.Vec4),
                extensions=None,
                extras=None,
                max=None,
                min=None,
                name=None,
                normalized=None,
                sparse=None,
                type=gltf2_io_constants.DataType.Vec4)
            attributes[joint_id] = joint

            # weights
            internal_weight = blender_primitive["attributes"][weight_id]
            # normalize first 4 weights, when not exporting all influences
            if not export_settings['gltf_all_vertex_influences']:
                for idx in range(0, len(internal_weight), 4):
                    weight_slice = internal_weight[idx:idx + 4]
                    total = sum(weight_slice)
                    if total > 0:
                        factor = 1.0 / total
                        internal_weight[idx:idx + 4] = [
                            w * factor for w in weight_slice
                        ]

            weight = gltf2_io.Accessor(
                buffer_view=gltf2_io_binary_data.BinaryData.from_list(
                    internal_weight, gltf2_io_constants.ComponentType.Float),
                byte_offset=None,
                component_type=gltf2_io_constants.ComponentType.Float,
                count=len(internal_weight) //
                gltf2_io_constants.DataType.num_elements(
                    gltf2_io_constants.DataType.Vec4),
                extensions=None,
                extras=None,
                max=None,
                min=None,
                name=None,
                normalized=None,
                sparse=None,
                type=gltf2_io_constants.DataType.Vec4)
            attributes[weight_id] = weight

            bone_set_index += 1
            joint_id = 'JOINTS_' + str(bone_set_index)
            weight_id = 'WEIGHTS_' + str(bone_set_index)
    return attributes
def __gather_output(channels: typing.Tuple[bpy.types.FCurve],
                    parent_inverse,
                    blender_object_if_armature: typing.Optional[bpy.types.Object],
                    non_keyed_values: typing.Tuple[typing.Optional[float]],
                    bake_bone: typing.Union[str, None],
                    bake_channel: typing.Union[str, None],
                    bake_range_start,
                    bake_range_end,
                    action_name,
                    driver_obj,
                    export_settings
                    ) -> gltf2_io.Accessor:
    """Gather the data of the keyframes."""
    keyframes = gltf2_blender_gather_animation_sampler_keyframes.gather_keyframes(blender_object_if_armature,
                                                                                  channels,
                                                                                  non_keyed_values,
                                                                                  bake_bone,
                                                                                  bake_channel,
                                                                                  bake_range_start,
                                                                                  bake_range_end,
                                                                                  action_name,
                                                                                  driver_obj,
                                                                                  export_settings)
    if bake_bone is not None:
        target_datapath = "pose.bones['" + bake_bone + "']." + bake_channel
    else:
        target_datapath = [c for c in channels if c is not None][0].data_path

    is_yup = export_settings[gltf2_blender_export_keys.YUP]

    # bone animations need to be handled differently as they are in a different coordinate system
    if bake_bone is None:
        object_path = get_target_object_path(target_datapath)
    else:
        object_path = None
    is_armature_animation = bake_bone is not None or (blender_object_if_armature is not None and object_path != "")

    if is_armature_animation:
        if bake_bone is None:
            bone = gltf2_blender_get.get_object_from_datapath(blender_object_if_armature, object_path)
        else:
            bone = blender_object_if_armature.pose.bones[bake_bone]
        if isinstance(bone, bpy.types.PoseBone):
            if bone.parent is None:
                axis_basis_change = mathutils.Matrix.Identity(4)
                if export_settings[gltf2_blender_export_keys.YUP]:
                    axis_basis_change = mathutils.Matrix(
                        ((1.0, 0.0, 0.0, 0.0),
                         (0.0, 0.0, 1.0, 0.0),
                         (0.0, -1.0, 0.0, 0.0),
                         (0.0, 0.0, 0.0, 1.0)))
                correction_matrix_local = axis_basis_change @ bone.bone.matrix_local
            else:
                correction_matrix_local = (
                    bone.parent.bone.matrix_local.inverted() @
                    bone.bone.matrix_local
                )

            transform = correction_matrix_local
        else:
            transform = mathutils.Matrix.Identity(4)
    else:
        transform = parent_inverse

    values = []
    for keyframe in keyframes:
        # Transform the data and build gltf control points
        value = gltf2_blender_math.transform(keyframe.value, target_datapath, transform)
        if is_yup and not is_armature_animation:
            value = gltf2_blender_math.swizzle_yup(value, target_datapath)
        keyframe_value = gltf2_blender_math.mathutils_to_gltf(value)

        if keyframe.in_tangent is not None:
            # we can directly transform the tangent as it currently is represented by a control point
            in_tangent = gltf2_blender_math.transform(keyframe.in_tangent, target_datapath, transform)
            if is_yup and blender_object_if_armature is None:
                in_tangent = gltf2_blender_math.swizzle_yup(in_tangent, target_datapath)
            # the tangent in glTF is relative to the keyframe value
            if not isinstance(value, list):
                in_tangent = value - in_tangent
            else:
                in_tangent = [value[i] - in_tangent[i] for i in range(len(value))]
            keyframe_value = gltf2_blender_math.mathutils_to_gltf(in_tangent) + keyframe_value  # append

        if keyframe.out_tangent is not None:
            # we can directly transform the tangent as it currently is represented by a control point
            out_tangent = gltf2_blender_math.transform(keyframe.out_tangent, target_datapath, transform)
            if is_yup and blender_object_if_armature is None:
                out_tangent = gltf2_blender_math.swizzle_yup(out_tangent, target_datapath)
            # the tangent in glTF is relative to the keyframe value
            if not isinstance(value, list):
                out_tangent = value - out_tangent
            else:
                out_tangent = [value[i] - out_tangent[i] for i in range(len(value))]
            keyframe_value = keyframe_value + gltf2_blender_math.mathutils_to_gltf(out_tangent)  # append

        values += keyframe_value

    # store the keyframe data in a binary buffer
    component_type = gltf2_io_constants.ComponentType.Float
    if get_target_property_name(target_datapath) == "value":
        # channels with 'weight' targets must have scalar accessors
        data_type = gltf2_io_constants.DataType.Scalar
    else:
        data_type = gltf2_io_constants.DataType.vec_type_from_num(len(keyframes[0].value))

    return gltf2_io.Accessor(
        buffer_view=gltf2_io_binary_data.BinaryData.from_list(values, component_type),
        byte_offset=None,
        component_type=component_type,
        count=len(values) // gltf2_io_constants.DataType.num_elements(data_type),
        extensions=None,
        extras=None,
        max=None,
        min=None,
        name=None,
        normalized=None,
        sparse=None,
        type=data_type
    )
Exemple #20
0
def custom_data_array_to_accessor(array):
    if type(array) is not np.ndarray:
        array = np.array(array, dtype=np.float32)

    assert array.dtype == np.float32
    assert len(array.shape) == 1

    # Calculate how big a sparse array would be and switch to it if smaller

    indices_nonzero = np.nonzero(array)[0]
    num_nonzero = len(indices_nonzero)

    if num_nonzero == 0:
        return gltf2_io.Accessor(
            count=len(array),
            component_type=gltf2_io_constants.ComponentType.Float,
            type=gltf2_io_constants.DataType.Scalar,
            buffer_view=None,
            byte_offset=None,
            extensions=None,
            extras=None,
            max=None,
            min=None,
            name=None,
            normalized=None,
            sparse=None,
        )

    index_size = (1 if indices_nonzero[-1] < 256 else
                  2 if indices_nonzero[-1] < 65536 else 4)
    value_size = 4  # float32

    dense_bin_size = len(array) * value_size
    sparse_bin_size = num_nonzero * (index_size + value_size)
    bin_size_increase = sparse_bin_size - dense_bin_size
    json_size_increase = 160  # approximate
    net_size_increase = bin_size_increase + json_size_increase

    if net_size_increase >= 0:
        # Dense is better
        return array_to_accessor(
            array,
            component_type=gltf2_io_constants.ComponentType.Float,
            data_type=gltf2_io_constants.DataType.Scalar,
        )

    index_type = (gltf2_io_constants.ComponentType.UnsignedByte
                  if index_size == 1 else
                  gltf2_io_constants.ComponentType.UnsignedShort if index_size
                  == 2 else gltf2_io_constants.ComponentType.UnsignedInt)
    index_dtype = gltf2_io_constants.ComponentType.to_numpy_dtype(index_type)
    indices_nonzero = indices_nonzero.astype(index_dtype)
    values_nonzero = array[indices_nonzero]

    return gltf2_io.Accessor(
        buffer_view=None,
        byte_offset=None,
        component_type=gltf2_io_constants.ComponentType.Float,
        count=len(array),
        extensions=None,
        extras=None,
        max=None,
        min=None,
        name=None,
        normalized=None,
        type=gltf2_io_constants.DataType.Scalar,
        sparse=gltf2_io.AccessorSparse(
            count=num_nonzero,
            indices=gltf2_io.AccessorSparseIndices(
                buffer_view=gltf2_io_binary_data.BinaryData(
                    indices_nonzero.tobytes()),
                component_type=index_type,
                byte_offset=None,
                extensions=None,
                extras=None,
            ),
            values=gltf2_io.AccessorSparseValues(
                buffer_view=gltf2_io_binary_data.BinaryData(
                    values_nonzero.tobytes()),
                byte_offset=None,
                extensions=None,
                extras=None,
            ),
            extensions=None,
            extras=None,
        ),
    )