Compare commits

...

38 Commits

Author SHA1 Message Date
113412bb5b making prefab work 2025-11-14 18:26:30 +01:00
c7b6a79270 feat: use renderable 2025-11-05 11:51:38 +01:00
54fa900dff fix: fixme checked (uploading already) 2025-11-05 11:51:26 +01:00
b6b40837a4 feat: prepare each mesh entt for rendering 2025-11-05 11:50:57 +01:00
d6267f7a4b chore: remove unnecessary logs 2025-11-05 11:48:33 +01:00
71a14af25c fix: correct import filename 2025-11-05 11:48:24 +01:00
339da4ef02 fix: use correct data 2025-11-05 11:48:08 +01:00
0c4b7ed285 feat: mesh component accept renderable 2025-11-05 08:07:24 +01:00
bdd5a16b3d feat: renderable interface 2025-11-05 08:06:54 +01:00
116f274228 feat: render support for mesh 2025-11-05 08:06:41 +01:00
07108956b9 feat: return raw pointer to texture 2025-11-04 18:04:20 +01:00
eb4b3bc78f fix: raw pointer used 2025-11-04 18:04:11 +01:00
8434ee8633 feat: transparent buffer class with global binding control 2025-11-04 18:04:02 +01:00
2d10e22a99 feat: use custom buffers 2025-11-04 18:03:45 +01:00
103fdcaa26 feat: use custom buffers 2025-11-04 18:03:33 +01:00
9e3bc4790b feat: refactor wavefront to support new mesh + material system 2025-11-04 18:03:18 +01:00
4bc74d0d2c feat: simplify and return raw pointer 2025-11-04 18:02:55 +01:00
3cca0b5c87 fix: correct import 2025-11-04 18:02:38 +01:00
bb4a2c926e feat: adapt old mesh class for further removing 2025-11-04 18:02:33 +01:00
808fad3001 feat: delete basics 2025-11-04 18:01:49 +01:00
73da0d79f5 feat: better buffer binding/unbinding control 2025-11-04 18:01:42 +01:00
788a302d75 feat: new vertex impl 2025-11-04 18:01:14 +01:00
e36a599d68 feat: abstracted Mesh class 2025-11-04 18:00:49 +01:00
9591ad403b feat: custom new material class 2025-11-04 18:00:40 +01:00
51ace4a800 feat: custom dynamic array impl 2025-11-04 18:00:34 +01:00
577336b5b7 chore: vscode settings 2025-11-04 18:00:27 +01:00
9b26cf909b feat: vertex array class 2025-10-30 18:31:48 +01:00
c6d83c1b51 feat: destructor 2025-10-24 13:17:15 +02:00
e459fea503 feat: instance + array buffer 2025-10-24 13:06:40 +02:00
b5ea21ad43 feat: uniform buffer configure shader 2025-10-24 12:37:09 +02:00
ee8e6012c8 test: try 10.000 cubes to render 2025-10-24 11:16:32 +02:00
063b228b97 feat: support uniform blocks 2025-10-23 22:43:03 +02:00
1440fd847c test: try uniform buffers 2025-10-23 22:42:54 +02:00
337da2b3b7 feat: opengl buffer & nested uniform buffer class 2025-10-23 22:42:41 +02:00
3574634c4c chore: formatting 2025-10-23 21:34:13 +02:00
32873d14ae chore: rename transform to Transform 2025-10-23 20:32:12 +02:00
ec7ef40aea feat: input class for abstracting SDL_Input functions 2025-10-23 16:35:40 +02:00
7b9858cffa feat: move fps and deltatime calculations in core engine 2025-10-23 16:10:22 +02:00
37 changed files with 5126 additions and 399 deletions

View File

@ -69,6 +69,7 @@
"regex": "cpp", "regex": "cpp",
"semaphore": "cpp", "semaphore": "cpp",
"shared_mutex": "cpp", "shared_mutex": "cpp",
"stop_token": "cpp" "stop_token": "cpp",
"filesystem": "cpp"
} }
} }

View File

@ -5,6 +5,10 @@ set(SOURCES
src/IO/file_manager.cpp src/IO/file_manager.cpp
src/renderer/debug.cpp src/renderer/debug.cpp
src/input/input.cpp
src/opengl/buffers.cpp
src/scene/scene.cpp src/scene/scene.cpp
src/window/window.cpp src/window/window.cpp

View File

@ -0,0 +1,122 @@
#ifndef CORE_ARRAY_H_
#define CORE_ARRAY_H_
#define ARRAY_INITIAL_CAPACITY 2
namespace Core {
template<typename Item, typename Size = unsigned int>
class Array {
public:
Array() {
m_size = 0;
m_capacity = ARRAY_INITIAL_CAPACITY;
m_items = static_cast<Item*>(::operator new[](m_capacity * sizeof(Item)));
}
Array(Size initialCapacity) {
m_size = 0;
m_capacity = initialCapacity;
m_items = static_cast<Item*>(::operator new[](m_capacity * sizeof(Item)));
}
~Array() {
for (unsigned int i = 0; i < m_size; ++i) {
m_items[i].~Item();
}
::operator delete[](m_items);
}
Array(Array&& other) {
m_capacity = other.m_capacity;
m_size = other.m_size;
m_items = other.m_items;
other.m_size = 0;
other.m_capacity = 0;
other.m_items = nullptr;
}
Array& operator=(Array&& other) noexcept {
if (this != &other) {
// Destroy current contents
for (unsigned int i = 0; i < m_size; ++i)
m_items[i].~Item();
::operator delete[](m_items);
// Move from other
m_items = other.m_items;
m_size = other.m_size;
m_capacity = other.m_capacity;
other.m_items = nullptr;
other.m_size = 0;
other.m_capacity = 0;
}
return *this;
}
Array(const Array&) = delete;
public:
inline const Size GetSize() const noexcept { return m_size; }
Item* Begin() { return m_items; }
const Item* Begin() const { return m_items; }
Item& Back() { assert(m_size > 0 && "Calling back() on empty array!"); return m_items[m_size - 1]; }
const Item& Back() const { assert(m_size > 0 && "Calling back() on empty array!"); return m_items[m_size - 1]; }
Item* End() { return m_items + m_size; }
const Item* End() const { return m_items + m_size; }
inline const bool Empty() const { return m_size == 0; }
public:
template<typename... Args>
void EmplaceBack(Args&&... args) {
_ensureSize(m_size + 1);
new (&m_items[m_size++]) Item(std::forward<Args>(args)...);
}
void PushBack(const Item& item) {
_ensureSize(m_size + 1);
new (&m_items[m_size++]) Item(item);
}
void PushBack(Item&& item) {
_ensureSize(m_size + 1);
new (&m_items[m_size++]) Item(std::move(item));
}
private:
void _ensureSize(Size size) {
if (size > m_capacity) {
_extend(static_cast<Size>(m_capacity + (m_capacity / 2)));
}
}
void _extend(Size newSize) {
auto newItems = static_cast<Item*>(::operator new[](newSize * sizeof(Item)));
std::uninitialized_move(
std::make_move_iterator(m_items),
std::make_move_iterator(m_items + m_size),
newItems
);
for (unsigned int i = 0; i < m_size; ++i) {
m_items[i].~Item();
}
::operator delete[](m_items);
m_items = newItems;
m_capacity = newSize;
}
private:
Item* m_items;
Size m_size;
Size m_capacity;
};
}
#endif // CORE_ARRAY_H_

View File

@ -0,0 +1,64 @@
#ifndef MATERIAL_H_
#define MATERIAL_H_
#include <glm/glm.hpp>
#include <memory>
#include "engine/renderer/texture.h"
#ifndef DEFAULT_MATERIAL_NAME
#define DEFAULT_MATERIAL_NAME "__default_material"
#endif
namespace Core {
typedef std::string MaterialID;
class Material {
public:
Material(const std::string& name) : m_id(name) {}
Material() : Material(DEFAULT_MATERIAL_NAME) {}
// Material(const Material&) = default;
public:
static Material Default() {
Material material;
material.SetAmbientColor(glm::vec3(0.52f, 0.52f, 0.52f));
return material;
}
public:
inline const MaterialID GetID() const { return m_id; }
inline const glm::vec3 GetAmbientColor() const { return m_ambient; }
inline const glm::vec3 GetDiffuseColor() const { return m_diffuse; }
inline const glm::vec3 GetSpecularColor() const { return m_specular; }
inline const float GetSpecularWeight() const { return m_shininess; }
inline const bool HasDiffuseTexture() const { return m_diffuse_tex != nullptr; }
inline const Texture* GetDiffuseTexture() const { return m_diffuse_tex; }
inline const float GetOpacity() const { return m_opacity; }
inline const int GetIllumination() const { return m_illum; }
public:
inline void SetAmbientColor(glm::vec3 ambient) { m_ambient = ambient; }
inline void SetDiffuseColor(glm::vec3 diffuse) { m_diffuse = diffuse; }
inline void SetSpecularColor(glm::vec3 specular) { m_specular = specular; }
inline void SetSpecularWeight(float weight) { m_shininess = weight; }
inline void SetDiffuseTexture(Texture* texture) { m_diffuse_tex = texture; }
inline void SetOpacity(float opacity) { m_opacity = opacity; }
inline void SetIllumination(float illum) { m_illum = illum; }
private:
glm::vec3 m_ambient { 0.2f, 0.2f, 0.2f };
glm::vec3 m_diffuse { 0.8f, 0.8f, 0.8f };
glm::vec3 m_specular { 1.0f, 1.0f, 1.0f };
float m_shininess { 32.0f };
float m_opacity { 1.0f };
int m_illum { 2 };
MaterialID m_id { 0 };
Texture* m_diffuse_tex { nullptr };
}; // class Material
} // namespace Core
#endif // MATERIAL_H_

View File

@ -0,0 +1,222 @@
#pragma once
#include "engine/3d/array.hpp"
#include "engine/3d/vertex.hpp"
#include "engine/3d/material.hpp"
#include "engine/opengl/buffers.h"
#include "engine/renderer/renderable.hpp"
#include "engine/export.h"
namespace Core {
class MeshGroup;
class ENGINE_API Mesh : public Renderable, public OpenGL::VertexArray {
friend class Core::MeshGroup;
public:
Mesh() = default;
Mesh(const Material& material) : m_material(material) {}
Mesh(const Mesh&) = delete;
Mesh(Mesh&&) = default;
public:
template<typename... Args>
uint32_t EmplaceVertex(Args&&... args) {
Vertex v(std::forward<Args>(args)...);
auto it = m_vti.find(v);
if (it != m_vti.end()) return it->second;
uint32_t newIndex = static_cast<uint32_t>(m_vertices.GetSize());
m_vertices.PushBack(std::move(v));
m_vti.emplace(m_vertices.Back(), newIndex);
return newIndex;
}
uint32_t PushVertex(const Vertex& vertex) {
auto it = m_vti.find(vertex);
if (it != m_vti.end()) return it->second;
uint32_t newIndex = static_cast<uint32_t>(m_vertices.GetSize());
m_vertices.PushBack(vertex);
m_vti.emplace(m_vertices.Back(), newIndex);
return newIndex;
}
void PushTriangle(uint32_t a, uint32_t b, uint32_t c) {
m_indices.PushBack(a);
m_indices.PushBack(b);
m_indices.PushBack(c);
}
const auto GetVerticesCount() const { return m_vertices.GetSize(); }
const auto GetIndicesCount() const { return m_indices.GetSize(); }
public:
inline const Material& GetMaterial() const { return m_material; }
private:
void Prepare() override {
// ---------- INIT -----------
m_ebo = 0;
// glGenVertexArrays(1, &m_vao);
// glGenBuffers(1, &m_vbo);
glGenBuffers(1, &m_ebo);
Bind();
SetupVertexBuffer(GL_DYNAMIC_DRAW);
// EBO (index buffer)
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_ebo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, 0, nullptr, GL_DYNAMIC_DRAW);
// attributes
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast<const void*>(offsetof(Vertex, position)));
glEnableVertexAttribArray(0);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast<const void*>(offsetof(Vertex, normal)));
glEnableVertexAttribArray(1);
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast<const void*>(offsetof(Vertex, uv)));
glEnableVertexAttribArray(2);
// TODO: delete after ebo moved in VertexArray
// glBindBuffer(GL_DYNAMIC_DRAW, 0);
Unbind();
// ---------- UPLOAD -----------
Bind();
VertexBufferData(m_vertices.GetSize() * sizeof(Vertex), m_vertices.Begin());
// Upload indices
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_ebo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, m_indices.GetSize() * sizeof(unsigned int), m_indices.Begin(), GL_DYNAMIC_DRAW);
// TODO: delete after ebo moved in VertexArray
// glBindBuffer(GL_DYNAMIC_DRAW, 0);
Unbind();
}
void Render(Shader& shader, Scene& scene, unsigned int count) override {
// --- Basic material properties ---
shader.setFloat("opacity", m_material.GetOpacity());
// Albedo (base color)
shader.setVec3("albedo", m_material.GetDiffuseColor());
// Metallic and roughness (defaults)
shader.setFloat("metallic", 0.8f);
shader.setFloat("roughness", 0.5f);
shader.setFloat("ao", 1.0f); // default ambient occlusion if none
// --- Optional textures ---
int texUnit = 0;
// Albedo texture
if (m_material.HasDiffuseTexture()) {
shader.setBool("useAlbedoMap", true);
glActiveTexture(GL_TEXTURE0 + texUnit);
glBindTexture(GL_TEXTURE_2D, m_material.GetDiffuseTexture()->GetID());
shader.setInt("albedoTex", texUnit++);
} else {
shader.setBool("useAlbedoMap", false);
}
// Metallic texture
// if (m_material.HasMetallicTexture()) {
if (false) {
shader.setBool("useMetallicMap", true);
glActiveTexture(GL_TEXTURE0 + texUnit);
// glBindTexture(GL_TEXTURE_2D, m_material.GetMetallicTexture()->GetID());
shader.setInt("metallicTex", texUnit++);
} else {
shader.setBool("useMetallicMap", false);
}
// Roughness texture
// if (m_material.HasRoughnessTexture()) {
if (false) {
shader.setBool("useRoughnessMap", true);
glActiveTexture(GL_TEXTURE0 + texUnit);
// glBindTexture(GL_TEXTURE_2D, m_material.GetRoughnessTexture()->GetID());
shader.setInt("roughnessTex", texUnit++);
} else {
shader.setBool("useRoughnessMap", false);
}
// AO texture
// if (m_material.HasAoTexture()) {
if (false) {
shader.setBool("useAoMap", true);
glActiveTexture(GL_TEXTURE0 + texUnit);
// glBindTexture(GL_TEXTURE_2D, m_material.GetAoTexture()->GetID());
shader.setInt("aoTex", texUnit++);
} else {
shader.setBool("useAoMap", false);
}
// --- Render mesh ---
Bind();
if (count > 1) {
glDrawElementsInstanced(GL_TRIANGLES, static_cast<GLsizei>(m_indices.GetSize()), GL_UNSIGNED_INT, 0, count);
} else {
glDrawElements(GL_TRIANGLES, static_cast<GLsizei>(m_indices.GetSize()), GL_UNSIGNED_INT, 0);
}
Unbind();
}
private:
Array<Vertex> m_vertices { VERTICES_INITIAL_CAPACITY };
Array<uint32_t> m_indices { INDICES_INITIAL_CAPACITY };
// TODO: move out
// ------- RENDERING ---------
unsigned int m_ebo;
// ------- RENDERING ---------
Material m_material;
std::unordered_map<Vertex, uint32_t, VertexHash> m_vti;
}; // class Mesh
// Right now it's just a list of meshes connected to each other.
// In future we might want to add support for global material
// that can affect all sub materials, aka this class
// will act like Parent Mesh that contains Child Meshes
class ENGINE_API MeshGroup : public Array<Mesh>, public Renderable {
public:
MeshGroup() {}
void Prepare() override {
for (auto it = Begin(); it != End(); ++it) {
it->Prepare();
}
}
void Render(Shader& shader, Scene& scene, unsigned int count) {
for (auto it = Begin(); it != End(); ++it) {
it->Render(shader, scene, count);
}
}
public:
inline Mesh* FindMeshByMaterial(const Material& material) {
for (auto it = Begin(); it != End(); ++it) {
if (it->GetMaterial().GetID() == material.GetID()) {
return it;
}
}
return End();
}
inline Mesh* FindMeshByMaterial(const Material* material) {
for (auto it = Begin(); it != End(); ++it) {
if (it->GetMaterial().GetID() == material->GetID()) {
return it;
}
}
return End();
}
};
} // namespace Core

