Browse Source

Engine: Implement skybox

master
Riyyi 4 months ago
parent
commit
fd8973d10d
  1. BIN
      assets/gfx/skybox-nx.jpg
  2. BIN
      assets/gfx/skybox-ny.jpg
  3. BIN
      assets/gfx/skybox-nz.jpg
  4. BIN
      assets/gfx/skybox-px.jpg
  5. BIN
      assets/gfx/skybox-py.jpg
  6. BIN
      assets/gfx/skybox-pz.jpg
  7. 49
      assets/glsl/batch-cubemap.frag
  8. 20
      assets/glsl/batch-cubemap.vert
  9. 13
      assets/scene/scene1.json
  10. 10
      src/inferno/application.cpp
  11. 27
      src/inferno/component/cubemap-component.cpp
  12. 25
      src/inferno/component/cubemap-component.h
  13. 2
      src/inferno/render/font.cpp
  14. 184
      src/inferno/render/renderer.cpp
  15. 62
      src/inferno/render/renderer.h
  16. 163
      src/inferno/render/texture.cpp
  17. 93
      src/inferno/render/texture.h
  18. 8
      src/inferno/scene/scene.cpp
  19. 6
      src/inferno/scene/scene.h
  20. 10
      src/inferno/system/camerasystem.cpp
  21. 10
      src/inferno/system/camerasystem.h
  22. 13
      src/inferno/system/rendersystem.cpp

BIN
assets/gfx/skybox-nx.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

BIN
assets/gfx/skybox-ny.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

BIN
assets/gfx/skybox-nz.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

BIN
assets/gfx/skybox-px.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB

BIN
assets/gfx/skybox-py.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 87 KiB

BIN
assets/gfx/skybox-pz.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

49
assets/glsl/batch-cubemap.frag

@ -0,0 +1,49 @@
#version 450 core
layout(location = 0) out vec4 color;
in vec4 v_color;
in vec3 v_textureCoordinates;
in flat float v_textureIndex;
uniform samplerCube u_textures[32];
void main()
{
vec4 textureColor = v_color;
switch(int(v_textureIndex)) {
case 0: break; // Texture unit 0 is reserved for no texture
case 1: textureColor *= texture(u_textures[1], v_textureCoordinates); break;
case 2: textureColor *= texture(u_textures[2], v_textureCoordinates); break;
case 3: textureColor *= texture(u_textures[3], v_textureCoordinates); break;
case 4: textureColor *= texture(u_textures[4], v_textureCoordinates); break;
case 5: textureColor *= texture(u_textures[5], v_textureCoordinates); break;
case 6: textureColor *= texture(u_textures[6], v_textureCoordinates); break;
case 7: textureColor *= texture(u_textures[7], v_textureCoordinates); break;
case 8: textureColor *= texture(u_textures[8], v_textureCoordinates); break;
case 9: textureColor *= texture(u_textures[9], v_textureCoordinates); break;
case 10: textureColor *= texture(u_textures[10], v_textureCoordinates); break;
case 11: textureColor *= texture(u_textures[11], v_textureCoordinates); break;
case 12: textureColor *= texture(u_textures[12], v_textureCoordinates); break;
case 13: textureColor *= texture(u_textures[13], v_textureCoordinates); break;
case 14: textureColor *= texture(u_textures[14], v_textureCoordinates); break;
case 15: textureColor *= texture(u_textures[15], v_textureCoordinates); break;
case 16: textureColor *= texture(u_textures[16], v_textureCoordinates); break;
case 17: textureColor *= texture(u_textures[17], v_textureCoordinates); break;
case 18: textureColor *= texture(u_textures[18], v_textureCoordinates); break;
case 19: textureColor *= texture(u_textures[19], v_textureCoordinates); break;
case 20: textureColor *= texture(u_textures[20], v_textureCoordinates); break;
case 21: textureColor *= texture(u_textures[21], v_textureCoordinates); break;
case 22: textureColor *= texture(u_textures[22], v_textureCoordinates); break;
case 23: textureColor *= texture(u_textures[23], v_textureCoordinates); break;
case 24: textureColor *= texture(u_textures[24], v_textureCoordinates); break;
case 25: textureColor *= texture(u_textures[25], v_textureCoordinates); break;
case 26: textureColor *= texture(u_textures[26], v_textureCoordinates); break;
case 27: textureColor *= texture(u_textures[27], v_textureCoordinates); break;
case 28: textureColor *= texture(u_textures[28], v_textureCoordinates); break;
case 29: textureColor *= texture(u_textures[29], v_textureCoordinates); break;
case 30: textureColor *= texture(u_textures[30], v_textureCoordinates); break;
case 31: textureColor *= texture(u_textures[31], v_textureCoordinates); break;
}
color = textureColor;
}

