Compare commits

...

10 Commits

Author SHA1 Message Date
113412bb5b making prefab work 2025-11-14 18:26:30 +01:00
c7b6a79270 feat: use renderable 2025-11-05 11:51:38 +01:00
54fa900dff fix: fixme checked (uploading already) 2025-11-05 11:51:26 +01:00
b6b40837a4 feat: prepare each mesh entt for rendering 2025-11-05 11:50:57 +01:00
d6267f7a4b chore: remove unnecessary logs 2025-11-05 11:48:33 +01:00
71a14af25c fix: correct import filename 2025-11-05 11:48:24 +01:00
339da4ef02 fix: use correct data 2025-11-05 11:48:08 +01:00
0c4b7ed285 feat: mesh component accept renderable 2025-11-05 08:07:24 +01:00
bdd5a16b3d feat: renderable interface 2025-11-05 08:06:54 +01:00
116f274228 feat: render support for mesh 2025-11-05 08:06:41 +01:00
14 changed files with 4207 additions and 61 deletions

View File

@ -24,7 +24,7 @@ public:
for (unsigned int i = 0; i < m_size; ++i) { for (unsigned int i = 0; i < m_size; ++i) {
m_items[i].~Item(); m_items[i].~Item();
} }
delete m_items; ::operator delete[](m_items);
} }
Array(Array&& other) { Array(Array&& other) {
@ -37,6 +37,25 @@ public:
other.m_items = nullptr; 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; Array(const Array&) = delete;
public: public:
inline const Size GetSize() const noexcept { return m_size; } inline const Size GetSize() const noexcept { return m_size; }
@ -79,11 +98,11 @@ private:
std::uninitialized_move( std::uninitialized_move(
std::make_move_iterator(m_items), std::make_move_iterator(m_items),
std::make_move_iterator(m_items + m_capacity), std::make_move_iterator(m_items + m_size),
newItems newItems
); );
for (unsigned int i = 0; i < m_capacity; ++i) { for (unsigned int i = 0; i < m_size; ++i) {
m_items[i].~Item(); m_items[i].~Item();
} }

View File

@ -3,12 +3,17 @@
#include "engine/3d/array.hpp" #include "engine/3d/array.hpp"
#include "engine/3d/vertex.hpp" #include "engine/3d/vertex.hpp"
#include "engine/3d/material.hpp" #include "engine/3d/material.hpp"
#include "engine/opengl/buffers.h"
#include "engine/renderer/renderable.hpp"
#include "engine/export.h" #include "engine/export.h"
namespace Core { namespace Core {
class ENGINE_API Mesh { class MeshGroup;
class ENGINE_API Mesh : public Renderable, public OpenGL::VertexArray {
friend class Core::MeshGroup;
public: public:
Mesh() = default; Mesh() = default;
Mesh(const Material& material) : m_material(material) {} Mesh(const Material& material) : m_material(material) {}
@ -41,12 +46,132 @@ public:
m_indices.PushBack(b); m_indices.PushBack(b);
m_indices.PushBack(c); m_indices.PushBack(c);
} }
const auto GetVerticesCount() const { return m_vertices.GetSize(); }
const auto GetIndicesCount() const { return m_indices.GetSize(); }
public: public:
inline const Material& GetMaterial() const { return m_material; } 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: private:
Array<Vertex> m_vertices { VERTICES_INITIAL_CAPACITY }; Array<Vertex> m_vertices { VERTICES_INITIAL_CAPACITY };
Array<uint32_t> m_indices { INDICES_INITIAL_CAPACITY }; Array<uint32_t> m_indices { INDICES_INITIAL_CAPACITY };
// TODO: move out
// ------- RENDERING ---------
unsigned int m_ebo;
// ------- RENDERING ---------
Material m_material; Material m_material;
std::unordered_map<Vertex, uint32_t, VertexHash> m_vti; std::unordered_map<Vertex, uint32_t, VertexHash> m_vti;
@ -56,10 +181,22 @@ private:
// In future we might want to add support for global material // In future we might want to add support for global material
// that can affect all sub materials, aka this class // that can affect all sub materials, aka this class
// will act like Parent Mesh that contains Child Meshes // will act like Parent Mesh that contains Child Meshes
class ENGINE_API MeshGroup : public Array<Mesh> { class ENGINE_API MeshGroup : public Array<Mesh>, public Renderable {
public: public:
MeshGroup() {} 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: public:
inline Mesh* FindMeshByMaterial(const Material& material) { inline Mesh* FindMeshByMaterial(const Material& material) {
for (auto it = Begin(); it != End(); ++it) { for (auto it = Begin(); it != End(); ++it) {

View File

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

View File

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

View File

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

View File

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

View File

@ -19,6 +19,7 @@ void batch::prepare(glm::mat4 *instances, unsigned int count) {
m_instance_count = count; m_instance_count = count;
} else if (count > m_instance_count) { } else if (count > m_instance_count) {
// Optional: reallocate only if you *really* have more instances than before // Optional: reallocate only if you *really* have more instances than before
// FIXME: what the hell is m_instance_vbo
glBindBuffer(GL_ARRAY_BUFFER, m_instance_vbo); glBindBuffer(GL_ARRAY_BUFFER, m_instance_vbo);
OpenGL::Buffer::Bind(m_instanceBuffer); OpenGL::Buffer::Bind(m_instanceBuffer);
OpenGL::Buffer::Data(m_instanceBuffer, nullptr, sizeof(glm::mat4) * count); OpenGL::Buffer::Data(m_instanceBuffer, nullptr, sizeof(glm::mat4) * count);

View File

@ -67,9 +67,8 @@ namespace OpenGL {
: ArrayBuffer(usage) {} : ArrayBuffer(usage) {}
VertexArray::VertexArray() : m_id(0) { VertexArray::VertexArray() : m_id(0) {
std::cout << "Vertex Array init" << std::endl;
glGenVertexArrays(1, &m_id); glGenVertexArrays(1, &m_id);
std::cout << "m_id: " << m_id << std::endl; std::cout << "[DEBUG] VArr initialized: " << m_id << std::endl;
} }
VertexArray::~VertexArray() { VertexArray::~VertexArray() {
@ -81,9 +80,9 @@ namespace OpenGL {
} }
void VertexArray::Bind() { void VertexArray::Bind() {
std::cout << "Binding VAO" << std::endl;
assert(m_id != 0 && "Vertex Array wasn't initialized."); assert(m_id != 0 && "Vertex Array wasn't initialized.");
std::cout << "[DEBUG] VArr binding: " << m_id << std::endl;
glBindVertexArray(m_id); glBindVertexArray(m_id);
} }

View File

@ -20,6 +20,7 @@
#include "engine/components/light.h" #include "engine/components/light.h"
#include "engine/components/mesh.h" #include "engine/components/mesh.h"
#include "engine/components/batch.h" #include "engine/components/batch.h"
#include "engine/3d/prefab.hpp"
namespace Core { namespace Core {
@ -57,6 +58,14 @@ Renderer::Renderer(std::shared_ptr<Scene> scene)
void Renderer::Init() { void Renderer::Init() {
GenerateShadowMaps(); GenerateShadowMaps();
for (auto [entt, mesh] : m_scene->m_registry.view<mesh>().each()) {
mesh.mesh->Prepare();
}
for (auto [entt, prefab] : m_scene->m_registry.view<Prefab>().each()) {
prefab.Prepare();
}
} }
void Renderer::OnWindowResized(int w, int h) { void Renderer::OnWindowResized(int w, int h) {
@ -138,46 +147,55 @@ void Renderer::UpdateView() {
} }
void Renderer::RenderScene(Shader &shader) { void Renderer::RenderScene(Shader &shader) {
std::unordered_map<unsigned int, std::vector<entt::entity>> batches; // std::unordered_map<unsigned int, std::vector<entt::entity>> batches;
for (auto [entt, item] : m_scene->m_registry.view<batch::item>().each()) { // for (auto [entt, item] : m_scene->m_registry.view<batch::item>().each()) {
if (batches.find(item.batchId) == batches.end()) // if (batches.find(item.batchId) == batches.end())
batches.insert(std::make_pair(item.batchId, std::vector<entt::entity>())); // batches.insert(std::make_pair(item.batchId, std::vector<entt::entity>()));
batches[item.batchId].push_back(entt); // batches[item.batchId].push_back(entt);
} // }
shader.setBool("u_isInstanced", true); // shader.setBool("u_isInstanced", true);
// shader.setBool("isLight", false);
// shader.setVec3("currentLightColor", glm::vec3(0.f));
// for (auto [entt, b, m] : m_scene->m_registry.view<batch, mesh>().each()) {
// // check if have items for batch render
// if (batches.find(b.id()) == batches.end()) continue;
// auto &batchItems = batches[b.id()];
// std::vector<glm::mat4> models;
// models.reserve(batchItems.size());
// for (auto item : batchItems) {
// auto &t = m_scene->m_registry.get<Transform>(item);
// glm::mat4 rotation = glm::yawPitchRoll(t.rotation.y, t.rotation.x, t.rotation.z);
// auto itemModel = glm::translate(glm::mat4(1.f), t.position) * rotation;
// models.push_back(itemModel);
// }
// auto prevState = b.Initialized();
// b.prepare(models.data(), models.size());
// if (!prevState) {
// std::cout << "[DEBUG] enabling batch" << std::endl;
// // TODO:
// // m.object->EnableBatch(b.m_instanceBuffer);
// }
// m.mesh->Render(shader);
// }
// shader.setBool("u_isInstanced", false);
// light cannot be batch rendered (yet :3)
shader.setBool("isLight", false); shader.setBool("isLight", false);
shader.setVec3("currentLightColor", glm::vec3(0.f)); shader.setVec3("currentLightColor", glm::vec3(0.f));
for (auto [entt, b, m] : m_scene->m_registry.view<batch, mesh>().each()) { for (auto [entity, prefab] : m_scene->m_registry.view<Prefab>().each()) {
// check if have items for batch render prefab.Render(shader, *m_scene.get(), 1);
if (batches.find(b.id()) == batches.end()) continue;
auto &batchItems = batches[b.id()];
std::vector<glm::mat4> models;
models.reserve(batchItems.size());
for (auto item : batchItems) {
auto &t = m_scene->m_registry.get<Transform>(item);
glm::mat4 rotation = glm::yawPitchRoll(t.rotation.y, t.rotation.x, t.rotation.z);
auto itemModel = glm::translate(glm::mat4(1.f), t.position) * rotation;
models.push_back(itemModel);
}
auto prevState = b.Initialized();
b.prepare(models.data(), models.size());
if (!prevState) {
std::cout << "[DEBUG] enabling batch" << std::endl;
m.object->EnableBatch(b.m_instanceBuffer);
}
m.object->Render(shader, batchItems.size());
} }
shader.setBool("u_isInstanced", false);
for (auto [entity, transf, mesh] : m_scene->m_registry.view<Transform, mesh>(entt::exclude<batch, batch::item>).each()) { // entt::exclude<batch, batch::item>
if (mesh.object == nullptr) { for (auto [entity, transf, mesh] : m_scene->m_registry.view<Transform, mesh>().each()) {
if (mesh.mesh == nullptr) {
std::cerr << "WARN: Entity doesn't have a mesh to render" << std::endl; std::cerr << "WARN: Entity doesn't have a mesh to render" << std::endl;
return; return;
} }
@ -196,7 +214,7 @@ void Renderer::RenderScene(Shader &shader) {
shader.setMat4("u_model", m_model); shader.setMat4("u_model", m_model);
mesh.object->Render(shader, 1); mesh.mesh->Render(shader, *m_scene.get(), 1);
} }
} }

View File

@ -301,6 +301,7 @@ Object* Object::LoadFile(const std::string& filename) {
x /= w; y /= w; z /= w; x /= w; y /= w; z /= w;
} }
obj->m_vertices.emplace_back(x, y, z); obj->m_vertices.emplace_back(x, y, z);
break; break;
} }
@ -343,7 +344,7 @@ Object* Object::LoadFile(const std::string& filename) {
Vertex v; Vertex v;
v.position = obj->m_vertices[vi]; v.position = obj->m_vertices[vi];
v.normal = (ni >= 0) ? obj->m_normals[ni] : glm::vec3(0.0f); v.normal = (ni >= 0) ? obj->m_normals[ni] : glm::vec3(0.0f);
v.uv = (ti >= 0) ? obj->m_texCoords[ti] : glm::vec3(0.0f); v.uv = (ti >= 0) ? obj->m_texCoords[ti] : glm::vec2(0.0f);
uint32_t idx = mesh.PushVertex(v); uint32_t idx = mesh.PushVertex(v);
faceIndices.push_back(idx); faceIndices.push_back(idx);
@ -351,6 +352,9 @@ Object* Object::LoadFile(const std::string& filename) {
// mesh.m_indexBuffer.push_back(mesh.m_vertexBuffer.size() - 1); // mesh.m_indexBuffer.push_back(mesh.m_vertexBuffer.size() - 1);
} }
// [0, 1, 2]
// ^
// triangulate polygon (fan) // triangulate polygon (fan)
if (faceIndices.size() >= 3) { if (faceIndices.size() >= 3) {
for (size_t i = 1; i + 1 < faceIndices.size(); ++i) { for (size_t i = 1; i + 1 < faceIndices.size(); ++i) {
@ -375,11 +379,10 @@ Object* Object::LoadFile(const std::string& filename) {
file.close(); file.close();
// FIXME: unsigned int i = 0;
for (auto it = obj->Begin(); it != obj->End(); ++it, ++i) {
// for (auto it = obj->Begin(); it != obj->End(); ++it) { std::cout << "Mesh #" << i << " primitives count: " << it->GetIndicesCount() / 3 << std::endl;
// it->Upload(); }
// }
return obj; return obj;
} }

3685
out.txt Normal file

File diff suppressed because it is too large Load Diff

View File

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

View File

@ -17,7 +17,8 @@
#include "engine/components/camera.h" #include "engine/components/camera.h"
#include "engine/components/mesh.h" #include "engine/components/mesh.h"
#include "engine/components/rotate.h" #include "engine/components/rotate.h"
#include "engine/components/batch.h"
#include "engine/3d/prefab.hpp"
#include "engine/scene/scene.h" #include "engine/scene/scene.h"
#include "engine/input/input.h" #include "engine/input/input.h"
@ -38,7 +39,7 @@ public:
lightEntity = scene->CreateEntity(); lightEntity = scene->CreateEntity();
lightEntity.AddComponent<Transform>(glm::vec3(5.f, 5.f, 5.f), glm::vec3(0.f)); lightEntity.AddComponent<Transform>(glm::vec3(5.f, 5.f, 5.f), glm::vec3(0.f));
lightEntity.AddComponent<light>(light::LightType::DIRECTIONAL, glm::vec3(1.f, 1.f, 1.f), 1.5f); lightEntity.AddComponent<light>(light::LightType::DIRECTIONAL, glm::vec3(1.f, 1.f, 1.f), 1.5f);
lightEntity.AddComponent<mesh>(std::shared_ptr<Object>(lightObj)); lightEntity.AddComponent<mesh>(std::shared_ptr<Renderable>(lightObj));
assert(lightEntity.HasComponent<mesh>() && "light doesn't have any mesh!"); assert(lightEntity.HasComponent<mesh>() && "light doesn't have any mesh!");
cameraEntity = scene->CreateEntity(); cameraEntity = scene->CreateEntity();
@ -50,18 +51,18 @@ public:
Object* targetObj = Object::LoadFile("./assets/wizard/wizard.obj"); Object* targetObj = Object::LoadFile("./assets/wizard/wizard.obj");
modelEntity = scene->CreateEntity(); modelEntity = scene->CreateEntity();
modelEntity.AddComponent<Transform>(glm::vec3(0.f, 0.0f, 0.f)); modelEntity.AddComponent<Transform>(glm::vec3(0.f, 0.0f, 0.f));
modelEntity.AddComponent<mesh>(std::shared_ptr<Object>(targetObj)); modelEntity.AddComponent<mesh>(std::shared_ptr<Renderable>(targetObj));
modelEntity.AddComponent<rotate>(); modelEntity.AddComponent<rotate>();
assert(modelEntity.HasComponent<mesh>() && "model doesn't have any mesh!"); assert(modelEntity.HasComponent<mesh>() && "model doesn't have any mesh!");
// Cube template (use shared object to avoid reloading 1000 times) // Cube template (use shared object to avoid reloading 1000 times)
std::shared_ptr<Object> cubeObj = std::shared_ptr<Object>(Object::LoadFile("./assets/grass_block/grass_block.obj")); auto cubeObj = Object::LoadFile("./assets/grass_block/grass_block.obj");
auto batchEntt = scene->CreateEntity(); auto batchEntt = scene->CreateEntity();
auto& cubeBatch = batchEntt.AddComponent<batch>(); auto& cubeBatch = batchEntt.AddComponent<Prefab>(std::move(*cubeObj));
// auto& cubeBatch = batchEntt.GetComponent<batch>(); // auto& cubeBatch = batchEntt.GetComponent<batch>();
batchEntt.AddComponent<mesh>(cubeObj); // batchEntt.AddComponent<mesh>(cubeObj);
assert(batchEntt.HasComponent<batch>() && "batch doesn't have any batch component!"); // assert(batchEntt.HasComponent<batch>() && "batch doesn't have any batch component!");
assert(batchEntt.HasComponent<mesh>() && "batch doesn't have any mesh component!"); // assert(batchEntt.HasComponent<mesh>() && "batch doesn't have any mesh component!");
// Generate 1000 random cubes // Generate 1000 random cubes
for (int i = 0; i < 100; ++i) { for (int i = 0; i < 100; ++i) {
auto cubeEntity = scene->CreateEntity(); auto cubeEntity = scene->CreateEntity();
@ -72,7 +73,7 @@ public:
cubeEntity.AddComponent<Transform>(glm::vec3(x, y, z)); cubeEntity.AddComponent<Transform>(glm::vec3(x, y, z));
cubeEntity.AddComponent<rotate>(); cubeEntity.AddComponent<rotate>();
cubeEntity.AddComponent<batch::item>(cubeBatch.id()); cubeEntity.AddComponent<batch::item>(cubeBatch.GetID());
} }
Object* floorObj = Object::LoadFile("./assets/common/plane/plane.obj"); Object* floorObj = Object::LoadFile("./assets/common/plane/plane.obj");

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

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