Files
gren/src/Asset.cpp
2026-01-09 13:50:52 +01:00

343 lines
9.3 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#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