20
assets/glsl/batch-cubemap.vert

@ -0,0 +1,20 @@
#version 450 core
layout(location = 0) in vec3 a_position;
layout(location = 1) in vec4 a_color;
layout(location = 2) in float a_textureIndex;
out vec4 v_color;
out vec3 v_textureCoordinates;
out flat float v_textureIndex;
uniform mat4 u_projectionView;
void main()
{
v_color = a_color;
v_textureCoordinates = a_position;
v_textureIndex = a_textureIndex;
// Vclip = Camera projection * Camera view * Model transform * Vlocal
gl_Position = u_projectionView * vec4(a_position, 1.0f);
}

13
assets/scene/scene1.json

@ -14,6 +14,19 @@
{ "path": "assets/lua/cameracontroller.lua" }
]
},
{
"id": { "id": 212563732 },
"tag": { "tag": "Skybox" },
"transform" : {
"translate": [0.0, 0.0, 0.0],
"rotate": [0.0, 0.0, 0.0],
"scale": [100.0, 100.0, 100.0]
},
"cubemap": {
"color": [ 1.0, 1.0, 1.0, 1.0 ],
"texture": "assets/gfx/skybox.jpg"
}
},
{
"id": { "id": 564564564 },
"tag": { "tag": "Quad" },

10
src/inferno/application.cpp

@ -1,9 +1,11 @@
/*
* Copyright (C) 2022 Riyyi
* Copyright (C) 2022,2024 Riyyi
*
* SPDX-License-Identifier: MIT
*/
#include <utility> // std::pair
#include "glm/gtc/type_ptr.hpp" // glm::make_mat4
#include "ruc/format/log.h"
#include "ruc/meta/assert.h"
@ -78,6 +80,7 @@ Application::~Application()
FontManager::destroy();
RendererCharacter::destroy();
Renderer2D::destroy();
RendererCubemap::destroy();
RenderCommand::destroy();
TextureManager::destroy();
ShaderManager::destroy();
@ -164,12 +167,15 @@ int Application::run()
RenderCommand::clearColor({ 0.2f, 0.3f, 0.3f, 1.0f });
RenderCommand::clear();
Renderer2D::the().beginScene(m_scene->cameraProjectionView()); // camera, lights, environment
std::pair<glm::mat4, glm::mat4> projectionView = m_scene->cameraProjectionView();
RendererCubemap::the().beginScene(projectionView.first, projectionView.second); // camera, lights, environment
Renderer2D::the().beginScene(projectionView.first, projectionView.second); // camera, lights, environment
RendererCharacter::the().beginScene();
m_scene->render();
// RendererCharacter::the().drawCharacter(character, f->texture());
RendererCubemap::the().endScene();
Renderer2D::the().endScene();
RendererCharacter::the().endScene();

27
src/inferno/component/cubemap-component.cpp

@ -0,0 +1,27 @@
/*
* Copyright (C) 2024 Riyyi
*
* SPDX-License-Identifier: MIT
*/
#include "ruc/json/json.h"
#include "inferno/component/cubemap-component.h"
#include "inferno/component/spritecomponent.h" // TODO: Move glm::x toJson/fromJson to separate file
#include "inferno/render/texture.h"
namespace Inferno {
void fromJson(const ruc::Json& json, CubemapComponent& value)
{
VERIFY(json.type() == ruc::Json::Type::Object);
if (json.exists("color")) {
json.at("color").getTo(value.color);
}
if (json.exists("texture") && json.at("texture").type() == ruc::Json::Type::String) {
value.texture = TextureManager::the().load(json.at("texture").asString(), Texture::Type::Cubemap);
}
}
} // namespace Inferno

25
src/inferno/component/cubemap-component.h

@ -0,0 +1,25 @@
/*
* Copyright (C) 2024 Riyyi
*
* SPDX-License-Identifier: MIT
*/
#pragma once
#include <memory> // std::shared_ptr
#include "glm/ext/vector_float4.hpp" // glm::vec4
#include "ruc/json/json.h"
#include "inferno/render/texture.h"
namespace Inferno {
struct CubemapComponent {
glm::vec4 color { 1.0f };
std::shared_ptr<Texture> texture;
};
void fromJson(const ruc::Json& json, CubemapComponent& value);
} // namespace Inferno

2
src/inferno/render/font.cpp

@ -32,7 +32,7 @@ Font::Font(const std::string& name)
std::string font = ruc::File(path).data();
parseFont(font);
m_texture = std::make_shared<Texture>(image);
m_texture = Texture2D::create(image);
}
// TODO: Move this to ruc

