Compare commits
43 Commits
71f1b2c6d2
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 113412bb5b | |||
| c7b6a79270 | |||
| 54fa900dff | |||
| b6b40837a4 | |||
| d6267f7a4b | |||
| 71a14af25c | |||
| 339da4ef02 | |||
| 0c4b7ed285 | |||
| bdd5a16b3d | |||
| 116f274228 | |||
| 07108956b9 | |||
| eb4b3bc78f | |||
| 8434ee8633 | |||
| 2d10e22a99 | |||
| 103fdcaa26 | |||
| 9e3bc4790b | |||
| 4bc74d0d2c | |||
| 3cca0b5c87 | |||
| bb4a2c926e | |||
| 808fad3001 | |||
| 73da0d79f5 | |||
| 788a302d75 | |||
| e36a599d68 | |||
| 9591ad403b | |||
| 51ace4a800 | |||
| 577336b5b7 | |||
| 9b26cf909b | |||
| c6d83c1b51 | |||
| e459fea503 | |||
| b5ea21ad43 | |||
| ee8e6012c8 | |||
| 063b228b97 | |||
| 1440fd847c | |||
| 337da2b3b7 | |||
| 3574634c4c | |||
| 32873d14ae | |||
| ec7ef40aea | |||
| 7b9858cffa | |||
| ef498ba210 | |||
| a427fb7099 | |||
| 37d35f990d | |||
| 4798c52e51 | |||
| ea593feb8d |
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
@ -69,6 +69,7 @@
|
||||
"regex": "cpp",
|
||||
"semaphore": "cpp",
|
||||
"shared_mutex": "cpp",
|
||||
"stop_token": "cpp"
|
||||
"stop_token": "cpp",
|
||||
"filesystem": "cpp"
|
||||
}
|
||||
}
|
||||
@ -5,9 +5,12 @@ set(SOURCES
|
||||
src/IO/file_manager.cpp
|
||||
src/renderer/debug.cpp
|
||||
|
||||
src/window/window.cpp
|
||||
src/input/input.cpp
|
||||
|
||||
src/opengl/buffers.cpp
|
||||
|
||||
src/scene/scene.cpp
|
||||
src/window/window.cpp
|
||||
|
||||
src/components/batch.cpp
|
||||
src/renderer/mesh.cpp
|
||||
|
||||
122
engine/include/engine/3d/array.hpp
Normal file
122
engine/include/engine/3d/array.hpp
Normal 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_
|
||||
64
engine/include/engine/3d/material.hpp
Normal file
64
engine/include/engine/3d/material.hpp
Normal 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_
|
||||
222
engine/include/engine/3d/mesh.hpp
Normal file
222
engine/include/engine/3d/mesh.hpp
Normal 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
|
||||
148
engine/include/engine/3d/prefab.hpp
Normal file
148
engine/include/engine/3d/prefab.hpp
Normal 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_
|
||||
63
engine/include/engine/3d/vertex.hpp
Normal file
63
engine/include/engine/3d/vertex.hpp
Normal 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;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
@ -3,6 +3,8 @@
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace Core {
|
||||
|
||||
class FileManager
|
||||
{
|
||||
public:
|
||||
@ -12,4 +14,6 @@ public:
|
||||
static std::string read(const std::string &filename);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // FILE_MANAGER_H
|
||||
@ -1,6 +1,8 @@
|
||||
#ifndef PARSER_H_
|
||||
#define PARSER_H_
|
||||
|
||||
namespace Core {
|
||||
|
||||
// Very fast OBJ/MTL line parser
|
||||
class Parser {
|
||||
private:
|
||||
@ -17,4 +19,6 @@ public:
|
||||
int TakeIndex(int baseCount);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // PARSER_H_
|
||||
@ -9,10 +9,10 @@
|
||||
#include "engine/app/app.h"
|
||||
#include "engine/renderer/core.h"
|
||||
|
||||
extern IApplication* CreateApplication();
|
||||
extern Core::IApplication* CreateApplication();
|
||||
|
||||
int main() {
|
||||
auto engine = Engine::GetInstance();
|
||||
engine->Run(std::unique_ptr<IApplication>(CreateApplication()));
|
||||
auto engine = Core::Engine::GetInstance();
|
||||
engine->Run(std::unique_ptr<Core::IApplication>(CreateApplication()));
|
||||
return 0;
|
||||
}
|
||||
@ -3,17 +3,21 @@
|
||||
|
||||
#include "engine/scene/scene.h"
|
||||
#include "engine/window/event.hpp"
|
||||
#include "engine/time/timestep.h"
|
||||
|
||||
#include "engine/export.h"
|
||||
|
||||
class ENGINE_API IApplication {
|
||||
public:
|
||||
virtual ~IApplication() = default;
|
||||
namespace Core {
|
||||
class ENGINE_API IApplication {
|
||||
public:
|
||||
virtual ~IApplication() = default;
|
||||
|
||||
virtual void OnInit(std::shared_ptr<Scene> scene) {};
|
||||
virtual void OnUpdate() {};
|
||||
virtual void OnShutdown() {};
|
||||
virtual void OnInit(std::shared_ptr<Scene> scene) {};
|
||||
virtual void OnUpdate(Timestep dt) {};
|
||||
virtual void OnShutdown() {};
|
||||
|
||||
virtual void OnEvent(const Event& event) {};
|
||||
};
|
||||
virtual void OnEvent(const Event& event) {};
|
||||
};
|
||||
}
|
||||
|
||||
#endif // APPLICATION_H_
|
||||
@ -2,13 +2,17 @@
|
||||
#define COMPONENT_BATCH_H_
|
||||
|
||||
#include <glm/mat4x4.hpp>
|
||||
#include <memory>
|
||||
|
||||
#include "engine/opengl/buffers.h"
|
||||
|
||||
#include "engine/export.h"
|
||||
|
||||
namespace Core {
|
||||
// requires mesh component
|
||||
struct ENGINE_API batch {
|
||||
friend class Renderer;
|
||||
public:
|
||||
// requires transform component
|
||||
// requires Transform component
|
||||
struct item {
|
||||
unsigned int batchId;
|
||||
};
|
||||
@ -16,14 +20,20 @@ public:
|
||||
batch();
|
||||
|
||||
inline const unsigned int id() const { return m_id; }
|
||||
inline const bool Initialized() const { return m_instanceBuffer != nullptr; }
|
||||
protected:
|
||||
static unsigned int LastID;
|
||||
private:
|
||||
unsigned int m_id;
|
||||
unsigned int m_instance_vbo { 0 };
|
||||
unsigned int m_instance_count { 0 };
|
||||
|
||||
// TODO: use static draw when possible
|
||||
OpenGL::InstanceBuffer* m_instanceBuffer = nullptr;
|
||||
private:
|
||||
friend class Renderer;
|
||||
void prepare(glm::mat4 *instances, unsigned int count);
|
||||
};
|
||||
}
|
||||
|
||||
#endif // COMPONENT_BATCH_H_
|
||||
@ -3,6 +3,8 @@
|
||||
|
||||
#include "engine/export.h"
|
||||
|
||||
namespace Core {
|
||||
struct ENGINE_API camera {};
|
||||
}
|
||||
|
||||
#endif // COMPONENTS_PLAYER_H_
|
||||
@ -6,6 +6,7 @@
|
||||
#include "engine/renderer/renderer.h"
|
||||
#include "engine/export.h"
|
||||
|
||||
namespace Core {
|
||||
struct ENGINE_API light {
|
||||
friend class Renderer;
|
||||
public:
|
||||
@ -25,5 +26,6 @@ private:
|
||||
glm::mat4 lightSpace;
|
||||
int shadowRes{1024};
|
||||
};
|
||||
}
|
||||
|
||||
#endif // COMPONENTS_LIGHT_H_
|
||||
@ -2,12 +2,14 @@
|
||||
#define COMPONENTS_MESH_H_
|
||||
|
||||
#include <memory>
|
||||
#include "engine/renderer/renderable.hpp"
|
||||
|
||||
#include "engine/renderer/wavefront.h"
|
||||
#include "engine/export.h"
|
||||
|
||||
namespace Core {
|
||||
struct ENGINE_API mesh {
|
||||
std::shared_ptr<Object> object;
|
||||
std::shared_ptr<Renderable> mesh;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // COMPONENTS_MESH_H_
|
||||
@ -3,6 +3,8 @@
|
||||
|
||||
#include "engine/export.h"
|
||||
|
||||
namespace Core {
|
||||
struct ENGINE_API rotate {};
|
||||
}
|
||||
|
||||
#endif // COMPONENT_ROTATE_H_
|
||||
@ -4,10 +4,14 @@
|
||||
#include <glm/glm.hpp>
|
||||
#include "engine/export.h"
|
||||
|
||||
struct ENGINE_API transform {
|
||||
namespace Core {
|
||||
|
||||
struct ENGINE_API Transform {
|
||||
glm::vec3 position;
|
||||
glm::vec3 rotation;
|
||||
glm::vec3 scale;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // COMPONENTS_TRANSFORM_H_
|
||||
29
engine/include/engine/input/input.h
Normal file
29
engine/include/engine/input/input.h
Normal 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_
|
||||
93
engine/include/engine/opengl/buffers.h
Normal file
93
engine/include/engine/opengl/buffers.h
Normal 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_
|
||||
@ -1,17 +0,0 @@
|
||||
#ifndef RENDERER_BASICS_H
|
||||
#define RENDERER_BASICS_H
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
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
|
||||
@ -13,6 +13,8 @@
|
||||
#include "engine/app/app.h"
|
||||
#include "engine/export.h"
|
||||
|
||||
namespace Core {
|
||||
|
||||
class ENGINE_API Engine : public EventHandler {
|
||||
public:
|
||||
static Engine* GetInstance();
|
||||
@ -28,8 +30,10 @@ private:
|
||||
std::shared_ptr<Window> m_window;
|
||||
std::unique_ptr<Renderer> m_renderer;
|
||||
std::shared_ptr<Scene> m_scene;
|
||||
uint64_t m_elapsed;
|
||||
bool m_running;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // ENGINE_H_
|
||||
@ -3,6 +3,8 @@
|
||||
|
||||
#include <GL/glew.h>
|
||||
|
||||
namespace Core {
|
||||
|
||||
void MessageCallback(GLenum source,
|
||||
GLenum type,
|
||||
GLuint id,
|
||||
@ -11,4 +13,6 @@ void MessageCallback(GLenum source,
|
||||
const GLchar* message,
|
||||
const void* userParam);
|
||||
|
||||
}
|
||||
|
||||
#endif // RENDERER_DEBUG_
|
||||
@ -6,6 +6,8 @@
|
||||
|
||||
#include "engine/renderer/texture.h"
|
||||
|
||||
namespace Core {
|
||||
|
||||
class Material {
|
||||
private:
|
||||
glm::vec3 m_ambient { 0.2f, 0.2f, 0.2f };
|
||||
@ -39,4 +41,6 @@ public:
|
||||
inline void SetIllumination(float illum) { m_illum = illum; }
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // MATERIAL_H_
|
||||
@ -5,23 +5,31 @@
|
||||
#include <string>
|
||||
#include <GL/glew.h>
|
||||
|
||||
#include "engine/renderer/basics.h"
|
||||
#include "engine/3d/vertex.hpp"
|
||||
#include "engine/opengl/buffers.h"
|
||||
|
||||
class Mesh {
|
||||
namespace Core {
|
||||
|
||||
class Mesh : public OpenGL::VertexArray {
|
||||
public: // TODO: abstract away
|
||||
unsigned int m_vao, m_vbo, m_ebo;
|
||||
unsigned int m_ebo;
|
||||
std::vector<Vertex> m_vertexBuffer;
|
||||
std::vector<unsigned int> m_indexBuffer;
|
||||
public: // TODO: abstract away
|
||||
void Bind() const { glBindVertexArray(m_vao); }
|
||||
void Unbind() { glBindVertexArray(0); }
|
||||
void Upload() const;
|
||||
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:
|
||||
Mesh();
|
||||
inline const std::string& GetMaterialName() const { return m_materialName; }
|
||||
void Upload();
|
||||
public:
|
||||
void Render(unsigned int count);
|
||||
private:
|
||||
std::string m_materialName;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // MESH_H_
|
||||
26
engine/include/engine/renderer/renderable.hpp
Normal file
26
engine/include/engine/renderer/renderable.hpp
Normal 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_
|
||||
@ -8,6 +8,10 @@
|
||||
#include "engine/export.h"
|
||||
#include "engine/components/light.h"
|
||||
|
||||
#include "engine/opengl/buffers.h"
|
||||
|
||||
namespace Core {
|
||||
|
||||
// TODO: make static or singleton
|
||||
class ENGINE_API Renderer {
|
||||
public:
|
||||
@ -24,6 +28,8 @@ private:
|
||||
void GenerateShadowMaps();
|
||||
void EnsureShadowResources(light& l);
|
||||
private:
|
||||
OpenGL::UniformBuffer m_uniform_matrices;
|
||||
|
||||
Shader m_shader;
|
||||
Shader m_depthShader;
|
||||
|
||||
@ -37,4 +43,6 @@ private:
|
||||
glm::mat4 m_view;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // RENDERER_H_
|
||||
@ -7,17 +7,19 @@
|
||||
|
||||
#include "engine/export.h"
|
||||
|
||||
namespace Core {
|
||||
|
||||
class ENGINE_API Shader
|
||||
{
|
||||
public:
|
||||
Shader();
|
||||
~Shader();
|
||||
|
||||
unsigned int m_id;
|
||||
|
||||
void init(const std::string &vertexCode, const std::string &fragmentCode);
|
||||
void use();
|
||||
|
||||
const unsigned int GetID() const { return m_id; }
|
||||
|
||||
void setBool(const std::string &name, bool value) const;
|
||||
void setInt(const std::string &name, int value) const;
|
||||
void setFloat(const std::string &name, float value) const;
|
||||
@ -30,8 +32,8 @@ public:
|
||||
void setMat2(const std::string &name, const glm::mat2 &mat) const;
|
||||
void setMat3(const std::string &name, const glm::mat3 &mat) const;
|
||||
void setMat4(const std::string &name, const glm::mat4 &mat) const;
|
||||
|
||||
private:
|
||||
unsigned int m_id;
|
||||
unsigned int m_vertexId;
|
||||
unsigned int m_fragmentId;
|
||||
|
||||
@ -45,4 +47,6 @@ private:
|
||||
void checkLinkingError();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // SHADER_H
|
||||
@ -3,14 +3,18 @@
|
||||
#include <string>
|
||||
#include <memory>
|
||||
|
||||
namespace Core {
|
||||
|
||||
class Texture {
|
||||
public:
|
||||
Texture() : m_id(0) {}
|
||||
static std::unique_ptr<Texture> LoadFile(const std::string& filename);
|
||||
static Texture* LoadFile(const std::string& filename);
|
||||
public:
|
||||
[[nodiscard]] unsigned int GetID() const { return m_id; }
|
||||
private:
|
||||
unsigned int m_id;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // TEXTURE_H_
|
||||
|
||||
@ -9,48 +9,56 @@
|
||||
|
||||
#include "engine/renderer/shader.h"
|
||||
#include "engine/renderer/renderer.h"
|
||||
#include "engine/renderer/material.h"
|
||||
#include "engine/renderer/mesh.h"
|
||||
#include "engine/3d/mesh.hpp"
|
||||
#include "engine/opengl/buffers.h"
|
||||
|
||||
#include "engine/export.h"
|
||||
|
||||
namespace Core {
|
||||
|
||||
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 };
|
||||
|
||||
class ENGINE_API Object {
|
||||
class ENGINE_API Object : public MeshGroup {
|
||||
friend class Renderer;
|
||||
private:
|
||||
static inline int NormalizeIndex(int idx, int baseCount);
|
||||
|
||||
private:
|
||||
Object();
|
||||
|
||||
public:
|
||||
~Object() = default;
|
||||
|
||||
public:
|
||||
static Object* LoadFile(const std::string& filename);
|
||||
|
||||
private:
|
||||
void LoadMaterials(const std::filesystem::path& filename);
|
||||
private:
|
||||
void AddMaterial(std::string name, std::shared_ptr<Material> material);
|
||||
std::shared_ptr<Material> GetMaterial(std::string name);
|
||||
void LoadMTL(const std::filesystem::path& filename);
|
||||
|
||||
private:
|
||||
void CreateNewMesh();
|
||||
void CreateNewMesh(const Material& material);
|
||||
Mesh& GetLastMesh();
|
||||
void CreateNewMesh(const std::string& materialName);
|
||||
|
||||
void AddMaterial(MaterialID id, const Material& material);
|
||||
Material* GetMaterial(const MaterialID& id);
|
||||
|
||||
public:
|
||||
void Render(Shader& shader, unsigned int count);
|
||||
[[nodiscard]] inline const std::string Name() const { return m_name; }
|
||||
|
||||
protected:
|
||||
void EnableBatch(unsigned int instanceVBO);
|
||||
void EnableBatch(const OpenGL::InstanceBuffer* instanceBuffer);
|
||||
|
||||
private:
|
||||
std::string m_name;
|
||||
std::vector<glm::vec3> m_vertices;
|
||||
std::vector<glm::vec3> m_normals;
|
||||
std::vector<glm::vec2> m_texCoords;
|
||||
|
||||
std::vector<Mesh> m_meshes;
|
||||
|
||||
std::unordered_map<std::string, std::shared_ptr<Material>> m_materials;
|
||||
std::unordered_map<MaterialID, Material> m_materials;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // MODEL_H_
|
||||
@ -1,15 +1,65 @@
|
||||
#ifndef ENGINE_SCENE_H_
|
||||
#define ENGINE_SCENE_H_
|
||||
#pragma once
|
||||
|
||||
#include <entt/entt.hpp>
|
||||
#include "engine/export.h"
|
||||
|
||||
class Scene {
|
||||
namespace Core {
|
||||
|
||||
class Entity;
|
||||
class Renderer;
|
||||
class Prefab;
|
||||
|
||||
class ENGINE_API Scene {
|
||||
public:
|
||||
Scene();
|
||||
Scene() = default;
|
||||
|
||||
Entity CreateEntity();
|
||||
private:
|
||||
entt::registry m_registry;
|
||||
friend class Renderer;
|
||||
friend class Game;
|
||||
friend class Core::Renderer;
|
||||
friend class Core::Entity;
|
||||
friend class Core::Prefab;
|
||||
};
|
||||
|
||||
#endif // ENGINE_SCENE_H_
|
||||
class ENGINE_API Entity {
|
||||
public:
|
||||
Entity() = default;
|
||||
Entity(entt::entity entity, Scene* scene) : m_entity(entity), m_scene(scene) {}
|
||||
Entity(const Entity& other) = default;
|
||||
|
||||
template<typename Type, typename... Args>
|
||||
decltype(auto) AddComponent(Args&&... args) {
|
||||
assert(m_entity != entt::null && "Entity is empty");
|
||||
assert(m_scene && "Scene has not been assigned to the entity");
|
||||
|
||||
if constexpr (std::is_void_v<decltype(m_scene->m_registry.emplace<Type>(m_entity, std::forward<Args>(args)...))>) {
|
||||
m_scene->m_registry.emplace<Type>(m_entity, std::forward<Args>(args)...);
|
||||
} else {
|
||||
return m_scene->m_registry.emplace<Type>(m_entity, std::forward<Args>(args)...);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Type>
|
||||
[[nodiscard]] Type& GetComponent() {
|
||||
assert(m_entity != entt::null && "Entity is empty");
|
||||
assert(m_scene && "Scene has not been assigned to the entity");
|
||||
return m_scene->m_registry.get<Type>(m_entity);
|
||||
}
|
||||
|
||||
template<typename Type>
|
||||
[[nodiscard]] const Type& GetComponent() const {
|
||||
assert(m_entity != entt::null && "Entity is empty");
|
||||
assert(m_scene && "Scene has not been assigned to the entity");
|
||||
return m_scene->m_registry.get<Type>(m_entity);
|
||||
}
|
||||
|
||||
template<typename Type>
|
||||
[[nodiscard]] bool HasComponent() const {
|
||||
return m_scene->m_registry.all_of<Type>(m_entity);
|
||||
}
|
||||
private:
|
||||
entt::entity m_entity { entt::null };
|
||||
Scene *m_scene = nullptr;
|
||||
};
|
||||
|
||||
} // namespace Core
|
||||
|
||||
22
engine/include/engine/time/timestep.h
Normal file
22
engine/include/engine/time/timestep.h
Normal 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_
|
||||
@ -7,6 +7,8 @@
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
namespace Core {
|
||||
|
||||
enum class EventType {
|
||||
WINDOW_RESIZE,
|
||||
WINDOW_CLOSE,
|
||||
@ -47,13 +49,13 @@ public:
|
||||
|
||||
EventEmitter() = default;
|
||||
|
||||
Handle Subscribe2(EventHandler* handler) {
|
||||
Handle Subscribe(EventHandler* handler) {
|
||||
auto slot = Slot{ m_next_id++, handler };
|
||||
m_subs.push_back(slot);
|
||||
return Handle{ slot.id };
|
||||
}
|
||||
|
||||
void Unsubscribe2(const Handle& h) {
|
||||
void UnSubscribe(const Handle& h) {
|
||||
m_subs.erase(std::remove_if(m_subs.begin(), m_subs.end(),
|
||||
[&](const Slot& s){ return s.id == h.id; }),
|
||||
m_subs.end());
|
||||
@ -71,4 +73,6 @@ private:
|
||||
std::size_t m_next_id = 1;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // EVENT_H_
|
||||
@ -3,6 +3,8 @@
|
||||
|
||||
#include "engine/window/event.hpp"
|
||||
|
||||
namespace Core {
|
||||
|
||||
class WindowEvent : public Event {
|
||||
public:
|
||||
WindowEvent() : Event(Event::EventCategory::WINDOW) {}
|
||||
@ -27,4 +29,6 @@ public:
|
||||
WindowCloseEvent() {}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // WINDOW_EVENTS_H_
|
||||
@ -13,6 +13,8 @@
|
||||
#define DEFAULT_WIDTH 1024
|
||||
#define DEFAULT_HEIGHT 768
|
||||
|
||||
namespace Core {
|
||||
|
||||
class Window : public EventEmitter {
|
||||
friend class Engine;
|
||||
private:
|
||||
@ -48,4 +50,6 @@ private:
|
||||
int m_height;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif //WINDOW_H_
|
||||
@ -4,6 +4,8 @@
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
|
||||
namespace Core {
|
||||
|
||||
FileManager::FileManager()
|
||||
{
|
||||
}
|
||||
@ -31,3 +33,5 @@ std::string FileManager::read(const std::string &filename)
|
||||
|
||||
return fileStream.str();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -4,6 +4,8 @@
|
||||
|
||||
#include "engine/IO/parser.h"
|
||||
|
||||
namespace Core {
|
||||
|
||||
// Skip whitespace
|
||||
void Parser::SkipSpaces() {
|
||||
while (*m_sv == ' ' || *m_sv == '\t') ++m_sv;
|
||||
@ -121,3 +123,5 @@ bool Parser::TakeFaceIndices(int &vi, int &ti, int &ni) {
|
||||
// Do NOT mutate indices (leave them raw). Let NormalizeIndex handle conversion.
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -2,6 +2,8 @@
|
||||
|
||||
#include "engine/components/batch.h"
|
||||
|
||||
namespace Core {
|
||||
|
||||
unsigned int batch::LastID = 0;
|
||||
|
||||
batch::batch() {
|
||||
@ -9,22 +11,24 @@ batch::batch() {
|
||||
}
|
||||
|
||||
void batch::prepare(glm::mat4 *instances, unsigned int count) {
|
||||
if (m_instance_vbo == 0) {
|
||||
glGenBuffers(1, &m_instance_vbo);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, m_instance_vbo);
|
||||
// Allocate *once*, no data yet — just reserve space
|
||||
glBufferData(GL_ARRAY_BUFFER, sizeof(glm::mat4) * count, nullptr, GL_DYNAMIC_DRAW);
|
||||
if (!m_instanceBuffer) {
|
||||
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;
|
||||
} else if (count > m_instance_count) {
|
||||
// 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);
|
||||
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;
|
||||
} else {
|
||||
glBindBuffer(GL_ARRAY_BUFFER, m_instance_vbo);
|
||||
}
|
||||
|
||||
// Just update the data region — much cheaper
|
||||
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(glm::mat4) * count, instances);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
OpenGL::Buffer::SubData(m_instanceBuffer, instances, sizeof(glm::mat4) * count, 0);
|
||||
}
|
||||
|
||||
}
|
||||
24
engine/src/input/input.cpp
Normal file
24
engine/src/input/input.cpp
Normal 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];
|
||||
}
|
||||
|
||||
}
|
||||
119
engine/src/opengl/buffers.cpp
Normal file
119
engine/src/opengl/buffers.cpp
Normal 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
|
||||
@ -5,34 +5,38 @@
|
||||
#include "engine/window/event.hpp"
|
||||
#include "engine/renderer/wavefront.h"
|
||||
|
||||
#include "engine/time/timestep.h"
|
||||
#include "engine/input/input.h"
|
||||
|
||||
namespace Core {
|
||||
|
||||
Engine* Engine::s_instance = nullptr;
|
||||
|
||||
void Engine::Run(std::unique_ptr<IApplication> app) {
|
||||
m_scene = std::make_shared<Scene>();
|
||||
m_renderer = std::make_unique<Renderer>(m_scene);
|
||||
m_window = Window::GetInstance();
|
||||
m_renderer = std::make_unique<Renderer>(m_scene);
|
||||
m_app = std::move(app);
|
||||
m_running = true;
|
||||
|
||||
m_app->OnInit(m_scene);
|
||||
m_renderer->Init();
|
||||
|
||||
// m_window->Subscribe<WindowCloseEvent>([&](const WindowCloseEvent& e) {
|
||||
m_window->Subscribe(this);
|
||||
|
||||
// m_app->OnEvent(e);
|
||||
// });
|
||||
|
||||
// m_window->Subscribe<WindowResizeEvent>([&](const WindowResizeEvent& e) {
|
||||
// m_renderer->OnWindowResized(e.GetWidth(), e.GetHeight());
|
||||
// m_app->OnEvent(e);
|
||||
// });
|
||||
|
||||
m_window->Subscribe2(this);
|
||||
uint64_t now = SDL_GetPerformanceCounter();
|
||||
m_elapsed = 0;
|
||||
|
||||
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_app->OnUpdate();
|
||||
Input::Update(dt);
|
||||
m_app->OnUpdate(dt);
|
||||
|
||||
m_renderer->Render();
|
||||
|
||||
@ -49,7 +53,7 @@ void Engine::OnEvent(const Event& event) {
|
||||
m_app->OnEvent(event);
|
||||
if (event.GetCategory() == Event::EventCategory::WINDOW) {
|
||||
if (event.GetType() == EventType::WINDOW_RESIZE) {
|
||||
auto e = static_cast<const WindowResizeEvent&>(event);
|
||||
const auto e = dynamic_cast<const WindowResizeEvent&>(event);
|
||||
m_renderer->OnWindowResized(e.GetWidth(), e.GetHeight());
|
||||
}
|
||||
if (event.GetType() == EventType::WINDOW_CLOSE) {
|
||||
@ -66,3 +70,4 @@ Engine* Engine::GetInstance() {
|
||||
return s_instance;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -2,6 +2,8 @@
|
||||
|
||||
#include <iostream>
|
||||
|
||||
namespace Core {
|
||||
|
||||
void MessageCallback(GLenum source,
|
||||
GLenum type,
|
||||
GLuint id,
|
||||
@ -59,3 +61,5 @@ void MessageCallback(GLenum source,
|
||||
// (type == GL_DEBUG_TYPE_ERROR ? "** GL ERROR **" : ""),
|
||||
// type, severity, message);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,51 +1,56 @@
|
||||
#include <iostream>
|
||||
#include <cstddef>
|
||||
|
||||
#include "engine/renderer/mesh.h"
|
||||
|
||||
Mesh::Mesh() {
|
||||
m_vao = 0;
|
||||
m_vbo = 0;
|
||||
namespace Core {
|
||||
|
||||
Mesh::Mesh(const std::string& name) : m_materialName(name.c_str()) {
|
||||
std::cout << "Mesh init" << std::endl;
|
||||
|
||||
// m_vao = 0;
|
||||
// m_vbo = 0;
|
||||
m_ebo = 0;
|
||||
|
||||
glGenVertexArrays(1, &m_vao);
|
||||
glGenBuffers(1, &m_vbo);
|
||||
// glGenVertexArrays(1, &m_vao);
|
||||
// glGenBuffers(1, &m_vbo);
|
||||
glGenBuffers(1, &m_ebo);
|
||||
|
||||
glBindVertexArray(m_vao);
|
||||
Bind();
|
||||
|
||||
// VBO (vertex buffer)
|
||||
glBindBuffer(GL_ARRAY_BUFFER, m_vbo);
|
||||
glBufferData(GL_ARRAY_BUFFER, 0, nullptr, GL_DYNAMIC_DRAW);
|
||||
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, m_position)));
|
||||
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, m_normal)));
|
||||
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, m_texCoord)));
|
||||
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast<const void*>(offsetof(Vertex, uv)));
|
||||
glEnableVertexAttribArray(2);
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
glBindVertexArray(0);
|
||||
// TODO: delete after ebo moved in VertexArray
|
||||
// glBindBuffer(GL_DYNAMIC_DRAW, 0);
|
||||
Unbind();
|
||||
}
|
||||
|
||||
void Mesh::Upload() const {
|
||||
glBindVertexArray(m_vao);
|
||||
void Mesh::Upload() {
|
||||
Bind();
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, m_vbo);
|
||||
glBufferData(GL_ARRAY_BUFFER, m_vertexBuffer.size() * sizeof(Vertex), m_vertexBuffer.data(), GL_DYNAMIC_DRAW);
|
||||
VertexBufferData(m_vertexBuffer.size() * sizeof(Vertex), m_vertexBuffer.data());
|
||||
|
||||
// Upload indices
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_ebo);
|
||||
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)
|
||||
@ -58,3 +63,5 @@ void Mesh::Render(unsigned int count)
|
||||
}
|
||||
Unbind();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -8,6 +8,9 @@
|
||||
#define GLM_ENABLE_EXPERIMENTAL
|
||||
#include <glm/gtx/euler_angles.hpp>
|
||||
|
||||
#include <glm/gtx/string_cast.hpp>
|
||||
#include <glm/gtc/type_ptr.hpp>
|
||||
|
||||
#include "engine/renderer/renderer.h"
|
||||
#include "engine/window/window.h"
|
||||
#include "engine/IO/file_manager.h"
|
||||
@ -17,16 +20,13 @@
|
||||
#include "engine/components/light.h"
|
||||
#include "engine/components/mesh.h"
|
||||
#include "engine/components/batch.h"
|
||||
#include "engine/3d/prefab.hpp"
|
||||
|
||||
Renderer::Renderer(std::shared_ptr<Scene> scene) : m_scene(scene)
|
||||
namespace Core {
|
||||
|
||||
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(
|
||||
FileManager::read("./engine/src/shaders/main.vs"),
|
||||
FileManager::read("./engine/src/shaders/pbr.fs")
|
||||
@ -37,6 +37,19 @@ Renderer::Renderer(std::shared_ptr<Scene> scene) : m_scene(scene)
|
||||
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_shader.use();
|
||||
@ -45,6 +58,14 @@ Renderer::Renderer(std::shared_ptr<Scene> scene) : m_scene(scene)
|
||||
|
||||
void Renderer::Init() {
|
||||
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) {
|
||||
@ -54,6 +75,7 @@ void Renderer::OnWindowResized(int w, int h) {
|
||||
0.01f,
|
||||
100.0f
|
||||
);
|
||||
m_uniform_matrices.UpdateUniform<glm::mat4>(glm::value_ptr(m_proj), 0);
|
||||
}
|
||||
|
||||
void Renderer::ApplyLights(Shader &shader) {
|
||||
@ -63,7 +85,7 @@ void Renderer::ApplyLights(Shader &shader) {
|
||||
size_t lightIndex = 0;
|
||||
for (auto entity : lights) {
|
||||
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.setVec3("lights[" + std::to_string(lightIndex) + "].position", transf.position);
|
||||
@ -107,8 +129,8 @@ void Renderer::EnsureShadowResources(light& l) {
|
||||
void Renderer::UpdateView() {
|
||||
auto camView = m_scene->m_registry.view<camera>();
|
||||
auto camTransform = camView.size() > 0 ?
|
||||
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(0.f, 0.f, 0.f)};
|
||||
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)};
|
||||
|
||||
m_view = glm::lookAt(
|
||||
camTransform.position,
|
||||
@ -116,6 +138,8 @@ void Renderer::UpdateView() {
|
||||
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.setMat4("u_view", m_view);
|
||||
@ -123,46 +147,55 @@ void Renderer::UpdateView() {
|
||||
}
|
||||
|
||||
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()) {
|
||||
if (batches.find(item.batchId) == batches.end())
|
||||
batches.insert(std::make_pair(item.batchId, std::vector<entt::entity>()));
|
||||
// for (auto [entt, item] : m_scene->m_registry.view<batch::item>().each()) {
|
||||
// if (batches.find(item.batchId) == batches.end())
|
||||
// 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.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 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());
|
||||
for (auto [entity, prefab] : m_scene->m_registry.view<Prefab>().each()) {
|
||||
prefab.Render(shader, *m_scene.get(), 1);
|
||||
}
|
||||
shader.setBool("u_isInstanced", false);
|
||||
|
||||
for (auto [entity, transf, mesh] : m_scene->m_registry.view<transform, mesh>(entt::exclude<batch, batch::item>).each()) {
|
||||
if (mesh.object == nullptr) {
|
||||
// entt::exclude<batch, batch::item>
|
||||
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;
|
||||
return;
|
||||
}
|
||||
@ -181,7 +214,7 @@ void Renderer::RenderScene(Shader &shader) {
|
||||
|
||||
shader.setMat4("u_model", m_model);
|
||||
|
||||
mesh.object->Render(shader, 1);
|
||||
mesh.mesh->Render(shader, *m_scene.get(), 1);
|
||||
}
|
||||
}
|
||||
|
||||
@ -202,7 +235,7 @@ void Renderer::Render() {
|
||||
|
||||
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()) {
|
||||
// TODO: support other light types when ready
|
||||
@ -246,3 +279,5 @@ void Renderer::Render() {
|
||||
UpdateView();
|
||||
RenderScene(m_shader);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -2,6 +2,8 @@
|
||||
#include <GL/glew.h>
|
||||
#include "engine/renderer/shader.h"
|
||||
|
||||
namespace Core {
|
||||
|
||||
Shader::Shader()
|
||||
{
|
||||
}
|
||||
@ -133,3 +135,5 @@ void Shader::checkLinkingError()
|
||||
<< std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -7,8 +7,10 @@
|
||||
#define STB_IMAGE_IMPLEMENTATION
|
||||
#include "stb_image.h"
|
||||
|
||||
std::unique_ptr<Texture> Texture::LoadFile(const std::string& filename) {
|
||||
auto texture = std::make_unique<Texture>();
|
||||
namespace Core {
|
||||
|
||||
Texture* Texture::LoadFile(const std::string& filename) {
|
||||
auto texture = new Texture();
|
||||
|
||||
int w, h, c;
|
||||
unsigned char *data = stbi_load(filename.c_str(), &w, &h, &c, 4);
|
||||
@ -17,8 +19,8 @@ std::unique_ptr<Texture> Texture::LoadFile(const std::string& filename) {
|
||||
std::exit(1);
|
||||
}
|
||||
|
||||
glGenTextures(1, &texture.get()->m_id);
|
||||
glBindTexture(GL_TEXTURE_2D, texture.get()->m_id);
|
||||
glGenTextures(1, &texture->m_id);
|
||||
glBindTexture(GL_TEXTURE_2D, texture->m_id);
|
||||
|
||||
// TODO: configure properly
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
|
||||
@ -32,5 +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;
|
||||
stbi_image_free(data);
|
||||
|
||||
return std::move(texture);
|
||||
return texture;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -8,21 +8,9 @@
|
||||
#include "engine/renderer/wavefront.h"
|
||||
|
||||
#include "engine/IO/parser.h"
|
||||
#include "engine/renderer/mesh.h"
|
||||
#include "engine/3d/mesh.hpp"
|
||||
|
||||
#define DEFAULT_MATERIAL_NAME "default"
|
||||
|
||||
// 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;
|
||||
// }
|
||||
namespace Core {
|
||||
|
||||
inline ObjElement toElement(const char* s) {
|
||||
switch (s[0]) {
|
||||
@ -40,22 +28,6 @@ inline ObjElement toElement(const char* s) {
|
||||
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) {
|
||||
switch (s[0]) {
|
||||
case '#': return MtlElement::MHASH;
|
||||
@ -102,7 +74,11 @@ Object::Object() {
|
||||
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);
|
||||
if (!file.is_open()) {
|
||||
std::cerr << "Failed to open MTL file: " << filename << std::endl;
|
||||
@ -110,7 +86,8 @@ void Object::LoadMaterials(const std::filesystem::path& filename) {
|
||||
}
|
||||
|
||||
std::string currentMaterialName;
|
||||
std::shared_ptr<Material> currentMaterial;
|
||||
Material currentMaterial;
|
||||
bool hasCurrent = false;
|
||||
|
||||
char line[1024]; // buffer per line
|
||||
|
||||
@ -126,15 +103,15 @@ void Object::LoadMaterials(const std::filesystem::path& filename) {
|
||||
case MtlElement::NEWMTL:
|
||||
{
|
||||
// If a material was being built, commit it first
|
||||
if (currentMaterial) {
|
||||
AddMaterial(currentMaterialName, std::move(currentMaterial));
|
||||
currentMaterial = nullptr;
|
||||
if (hasCurrent) {
|
||||
AddMaterial(currentMaterialName, currentMaterial);
|
||||
}
|
||||
|
||||
char* materialName = p.TakeWord();
|
||||
if (materialName) {
|
||||
currentMaterialName = materialName;
|
||||
currentMaterial = std::make_shared<Material>();
|
||||
currentMaterial = Material(currentMaterialName);
|
||||
hasCurrent = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -142,7 +119,7 @@ void Object::LoadMaterials(const std::filesystem::path& filename) {
|
||||
case MtlElement::NS: // specular weight
|
||||
{
|
||||
float weight = p.TakeFloat();
|
||||
if (currentMaterial) currentMaterial->SetSpecularWeight(weight);
|
||||
if (hasCurrent) currentMaterial.SetSpecularWeight(weight);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -151,7 +128,7 @@ void Object::LoadMaterials(const std::filesystem::path& filename) {
|
||||
float r = p.TakeFloat();
|
||||
float g = 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;
|
||||
}
|
||||
|
||||
@ -160,7 +137,7 @@ void Object::LoadMaterials(const std::filesystem::path& filename) {
|
||||
float r = p.TakeFloat();
|
||||
float g = 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;
|
||||
}
|
||||
|
||||
@ -169,21 +146,21 @@ void Object::LoadMaterials(const std::filesystem::path& filename) {
|
||||
float r = p.TakeFloat();
|
||||
float g = 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;
|
||||
}
|
||||
|
||||
case MtlElement::D: // opacity
|
||||
{
|
||||
float d = p.TakeFloat();
|
||||
if (currentMaterial) currentMaterial->SetOpacity(d);
|
||||
if (hasCurrent) currentMaterial.SetOpacity(d);
|
||||
break;
|
||||
}
|
||||
|
||||
case MtlElement::ILLUM: // illumination model
|
||||
{
|
||||
int illum = p.TakeInt();
|
||||
if (currentMaterial) currentMaterial->SetIllumination(illum);
|
||||
if (hasCurrent) currentMaterial.SetIllumination(illum);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -191,7 +168,7 @@ void Object::LoadMaterials(const std::filesystem::path& filename) {
|
||||
{
|
||||
// take rest of line as texture path (can contain spaces)
|
||||
char* texPath = p.TakeUntil('\0');
|
||||
if (texPath && currentMaterial) {
|
||||
if (texPath && hasCurrent) {
|
||||
// trim trailing spaces
|
||||
size_t len = std::strlen(texPath);
|
||||
while (len > 0 && (texPath[len - 1] == ' ' || texPath[len - 1] == '\t'))
|
||||
@ -199,7 +176,7 @@ void Object::LoadMaterials(const std::filesystem::path& filename) {
|
||||
|
||||
std::filesystem::path texturePath = filename.parent_path() / texPath;
|
||||
|
||||
currentMaterial->SetDiffuseTexture(Texture::LoadFile(texturePath.string()));
|
||||
currentMaterial.SetDiffuseTexture(Texture::LoadFile(texturePath.string()));
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -207,13 +184,13 @@ void Object::LoadMaterials(const std::filesystem::path& filename) {
|
||||
case MtlElement::MAP_KA: // ambient texture map
|
||||
{
|
||||
char* texPath = p.TakeUntil('\0');
|
||||
if (texPath && currentMaterial) {
|
||||
if (texPath && hasCurrent) {
|
||||
size_t len = std::strlen(texPath);
|
||||
while (len > 0 && (texPath[len - 1] == ' ' || texPath[len - 1] == '\t'))
|
||||
texPath[--len] = '\0';
|
||||
|
||||
// optional: handle ambient texture
|
||||
// currentMaterial->SetAmbientTexture(Texture::LoadFile(texPath));
|
||||
// currentMaterial.SetAmbientTexture(Texture::LoadFile(texPath));
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -225,41 +202,36 @@ void Object::LoadMaterials(const std::filesystem::path& filename) {
|
||||
}
|
||||
|
||||
// Commit last material if pending
|
||||
if (currentMaterial) {
|
||||
AddMaterial(currentMaterialName, std::move(currentMaterial));
|
||||
if (hasCurrent) {
|
||||
AddMaterial(currentMaterialName, currentMaterial);
|
||||
}
|
||||
|
||||
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)));
|
||||
}
|
||||
|
||||
std::shared_ptr<Material> Object::GetMaterial(std::string name)
|
||||
{
|
||||
auto material = m_materials.find(name);
|
||||
auto material = m_materials.find(id);
|
||||
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;
|
||||
mesh.materialName = materialName;
|
||||
m_meshes.push_back(mesh);
|
||||
EmplaceBack(material);
|
||||
}
|
||||
|
||||
void Object::CreateNewMesh()
|
||||
{
|
||||
EmplaceBack();
|
||||
}
|
||||
|
||||
Mesh& Object::GetLastMesh()
|
||||
{
|
||||
if (m_meshes.empty()) {
|
||||
auto material = std::make_shared<Material>();
|
||||
material->SetAmbientColor(glm::vec3(0.52f, 0.52f, 0.52f));
|
||||
AddMaterial(DEFAULT_MATERIAL_NAME, std::move(material));
|
||||
CreateNewMesh(DEFAULT_MATERIAL_NAME);
|
||||
if (Empty()) {
|
||||
CreateNewMesh(Material::Default());
|
||||
}
|
||||
return m_meshes.back();
|
||||
return Back();
|
||||
}
|
||||
|
||||
Object* Object::LoadFile(const std::string& filename) {
|
||||
@ -287,7 +259,7 @@ Object* Object::LoadFile(const std::string& filename) {
|
||||
if (mtlFile) {
|
||||
std::filesystem::path fullPath = filename;
|
||||
std::filesystem::path mtlPath = fullPath.replace_filename(mtlFile);
|
||||
obj->LoadMaterials(mtlPath);
|
||||
obj->LoadMTL(mtlPath);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -296,11 +268,16 @@ Object* Object::LoadFile(const std::string& filename) {
|
||||
{
|
||||
char* materialName = p.TakeWord();
|
||||
if (materialName) {
|
||||
auto& mesh = obj->GetLastMesh();
|
||||
if (mesh.materialName != materialName) {
|
||||
Mesh newMesh;
|
||||
newMesh.materialName = materialName;
|
||||
obj->m_meshes.push_back(newMesh);
|
||||
auto material = obj->GetMaterial(materialName);
|
||||
if (!material) {
|
||||
// Not defined material being used.
|
||||
std::cerr << "[WARN] WavefrontError: use of undefined material '"
|
||||
<< materialName << "'" << std::endl;
|
||||
material = new Material();
|
||||
}
|
||||
auto mesh = obj->FindMeshByMaterial(material);
|
||||
if (mesh == obj->End()) {
|
||||
obj->CreateNewMesh(*material);
|
||||
}
|
||||
}
|
||||
break;
|
||||
@ -324,6 +301,7 @@ Object* Object::LoadFile(const std::string& filename) {
|
||||
x /= w; y /= w; z /= w;
|
||||
}
|
||||
obj->m_vertices.emplace_back(x, y, z);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
@ -347,8 +325,11 @@ Object* Object::LoadFile(const std::string& filename) {
|
||||
case ObjElement::F: // face
|
||||
{
|
||||
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)) {
|
||||
// Convert raw OBJ indices to 0-based / -1 sentinel
|
||||
int vi = Object::NormalizeIndex(raw_vi, (int)obj->m_vertices.size());
|
||||
@ -360,15 +341,25 @@ Object* Object::LoadFile(const std::string& filename) {
|
||||
continue;
|
||||
}
|
||||
|
||||
glm::vec3 vert = obj->m_vertices[vi];
|
||||
glm::vec3 norm(0.0f);
|
||||
glm::vec2 texCoord(0.0f);
|
||||
Vertex v;
|
||||
v.position = obj->m_vertices[vi];
|
||||
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];
|
||||
if (ti >= 0) texCoord = obj->m_texCoords[ti];
|
||||
uint32_t idx = mesh.PushVertex(v);
|
||||
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);
|
||||
mesh.m_indexBuffer.push_back(mesh.m_vertexBuffer.size() - 1);
|
||||
// [0, 1, 2]
|
||||
// ^
|
||||
|
||||
// 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;
|
||||
}
|
||||
@ -383,130 +374,107 @@ Object* Object::LoadFile(const std::string& filename) {
|
||||
std::cout << "Vertices count: " << obj->m_vertices.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 << "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;
|
||||
|
||||
file.close();
|
||||
|
||||
for (auto &mesh : obj->m_meshes) {
|
||||
mesh.Upload();
|
||||
unsigned int i = 0;
|
||||
for (auto it = obj->Begin(); it != obj->End(); ++it, ++i) {
|
||||
std::cout << "Mesh #" << i << " primitives count: " << it->GetIndicesCount() / 3 << std::endl;
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
void Object::EnableBatch(unsigned int instanceVBO) {
|
||||
for (auto &mesh : m_meshes) {
|
||||
mesh.Bind();
|
||||
void Object::EnableBatch(const OpenGL::InstanceBuffer* instanceBuffer) {
|
||||
// FIXME:
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, instanceVBO);
|
||||
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
|
||||
}
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
// for (auto &mesh : m_meshes) {
|
||||
// mesh.Bind();
|
||||
|
||||
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)
|
||||
{
|
||||
for (auto &mesh : m_meshes)
|
||||
{
|
||||
auto material = GetMaterial(mesh.materialName);
|
||||
// FIXME:
|
||||
|
||||
// --- Basic material properties ---
|
||||
shader.setFloat("opacity", material->GetOpacity());
|
||||
// for (auto &mesh : m_meshes)
|
||||
// {
|
||||
// auto material = GetMaterial(mesh.GetMaterialName());
|
||||
|
||||
// Albedo (base color)
|
||||
shader.setVec3("albedo", material->GetDiffuseColor());
|
||||
// // --- Basic material properties ---
|
||||
// shader.setFloat("opacity", material->GetOpacity());
|
||||
|
||||
// Metallic and roughness (defaults)
|
||||
shader.setFloat("metallic", 0.8f);
|
||||
shader.setFloat("roughness", 0.5f);
|
||||
shader.setFloat("ao", 1.0f); // default ambient occlusion if none
|
||||
// // Albedo (base color)
|
||||
// shader.setVec3("albedo", material->GetDiffuseColor());
|
||||
|
||||
// --- Optional textures ---
|
||||
int texUnit = 0;
|
||||
// // Metallic and roughness (defaults)
|
||||
// shader.setFloat("metallic", 0.8f);
|
||||
// shader.setFloat("roughness", 0.5f);
|
||||
// shader.setFloat("ao", 1.0f); // default ambient occlusion if none
|
||||
|
||||
// Albedo texture
|
||||
if (material->HasDiffuseTexture()) {
|
||||
shader.setBool("useAlbedoMap", true);
|
||||
glActiveTexture(GL_TEXTURE0 + texUnit);
|
||||
glBindTexture(GL_TEXTURE_2D, material->GetDiffuseTexture()->GetID());
|
||||
shader.setInt("albedoTex", texUnit++);
|
||||
} else {
|
||||
shader.setBool("useAlbedoMap", false);
|
||||
}
|
||||
// // --- Optional textures ---
|
||||
// int texUnit = 0;
|
||||
|
||||
// Metallic texture
|
||||
// if (material->HasMetallicTexture()) {
|
||||
if (false) {
|
||||
shader.setBool("useMetallicMap", true);
|
||||
glActiveTexture(GL_TEXTURE0 + texUnit);
|
||||
// glBindTexture(GL_TEXTURE_2D, material->GetMetallicTexture()->GetID());
|
||||
shader.setInt("metallicTex", texUnit++);
|
||||
} else {
|
||||
shader.setBool("useMetallicMap", false);
|
||||
}
|
||||
// // Albedo texture
|
||||
// if (material->HasDiffuseTexture()) {
|
||||
// shader.setBool("useAlbedoMap", true);
|
||||
// glActiveTexture(GL_TEXTURE0 + texUnit);
|
||||
// glBindTexture(GL_TEXTURE_2D, material->GetDiffuseTexture()->GetID());
|
||||
// shader.setInt("albedoTex", texUnit++);
|
||||
// } else {
|
||||
// shader.setBool("useAlbedoMap", false);
|
||||
// }
|
||||
|
||||
// Roughness texture
|
||||
// if (material->HasRoughnessTexture()) {
|
||||
if (false) {
|
||||
shader.setBool("useRoughnessMap", true);
|
||||
glActiveTexture(GL_TEXTURE0 + texUnit);
|
||||
// glBindTexture(GL_TEXTURE_2D, material->GetRoughnessTexture()->GetID());
|
||||
shader.setInt("roughnessTex", texUnit++);
|
||||
} else {
|
||||
shader.setBool("useRoughnessMap", false);
|
||||
}
|
||||
// // Metallic texture
|
||||
// // if (material->HasMetallicTexture()) {
|
||||
// if (false) {
|
||||
// shader.setBool("useMetallicMap", true);
|
||||
// glActiveTexture(GL_TEXTURE0 + texUnit);
|
||||
// // glBindTexture(GL_TEXTURE_2D, material->GetMetallicTexture()->GetID());
|
||||
// shader.setInt("metallicTex", texUnit++);
|
||||
// } else {
|
||||
// shader.setBool("useMetallicMap", false);
|
||||
// }
|
||||
|
||||
// AO texture
|
||||
// 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);
|
||||
}
|
||||
// // Roughness texture
|
||||
// // if (material->HasRoughnessTexture()) {
|
||||
// if (false) {
|
||||
// shader.setBool("useRoughnessMap", true);
|
||||
// glActiveTexture(GL_TEXTURE0 + texUnit);
|
||||
// // glBindTexture(GL_TEXTURE_2D, material->GetRoughnessTexture()->GetID());
|
||||
// shader.setInt("roughnessTex", texUnit++);
|
||||
// } else {
|
||||
// shader.setBool("useRoughnessMap", false);
|
||||
// }
|
||||
|
||||
// --- Render mesh ---
|
||||
mesh.Render(count);
|
||||
}
|
||||
// // AO texture
|
||||
// // 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);
|
||||
// }
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@ -1,4 +1,13 @@
|
||||
#include "engine/scene/scene.h"
|
||||
|
||||
Scene::Scene() : m_registry() {
|
||||
#include <iostream>
|
||||
|
||||
namespace Core {
|
||||
|
||||
Entity Scene::CreateEntity() {
|
||||
Entity entity = { m_registry.create(), this };
|
||||
// std::cout << "Entities: " << (int)m_registry.view<entt::entity>().size() << std::endl;
|
||||
return entity;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,13 +1,23 @@
|
||||
#version 410 core
|
||||
#version 460 core
|
||||
|
||||
// Input vertex attributes
|
||||
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
|
||||
uniform mat4 u_model; // Model matrix: transforms from local space to world space
|
||||
uniform mat4 u_lightSpace;
|
||||
uniform bool u_isInstanced;
|
||||
|
||||
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);
|
||||
}
|
||||
@ -6,6 +6,12 @@ layout (location = 1) in vec3 normal; // vertex normal
|
||||
layout (location = 2) in vec2 texCoord; // 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
|
||||
out vec3 vertexPos;
|
||||
out vec3 vertexNormal;
|
||||
@ -14,8 +20,8 @@ out vec4 fragPosLightSpace;
|
||||
|
||||
// Uniforms for transformation matrices
|
||||
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_projection; // Projection matrix: transforms from camera space to clip space
|
||||
// uniform mat4 u_view;
|
||||
// uniform mat4 u_projection;
|
||||
uniform bool u_isInstanced;
|
||||
|
||||
void main()
|
||||
@ -32,5 +38,5 @@ void main()
|
||||
|
||||
// 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);
|
||||
}
|
||||
@ -8,6 +8,8 @@
|
||||
|
||||
#include "engine/renderer/debug.h"
|
||||
|
||||
namespace Core {
|
||||
|
||||
std::shared_ptr<Window> Window::s_instance = nullptr;
|
||||
|
||||
Window::Window(const char* title, int width, int height) {
|
||||
@ -100,12 +102,10 @@ void Window::ProcessEvents() {
|
||||
switch (event.type) {
|
||||
case SDL_EVENT_WINDOW_CLOSE_REQUESTED:
|
||||
case SDL_EVENT_QUIT:
|
||||
Dispatch(WindowCloseEvent());
|
||||
EmitEvent(WindowCloseEvent{});
|
||||
break;
|
||||
case SDL_EVENT_KEY_DOWN:
|
||||
if (event.key.scancode == SDL_SCANCODE_ESCAPE) {
|
||||
Dispatch(WindowCloseEvent());
|
||||
EmitEvent(WindowCloseEvent{});
|
||||
}
|
||||
if (event.key.scancode == SDL_SCANCODE_F11) {
|
||||
@ -125,7 +125,6 @@ void Window::ProcessEvents() {
|
||||
width,
|
||||
height);
|
||||
auto event = WindowResizeEvent(static_cast<unsigned int>(m_width), static_cast<unsigned int>(m_height));
|
||||
Dispatch(event);
|
||||
EmitEvent(event);
|
||||
SDL_SetWindowRelativeMouseMode(m_handle, true);
|
||||
SDL_Rect boundaries = {0, 0, m_width, m_height};
|
||||
@ -152,3 +151,4 @@ void Window::Destroy() const {
|
||||
SDL_DestroyWindow(m_handle);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,13 +1,21 @@
|
||||
set(SANDBOX_TARGET sandbox)
|
||||
set(MODEL_TARGET model)
|
||||
|
||||
add_executable(${SANDBOX_TARGET} src/main.cpp)
|
||||
add_executable(${MODEL_TARGET} src/model.cpp)
|
||||
|
||||
set_target_properties(${SANDBOX_TARGET} PROPERTIES
|
||||
CXX_STANDARD 17
|
||||
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(${MODEL_TARGET} PRIVATE ${ENGINE_TARGET})
|
||||
|
||||
# --- Copy engine.dll and all dependent DLLs next to sandbox.exe ---
|
||||
if (WIN32)
|
||||
@ -16,4 +24,10 @@ if (WIN32)
|
||||
$<TARGET_RUNTIME_DLLS:${SANDBOX_TARGET}> $<TARGET_FILE_DIR:${SANDBOX_TARGET}>
|
||||
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()
|
||||
|
||||
@ -17,90 +17,85 @@
|
||||
#include "engine/components/camera.h"
|
||||
#include "engine/components/mesh.h"
|
||||
#include "engine/components/rotate.h"
|
||||
#include "engine/components/batch.h"
|
||||
|
||||
#include "engine/3d/prefab.hpp"
|
||||
|
||||
#include "engine/scene/scene.h"
|
||||
#include "engine/input/input.h"
|
||||
|
||||
#include "engine/api.h"
|
||||
|
||||
using namespace Core;
|
||||
|
||||
class Game : public IApplication {
|
||||
public:
|
||||
Game() {}
|
||||
Game() = default;
|
||||
~Game() override {}
|
||||
|
||||
void OnInit(std::shared_ptr<Scene> scene) override {
|
||||
m_scene = scene;
|
||||
|
||||
Object* lightObj = Object::LoadFile("./assets/common/sphere/sphere.obj");
|
||||
const auto lght = scene->m_registry.create();
|
||||
scene->m_registry.emplace<transform>(lght, glm::vec3(5.f, 5.f, 5.f), glm::vec3(0.f));
|
||||
scene->m_registry.emplace<light>(lght, light::LightType::DIRECTIONAL, glm::vec3(1.f, 1.f, 1.f), 1.5f);
|
||||
scene->m_registry.emplace<mesh>(lght, std::shared_ptr<Object>(lightObj));
|
||||
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!");
|
||||
|
||||
const auto cameraEntity = scene->m_registry.create();
|
||||
scene->m_registry.emplace<transform>(cameraEntity, glm::vec3(0.f, 2.f, 2.f));
|
||||
scene->m_registry.emplace<camera>(cameraEntity);
|
||||
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/wizard/wizard.obj");
|
||||
const auto targetEntity = scene->m_registry.create();
|
||||
scene->m_registry.emplace<transform>(targetEntity, glm::vec3(0.f, 0.0f, 0.f));
|
||||
scene->m_registry.emplace<mesh>(targetEntity, std::shared_ptr<Object>(targetObj));
|
||||
scene->m_registry.emplace<rotate>(targetEntity);
|
||||
|
||||
Object* grass = Object::LoadFile("./assets/common/cube/cube.obj");
|
||||
const auto cubeEntity = scene->m_registry.create();
|
||||
scene->m_registry.emplace<transform>(cubeEntity, glm::vec3(-1.5f, 0.4f, 0.f));
|
||||
scene->m_registry.emplace<mesh>(cubeEntity, std::shared_ptr<Object>(grass));
|
||||
modelEntity = scene->CreateEntity();
|
||||
modelEntity.AddComponent<Transform>(glm::vec3(0.f, 0.0f, 0.f));
|
||||
modelEntity.AddComponent<mesh>(std::shared_ptr<Renderable>(targetObj));
|
||||
modelEntity.AddComponent<rotate>();
|
||||
assert(modelEntity.HasComponent<mesh>() && "model doesn't have any mesh!");
|
||||
|
||||
// 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"));
|
||||
const auto batchEntt = scene->m_registry.create();
|
||||
scene->m_registry.emplace<batch>(batchEntt);
|
||||
scene->m_registry.emplace<mesh>(batchEntt, cubeObj);
|
||||
auto cubeBatch = scene->m_registry.get<batch>(batchEntt);
|
||||
auto cubeObj = Object::LoadFile("./assets/grass_block/grass_block.obj");
|
||||
auto batchEntt = scene->CreateEntity();
|
||||
auto& cubeBatch = batchEntt.AddComponent<Prefab>(std::move(*cubeObj));
|
||||
// auto& cubeBatch = batchEntt.GetComponent<batch>();
|
||||
// batchEntt.AddComponent<mesh>(cubeObj);
|
||||
// assert(batchEntt.HasComponent<batch>() && "batch doesn't have any batch component!");
|
||||
// assert(batchEntt.HasComponent<mesh>() && "batch doesn't have any mesh component!");
|
||||
// Generate 1000 random cubes
|
||||
for (int i = 0; i < 1000; ++i) {
|
||||
const auto cubeEntity = scene->m_registry.create();
|
||||
for (int i = 0; i < 100; ++i) {
|
||||
auto cubeEntity = scene->CreateEntity();
|
||||
|
||||
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 z = static_cast<float>(rand()) / RAND_MAX * 200.f - 100.f; // range [-100, 100]
|
||||
|
||||
scene->m_registry.emplace<transform>(cubeEntity, glm::vec3(x, y, z));
|
||||
scene->m_registry.emplace<rotate>(cubeEntity);
|
||||
scene->m_registry.emplace<batch::item>(cubeEntity, cubeBatch.id());
|
||||
cubeEntity.AddComponent<Transform>(glm::vec3(x, y, z));
|
||||
cubeEntity.AddComponent<rotate>();
|
||||
cubeEntity.AddComponent<batch::item>(cubeBatch.GetID());
|
||||
}
|
||||
|
||||
Object* floorObj = Object::LoadFile("./assets/common/plane/plane.obj");
|
||||
const auto floorEntt = scene->m_registry.create();
|
||||
scene->m_registry.emplace<transform>(floorEntt, glm::vec3(0.f));
|
||||
scene->m_registry.emplace<mesh>(floorEntt, std::shared_ptr<Object>(floorObj));
|
||||
auto floorEntt = scene->CreateEntity();
|
||||
floorEntt.AddComponent<Transform>(glm::vec3(0.f));
|
||||
floorEntt.AddComponent<mesh>(std::shared_ptr<Object>(floorObj));
|
||||
assert(floorEntt.HasComponent<mesh>() && "floor doesn't have any mesh component!");
|
||||
|
||||
std::cout << "Game initialized" << std::endl;
|
||||
|
||||
m_angle = 3.45f;
|
||||
m_lastTicks = SDL_GetTicks();
|
||||
|
||||
m_paused = false;
|
||||
|
||||
m_yaw = -90.0f; // looking along -Z initially
|
||||
m_pitch = 0.0f; // no vertical tilt
|
||||
|
||||
// FPS tracking
|
||||
m_startTicks = SDL_GetTicks();
|
||||
m_frameCount = 0;
|
||||
}
|
||||
|
||||
void OnUpdate() override {
|
||||
m_currentTicks = SDL_GetTicks();
|
||||
float deltaTime = static_cast<float>(m_currentTicks - m_lastTicks) / 1000.0f; // seconds
|
||||
|
||||
m_lastTicks = m_currentTicks;
|
||||
|
||||
float mouseXRel, mouseYRel;
|
||||
SDL_GetRelativeMouseState(&mouseXRel, &mouseYRel);
|
||||
void OnUpdate(Timestep dt) override {
|
||||
glm::vec2 mouseRel = Input::GetRelativeMouse();
|
||||
|
||||
float sensitivity = 0.1f; // tweak as needed
|
||||
m_yaw += mouseXRel * sensitivity;
|
||||
m_pitch -= mouseYRel * sensitivity; // invert Y for typical FPS control
|
||||
m_yaw += mouseRel.x * sensitivity;
|
||||
m_pitch -= mouseRel.y * sensitivity; // invert Y for typical FPS control
|
||||
|
||||
// clamp pitch to avoid flipping
|
||||
// if (pitch > 89.0f) pitch = 89.0f;
|
||||
@ -116,36 +111,28 @@ public:
|
||||
|
||||
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 right = glm::normalize(glm::cross(front, glm::vec3(0.f, 1.f, 0.f)));
|
||||
|
||||
if (state[SDL_SCANCODE_W]) velocity += front;
|
||||
if (state[SDL_SCANCODE_S]) velocity -= front;
|
||||
if (state[SDL_SCANCODE_A]) velocity -= right;
|
||||
if (state[SDL_SCANCODE_D]) velocity += right;
|
||||
if (state[SDL_SCANCODE_SPACE]) velocity.y += 1.f;
|
||||
if (state[SDL_SCANCODE_LSHIFT]) velocity.y -= 1.f;
|
||||
if (Input::IsKeyPressed(SDL_SCANCODE_W)) velocity += front;
|
||||
if (Input::IsKeyPressed(SDL_SCANCODE_S)) velocity -= front;
|
||||
if (Input::IsKeyPressed(SDL_SCANCODE_A)) velocity -= right;
|
||||
if (Input::IsKeyPressed(SDL_SCANCODE_D)) velocity += right;
|
||||
if (Input::IsKeyPressed(SDL_SCANCODE_SPACE)) velocity.y += 1.f;
|
||||
if (Input::IsKeyPressed(SDL_SCANCODE_LSHIFT)) velocity.y -= 1.f;
|
||||
|
||||
auto view = m_scene->m_registry.view<camera, transform>();
|
||||
for (auto [cam, camTransform] : view.each()) {
|
||||
camTransform.position += velocity * deltaTime * 2.5f; // speed is e.g. 2.5f
|
||||
camTransform.rotation = cameraViewDirection;
|
||||
}
|
||||
auto& camTransform = cameraEntity.GetComponent<Transform>();
|
||||
camTransform.position += velocity * (float)dt * 2.5f; // speed is e.g. 2.5f
|
||||
camTransform.rotation = cameraViewDirection;
|
||||
|
||||
// update rotation
|
||||
if (!m_paused) {
|
||||
m_angle += glm::radians(45.0f) * deltaTime; // 72° per second
|
||||
if (m_angle > glm::two_pi<float>()) {
|
||||
m_angle -= glm::two_pi<float>(); // keep value small
|
||||
}
|
||||
m_angle += glm::radians(45.0f) * dt; // 72° per second
|
||||
if (m_angle > glm::two_pi<float>()) {
|
||||
m_angle -= glm::two_pi<float>(); // keep value small
|
||||
}
|
||||
|
||||
// ---- Day-night simulation ----
|
||||
m_dayTime += deltaTime;
|
||||
m_dayTime += dt;
|
||||
if (m_dayTime > m_dayLength)
|
||||
m_dayTime -= m_dayLength; // loop every "day"
|
||||
|
||||
@ -167,34 +154,22 @@ public:
|
||||
glm::vec3 sunColor = glm::mix(dayColor, sunsetColor, sunsetFactor);
|
||||
|
||||
// Update the directional light in the registry
|
||||
auto lightsView = m_scene->m_registry.view<light, transform>();
|
||||
for (auto [entity, l, t] : lightsView.each()) {
|
||||
if (l.type == light::LightType::DIRECTIONAL) {
|
||||
// "position" for directional light often stores direction vector
|
||||
// If your system instead uses transform.rotation, adjust accordingly
|
||||
t.position = sunDir * 15.f; // use this as light direction
|
||||
l.color = sunColor;
|
||||
l.intensity = intensity;
|
||||
}
|
||||
auto& l = lightEntity.GetComponent<light>();
|
||||
auto& t = lightEntity.GetComponent<Transform>();
|
||||
if (l.type == light::LightType::DIRECTIONAL) {
|
||||
// "position" for directional light often stores direction vector
|
||||
// If your system instead uses transform.rotation, adjust accordingly
|
||||
t.position = sunDir * 15.f; // use this as light direction
|
||||
l.color = sunColor;
|
||||
l.intensity = intensity;
|
||||
}
|
||||
|
||||
auto rotateEntts = m_scene->m_registry.view<transform, rotate>();
|
||||
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_elapsed += dt.GetMilliseconds();
|
||||
|
||||
m_frameCount++;
|
||||
m_currentTicks = SDL_GetTicks();
|
||||
Uint64 elapsed = m_currentTicks - m_startTicks;
|
||||
|
||||
if (elapsed >= 1000) { // one second passed
|
||||
double fps = static_cast<double>(m_frameCount) / (static_cast<double>(elapsed) / 1000.0);
|
||||
if (m_elapsed >= 1000) { // one second passed
|
||||
m_elapsed = 0;
|
||||
double fps = 1 / dt;
|
||||
std::cout << "FPS: " << fps << std::endl;
|
||||
m_frameCount = 0;
|
||||
m_startTicks = m_currentTicks;
|
||||
}
|
||||
}
|
||||
|
||||
@ -208,24 +183,22 @@ public:
|
||||
}
|
||||
}
|
||||
private:
|
||||
// for internal 1-second timer
|
||||
int m_elapsed;
|
||||
|
||||
std::shared_ptr<Scene> m_scene;
|
||||
|
||||
Entity lightEntity;
|
||||
Entity cameraEntity;
|
||||
Entity modelEntity;
|
||||
|
||||
float m_angle;
|
||||
Uint64 m_lastTicks;
|
||||
|
||||
float m_dayTime = 0.0f; // accumulates time for day-night 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_pitch = 0.0f; // no vertical tilt
|
||||
|
||||
// FPS tracking
|
||||
Uint64 m_startTicks;
|
||||
int m_frameCount;
|
||||
|
||||
Uint64 m_currentTicks;
|
||||
};
|
||||
|
||||
IApplication* CreateApplication() {
|
||||
|
||||
92
sandbox/src/model.cpp
Normal file
92
sandbox/src/model.cpp
Normal 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();
|
||||
}
|
||||
Reference in New Issue
Block a user