first commit

This commit is contained in:
David Ali
2026-01-09 13:50:52 +01:00
commit 9f87935db1
24 changed files with 7925 additions and 0 deletions

343
src/Asset.cpp Normal file
View File

@@ -0,0 +1,343 @@
#include "Asset.hpp"
#include <cmath>
#include <array>
#include <glm/glm.hpp>
namespace asset {
Mesh make_unit_quad(float size) {
Mesh m;
const float h = size * 0.5f;
m.positions = {-size, -size, 0.0f, size, -size, 0.0f,
size, size, 0.0f, -size, size, 0.0f};
m.uvs = {0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f};
m.indices = {0, 1, 2, 2, 3, 0};
return m;
}
Mesh make_unit_cube(float size) {
Mesh m;
const float h = size * 0.5f;
// 6 faces × 4 verts (CCW when viewed from outside)
const float P[] = {
// +X (right)
h,-h,-h, h, h,-h, h, h, h, h,-h, h,
// -X (left)
-h,-h, h, -h, h, h, -h, h,-h, -h,-h,-h,
// +Y (top) *** fixed winding ***
-h, h,-h, -h, h, h, h, h, h, h, h,-h,
// -Y (bottom) *** fixed winding ***
-h,-h,-h, h,-h,-h, h,-h, h, -h,-h, h,
// +Z (front)
-h,-h, h, h,-h, h, h, h, h, -h, h, h,
// -Z (back)
h,-h,-h, -h,-h,-h, -h, h,-h, h, h,-h
};
const float N[] = {
// +X
1,0,0, 1,0,0, 1,0,0, 1,0,0,
// -X
-1,0,0, -1,0,0, -1,0,0, -1,0,0,
// +Y
0,1,0, 0,1,0, 0,1,0, 0,1,0,
// -Y
0,-1,0, 0,-1,0, 0,-1,0, 0,-1,0,
// +Z
0,0,1, 0,0,1, 0,0,1, 0,0,1,
// -Z
0,0,-1, 0,0,-1, 0,0,-1, 0,0,-1
};
// 0..1 quad per face; orientation is fine with the corrected positions
const float UV[] = {
0,0, 1,0, 1,1, 0,1,
0,0, 1,0, 1,1, 0,1,
0,0, 1,0, 1,1, 0,1,
0,0, 1,0, 1,1, 0,1,
0,0, 1,0, 1,1, 0,1,
0,0, 1,0, 1,1, 0,1
};
// 2 triangles per face
const uint32_t I[] = {
0, 1, 2, 0, 2, 3, // +X
4, 5, 6, 4, 6, 7, // -X
8, 9, 10, 8, 10, 11, // +Y (fixed)
12, 13, 14, 12, 14, 15, // -Y (fixed)
16, 17, 18, 16, 18, 19, // +Z
20, 21, 22, 20, 22, 23 // -Z
};
m.positions.assign(std::begin(P), std::end(P));
m.normals.assign (std::begin(N), std::end(N));
m.uvs.assign (std::begin(UV), std::end(UV));
m.indices.assign (std::begin(I), std::end(I));
return m;
}
Mesh make_unit_ico(float size){
Mesh m;
// Golden ratio
const float phi = (1.0f + std::sqrt(5.0f)) * 0.5f;
// Canonical 12 vertices (right-handed)
const std::array<glm::vec3, 12> V = {
glm::vec3(-1, phi, 0),
glm::vec3( 1, phi, 0),
glm::vec3(-1, -phi, 0),
glm::vec3( 1, -phi, 0),
glm::vec3( 0, -1, phi),
glm::vec3( 0, 1, phi),
glm::vec3( 0, -1, -phi),
glm::vec3( 0, 1, -phi),
glm::vec3( phi, 0, -1),
glm::vec3( phi, 0, 1),
glm::vec3(-phi, 0, -1),
glm::vec3(-phi, 0, 1)
};
// Normalize to unit sphere and scale to radius = size * 0.5
const float r = size * 0.5f;
m.positions.reserve(12 * 3);
m.normals .reserve(12 * 3);
for (auto p : V) {
glm::vec3 n = glm::normalize(p);
glm::vec3 s = r * n;
m.positions.insert(m.positions.end(), {s.x, s.y, s.z});
m.normals .insert(m.normals .end(), {n.x, n.y, n.z});
}
// 20 faces (CCW)
static const uint32_t I[] = {
0, 11, 5,
0, 5, 1,
0, 1, 7,
0, 7, 10,
0, 10, 11,
1, 5, 9,
5, 11, 4,
11, 10, 2,
10, 7, 6,
7, 1, 8,
3, 9, 4,
3, 4, 2,
3, 2, 6,
3, 6, 8,
3, 8, 9,
4, 9, 5,
2, 4, 11,
6, 2, 10,
8, 6, 7,
9, 8, 1
};
m.indices.assign(std::begin(I), std::end(I));
// UVs omitted (non-trivial on icosahedra)
m.uvs.clear();
return m;
}
Mesh make_unit_ico_flat(float size) {
Mesh m;
m.positions.clear();
m.normals.clear();
m.uvs.clear();
m.indices.clear(); // draw non-indexed (or fill 0..N-1 if you prefer)
// Golden ratio
const float phi = (1.0f + std::sqrt(5.0f)) * 0.5f;
const float r = size * 0.5f; // radius
// 12 canonical vertices (right-handed)
const std::array<glm::vec3, 12> V = {
glm::vec3(-1, phi, 0),
glm::vec3( 1, phi, 0),
glm::vec3(-1, -phi, 0),
glm::vec3( 1, -phi, 0),
glm::vec3( 0, -1, phi),
glm::vec3( 0, 1, phi),
glm::vec3( 0, -1, -phi),
glm::vec3( 0, 1, -phi),
glm::vec3( phi, 0, -1),
glm::vec3( phi, 0, 1),
glm::vec3(-phi, 0, -1),
glm::vec3(-phi, 0, 1)
};
// 20 faces (CCW)
static const uint32_t F[20][3] = {
{ 0,11, 5}, { 0, 5, 1}, { 0, 1, 7}, { 0, 7,10}, { 0,10,11},
{ 1, 5, 9}, { 5,11, 4}, {11,10, 2}, {10, 7, 6}, { 7, 1, 8},
{ 3, 9, 4}, { 3, 4, 2}, { 3, 2, 6}, { 3, 6, 8}, { 3, 8, 9},
{ 4, 9, 5}, { 2, 4,11}, { 6, 2,10}, { 8, 6, 7}, { 9, 8, 1}
};
m.positions.reserve(20 * 3 * 3);
m.normals.reserve (20 * 3 * 3);
for (const auto& tri : F) {
// Sphere positions (normalized then scaled to radius r)
glm::vec3 p0 = glm::normalize(V[tri[0]]) * r;
glm::vec3 p1 = glm::normalize(V[tri[1]]) * r;
glm::vec3 p2 = glm::normalize(V[tri[2]]) * r;
// Flat face normal (one per triangle)
glm::vec3 n = glm::normalize(glm::cross(p1 - p0, p2 - p0));
// Duplicate vertices per face so normals don't interpolate across edges
const glm::vec3 P[3] = { p0, p1, p2 };
for (int i = 0; i < 3; ++i) {
m.positions.push_back(P[i].x);
m.positions.push_back(P[i].y);
m.positions.push_back(P[i].z);
m.normals.push_back(n.x);
m.normals.push_back(n.y);
m.normals.push_back(n.z);
}
}
// If you prefer indexed draw, uncomment to fill 0..N-1
// m.indices.resize(m.positions.size() / 3);
// std::iota(m.indices.begin(), m.indices.end(), 0u);
return m;
}
Mesh make_grid(int N, float extent) {
Mesh m;
const int V = (N+1);
m.positions.reserve(V*V*3);
m.uvs .reserve(V*V*2);
const float half = extent*0.5f;
for (int y=0; y<V; ++y){
for (int x=0; x<V; ++x){
float fx = float(x)/N, fy = float(y)/N;
float X = -half + fx*extent;
float Y = -half + fy*extent;
m.positions.insert(m.positions.end(), {X,Y,0.0f}); // Z = 0 (well displace in VS)
m.uvs.insert(m.uvs.end(), {fx, fy}); // 0..1 for height/albedo sampling
}
}
m.indices.reserve(N*N*6);
for (int y=0; y<N; ++y){
for (int x=0; x<N; ++x){
uint32_t i0 = y *V + x;
uint32_t i1 = y *V + (x+1);
uint32_t i2 = (y+1)*V + x;
uint32_t i3 = (y+1)*V + (x+1);
m.indices.insert(m.indices.end(), { i0,i2,i1, i1,i2,i3 }); // CCW
}
}
return m;
}
static inline void add3(std::vector<float>& v, size_t i, const glm::vec3& a) {
v[i+0] += a.x; v[i+1] += a.y; v[i+2] += a.z;
}
Mesh make_terrain(int W, int H, float dx, float dz,
HeightFn heightFn,
bool genNormals,
bool genUVs,
float x0, float z0)
{
assert(W > 0 && H > 0 && dx > 0.0f && dz > 0.0f);
Mesh m;
const int VX = (W + 1);
const int VZ = (H + 1);
const int N = VX * VZ;
m.positions.resize(3 * N);
if (genNormals) m.normals.assign(3 * N, 0.0f);
if (genUVs) m.uvs.resize(2 * N);
// --- Vertices (positions + uvs) ---
for (int j = 0; j < VZ; ++j) {
for (int i = 0; i < VX; ++i) {
const int v = j * VX + i;
const size_t p3 = 3 * v;
const size_t t2 = 2 * v;
// Grid spans the XY plane; height goes into Z
const float X = x0 + i * dx;
const float Y = z0 + j * dz; // reuse dz as grid step in Y
const float Z = heightFn ? heightFn(X, Y) : 0.0f;
m.positions[p3 + 0] = X; // x
m.positions[p3 + 1] = Y; // y (planar)
m.positions[p3 + 2] = Z; // z (height)
if (genUVs) {
m.uvs[t2 + 0] = (VX > 1) ? (float)i / (float)(VX - 1) : 0.0f;
m.uvs[t2 + 1] = (VZ > 1) ? (float)j / (float)(VZ - 1) : 0.0f;
}
}
}
// --- Indices (two triangles per quad), CCW in XY so normals point +Z ---
m.indices.reserve(W * H * 6);
for (int j = 0; j < H; ++j) {
for (int i = 0; i < W; ++i) {
const uint32_t i0 = (uint32_t)( j * VX + i );
const uint32_t i1 = (uint32_t)( j * VX + i + 1);
const uint32_t i2 = (uint32_t)((j+1) * VX + i );
const uint32_t i3 = (uint32_t)((j+1) * VX + i + 1);
m.indices.push_back(i0); m.indices.push_back(i1); m.indices.push_back(i2);
m.indices.push_back(i1); m.indices.push_back(i3); m.indices.push_back(i2);
}
}
// --- Smoothed per-vertex normals (area-weighted) ---
if (genNormals) {
// Accumulate face normals
for (size_t k = 0; k < m.indices.size(); k += 3) {
const uint32_t ia = m.indices[k+0];
const uint32_t ib = m.indices[k+1];
const uint32_t ic = m.indices[k+2];
const glm::vec3 A{ m.positions[3*ia+0], m.positions[3*ia+1], m.positions[3*ia+2] };
const glm::vec3 B{ m.positions[3*ib+0], m.positions[3*ib+1], m.positions[3*ib+2] };
const glm::vec3 C{ m.positions[3*ic+0], m.positions[3*ic+1], m.positions[3*ic+2] };
const glm::vec3 N = glm::cross(B - A, C - A); // CCW -> +Z on flat areas
add3(m.normals, 3*ia, N);
add3(m.normals, 3*ib, N);
add3(m.normals, 3*ic, N);
}
// Normalize
for (int v = 0; v < N; ++v) {
glm::vec3 n{ m.normals[3*v+0], m.normals[3*v+1], m.normals[3*v+2] };
const float len = glm::length(n);
if (len > 1e-20f) {
n /= len;
} else {
n = glm::vec3(0, 0, 1); // fallback for degenerate spots
}
m.normals[3*v+0] = n.x;
m.normals[3*v+1] = n.y;
m.normals[3*v+2] = n.z;
}
}
return m;
}
} // namespace asset