184
src/inferno/render/renderer.cpp

@ -1,5 +1,5 @@
/*
* Copyright (C) 2022 Riyyi
* Copyright (C) 2022,2024 Riyyi
*
* SPDX-License-Identifier: MIT
*/
@ -216,10 +216,10 @@ Renderer2D::~Renderer2D()
Renderer::destroy();
}
void Renderer2D::beginScene(glm::mat4 cameraProjectionView)
void Renderer2D::beginScene(glm::mat4 cameraProjection, glm::mat4 cameraView)
{
m_shader->bind();
m_shader->setFloat("u_projectionView", cameraProjectionView);
m_shader->setFloat("u_projectionView", cameraProjection * cameraView);
m_shader->unbind();
}
@ -311,6 +311,184 @@ void Renderer2D::nextBatch()
// -----------------------------------------
RendererCubemap::RendererCubemap(s)
{
Renderer::initialize();
// CPU
// ---------------------------------
// Create array for storing quads vertices
m_vertexBufferBase = std::make_unique<CubemapVertex[]>(vertexCount);
m_vertexBufferPtr = m_vertexBufferBase.get();
// Set default cubemap vertex positions
// Back face - v
m_vertexPositions[0] = { 0.5f, -0.5f, 0.5f, 1.0f };
m_vertexPositions[1] = { -0.5f, -0.5f, 0.5f, 1.0f };
m_vertexPositions[2] = { -0.5f, 0.5f, 0.5f, 1.0f };
m_vertexPositions[3] = { 0.5f, 0.5f, 0.5f, 1.0f };
// Left face - v
m_vertexPositions[7] = { -0.5f, 0.5f, 0.5f, 1.0f };
m_vertexPositions[6] = { -0.5f, 0.5f, -0.5f, 1.0f };
m_vertexPositions[5] = { -0.5f, -0.5f, -0.5f, 1.0f };
m_vertexPositions[4] = { -0.5f, -0.5f, 0.5f, 1.0f };
// Right face - v
m_vertexPositions[8] = { 0.5f, -0.5f, -0.5f, 1.0f };
m_vertexPositions[9] = { 0.5f, -0.5f, 0.5f, 1.0f };
m_vertexPositions[10] = { 0.5f, 0.5f, 0.5f, 1.0f };
m_vertexPositions[11] = { 0.5f, 0.5f, -0.5f, 1.0f };
// Front face - v
m_vertexPositions[12] = { -0.5f, -0.5f, -0.5f, 1.0f };
m_vertexPositions[13] = { 0.5f, -0.5f, -0.5f, 1.0f };
m_vertexPositions[14] = { 0.5f, 0.5f, -0.5f, 1.0f };
m_vertexPositions[15] = { -0.5f, 0.5f, -0.5f, 1.0f };
// Top face
m_vertexPositions[16] = { -0.5f, 0.5f, -0.5f, 1.0f };
m_vertexPositions[17] = { 0.5f, 0.5f, -0.5f, 1.0f };
m_vertexPositions[18] = { 0.5f, 0.5f, 0.5f, 1.0f };
m_vertexPositions[19] = { -0.5f, 0.5f, 0.5f, 1.0f };
// Bottom face
m_vertexPositions[20] = { -0.5f, -0.5f, -0.5f, 1.0f };
m_vertexPositions[21] = { -0.5f, -0.5f, 0.5f, 1.0f };
m_vertexPositions[22] = { 0.5f, -0.5f, 0.5f, 1.0f };
m_vertexPositions[23] = { 0.5f, -0.5f, -0.5f, 1.0f };
// Generate indices
uint32_t* indices = new uint32_t[indexCount];
uint32_t offset = 0;
for (uint32_t i = 0; i < indexCount; i += indexPerQuad) {
indices[i + 0] = offset + 0;
indices[i + 1] = offset + 1;
indices[i + 2] = offset + 2;
indices[i + 3] = offset + 2;
indices[i + 4] = offset + 3;
indices[i + 5] = offset + 0;
offset += vertexPerQuad;
}
// GPU
// ---------------------------------
// Create vertex buffer
auto vertexBuffer = std::make_shared<VertexBuffer>(sizeof(CubemapVertex) * vertexCount);
vertexBuffer->setLayout({
{ BufferElementType::Vec3, "a_position" },
{ BufferElementType::Vec4, "a_color" },
{ BufferElementType::Float, "a_textureIndex" },
});
m_vertexArray->addVertexBuffer(vertexBuffer);
// Create index buffer
auto indexBuffer = std::make_shared<IndexBuffer>(indices, sizeof(uint32_t) * indexCount);
m_vertexArray->setIndexBuffer(indexBuffer);
delete[] indices;
ruc::info("RendererCubemap initialized");
}
RendererCubemap::~RendererCubemap()
{
Renderer::destroy();
}
void RendererCubemap::beginScene(glm::mat4 cameraProjection, glm::mat4 cameraView)
{
// We want the skybox fixed in position, so only retain the rotation and scale.
// Set the translation of the camera's view matrix to 0, meaning:
// x x x 0
// x x x 0
// x x x 0
// 0 0 0 1
cameraView = glm::mat4(glm::mat3(cameraView));
m_shader->bind();
m_shader->setFloat("u_projectionView", cameraProjection * cameraView);
m_shader->unbind();
}
void RendererCubemap::endScene()
{
nextBatch();
}
void RendererCubemap::drawCubemap(const TransformComponent& transform, glm::vec4 color, std::shared_ptr<Texture> texture)
{
drawCubemap(transform, glm::mat4(color, color, color, color), texture);
}
void RendererCubemap::drawCubemap(const TransformComponent& transform, glm::mat4 color, std::shared_ptr<Texture> texture)
{
// Create a new batch if the quad limit has been reached
if (m_quadIndex >= quadCount) {
nextBatch();
}
uint32_t textureUnitIndex = addTextureUnit(texture);
// Add the quads 4 vertices
for (uint32_t i = 0; i < vertexPerQuad * quadPerCube; i++) {
m_vertexBufferPtr->position = transform.transform * m_vertexPositions[i];
m_vertexBufferPtr->color = color[i % 4];
m_vertexBufferPtr->textureIndex = (float)textureUnitIndex;
m_vertexBufferPtr++;
}
m_quadIndex += quadPerCube;
}
void RendererCubemap::loadShader()
{
m_shader = ShaderManager::the().load("assets/glsl/batch-cubemap");
}
void RendererCubemap::flush()
{
if (m_quadIndex == 0) {
return;
}
// Upload vertex data to GPU
m_vertexArray->getVertexBuffers().at(0)->uploadData(
m_vertexBufferBase.get(),
m_quadIndex * vertexPerQuad * sizeof(CubemapVertex));
bind();
// Render
bool depthTest = RenderCommand::depthTest();
RenderCommand::setDepthTest(false);
RenderCommand::drawIndexed(*m_vertexArray, m_quadIndex * indexPerQuad);
RenderCommand::setDepthTest(depthTest);
unbind();
}
void RendererCubemap::startBatch()
{
m_quadIndex = 0;
m_vertexBufferPtr = m_vertexBufferBase.get();
m_textureUnitIndex = 1;
}
void RendererCubemap::nextBatch()
{
flush();
startBatch();
}
// -----------------------------------------
RendererCharacter::RendererCharacter(s)
{
Renderer::initialize();

62
src/inferno/render/renderer.h

@ -1,5 +1,5 @@
/*
* Copyright (C) 2022 Riyyi
* Copyright (C) 2022,2024 Riyyi
*
* SPDX-License-Identifier: MIT
*/
@ -27,7 +27,13 @@ struct QuadVertex {
glm::vec3 position { 0.0f, 0.0f, 0.0f };
glm::vec4 color { 1.0f, 1.0f, 1.0f, 1.0f };
glm::vec2 textureCoordinates { 0.0f, 0.0f };
float textureIndex = 0; // @Todo get int to pass to fragment correctly
float textureIndex = 0; // TODO: get int to pass to fragment correctly
};
struct CubemapVertex {
glm::vec3 position { 0.0f, 0.0f, 0.0f };
glm::vec4 color { 1.0f, 1.0f, 1.0f, 1.0f };
float textureIndex = 0; // TODO: get int to pass to fragment correctly
};
struct CharacterVertex {
@ -66,9 +72,10 @@ public:
class Renderer {
public:
static const uint32_t vertexPerQuad = 4;
static const uint32_t indexPerQuad = 6;
static const uint32_t textureUnitPerBatch = 32;
static constexpr const uint32_t vertexPerQuad = 4;
static constexpr const uint32_t indexPerQuad = 6;
static constexpr const uint32_t quadPerCube = 6;
static constexpr const uint32_t textureUnitPerBatch = 32;
protected:
Renderer() {}
@ -108,11 +115,12 @@ public:
using Singleton<Renderer2D>::destroy;
static const uint32_t quadCount = 1000;
static const uint32_t vertexCount = quadCount * vertexPerQuad;
static const uint32_t indexCount = quadCount * indexPerQuad;
// When to start a new batch
static constexpr const uint32_t quadCount = 1000;
static constexpr const uint32_t vertexCount = quadCount * vertexPerQuad;
static constexpr const uint32_t indexCount = quadCount * indexPerQuad;
void beginScene(glm::mat4 cameraProjectionView);
void beginScene(glm::mat4 cameraProjectionView, glm::mat4 cameraView);
void endScene();
void drawQuad(const TransformComponent& transform, glm::vec4 color);
@ -133,6 +141,42 @@ private:
// Default quad vertex positions
glm::vec4 m_vertexPositions[vertexPerQuad];
};
// -------------------------------------
class RendererCubemap final
: public Renderer
, public ruc::Singleton<RendererCubemap> {
public:
RendererCubemap(s);
virtual ~RendererCubemap();
using Singleton<RendererCubemap>::destroy;
// When to start a new batch
static constexpr const uint32_t cubemapCount = 166;
static constexpr const uint32_t quadCount = cubemapCount * quadPerCube;
static constexpr const uint32_t vertexCount = quadCount * vertexPerQuad;
static constexpr const uint32_t indexCount = quadCount * indexPerQuad;
void beginScene(glm::mat4 cameraProjectionView, glm::mat4 cameraView);
void endScene();
void drawCubemap(const TransformComponent& transform, glm::vec4 color, std::shared_ptr<Texture> texture);
void drawCubemap(const TransformComponent& transform, glm::mat4 color, std::shared_ptr<Texture> texture);
private:
void loadShader() override;
void flush() override;
void startBatch() override;
void nextBatch() override;
// CPU quad vertices
std::unique_ptr<CubemapVertex[]> m_vertexBufferBase;
CubemapVertex* m_vertexBufferPtr { nullptr };
// Default cubemap vertex positions
glm::vec4 m_vertexPositions[vertexPerQuad * quadPerCube];
};
// -------------------------------------

163
src/inferno/render/texture.cpp

@ -1,5 +1,5 @@
/*
* Copyright (C) 2022 Riyyi
* Copyright (C) 2022,2024 Riyyi
*
* SPDX-License-Identifier: MIT
*/
@ -19,36 +19,47 @@
namespace Inferno {
Texture::Texture(const std::string& path)
: m_path(std::move(path))
Texture::~Texture()
{
unsigned char* data = nullptr;
glDeleteTextures(1, &m_id);
}
void Texture::init(uint32_t width, uint32_t height, uint8_t channels)
{
m_width = width;
m_height = height;
m_internalFormat = (channels == 3) ? GL_RGB8 : GL_RGBA8;
m_dataFormat = (channels == 3) ? GL_RGB : GL_RGBA;
}
// -----------------------------------------
std::shared_ptr<Texture> Texture2D::create(const std::string& path)
{
auto result = std::shared_ptr<Texture2D>(new Texture2D);
result->m_path = path;
int width = 0;
int height = 0;
int channels = 0;
unsigned char* data = nullptr;
// Load image data
stbi_set_flip_vertically_on_load(1);
data = stbi_load(path.c_str(), &width, &height, &channels, STBI_default);
VERIFY(data, "failed to load image: '{}'", path);
init(data, width, height, channels);
result->init(width, height, channels);
result->create(data);
// Clean resources
stbi_image_free(data);
}
Texture::Texture(unsigned char* data, uint32_t width, uint32_t height, uint8_t channels)
{
init(data, width, height, channels);
}
Texture::~Texture()
{
glDeleteTextures(1, &m_id);
return result;
}
void Texture::bind(uint32_t unit) const
void Texture2D::bind(uint32_t unit) const
{
// Set active unit
glActiveTexture(GL_TEXTURE0 + unit);
@ -59,31 +70,12 @@ void Texture::bind(uint32_t unit) const
glActiveTexture(GL_TEXTURE0);
}
void Texture::unbind() const
void Texture2D::unbind() const
{
glBindTexture(GL_TEXTURE_2D, 0);
}
// -----------------------------------------
void Texture::init(unsigned char* data, uint32_t width, uint32_t height, uint8_t channels)
{
m_width = width;
m_height = height;
if (channels == 4) {
m_internalFormat = GL_RGBA8;
m_dataFormat = GL_RGBA;
}
else if (channels == 3) {
m_internalFormat = GL_RGB8;
m_dataFormat = GL_RGB;
}
create(data);
}
void Texture::create(unsigned char* data)
void Texture2D::create(unsigned char* data)
{
m_id = UINT_MAX;
@ -109,10 +101,10 @@ void Texture::create(unsigned char* data)
data); // Image data
// Set the texture wrapping / filtering options
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); // Magnify
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); // Minify
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); // X
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); // Y
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); // Minify
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); // Magnify
// Automatically generate all mipmap levels
glGenerateMipmap(GL_TEXTURE_2D);
@ -123,6 +115,94 @@ void Texture::create(unsigned char* data)
// -----------------------------------------
std::shared_ptr<Texture> TextureCubemap::create(const std::string& path)
{
auto result = std::shared_ptr<TextureCubemap>(new TextureCubemap);
result->m_path = path;
result->create();
return result;
}
void TextureCubemap::bind(uint32_t unit) const
{
// Set active unit
glActiveTexture(GL_TEXTURE0 + unit);
glBindTexture(GL_TEXTURE_CUBE_MAP, m_id);
// Reset unit
glActiveTexture(GL_TEXTURE0);
}
void TextureCubemap::unbind() const
{
glBindTexture(GL_TEXTURE_CUBE_MAP, 0);
}
void TextureCubemap::create()
{
m_id = UINT_MAX;
// Create texture object
glGenTextures(1, &m_id);
// Bind texture object
glBindTexture(GL_TEXTURE_CUBE_MAP, m_id);
// Set unpacking of pixel data to byte-alignment,
// this prevents alignment issues when using a single byte for color
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
stbi_set_flip_vertically_on_load(0);
static constexpr const char* cubemapPaths[6] { "-px", "-nx", "-py", "-ny", "-pz", "-nz" };
size_t dotIndex = m_path.find_last_of('.');
std::string path = m_path.substr(0, dotIndex);
std::string extension = m_path.substr(dotIndex);
int width = 0;
int height = 0;
int channels = 0;
unsigned char* data = nullptr;
for (size_t i = 0; i < 6; ++i) {
std::string facePath = path + cubemapPaths[i] + extension;
// Load image data
data = stbi_load(facePath.c_str(), &width, &height, &channels, STBI_default);
VERIFY(data, "failed to load image: '{}'", facePath.c_str());
init(width, height, channels);
// Generate texture face
glTexImage2D(
GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, // Texture target
0, // Midmap level, base starts at level 0
m_internalFormat, // Internal format
m_width, m_height, // Image width/height
0, // Always 0 (legacy)
m_dataFormat, // Texture source format
GL_UNSIGNED_BYTE, // Texture source datatype
data); // Image data
// Clean resources
stbi_image_free(data);
}
// Set the texture wrapping / filtering options
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR); // Magnify
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR); // Minify
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); // X
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); // Y
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); // Z
// Unbind texture object
glBindTexture(GL_TEXTURE_CUBE_MAP, 0);
}
// -----------------------------------------
TextureManager::TextureManager(s)
{
ruc::info("TextureManager initialized");
@ -138,15 +218,16 @@ void TextureManager::add(const std::string& path, std::shared_ptr<Texture> textu
m_textureList.emplace(std::move(path), std::move(texture));
}
std::shared_ptr<Texture> TextureManager::load(const std::string& path)
std::shared_ptr<Texture> TextureManager::load(const std::string& path, Texture::Type type)
{
if (exists(path)) {
return get(path);
}
std::shared_ptr<Texture> texture = std::make_shared<Texture>(path);
auto texture = (type == Texture::TwoDimensional) ? Texture2D::create(path) : TextureCubemap::create(path);
add(path, texture);
return get(path);
return texture;
}
std::shared_ptr<Texture> TextureManager::get(const std::string& path)

93
src/inferno/render/texture.h

@ -1,5 +1,5 @@
/*
* Copyright (C) 2022 Riyyi
* Copyright (C) 2022,2024 Riyyi
*
* SPDX-License-Identifier: MIT
*/
@ -15,33 +15,88 @@
namespace Inferno {
class Texture2D;
class TextureCubemap;
class Texture {
public:
Texture(const std::string& path);
Texture(unsigned char* data, uint32_t width, uint32_t height, uint8_t channels = 3);
virtual ~Texture();
void bind(uint32_t unit = 0) const;
void unbind() const;
enum Type : uint8_t {
TwoDimensional = 0,
Cubemap,
};
void init(uint32_t width, uint32_t height, uint8_t channels);
virtual void bind(uint32_t unit = 0) const = 0;
virtual void unbind() const = 0;
std::string path() const { return m_path; }
uint32_t width() const { return m_width; }
uint32_t height() const { return m_height; }
uint32_t id() const { return m_id; }
uint32_t internalFormat() const { return m_internalFormat; }
uint32_t dataFormat() const { return m_dataFormat; }
inline std::string path() const { return m_path; }
inline uint32_t width() const { return m_width; }
inline uint32_t height() const { return m_height; }
inline uint32_t id() const { return m_id; }
inline uint32_t internalFormat() const { return m_internalFormat; }
inline uint32_t dataFormat() const { return m_dataFormat; }
virtual bool is2D() const { return false; }
virtual bool isCubeMap() const { return false; }
friend Texture2D;
friend TextureCubemap;
protected:
Texture() {}
protected:
void init(unsigned char* data, uint32_t width, uint32_t height, uint8_t channels);
std::string m_path;
uint32_t m_width { 0 };
uint32_t m_height { 0 };
uint32_t m_id { 0 };
uint32_t m_internalFormat { 0 };
uint32_t m_dataFormat { 0 };
};
// -------------------------------------
class Texture2D final : public Texture {
public:
virtual ~Texture2D() = default;
// Factory function
static std::shared_ptr<Texture> create(const std::string& path);
virtual void bind(uint32_t unit = 0) const override;
virtual void unbind() const override;
private:
Texture2D() {}
virtual bool is2D() const override { return true; }
private:
void create(unsigned char* data);
};
// -------------------------------------
class TextureCubemap final : public Texture {
public:
virtual ~TextureCubemap() = default;
// Factory function
static std::shared_ptr<Texture> create(const std::string& path);
virtual void bind(uint32_t unit = 0) const override;
virtual void unbind() const override;
private:
std::string m_path;
uint32_t m_width;
uint32_t m_height;
uint32_t m_id;
uint32_t m_internalFormat;
uint32_t m_dataFormat;
TextureCubemap() {};
virtual bool isCubeMap() const override { return true; }
private:
void create();
};
// -------------------------------------
@ -52,7 +107,7 @@ public:
~TextureManager();
void add(const std::string& path, std::shared_ptr<Texture> texture);
std::shared_ptr<Texture> load(const std::string& path);
std::shared_ptr<Texture> load(const std::string& path, Texture::Type type = Texture::Type::TwoDimensional);
std::shared_ptr<Texture> get(const std::string& path);
bool exists(const std::string& path);

8
src/inferno/scene/scene.cpp

@ -7,6 +7,7 @@
#include <cstddef> // size_t
#include <cstdint> // uint32_t
#include <limits> // std::numeric_limits
#include <utility> // std::pair
#include "entt/entity/entity.hpp" // ent::entity
#include "ruc/file.h"
@ -15,6 +16,7 @@
#include "ruc/meta/assert.h"
#include "inferno/component/cameracomponent.h"
#include "inferno/component/cubemap-component.h"
#include "inferno/component/id-component.h"
#include "inferno/component/luascriptcomponent.h"
#include "inferno/component/nativescriptcomponent.h"
@ -156,6 +158,10 @@ uint32_t Scene::loadEntity(ruc::Json components, uint32_t parentEntity)
auto& sprite = addComponent<SpriteComponent>(entity);
components.at("sprite").getTo(sprite);
}
if (components.exists("cubemap")) {
auto& cubemap = addComponent<CubemapComponent>(entity);
components.at("cubemap").getTo(cubemap);
}
if (components.exists("text")) {
auto& text = addComponent<TextAreaComponent>(entity);
components.at("text").getTo(text);
@ -192,7 +198,7 @@ void Scene::destroyEntity(uint32_t entity)
// -----------------------------------------
glm::mat4 Scene::cameraProjectionView()
std::pair<glm::mat4, glm::mat4> Scene::cameraProjectionView()
{
return CameraSystem::the().projectionView();
}

6
src/inferno/scene/scene.h

@ -9,6 +9,7 @@
#include <cstddef> // size_t
#include <cstdint> // uint32_t
#include <memory> // std::shared_ptr
#include <utility> // std::pair
#include "entt/entity/registry.hpp" // entt::entity, entt::registry
#include "glm/ext/matrix_float4x4.hpp" // glm::mat4
@ -35,7 +36,10 @@ public:
uint32_t findEntity(std::string_view name);
void destroyEntity(uint32_t entity);
glm::mat4 cameraProjectionView();
/**
* @brief Return a pair from the camera component: { projection, view }
*/
std::pair<glm::mat4, glm::mat4> cameraProjectionView();
void validEntity(uint32_t entity) const;

10
src/inferno/system/camerasystem.cpp

@ -1,9 +1,11 @@
/*
* Copyright (C) 2022 Riyyi
* Copyright (C) 2022,2024 Riyyi
*
* SPDX-License-Identifier: MIT
*/
#include <utility> // std::pair
#include "glm/ext/matrix_clip_space.hpp" // glm::perspective, glm::ortho
#include "glm/ext/matrix_transform.hpp" // glm::radians, glm::lookAt
#include "ruc/format/log.h"
@ -42,17 +44,17 @@ void CameraSystem::update()
}
}
glm::mat4 CameraSystem::projectionView()
std::pair<glm::mat4, glm::mat4> CameraSystem::projectionView()
{
auto view = m_registry->view<TransformComponent, CameraComponent>();
for (auto [entity, transform, camera] : view.each()) {
return camera.projection * transform.transform;
return { camera.projection, transform.transform };
}
VERIFY_NOT_REACHED();
return glm::mat4 { 1.0f };
return {};
}
void CameraSystem::updateOrthographic(TransformComponent& transform, CameraComponent& camera)

