rusting_engine 2.7.0

Vulkan 3D game engine with GPU-accelerated physics for massive physics-heavy scenes
#version 450

// Deforms one mesh: adds its blend shapes, then blends four joints per
// vertex, the same steps as `runtime::morph_mesh` and `skin_mesh`. See
// `rendering::gpu_skinning`.

layout(local_size_x = 64) in;

// `SceneVertex` in 16 words: position, normal, uv and tangent as floats,
// then the color as four halves, the vertex data word and a spare word.
// Words, so the halves and data bits copy through unchanged.
layout(set = 0, binding = 0) readonly buffer Base { uint base[]; };
layout(set = 0, binding = 1) writeonly buffer Out { uint deformed[]; };
layout(set = 0, binding = 2) readonly buffer Joints { uvec4 joints[]; };
layout(set = 0, binding = 3) readonly buffer Weights { vec4 weights[]; };
// Per blend shape, per vertex: position offset, then normal offset.
layout(set = 0, binding = 4) readonly buffer Morph { float morph[]; };
layout(set = 0, binding = 5) readonly buffer Matrices { mat4 matrices[]; };
layout(set = 0, binding = 6) readonly buffer Shapes { float shape_weights[]; };

layout(push_constant) uniform Counts {
    // Vertices, joint matrices, blend shapes, 1 when a shape weight is
    // not zero.
    uvec4 counts;
};

const float EPSILON = 1.1920929e-7;

vec3 turn(vec3 value, mat3 by) {
    vec3 turned = by * value;
    float length_ = length(turned);
    return length_ > EPSILON ? turned / length_ : value;
}

void main() {
    uint v = gl_GlobalInvocationID.x;
    if (v >= counts.x) {
        return;
    }
    uint at = v * 16;
    vec3 position =
        uintBitsToFloat(uvec3(base[at], base[at + 1], base[at + 2]));
    vec3 normal =
        uintBitsToFloat(uvec3(base[at + 3], base[at + 4], base[at + 5]));
    vec3 tangent =
        uintBitsToFloat(uvec3(base[at + 8], base[at + 9], base[at + 10]));
    for (uint shape = 0; shape < counts.z; shape++) {
        float weight = shape_weights[shape];
        if (weight == 0.0) {
            continue;
        }
        uint row = (shape * counts.x + v) * 6;
        position += vec3(morph[row], morph[row + 1], morph[row + 2]) * weight;
        normal +=
            vec3(morph[row + 3], morph[row + 4], morph[row + 5]) * weight;
    }
    if (counts.w != 0) {
        float length_ = length(normal);
        normal = length_ > EPSILON ? normal / length_ : normal;
    }
    vec4 amounts = weights[v];
    float total = amounts.x + amounts.y + amounts.z + amounts.w;
    if (counts.y != 0 && total > EPSILON) {
        mat4 blend = mat4(0.0);
        for (uint i = 0; i < 4; i++) {
            uint joint = joints[v][i];
            if (joint < counts.y) {
                blend += matrices[joint] * (amounts[i] / total);
            }
        }
        mat3 linear = mat3(blend);
        mat3 normals =
            determinant(linear) != 0.0 ? transpose(inverse(linear)) : linear;
        vec4 moved = blend * vec4(position, 1.0);
        position = moved.w != 0.0 ? moved.xyz / moved.w : moved.xyz;
        normal = turn(normal, normals);
        tangent = turn(tangent, linear);
    }
    for (uint i = 0; i < 16; i++) {
        deformed[at + i] = base[at + i];
    }
    uvec3 moved = floatBitsToUint(position);
    uvec3 turned = floatBitsToUint(normal);
    uvec3 bent = floatBitsToUint(tangent);
    deformed[at] = moved.x;
    deformed[at + 1] = moved.y;
    deformed[at + 2] = moved.z;
    deformed[at + 3] = turned.x;
    deformed[at + 4] = turned.y;
    deformed[at + 5] = turned.z;
    deformed[at + 8] = bent.x;
    deformed[at + 9] = bent.y;
    deformed[at + 10] = bent.z;
}