Compare commits

...

3 Commits

  1. 2
      README.org
  2. BIN
      assets/gfx/skybox-nx.jpg
  3. BIN
      assets/gfx/skybox-ny.jpg
  4. BIN
      assets/gfx/skybox-nz.jpg
  5. BIN
      assets/gfx/skybox-px.jpg
  6. BIN
      assets/gfx/skybox-py.jpg
  7. BIN
      assets/gfx/skybox-pz.jpg
  8. 49
      assets/glsl/batch-cubemap.frag
  9. 20
      assets/glsl/batch-cubemap.vert
  10. 139
      assets/scene/scene1.json
  11. 10
      src/inferno/application.cpp
  12. 26
      src/inferno/component/cameracomponent.cpp
  13. 3
      src/inferno/component/cameracomponent.h
  14. 27
      src/inferno/component/cubemap-component.cpp
  15. 25
      src/inferno/component/cubemap-component.h
  16. 22
      src/inferno/component/id-component.cpp
  17. 28
      src/inferno/component/id-component.h
  18. 21
      src/inferno/component/luascriptcomponent.cpp
  19. 7
      src/inferno/component/luascriptcomponent.h
  20. 21
      src/inferno/component/nativescriptcomponent.cpp
  21. 19
      src/inferno/component/nativescriptcomponent.h
  22. 22
      src/inferno/component/tagcomponent.cpp
  23. 4
      src/inferno/component/tagcomponent.h
  24. 4
      src/inferno/component/textareacomponent.h
  25. 9
      src/inferno/component/transformcomponent.h
  26. 2
      src/inferno/render/font.cpp
  27. 184
      src/inferno/render/renderer.cpp
  28. 62
      src/inferno/render/renderer.h
  29. 163
      src/inferno/render/texture.cpp
  30. 93
      src/inferno/render/texture.h
  31. 142
      src/inferno/scene/scene.cpp
  32. 19
      src/inferno/scene/scene.h
  33. 4
      src/inferno/script/nativescript.h
  34. 10
      src/inferno/system/camerasystem.cpp
  35. 10
      src/inferno/system/camerasystem.h
  36. 13
      src/inferno/system/rendersystem.cpp
  37. 30
      src/inferno/system/transformsystem.cpp
  38. 11
      src/inferno/system/transformsystem.h
  39. 38
      src/inferno/uid.cpp
  40. 47
      src/inferno/uid.h

2
README.org

