three 0.3.0

Three.js inspired 3D engine in Rust
Documentation
#version 150 core
#define MAX_TARGETS 8U
#include <globals>

const int DISPLACEMENT_BUFFER = 1 << 5;

in vec4 a_Position;
in vec2 a_TexCoord;
in vec4 a_Normal;
in vec4 a_Tangent;
in ivec4 a_JointIndices;
in vec4 a_JointWeights;

out vec3 v_Position;
out vec2 v_TexCoord;
out mat3 v_Tbn;

in vec4 i_World0;
in vec4 i_World1;
in vec4 i_World2;

// Toggles displacement contributions to `a_Position/a_Normal/a_Tangent`.
struct DisplacementContribution {
    // position: 1.0 if morph target weights should influence a_Position
    // normal: 1.0 if morph target weights should influence a_Normal
    // tangent: 1.0 if morph target weights should influence a_Tangent
    // weight: The weight to be applied.
    float position, normal, tangent, weight;
};

layout(std140) uniform b_DisplacementContributions {
    DisplacementContribution u_DisplacementContributions[MAX_TARGETS];
};

layout(std140) uniform b_PbrParams {
    vec4 u_BaseColorFactor;
    vec3 u_Camera;
    vec3 u_EmissiveFactor;
    vec2 u_MetallicRoughnessValues;
    float u_NormalScale;
    float u_OcclusionStrength;
    int u_PbrFlags;
};

uniform samplerBuffer b_JointTransforms;
uniform sampler2D u_Displacements;

//TODO: store each join transform in 3 vectors, similar to `i_WorldX`

mat4 fetch_joint_transform(int i) {
    //Note: has to match `render::VECS_PER_BONE`
    vec4 row0 = texelFetch(b_JointTransforms, 3 * i + 0);
    vec4 row1 = texelFetch(b_JointTransforms, 3 * i + 1);
    vec4 row2 = texelFetch(b_JointTransforms, 3 * i + 2);

    return transpose(mat4(row0, row1, row2, vec4(0.0, 0.0, 0.0, 1.0)));
}

mat4 compute_skin_transform() {
    return
        a_JointWeights.x * fetch_joint_transform(a_JointIndices.x) +
        a_JointWeights.y * fetch_joint_transform(a_JointIndices.y) +
        a_JointWeights.z * fetch_joint_transform(a_JointIndices.z) +
        a_JointWeights.w * fetch_joint_transform(a_JointIndices.w);
}

bool available(int flag) {
    return (u_PbrFlags & flag) == flag;
}

void main() {
    vec3 local_position = a_Position.xyz;
    vec3 local_normal = a_Normal.xyz;
    vec3 local_tangent = a_Tangent.xyz;

    if (available(DISPLACEMENT_BUFFER)) {
        uint num_targets = uvec2(textureSize(u_Displacements, 0)).y / 3U;
        for (uint i = 0U; i < min(num_targets, MAX_TARGETS); ++i) {
            DisplacementContribution disp = u_DisplacementContributions[i];
            if (disp.weight == 0.0) continue;
            local_position += disp.position * disp.weight * texelFetch(u_Displacements, ivec2(gl_VertexID, 3U*i+0U), 0).xyz;
            local_normal   += disp.normal   * disp.weight * texelFetch(u_Displacements, ivec2(gl_VertexID, 3U*i+1U), 0).xyz;
            local_tangent  += disp.tangent  * disp.weight * texelFetch(u_Displacements, ivec2(gl_VertexID, 3U*i+2U), 0).xyz;
        }
    }

    mat4 mx_world = transpose(mat4(i_World0, i_World1, i_World2, vec4(0.0, 0.0, 0.0, 1.0)));
    mat4 mx_mvp = u_ViewProj * mx_world;
    mat4 mx_skin = compute_skin_transform();

    vec4 world_position = mx_world * vec4(local_position, a_Position.w);
    vec3 world_normal = mat3(mx_world) * normalize(local_normal);
    vec3 world_tangent = mat3(mx_world) * normalize(local_tangent);
    vec3 world_bitangent = cross(world_normal, world_tangent) * a_Tangent.w;

    v_Tbn = mat3(world_tangent, world_bitangent, world_normal);
    v_Position = world_position.xyz / world_position.w;
    v_TexCoord = a_TexCoord;

    gl_Position = mx_mvp * mx_skin * vec4(local_position, a_Position.w);
}