View File

@ -0,0 +1,148 @@
#ifndef CORE_PREFAB_H_
#define CORE_PREFAB_H_
#include <glm/glm.hpp>
#include <glm/ext/matrix_clip_space.hpp>
#ifdef _WIN32
#include <corecrt_math_defines.h>
#endif
#include <glm/ext/matrix_transform.hpp>
#define GLM_ENABLE_EXPERIMENTAL
#include <glm/gtx/euler_angles.hpp>
#include "engine/opengl/buffers.h"
#include "engine/renderer/renderable.hpp"
#include "engine/scene/scene.h"
#include "engine/3d/array.hpp"
#include "engine/3d/mesh.hpp"
#include "engine/components/transform.h"
#include "engine/components/batch.h"
namespace Core {
class Prefab : public Renderable {
public:
Prefab(MeshGroup&& mesh) : m_mesh(std::move(mesh)), m_id(++LastID) {}
const unsigned int GetID() const { return m_id; }
public:
void Prepare() override {
std::cout << "[PREFAB] Prepare called" << std::endl;
EnsureResources();
m_mesh.Prepare();
for (auto it = m_mesh.Begin(); it != m_mesh.End(); ++it) {
it->Bind();
std::cout << "[PREFAB] Configuring instance buffer for mesh" << std::endl;
m_instanceBuffer->StartConfigure();
std::size_t vec4Size = sizeof(glm::vec4);
for (int i = 0; i < 4; ++i) {
glEnableVertexAttribArray(3 + i); // use locations 3,4,5,6 for instance matrix
glVertexAttribPointer(3 + i, 4, GL_FLOAT, GL_FALSE,
sizeof(glm::mat4), (void*)(i * vec4Size));
glVertexAttribDivisor(3 + i, 1); // IMPORTANT: one per instance, not per vertex
}
m_instanceBuffer->EndConfigure();
std::cout << "[PREFAB] Finished configuring" << std::endl;
it->Unbind();
}
}
void Render(Shader& shader, Scene& scene, unsigned int count) override {
std::cout << "[PREFAB] Render called" << std::endl;
Array<entt::entity> batches;
for (auto [entt, item] : scene.m_registry.view<batch::item>().each()) {
if (item.batchId == m_id) {
batches.PushBack(entt);
}
}
std::cout << "[PREFAB] Collected " << batches.GetSize() << " batch items" << std::endl;
std::vector<glm::mat4> models;
models.reserve(batches.GetSize());
std::cout << "[PREFAB] Starting collecting models..." << std::endl;
for (auto it = batches.Begin(); it != batches.End(); ++it) {
auto &t = scene.m_registry.get<Transform>(*it);
glm::mat4 rotation = glm::yawPitchRoll(t.rotation.y, t.rotation.x, t.rotation.z);
auto itemModel = glm::translate(glm::mat4(1.f), t.position) * rotation;
models.push_back(itemModel);
}
std::cout << "[PREFAB] Collected " << models.size() << " models" << std::endl;
UploadInstances(models.data(), models.size());
shader.setBool("u_isInstanced", true);
shader.setBool("isLight", false);
shader.setVec3("currentLightColor", glm::vec3(0.f));
std::cout << "[PREFAB] Rendering mesh in instanced mode with size = " << models.size() << std::endl;
m_mesh.Render(shader, scene, models.size());
shader.setBool("u_isInstanced", false);
}
private:
void EnsureResources(unsigned int count = 0) {
if (!m_instanceBuffer) {
std::cout << "[PREFAB] Instance buffer init..." << std::endl;
m_instanceBuffer = new OpenGL::InstanceBuffer(GL_DYNAMIC_DRAW);
OpenGL::Buffer::Bind(m_instanceBuffer);
OpenGL::Buffer::Data(m_instanceBuffer, nullptr, sizeof(glm::mat4) * count);
OpenGL::Buffer::Unbind(m_instanceBuffer);
m_instance_count = count;
}
}
void UploadInstances(glm::mat4 *instances, unsigned int count) {
std::cout << "[PREFAB] UploadInstances called" << std::endl;
EnsureResources(count);
if (count > m_instance_count) {
std::cout << "[PREFAB] Reallocate buffer. Current = " << m_instance_count << " ; required = " << count << std::endl;
// Optional: reallocate only if you *really* have more instances than before
// FIXME: what the hell is m_instance_vbo
// glBindBuffer(GL_ARRAY_BUFFER, m_instance_vbo);
OpenGL::Buffer::Bind(m_instanceBuffer);
OpenGL::Buffer::Data(m_instanceBuffer, nullptr, sizeof(glm::mat4) * count);
OpenGL::Buffer::Unbind(m_instanceBuffer);
m_instance_count = count;
}
// Just update the data region — much cheaper
std::cout << "[PREFAB] Updating data of instance buffer..." << std::endl;
// std::cout << "[PREFAB] buffer target " << (m_instanceBuffer->GetTarget() == GL_ARRAY_BUFFER ? "array buffer" : "other") << std::endl;
// std::cout << "[PREFAB] buffer id " << m_instanceBuffer->GetID() << std::endl;
std::cout << "count = " << count << std::endl;
std::cout << "instances = " << instances << std::endl;
OpenGL::Buffer::Bind(m_instanceBuffer);
OpenGL::Buffer::SubData(m_instanceBuffer, instances, sizeof(glm::mat4) * count, 0);
}
protected:
static unsigned int LastID;
private:
unsigned int m_id;
MeshGroup m_mesh;
OpenGL::InstanceBuffer* m_instanceBuffer = nullptr;
unsigned int m_instance_count = 0;
unsigned int m_instance_vbo = 0;
};
unsigned int Prefab::LastID = 0;
}
#endif // CORE_PREFAB_H_

View File

@ -0,0 +1,63 @@
#pragma once
#include <cstring>
#include <glm/glm.hpp>
#define VERTICES_INITIAL_CAPACITY 256
#define INDICES_INITIAL_CAPACITY 512
namespace Core {
struct Vertex {
public:
Vertex(glm::vec3 position, glm::vec3 normal, glm::vec2 uv)
: position(position), normal(normal), uv(uv) {}
Vertex()
: position(glm::vec3()), normal(glm::vec3()), uv(glm::vec2()) {}
Vertex(const Vertex&) noexcept = default;
Vertex(Vertex&&) noexcept = default;
bool operator ==(Vertex const& o) const noexcept {
return o.position == position
&& o.normal == normal
&& o.uv == uv;
}
public:
glm::vec3 position;
glm::vec3 normal;
glm::vec2 uv;
friend class VertexHash;
};
struct VertexHash {
size_t operator()(Vertex const& v) const noexcept {
auto h = [](float f) -> size_t {
uint32_t b;
static_assert(sizeof(float) == sizeof(uint32_t));
std::memcpy(&b, &f, sizeof(float));
// splitmix64-like mixing (simple)
uint64_t x = b;
x = (x ^ (x >> 30)) * 0xbf58476d1ce4e5b9ULL;
x = (x ^ (x >> 27)) * 0x94d049bb133111ebULL;
x ^= x >> 31;
return static_cast<size_t>(x);
};
size_t res = 1469598103934665603ULL;
res ^= h(v.position.x) + 0x9e3779b97f4a7c15ULL + (res<<6) + (res>>2);
res ^= h(v.position.y) + 0x9e3779b97f4a7c15ULL + (res<<6) + (res>>2);
res ^= h(v.position.z) + 0x9e3779b97f4a7c15ULL + (res<<6) + (res>>2);
res ^= h(v.normal.x) + 0x9e3779b97f4a7c15ULL + (res<<6) + (res>>2);
res ^= h(v.normal.y) + 0x9e3779b97f4a7c15ULL + (res<<6) + (res>>2);
res ^= h(v.normal.z) + 0x9e3779b97f4a7c15ULL + (res<<6) + (res>>2);
res ^= h(v.uv.x) + 0x9e3779b97f4a7c15ULL + (res<<6) + (res>>2);
res ^= h(v.uv.y) + 0x9e3779b97f4a7c15ULL + (res<<6) + (res>>2);
return res;
}
};
}

View File