10
src/inferno/system/camerasystem.h

@ -1,12 +1,13 @@
/*
* Copyright (C) 2022 Riyyi
* Copyright (C) 2022,2024 Riyyi
*
* SPDX-License-Identifier: MIT
*/
#pragma once
#include <memory> //std::shared_ptr
#include <memory> //std::shared_ptr
#include <utility> // std::pair
#include "entt/entity/registry.hpp" // entt::entity, entt::registry
#include "glm/ext/matrix_float4x4.hpp" // glm::mat4
@ -27,7 +28,10 @@ public:
void update();
glm::mat4 projectionView();
/**
* @brief Return a pair from the camera component: { projection, view }
*/
std::pair<glm::mat4, glm::mat4> projectionView();
void setRegistry(std::shared_ptr<entt::registry> registry) { m_registry = registry; };

13
src/inferno/system/rendersystem.cpp

@ -1,5 +1,5 @@
/*
* Copyright (C) 2022 Riyyi
* Copyright (C) 2022,2024 Riyyi
*
* SPDX-License-Identifier: MIT
*/
@ -7,6 +7,7 @@
#include "glm/ext/matrix_transform.hpp" // glm::translate, glm::rotate, glm::scale, glm::radians
#include "ruc/format/log.h"
#include "inferno/component/cubemap-component.h"
#include "inferno/component/spritecomponent.h"
#include "inferno/component/transformcomponent.h"
#include "inferno/render/renderer.h"
@ -25,11 +26,17 @@ RenderSystem::~RenderSystem()
void RenderSystem::render()
{
auto group = m_registry->group<TransformComponent, SpriteComponent>();
auto quadView = m_registry->view<TransformComponent, SpriteComponent>();
for (auto [entity, transform, sprite] : group.each()) {
for (auto [entity, transform, sprite] : quadView.each()) {
Renderer2D::the().drawQuad(transform, sprite.color, sprite.texture);
}
auto cubemapView = m_registry->view<TransformComponent, CubemapComponent>();
for (auto [entity, transform, cubemap] : cubemapView.each()) {
RendererCubemap::the().drawCubemap(transform, cubemap.color, cubemap.texture);
}
}
} // namespace Inferno

Loading…
Cancel
Save