@ -48,3 +48,5 @@ $ cmake .. && make
- [[https://www.glfw.org/docs/latest/build_guide.html#build_link_cmake_source][Build GLFW using CMake]] - [[https://www.glfw.org/docs/latest/build_guide.html#build_link_cmake_source][Build GLFW using CMake]]
- [[https://learnopengl.com][Learn OpenGL]] - [[https://learnopengl.com][Learn OpenGL]]
- [[https://www.youtube.com/playlist?list=PLlrATfBNZ98dC-V-N3m0Go4deliWHPFwT][Game Engine]] by The Cherno - [[https://www.youtube.com/playlist?list=PLlrATfBNZ98dC-V-N3m0Go4deliWHPFwT][Game Engine]] by The Cherno
- [[https://www.youtube.com/watch?v=mnIQEQoHHCU][OpenGL 3D Game Tutorial 32: Font Rendering]]
- [[https://youtu.be/d8cfgcJR9Tk][OpenGL 3D Game Tutorial 33: Distance Field Text Rendering]]

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);
}

139
assets/scene/scene1.json

@ -1,50 +1,111 @@
{ {
"init": "assets/lua/scene1-init.lua", "init": "assets/lua/scene1-init.lua",
"camera": { "entities": [
"name": "Camera",
"translate": [ 0.0, 0.0, 1.0 ],
"rotate": [ 0.0, 0.0, -1.0 ],
"scale": [ 1.0, 1.0, 1.0 ],
"type": "perspective",
"script": {
"type": "lua",
"name": "assets/lua/cameracontroller.lua"
}
},
"quad": [
{ {
"name": "Quad", "id": { "id": 12312312 },
"translate": [ 0.0, 0.0, 0.0 ], "tag": { "tag": "Camera" },
"rotate": [ 0.0, 0.0, 0.0 ], "transform" : {
"scale": [ 1.0, 1.0, 1.0 ], "translate": [0.0, 0.0, 1.0],
"color": [ 1.0, 1.0, 1.0, 1.0 ], "rotate": [0.0, 0.0, -1.0],
"texture": "assets/gfx/test.png" "scale": [1.0, 1.0, 1.0]
},
"camera": { "type": "perspective" },
"lua-scripts": [
{ "path": "assets/lua/cameracontroller.lua" }
]
}, },
{ {
"name": "Quad 2", "id": { "id": 212563732 },
"translate": [ 1.1, 0.0, 0.0 ], "tag": { "tag": "Skybox" },
"rotate": [ 0.0, 0.0, 0.0 ], "transform" : {
"scale": [ 1.0, 1.0, 1.0 ], "translate": [0.0, 0.0, 0.0],
"color": [ 0.5, 0.6, 0.8, 1.0 ], "rotate": [0.0, 0.0, 0.0],
"texture": "assets/gfx/test.png" "scale": [100.0, 100.0, 100.0]
},
"cubemap": {
"color": [ 1.0, 1.0, 1.0, 1.0 ],
"texture": "assets/gfx/skybox.jpg"
}
}, },
{ {
"name": "Quad 3", "id": { "id": 564564564 },
"translate": [ 2.2, 0.0, 0.0 ], "tag": { "tag": "Quad" },
"rotate": [ 0.0, 0.0, 0.0 ], "transform" : {
"scale": [ 1.0, 1.0, 1.0 ], "translate": [ 0.0, 0.0, 0.0 ],
"color": [ 1.0, 1.0, 1.0, 1.0 ], "rotate": [ 0.0, 0.0, 0.0 ],
"texture": "assets/gfx/test-inverted.png" "scale": [ 1.0, 1.0, 1.0 ]
} },
], "sprite": {
"text": [ "color": [ 1.0, 1.0, 1.0, 1.0 ],
"texture": "assets/gfx/test.png"
}
},
{
"id": { "id": 97897897 },
"tag": { "tag": "Quad 2" },
"transform" : {
"translate": [ 1.1, 0.0, 0.0 ],
"rotate": [ 0.0, 0.0, 0.0 ],
"scale": [ 1.0, 1.0, 1.0 ]
},
"sprite": {
"color": [ 0.5, 0.6, 0.8, 1.0 ],
"texture": "assets/gfx/test.png"
}
},
{
"id": { "id": 3424242 },
"tag": { "tag": "Quad 3" },
"transform" : {
"translate": [ 2.2, 1.0, 0.0 ],
"rotate": [ 0.0, 0.0, -20.0 ],
"scale": [ 1.0, 1.0, 1.0 ]
},
"sprite": {
"color": [ 1.0, 1.0, 1.0, 1.0 ],
"texture": "assets/gfx/test-inverted.png"
},
"children": [
{
"id": { "id": 4345472 },
"tag": { "tag": "Quad 4" },
"transform" : {
"translate": [ 0.85, 0.0, 0.0 ],
"rotate": [ 0.0, 0.0, 0.0 ],
"scale": [ 0.5, 0.5, 1.0 ]
},
"sprite": {
"color": [ 1.0, 1.0, 1.0, 1.0 ],
"texture": "assets/gfx/test-inverted.png"
},
"children": [
{
"id": { "id": 5234723 },
"tag": { "tag": "Quad 5" },
"transform" : {
"translate": [ 1.0, 0.0, 0.0 ],
"rotate": [ 0.0, 0.0, -20.0 ],
"scale": [ 0.5, 0.5, 1.0 ]
},
"sprite": {
"color": [ 1.0, 1.0, 1.0, 1.0 ],
"texture": "assets/gfx/test-inverted.png"
}
}
]
}
]
},
{ {
"name": "Text", "id": { "id": 675754 },
"content": "Hello World!", "tag": { "tag": "Text" },
"font": "assets/fnt/open-sans", "text": {
"font-size": 24, "content": "Hello World!",
"line-spacing": 1.0, "font": "assets/fnt/open-sans",
"width": 150 "font-size": 24,
"line-spacing": 1.0,
"width": 150
}
} }
] ]
} }

10
src/inferno/application.cpp

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

26
src/inferno/component/cameracomponent.cpp

@ -0,0 +1,26 @@
/*
* Copyright (C) 2023 Riyyi
*
* SPDX-License-Identifier: MIT
*/
#include "ruc/format/print.h"
#include "ruc/json/json.h"
#include "inferno/component/cameracomponent.h"
namespace Inferno {
void fromJson(const ruc::Json& json, CameraComponent& value)
{
VERIFY(json.type() == ruc::Json::Type::Object);
if (json.exists("type")) {
value.type = json.at("type").get<std::string>() == "orthographic" ? CameraType::Orthographic : CameraType::Perspective;
}
if (json.exists("zoom-level")) {
json.at("zoom-level").getTo(value.zoomLevel);
}
}
} // namespace Inferno

3
src/inferno/component/cameracomponent.h

@ -8,6 +8,7 @@
#include "glm/ext/matrix_float4x4.hpp" // glm::mat4 #include "glm/ext/matrix_float4x4.hpp" // glm::mat4
#include "glm/ext/vector_float3.hpp" // glm::vec3 #include "glm/ext/vector_float3.hpp" // glm::vec3
#include "ruc/json/json.h"
namespace Inferno { namespace Inferno {
@ -32,4 +33,6 @@ struct CameraComponent {
glm::mat4 projection { 1.0f }; // Identity matrix glm::mat4 projection { 1.0f }; // Identity matrix
}; };
void fromJson(const ruc::Json& json, CameraComponent& value);
} // namespace Inferno } // namespace Inferno

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

22
src/inferno/component/id-component.cpp

@ -0,0 +1,22 @@
/*
* Copyright (C) 2023 Riyyi
*
* SPDX-License-Identifier: MIT
*/
#include "ruc/json/json.h"
#include "inferno/component/id-component.h"
namespace Inferno {
void fromJson(const ruc::Json& json, IDComponent& value)
{
VERIFY(json.type() == ruc::Json::Type::Object);
if (json.exists("id")) {
json.at("id").getTo(value.id);
}
}
} // namespace Inferno

28
src/inferno/component/id-component.h

@ -0,0 +1,28 @@
/*
* Copyright (C) 2023 Riyyi
*
* SPDX-License-Identifier: MIT
*/
#pragma once
#include "ruc/json/json.h"
#include "inferno/uid.h"
namespace Inferno {
struct IDComponent {
UID id;
IDComponent() = default;
IDComponent(UID id)
: id(id)
{
}
IDComponent(const IDComponent&) = default;
};
void fromJson(const ruc::Json& json, IDComponent& value);
} // namespace Inferno

21
src/inferno/component/luascriptcomponent.cpp

@ -0,0 +1,21 @@
/*
* Copyright (C) 2023 Riyyi
*
* SPDX-License-Identifier: MIT
*/
#include "ruc/json/json.h"
#include "inferno/component/luascriptcomponent.h"
namespace Inferno {
void fromJson(const ruc::Json& json, LuaScriptComponent& value)
{
VERIFY(json.type() == ruc::Json::Type::Object);
VERIFY(json.exists("path"), "path not found");
json.at("path").getTo(value.path);
}
} // namespace Inferno

7
src/inferno/component/luascriptcomponent.h

@ -6,9 +6,11 @@
#pragma once #pragma once
#include <string> // std::string #include <string>
#include <utility> // std::move #include <utility> // std::move
#include "ruc/json/json.h"
namespace Inferno { namespace Inferno {
class LuaScript; class LuaScript;
@ -24,4 +26,7 @@ struct LuaScriptComponent {
{ {
} }
}; };
void fromJson(const ruc::Json& json, LuaScriptComponent& value);
} // namespace Inferno } // namespace Inferno

21
src/inferno/component/nativescriptcomponent.cpp

@ -0,0 +1,21 @@
/*
* Copyright (C) 2023 Riyyi
*
* SPDX-License-Identifier: MIT
*/
#include "ruc/json/json.h"
#include "inferno/component/nativescriptcomponent.h"
namespace Inferno {
void fromJson(const ruc::Json& json, NativeScriptComponent& value)
{
VERIFY(json.type() == ruc::Json::Type::Object);
VERIFY(json.exists("name"), "name not found");
json.at("name").getTo(value.name);
}
} // namespace Inferno

19
src/inferno/component/nativescriptcomponent.h

@ -7,6 +7,7 @@
#pragma once #pragma once
#include <string> #include <string>
#include <utility> // std::move
#include "inferno/script/nativescript.h" #include "inferno/script/nativescript.h"
@ -14,17 +15,29 @@ namespace Inferno {
struct NativeScriptComponent { struct NativeScriptComponent {
NativeScript* instance { nullptr }; NativeScript* instance { nullptr };
std::string name;
NativeScript::InitializeFunction initialize { nullptr }; NativeScript::InitializeFunction initialize { nullptr };
NativeScript::DestroyFunction destroy { nullptr }; NativeScript::DestroyFunction destroy { nullptr };
// Dont allow manually setting instance during construction // Dont allow manually setting instance during construction
NativeScriptComponent() {} NativeScriptComponent() {}
NativeScriptComponent(const std::string& binding) NativeScriptComponent(const std::string& name)
: name(std::move(name))
{ {
initialize = NativeScriptBinding::the().initializeBinding(binding); bind();
destroy = NativeScriptBinding::the().destroyBinding(binding); }
void bind()
{
VERIFY(initialize == nullptr && destroy == nullptr, "NativeScript already bound");
VERIFY(name != "", "name not set");
initialize = NativeScriptBinding::the().initializeBinding(name);
destroy = NativeScriptBinding::the().destroyBinding(name);
} }
}; };
void fromJson(const ruc::Json& json, NativeScriptComponent& value);
} // namespace Inferno } // namespace Inferno

22
src/inferno/component/tagcomponent.cpp

@ -0,0 +1,22 @@
/*
* Copyright (C) 2023 Riyyi
*
* SPDX-License-Identifier: MIT
*/
#include "ruc/json/json.h"
#include "inferno/component/tagcomponent.h"
namespace Inferno {
void fromJson(const ruc::Json& json, TagComponent& value)
{
VERIFY(json.type() == ruc::Json::Type::Object);
if (json.exists("tag")) {
json.at("tag").getTo(value.tag);
}
}
} // namespace Inferno

4
src/inferno/component/tagcomponent.h

@ -9,6 +9,8 @@
#include <string> // std::string #include <string> // std::string
#include <utility> // std::move #include <utility> // std::move
#include "ruc/json/json.h"
namespace Inferno { namespace Inferno {
struct TagComponent { struct TagComponent {
@ -23,4 +25,6 @@ struct TagComponent {
operator const std::string&() const { return tag; } operator const std::string&() const { return tag; }
}; };
void fromJson(const ruc::Json& json, TagComponent& value);
} // namespace Inferno } // namespace Inferno

4
src/inferno/component/textareacomponent.h

@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2022 Riyyi * Copyright (C) 2022-2024 Riyyi
* *
* SPDX-License-Identifier: MIT * SPDX-License-Identifier: MIT
*/ */
@ -18,7 +18,7 @@ namespace Inferno {
struct TextAreaComponent { struct TextAreaComponent {
std::string content; std::string content;
std::string font; std::string font;
unsigned char fontSize { 0 }; unsigned char fontSize { 12 };
float lineSpacing { 1.0f }; float lineSpacing { 1.0f };
uint32_t width { 0 }; uint32_t width { 0 };

9
src/inferno/component/transformcomponent.h

@ -1,11 +1,16 @@
/* /*
* Copyright (C) 2022 Riyyi * Copyright (C) 2022,2024 Riyyi
* *
* SPDX-License-Identifier: MIT * SPDX-License-Identifier: MIT
*/ */
#pragma once #pragma once
#include <cstdint> // uint32_t
#include <optional>
#include "entt/entity/entity.hpp" // entt::null
#include "entt/entity/fwd.hpp" // entt::entity
#include "glm/ext/matrix_float4x4.hpp" // glm::mat4 #include "glm/ext/matrix_float4x4.hpp" // glm::mat4
#include "glm/ext/vector_float3.hpp" // glm::vec3 #include "glm/ext/vector_float3.hpp" // glm::vec3
#include "ruc/format/format.h" #include "ruc/format/format.h"
@ -17,6 +22,8 @@ struct TransformComponent {
glm::vec3 translate { 0.0f, 0.0f, 0.0f }; glm::vec3 translate { 0.0f, 0.0f, 0.0f };
glm::vec3 rotate { 0.0f, 0.0f, 0.0f }; glm::vec3 rotate { 0.0f, 0.0f, 0.0f };
glm::vec3 scale { 1.0f, 1.0f, 1.0f }; glm::vec3 scale { 1.0f, 1.0f, 1.0f };
entt::entity parent { entt::null };
glm::mat4 transform { 1.0f }; // Identity matrix glm::mat4 transform { 1.0f }; // Identity matrix
}; };

2
src/inferno/render/font.cpp

@ -32,7 +32,7 @@ Font::Font(const std::string& name)
std::string font = ruc::File(path).data(); std::string font = ruc::File(path).data();
parseFont(font); parseFont(font);
m_texture = std::make_shared<Texture>(image); m_texture = Texture2D::create(image);
} }
// TODO: Move this to ruc // 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 * SPDX-License-Identifier: MIT
*/ */
@ -216,10 +216,10 @@ Renderer2D::~Renderer2D()
Renderer::destroy(); Renderer::destroy();
} }
void Renderer2D::beginScene(glm::mat4 cameraProjectionView) void Renderer2D::beginScene(glm::mat4 cameraProjection, glm::mat4 cameraView)
{ {
m_shader->bind(); m_shader->bind();
m_shader->setFloat("u_projectionView", cameraProjectionView); m_shader->setFloat("u_projectionView", cameraProjection * cameraView);
m_shader->unbind(); 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) RendererCharacter::RendererCharacter(s)
{ {
Renderer::initialize(); 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 * SPDX-License-Identifier: MIT
*/ */
@ -27,7 +27,13 @@ struct QuadVertex {
glm::vec3 position { 0.0f, 0.0f, 0.0f }; glm::vec3 position { 0.0f, 0.0f, 0.0f };
glm::vec4 color { 1.0f, 1.0f, 1.0f, 1.0f }; glm::vec4 color { 1.0f, 1.0f, 1.0f, 1.0f };
glm::vec2 textureCoordinates { 0.0f, 0.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 { struct CharacterVertex {
@ -66,9 +72,10 @@ public:
class Renderer { class Renderer {
public: public:
static const uint32_t vertexPerQuad = 4; static constexpr const uint32_t vertexPerQuad = 4;
static const uint32_t indexPerQuad = 6; static constexpr const uint32_t indexPerQuad = 6;
static const uint32_t textureUnitPerBatch = 32; static constexpr const uint32_t quadPerCube = 6;
static constexpr const uint32_t textureUnitPerBatch = 32;
protected: protected:
Renderer() {} Renderer() {}
@ -108,11 +115,12 @@ public:
using Singleton<Renderer2D>::destroy; using Singleton<Renderer2D>::destroy;
static const uint32_t quadCount = 1000; // When to start a new batch
static const uint32_t vertexCount = quadCount * vertexPerQuad; static constexpr const uint32_t quadCount = 1000;
static const uint32_t indexCount = quadCount * indexPerQuad; 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 endScene();
void drawQuad(const TransformComponent& transform, glm::vec4 color); void drawQuad(const TransformComponent& transform, glm::vec4 color);
@ -133,6 +141,42 @@ private:
// Default quad vertex positions // Default quad vertex positions
glm::vec4 m_vertexPositions[vertexPerQuad]; 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 * SPDX-License-Identifier: MIT
*/ */
@ -19,36 +19,47 @@
namespace Inferno { namespace Inferno {
Texture::Texture(const std::string& path) Texture::~Texture()
: m_path(std::move(path))
{ {
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 width = 0;
int height = 0; int height = 0;
int channels = 0; int channels = 0;
unsigned char* data = nullptr;
// Load image data // Load image data
stbi_set_flip_vertically_on_load(1); stbi_set_flip_vertically_on_load(1);
data = stbi_load(path.c_str(), &width, &height, &channels, STBI_default); data = stbi_load(path.c_str(), &width, &height, &channels, STBI_default);
VERIFY(data, "failed to load image: '{}'", path); VERIFY(data, "failed to load image: '{}'", path);
init(data, width, height, channels); result->init(width, height, channels);
result->create(data);
// Clean resources // Clean resources
stbi_image_free(data); stbi_image_free(data);
}
Texture::Texture(unsigned char* data, uint32_t width, uint32_t height, uint8_t channels) return result;
{
init(data, width, height, channels);
}
Texture::~Texture()
{
glDeleteTextures(1, &m_id);
} }
void Texture::bind(uint32_t unit) const void Texture2D::bind(uint32_t unit) const
{ {
// Set active unit // Set active unit
glActiveTexture(GL_TEXTURE0 + unit); glActiveTexture(GL_TEXTURE0 + unit);
@ -59,31 +70,12 @@ void Texture::bind(uint32_t unit) const
glActiveTexture(GL_TEXTURE0); glActiveTexture(GL_TEXTURE0);
} }
void Texture::unbind() const void Texture2D::unbind() const
{ {
glBindTexture(GL_TEXTURE_2D, 0); glBindTexture(GL_TEXTURE_2D, 0);
} }
// ----------------------------------------- void Texture2D::create(unsigned char* data)
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)
{ {
m_id = UINT_MAX; m_id = UINT_MAX;
@ -109,10 +101,10 @@ void Texture::create(unsigned char* data)
data); // Image data data); // Image data
// Set the texture wrapping / filtering options // 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_S, GL_REPEAT); // X
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); // Y 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 // Automatically generate all mipmap levels
glGenerateMipmap(GL_TEXTURE_2D); 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) TextureManager::TextureManager(s)
{ {
ruc::info("TextureManager initialized"); 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)); 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)) { if (exists(path)) {
return get(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); add(path, texture);
return get(path);
return texture;
} }
std::shared_ptr<Texture> TextureManager::get(const std::string& path) 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 * SPDX-License-Identifier: MIT
*/ */
@ -15,33 +15,88 @@
namespace Inferno { namespace Inferno {
class Texture2D;
class TextureCubemap;
class Texture { class Texture {
public: public:
Texture(const std::string& path);
Texture(unsigned char* data, uint32_t width, uint32_t height, uint8_t channels = 3);
virtual ~Texture(); virtual ~Texture();
void bind(uint32_t unit = 0) const; enum Type : uint8_t {
void unbind() const; 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; } virtual bool is2D() const { return false; }
inline uint32_t width() const { return m_width; } virtual bool isCubeMap() const { return false; }
inline uint32_t height() const { return m_height; }
inline uint32_t id() const { return m_id; } friend Texture2D;
inline uint32_t internalFormat() const { return m_internalFormat; } friend TextureCubemap;
inline uint32_t dataFormat() const { return m_dataFormat; }
protected:
Texture() {}
protected: 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); 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: private:
std::string m_path; TextureCubemap() {};
uint32_t m_width;
uint32_t m_height; virtual bool isCubeMap() const override { return true; }
uint32_t m_id;
uint32_t m_internalFormat; private:
uint32_t m_dataFormat; void create();
}; };
// ------------------------------------- // -------------------------------------
@ -52,7 +107,7 @@ public:
~TextureManager(); ~TextureManager();
void add(const std::string& path, std::shared_ptr<Texture> texture); 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); std::shared_ptr<Texture> get(const std::string& path);
bool exists(const std::string& path); bool exists(const std::string& path);

142
src/inferno/scene/scene.cpp

@ -1,18 +1,23 @@
/* /*
* Copyright (C) 2022 Riyyi * Copyright (C) 2022-2024 Riyyi
* *
* SPDX-License-Identifier: MIT * SPDX-License-Identifier: MIT
*/ */
#include <cstddef> // size_t
#include <cstdint> // uint32_t #include <cstdint> // uint32_t
#include <limits> // std::numeric_limits #include <limits> // std::numeric_limits
#include <utility> // std::pair
#include "entt/entity/entity.hpp" // ent::entity
#include "ruc/file.h" #include "ruc/file.h"
#include "ruc/format/log.h" #include "ruc/format/log.h"
#include "ruc/json/json.h" #include "ruc/json/json.h"
#include "ruc/meta/assert.h" #include "ruc/meta/assert.h"
#include "inferno/component/cameracomponent.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/luascriptcomponent.h"
#include "inferno/component/nativescriptcomponent.h" #include "inferno/component/nativescriptcomponent.h"
#include "inferno/component/spritecomponent.h" #include "inferno/component/spritecomponent.h"
@ -26,6 +31,7 @@
#include "inferno/system/scriptsystem.h" #include "inferno/system/scriptsystem.h"
#include "inferno/system/textareasystem.h" #include "inferno/system/textareasystem.h"
#include "inferno/system/transformsystem.h" #include "inferno/system/transformsystem.h"
#include "inferno/uid.h"
namespace Inferno { namespace Inferno {
@ -47,54 +53,19 @@ void Scene::initialize()
auto sceneJson = ruc::Json::parse(ruc::File("assets/scene/scene1.json").data()); auto sceneJson = ruc::Json::parse(ruc::File("assets/scene/scene1.json").data());
// Camera if (sceneJson.exists("init")) {
// TODO: load either NativeScript or LuaScript?
VERIFY(sceneJson.exists("camera"), "scene doesnt contain a camera");
auto& cameraJson = sceneJson.at("camera");
uint32_t camera = loadEntity(cameraJson);
auto& cameraComponent = addComponent<CameraComponent>(camera);
if (cameraJson.exists("type") && cameraJson.at("type").get<std::string>() == "orthographic") {
cameraComponent.type = CameraType::Orthographic;
}
if (cameraJson.exists("zoom-level") && cameraJson.at("zoom-level").type() == ruc::Json::Type::Number) {
cameraComponent.zoomLevel = cameraJson.at("zoom-level").asDouble();
}
if (cameraJson.exists("script")) {
auto& cameraScript = cameraJson.at("script");
if (cameraScript.exists("type") && cameraScript.exists("name")) {
auto name = cameraScript.at("name").get<std::string>();
if (cameraScript.at("type").get<std::string>() == "lua") {
addComponent<LuaScriptComponent>(camera, name);
}
else {
addComponent<NativeScriptComponent>(camera, name);
}
}
}
// Quads
if (sceneJson.exists("quad") && sceneJson.at("quad").type() == ruc::Json::Type::Array) {
auto& quads = sceneJson.at("quad").asArray().elements();
for (const auto& quad : quads) {
uint32_t quadEntity = loadEntity(quad);
addComponent<SpriteComponent>(quadEntity);
auto& spriteComponent = getComponent<SpriteComponent>(quadEntity);
quad.getTo(spriteComponent);
}
} }
// Text // Entities
// -------------------------------------
if (sceneJson.exists("text") && sceneJson.at("text").type() == ruc::Json::Type::Array) { if (sceneJson.exists("entities")) {
auto& texts = sceneJson.at("text").asArray().elements(); const auto& entityJson = sceneJson.at("entities");
for (const auto& text : texts) { VERIFY(entityJson.type() == ruc::Json::Type::Array);
uint32_t textEntity = loadEntity(text); const auto& entities = entityJson.asArray();
addComponent<TextAreaComponent>(textEntity); for (size_t i = 0; i < entities.size(); ++i) {
auto& textAreaComponent = getComponent<TextAreaComponent>(textEntity); loadEntity(entities.at(i));
text.getTo(textAreaComponent);
} }
} }
@ -123,22 +94,85 @@ void Scene::destroy()
TransformSystem::destroy(); TransformSystem::destroy();
} }
// -----------------------------------------
uint32_t Scene::createEntity(const std::string& name) uint32_t Scene::createEntity(const std::string& name)
{
return createEntityWithUID(UID(), name);
}
uint32_t Scene::createEntityWithUID(UID id, const std::string& name)
{ {
uint32_t entity = static_cast<uint32_t>(m_registry->create()); uint32_t entity = static_cast<uint32_t>(m_registry->create());
addComponent<IDComponent>(entity, id);
addComponent<TagComponent>(entity, name.empty() ? "Unnamed Entity" : name); addComponent<TagComponent>(entity, name.empty() ? "Unnamed Entity" : name);
addComponent<TransformComponent>(entity); addComponent<TransformComponent>(entity);
TransformSystem::the().add(entity);
return entity; return entity;
} }
uint32_t Scene::loadEntity(ruc::Json json) uint32_t Scene::loadEntity(ruc::Json components, uint32_t parentEntity)
{ {
uint32_t entity = createEntity((json.exists("name")) VERIFY(components.type() == ruc::Json::Type::Object);
? json.at("name").get<std::string>()
: ""); uint32_t entity = createEntity();
auto& transform = getComponent<TransformComponent>(entity);
json.getTo(transform); // At minimum, ID is required
VERIFY(components.exists("id"), "id not found");
auto& id = getComponent<IDComponent>(entity);
components.at("id").getTo(id);
if (components.exists("tag")) {
auto& tag = getComponent<TagComponent>(entity);
components.at("tag").getTo(tag);
}
if (components.exists("transform")) {
auto& transform = getComponent<TransformComponent>(entity);
components.at("transform").getTo(transform);
transform.parent = static_cast<entt::entity>(parentEntity);
}
if (components.exists("camera")) {
auto& camera = addComponent<CameraComponent>(entity);
components.at("camera").getTo(camera);
}
if (components.exists("lua-scripts")) {
VERIFY(components.at("lua-scripts").type() == ruc::Json::Type::Array);
const auto& scripts = components.at("lua-scripts").asArray();
for (size_t i = 0; i < scripts.size(); ++i) {
auto& script = addComponent<LuaScriptComponent>(entity);
scripts.at(i).getTo(script);
}
}
if (components.exists("native-scripts")) {
VERIFY(components.at("native-scripts").type() == ruc::Json::Type::Array);
const auto& scripts = components.at("native-scripts").asArray();
for (size_t i = 0; i < scripts.size(); ++i) {
auto& script = addComponent<NativeScriptComponent>(entity);
scripts.at(i).getTo(script);
script.bind();
}
}
if (components.exists("sprite")) {
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);
}
if (components.exists("children")) {
VERIFY(components.at("children").type() == ruc::Json::Type::Array);
const auto& children = components.at("children").asArray();
for (size_t i = 0; i < children.size(); ++i) {
loadEntity(components.at("children")[i], entity);
}
}
return entity; return entity;
} }
@ -162,7 +196,9 @@ void Scene::destroyEntity(uint32_t entity)
m_registry->destroy(entt::entity { entity }); m_registry->destroy(entt::entity { entity });
} }
glm::mat4 Scene::cameraProjectionView() // -----------------------------------------
std::pair<glm::mat4, glm::mat4> Scene::cameraProjectionView()
{ {
return CameraSystem::the().projectionView(); return CameraSystem::the().projectionView();
} }

19
src/inferno/scene/scene.h

@ -1,19 +1,23 @@
/* /*
* Copyright (C) 2022 Riyyi * Copyright (C) 2022,2024 Riyyi
* *
* SPDX-License-Identifier: MIT * SPDX-License-Identifier: MIT
*/ */
#pragma once #pragma once
#include <cstddef> // size_t
#include <cstdint> // uint32_t #include <cstdint> // uint32_t
#include <memory> // std::shared_ptr #include <memory> // std::shared_ptr
#include <utility> // std::pair
#include "entt/entity/registry.hpp" // entt::entity, entt::registry #include "entt/entity/registry.hpp" // entt::entity, entt::registry
#include "glm/ext/matrix_float4x4.hpp" // glm::mat4 #include "glm/ext/matrix_float4x4.hpp" // glm::mat4
#include "ruc/format/format.h" #include "ruc/format/format.h"
#include "ruc/json/json.h" #include "ruc/json/json.h"
#include "inferno/uid.h"
namespace Inferno { namespace Inferno {
class Camera; class Camera;
@ -27,11 +31,15 @@ public:
void destroy(); void destroy();
uint32_t createEntity(const std::string& name = ""); uint32_t createEntity(const std::string& name = "");
uint32_t loadEntity(ruc::Json json); uint32_t createEntityWithUID(UID id, const std::string& name = "");
uint32_t loadEntity(ruc::Json components, uint32_t parentEntity = entt::null);
uint32_t findEntity(std::string_view name); uint32_t findEntity(std::string_view name);
void destroyEntity(uint32_t entity); 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; void validEntity(uint32_t entity) const;
@ -49,7 +57,8 @@ public:
return m_registry->any<T...>(entt::entity { entity }); return m_registry->any<T...>(entt::entity { entity });
} }
// @Todo Should replace be allowed? could trigger memory leaks with nativescript // TODO: Should replace be allowed? could trigger memory leaks with nativescript
// TODO: Replace will make it so an entity cant have multiple scripts
template<typename T, typename... P> template<typename T, typename... P>
T& addComponent(uint32_t entity, P&&... parameters) const T& addComponent(uint32_t entity, P&&... parameters) const
{ {
@ -64,7 +73,7 @@ public:
return m_registry->remove_if_exists<T>(entt::entity { entity }); return m_registry->remove_if_exists<T>(entt::entity { entity });
} }
// @Todo Should replace be allowed? could trigger memory leaks with nativescript // TODO: Should replace be allowed? could trigger memory leaks with nativescript
template<typename T, typename... P> template<typename T, typename... P>
T& getComponent(uint32_t entity, P&&... parameters) const T& getComponent(uint32_t entity, P&&... parameters) const
{ {

4
src/inferno/script/nativescript.h

@ -55,8 +55,8 @@ public:
m_detroyBindings.emplace(binding, destroy); m_detroyBindings.emplace(binding, destroy);
} }
NativeScript::InitializeFunction initializeBinding(const std::string& binding) { return m_initializeBindings[binding]; } NativeScript::InitializeFunction initializeBinding(const std::string& name) { return m_initializeBindings[name]; }
NativeScript::DestroyFunction destroyBinding(const std::string& binding) { return m_detroyBindings[binding]; } NativeScript::DestroyFunction destroyBinding(const std::string& name) { return m_detroyBindings[name]; }
private: private:
std::unordered_map<std::string, NativeScript::InitializeFunction> m_initializeBindings; std::unordered_map<std::string, NativeScript::InitializeFunction> m_initializeBindings;

10
src/inferno/system/camerasystem.cpp

@ -1,9 +1,11 @@
/* /*
* Copyright (C) 2022 Riyyi * Copyright (C) 2022,2024 Riyyi
* *
* SPDX-License-Identifier: MIT * SPDX-License-Identifier: MIT
*/ */
#include <utility> // std::pair
#include "glm/ext/matrix_clip_space.hpp" // glm::perspective, glm::ortho #include "glm/ext/matrix_clip_space.hpp" // glm::perspective, glm::ortho
#include "glm/ext/matrix_transform.hpp" // glm::radians, glm::lookAt #include "glm/ext/matrix_transform.hpp" // glm::radians, glm::lookAt
#include "ruc/format/log.h" #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>(); auto view = m_registry->view<TransformComponent, CameraComponent>();
for (auto [entity, transform, camera] : view.each()) { for (auto [entity, transform, camera] : view.each()) {
return camera.projection * transform.transform; return { camera.projection, transform.transform };
} }
VERIFY_NOT_REACHED(); VERIFY_NOT_REACHED();
return glm::mat4 { 1.0f }; return {};
} }
void CameraSystem::updateOrthographic(TransformComponent& transform, CameraComponent& camera) 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 * SPDX-License-Identifier: MIT
*/ */
#pragma once #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 "entt/entity/registry.hpp" // entt::entity, entt::registry
#include "glm/ext/matrix_float4x4.hpp" // glm::mat4 #include "glm/ext/matrix_float4x4.hpp" // glm::mat4
@ -27,7 +28,10 @@ public:
void update(); 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; }; 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 * SPDX-License-Identifier: MIT
*/ */
@ -7,6 +7,7 @@
#include "glm/ext/matrix_transform.hpp" // glm::translate, glm::rotate, glm::scale, glm::radians #include "glm/ext/matrix_transform.hpp" // glm::translate, glm::rotate, glm::scale, glm::radians
#include "ruc/format/log.h" #include "ruc/format/log.h"
#include "inferno/component/cubemap-component.h"
#include "inferno/component/spritecomponent.h" #include "inferno/component/spritecomponent.h"
#include "inferno/component/transformcomponent.h" #include "inferno/component/transformcomponent.h"
#include "inferno/render/renderer.h" #include "inferno/render/renderer.h"
@ -25,11 +26,17 @@ RenderSystem::~RenderSystem()
void RenderSystem::render() 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); 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 } // namespace Inferno

30
src/inferno/system/transformsystem.cpp

@ -1,9 +1,13 @@
/* /*
* Copyright (C) 2022 Riyyi * Copyright (C) 2022,2024 Riyyi
* *
* SPDX-License-Identifier: MIT * SPDX-License-Identifier: MIT
*/ */
#include <algorithm> // std::sort
#include <cstdint> // uint32_t
#include "entt/entity/fwd.hpp" // entt:entity
#include "glm/ext/matrix_transform.hpp" // glm::translate, glm::rotate, glm::scale, glm::radians #include "glm/ext/matrix_transform.hpp" // glm::translate, glm::rotate, glm::scale, glm::radians
#include "ruc/format/log.h" #include "ruc/format/log.h"
@ -25,7 +29,7 @@ void TransformSystem::update()
{ {
auto view = m_registry->view<TransformComponent>(); auto view = m_registry->view<TransformComponent>();
for (auto entity : view) { for (auto entity : m_hierarchy) {
auto& component = view.get<TransformComponent>(entity); auto& component = view.get<TransformComponent>(entity);
@ -42,7 +46,29 @@ void TransformSystem::update()
// Scale // Scale
component.transform = glm::scale(component.transform, component.scale); component.transform = glm::scale(component.transform, component.scale);
// Apply the parent transform to the child transform
if (component.parent != entt::null) {
auto& parent = view.get<TransformComponent>(component.parent);
component.transform = parent.transform * component.transform;
}
} }
} }
void TransformSystem::add(uint32_t entity)
{
m_hierarchy.push_back(static_cast<entt::entity>(entity));
}
void TransformSystem::sort()
{
std::sort(m_hierarchy.begin(), m_hierarchy.end(),
[](entt::entity a, entt::entity b) {
return (a == entt::null && b == entt::null) ? false
: (a == entt::null) ? true
: (b == entt::null) ? false
: a < b;
});
}
} // namespace Inferno } // namespace Inferno

11
src/inferno/system/transformsystem.h

@ -1,12 +1,14 @@
/* /*
* Copyright (C) 2022 Riyyi * Copyright (C) 2022,2024 Riyyi
* *
* SPDX-License-Identifier: MIT * SPDX-License-Identifier: MIT
*/ */
#pragma once #pragma once
#include <memory> // std::shared_ptr #include <cstdint> // uint32_t
#include <memory> // std::shared_ptr
#include <vector>
#include "entt/entity/registry.hpp" // entt::entity, entt::registry #include "entt/entity/registry.hpp" // entt::entity, entt::registry
#include "ruc/singleton.h" #include "ruc/singleton.h"
@ -20,9 +22,14 @@ public:
void update(); void update();
void add(uint32_t entity);
void sort();
void setRegistry(std::shared_ptr<entt::registry> registry) { m_registry = registry; }; void setRegistry(std::shared_ptr<entt::registry> registry) { m_registry = registry; };
private: private:
std::vector<entt::entity> m_hierarchy;
std::shared_ptr<entt::registry> m_registry; std::shared_ptr<entt::registry> m_registry;
}; };

38
src/inferno/uid.cpp

@ -0,0 +1,38 @@
/*
* Copyright (C) 2023 Riyyi
*
* SPDX-License-Identifier: MIT
*/
#include <cstdint>
#include <random>
#include "ruc/format/log.h"
#include "ruc/json/json.h"
#include "inferno/uid.h"
namespace Inferno {
static std::random_device s_seed;
static std::mt19937_64 s_engine(s_seed());
static std::uniform_int_distribution<uint64_t> s_distribution;
UID::UID()
: m_uid(s_distribution(s_engine))
{
}
UID::UID(uint64_t uid)
: m_uid(uid)
{
}
void fromJson(const ruc::Json& json, UID& value)
{
VERIFY(json.type() == ruc::Json::Type::Number);
value = UID((int64_t)json.asDouble());
}
} // namespace Inferno

47
src/inferno/uid.h

@ -0,0 +1,47 @@
/*
* Copyright (C) 2023 Riyyi
*
* SPDX-License-Identifier: MIT
*/
#pragma once
#include <cstdint> // uint64_t
#include "ruc/json/json.h"
namespace Inferno {
class UID {
public:
UID();
UID(uint64_t uid);
// Comparison operator for std::map
bool operator<(const UID& other) const
{
return m_uid < other.m_uid;
}
operator uint64_t() const { return m_uid; }
private:
uint64_t m_uid;
};
void fromJson(const ruc::Json& json, UID& value);
} // namespace Inferno
namespace std {
// Hash function for std::unordered_map
template<>
struct hash<Inferno::UID> {
size_t operator()(const Inferno::UID& uid) const
{
return hash<uint64_t>()(static_cast<uint64_t>(uid));
}
};
} // namespace std
Loading…
Cancel
Save