@ -3,6 +3,8 @@
#include "engine/scene/scene.h" #include "engine/scene/scene.h"
#include "engine/window/event.hpp" #include "engine/window/event.hpp"
#include "engine/time/timestep.h"
#include "engine/export.h" #include "engine/export.h"
namespace Core { namespace Core {
@ -11,7 +13,7 @@ namespace Core {
virtual ~IApplication() = default; virtual ~IApplication() = default;
virtual void OnInit(std::shared_ptr<Scene> scene) {}; virtual void OnInit(std::shared_ptr<Scene> scene) {};
virtual void OnUpdate() {}; virtual void OnUpdate(Timestep dt) {};
virtual void OnShutdown() {}; virtual void OnShutdown() {};
virtual void OnEvent(const Event& event) {}; virtual void OnEvent(const Event& event) {};

View File

@ -2,14 +2,17 @@
#define COMPONENT_BATCH_H_ #define COMPONENT_BATCH_H_
#include <glm/mat4x4.hpp> #include <glm/mat4x4.hpp>
#include <memory>
#include "engine/opengl/buffers.h"
#include "engine/export.h" #include "engine/export.h"
namespace Core { namespace Core {
// requires mesh component // requires mesh component
struct ENGINE_API batch { struct ENGINE_API batch {
friend class Renderer;
public: public:
// requires transform component // requires Transform component
struct item { struct item {
unsigned int batchId; unsigned int batchId;
}; };
@ -17,13 +20,18 @@ public:
batch(); batch();
inline const unsigned int id() const { return m_id; } inline const unsigned int id() const { return m_id; }
inline const bool Initialized() const { return m_instanceBuffer != nullptr; }
protected: protected:
static unsigned int LastID; static unsigned int LastID;
private: private:
unsigned int m_id; unsigned int m_id;
unsigned int m_instance_vbo { 0 }; unsigned int m_instance_vbo { 0 };
unsigned int m_instance_count { 0 }; unsigned int m_instance_count { 0 };
// TODO: use static draw when possible
OpenGL::InstanceBuffer* m_instanceBuffer = nullptr;
private: private:
friend class Renderer;
void prepare(glm::mat4 *instances, unsigned int count); void prepare(glm::mat4 *instances, unsigned int count);
}; };
} }

View File

@ -2,13 +2,13 @@
#define COMPONENTS_MESH_H_ #define COMPONENTS_MESH_H_
#include <memory> #include <memory>
#include "engine/renderer/renderable.hpp"
#include "engine/renderer/wavefront.h"
#include "engine/export.h" #include "engine/export.h"
namespace Core { namespace Core {
struct ENGINE_API mesh { struct ENGINE_API mesh {
std::shared_ptr<Object> object; std::shared_ptr<Renderable> mesh;
}; };
} }

View File

@ -5,11 +5,13 @@
#include "engine/export.h" #include "engine/export.h"
namespace Core { namespace Core {
struct ENGINE_API transform {
struct ENGINE_API Transform {
glm::vec3 position; glm::vec3 position;
glm::vec3 rotation; glm::vec3 rotation;
glm::vec3 scale; glm::vec3 scale;
}; };
} }
#endif // COMPONENTS_TRANSFORM_H_ #endif // COMPONENTS_TRANSFORM_H_

View File

@ -0,0 +1,29 @@
#ifndef ENGINE_INPUT_H_
#define ENGINE_INPUT_H_
#include <SDL3/SDL.h>
#include <glm/glm.hpp>
#include "engine/time/timestep.h"
#include "engine/export.h"
namespace Core {
class ENGINE_API Input {
public:
Input() = delete;
private:
friend class Engine;
static void Update(Timestep dt);
public:
[[nodiscard]] static glm::vec2 GetRelativeMouse();
[[nodiscard]] static bool IsKeyPressed(SDL_Scancode keyCode);
private:
static bool* s_keys_state;
static int s_keys_state_size;
};
}
#endif // ENGINE_INPUT_H_

View File

@ -0,0 +1,93 @@
#ifndef OPENGL_BUFFERS_H_
#define OPENGL_BUFFERS_H_
#include <GL/glew.h>
#include "engine/renderer/shader.h"
#include "engine/export.h"
namespace Core {
namespace OpenGL {
using BufferTarget = GLenum;
using BufferUsage = GLenum;
using BufferID = unsigned int;
class ENGINE_API Buffer {
public:
Buffer(BufferTarget target, BufferUsage usage);
Buffer(BufferTarget target);
~Buffer();
inline const BufferID GetID() const { return m_buffer; }
inline const BufferTarget GetTarget() const { return m_target; }
public:
static inline const bool IsBound(const Buffer& buffer) { return m_bound == buffer.GetID(); }
static inline const bool IsBound(const Buffer* buffer) { return buffer && m_bound == buffer->GetID(); }
// static inline void Bind(const Buffer& buffer) { glBindBuffer(buffer.GetTarget(), buffer.GetID()); }
static inline void Bind(const Buffer* buffer) { glBindBuffer(buffer->GetTarget(), buffer->GetID()); m_bound = buffer->GetID(); }
static inline void Unbind(const Buffer* buffer) { glBindBuffer(buffer->GetTarget(), 0); m_bound = 0; }
static void Data(const Buffer* buffer, const void* data, size_t size);
static void SubData(const Buffer* buffer, const void *data, size_t size, size_t offset);
protected:
void BindBuffer(unsigned int index) const;
void BindBufferRanged(unsigned int index, size_t offset, size_t size) const;
private:
BufferID m_buffer;
BufferTarget m_target;
BufferUsage m_usage;
private:
static BufferID m_bound;
};
// TODO: Implement custom fields structuring via ordered_map?
class ENGINE_API UniformBuffer : public Buffer {
public:
UniformBuffer(size_t size, unsigned int index);
void ConfigureShader(Shader& shader, const char* uniformName) const;
template<typename T, typename S = size_t>
void UpdateUniform(void* data, S offset) {
SubData(this, data, sizeof(T), offset);
}
private:
unsigned int m_uniformBinding;
private:
static unsigned int s_bufferNextId;
};
class ENGINE_API ArrayBuffer : public Buffer {
public:
ArrayBuffer(BufferUsage usage);
};
class ENGINE_API InstanceBuffer : public ArrayBuffer {
public:
InstanceBuffer(BufferUsage usage);
inline void StartConfigure() const { Buffer::Bind(this); }
inline void EndConfigure() const { Buffer::Unbind(this); }
};
class ENGINE_API VertexArray {
public:
VertexArray();
~VertexArray();
void Bind();
void Unbind();
void SetupVertexBuffer(BufferUsage usage);
public:
void VertexBufferData(size_t size, const void* data);
private:
unsigned int m_id { 0 };
ArrayBuffer* m_vbo { nullptr };
};
} // namespace OpenGL
} // namespace Core
#endif // OPENGL_BUFFERS_H_

View File

@ -1,21 +0,0 @@
#ifndef RENDERER_BASICS_H
#define RENDERER_BASICS_H
#include <glm/glm.hpp>
namespace Core {
class Vertex {
friend class Mesh;
private:
glm::vec3 m_position;
glm::vec3 m_normal;
glm::vec2 m_texCoord;
public:
Vertex(glm::vec3 position, glm::vec3 normal, glm::vec2 texCoord)
: m_position(position), m_normal(normal), m_texCoord(texCoord) {}
};
}
#endif // RENDERER_BASICS_H

View File

@ -30,6 +30,7 @@ private:
std::shared_ptr<Window> m_window; std::shared_ptr<Window> m_window;
std::unique_ptr<Renderer> m_renderer; std::unique_ptr<Renderer> m_renderer;
std::shared_ptr<Scene> m_scene; std::shared_ptr<Scene> m_scene;
uint64_t m_elapsed;
bool m_running; bool m_running;
}; };

View File

@ -5,25 +5,29 @@
#include <string> #include <string>
#include <GL/glew.h> #include <GL/glew.h>
#include "engine/renderer/basics.h" #include "engine/3d/vertex.hpp"
#include "engine/opengl/buffers.h"
namespace Core { namespace Core {
class Mesh { class Mesh : public OpenGL::VertexArray {
public: // TODO: abstract away public: // TODO: abstract away
unsigned int m_vao, m_vbo, m_ebo; unsigned int m_ebo;
std::vector<Vertex> m_vertexBuffer; std::vector<Vertex> m_vertexBuffer;
std::vector<unsigned int> m_indexBuffer; std::vector<unsigned int> m_indexBuffer;
public: // TODO: abstract away
void Bind() const { glBindVertexArray(m_vao); }
void Unbind() { glBindVertexArray(0); }
void Upload() const;
public: public:
std::string materialName; Mesh(const std::string& name);
Mesh(const Mesh& other) = delete;
Mesh(Mesh&& other) noexcept
: m_ebo(other.m_ebo), m_vertexBuffer(std::move(other.m_vertexBuffer)), m_indexBuffer(std::move(other.m_indexBuffer)), m_materialName(std::move(other.m_materialName)) {}
public: public:
Mesh(); inline const std::string& GetMaterialName() const { return m_materialName; }
void Upload();
public: public:
void Render(unsigned int count); void Render(unsigned int count);
private:
std::string m_materialName;
}; };
} }

View File

@ -0,0 +1,26 @@
#ifndef CORE_RENDERABLE_H_
#define CORE_RENDERABLE_H_
#include "engine/renderer/shader.h"
#include "engine/export.h"
namespace Core {
class Renderer;
class ENGINE_API Renderable {
protected:
Renderable() = default;
virtual ~Renderable() = default;
private:
virtual void Prepare() = 0;
virtual void Render(Shader& shader, Scene& scene, unsigned int count = 1) = 0;
friend class Core::Renderer;
};
} // namespace Core
#endif // CORE_RENDERABLE_H_

View File

@ -8,6 +8,8 @@
#include "engine/export.h" #include "engine/export.h"
#include "engine/components/light.h" #include "engine/components/light.h"
#include "engine/opengl/buffers.h"
namespace Core { namespace Core {
// TODO: make static or singleton // TODO: make static or singleton
@ -26,6 +28,8 @@ private:
void GenerateShadowMaps(); void GenerateShadowMaps();
void EnsureShadowResources(light& l); void EnsureShadowResources(light& l);
private: private:
OpenGL::UniformBuffer m_uniform_matrices;
Shader m_shader; Shader m_shader;
Shader m_depthShader; Shader m_depthShader;

View File

@ -15,11 +15,11 @@ public:
Shader(); Shader();
~Shader(); ~Shader();
unsigned int m_id;
void init(const std::string &vertexCode, const std::string &fragmentCode); void init(const std::string &vertexCode, const std::string &fragmentCode);
void use(); void use();
const unsigned int GetID() const { return m_id; }
void setBool(const std::string &name, bool value) const; void setBool(const std::string &name, bool value) const;
void setInt(const std::string &name, int value) const; void setInt(const std::string &name, int value) const;
void setFloat(const std::string &name, float value) const; void setFloat(const std::string &name, float value) const;
@ -32,8 +32,8 @@ public:
void setMat2(const std::string &name, const glm::mat2 &mat) const; void setMat2(const std::string &name, const glm::mat2 &mat) const;
void setMat3(const std::string &name, const glm::mat3 &mat) const; void setMat3(const std::string &name, const glm::mat3 &mat) const;
void setMat4(const std::string &name, const glm::mat4 &mat) const; void setMat4(const std::string &name, const glm::mat4 &mat) const;
private: private:
unsigned int m_id;
unsigned int m_vertexId; unsigned int m_vertexId;
unsigned int m_fragmentId; unsigned int m_fragmentId;

View File

@ -8,7 +8,7 @@ namespace Core {
class Texture { class Texture {
public: public:
Texture() : m_id(0) {} Texture() : m_id(0) {}
static std::unique_ptr<Texture> LoadFile(const std::string& filename); static Texture* LoadFile(const std::string& filename);
public: public:
[[nodiscard]] unsigned int GetID() const { return m_id; } [[nodiscard]] unsigned int GetID() const { return m_id; }
private: private:

View File

@ -9,8 +9,8 @@
#include "engine/renderer/shader.h" #include "engine/renderer/shader.h"
#include "engine/renderer/renderer.h" #include "engine/renderer/renderer.h"
#include "engine/renderer/material.h" #include "engine/3d/mesh.hpp"
#include "engine/renderer/mesh.h" #include "engine/opengl/buffers.h"
#include "engine/export.h" #include "engine/export.h"
@ -19,40 +19,44 @@ namespace Core {
enum ObjElement { OHASH, MTLLIB, USEMTL, O, V, VN, VT, F, OUNKNOWN }; enum ObjElement { OHASH, MTLLIB, USEMTL, O, V, VN, VT, F, OUNKNOWN };
enum MtlElement { MHASH, NEWMTL, NS, KA, KS, KD, NI, D, ILLUM, MAP_KD, MAP_KA, MUNKNOWN }; enum MtlElement { MHASH, NEWMTL, NS, KA, KS, KD, NI, D, ILLUM, MAP_KD, MAP_KA, MUNKNOWN };
class ENGINE_API Object { class ENGINE_API Object : public MeshGroup {
friend class Renderer; friend class Renderer;
private: private:
static inline int NormalizeIndex(int idx, int baseCount); static inline int NormalizeIndex(int idx, int baseCount);
private:
Object(); Object();
public: public:
~Object() = default; ~Object() = default;
public: public:
static Object* LoadFile(const std::string& filename); static Object* LoadFile(const std::string& filename);
private: private:
void LoadMaterials(const std::filesystem::path& filename); void LoadMTL(const std::filesystem::path& filename);
private:
void AddMaterial(std::string name, std::shared_ptr<Material> material);
std::shared_ptr<Material> GetMaterial(std::string name);
private: private:
void CreateNewMesh();
void CreateNewMesh(const Material& material);
Mesh& GetLastMesh(); Mesh& GetLastMesh();
void CreateNewMesh(const std::string& materialName);
void AddMaterial(MaterialID id, const Material& material);
Material* GetMaterial(const MaterialID& id);
public: public:
void Render(Shader& shader, unsigned int count); void Render(Shader& shader, unsigned int count);
[[nodiscard]] inline const std::string Name() const { return m_name; } [[nodiscard]] inline const std::string Name() const { return m_name; }
protected: protected:
void EnableBatch(unsigned int instanceVBO); void EnableBatch(const OpenGL::InstanceBuffer* instanceBuffer);
private: private:
std::string m_name; std::string m_name;
std::vector<glm::vec3> m_vertices; std::vector<glm::vec3> m_vertices;
std::vector<glm::vec3> m_normals; std::vector<glm::vec3> m_normals;
std::vector<glm::vec2> m_texCoords; std::vector<glm::vec2> m_texCoords;
std::vector<Mesh> m_meshes; std::unordered_map<MaterialID, Material> m_materials;
std::unordered_map<std::string, std::shared_ptr<Material>> m_materials;
}; };
} }

View File

@ -6,6 +6,8 @@
namespace Core { namespace Core {
class Entity; class Entity;
class Renderer;
class Prefab;
class ENGINE_API Scene { class ENGINE_API Scene {
public: public:
@ -14,8 +16,9 @@ public:
Entity CreateEntity(); Entity CreateEntity();
private: private:
entt::registry m_registry; entt::registry m_registry;
friend class Renderer; friend class Core::Renderer;
friend class Entity; friend class Core::Entity;
friend class Core::Prefab;
}; };
class ENGINE_API Entity { class ENGINE_API Entity {

View File

@ -0,0 +1,22 @@
#ifndef TIME_TIMESTEP_H_
#define TIME_TIMESTEP_H_
namespace Core {
class Timestep {
public:
Timestep(float time = 0.0f)
: m_time(time) {}
[[nodiscard]] float GetSeconds() const { return m_time; }
[[nodiscard]] float GetMilliseconds() const { return m_time * 1000.f; }
operator float() const { return m_time; }
public:
static Timestep FromMilliseconds(float milliseconds) { return Timestep(milliseconds * 0.001f); }
static Timestep FromSeconds(float seconds) { return Timestep(seconds); }
private:
float m_time;
};
}
#endif // TIME_TIMESTEP_H_

View File

@ -11,24 +11,24 @@ batch::batch() {
} }
void batch::prepare(glm::mat4 *instances, unsigned int count) { void batch::prepare(glm::mat4 *instances, unsigned int count) {
if (m_instance_vbo == 0) { if (!m_instanceBuffer) {
glGenBuffers(1, &m_instance_vbo); m_instanceBuffer = new OpenGL::InstanceBuffer(GL_DYNAMIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, m_instance_vbo); OpenGL::Buffer::Bind(m_instanceBuffer);
// Allocate *once*, no data yet — just reserve space OpenGL::Buffer::Data(m_instanceBuffer, nullptr, sizeof(glm::mat4) * count);
glBufferData(GL_ARRAY_BUFFER, sizeof(glm::mat4) * count, nullptr, GL_DYNAMIC_DRAW); OpenGL::Buffer::Unbind(m_instanceBuffer);
m_instance_count = count; m_instance_count = count;
} else if (count > m_instance_count) { } else if (count > m_instance_count) {
// Optional: reallocate only if you *really* have more instances than before // Optional: reallocate only if you *really* have more instances than before
// FIXME: what the hell is m_instance_vbo
glBindBuffer(GL_ARRAY_BUFFER, m_instance_vbo); glBindBuffer(GL_ARRAY_BUFFER, m_instance_vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(glm::mat4) * count, nullptr, GL_DYNAMIC_DRAW); OpenGL::Buffer::Bind(m_instanceBuffer);
OpenGL::Buffer::Data(m_instanceBuffer, nullptr, sizeof(glm::mat4) * count);
OpenGL::Buffer::Unbind(m_instanceBuffer);
m_instance_count = count; m_instance_count = count;
} else {
glBindBuffer(GL_ARRAY_BUFFER, m_instance_vbo);
} }
// Just update the data region — much cheaper // Just update the data region — much cheaper
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(glm::mat4) * count, instances); OpenGL::Buffer::SubData(m_instanceBuffer, instances, sizeof(glm::mat4) * count, 0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
} }
} }

View File

@ -0,0 +1,24 @@
#include "engine/input/input.h"
namespace Core {
bool* Input::s_keys_state = nullptr;
int Input::s_keys_state_size = 0;
// Engine only function
void Input::Update(Timestep dt) {
s_keys_state = (bool*)SDL_GetKeyboardState(&s_keys_state_size);
}
glm::vec2 Input::GetRelativeMouse() {
glm::vec2 mouse;
SDL_GetRelativeMouseState(&mouse.x, &mouse.y);
return mouse;
}
bool Input::IsKeyPressed(SDL_Scancode keyCode) {
assert(keyCode < s_keys_state_size && "Key is out of bounds of the key input state");
return s_keys_state[keyCode];
}
}

View File

@ -0,0 +1,119 @@
#include <iostream>
#include <cassert>
#include "engine/opengl/buffers.h"
namespace Core {
namespace OpenGL {
BufferID Buffer::m_bound = 0;
Buffer::Buffer(BufferTarget target, BufferUsage usage)
: m_target(target), m_usage(usage)
{
glGenBuffers(1, &m_buffer);
}
Buffer::Buffer(BufferTarget target)
: Buffer(target, GL_STATIC_DRAW) {}
Buffer::~Buffer() {
glDeleteBuffers(1, &m_buffer);
}
void Buffer::Data(const Buffer* buffer, const void *data, size_t size) {
if (!IsBound(buffer)) Bind(buffer);
glBufferData(buffer->m_target, size, data, buffer->m_usage);
}
void Buffer::SubData(const Buffer* buffer, const void *data, size_t size, size_t offset) {
if (!IsBound(buffer)) Bind(buffer);
glBufferSubData(buffer->m_target, offset, size, data);
}
void Buffer::BindBuffer(unsigned int index) const {
Buffer::Bind(this);
glBindBufferBase(m_target, index, m_buffer);
Buffer::Unbind(this);
}
void Buffer::BindBufferRanged(unsigned int index, size_t offset, size_t size) const {
Buffer::Bind(this);
glBindBufferRange(m_target, index, m_buffer, offset, size);
Buffer::Unbind(this);
}
unsigned int UniformBuffer::s_bufferNextId = 1;
UniformBuffer::UniformBuffer(size_t size, unsigned int index)
: Buffer(GL_UNIFORM_BUFFER, GL_STATIC_DRAW), m_uniformBinding(s_bufferNextId++)
{
Buffer::Bind(this);
Data(this, nullptr, size);
Buffer::Unbind(this);
BindBuffer(m_uniformBinding);
}
void UniformBuffer::ConfigureShader(Shader& shader, const char* uniformName) const {
auto uniformIndex = glGetUniformBlockIndex(shader.GetID(), uniformName);
glUniformBlockBinding(shader.GetID(), uniformIndex, m_uniformBinding);
}
ArrayBuffer::ArrayBuffer(BufferUsage usage)
: Buffer(GL_ARRAY_BUFFER, usage) {}
InstanceBuffer::InstanceBuffer(BufferUsage usage)
: ArrayBuffer(usage) {}
VertexArray::VertexArray() : m_id(0) {
glGenVertexArrays(1, &m_id);
std::cout << "[DEBUG] VArr initialized: " << m_id << std::endl;
}
VertexArray::~VertexArray() {
// if (m_vbo) {
// delete m_vbo;
// }
glDeleteVertexArrays(1, &m_id);
}
void VertexArray::Bind() {
assert(m_id != 0 && "Vertex Array wasn't initialized.");
std::cout << "[DEBUG] VArr binding: " << m_id << std::endl;
glBindVertexArray(m_id);
}
void VertexArray::Unbind() {
assert(m_id != 0 && "Vertex Array wasn't initialized.");
// TODO: Add EBO as well
if (Buffer::IsBound(m_vbo)) {
Buffer::Unbind(m_vbo);
}
glBindVertexArray(0);
}
void VertexArray::SetupVertexBuffer(BufferUsage usage) {
if (m_vbo) {
delete m_vbo;
}
m_vbo = new ArrayBuffer(usage);
Buffer::Bind(m_vbo);
Buffer::Data(m_vbo, nullptr, 0);
}
void VertexArray::VertexBufferData(size_t size, const void* data) {
assert(m_vbo != nullptr && "Trying to upload vertex buffer data to nullptr");
Buffer::Bind(m_vbo);
Buffer::Data(m_vbo, data, size);
}
} // namespace OpenGL
} // namespace Core

View File

@ -5,6 +5,9 @@
#include "engine/window/event.hpp" #include "engine/window/event.hpp"
#include "engine/renderer/wavefront.h" #include "engine/renderer/wavefront.h"
#include "engine/time/timestep.h"
#include "engine/input/input.h"
namespace Core { namespace Core {
Engine* Engine::s_instance = nullptr; Engine* Engine::s_instance = nullptr;
@ -21,10 +24,19 @@ void Engine::Run(std::unique_ptr<IApplication> app) {
m_window->Subscribe(this); m_window->Subscribe(this);
uint64_t now = SDL_GetPerformanceCounter();
m_elapsed = 0;
while (m_running) { while (m_running) {
m_elapsed = now;
now = SDL_GetPerformanceCounter();
auto dt = Timestep::FromMilliseconds((float)((now - m_elapsed)*1000 / (float)SDL_GetPerformanceFrequency()));
m_window->ProcessEvents(); m_window->ProcessEvents();
m_app->OnUpdate(); Input::Update(dt);
m_app->OnUpdate(dt);
m_renderer->Render(); m_renderer->Render();

View File

@ -1,53 +1,56 @@
#include <iostream>
#include <cstddef> #include <cstddef>
#include "engine/renderer/mesh.h" #include "engine/renderer/mesh.h"
namespace Core { namespace Core {
Mesh::Mesh() { Mesh::Mesh(const std::string& name) : m_materialName(name.c_str()) {
m_vao = 0; std::cout << "Mesh init" << std::endl;
m_vbo = 0;
// m_vao = 0;
// m_vbo = 0;
m_ebo = 0; m_ebo = 0;
glGenVertexArrays(1, &m_vao); // glGenVertexArrays(1, &m_vao);
glGenBuffers(1, &m_vbo); // glGenBuffers(1, &m_vbo);
glGenBuffers(1, &m_ebo); glGenBuffers(1, &m_ebo);
glBindVertexArray(m_vao); Bind();
// VBO (vertex buffer) SetupVertexBuffer(GL_DYNAMIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, m_vbo);
glBufferData(GL_ARRAY_BUFFER, 0, nullptr, GL_DYNAMIC_DRAW);
// EBO (index buffer) // EBO (index buffer)
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_ebo); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_ebo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, 0, nullptr, GL_DYNAMIC_DRAW); glBufferData(GL_ELEMENT_ARRAY_BUFFER, 0, nullptr, GL_DYNAMIC_DRAW);
// attributes // attributes
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast<const void*>(offsetof(Vertex, m_position))); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast<const void*>(offsetof(Vertex, position)));
glEnableVertexAttribArray(0); glEnableVertexAttribArray(0);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast<const void*>(offsetof(Vertex, m_normal))); glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast<const void*>(offsetof(Vertex, normal)));
glEnableVertexAttribArray(1); glEnableVertexAttribArray(1);
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast<const void*>(offsetof(Vertex, m_texCoord))); glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast<const void*>(offsetof(Vertex, uv)));
glEnableVertexAttribArray(2); glEnableVertexAttribArray(2);
glBindBuffer(GL_ARRAY_BUFFER, 0); // TODO: delete after ebo moved in VertexArray
glBindVertexArray(0); // glBindBuffer(GL_DYNAMIC_DRAW, 0);
Unbind();
} }
void Mesh::Upload() const { void Mesh::Upload() {
glBindVertexArray(m_vao); Bind();
glBindBuffer(GL_ARRAY_BUFFER, m_vbo); VertexBufferData(m_vertexBuffer.size() * sizeof(Vertex), m_vertexBuffer.data());
glBufferData(GL_ARRAY_BUFFER, m_vertexBuffer.size() * sizeof(Vertex), m_vertexBuffer.data(), GL_DYNAMIC_DRAW);
// Upload indices // Upload indices
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_ebo); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_ebo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, m_indexBuffer.size() * sizeof(unsigned int), m_indexBuffer.data(), GL_DYNAMIC_DRAW); glBufferData(GL_ELEMENT_ARRAY_BUFFER, m_indexBuffer.size() * sizeof(unsigned int), m_indexBuffer.data(), GL_DYNAMIC_DRAW);
glBindVertexArray(0); // TODO: delete after ebo moved in VertexArray
// glBindBuffer(GL_DYNAMIC_DRAW, 0);
Unbind();
} }
void Mesh::Render(unsigned int count) void Mesh::Render(unsigned int count)

View File

@ -9,6 +9,7 @@
#include <glm/gtx/euler_angles.hpp> #include <glm/gtx/euler_angles.hpp>
#include <glm/gtx/string_cast.hpp> #include <glm/gtx/string_cast.hpp>
#include <glm/gtc/type_ptr.hpp>
#include "engine/renderer/renderer.h" #include "engine/renderer/renderer.h"
#include "engine/window/window.h" #include "engine/window/window.h"
@ -19,18 +20,13 @@
#include "engine/components/light.h" #include "engine/components/light.h"
#include "engine/components/mesh.h" #include "engine/components/mesh.h"
#include "engine/components/batch.h" #include "engine/components/batch.h"
#include "engine/3d/prefab.hpp"
namespace Core { namespace Core {
Renderer::Renderer(std::shared_ptr<Scene> scene) : m_scene(scene) Renderer::Renderer(std::shared_ptr<Scene> scene)
: m_scene(scene), m_uniform_matrices(2 * sizeof(glm::mat4), 1)
{ {
m_proj = glm::perspective(
static_cast<float>(M_PI_2),
static_cast<float>(Window::GetWidth()) / static_cast<float>(Window::GetHeight()),
0.01f,
100.0f
);
m_shader.init( m_shader.init(
FileManager::read("./engine/src/shaders/main.vs"), FileManager::read("./engine/src/shaders/main.vs"),
FileManager::read("./engine/src/shaders/pbr.fs") FileManager::read("./engine/src/shaders/pbr.fs")
@ -41,6 +37,19 @@ Renderer::Renderer(std::shared_ptr<Scene> scene) : m_scene(scene)
FileManager::read("./engine/src/shaders/depth.fs") FileManager::read("./engine/src/shaders/depth.fs")
); );
// glUniformBlockBinding(m_shader.m_id, glGetUniformBlockIndex(m_shader.m_id, "Matrices"), 1);
m_uniform_matrices.ConfigureShader(m_shader, "Matrices");
m_proj = glm::perspective(
static_cast<float>(M_PI_2),
static_cast<float>(Window::GetWidth()) / static_cast<float>(Window::GetHeight()),
0.01f,
100.0f
);
m_uniform_matrices.UpdateUniform<glm::mat4>(glm::value_ptr(m_proj), 0);
m_model = glm::mat4(1.f); m_model = glm::mat4(1.f);
m_shader.use(); m_shader.use();
@ -49,6 +58,14 @@ Renderer::Renderer(std::shared_ptr<Scene> scene) : m_scene(scene)
void Renderer::Init() { void Renderer::Init() {
GenerateShadowMaps(); GenerateShadowMaps();
for (auto [entt, mesh] : m_scene->m_registry.view<mesh>().each()) {
mesh.mesh->Prepare();
}
for (auto [entt, prefab] : m_scene->m_registry.view<Prefab>().each()) {
prefab.Prepare();
}
} }
void Renderer::OnWindowResized(int w, int h) { void Renderer::OnWindowResized(int w, int h) {
@ -58,6 +75,7 @@ void Renderer::OnWindowResized(int w, int h) {
0.01f, 0.01f,
100.0f 100.0f
); );
m_uniform_matrices.UpdateUniform<glm::mat4>(glm::value_ptr(m_proj), 0);
} }
void Renderer::ApplyLights(Shader &shader) { void Renderer::ApplyLights(Shader &shader) {
@ -67,7 +85,7 @@ void Renderer::ApplyLights(Shader &shader) {
size_t lightIndex = 0; size_t lightIndex = 0;
for (auto entity : lights) { for (auto entity : lights) {
auto &l = m_scene->m_registry.get<light>(entity); auto &l = m_scene->m_registry.get<light>(entity);
auto &transf = m_scene->m_registry.get<transform>(entity); auto &transf = m_scene->m_registry.get<Transform>(entity);
shader.setInt("lights[" + std::to_string(lightIndex) + "].type", static_cast<int>(l.type)); shader.setInt("lights[" + std::to_string(lightIndex) + "].type", static_cast<int>(l.type));
shader.setVec3("lights[" + std::to_string(lightIndex) + "].position", transf.position); shader.setVec3("lights[" + std::to_string(lightIndex) + "].position", transf.position);
@ -111,8 +129,8 @@ void Renderer::EnsureShadowResources(light& l) {
void Renderer::UpdateView() { void Renderer::UpdateView() {
auto camView = m_scene->m_registry.view<camera>(); auto camView = m_scene->m_registry.view<camera>();
auto camTransform = camView.size() > 0 ? auto camTransform = camView.size() > 0 ?
m_scene->m_registry.get<transform>(camView.back()) : m_scene->m_registry.get<Transform>(camView.back()) :
transform {glm::vec3(0.f, 0.f, 0.f), glm::vec3(0.f, 0.f, 0.f), glm::vec3(1.f, 1.f, 1.f)}; Transform {glm::vec3(0.f, 0.f, 0.f), glm::vec3(0.f, 0.f, 0.f), glm::vec3(1.f, 1.f, 1.f)};
m_view = glm::lookAt( m_view = glm::lookAt(
camTransform.position, camTransform.position,
@ -120,6 +138,8 @@ void Renderer::UpdateView() {
glm::vec3(0.f, 1.f, 0.f) glm::vec3(0.f, 1.f, 0.f)
); );
m_uniform_matrices.UpdateUniform<glm::mat4>(glm::value_ptr(m_view), sizeof(glm::mat4));
m_shader.setVec3("viewPos", camTransform.position); m_shader.setVec3("viewPos", camTransform.position);
m_shader.setMat4("u_view", m_view); m_shader.setMat4("u_view", m_view);
@ -127,46 +147,55 @@ void Renderer::UpdateView() {
} }
void Renderer::RenderScene(Shader &shader) { void Renderer::RenderScene(Shader &shader) {
std::unordered_map<unsigned int, std::vector<entt::entity>> batches; // std::unordered_map<unsigned int, std::vector<entt::entity>> batches;
for (auto [entt, item] : m_scene->m_registry.view<batch::item>().each()) { // for (auto [entt, item] : m_scene->m_registry.view<batch::item>().each()) {
if (batches.find(item.batchId) == batches.end()) // if (batches.find(item.batchId) == batches.end())
batches.insert(std::make_pair(item.batchId, std::vector<entt::entity>())); // batches.insert(std::make_pair(item.batchId, std::vector<entt::entity>()));
batches[item.batchId].push_back(entt); // batches[item.batchId].push_back(entt);
} // }
shader.setBool("u_isInstanced", true); // shader.setBool("u_isInstanced", true);
// shader.setBool("isLight", false);
// shader.setVec3("currentLightColor", glm::vec3(0.f));
// for (auto [entt, b, m] : m_scene->m_registry.view<batch, mesh>().each()) {
// // check if have items for batch render
// if (batches.find(b.id()) == batches.end()) continue;
// auto &batchItems = batches[b.id()];
// std::vector<glm::mat4> models;
// models.reserve(batchItems.size());
// for (auto item : batchItems) {
// auto &t = m_scene->m_registry.get<Transform>(item);
// glm::mat4 rotation = glm::yawPitchRoll(t.rotation.y, t.rotation.x, t.rotation.z);
// auto itemModel = glm::translate(glm::mat4(1.f), t.position) * rotation;
// models.push_back(itemModel);
// }
// auto prevState = b.Initialized();
// b.prepare(models.data(), models.size());
// if (!prevState) {
// std::cout << "[DEBUG] enabling batch" << std::endl;
// // TODO:
// // m.object->EnableBatch(b.m_instanceBuffer);
// }
// m.mesh->Render(shader);
// }
// shader.setBool("u_isInstanced", false);
// light cannot be batch rendered (yet :3)
shader.setBool("isLight", false); shader.setBool("isLight", false);
shader.setVec3("currentLightColor", glm::vec3(0.f)); shader.setVec3("currentLightColor", glm::vec3(0.f));
for (auto [entt, b, m] : m_scene->m_registry.view<batch, mesh>().each()) { for (auto [entity, prefab] : m_scene->m_registry.view<Prefab>().each()) {
// check if have items for batch render prefab.Render(shader, *m_scene.get(), 1);
if (batches.find(b.id()) == batches.end()) continue;
auto &batchItems = batches[b.id()];
std::vector<glm::mat4> models;
models.reserve(batchItems.size());
for (auto item : batchItems) {
auto &t = m_scene->m_registry.get<transform>(item);
glm::mat4 rotation = glm::yawPitchRoll(t.rotation.y, t.rotation.x, t.rotation.z);
auto itemModel = glm::translate(glm::mat4(1.f), t.position) * rotation;
models.push_back(itemModel);
}
auto prevInstanceVBO = b.m_instance_vbo;
b.prepare(models.data(), models.size());
if (prevInstanceVBO <= 0) {
std::cout << "[DEBUG] enabling batch" << std::endl;
m.object->EnableBatch(b.m_instance_vbo);
}
m.object->Render(shader, batchItems.size());
} }
shader.setBool("u_isInstanced", false);
for (auto [entity, transf, mesh] : m_scene->m_registry.view<transform, mesh>(entt::exclude<batch, batch::item>).each()) { // entt::exclude<batch, batch::item>
if (mesh.object == nullptr) { for (auto [entity, transf, mesh] : m_scene->m_registry.view<Transform, mesh>().each()) {
if (mesh.mesh == nullptr) {
std::cerr << "WARN: Entity doesn't have a mesh to render" << std::endl; std::cerr << "WARN: Entity doesn't have a mesh to render" << std::endl;
return; return;
} }
@ -185,7 +214,7 @@ void Renderer::RenderScene(Shader &shader) {
shader.setMat4("u_model", m_model); shader.setMat4("u_model", m_model);
mesh.object->Render(shader, 1); mesh.mesh->Render(shader, *m_scene.get(), 1);
} }
} }
@ -206,7 +235,7 @@ void Renderer::Render() {
glCullFace(GL_FRONT); glCullFace(GL_FRONT);
const auto lights = m_scene->m_registry.view<light, transform>(); const auto lights = m_scene->m_registry.view<light, Transform>();
for (auto [_, l, t] : lights.each()) { for (auto [_, l, t] : lights.each()) {
// TODO: support other light types when ready // TODO: support other light types when ready

View File

@ -9,8 +9,8 @@
namespace Core { namespace Core {
std::unique_ptr<Texture> Texture::LoadFile(const std::string& filename) { Texture* Texture::LoadFile(const std::string& filename) {
auto texture = std::make_unique<Texture>(); auto texture = new Texture();
int w, h, c; int w, h, c;
unsigned char *data = stbi_load(filename.c_str(), &w, &h, &c, 4); unsigned char *data = stbi_load(filename.c_str(), &w, &h, &c, 4);
@ -19,8 +19,8 @@ std::unique_ptr<Texture> Texture::LoadFile(const std::string& filename) {
std::exit(1); std::exit(1);
} }
glGenTextures(1, &texture.get()->m_id); glGenTextures(1, &texture->m_id);
glBindTexture(GL_TEXTURE_2D, texture.get()->m_id); glBindTexture(GL_TEXTURE_2D, texture->m_id);
// TODO: configure properly // TODO: configure properly
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
@ -34,7 +34,7 @@ std::unique_ptr<Texture> Texture::LoadFile(const std::string& filename) {
std::cout << "Loaded texture under '" << filename << "' with size of " << sizeof(data) << " bytes" << std::endl; std::cout << "Loaded texture under '" << filename << "' with size of " << sizeof(data) << " bytes" << std::endl;
stbi_image_free(data); stbi_image_free(data);
return std::move(texture); return texture;
} }
} }

View File

@ -8,24 +8,10 @@
#include "engine/renderer/wavefront.h" #include "engine/renderer/wavefront.h"
#include "engine/IO/parser.h" #include "engine/IO/parser.h"
#include "engine/renderer/mesh.h" #include "engine/3d/mesh.hpp"
#define DEFAULT_MATERIAL_NAME "default"
namespace Core { namespace Core {
// ObjElement toElement(const std::string &s) {
// if (s == "#") return ObjElement::OHASH;
// if (s == "mtllib") return ObjElement::MTLLIB;
// if (s == "usemtl") return ObjElement::USEMTL;
// if (s == "o") return ObjElement::O;
// if (s == "v") return ObjElement::V;
// if (s == "vn") return ObjElement::VN;
// if (s == "vt") return ObjElement::VT;
// if (s == "f") return ObjElement::F;
// return ObjElement::OUNKNOWN;
// }
inline ObjElement toElement(const char* s) { inline ObjElement toElement(const char* s) {
switch (s[0]) { switch (s[0]) {
case '#': return ObjElement::OHASH; case '#': return ObjElement::OHASH;
@ -42,22 +28,6 @@ inline ObjElement toElement(const char* s) {
return ObjElement::OUNKNOWN; return ObjElement::OUNKNOWN;
} }
// MtlElement toMtlElement(const std::string &s) {
// if (s == "#") return MtlElement::MHASH;
// if (s == "newmtl") return MtlElement::NEWMTL;
// if (s == "Ns") return MtlElement::NS;
// if (s == "Ka") return MtlElement::KA;
// if (s == "Ks") return MtlElement::KS;
// if (s == "Kd") return MtlElement::KD;
// if (s == "Ni") return MtlElement::NI;
// if (s == "d") return MtlElement::D;
// if (s == "illum") return MtlElement::ILLUM;
// if (s == "map_Kd") return MtlElement::MAP_KD;
// if (s == "map_Ka") return MtlElement::MAP_KA;
// // if (s == "map_Ke") return MtlElement::MAP_KE;
// return MtlElement::MUNKNOWN;
// }
inline MtlElement toMtlElement(const char* s) { inline MtlElement toMtlElement(const char* s) {
switch (s[0]) { switch (s[0]) {
case '#': return MtlElement::MHASH; case '#': return MtlElement::MHASH;
@ -104,7 +74,11 @@ Object::Object() {
m_texCoords = std::vector<glm::vec2>(); m_texCoords = std::vector<glm::vec2>();
} }
void Object::LoadMaterials(const std::filesystem::path& filename) { void Object::AddMaterial(MaterialID id, const Material& material) {
m_materials.insert(std::make_pair(id, material));
}
void Object::LoadMTL(const std::filesystem::path& filename) {
std::ifstream file(filename); std::ifstream file(filename);
if (!file.is_open()) { if (!file.is_open()) {
std::cerr << "Failed to open MTL file: " << filename << std::endl; std::cerr << "Failed to open MTL file: " << filename << std::endl;
@ -112,7 +86,8 @@ void Object::LoadMaterials(const std::filesystem::path& filename) {
} }
std::string currentMaterialName; std::string currentMaterialName;
std::shared_ptr<Material> currentMaterial; Material currentMaterial;
bool hasCurrent = false;
char line[1024]; // buffer per line char line[1024]; // buffer per line
@ -128,15 +103,15 @@ void Object::LoadMaterials(const std::filesystem::path& filename) {
case MtlElement::NEWMTL: case MtlElement::NEWMTL:
{ {
// If a material was being built, commit it first // If a material was being built, commit it first
if (currentMaterial) { if (hasCurrent) {
AddMaterial(currentMaterialName, std::move(currentMaterial)); AddMaterial(currentMaterialName, currentMaterial);
currentMaterial = nullptr;
} }
char* materialName = p.TakeWord(); char* materialName = p.TakeWord();
if (materialName) { if (materialName) {
currentMaterialName = materialName; currentMaterialName = materialName;
currentMaterial = std::make_shared<Material>(); currentMaterial = Material(currentMaterialName);
hasCurrent = true;
} }
break; break;
} }
@ -144,7 +119,7 @@ void Object::LoadMaterials(const std::filesystem::path& filename) {
case MtlElement::NS: // specular weight case MtlElement::NS: // specular weight
{ {
float weight = p.TakeFloat(); float weight = p.TakeFloat();
if (currentMaterial) currentMaterial->SetSpecularWeight(weight); if (hasCurrent) currentMaterial.SetSpecularWeight(weight);
break; break;
} }
@ -153,7 +128,7 @@ void Object::LoadMaterials(const std::filesystem::path& filename) {
float r = p.TakeFloat(); float r = p.TakeFloat();
float g = p.TakeFloat(); float g = p.TakeFloat();
float b = p.TakeFloat(); float b = p.TakeFloat();
if (currentMaterial) currentMaterial->SetAmbientColor(glm::vec3(r, g, b)); if (hasCurrent) currentMaterial.SetAmbientColor(glm::vec3(r, g, b));
break; break;
} }
@ -162,7 +137,7 @@ void Object::LoadMaterials(const std::filesystem::path& filename) {
float r = p.TakeFloat(); float r = p.TakeFloat();
float g = p.TakeFloat(); float g = p.TakeFloat();
float b = p.TakeFloat(); float b = p.TakeFloat();
if (currentMaterial) currentMaterial->SetSpecularColor(glm::vec3(r, g, b)); if (hasCurrent) currentMaterial.SetSpecularColor(glm::vec3(r, g, b));
break; break;
} }
@ -171,21 +146,21 @@ void Object::LoadMaterials(const std::filesystem::path& filename) {
float r = p.TakeFloat(); float r = p.TakeFloat();
float g = p.TakeFloat(); float g = p.TakeFloat();
float b = p.TakeFloat(); float b = p.TakeFloat();
if (currentMaterial) currentMaterial->SetDiffuseColor(glm::vec3(r, g, b)); if (hasCurrent) currentMaterial.SetDiffuseColor(glm::vec3(r, g, b));
break; break;
} }
case MtlElement::D: // opacity case MtlElement::D: // opacity
{ {
float d = p.TakeFloat(); float d = p.TakeFloat();
if (currentMaterial) currentMaterial->SetOpacity(d); if (hasCurrent) currentMaterial.SetOpacity(d);
break; break;
} }
case MtlElement::ILLUM: // illumination model case MtlElement::ILLUM: // illumination model
{ {
int illum = p.TakeInt(); int illum = p.TakeInt();
if (currentMaterial) currentMaterial->SetIllumination(illum); if (hasCurrent) currentMaterial.SetIllumination(illum);
break; break;
} }
@ -193,7 +168,7 @@ void Object::LoadMaterials(const std::filesystem::path& filename) {
{ {
// take rest of line as texture path (can contain spaces) // take rest of line as texture path (can contain spaces)
char* texPath = p.TakeUntil('\0'); char* texPath = p.TakeUntil('\0');
if (texPath && currentMaterial) { if (texPath && hasCurrent) {
// trim trailing spaces // trim trailing spaces
size_t len = std::strlen(texPath); size_t len = std::strlen(texPath);
while (len > 0 && (texPath[len - 1] == ' ' || texPath[len - 1] == '\t')) while (len > 0 && (texPath[len - 1] == ' ' || texPath[len - 1] == '\t'))
@ -201,7 +176,7 @@ void Object::LoadMaterials(const std::filesystem::path& filename) {
std::filesystem::path texturePath = filename.parent_path() / texPath; std::filesystem::path texturePath = filename.parent_path() / texPath;
currentMaterial->SetDiffuseTexture(Texture::LoadFile(texturePath.string())); currentMaterial.SetDiffuseTexture(Texture::LoadFile(texturePath.string()));
} }
break; break;
} }
@ -209,13 +184,13 @@ void Object::LoadMaterials(const std::filesystem::path& filename) {
case MtlElement::MAP_KA: // ambient texture map case MtlElement::MAP_KA: // ambient texture map
{ {
char* texPath = p.TakeUntil('\0'); char* texPath = p.TakeUntil('\0');
if (texPath && currentMaterial) { if (texPath && hasCurrent) {
size_t len = std::strlen(texPath); size_t len = std::strlen(texPath);
while (len > 0 && (texPath[len - 1] == ' ' || texPath[len - 1] == '\t')) while (len > 0 && (texPath[len - 1] == ' ' || texPath[len - 1] == '\t'))
texPath[--len] = '\0'; texPath[--len] = '\0';
// optional: handle ambient texture // optional: handle ambient texture
// currentMaterial->SetAmbientTexture(Texture::LoadFile(texPath)); // currentMaterial.SetAmbientTexture(Texture::LoadFile(texPath));
} }
break; break;
} }
@ -227,41 +202,36 @@ void Object::LoadMaterials(const std::filesystem::path& filename) {
} }
// Commit last material if pending // Commit last material if pending
if (currentMaterial) { if (hasCurrent) {
AddMaterial(currentMaterialName, std::move(currentMaterial)); AddMaterial(currentMaterialName, currentMaterial);
} }
file.close(); file.close();
} }
void Object::AddMaterial(std::string name, std::shared_ptr<Material> material) Material* Object::GetMaterial(const MaterialID& id)
{ {
m_materials.insert(std::make_pair(std::move(name), std::move(material))); auto material = m_materials.find(id);
}
std::shared_ptr<Material> Object::GetMaterial(std::string name)
{
auto material = m_materials.find(name);
if (material == m_materials.end()) return nullptr; if (material == m_materials.end()) return nullptr;
return material->second; return &material->second;
} }
void Object::CreateNewMesh(const std::string& materialName) void Object::CreateNewMesh(const Material& material)
{ {
Mesh mesh; EmplaceBack(material);
mesh.materialName = materialName; }
m_meshes.push_back(mesh);
void Object::CreateNewMesh()
{
EmplaceBack();
} }
Mesh& Object::GetLastMesh() Mesh& Object::GetLastMesh()
{ {
if (m_meshes.empty()) { if (Empty()) {
auto material = std::make_shared<Material>(); CreateNewMesh(Material::Default());
material->SetAmbientColor(glm::vec3(0.52f, 0.52f, 0.52f));
AddMaterial(DEFAULT_MATERIAL_NAME, std::move(material));
CreateNewMesh(DEFAULT_MATERIAL_NAME);
} }
return m_meshes.back(); return Back();
} }
Object* Object::LoadFile(const std::string& filename) { Object* Object::LoadFile(const std::string& filename) {
@ -289,7 +259,7 @@ Object* Object::LoadFile(const std::string& filename) {
if (mtlFile) { if (mtlFile) {
std::filesystem::path fullPath = filename; std::filesystem::path fullPath = filename;
std::filesystem::path mtlPath = fullPath.replace_filename(mtlFile); std::filesystem::path mtlPath = fullPath.replace_filename(mtlFile);
obj->LoadMaterials(mtlPath); obj->LoadMTL(mtlPath);
} }
break; break;
} }
@ -298,11 +268,16 @@ Object* Object::LoadFile(const std::string& filename) {
{ {
char* materialName = p.TakeWord(); char* materialName = p.TakeWord();
if (materialName) { if (materialName) {
auto& mesh = obj->GetLastMesh(); auto material = obj->GetMaterial(materialName);
if (mesh.materialName != materialName) { if (!material) {
Mesh newMesh; // Not defined material being used.
newMesh.materialName = materialName; std::cerr << "[WARN] WavefrontError: use of undefined material '"
obj->m_meshes.push_back(newMesh); << materialName << "'" << std::endl;
material = new Material();
}
auto mesh = obj->FindMeshByMaterial(material);
if (mesh == obj->End()) {
obj->CreateNewMesh(*material);
} }
} }
break; break;
@ -326,6 +301,7 @@ Object* Object::LoadFile(const std::string& filename) {
x /= w; y /= w; z /= w; x /= w; y /= w; z /= w;
} }
obj->m_vertices.emplace_back(x, y, z); obj->m_vertices.emplace_back(x, y, z);
break; break;
} }
@ -349,8 +325,11 @@ Object* Object::LoadFile(const std::string& filename) {
case ObjElement::F: // face case ObjElement::F: // face
{ {
auto& mesh = obj->GetLastMesh(); auto& mesh = obj->GetLastMesh();
int raw_vi, raw_ti, raw_ni;
std::vector<uint32_t> faceIndices;
faceIndices.reserve(8);
int raw_vi, raw_ti, raw_ni;
while (p.TakeFaceIndices(raw_vi, raw_ti, raw_ni)) { while (p.TakeFaceIndices(raw_vi, raw_ti, raw_ni)) {
// Convert raw OBJ indices to 0-based / -1 sentinel // Convert raw OBJ indices to 0-based / -1 sentinel
int vi = Object::NormalizeIndex(raw_vi, (int)obj->m_vertices.size()); int vi = Object::NormalizeIndex(raw_vi, (int)obj->m_vertices.size());
@ -362,15 +341,25 @@ Object* Object::LoadFile(const std::string& filename) {
continue; continue;
} }
glm::vec3 vert = obj->m_vertices[vi]; Vertex v;
glm::vec3 norm(0.0f); v.position = obj->m_vertices[vi];
glm::vec2 texCoord(0.0f); v.normal = (ni >= 0) ? obj->m_normals[ni] : glm::vec3(0.0f);
v.uv = (ti >= 0) ? obj->m_texCoords[ti] : glm::vec2(0.0f);
if (ni >= 0) norm = obj->m_normals[ni]; uint32_t idx = mesh.PushVertex(v);
if (ti >= 0) texCoord = obj->m_texCoords[ti]; faceIndices.push_back(idx);
// mesh.m_vertexBuffer.emplace_back(vert, norm, texCoord);
// mesh.m_indexBuffer.push_back(mesh.m_vertexBuffer.size() - 1);
}
mesh.m_vertexBuffer.emplace_back(vert, norm, texCoord); // [0, 1, 2]
mesh.m_indexBuffer.push_back(mesh.m_vertexBuffer.size() - 1); // ^
// triangulate polygon (fan)
if (faceIndices.size() >= 3) {
for (size_t i = 1; i + 1 < faceIndices.size(); ++i) {
mesh.PushTriangle(faceIndices[0], faceIndices[i], faceIndices[i+1]);
}
} }
break; break;
} }
@ -385,130 +374,107 @@ Object* Object::LoadFile(const std::string& filename) {
std::cout << "Vertices count: " << obj->m_vertices.size() << std::endl; std::cout << "Vertices count: " << obj->m_vertices.size() << std::endl;
std::cout << "Normals count: " << obj->m_normals.size() << std::endl; std::cout << "Normals count: " << obj->m_normals.size() << std::endl;
std::cout << "TexCoords count: " << obj->m_texCoords.size() << std::endl; std::cout << "TexCoords count: " << obj->m_texCoords.size() << std::endl;
std::cout << "Meshes count: " << obj->m_meshes.size() << std::endl; std::cout << "Meshes count: " << obj->GetSize() << std::endl;
std::cout << "Materials count: " << obj->m_materials.size() << std::endl; std::cout << "Materials count: " << obj->m_materials.size() << std::endl;
file.close(); file.close();
for (auto &mesh : obj->m_meshes) { unsigned int i = 0;
mesh.Upload(); for (auto it = obj->Begin(); it != obj->End(); ++it, ++i) {
std::cout << "Mesh #" << i << " primitives count: " << it->GetIndicesCount() / 3 << std::endl;
} }
return obj; return obj;
} }
void Object::EnableBatch(unsigned int instanceVBO) { void Object::EnableBatch(const OpenGL::InstanceBuffer* instanceBuffer) {
for (auto &mesh : m_meshes) { // FIXME:
mesh.Bind();
glBindBuffer(GL_ARRAY_BUFFER, instanceVBO); // for (auto &mesh : m_meshes) {
std::size_t vec4Size = sizeof(glm::vec4); // mesh.Bind();
for (int i = 0; i < 4; ++i) {
glEnableVertexAttribArray(3 + i); // use locations 3,4,5,6 for instance matrix
glVertexAttribPointer(3 + i, 4, GL_FLOAT, GL_FALSE,
sizeof(glm::mat4), (void*)(i * vec4Size));
glVertexAttribDivisor(3 + i, 1); // IMPORTANT: one per instance, not per vertex
}
glBindBuffer(GL_ARRAY_BUFFER, 0);
mesh.Unbind(); // instanceBuffer->StartConfigure();
} // std::size_t vec4Size = sizeof(glm::vec4);
// for (int i = 0; i < 4; ++i) {
// glEnableVertexAttribArray(3 + i); // use locations 3,4,5,6 for instance matrix
// glVertexAttribPointer(3 + i, 4, GL_FLOAT, GL_FALSE,
// sizeof(glm::mat4), (void*)(i * vec4Size));
// glVertexAttribDivisor(3 + i, 1); // IMPORTANT: one per instance, not per vertex
// }
// instanceBuffer->EndConfigure();
// mesh.Unbind();
// }
} }
// void Object::Render(Shader& shader)
// {
// for (auto &mesh : m_meshes) {
// auto material = GetMaterial(mesh.materialName);
// shader.setFloat("ambientStrength", 0.2f);
// shader.setFloat("shininess", material->GetSpecularWeight());
// shader.setFloat("opacity", material->GetOpacity());
// shader.setBool("useSpecular", material->GetIllumination() >= 2);
// shader.setFloat("specularStrength", 1.0f);
// shader.setVec3("ambientColor", material->GetAmbientColor());
// shader.setVec3("diffuseColor", material->GetDiffuseColor());
// shader.setVec3("specularColor", material->GetSpecularColor());
// if (material->HasDiffuseTexture()) {
// shader.setBool("useTexture", true);
// glActiveTexture(GL_TEXTURE0);
// glBindTexture(GL_TEXTURE_2D, material->GetDiffuseTexture()->GetID());
// shader.setInt("diffuseTex", 0);
// } else {
// shader.setBool("useTexture", false);
// }
// mesh.Render();
// }
// }
void Object::Render(Shader& shader, unsigned int count) void Object::Render(Shader& shader, unsigned int count)
{ {
for (auto &mesh : m_meshes) // FIXME:
{
auto material = GetMaterial(mesh.materialName);
// --- Basic material properties --- // for (auto &mesh : m_meshes)
shader.setFloat("opacity", material->GetOpacity()); // {
// auto material = GetMaterial(mesh.GetMaterialName());
// Albedo (base color) // // --- Basic material properties ---
shader.setVec3("albedo", material->GetDiffuseColor()); // shader.setFloat("opacity", material->GetOpacity());
// Metallic and roughness (defaults) // // Albedo (base color)
shader.setFloat("metallic", 0.8f); // shader.setVec3("albedo", material->GetDiffuseColor());
shader.setFloat("roughness", 0.5f);
shader.setFloat("ao", 1.0f); // default ambient occlusion if none
// --- Optional textures --- // // Metallic and roughness (defaults)
int texUnit = 0; // shader.setFloat("metallic", 0.8f);
// shader.setFloat("roughness", 0.5f);
// shader.setFloat("ao", 1.0f); // default ambient occlusion if none
// Albedo texture // // --- Optional textures ---
if (material->HasDiffuseTexture()) { // int texUnit = 0;
shader.setBool("useAlbedoMap", true);
glActiveTexture(GL_TEXTURE0 + texUnit);
glBindTexture(GL_TEXTURE_2D, material->GetDiffuseTexture()->GetID());
shader.setInt("albedoTex", texUnit++);
} else {
shader.setBool("useAlbedoMap", false);
}
// Metallic texture // // Albedo texture
// if (material->HasMetallicTexture()) { // if (material->HasDiffuseTexture()) {
if (false) { // shader.setBool("useAlbedoMap", true);
shader.setBool("useMetallicMap", true); // glActiveTexture(GL_TEXTURE0 + texUnit);
glActiveTexture(GL_TEXTURE0 + texUnit); // glBindTexture(GL_TEXTURE_2D, material->GetDiffuseTexture()->GetID());
// glBindTexture(GL_TEXTURE_2D, material->GetMetallicTexture()->GetID()); // shader.setInt("albedoTex", texUnit++);
shader.setInt("metallicTex", texUnit++); // } else {
} else { // shader.setBool("useAlbedoMap", false);
shader.setBool("useMetallicMap", false); // }
}
// Roughness texture // // Metallic texture
// if (material->HasRoughnessTexture()) { // // if (material->HasMetallicTexture()) {
if (false) { // if (false) {
shader.setBool("useRoughnessMap", true); // shader.setBool("useMetallicMap", true);
glActiveTexture(GL_TEXTURE0 + texUnit); // glActiveTexture(GL_TEXTURE0 + texUnit);
// glBindTexture(GL_TEXTURE_2D, material->GetRoughnessTexture()->GetID()); // // glBindTexture(GL_TEXTURE_2D, material->GetMetallicTexture()->GetID());
shader.setInt("roughnessTex", texUnit++); // shader.setInt("metallicTex", texUnit++);
} else { // } else {
shader.setBool("useRoughnessMap", false); // shader.setBool("useMetallicMap", false);
} // }
// AO texture // // Roughness texture
// if (material->HasAoTexture()) { // // if (material->HasRoughnessTexture()) {
if (false) { // if (false) {
shader.setBool("useAoMap", true); // shader.setBool("useRoughnessMap", true);
glActiveTexture(GL_TEXTURE0 + texUnit); // glActiveTexture(GL_TEXTURE0 + texUnit);
// glBindTexture(GL_TEXTURE_2D, material->GetAoTexture()->GetID()); // // glBindTexture(GL_TEXTURE_2D, material->GetRoughnessTexture()->GetID());
shader.setInt("aoTex", texUnit++); // shader.setInt("roughnessTex", texUnit++);
} else { // } else {
shader.setBool("useAoMap", false); // shader.setBool("useRoughnessMap", false);
} // }
// --- Render mesh --- // // AO texture
mesh.Render(count); // // if (material->HasAoTexture()) {
} // if (false) {
// shader.setBool("useAoMap", true);
// glActiveTexture(GL_TEXTURE0 + texUnit);
// // glBindTexture(GL_TEXTURE_2D, material->GetAoTexture()->GetID());
// shader.setInt("aoTex", texUnit++);
// } else {
// shader.setBool("useAoMap", false);
// }
// // --- Render mesh ---
// mesh.Render(count);
// }
} }
} }

View File

@ -1,13 +1,23 @@
#version 410 core #version 460 core
// Input vertex attributes // Input vertex attributes
layout (location = 0) in vec3 position; // Vertex position in local space (model space) layout (location = 0) in vec3 position; // Vertex position in local space (model space)
layout (location = 3) in mat4 instanceModel; // Vertex texture uv
// layout (std140, binding = 1) uniform Matrices
// {
// mat4 projection;
// mat4 view;
// };
// Uniforms for transformation matrices // Uniforms for transformation matrices
uniform mat4 u_model; // Model matrix: transforms from local space to world space uniform mat4 u_model; // Model matrix: transforms from local space to world space
uniform mat4 u_lightSpace; uniform mat4 u_lightSpace;
uniform bool u_isInstanced;
void main() void main()
{ {
gl_Position = u_lightSpace * u_model * vec4(position, 1.0); mat4 model = u_isInstanced ? instanceModel : u_model;
gl_Position = u_lightSpace * model * vec4(position, 1.0);
} }

View File

@ -6,6 +6,12 @@ layout (location = 1) in vec3 normal; // vertex normal
layout (location = 2) in vec2 texCoord; // Vertex texture uv layout (location = 2) in vec2 texCoord; // Vertex texture uv
layout (location = 3) in mat4 instanceModel; // Vertex texture uv layout (location = 3) in mat4 instanceModel; // Vertex texture uv
layout (std140, binding = 1) uniform Matrices
{
mat4 projection;
mat4 view;
};
// Output to fragment shader // Output to fragment shader
out vec3 vertexPos; out vec3 vertexPos;
out vec3 vertexNormal; out vec3 vertexNormal;
@ -14,8 +20,8 @@ out vec4 fragPosLightSpace;
// Uniforms for transformation matrices // Uniforms for transformation matrices
uniform mat4 u_model; // Model matrix: transforms from local space to world space uniform mat4 u_model; // Model matrix: transforms from local space to world space
uniform mat4 u_view; // View matrix: transforms from world space to camera space (view space) // uniform mat4 u_view;
uniform mat4 u_projection; // Projection matrix: transforms from camera space to clip space // uniform mat4 u_projection;
uniform bool u_isInstanced; uniform bool u_isInstanced;
void main() void main()
@ -32,5 +38,5 @@ void main()
// fragPosLightSpace = u_lightSpace * vec4(vertexPos, 1.0); // fragPosLightSpace = u_lightSpace * vec4(vertexPos, 1.0);
gl_Position = u_projection * u_view * vec4(vertexPos, 1.0); gl_Position = projection * view * vec4(vertexPos, 1.0);
} }

3685
out.txt Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,13 +1,21 @@
set(SANDBOX_TARGET sandbox) set(SANDBOX_TARGET sandbox)
set(MODEL_TARGET model)
add_executable(${SANDBOX_TARGET} src/main.cpp) add_executable(${SANDBOX_TARGET} src/main.cpp)
add_executable(${MODEL_TARGET} src/model.cpp)
set_target_properties(${SANDBOX_TARGET} PROPERTIES set_target_properties(${SANDBOX_TARGET} PROPERTIES
CXX_STANDARD 17 CXX_STANDARD 17
CXX_STANDARD_REQUIRED ON CXX_STANDARD_REQUIRED ON
) )
set_target_properties(${MODEL_TARGET} PROPERTIES
CXX_STANDARD 17
CXX_STANDARD_REQUIRED ON
)
target_link_libraries(${SANDBOX_TARGET} PRIVATE ${ENGINE_TARGET}) target_link_libraries(${SANDBOX_TARGET} PRIVATE ${ENGINE_TARGET})
target_link_libraries(${MODEL_TARGET} PRIVATE ${ENGINE_TARGET})
# --- Copy engine.dll and all dependent DLLs next to sandbox.exe --- # --- Copy engine.dll and all dependent DLLs next to sandbox.exe ---
if (WIN32) if (WIN32)
@ -16,4 +24,10 @@ if (WIN32)
$<TARGET_RUNTIME_DLLS:${SANDBOX_TARGET}> $<TARGET_FILE_DIR:${SANDBOX_TARGET}> $<TARGET_RUNTIME_DLLS:${SANDBOX_TARGET}> $<TARGET_FILE_DIR:${SANDBOX_TARGET}>
COMMAND_EXPAND_LISTS COMMAND_EXPAND_LISTS
) )
add_custom_command(TARGET ${MODEL_TARGET} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
$<TARGET_RUNTIME_DLLS:${MODEL_TARGET}> $<TARGET_FILE_DIR:${MODEL_TARGET}>
COMMAND_EXPAND_LISTS
)
endif() endif()

View File

@ -17,9 +17,11 @@
#include "engine/components/camera.h" #include "engine/components/camera.h"
#include "engine/components/mesh.h" #include "engine/components/mesh.h"
#include "engine/components/rotate.h" #include "engine/components/rotate.h"
#include "engine/components/batch.h"
#include "engine/3d/prefab.hpp"
#include "engine/scene/scene.h" #include "engine/scene/scene.h"
#include "engine/input/input.h"
#include "engine/api.h" #include "engine/api.h"
@ -35,78 +37,65 @@ public:
Object* lightObj = Object::LoadFile("./assets/common/sphere/sphere.obj"); Object* lightObj = Object::LoadFile("./assets/common/sphere/sphere.obj");
lightEntity = scene->CreateEntity(); lightEntity = scene->CreateEntity();
lightEntity.AddComponent<transform>(glm::vec3(5.f, 5.f, 5.f), glm::vec3(0.f)); lightEntity.AddComponent<Transform>(glm::vec3(5.f, 5.f, 5.f), glm::vec3(0.f));
lightEntity.AddComponent<light>(light::LightType::DIRECTIONAL, glm::vec3(1.f, 1.f, 1.f), 1.5f); lightEntity.AddComponent<light>(light::LightType::DIRECTIONAL, glm::vec3(1.f, 1.f, 1.f), 1.5f);
lightEntity.AddComponent<mesh>(std::shared_ptr<Object>(lightObj)); lightEntity.AddComponent<mesh>(std::shared_ptr<Renderable>(lightObj));
assert(lightEntity.HasComponent<mesh>() && "light doesn't have any mesh!"); assert(lightEntity.HasComponent<mesh>() && "light doesn't have any mesh!");
cameraEntity = scene->CreateEntity(); cameraEntity = scene->CreateEntity();
cameraEntity.AddComponent<camera>(); cameraEntity.AddComponent<camera>();
cameraEntity.AddComponent<transform>(glm::vec3(0.f, 2.f, 2.f)); cameraEntity.AddComponent<Transform>(glm::vec3(0.f, 2.f, 2.f));
assert(cameraEntity.HasComponent<camera>() && "Camera doesn't have required 'camera' component"); assert(cameraEntity.HasComponent<camera>() && "Camera doesn't have required 'camera' component");
assert(cameraEntity.HasComponent<transform>() && "Camera doesn't have 'transform' component"); assert(cameraEntity.HasComponent<Transform>() && "Camera doesn't have 'transform' component");
Object* targetObj = Object::LoadFile("./assets/wizard/wizard.obj"); Object* targetObj = Object::LoadFile("./assets/wizard/wizard.obj");
modelEntity = scene->CreateEntity(); modelEntity = scene->CreateEntity();
modelEntity.AddComponent<transform>(glm::vec3(0.f, 0.0f, 0.f)); modelEntity.AddComponent<Transform>(glm::vec3(0.f, 0.0f, 0.f));
modelEntity.AddComponent<mesh>(std::shared_ptr<Object>(targetObj)); modelEntity.AddComponent<mesh>(std::shared_ptr<Renderable>(targetObj));
modelEntity.AddComponent<rotate>(); modelEntity.AddComponent<rotate>();
assert(modelEntity.HasComponent<mesh>() && "model doesn't have any mesh!"); assert(modelEntity.HasComponent<mesh>() && "model doesn't have any mesh!");
// Cube template (use shared object to avoid reloading 1000 times) // Cube template (use shared object to avoid reloading 1000 times)
std::shared_ptr<Object> cubeObj = std::shared_ptr<Object>(Object::LoadFile("./assets/grass_block/grass_block.obj")); auto cubeObj = Object::LoadFile("./assets/grass_block/grass_block.obj");
auto batchEntt = scene->CreateEntity(); auto batchEntt = scene->CreateEntity();
auto& cubeBatch = batchEntt.AddComponent<batch>(); auto& cubeBatch = batchEntt.AddComponent<Prefab>(std::move(*cubeObj));
// auto& cubeBatch = batchEntt.GetComponent<batch>(); // auto& cubeBatch = batchEntt.GetComponent<batch>();
batchEntt.AddComponent<mesh>(cubeObj); // batchEntt.AddComponent<mesh>(cubeObj);
assert(batchEntt.HasComponent<batch>() && "batch doesn't have any batch component!"); // assert(batchEntt.HasComponent<batch>() && "batch doesn't have any batch component!");
assert(batchEntt.HasComponent<mesh>() && "batch doesn't have any mesh component!"); // assert(batchEntt.HasComponent<mesh>() && "batch doesn't have any mesh component!");
// Generate 1000 random cubes // Generate 1000 random cubes
for (int i = 0; i < 1000; ++i) { for (int i = 0; i < 100; ++i) {
auto cubeEntity = scene->CreateEntity(); auto cubeEntity = scene->CreateEntity();
float x = static_cast<float>(rand()) / RAND_MAX * 200.f - 100.f; // range [-100, 100] float x = static_cast<float>(rand()) / RAND_MAX * 200.f - 100.f; // range [-100, 100]
float y = static_cast<float>(rand()) / RAND_MAX * 10.f; // range [0, 10] float y = static_cast<float>(rand()) / RAND_MAX * 10.f; // range [0, 10]
float z = static_cast<float>(rand()) / RAND_MAX * 200.f - 100.f; // range [-100, 100] float z = static_cast<float>(rand()) / RAND_MAX * 200.f - 100.f; // range [-100, 100]
cubeEntity.AddComponent<transform>(glm::vec3(x, y, z)); cubeEntity.AddComponent<Transform>(glm::vec3(x, y, z));
cubeEntity.AddComponent<rotate>(); cubeEntity.AddComponent<rotate>();
cubeEntity.AddComponent<batch::item>(cubeBatch.id()); cubeEntity.AddComponent<batch::item>(cubeBatch.GetID());
} }
Object* floorObj = Object::LoadFile("./assets/common/plane/plane.obj"); Object* floorObj = Object::LoadFile("./assets/common/plane/plane.obj");
auto floorEntt = scene->CreateEntity(); auto floorEntt = scene->CreateEntity();
floorEntt.AddComponent<transform>(glm::vec3(0.f)); floorEntt.AddComponent<Transform>(glm::vec3(0.f));
floorEntt.AddComponent<mesh>(std::shared_ptr<Object>(floorObj)); floorEntt.AddComponent<mesh>(std::shared_ptr<Object>(floorObj));
assert(floorEntt.HasComponent<mesh>() && "floor doesn't have any mesh component!"); assert(floorEntt.HasComponent<mesh>() && "floor doesn't have any mesh component!");
std::cout << "Game initialized" << std::endl; std::cout << "Game initialized" << std::endl;
m_angle = 3.45f; m_angle = 3.45f;
m_lastTicks = SDL_GetTicks();
m_paused = false;
m_yaw = -90.0f; // looking along -Z initially m_yaw = -90.0f; // looking along -Z initially
m_pitch = 0.0f; // no vertical tilt m_pitch = 0.0f; // no vertical tilt
// FPS tracking
m_startTicks = SDL_GetTicks();
m_frameCount = 0;
} }
void OnUpdate() override { void OnUpdate(Timestep dt) override {
m_currentTicks = SDL_GetTicks(); glm::vec2 mouseRel = Input::GetRelativeMouse();
float deltaTime = static_cast<float>(m_currentTicks - m_lastTicks) / 1000.0f; // seconds
m_lastTicks = m_currentTicks;
float mouseXRel, mouseYRel;
SDL_GetRelativeMouseState(&mouseXRel, &mouseYRel);
float sensitivity = 0.1f; // tweak as needed float sensitivity = 0.1f; // tweak as needed
m_yaw += mouseXRel * sensitivity; m_yaw += mouseRel.x * sensitivity;
m_pitch -= mouseYRel * sensitivity; // invert Y for typical FPS control m_pitch -= mouseRel.y * sensitivity; // invert Y for typical FPS control
// clamp pitch to avoid flipping // clamp pitch to avoid flipping
// if (pitch > 89.0f) pitch = 89.0f; // if (pitch > 89.0f) pitch = 89.0f;
@ -122,34 +111,28 @@ public:
glm::vec3 velocity(0.f); glm::vec3 velocity(0.f);
const bool* state = SDL_GetKeyboardState(nullptr);
if (state[SDL_SCANCODE_P]) m_paused = !m_paused;
glm::vec3 front = glm::normalize(glm::vec3(cameraViewDirection.x, 0.f, cameraViewDirection.z)); glm::vec3 front = glm::normalize(glm::vec3(cameraViewDirection.x, 0.f, cameraViewDirection.z));
glm::vec3 right = glm::normalize(glm::cross(front, glm::vec3(0.f, 1.f, 0.f))); glm::vec3 right = glm::normalize(glm::cross(front, glm::vec3(0.f, 1.f, 0.f)));
if (state[SDL_SCANCODE_W]) velocity += front; if (Input::IsKeyPressed(SDL_SCANCODE_W)) velocity += front;
if (state[SDL_SCANCODE_S]) velocity -= front; if (Input::IsKeyPressed(SDL_SCANCODE_S)) velocity -= front;
if (state[SDL_SCANCODE_A]) velocity -= right; if (Input::IsKeyPressed(SDL_SCANCODE_A)) velocity -= right;
if (state[SDL_SCANCODE_D]) velocity += right; if (Input::IsKeyPressed(SDL_SCANCODE_D)) velocity += right;
if (state[SDL_SCANCODE_SPACE]) velocity.y += 1.f; if (Input::IsKeyPressed(SDL_SCANCODE_SPACE)) velocity.y += 1.f;
if (state[SDL_SCANCODE_LSHIFT]) velocity.y -= 1.f; if (Input::IsKeyPressed(SDL_SCANCODE_LSHIFT)) velocity.y -= 1.f;
auto& camTransform = cameraEntity.GetComponent<transform>(); auto& camTransform = cameraEntity.GetComponent<Transform>();
camTransform.position += velocity * deltaTime * 2.5f; // speed is e.g. 2.5f camTransform.position += velocity * (float)dt * 2.5f; // speed is e.g. 2.5f
camTransform.rotation = cameraViewDirection; camTransform.rotation = cameraViewDirection;
// update rotation // update rotation
if (!m_paused) { m_angle += glm::radians(45.0f) * dt; // 72° per second
m_angle += glm::radians(45.0f) * deltaTime; // 72° per second if (m_angle > glm::two_pi<float>()) {
if (m_angle > glm::two_pi<float>()) { m_angle -= glm::two_pi<float>(); // keep value small
m_angle -= glm::two_pi<float>(); // keep value small
}
} }
// ---- Day-night simulation ---- // ---- Day-night simulation ----
m_dayTime += deltaTime; m_dayTime += dt;
if (m_dayTime > m_dayLength) if (m_dayTime > m_dayLength)
m_dayTime -= m_dayLength; // loop every "day" m_dayTime -= m_dayLength; // loop every "day"
@ -172,7 +155,7 @@ public:
// Update the directional light in the registry // Update the directional light in the registry
auto& l = lightEntity.GetComponent<light>(); auto& l = lightEntity.GetComponent<light>();
auto& t = lightEntity.GetComponent<transform>(); auto& t = lightEntity.GetComponent<Transform>();
if (l.type == light::LightType::DIRECTIONAL) { if (l.type == light::LightType::DIRECTIONAL) {
// "position" for directional light often stores direction vector // "position" for directional light often stores direction vector
// If your system instead uses transform.rotation, adjust accordingly // If your system instead uses transform.rotation, adjust accordingly
@ -181,23 +164,12 @@ public:
l.intensity = intensity; l.intensity = intensity;
} }
// auto rotateEntts = m_scene->m_registry.view<transform, rotate>(); m_elapsed += dt.GetMilliseconds();
// for (auto [entity, t] : rotateEntts.each()) {
// // auto targetTransform = rotateEntts.get<transform>(entity);
// if (!m_scene->m_registry.all_of<light>(entity)) {
// t.rotation.y = m_angle;
// }
// }
m_frameCount++; if (m_elapsed >= 1000) { // one second passed
m_currentTicks = SDL_GetTicks(); m_elapsed = 0;
Uint64 elapsed = m_currentTicks - m_startTicks; double fps = 1 / dt;
if (elapsed >= 1000) { // one second passed
double fps = static_cast<double>(m_frameCount) / (static_cast<double>(elapsed) / 1000.0);
std::cout << "FPS: " << fps << std::endl; std::cout << "FPS: " << fps << std::endl;
m_frameCount = 0;
m_startTicks = m_currentTicks;
} }
} }
@ -211,6 +183,9 @@ public:
} }
} }
private: private:
// for internal 1-second timer
int m_elapsed;
std::shared_ptr<Scene> m_scene; std::shared_ptr<Scene> m_scene;
Entity lightEntity; Entity lightEntity;
@ -218,21 +193,12 @@ private:
Entity modelEntity; Entity modelEntity;
float m_angle; float m_angle;
Uint64 m_lastTicks;
float m_dayTime = 0.0f; // accumulates time for day-night cycle float m_dayTime = 0.0f; // accumulates time for day-night cycle
float m_dayLength = 60.0f; // seconds per full day cycle float m_dayLength = 60.0f; // seconds per full day cycle
bool m_paused = false;
float m_yaw = -90.0f; // looking along -Z initially float m_yaw = -90.0f; // looking along -Z initially
float m_pitch = 0.0f; // no vertical tilt float m_pitch = 0.0f; // no vertical tilt
// FPS tracking
Uint64 m_startTicks;
int m_frameCount;
Uint64 m_currentTicks;
}; };
IApplication* CreateApplication() { IApplication* CreateApplication() {

92
sandbox/src/model.cpp Normal file
View File

@ -0,0 +1,92 @@
#include <iostream>
#include <glm/glm.hpp>
#include <glm/gtc/constants.hpp>
#ifdef _WIN32
#define GLM_ENABLE_EXPERIMENTAL
#include <glm/gtx/extended_min_max.hpp>
#endif
#include "engine/renderer/wavefront.h"
#include "engine/app/app.h"
#include "engine/components/transform.h"
#include "engine/components/light.h"
#include "engine/components/camera.h"
#include "engine/components/mesh.h"
#include "engine/components/rotate.h"
#include "engine/3d/prefab.hpp"
#include "engine/scene/scene.h"
#include "engine/input/input.h"
#include "engine/api.h"
using namespace Core;
class ModelViewer : public IApplication {
public:
ModelViewer() = default;
~ModelViewer() override {}
void OnInit(std::shared_ptr<Scene> scene) override {
m_scene = scene;
Object* lightObj = Object::LoadFile("./assets/common/sphere/sphere.obj");
lightEntity = scene->CreateEntity();
lightEntity.AddComponent<Transform>(glm::vec3(5.f, 5.f, 5.f), glm::vec3(0.f));
lightEntity.AddComponent<light>(light::LightType::DIRECTIONAL, glm::vec3(1.f, 1.f, 1.f), 1.5f);
lightEntity.AddComponent<mesh>(std::shared_ptr<Renderable>(lightObj));
assert(lightEntity.HasComponent<mesh>() && "light doesn't have any mesh!");
cameraEntity = scene->CreateEntity();
cameraEntity.AddComponent<camera>();
cameraEntity.AddComponent<Transform>(glm::vec3(0.f, 2.f, 2.f));
assert(cameraEntity.HasComponent<camera>() && "Camera doesn't have required 'camera' component");
assert(cameraEntity.HasComponent<Transform>() && "Camera doesn't have 'transform' component");
Object* targetObj = Object::LoadFile("./assets/grass_block/grass_block.obj");
modelEntity = scene->CreateEntity();
modelEntity.AddComponent<Transform>(glm::vec3(0.f, 0.0f, 0.f));
modelEntity.AddComponent<mesh>(std::shared_ptr<Renderable>(targetObj));
assert(modelEntity.HasComponent<mesh>() && "model doesn't have any mesh!");
std::cout << "ModelViewer initialized" << std::endl;
}
void OnUpdate(Timestep dt) override {
m_elapsed += dt.GetMilliseconds();
if (m_elapsed >= 1000) { // one second passed
m_elapsed = 0;
double fps = 1 / dt;
std::cout << "FPS: " << fps << std::endl;
}
}
void OnEvent(const Event& event) override {
if (event.GetType() == EventType::WINDOW_RESIZE) {
auto resizeEvent = static_cast<const WindowResizeEvent&>(event);
std::cout << "[DEBUG] <EVENT> Window resized to " << resizeEvent.GetWidth() << "x" << resizeEvent.GetHeight() << std::endl;
}
else if (event.GetType() == EventType::WINDOW_CLOSE) {
std::cout << "[DEBUG] <EVENT> Window closing" << std::endl;
}
}
private:
// for internal 1-second timer
int m_elapsed;
std::shared_ptr<Scene> m_scene;
Entity lightEntity;
Entity cameraEntity;
Entity modelEntity;
};
IApplication* CreateApplication() {
return new ModelViewer();
}