Skip to content

Commit 466c0ec

Browse files
committed
Fix lag by remaking skybox for new state system
1 parent a9902c2 commit 466c0ec

7 files changed

Lines changed: 257 additions & 64 deletions

File tree

src/engine/resources/resource_manager.cpp

Lines changed: 36 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,11 @@ namespace Engine {
2525
if (!tmpTex.has_value())
2626
return std::unexpected(FW_ERROR(tmpTex.error(), "Failed to load error texture"));
2727
errorTexture = std::make_shared<Resource::ManagedTexture>(tmpTex.value());
28+
// CUBEMAP
29+
const auto tmpCubemap = Resource::Loading::loadCubemapSingle(BIN_ERROR_PNG.data(), BIN_ERROR_PNG.size());
30+
if (!tmpCubemap.has_value())
31+
return std::unexpected(FW_ERROR(tmpCubemap.error(), "Failed to load error cubemap"));
32+
errorCubemap = std::make_shared<Resource::ManagedTexture>(tmpCubemap.value());
2833

2934
// SHADER
3035
const auto vertShaderID = Resource::Loading::loadGLShaderSource(
@@ -79,10 +84,10 @@ namespace Engine {
7984
}
8085

8186
std::shared_ptr<Resource::ManagedTexture>
82-
ResourceManager::loadTexture(const std::string& texturePath, const Resource::TextureType type)
87+
ResourceManager::loadTexture(const std::string& texturePath)
8388
{
8489
SPDLOG_DEBUG("Loading texture: {}", texturePath);
85-
if (errorTexture == nullptr || errorTexture.get()->textureID == 0)
90+
if (errorTexture == nullptr || errorTexture->textureID == 0)
8691
throw std::runtime_error("Error texture is uninitialised or invalid. Refusing to proceed.");
8792

8893
if (textures.contains(texturePath)) {
@@ -92,14 +97,7 @@ namespace Engine {
9297
return textures[texturePath].lock();
9398
}
9499

95-
std::expected<unsigned int, Error> textureID;
96-
switch (type) {
97-
case Resource::TextureType::TEXTURE_2D: textureID = Resource::Loading::loadTexture(texturePath.c_str()); break;
98-
case Resource::TextureType::CUBEMAP: textureID = Resource::Loading::loadCubeMap(texturePath); break;
99-
default:
100-
reportError(ERROR(fmt::format("Unknown texture type: {}", static_cast<int>(type))));
101-
return errorTexture;
102-
}
100+
std::expected<unsigned int, Error> textureID = Resource::Loading::loadTexture(texturePath.c_str());
103101
if (!textureID.has_value()) {
104102
textures[texturePath] = errorTexture; // Only error once, then use the error texture
105103
reportError(FW_ERROR(textureID.error(), "Failed to load uncached texture"));
@@ -111,6 +109,34 @@ namespace Engine {
111109
return ptr;
112110
}
113111

112+
std::shared_ptr<Resource::ManagedTexture>
113+
ResourceManager::loadCubemap(const std::string& cubemapPath)
114+
{
115+
SPDLOG_DEBUG("Loading cubemap: {}", cubemapPath);
116+
if (errorCubemap == nullptr || errorCubemap->textureID == 0)
117+
throw std::runtime_error("Error cubemap is uninitialised or invalid. Refusing to proceed.");
118+
119+
// TODO: This could conflict with a texture with the same name...
120+
// I need to figure out a better solution for identifying resources
121+
if (textures.contains(cubemapPath)) {
122+
if (textures[cubemapPath].expired())
123+
textures.erase(cubemapPath);
124+
else
125+
return textures[cubemapPath].lock();
126+
}
127+
128+
std::expected<unsigned int, Error> cubemapID = Resource::Loading::loadCubemap(cubemapPath);
129+
if (!cubemapID.has_value()) {
130+
textures[cubemapPath] = errorCubemap; // Only error once, then use the error cubemap
131+
reportError(FW_ERROR(cubemapID.error(), "Failed to load uncached cubemap"));
132+
return errorCubemap;
133+
}
134+
135+
auto ptr = std::make_shared<Resource::ManagedTexture>(cubemapID.value());
136+
textures[cubemapPath] = ptr;
137+
return ptr;
138+
}
139+
114140
std::shared_ptr<Resource::Shader>
115141
ResourceManager::loadShader(const std::map<Resource::ShaderType, std::string>& shaders)
116142
{

src/engine/resources/resource_manager.h

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,12 @@ namespace Engine {
1515
std::unordered_map<std::string, std::weak_ptr<Resource::Shader>> shaders{};
1616
std::unordered_map<std::string, std::weak_ptr<Resource::ManagedTexture>> textures{};
1717
std::unordered_map<std::string, std::weak_ptr<Resource::Scene>> scenes{};
18-
18+
public:
1919
std::shared_ptr<Resource::Shader> errorShader;
2020
std::shared_ptr<Resource::ManagedTexture> errorTexture;
21+
std::shared_ptr<Resource::ManagedTexture> errorCubemap;
2122
std::shared_ptr<Resource::Scene> errorScene;
22-
public:
23+
2324
/*!
2425
* @brief Creates a resource manager.
2526
* @note The constructor will not load any resources and the manager will be in a largely INVALID state.
@@ -32,6 +33,7 @@ namespace Engine {
3233
*/
3334
[[nodiscard]] Expected<void> populateErrorResources();
3435

36+
public:
3537
// Shader
3638
[[nodiscard]] std::shared_ptr<Resource::Shader>
3739
loadShader(const std::map<Resource::ShaderType, std::string>& shaders);
@@ -44,7 +46,9 @@ namespace Engine {
4446

4547
// Texture
4648
[[nodiscard]] std::shared_ptr<Resource::ManagedTexture>
47-
loadTexture(const std::string &texturePath, Resource::TextureType type = Resource::TextureType::TEXTURE_2D);
49+
loadTexture(const std::string &texturePath);
50+
[[nodiscard]] std::shared_ptr<Resource::ManagedTexture>
51+
loadCubemap(const std::string &cubemapPath);
4852

4953
// Scene
5054
[[nodiscard]] std::shared_ptr<Resource::Scene>

src/engine/resources/texture.cpp

Lines changed: 141 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
#include "texture.h"
22

3-
#include <unordered_map>
43
#define STB_IMAGE_IMPLEMENTATION
54
#include <stb_image.h>
65

@@ -44,7 +43,7 @@ namespace Resource::Loading {
4443
return ImageData{width, height, channelCount, imgData};
4544
}
4645

47-
GLint getChannelCount(const int channelCount) {
46+
GLint getGLChannels(const int channelCount) {
4847
switch (channelCount) {
4948
case 1: return GL_RED;
5049
case 3: return GL_RGB;
@@ -57,7 +56,7 @@ namespace Resource::Loading {
5756
}
5857

5958
std::expected<unsigned int, Error> loadTexture(const ImageData& imgData) {
60-
const GLint format = getChannelCount(imgData.channelCount);
59+
const GLint format = getGLChannels(imgData.channelCount);
6160

6261
unsigned int textureID;
6362
glGenTextures(1, &textureID);
@@ -96,37 +95,162 @@ namespace Resource::Loading {
9695
return texture;
9796
}
9897

99-
std::expected<unsigned int, Error> loadCubeMap(const std::string& filePath) {
98+
constexpr std::array<std::string, 6> cubemapFaces = {"right", "left", "top", "bottom", "front", "back"};
99+
100+
std::expected<unsigned int, Error> loadCubemap(const std::string& filePath) {
100101
unsigned int textureID;
101102
glGenTextures(1, &textureID);
102103
glBindTexture(GL_TEXTURE_CUBE_MAP, textureID);
103104

104-
const std::pmr::unordered_map<GLint, std::string> dirMap = {
105-
{GL_TEXTURE_CUBE_MAP_POSITIVE_X, "right"},
106-
{GL_TEXTURE_CUBE_MAP_NEGATIVE_X, "left"},
107-
{GL_TEXTURE_CUBE_MAP_POSITIVE_Y, "top"},
108-
{GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, "bottom"},
109-
{GL_TEXTURE_CUBE_MAP_POSITIVE_Z, "front"},
110-
{GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, "back"}
111-
};
112-
113105
const auto extension_index = filePath.find_last_of('.');
106+
const auto pathPrefix = filePath.substr(0, extension_index) + "_";
107+
108+
ImageData firstData = {};
114109
for (int i = 0; i < 6; i++) {
110+
const std::string path = pathPrefix + cubemapFaces[i] + filePath.substr(extension_index);
111+
112+
Expected<ImageData> imgData = loadImage(path.c_str());
113+
if (!imgData) {
114+
glDeleteTextures(1, &textureID);
115+
return std::unexpected(FW_ERROR(imgData.error(), "Failed to load cubemap texture"));
116+
}
117+
if (imgData->width != imgData->height) {
118+
glDeleteTextures(1, &textureID);
119+
stbi_image_free(imgData->imgData);
120+
return std::unexpected(ERROR("Cubemap texture must be square"));
121+
}
122+
if (i == 0)
123+
firstData = imgData.value();
124+
else if (imgData->width != firstData.width || imgData->channelCount != firstData.channelCount) {
125+
glDeleteTextures(1, &textureID);
126+
stbi_image_free(imgData->imgData);
127+
return std::unexpected(ERROR("Cubemap texture faces must have the same dimensions and channel counts"));
128+
}
129+
SPDLOG_DEBUG("Loaded cubemap %s texture \"%s\" with dimensions %dx%d",
130+
cubemapFaces[i].c_str(), path.c_str(), imgData->width, imgData->height);
131+
115132
const GLint faceDir = GL_TEXTURE_CUBE_MAP_POSITIVE_X + i;
133+
assert(faceDir >= GL_TEXTURE_CUBE_MAP_POSITIVE_X && faceDir <= GL_TEXTURE_CUBE_MAP_NEGATIVE_Z);
134+
135+
const GLint format = getGLChannels(imgData->channelCount);
136+
glTexImage2D(faceDir,
137+
0, format, imgData->width, imgData->height, 0, format, GL_UNSIGNED_BYTE, imgData->imgData);
138+
stbi_image_free(imgData->imgData);
139+
}
140+
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
141+
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
142+
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
143+
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
144+
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
116145

117-
const auto path = filePath.substr(0, extension_index) + "_" + dirMap.at(faceDir) + filePath.substr(extension_index);
146+
return textureID;
147+
}
148+
std::expected<unsigned int, Error> loadCubemap(const std::array<const unsigned char*, 6>& data, const std::array<int, 6>& sizes)
149+
{
150+
unsigned int textureID;
151+
glGenTextures(1, &textureID);
152+
glBindTexture(GL_TEXTURE_CUBE_MAP, textureID);
118153

119-
Expected<ImageData> imgData = loadImage(path.c_str());
154+
ImageData firstData = {};
155+
for (int i = 0; i < 6; i++) {
156+
Expected<ImageData> imgData = loadImageMemory(data[i], sizes[i]);
120157
if (!imgData) {
121158
glDeleteTextures(1, &textureID);
122159
return std::unexpected(FW_ERROR(imgData.error(), "Failed to load cubemap texture"));
123160
}
124-
SPDLOG_DEBUG("Loaded cubemap %s texture \"%s\" with dimensions %dx%d", dirMap.at(faceDir).c_str(), path.c_str(), imgData->width, imgData->height);
161+
if (imgData->width != imgData->height) {
162+
glDeleteTextures(1, &textureID);
163+
stbi_image_free(imgData->imgData);
164+
return std::unexpected(ERROR("Cubemap texture must be square"));
165+
}
166+
if (i == 0)
167+
firstData = imgData.value();
168+
else if (imgData->width != firstData.width || imgData->channelCount != firstData.channelCount) {
169+
glDeleteTextures(1, &textureID);
170+
stbi_image_free(imgData->imgData);
171+
return std::unexpected(ERROR("Cubemap texture faces must have the same dimensions and channel counts"));
172+
}
173+
SPDLOG_DEBUG("Loaded cubemap %d texture with dimensions %dx%d",
174+
cubemapFaces[i].c_str(), imgData->width, imgData->height);
175+
176+
const GLint faceDir = GL_TEXTURE_CUBE_MAP_POSITIVE_X + i;
177+
assert(faceDir >= GL_TEXTURE_CUBE_MAP_POSITIVE_X && faceDir <= GL_TEXTURE_CUBE_MAP_NEGATIVE_Z);
178+
179+
const GLint format = getGLChannels(imgData->channelCount);
180+
glTexImage2D(faceDir,
181+
0, format, imgData->width, imgData->height, 0, format, GL_UNSIGNED_BYTE, imgData->imgData);
182+
stbi_image_free(imgData->imgData);
183+
}
184+
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
185+
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
186+
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
187+
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
188+
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
189+
190+
return textureID;
191+
}
192+
193+
std::expected<unsigned int, Error> loadCubemapSingle(const std::string& filePath)
194+
{
195+
unsigned int textureID;
196+
glGenTextures(1, &textureID);
197+
glBindTexture(GL_TEXTURE_CUBE_MAP, textureID);
198+
199+
Expected<ImageData> imgData = loadImage(filePath.c_str());
200+
if (!imgData) {
201+
glDeleteTextures(1, &textureID);
202+
return std::unexpected(FW_ERROR(imgData.error(), "Failed to load cubemap texture"));
203+
}
204+
if (imgData->width != imgData->height) {
205+
glDeleteTextures(1, &textureID);
206+
stbi_image_free(imgData->imgData);
207+
return std::unexpected(ERROR("Cubemap texture must be square"));
208+
}
209+
210+
const GLint format = getGLChannels(imgData->channelCount);
211+
for (int i = 0; i < 6; i++) {
212+
const GLint faceDir = GL_TEXTURE_CUBE_MAP_POSITIVE_X + i;
213+
assert(faceDir >= GL_TEXTURE_CUBE_MAP_POSITIVE_X && faceDir <= GL_TEXTURE_CUBE_MAP_NEGATIVE_Z);
214+
glTexImage2D(faceDir,
215+
0, format, imgData->width, imgData->height, 0, format, GL_UNSIGNED_BYTE, imgData->imgData);
216+
}
217+
stbi_image_free(imgData->imgData);
218+
219+
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
220+
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
221+
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
222+
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
223+
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
224+
225+
return textureID;
226+
}
227+
228+
std::expected<unsigned int, Error> loadCubemapSingle(const unsigned char* data, int size)
229+
{
230+
unsigned int textureID;
231+
glGenTextures(1, &textureID);
232+
glBindTexture(GL_TEXTURE_CUBE_MAP, textureID);
125233

126-
const GLint format = getChannelCount(imgData->channelCount);
234+
Expected<ImageData> imgData = loadImageMemory(data, size);
235+
if (!imgData) {
236+
glDeleteTextures(1, &textureID);
237+
return std::unexpected(FW_ERROR(imgData.error(), "Failed to load cubemap texture"));
238+
}
239+
if (imgData->width != imgData->height) {
240+
glDeleteTextures(1, &textureID);
241+
stbi_image_free(imgData->imgData);
242+
return std::unexpected(ERROR("Cubemap texture must be square"));
243+
}
244+
245+
const GLint format = getGLChannels(imgData->channelCount);
246+
for (int i = 0; i < 6; i++) {
247+
const GLint faceDir = GL_TEXTURE_CUBE_MAP_POSITIVE_X + i;
248+
assert(faceDir >= GL_TEXTURE_CUBE_MAP_POSITIVE_X && faceDir <= GL_TEXTURE_CUBE_MAP_NEGATIVE_Z);
127249
glTexImage2D(faceDir,
128250
0, format, imgData->width, imgData->height, 0, format, GL_UNSIGNED_BYTE, imgData->imgData);
129251
}
252+
stbi_image_free(imgData->imgData);
253+
130254
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
131255
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
132256
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);

src/engine/resources/texture.h

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,6 @@
66

77
namespace Resource
88
{
9-
enum TextureType {
10-
TEXTURE_2D,
11-
CUBEMAP
12-
};
13-
149
/*!
1510
* Class that stores an OpenGL texture ID and deletes it when it goes out of scope
1611
*/
@@ -48,5 +43,31 @@ namespace Resource::Loading
4843
* @attention If returned successfully, it is YOUR responsibility to free the memory allocated by opengl.
4944
* @note The file name suffixes are: `_right`, `_left`, `_top`, `_bottom`, `_front`, `_back`.
5045
*/
51-
std::expected<unsigned int, Error> loadCubeMap(const std::string& filePath);
46+
std::expected<unsigned int, Error> loadCubemap(const std::string& filePath);
47+
/*!
48+
* Loads a cubemap texture from a set of data.
49+
* @param data The data for each side of the cubemap.
50+
* @param sizes The sizes of each side of the cubemap.
51+
* @return The texture ID if successful, or an error if not.
52+
* @attention If returned successfully, it is YOUR responsibility to free the memory allocated by opengl.
53+
*/
54+
std::expected<unsigned int, Error> loadCubemap(
55+
const std::array<const unsigned char*, 6>& data,
56+
const std::array<int, 6>& sizes);
57+
58+
/*!
59+
* Loads a cubemap texture from a single file to be used for all sides.
60+
* @param filePath The path to the file.
61+
* @return The texture ID if successful, or an error if not.
62+
* @attention If returned successfully, it is YOUR responsibility to free the memory allocated by opengl.
63+
*/
64+
std::expected<unsigned int, Error> loadCubemapSingle(const std::string& filePath);
65+
/*!
66+
* Loads a cubemap texture from a single byte array to be used for all sides.
67+
* @param data Byte array of the image data.
68+
* @param size The size of the byte array.
69+
* @return The texture ID if successful, or an error if not.
70+
* @attention If returned successfully, it is YOUR responsibility to free the memory allocated by opengl.
71+
*/
72+
std::expected<unsigned int, Error> loadCubemapSingle(const unsigned char* data, int size);
5273
}

src/game/game.cpp

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ std::unique_ptr<FrameBuffer> frameBuffer;
2727
std::vector<std::shared_ptr<Resource::Scene>> scenes;
2828
Skybox *skybox;
2929
std::shared_ptr<Resource::Shader> mainShader;
30-
std::shared_ptr<Resource::Shader> sbShader;
3130

3231
bool setupGame() {
3332
gameState = new GameState();
@@ -54,13 +53,14 @@ bool setupGame() {
5453
if (!matricesBinding.has_value())
5554
throw std::runtime_error(stringifyError(FW_ERROR(matricesBinding.error(), "Failed to bind matrices uniform block")));
5655

57-
sbShader = engineState->resourceManager.loadShader(
56+
// TODO: So super duper mega ultra scuffed and a remnant from when we initialised the skybox stuff manually each frame
57+
const std::shared_ptr<Resource::Shader> sbShader = engineState->resourceManager.loadShader(
5858
"resources/assets/shaders/sb_vert.vert", "resources/assets/shaders/sb_frag.frag");
5959
sbShader->use();
6060
matricesBinding = sbShader->bindUniformBlock("Matrices", 0);
6161
if (!matricesBinding.has_value())
6262
throw std::runtime_error(stringifyError(FW_ERROR(matricesBinding.error(), "Failed to bind matrices uniform block")));
63-
skybox = new Skybox();
63+
skybox = new Skybox(engineState->resourceManager.loadCubemap("resources/assets/textures/skybox/sky.png"));
6464

6565
glGenBuffers(1, &uboMatrices);
6666
glBindBuffer(GL_UNIFORM_BUFFER, uboMatrices);
@@ -169,12 +169,7 @@ bool renderUpdate(const double deltaTime) {
169169
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
170170

171171
#pragma region Skybox
172-
// We render the skybox manually, since we don't need any of the fancy scene stuff
173-
sbShader->use();
174-
175-
auto skyboxTex = engineState->resourceManager.loadTexture(
176-
"resources/assets/textures/skybox/sky.png", Resource::CUBEMAP);
177-
skybox->draw(skyboxTex->textureID, *sbShader);
172+
skybox->draw();
178173
#pragma endregion
179174

180175
#pragma region "Transfer color buffer to the default framebuffer before rendering overlays"

0 commit comments

Comments
 (0)