/**
 *
 * @param {number[]|Float32Array} destination
 * @param {number} destination_offset
 * @param {number[]|Float32Array} positions
 * @param {number} vertex_count
 * @param {number[]|Float32Array} skin_weights_array
 * @param {number[]|Uint32Array} skin_indices_array
 * @param {number[]|Float32Array} bone_matrices 4x3 transform matrices
 * @param {number[]|Float32Array} m4_bind
 * @param {number[]|Float32Array} m4_bind_inv
 * @param {number} [bones_per_vertex]
 */
export function mesh_apply_skeletal_vertex_skinning(
    destination,
    destination_offset,
    positions,
    vertex_count,
    skin_weights_array,
    skin_indices_array,
    bone_matrices,
    m4_bind,
    m4_bind_inv,
    bones_per_vertex = 4
) {

    let index, i, x, y, z;

    for (index = 0; index < vertex_count; index++) {

        const index3 = index * 3;

        //read vertex position
        const px = positions[index3];
        const py = positions[index3 + 1];
        const pz = positions[index3 + 2];

        //apply bind matrix transform to vertex
        const bx = m4_bind[0] * px + m4_bind[4] * py + m4_bind[8] * pz + m4_bind[12];
        const by = m4_bind[1] * px + m4_bind[5] * py + m4_bind[9] * pz + m4_bind[13];
        const bz = m4_bind[2] * px + m4_bind[6] * py + m4_bind[10] * pz + m4_bind[14];

        x = 0;
        y = 0;
        z = 0;

        for (i = 0; i < bones_per_vertex; i++) {

            const skin_index = index * bones_per_vertex + i;

            const skin_weight = skin_weights_array[skin_index];

            if (skin_weight !== 0) {

                const boneIndex = skin_indices_array[skin_index];

                const bdIndex = boneIndex * 12;

                const sx = bone_matrices[bdIndex] * bx + bone_matrices[bdIndex + 1] * by + bone_matrices[bdIndex + 2] * bz + bone_matrices[bdIndex + 3];
                const sy = bone_matrices[bdIndex + 4] * bx + bone_matrices[bdIndex + 5] * by + bone_matrices[bdIndex + 6] * bz + bone_matrices[bdIndex + 7];
                const sz = bone_matrices[bdIndex + 8] * bx + bone_matrices[bdIndex + 9] * by + bone_matrices[bdIndex + 10] * bz + bone_matrices[bdIndex + 11];

                const tx = sx * skin_weight;
                const ty = sy * skin_weight;
                const tz = sz * skin_weight;

                x += tx;
                y += ty;
                z += tz;

            }
        }

        //apply inverse bind matrix
        const wx = m4_bind_inv[0] * x + m4_bind_inv[4] * y + m4_bind_inv[8] * z + m4_bind_inv[12];
        const wy = m4_bind_inv[1] * x + m4_bind_inv[5] * y + m4_bind_inv[9] * z + m4_bind_inv[13];
        const wz = m4_bind_inv[2] * x + m4_bind_inv[6] * y + m4_bind_inv[10] * z + m4_bind_inv[14];

        const destination_address = destination_offset + index3;

        destination[destination_address] = wx;
        destination[destination_address + 1] = wy;
        destination[destination_address + 2] = wz;
    }
}