#version 330 core layout (location = 0) in vec3 aPos; layout (location = 1) in vec3 aNormal; layout (location = 2) in vec2 aTexCoord; out vec3 FragPos; out vec3 Normal; out vec2 TexCoord; uniform mat4 proj; uniform mat4 view; uniform mat4 modl; uniform int uvMode = 0; // 0 = stretch, 1 = tile uniform vec3 tileScale = vec3(1.0); uniform vec2 uvOffset = vec2(0.0); // Same direction as transpose(inverse(m)) for normals, without the per vertex inverse mat3 CofactorMatrix(mat3 m) { return mat3(cross(m[1], m[2]), cross(m[2], m[0]), cross(m[0], m[1])); } // Tiling ignores the mesh's uvs and projects from whichever axis the surface faces, // so every face of a stretched cube repeats at the same size vec2 SurfaceUV() { if (uvMode == 0) return aTexCoord + uvOffset; // Unscale the normal first, so a stretched mesh picks the axis it faces in the world vec3 n = abs(aNormal / max(tileScale, vec3(1e-4))); vec3 p = aPos * tileScale; if (n.y >= n.x && n.y >= n.z) return p.xz + uvOffset; if (n.x >= n.z) return p.zy + uvOffset; return p.xy + uvOffset; } void main() { vec4 worldPos = modl * vec4(aPos, 1.0); FragPos = worldPos.xyz; Normal = normalize(CofactorMatrix(mat3(modl)) * aNormal); TexCoord = SurfaceUV(); gl_Position = proj * view * worldPos; }