Skip to content

Commit 43a94ce

Browse files
committed
Opt for a 3D skybox instead
Simpler to just go with a 3d skybox, although I had to change the default 'Sphere' to be a UV Sphere which shows off textures better than an icosphere which is now its own explicit model.
1 parent 773de53 commit 43a94ce

File tree

13 files changed

+288
-118
lines changed

13 files changed

+288
-118
lines changed

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ release/resources/*
1313
!release/resources/*.attribution.md
1414

1515
# PNG
16-
!release/resources/CubeMap.png
16+
!release/resources/ClearNight.png
1717

1818
# GLTF
1919
!release/resources/FlightHelmet

release/resources/CubeMap.attribution.md renamed to release/resources/ClearNight.attribution.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,4 @@ It was sourced from https://polyhaven.com/a/rogland_clear_night
44

55
Created by Greg Zaal.
66

7-
Converted into a cubemap from the HDRI using external software.
7+
Converted into a PNG image using external software.

release/resources/ClearNight.png

6.37 MB
Loading

release/resources/CubeMap.png

-3.08 MB
Binary file not shown.

release/shaders/main2d.frag

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,16 @@
11
#version 450 core
22

3+
struct Texture {
4+
vec2 uvScale;
5+
vec2 uvOffset;
6+
7+
/// -1 if no texture
8+
int index;
9+
10+
/// Rotation in radians
11+
float uvRotation;
12+
};
13+
314
in vec2 fragPos;
415
in vec2 fragUV;
516

@@ -8,10 +19,35 @@ layout(location = 1) uniform int textureIdx;
819

920
layout(std140, binding = 0) uniform MaterialBlock {
1021
vec3 materialColor;
22+
Texture materialTexture;
1123
};
1224

1325
out vec4 outColor;
1426

27+
vec2 transformUV(vec2 uv, Texture tex) {
28+
vec2 transformedUV = uv;
29+
transformedUV *= tex.uvScale;
30+
transformedUV += tex.uvOffset;
31+
32+
return transformedUV;
33+
}
34+
1535
void main() {
16-
outColor = vec4(materialColor, 1.0);
36+
if (materialTexture.index >= 0) {
37+
// Transform UV coordinates
38+
vec2 uv = transformUV(fragUV, materialTexture);
39+
40+
// Sample the texture
41+
vec4 texColor = texture(textureList, vec3(uv, materialTexture.index));
42+
43+
// Apply alpha test
44+
if (texColor.a < 0.5) {
45+
discard; // Discard fragments with low alpha
46+
}
47+
48+
// Combine texture color with material color
49+
outColor = vec4(materialColor * texColor.rgb, texColor.a);
50+
} else {
51+
outColor = vec4(materialColor, 1.0);
52+
}
1753
}

sample.png

1.09 MB
Loading

src/render/material/material2d.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
namespace material {
99
struct Material2D {
1010
alignas(16) glm::vec3 color;
11+
texture::Texture texture;
1112
};
1213

1314
class Manager2D {

src/render/model/3d/icosphere.cpp

Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
#include "icosphere.hpp"
2+
3+
#include <array>
4+
#include <cmath>
5+
#include <unordered_map>
6+
7+
#include "constants.hpp"
8+
9+
namespace {
10+
// Hash function for glm::vec3 to use in unordered_map
11+
struct Vec3Hash {
12+
std::size_t operator()(const glm::vec3& v) const {
13+
return std::hash<float>()(v.x) ^ (std::hash<float>()(v.y) << 1) ^ (std::hash<float>()(v.z) << 2);
14+
}
15+
};
16+
17+
// Equality function for glm::vec3
18+
struct Vec3Equal {
19+
bool operator()(const glm::vec3& a, const glm::vec3& b) const {
20+
const float epsilon = 1e-6f;
21+
return std::abs(a.x - b.x) < epsilon && std::abs(a.y - b.y) < epsilon && std::abs(a.z - b.z) < epsilon;
22+
}
23+
};
24+
}
25+
26+
model::Icosphere::Icosphere(float radius, int subdivisions) : radius(radius), subdivisions(subdivisions) {
27+
glCreateVertexArrays(1, &glAttributesIdx);
28+
glCreateBuffers(1, &glBufferIdx);
29+
glCreateBuffers(1, &glIndexBufferIdx);
30+
31+
{
32+
GLuint glAttrSlot1 = 0;
33+
34+
glVertexArrayVertexBuffer(glAttributesIdx, glAttrSlot1, glBufferIdx, 0, sizeof(Vertex3D));
35+
36+
glVertexArrayAttribFormat(glAttributesIdx, 0, 3, GL_FLOAT, GL_FALSE, 0);
37+
glEnableVertexArrayAttrib(glAttributesIdx, 0);
38+
glVertexArrayAttribBinding(glAttributesIdx, 0, glAttrSlot1);
39+
40+
glVertexArrayAttribFormat(glAttributesIdx, 1, 3, GL_FLOAT, GL_FALSE, offsetof(Vertex3D, normal));
41+
glEnableVertexArrayAttrib(glAttributesIdx, 1);
42+
glVertexArrayAttribBinding(glAttributesIdx, 1, glAttrSlot1);
43+
44+
glVertexArrayAttribFormat(glAttributesIdx, 2, 2, GL_FLOAT, GL_FALSE, offsetof(Vertex3D, uv));
45+
glEnableVertexArrayAttrib(glAttributesIdx, 2);
46+
glVertexArrayAttribBinding(glAttributesIdx, 2, glAttrSlot1);
47+
48+
glVertexArrayAttribFormat(glAttributesIdx, 3, 3, GL_FLOAT, GL_FALSE, offsetof(Vertex3D, tangent));
49+
glEnableVertexArrayAttrib(glAttributesIdx, 3);
50+
glVertexArrayAttribBinding(glAttributesIdx, 3, glAttrSlot1);
51+
52+
glVertexArrayElementBuffer(glAttributesIdx, glIndexBufferIdx);
53+
}
54+
55+
generateIcosphere(radius, subdivisions);
56+
57+
glNamedBufferData(glBufferIdx, vertices.size() * sizeof(Vertex3D), vertices.data(), GL_STATIC_DRAW);
58+
glNamedBufferData(glIndexBufferIdx, indices.size() * sizeof(GLuint), indices.data(), GL_STATIC_DRAW);
59+
}
60+
61+
model::Icosphere::~Icosphere() {
62+
glDeleteVertexArrays(1, &glAttributesIdx);
63+
glDeleteBuffers(1, &glBufferIdx);
64+
glDeleteBuffers(1, &glIndexBufferIdx);
65+
}
66+
67+
void model::Icosphere::draw() const {
68+
glBindVertexArray(glAttributesIdx);
69+
glDrawElements(GL_TRIANGLES, indices.size(), GL_UNSIGNED_INT, 0);
70+
}
71+
72+
void model::Icosphere::generateIcosphere(float radius, int subdivisions) {
73+
vertices.clear();
74+
indices.clear();
75+
76+
// Create icosahedron vertices
77+
const float t = (1.0f + std::sqrt(5.0f)) / 2.0f; // Golden ratio
78+
79+
std::vector<glm::vec3> icosahedronVertices = {
80+
glm::normalize(glm::vec3(-1, t, 0)), glm::normalize(glm::vec3(1, t, 0)), glm::normalize(glm::vec3(-1, -t, 0)),
81+
glm::normalize(glm::vec3(1, -t, 0)), glm::normalize(glm::vec3(0, -1, t)), glm::normalize(glm::vec3(0, 1, t)),
82+
glm::normalize(glm::vec3(0, -1, -t)), glm::normalize(glm::vec3(0, 1, -t)), glm::normalize(glm::vec3(t, 0, -1)),
83+
glm::normalize(glm::vec3(t, 0, 1)), glm::normalize(glm::vec3(-t, 0, -1)), glm::normalize(glm::vec3(-t, 0, 1))};
84+
85+
// Create icosahedron faces
86+
std::vector<std::array<int, 3>> icosahedronFaces = {
87+
{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},
88+
{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}};
89+
90+
// Use a map to avoid duplicate vertices
91+
std::unordered_map<glm::vec3, GLuint, Vec3Hash, Vec3Equal> vertexMap;
92+
93+
auto addVertex = [&](const glm::vec3& position) -> GLuint {
94+
auto it = vertexMap.find(position);
95+
if (it != vertexMap.end()) {
96+
return it->second;
97+
}
98+
99+
GLuint index = vertices.size();
100+
glm::vec3 normal = glm::normalize(position);
101+
glm::vec3 scaledPosition = normal * radius;
102+
glm::vec2 uv = calculateSphericalUV(normal);
103+
104+
vertices.push_back({scaledPosition, normal, uv});
105+
vertexMap[position] = index;
106+
return index;
107+
};
108+
109+
// Add initial icosahedron vertices
110+
for (const auto& vertex : icosahedronVertices) {
111+
addVertex(vertex);
112+
}
113+
114+
// Add initial faces
115+
std::vector<std::array<GLuint, 3>> faces;
116+
for (const auto& face : icosahedronFaces) {
117+
faces.push_back({static_cast<GLuint>(face[0]), static_cast<GLuint>(face[1]), static_cast<GLuint>(face[2])});
118+
}
119+
120+
// Subdivide faces
121+
for (int level = 0; level < subdivisions; ++level) {
122+
std::vector<std::array<GLuint, 3>> newFaces;
123+
124+
for (const auto& face : faces) {
125+
glm::vec3 v1 = glm::normalize(vertices[face[0]].pos / radius);
126+
glm::vec3 v2 = glm::normalize(vertices[face[1]].pos / radius);
127+
glm::vec3 v3 = glm::normalize(vertices[face[2]].pos / radius);
128+
129+
// Calculate midpoints and normalize them to the sphere surface
130+
glm::vec3 m1 = glm::normalize((v1 + v2) * 0.5f);
131+
glm::vec3 m2 = glm::normalize((v2 + v3) * 0.5f);
132+
glm::vec3 m3 = glm::normalize((v3 + v1) * 0.5f);
133+
134+
GLuint i1 = addVertex(v1);
135+
GLuint i2 = addVertex(v2);
136+
GLuint i3 = addVertex(v3);
137+
GLuint im1 = addVertex(m1);
138+
GLuint im2 = addVertex(m2);
139+
GLuint im3 = addVertex(m3);
140+
141+
// Create 4 new triangles
142+
newFaces.push_back({i1, im1, im3});
143+
newFaces.push_back({i2, im2, im1});
144+
newFaces.push_back({i3, im3, im2});
145+
newFaces.push_back({im1, im2, im3});
146+
}
147+
148+
faces = std::move(newFaces);
149+
}
150+
151+
// Convert faces to indices
152+
for (const auto& face : faces) {
153+
indices.push_back(face[0]);
154+
indices.push_back(face[1]);
155+
indices.push_back(face[2]);
156+
}
157+
}
158+
159+
glm::vec2 model::Icosphere::calculateSphericalUV(const glm::vec3& normal) {
160+
float u = 0.5f + std::atan2(normal.z, normal.x) / constants::TAU;
161+
float v = 0.5f - std::asin(normal.y) / constants::PI;
162+
return glm::vec2(u, v);
163+
}

src/render/model/3d/icosphere.hpp

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
#pragma once
2+
3+
#include <glad/gl.h>
4+
#include <glm/glm.hpp>
5+
#include <glm/gtc/quaternion.hpp>
6+
#include <vector>
7+
8+
#include "render/model/model.hpp"
9+
#include "render/vertex.hpp"
10+
11+
namespace model {
12+
class Icosphere : public Model3D {
13+
public:
14+
Icosphere(float radius = 1.0f, int subdivisions = 2);
15+
~Icosphere();
16+
void draw() const;
17+
18+
private:
19+
void generateIcosphere(float radius, int subdivisions);
20+
void subdivideTriangle(const glm::vec3& v1, const glm::vec3& v2, const glm::vec3& v3, int depth);
21+
glm::vec2 calculateSphericalUV(const glm::vec3& normal);
22+
23+
float radius;
24+
int subdivisions;
25+
26+
std::vector<Vertex3D> vertices;
27+
std::vector<GLuint> indices;
28+
GLuint glAttributesIdx;
29+
GLuint glBufferIdx;
30+
GLuint glIndexBufferIdx;
31+
};
32+
}

0 commit comments

Comments
 (0)