entity editor, scene graph viewer, scene viewer basics done. Can rotate, translate and scale selected models.
This commit is contained in:
93
src/Camera.cpp
Normal file
93
src/Camera.cpp
Normal file
@@ -0,0 +1,93 @@
|
||||
//
|
||||
// Created by slinky on 5/3/26.
|
||||
//
|
||||
|
||||
#include "Camera.h"
|
||||
|
||||
#include "glm/ext/matrix_transform.hpp"
|
||||
#include "glm/ext/matrix_clip_space.hpp"
|
||||
|
||||
Camera::Camera(const glm::vec3 &pos, float pitch, float yaw, float aspectRatio) : _position(pos), _yaw(yaw), _pitch(pitch), _aspectRatio(aspectRatio) {
|
||||
update_camera_vectors();
|
||||
update_view();
|
||||
update_projection();
|
||||
}
|
||||
|
||||
const glm::mat4& Camera::view() const {
|
||||
return _view;
|
||||
}
|
||||
|
||||
const glm::mat4& Camera::projection() const {
|
||||
return _projection;
|
||||
}
|
||||
|
||||
const glm::vec3& Camera::position() const {
|
||||
return _position;
|
||||
}
|
||||
|
||||
glm::vec3 Camera::front() const {
|
||||
return glm::normalize(_front);
|
||||
}
|
||||
|
||||
glm::vec3 Camera::right() const {
|
||||
return glm::normalize(_right);
|
||||
}
|
||||
|
||||
glm::vec3 Camera::up() const {
|
||||
return glm::normalize(_up);
|
||||
}
|
||||
|
||||
float Camera::aspect_ratio() const {
|
||||
return _aspectRatio;
|
||||
}
|
||||
|
||||
void Camera::rotate_pitch(float offset) {
|
||||
_pitch += offset;
|
||||
|
||||
if (_pitch < MIN_PITCH) {
|
||||
_pitch = MIN_PITCH;
|
||||
}
|
||||
|
||||
if (_pitch > MAX_PITCH) {
|
||||
_pitch = MAX_PITCH;
|
||||
}
|
||||
|
||||
update_camera_vectors();
|
||||
update_view();
|
||||
}
|
||||
|
||||
void Camera::rotate_yaw(float offset) {
|
||||
_yaw += offset;
|
||||
update_camera_vectors();
|
||||
update_view();
|
||||
}
|
||||
|
||||
void Camera::translate(const glm::vec3& translation) {
|
||||
_position += translation;
|
||||
update_view();
|
||||
}
|
||||
|
||||
void Camera::update_aspect_ratio(float aspectRatio) {
|
||||
_aspectRatio = aspectRatio;
|
||||
_projection = glm::perspective(glm::radians(75.f), _aspectRatio, 0.1f, 100.0f);
|
||||
}
|
||||
|
||||
void Camera::update_camera_vectors() {
|
||||
_front = {};
|
||||
_front.x = cos(glm::radians(_yaw)) * cos(glm::radians(_pitch));
|
||||
_front.y = sin(glm::radians(_pitch));
|
||||
_front.z = sin(glm::radians(_yaw)) * cos(glm::radians(_pitch));
|
||||
|
||||
_front = glm::normalize(_front);
|
||||
|
||||
_right = glm::normalize(glm::cross(_front, WORLD_UP));
|
||||
_up = glm::normalize(glm::cross(_right, _front));
|
||||
}
|
||||
|
||||
void Camera::update_projection() {
|
||||
_projection = glm::perspective(glm::radians(75.f), _aspectRatio, 0.1f, 100.0f);
|
||||
}
|
||||
|
||||
void Camera::update_view() {
|
||||
_view = glm::lookAt(_position, _position + _front, _up);
|
||||
}
|
||||
@@ -12,29 +12,24 @@ constexpr float MIN_PITCH = -89.f;
|
||||
|
||||
constexpr glm::vec3 WORLD_UP = {0.0f, 1.0f, 0.0f};
|
||||
|
||||
enum CAMERA_MOVEMENT {
|
||||
FORWARD,
|
||||
BACKWARD,
|
||||
LEFT,
|
||||
RIGHT,
|
||||
UP,
|
||||
DOWN,
|
||||
};
|
||||
|
||||
class FreeCamera {
|
||||
class Camera {
|
||||
public:
|
||||
FreeCamera() = default;
|
||||
FreeCamera(const glm::vec3 &pos, float pitch, float yaw, float aspectRatio);
|
||||
Camera() = default;
|
||||
Camera(const glm::vec3 &pos, float pitch, float yaw, float aspectRatio);
|
||||
|
||||
[[nodiscard]] const glm::mat4& projection() const;
|
||||
[[nodiscard]] const glm::mat4& view() const;
|
||||
[[nodiscard]] const glm::vec3& position() const;
|
||||
[[nodiscard]] glm::vec3 front() const;
|
||||
[[nodiscard]] glm::vec3 up() const;
|
||||
[[nodiscard]] glm::vec3 right() const;
|
||||
[[nodiscard]] float aspect_ratio() const;
|
||||
|
||||
void update_aspect_ratio(float aspectRatio);
|
||||
void rotate_yaw(float offset);
|
||||
void rotate_pitch(float offset);
|
||||
|
||||
void move(CAMERA_MOVEMENT direction, float offset);
|
||||
void translate(const glm::vec3& direction);
|
||||
private:
|
||||
void update_camera_vectors();
|
||||
void update_projection();
|
||||
@@ -42,9 +37,9 @@ private:
|
||||
|
||||
glm::vec3 _position{};
|
||||
|
||||
glm::vec3 front{};
|
||||
glm::vec3 up{};
|
||||
glm::vec3 right{};
|
||||
glm::vec3 _front{};
|
||||
glm::vec3 _up{};
|
||||
glm::vec3 _right{};
|
||||
|
||||
glm::mat4 _view{};
|
||||
glm::mat4 _projection{};
|
||||
@@ -17,6 +17,7 @@ namespace Components {
|
||||
glm::vec3 rotation{};
|
||||
glm::vec3 scale{};
|
||||
glm::mat4 model = glm::identity<glm::mat4>();
|
||||
bool dirty {true};
|
||||
};
|
||||
|
||||
struct Relationship {
|
||||
|
||||
21
src/EditorContext.h
Normal file
21
src/EditorContext.h
Normal file
@@ -0,0 +1,21 @@
|
||||
//
|
||||
// Created by slinky on 5/11/26.
|
||||
//
|
||||
|
||||
#ifndef B_ENGINE_EDITORCONTEXT_H
|
||||
#define B_ENGINE_EDITORCONTEXT_H
|
||||
|
||||
#include "Scene.h"
|
||||
#include "GLFW/glfw3.h"
|
||||
|
||||
struct EditorContext {
|
||||
bool sceneViewerFocused = false;
|
||||
glm::vec2 viewportSize = glm::vec2 {};
|
||||
float viewportAspectRatio = 1;
|
||||
|
||||
Scene* scene {nullptr};
|
||||
GLFWwindow* window;
|
||||
entt::entity selectedEntity;
|
||||
};
|
||||
|
||||
#endif //B_ENGINE_EDITORCONTEXT_H
|
||||
@@ -1,99 +0,0 @@
|
||||
//
|
||||
// Created by slinky on 5/3/26.
|
||||
//
|
||||
|
||||
#include "FreeCamera.h"
|
||||
|
||||
#include "glm/ext/matrix_transform.hpp"
|
||||
#include "glm/ext/matrix_clip_space.hpp"
|
||||
|
||||
FreeCamera::FreeCamera(const glm::vec3 &pos, float pitch, float yaw, float aspectRatio) : _position(pos), _yaw(yaw), _pitch(pitch), _aspectRatio(aspectRatio) {
|
||||
update_camera_vectors();
|
||||
update_view();
|
||||
update_projection();
|
||||
}
|
||||
|
||||
const glm::mat4& FreeCamera::view() const {
|
||||
return _view;
|
||||
}
|
||||
|
||||
const glm::mat4& FreeCamera::projection() const {
|
||||
return _projection;
|
||||
}
|
||||
|
||||
const glm::vec3& FreeCamera::position() const {
|
||||
return _position;
|
||||
}
|
||||
|
||||
void FreeCamera::rotate_pitch(float offset) {
|
||||
_pitch += offset;
|
||||
|
||||
if (_pitch < MIN_PITCH) {
|
||||
_pitch = MIN_PITCH;
|
||||
}
|
||||
|
||||
if (_pitch > MAX_PITCH) {
|
||||
_pitch = MAX_PITCH;
|
||||
}
|
||||
|
||||
update_camera_vectors();
|
||||
update_view();
|
||||
}
|
||||
|
||||
void FreeCamera::rotate_yaw(float offset) {
|
||||
_yaw += offset;
|
||||
update_camera_vectors();
|
||||
update_view();
|
||||
}
|
||||
|
||||
void FreeCamera::move(CAMERA_MOVEMENT direction, float offset) {
|
||||
glm::vec3 finalDirection = {};
|
||||
|
||||
switch (direction) {
|
||||
case FORWARD:
|
||||
finalDirection = front;
|
||||
break;
|
||||
case BACKWARD:
|
||||
finalDirection = -front;
|
||||
break;
|
||||
case LEFT:
|
||||
finalDirection = -right;
|
||||
break;
|
||||
case RIGHT:
|
||||
finalDirection = right;
|
||||
break;
|
||||
case UP:
|
||||
finalDirection = up;
|
||||
break;
|
||||
case DOWN:
|
||||
finalDirection = -up;
|
||||
break;
|
||||
}
|
||||
|
||||
finalDirection = glm::normalize(finalDirection);
|
||||
_position += finalDirection * offset;
|
||||
_view = glm::lookAt(_position, _position + front, up);
|
||||
}
|
||||
|
||||
void FreeCamera::update_aspect_ratio(float aspectRatio) {
|
||||
_aspectRatio = aspectRatio;
|
||||
_projection = glm::perspective(glm::radians(75.f), _aspectRatio, 0.1f, 100.0f);
|
||||
}
|
||||
|
||||
void FreeCamera::update_camera_vectors() {
|
||||
front = {};
|
||||
front.x = cos(glm::radians(_yaw)) * cos(glm::radians(_pitch));
|
||||
front.y = sin(glm::radians(_pitch));
|
||||
front.z = sin(glm::radians(_yaw)) * cos(glm::radians(_pitch));
|
||||
|
||||
right = glm::normalize(glm::cross(front, WORLD_UP));
|
||||
up = glm::normalize(glm::cross(right, front));
|
||||
}
|
||||
|
||||
void FreeCamera::update_projection() {
|
||||
_projection = glm::perspective(glm::radians(75.f), _aspectRatio, 0.1f, 100.0f);
|
||||
}
|
||||
|
||||
void FreeCamera::update_view() {
|
||||
_view = glm::lookAt(_position, _position + front, up);
|
||||
}
|
||||
57
src/FreeCameraController.cpp
Normal file
57
src/FreeCameraController.cpp
Normal file
@@ -0,0 +1,57 @@
|
||||
//
|
||||
// Created by slinky on 5/11/26.
|
||||
//
|
||||
|
||||
#include "FreeCameraController.h"
|
||||
#include "InputManager.h"
|
||||
|
||||
void FreeCameraController::update(EditorContext &ctx) {
|
||||
checkInput = ctx.sceneViewerFocused;
|
||||
|
||||
if (!checkInput) return;
|
||||
|
||||
if (InputManager::check_action_performed("move_forward")) {
|
||||
const glm::vec3 translation = camera->front() * movement_speed;
|
||||
camera->translate(translation);
|
||||
}
|
||||
|
||||
if (InputManager::check_action_performed("move_backward")) {
|
||||
const glm::vec3 translation = camera->front() * -movement_speed;
|
||||
camera->translate(translation);
|
||||
}
|
||||
|
||||
if (InputManager::check_action_performed("move_left")) {
|
||||
const glm::vec3 translation = camera->right() * -movement_speed;
|
||||
camera->translate(translation);
|
||||
}
|
||||
|
||||
if (InputManager::check_action_performed("move_right")) {
|
||||
const glm::vec3 translation = camera->right() * movement_speed;
|
||||
camera->translate(translation);
|
||||
}
|
||||
|
||||
if (InputManager::check_action_performed("move_up")) {
|
||||
const glm::vec3 translation = camera->up() * movement_speed;
|
||||
camera->translate(translation);
|
||||
}
|
||||
|
||||
if (InputManager::check_action_performed("move_down")) {
|
||||
const glm::vec3 translation = camera->up() * -movement_speed;
|
||||
camera->translate(translation);
|
||||
}
|
||||
}
|
||||
|
||||
void FreeCameraController::on_mouse_delta(float xoff, float yoff) const {
|
||||
if (!checkInput) return;
|
||||
|
||||
camera->rotate_pitch(yoff * look_sensitivity);
|
||||
camera->rotate_yaw(xoff * look_sensitivity);
|
||||
}
|
||||
|
||||
void FreeCameraController::set_camera(Camera& c) {
|
||||
camera = &c;
|
||||
}
|
||||
|
||||
void FreeCameraController::release_camera() {
|
||||
camera = nullptr;
|
||||
}
|
||||
30
src/FreeCameraController.h
Normal file
30
src/FreeCameraController.h
Normal file
@@ -0,0 +1,30 @@
|
||||
//
|
||||
// Created by slinky on 5/11/26.
|
||||
//
|
||||
|
||||
#ifndef B_ENGINE_FREECAMERACONTROLLER_H
|
||||
#define B_ENGINE_FREECAMERACONTROLLER_H
|
||||
|
||||
#include "Camera.h"
|
||||
#include "EditorContext.h"
|
||||
|
||||
class FreeCameraController {
|
||||
public:
|
||||
FreeCameraController() = default;
|
||||
|
||||
void update(EditorContext& ctx);
|
||||
void on_mouse_delta(float xoff, float yoff) const;
|
||||
|
||||
void set_camera(Camera& c);
|
||||
void release_camera();
|
||||
|
||||
float movement_speed = .01;
|
||||
float look_sensitivity = .01;
|
||||
private:
|
||||
Camera* camera {nullptr};
|
||||
|
||||
bool checkInput = false;
|
||||
};
|
||||
|
||||
|
||||
#endif //B_ENGINE_FREECAMERACONTROLLER_H
|
||||
@@ -18,6 +18,10 @@ entt::entity Scene::create_game_object() {
|
||||
}
|
||||
|
||||
entt::entity Scene::create_game_object(entt::entity parent) {
|
||||
if (parent == entt::null) {
|
||||
parent = root;
|
||||
}
|
||||
|
||||
const entt::entity entity = _registry.create();
|
||||
attach_component<Components::Transform>(entity);
|
||||
attach_component<Components::Relationship>(entity);
|
||||
@@ -30,10 +34,72 @@ entt::entity Scene::create_game_object(entt::entity parent) {
|
||||
}
|
||||
|
||||
void Scene::update_transforms() {
|
||||
|
||||
std::vector<entt::entity> children = get_children(root);
|
||||
for (auto child: children) {
|
||||
update_transforms(child);
|
||||
}
|
||||
}
|
||||
|
||||
void Scene::draw_scene(const ShaderProgram *program) {
|
||||
void Scene::update_transforms(entt::entity entity) {
|
||||
auto& transform = fetch_component<Components::Transform>(entity);
|
||||
|
||||
if (!transform.dirty)
|
||||
return;
|
||||
|
||||
glm::mat4 translation =
|
||||
glm::translate(glm::mat4(1.0f), transform.position);
|
||||
|
||||
glm::mat4 rotationX =
|
||||
glm::rotate(glm::mat4(1.0f), transform.rotation.x, glm::vec3(1,0,0));
|
||||
|
||||
glm::mat4 rotationY =
|
||||
glm::rotate(glm::mat4(1.0f), transform.rotation.y, glm::vec3(0,1,0));
|
||||
|
||||
glm::mat4 rotationZ =
|
||||
glm::rotate(glm::mat4(1.0f), transform.rotation.z, glm::vec3(0,0,1));
|
||||
|
||||
glm::mat4 scaling =
|
||||
glm::scale(glm::mat4(1.0f), transform.scale);
|
||||
|
||||
glm::mat4 localModel =
|
||||
translation *
|
||||
rotationZ *
|
||||
rotationY *
|
||||
rotationX *
|
||||
scaling;
|
||||
|
||||
auto& rel = fetch_component<Components::Relationship>(entity);
|
||||
if (rel.parent != entt::null) {
|
||||
auto& parentTransform = fetch_component<Components::Transform>(rel.parent);
|
||||
transform.model = parentTransform.model * localModel;
|
||||
} else {
|
||||
transform.model = localModel;
|
||||
}
|
||||
|
||||
transform.dirty = false;
|
||||
|
||||
std::vector<entt::entity> children = get_children(entity);
|
||||
for (auto child: children) {
|
||||
update_transforms(child);
|
||||
}
|
||||
}
|
||||
|
||||
void Scene::draw_scene(ShaderProgram *program) {
|
||||
program->bind();
|
||||
program->setMat4("projection", activeCamera->projection());
|
||||
program->setMat4("view", activeCamera->view());
|
||||
program->setVec3("viewPosition", activeCamera->position());
|
||||
|
||||
auto dirLights = _registry.view<Components::DirectionalLight>();
|
||||
for (const auto e : dirLights) {
|
||||
auto dirLight = dirLights.get<Components::DirectionalLight>(e);
|
||||
program->setVec3("lightDirection", dirLight.direction);
|
||||
program->setVec3("lightAmbient", dirLight.ambient);
|
||||
program->setVec3("lightDiffuse", dirLight.diffuse);
|
||||
program->setVec3("lightSpecular", dirLight.specular);
|
||||
break;
|
||||
}
|
||||
|
||||
auto view = _registry.view<Components::Transform, Components::Drawable>();
|
||||
for (const auto e : view) {
|
||||
const auto& transform = view.get<Components::Transform>(e);
|
||||
@@ -108,3 +174,7 @@ void Scene::add_child(entt::entity parent, entt::entity child) {
|
||||
childRelationship.next_sibling = entt::null;
|
||||
childRelationship.parent = parent;
|
||||
}
|
||||
|
||||
void Scene::set_active_camera(Camera *camera) {
|
||||
activeCamera = camera;
|
||||
}
|
||||
|
||||
13
src/Scene.h
13
src/Scene.h
@@ -5,6 +5,7 @@
|
||||
#ifndef B_ENGINE_SCENE_H
|
||||
#define B_ENGINE_SCENE_H
|
||||
|
||||
#include "Camera.h"
|
||||
#include "ShaderProgram.h"
|
||||
#include "entt/entt.hpp"
|
||||
|
||||
@@ -31,16 +32,26 @@ public:
|
||||
return _registry.get<T>(e);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool has_component(entt::entity e) {
|
||||
return _registry.all_of<T>(e);
|
||||
}
|
||||
|
||||
void update_transforms();
|
||||
void draw_scene(const ShaderProgram* program);
|
||||
void draw_scene(ShaderProgram* program);
|
||||
|
||||
[[nodiscard]] entt::entity get_root() const;
|
||||
[[nodiscard]] std::vector<entt::entity> get_children(entt::entity parent);
|
||||
|
||||
void set_active_camera(Camera *camera);
|
||||
private:
|
||||
entt::registry _registry{};
|
||||
entt::entity root{entt::null};
|
||||
|
||||
void add_child(entt::entity parent, entt::entity child);
|
||||
void update_transforms(entt::entity parent);
|
||||
|
||||
Camera* activeCamera {nullptr};
|
||||
};
|
||||
|
||||
#endif //B_ENGINE_SCENE_H
|
||||
88
src/UIManager.cpp
Normal file
88
src/UIManager.cpp
Normal file
@@ -0,0 +1,88 @@
|
||||
//
|
||||
// Created by slinky on 5/11/26.
|
||||
//
|
||||
|
||||
#include "UIManager.h"
|
||||
|
||||
#include <ranges>
|
||||
|
||||
#include "imgui.h"
|
||||
#include "imgui_impl_glfw.h"
|
||||
#include "imgui_impl_opengl3.h"
|
||||
#include "imgui_internal.h"
|
||||
|
||||
std::unordered_map<std::string, std::shared_ptr<IUIPanel>> UIManager::uiPanels {};
|
||||
GLFWwindow* UIManager::window {nullptr};
|
||||
bool UIManager::updateDockspace {true};
|
||||
|
||||
void UIManager::init(GLFWwindow* _window) {
|
||||
IMGUI_CHECKVERSION();
|
||||
ImGui::CreateContext();
|
||||
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
io.ConfigFlags |= ImGuiConfigFlags_DockingEnable;
|
||||
|
||||
ImGui::StyleColorsDark();
|
||||
ImGui_ImplGlfw_InitForOpenGL(_window, true);
|
||||
ImGui_ImplOpenGL3_Init("#version 400");
|
||||
|
||||
window = _window;
|
||||
}
|
||||
|
||||
void UIManager::update(EditorContext& ctx) {
|
||||
ImGui_ImplOpenGL3_NewFrame();
|
||||
ImGui_ImplGlfw_NewFrame();
|
||||
|
||||
ImGui::NewFrame();
|
||||
{
|
||||
ImGuiWindowFlags window_flags = ImGuiWindowFlags_MenuBar | ImGuiWindowFlags_NoDocking;
|
||||
ImGuiViewport* viewport = ImGui::GetMainViewport();
|
||||
ImGui::SetNextWindowPos(viewport->Pos);
|
||||
ImGui::SetNextWindowSize(viewport->Size);
|
||||
ImGui::SetNextWindowViewport(viewport->ID);
|
||||
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f);
|
||||
window_flags |= ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove;
|
||||
window_flags |= ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoNavFocus;
|
||||
|
||||
ImGui::Begin("b_engine dockspace", nullptr, window_flags);
|
||||
ImGui::PopStyleVar(2);
|
||||
|
||||
ImGuiID dockspace_id = ImGui::GetID("bengine_dockspace");
|
||||
ImGui::DockSpace(dockspace_id, ImVec2(0.0f, 0.0f), ImGuiDockNodeFlags_None);
|
||||
|
||||
if (updateDockspace) {
|
||||
updateDockspace = false;
|
||||
|
||||
ImGui::DockBuilderRemoveNode(dockspace_id);
|
||||
ImGui::DockBuilderAddNode(dockspace_id, ImGuiDockNodeFlags_DockSpace);
|
||||
ImGui::DockBuilderSetNodeSize(dockspace_id, viewport->Size);
|
||||
|
||||
ImGuiID dock_main_id = dockspace_id;
|
||||
|
||||
const ImGuiID dock_id_left =
|
||||
ImGui::DockBuilderSplitNode(dock_main_id, ImGuiDir_Left, 0.2f, nullptr, &dock_main_id);
|
||||
const ImGuiID dock_id_right =
|
||||
ImGui::DockBuilderSplitNode(dock_main_id, ImGuiDir_Right, 0.25f, nullptr, &dock_main_id);
|
||||
|
||||
ImGui::DockBuilderDockWindow("Hierarchy", dock_id_left);
|
||||
ImGui::DockBuilderDockWindow("Inspector", dock_id_right);
|
||||
ImGui::DockBuilderDockWindow("Scene", dock_main_id);
|
||||
|
||||
ImGui::DockBuilderFinish(dockspace_id);
|
||||
}
|
||||
|
||||
for (const auto &panel: uiPanels | std::views::values) {
|
||||
if (!panel) continue;
|
||||
panel->update(ctx);
|
||||
}
|
||||
|
||||
ImGui::End(); // dockspace
|
||||
}
|
||||
ImGui::Render();
|
||||
}
|
||||
|
||||
void UIManager::draw() {
|
||||
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
|
||||
}
|
||||
42
src/UIManager.h
Normal file
42
src/UIManager.h
Normal file
@@ -0,0 +1,42 @@
|
||||
//
|
||||
// Created by slinky on 5/11/26.
|
||||
//
|
||||
|
||||
#ifndef B_ENGINE_UIMANAGER_H
|
||||
#define B_ENGINE_UIMANAGER_H
|
||||
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
#include <functional>
|
||||
|
||||
#include "EditorContext.h"
|
||||
#include "GLFW/glfw3.h"
|
||||
#include "ui/IUIPanel.h"
|
||||
|
||||
class UIManager {
|
||||
public:
|
||||
static void init(GLFWwindow* _window);
|
||||
static void update(EditorContext& ctx);
|
||||
static void draw();
|
||||
|
||||
template<typename T = IUIPanel, typename... Args>
|
||||
static T* add_ui_panel(std::string_view id, Args &&... args) {
|
||||
uiPanels[std::string(id)] = std::make_shared<T>(std::forward<Args>(args)...);
|
||||
return dynamic_cast<T*>(uiPanels[std::string(id)].get());
|
||||
}
|
||||
|
||||
static std::shared_ptr<IUIPanel> get_ui_panel(std::string_view id) {
|
||||
if (const auto it = uiPanels.find(std::string(id)); it != uiPanels.end()) {
|
||||
return it->second;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
private:
|
||||
static bool updateDockspace;
|
||||
static GLFWwindow* window;
|
||||
static std::unordered_map<std::string, std::shared_ptr<IUIPanel>> uiPanels;
|
||||
};
|
||||
|
||||
|
||||
#endif //B_ENGINE_UIMANAGER_H
|
||||
245
src/main.cpp
245
src/main.cpp
@@ -5,29 +5,26 @@
|
||||
|
||||
#include "glm/glm.hpp"
|
||||
#include "glm/ext/matrix_clip_space.hpp"
|
||||
#include "glm/ext/matrix_transform.hpp"
|
||||
|
||||
#include "entt/entt.hpp"
|
||||
|
||||
#define STB_IMAGE_IMPLEMENTATION
|
||||
#include "Components.h"
|
||||
#include "FreeCamera.h"
|
||||
#include "imgui.h"
|
||||
#include "imgui_impl_glfw.h"
|
||||
#include "imgui_impl_opengl3.h"
|
||||
#include "imgui_internal.h"
|
||||
#include "InputManager.h"
|
||||
#include "stb_image.h"
|
||||
|
||||
#include "Model.h"
|
||||
#include "Camera.h"
|
||||
#include "Components.h"
|
||||
#include "EditorContext.h"
|
||||
#include "FreeCameraController.h"
|
||||
#include "InputManager.h"
|
||||
#include "ModelManager.h"
|
||||
#include "Scene.h"
|
||||
#include "ShaderManager.h"
|
||||
#include "ShaderProgram.h"
|
||||
#include "Texture.h"
|
||||
#include "TextureManager.h"
|
||||
|
||||
#include "fmt/base.h"
|
||||
#include "UIManager.h"
|
||||
#include "ui/UIEntityInspector.h"
|
||||
#include "ui/UIMenuBar.h"
|
||||
#include "ui/UISceneGraph.h"
|
||||
#include "ui/UISceneViewer.h"
|
||||
|
||||
GLFWwindow* gWindow = nullptr;
|
||||
int gWindowWidth = 1920;
|
||||
@@ -40,11 +37,10 @@ float gAspectRatio = 0.f;
|
||||
|
||||
float gMouseSensitivity = 0.1f;
|
||||
|
||||
FreeCamera gCamera {};
|
||||
|
||||
Camera gCamera {};
|
||||
Scene gScene{};
|
||||
|
||||
bool gUiFirstRender = true;
|
||||
EditorContext gEditorCtx{};
|
||||
FreeCameraController gCameraController{};
|
||||
|
||||
void glfw_error_callback(int error, const char* description);
|
||||
void glfw_key_callback(GLFWwindow* window, int key, int scancode, int action, int mods);
|
||||
@@ -53,14 +49,15 @@ void glfw_framebuffer_size_callback(GLFWwindow* window, int width, int height);
|
||||
void init_glfw();
|
||||
void create_main_window();
|
||||
void load_gl();
|
||||
void load_imgui();
|
||||
void init_camera();
|
||||
void init_scene();
|
||||
void load_default_textures();
|
||||
void load_shaders();
|
||||
void load_inputs();
|
||||
void load_default_models();
|
||||
void loop();
|
||||
void draw_ui();
|
||||
|
||||
void create_entity(entt::entity parent);
|
||||
|
||||
auto xAxis = glm::vec3{1.f, 0.f, 0.f};
|
||||
auto yAxis = glm::vec3{0.f, 1.f, 0.f};
|
||||
@@ -75,10 +72,6 @@ int main() {
|
||||
|
||||
load_gl();
|
||||
|
||||
load_imgui();
|
||||
|
||||
init_camera();
|
||||
|
||||
stbi_set_flip_vertically_on_load(true);
|
||||
|
||||
load_default_textures();
|
||||
@@ -89,15 +82,26 @@ int main() {
|
||||
|
||||
load_default_models();
|
||||
|
||||
entt::entity dirLight = gScene.create_game_object();
|
||||
auto& [direction, ambient, diffuse, specular] = gScene.attach_component<Components::DirectionalLight>(dirLight);
|
||||
direction = {1, -1, 1};
|
||||
ambient = {0.3f, 0.3f, 0.3f};
|
||||
diffuse = {0.5f, 0.5f, 0.5f};
|
||||
specular = {1.0f, 1.0f, 1.0f};
|
||||
UIManager::init(gWindow);
|
||||
UIMenuBar* menuBar = UIManager::add_ui_panel<UIMenuBar>("menu_bar");
|
||||
menuBar->create_entity = create_entity;
|
||||
UIManager::add_ui_panel<UISceneViewer>("scene_viewer");
|
||||
UIManager::add_ui_panel<UISceneGraph>("scene_graph");
|
||||
UIManager::add_ui_panel<UIEntityInspector>("entity_inspector");
|
||||
|
||||
auto& tag = gScene.fetch_component<Components::Tag>(dirLight);
|
||||
tag.name = "directional light";
|
||||
init_camera();
|
||||
init_scene();
|
||||
|
||||
gCameraController.set_camera(gCamera);
|
||||
gCameraController.look_sensitivity = 0.2;
|
||||
gCameraController.movement_speed = 0.2;
|
||||
InputManager::add_mouse_listener([](float xoff, float yoff) {
|
||||
gCameraController.on_mouse_delta(xoff, yoff);
|
||||
});
|
||||
|
||||
gEditorCtx.scene = &gScene;
|
||||
gEditorCtx.window = gWindow;
|
||||
gEditorCtx.selectedEntity = entt::null;
|
||||
|
||||
loop();
|
||||
|
||||
@@ -174,27 +178,23 @@ void load_gl()
|
||||
glEnable(GL_MULTISAMPLE);
|
||||
}
|
||||
|
||||
void load_imgui() {
|
||||
IMGUI_CHECKVERSION();
|
||||
ImGui::CreateContext();
|
||||
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
io.ConfigFlags |= ImGuiConfigFlags_DockingEnable;
|
||||
|
||||
ImGui::StyleColorsDark();
|
||||
ImGui_ImplGlfw_InitForOpenGL(gWindow, true);
|
||||
ImGui_ImplOpenGL3_Init("#version 330");
|
||||
}
|
||||
|
||||
void init_camera() {
|
||||
gCamera = {{5.f, 5.f, 5.f}, 0, 0, gAspectRatio};
|
||||
gCamera = {{10.f, 10.f, 10.f}, 0, 0, gAspectRatio};
|
||||
gCamera.rotate_yaw(-135.f);
|
||||
gCamera.rotate_pitch(-35.f);
|
||||
}
|
||||
|
||||
InputManager::add_mouse_listener([](float xoff, float yoff) {
|
||||
gCamera.rotate_yaw(xoff * gMouseSensitivity);
|
||||
gCamera.rotate_pitch(yoff * gMouseSensitivity);
|
||||
});
|
||||
void init_scene() {
|
||||
gScene.set_active_camera(&gCamera);
|
||||
|
||||
const auto defaultLight = gScene.create_game_object();
|
||||
auto& [name] = gScene.fetch_component<Components::Tag>(defaultLight);
|
||||
name = "Directional Light";
|
||||
auto& [direction, ambient, diffuse, specular] = gScene.attach_component<Components::DirectionalLight>(defaultLight);
|
||||
direction = glm::vec3(-1, -1, -1);
|
||||
ambient = glm::vec3(0.3);
|
||||
diffuse = glm::vec3(0.7);
|
||||
specular = glm::vec3(1.0);
|
||||
}
|
||||
|
||||
void load_default_textures() {
|
||||
@@ -224,7 +224,6 @@ void load_inputs() {
|
||||
InputManager::generate_input_action("move_left", {{GLFW_KEY_A, KEY_DOWN}});
|
||||
InputManager::generate_input_action("move_up", {{GLFW_KEY_SPACE, KEY_DOWN}});
|
||||
InputManager::generate_input_action("move_down", {{GLFW_KEY_LEFT_SHIFT, KEY_DOWN}});
|
||||
InputManager::generate_input_action("exit_application", {{GLFW_KEY_ESCAPE, KEY_DOWN}});
|
||||
}
|
||||
|
||||
void load_default_models() {
|
||||
@@ -236,147 +235,47 @@ void load_default_models() {
|
||||
|
||||
void loop() {
|
||||
while (!glfwWindowShouldClose(gWindow)) {
|
||||
// input
|
||||
glfwPollEvents();
|
||||
|
||||
// check inputs
|
||||
/*if (InputManager::check_action_performed("exit_application")) {
|
||||
glfwSetWindowShouldClose(gWindow, true);
|
||||
continue;
|
||||
}*/
|
||||
// update
|
||||
UIManager::update(gEditorCtx);
|
||||
gCameraController.update(gEditorCtx);
|
||||
|
||||
if (InputManager::check_action_performed("move_forward")) {
|
||||
gCamera.move(FORWARD, .05);
|
||||
if (gCamera.aspect_ratio() != gEditorCtx.viewportAspectRatio) {
|
||||
gCamera.update_aspect_ratio(gEditorCtx.viewportAspectRatio);
|
||||
}
|
||||
|
||||
if (InputManager::check_action_performed("move_backward")) {
|
||||
gCamera.move(BACKWARD, .05);
|
||||
gScene.update_transforms();
|
||||
|
||||
const auto* uiScenePanel = dynamic_cast<UISceneViewer*>(UIManager::get_ui_panel("scene_viewer").get());
|
||||
if (uiScenePanel) {
|
||||
uiScenePanel->bind_fbo();
|
||||
}
|
||||
|
||||
if (InputManager::check_action_performed("move_left")) {
|
||||
gCamera.move(LEFT, .05);
|
||||
}
|
||||
|
||||
if (InputManager::check_action_performed("move_right")) {
|
||||
gCamera.move(RIGHT, .05);
|
||||
}
|
||||
|
||||
if (InputManager::check_action_performed("move_up")) {
|
||||
gCamera.move(UP, .05);
|
||||
}
|
||||
|
||||
if (InputManager::check_action_performed("move_down")) {
|
||||
gCamera.move(DOWN, .05);
|
||||
}
|
||||
|
||||
draw_ui();
|
||||
|
||||
// gl frame prep
|
||||
// draw
|
||||
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
|
||||
|
||||
const auto shader = ShaderManager::shaders["phong_shader"];
|
||||
shader->bind();
|
||||
shader->setMat4("projection", gCamera.projection());
|
||||
shader->setMat4("view", gCamera.view());
|
||||
shader->setVec3("viewPosition", gCamera.position());
|
||||
|
||||
gScene.draw_scene(shader.get());
|
||||
|
||||
glBindVertexArray(0);
|
||||
ShaderProgram::unbind();
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
|
||||
// gl end frame stuff
|
||||
UIManager::draw();
|
||||
|
||||
// end frame
|
||||
glfwSwapBuffers(gWindow);
|
||||
}
|
||||
}
|
||||
|
||||
void draw_ui() {
|
||||
ImGui_ImplOpenGL3_NewFrame();
|
||||
ImGui_ImplGlfw_NewFrame();
|
||||
ImGui::NewFrame();
|
||||
{
|
||||
ImGuiWindowFlags window_flags = ImGuiWindowFlags_MenuBar | ImGuiWindowFlags_NoDocking;
|
||||
ImGuiViewport* viewport = ImGui::GetMainViewport();
|
||||
ImGui::SetNextWindowPos(viewport->Pos);
|
||||
ImGui::SetNextWindowSize(viewport->Size);
|
||||
ImGui::SetNextWindowViewport(viewport->ID);
|
||||
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f);
|
||||
window_flags |= ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove;
|
||||
window_flags |= ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoNavFocus;
|
||||
|
||||
ImGui::Begin("b_engine dockspace", nullptr, window_flags);
|
||||
ImGui::PopStyleVar(2);
|
||||
|
||||
ImGuiID dockspace_id = ImGui::GetID("bengine_dockspace");
|
||||
ImGui::DockSpace(dockspace_id, ImVec2(0.0f, 0.0f), ImGuiDockNodeFlags_None);
|
||||
|
||||
if (gUiFirstRender) {
|
||||
gUiFirstRender = false;
|
||||
|
||||
ImGui::DockBuilderRemoveNode(dockspace_id); // Clear any previous layout
|
||||
ImGui::DockBuilderAddNode(dockspace_id, ImGuiDockNodeFlags_DockSpace);
|
||||
ImGui::DockBuilderSetNodeSize(dockspace_id, viewport->Size);
|
||||
|
||||
ImGuiID dock_main_id = dockspace_id; // The center remains for the game view
|
||||
|
||||
const ImGuiID dock_id_left =
|
||||
ImGui::DockBuilderSplitNode(dock_main_id, ImGuiDir_Left, 0.2f, nullptr, &dock_main_id);
|
||||
const ImGuiID dock_id_right =
|
||||
ImGui::DockBuilderSplitNode(dock_main_id, ImGuiDir_Right, 0.25f, nullptr, &dock_main_id);
|
||||
|
||||
// Assign your windows to these specific dock IDs
|
||||
ImGui::DockBuilderDockWindow("Hierarchy", dock_id_left);
|
||||
ImGui::DockBuilderDockWindow("Inspector", dock_id_right);
|
||||
ImGui::DockBuilderDockWindow("Scene", dock_main_id);
|
||||
|
||||
ImGui::DockBuilderFinish(dockspace_id);
|
||||
}
|
||||
|
||||
ImGui::Begin("Hierarchy");
|
||||
ImGui::Text("List of GameObjects...");
|
||||
ImGui::End();
|
||||
|
||||
ImGui::Begin("Inspector");
|
||||
ImGui::Text("Component Properties...");
|
||||
ImGui::End();
|
||||
|
||||
ImGui::Begin("Scene");
|
||||
ImGui::Text("This is where your 3D view goes!");
|
||||
ImGui::End();
|
||||
|
||||
if (ImGui::BeginMainMenuBar()) {
|
||||
if (ImGui::BeginMenu("File")) {
|
||||
if (ImGui::MenuItem("New Scene")) {
|
||||
/* Handle New File logic */
|
||||
}
|
||||
if (ImGui::MenuItem("Open Scene")) {
|
||||
/* Handle Open logic */
|
||||
}
|
||||
ImGui::Separator(); // Adds a visual line between sections
|
||||
if (ImGui::MenuItem("Save", "Ctrl+S")) {
|
||||
/* Handle Save logic */
|
||||
}
|
||||
if (ImGui::MenuItem("Exit", "Alt+F4")) {
|
||||
glfwSetWindowShouldClose(gWindow, true);
|
||||
}
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
|
||||
if (ImGui::BeginMenu("Scene")) {
|
||||
if (ImGui::MenuItem("New Scene Object")) {
|
||||
gScene.create_game_object();
|
||||
// Do other stuff here, open this entity in the entity editor, etc...
|
||||
}
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
ImGui::EndMainMenuBar();
|
||||
}
|
||||
|
||||
ImGui::End();
|
||||
}
|
||||
ImGui::Render();
|
||||
}
|
||||
void create_entity(entt::entity parent) {
|
||||
auto entity = gScene.create_game_object(parent);
|
||||
auto& drawable = gScene.attach_component<Components::Drawable>(entity);
|
||||
drawable.model = ModelManager::models["vette"];
|
||||
auto& transform = gScene.fetch_component<Components::Transform>(entity);
|
||||
transform.scale = {0.1, 0.1, 0.1};
|
||||
transform.dirty = true;
|
||||
}
|
||||
|
||||
18
src/ui/IUIPanel.h
Normal file
18
src/ui/IUIPanel.h
Normal file
@@ -0,0 +1,18 @@
|
||||
//
|
||||
// Created by slinky on 5/11/26.
|
||||
//
|
||||
|
||||
#ifndef B_ENGINE_IUIPANEL_H
|
||||
#define B_ENGINE_IUIPANEL_H
|
||||
|
||||
#include "EditorContext.h"
|
||||
|
||||
class IUIPanel {
|
||||
public:
|
||||
virtual ~IUIPanel() = default;
|
||||
virtual void update(EditorContext& context) = 0;
|
||||
bool IsOpen = true;
|
||||
bool HasFocus = true;
|
||||
};
|
||||
|
||||
#endif //B_ENGINE_IUIPANEL_H
|
||||
63
src/ui/UIEntityInspector.cpp
Normal file
63
src/ui/UIEntityInspector.cpp
Normal file
@@ -0,0 +1,63 @@
|
||||
//
|
||||
// Created by slinky on 5/11/26.
|
||||
//
|
||||
|
||||
#include "UIEntityInspector.h"
|
||||
|
||||
#include "Components.h"
|
||||
#include "imgui.h"
|
||||
#include "glm/gtc/type_ptr.hpp"
|
||||
|
||||
void UIEntityInspector::update(EditorContext &ctx) {
|
||||
ImGui::Begin("Inspector");
|
||||
|
||||
if (ctx.selectedEntity == entt::null)
|
||||
{
|
||||
ImGui::TextDisabled("No entity selected");
|
||||
ImGui::End();
|
||||
return;
|
||||
}
|
||||
|
||||
Scene* scene = ctx.scene;
|
||||
auto entity = ctx.selectedEntity;
|
||||
|
||||
if (scene->has_component<Components::Tag>(entity))
|
||||
{
|
||||
auto& [name] = scene->fetch_component<Components::Tag>(entity);
|
||||
|
||||
char buffer[256]{};
|
||||
memset(buffer, 0, sizeof(buffer));
|
||||
|
||||
strncpy(buffer, name.c_str(), sizeof(buffer) - 1);
|
||||
|
||||
if (ImGui::InputText("Tag", buffer, sizeof(buffer)))
|
||||
{
|
||||
name = std::string(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
if (scene->has_component<Components::Transform>(entity))
|
||||
{
|
||||
auto& transform = scene->fetch_component<Components::Transform>(entity);
|
||||
ImGui::Text("Transform");
|
||||
|
||||
if (ImGui::DragFloat3("Position", glm::value_ptr(transform.position), 0.1f))
|
||||
{
|
||||
transform.dirty = true;
|
||||
}
|
||||
|
||||
if (ImGui::DragFloat3("Rotation", glm::value_ptr(transform.rotation), 0.05f))
|
||||
{
|
||||
transform.dirty = true;
|
||||
}
|
||||
|
||||
if (ImGui::DragFloat3("Scale", glm::value_ptr(transform.scale), 0.1f))
|
||||
{
|
||||
transform.dirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::End();
|
||||
}
|
||||
17
src/ui/UIEntityInspector.h
Normal file
17
src/ui/UIEntityInspector.h
Normal file
@@ -0,0 +1,17 @@
|
||||
//
|
||||
// Created by slinky on 5/11/26.
|
||||
//
|
||||
|
||||
#ifndef B_ENGINE_UIENTITYINSPECTOR_H
|
||||
#define B_ENGINE_UIENTITYINSPECTOR_H
|
||||
|
||||
#include "EditorContext.h"
|
||||
#include "IUIPanel.h"
|
||||
|
||||
class UIEntityInspector : public IUIPanel {
|
||||
public:
|
||||
void update(EditorContext& ctx) override;
|
||||
};
|
||||
|
||||
|
||||
#endif //B_ENGINE_UIENTITYINSPECTOR_H
|
||||
37
src/ui/UIMenuBar.cpp
Normal file
37
src/ui/UIMenuBar.cpp
Normal file
@@ -0,0 +1,37 @@
|
||||
//
|
||||
// Created by slinky on 5/11/26.
|
||||
//
|
||||
|
||||
#include "UIMenuBar.h"
|
||||
#include "UIManager.h"
|
||||
|
||||
#include "imgui.h"
|
||||
#include "UISceneGraph.h"
|
||||
|
||||
void UIMenuBar::update(EditorContext &context) {
|
||||
if (ImGui::BeginMainMenuBar()) {
|
||||
if (ImGui::BeginMenu("File")) {
|
||||
if (ImGui::MenuItem("New Scene")) {
|
||||
}
|
||||
if (ImGui::MenuItem("Open Scene")) {
|
||||
}
|
||||
ImGui::Separator(); // Adds a visual line between sections
|
||||
if (ImGui::MenuItem("Save", "Ctrl+S")) {
|
||||
}
|
||||
if (ImGui::MenuItem("Exit", "Alt+F4")) {
|
||||
}
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
|
||||
if (ImGui::BeginMenu("Scene")) {
|
||||
if (ImGui::MenuItem("New Scene Object")) {
|
||||
const auto* sceneGraph = dynamic_cast<UISceneGraph*>(UIManager::get_ui_panel("scene_graph").get());
|
||||
if (sceneGraph) {
|
||||
create_entity(context.selectedEntity);
|
||||
}
|
||||
}
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
ImGui::EndMainMenuBar();
|
||||
}
|
||||
}
|
||||
18
src/ui/UIMenuBar.h
Normal file
18
src/ui/UIMenuBar.h
Normal file
@@ -0,0 +1,18 @@
|
||||
//
|
||||
// Created by slinky on 5/11/26.
|
||||
//
|
||||
|
||||
#ifndef B_ENGINE_UIMENUBAR_H
|
||||
#define B_ENGINE_UIMENUBAR_H
|
||||
|
||||
#include "IUIPanel.h"
|
||||
|
||||
class UIMenuBar : public IUIPanel {
|
||||
public:
|
||||
void update(EditorContext &context) override;
|
||||
|
||||
std::function<void(entt::entity)> create_entity;
|
||||
};
|
||||
|
||||
|
||||
#endif //B_ENGINE_UIMENUBAR_H
|
||||
53
src/ui/UISceneGraph.cpp
Normal file
53
src/ui/UISceneGraph.cpp
Normal file
@@ -0,0 +1,53 @@
|
||||
//
|
||||
// Created by slinky on 5/11/26.
|
||||
//
|
||||
|
||||
#include "UISceneGraph.h"
|
||||
|
||||
#include "Components.h"
|
||||
#include "imgui.h"
|
||||
#include "Scene.h"
|
||||
|
||||
void UISceneGraph::update(EditorContext &ctx) {
|
||||
ImGui::Begin("Hierarchy");
|
||||
|
||||
if (ImGui::IsWindowHovered() && ImGui::IsMouseClicked(ImGuiMouseButton_Left))
|
||||
{
|
||||
ctx.selectedEntity = entt::null;
|
||||
}
|
||||
|
||||
Scene* scene = ctx.scene;
|
||||
if (!scene) return;
|
||||
|
||||
draw_children(scene->get_root(), scene, ctx);
|
||||
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
void UISceneGraph::draw_children(entt::entity parent, Scene *scene, EditorContext& ctx) {
|
||||
std::vector<entt::entity> children = scene->get_children(parent);
|
||||
for (const auto e : children) {
|
||||
const auto& rel = scene->fetch_component<Components::Relationship>(e);
|
||||
ImGuiTreeNodeFlags flags =
|
||||
ImGuiTreeNodeFlags_OpenOnArrow |
|
||||
ImGuiTreeNodeFlags_SpanFullWidth;
|
||||
|
||||
if (rel.first_child == entt::null) {
|
||||
flags |= ImGuiTreeNodeFlags_Leaf;
|
||||
}
|
||||
|
||||
const auto& [name] = scene->fetch_component<Components::Tag>(e);
|
||||
if (ImGui::TreeNodeEx((void*)(intptr_t)e, flags, "%s", name.data()))
|
||||
{
|
||||
draw_children(e, scene, ctx);
|
||||
ImGui::TreePop();
|
||||
}
|
||||
|
||||
if (ImGui::IsItemClicked())
|
||||
{
|
||||
ctx.selectedEntity = e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
20
src/ui/UISceneGraph.h
Normal file
20
src/ui/UISceneGraph.h
Normal file
@@ -0,0 +1,20 @@
|
||||
//
|
||||
// Created by slinky on 5/11/26.
|
||||
//
|
||||
|
||||
#ifndef B_ENGINE_UISCENEGRAPH_H
|
||||
#define B_ENGINE_UISCENEGRAPH_H
|
||||
|
||||
#include "IUIPanel.h"
|
||||
#include "Scene.h"
|
||||
|
||||
#include "entt/entt.hpp"
|
||||
|
||||
class UISceneGraph : public IUIPanel {
|
||||
public:
|
||||
void update(EditorContext& ctx) override;
|
||||
private:
|
||||
void draw_children(entt::entity e, Scene* scene, EditorContext& ctx);
|
||||
};
|
||||
|
||||
#endif //B_ENGINE_UISCENEGRAPH_H
|
||||
88
src/ui/UISceneViewer.cpp
Normal file
88
src/ui/UISceneViewer.cpp
Normal file
@@ -0,0 +1,88 @@
|
||||
//
|
||||
// Created by slinky on 5/11/26.
|
||||
//
|
||||
|
||||
#include "UISceneViewer.h"
|
||||
|
||||
#include "GLFW/glfw3.h"
|
||||
|
||||
UISceneViewer::UISceneViewer() {
|
||||
create_scene_framebuffer();
|
||||
resize_scene_framebuffer(1, 1);
|
||||
}
|
||||
|
||||
void UISceneViewer::update(EditorContext& ctx) {
|
||||
ImGui::Begin("Scene");
|
||||
|
||||
const ImVec2 viewportSize = ImGui::GetContentRegionAvail();
|
||||
if (viewportSize.x > 0.0f && viewportSize.y > 0.0f)
|
||||
{
|
||||
if (sceneViewportSize.x != viewportSize.x ||
|
||||
sceneViewportSize.y != viewportSize.y)
|
||||
{
|
||||
sceneViewportSize = viewportSize;
|
||||
resize_scene_framebuffer((int)sceneViewportSize.x, (int)sceneViewportSize.y);
|
||||
|
||||
ctx.viewportSize = {sceneViewportSize.x, sceneViewportSize.y};
|
||||
ctx.viewportAspectRatio = sceneViewportSize.x / sceneViewportSize.y;
|
||||
}
|
||||
|
||||
ImGui::Image(
|
||||
sceneRenderTexture,
|
||||
viewportSize,
|
||||
ImVec2(0, 1),
|
||||
ImVec2(1, 0)
|
||||
);
|
||||
|
||||
bool hovered = ImGui::IsItemHovered();
|
||||
if (hovered && ImGui::IsMouseClicked(ImGuiMouseButton_Left))
|
||||
{
|
||||
capturedMouse = true;
|
||||
ctx.sceneViewerFocused = true;
|
||||
|
||||
glfwSetInputMode(ctx.window,
|
||||
GLFW_CURSOR,
|
||||
GLFW_CURSOR_DISABLED);
|
||||
}
|
||||
|
||||
if (capturedMouse && ImGui::IsMouseReleased(ImGuiMouseButton_Right))
|
||||
{
|
||||
capturedMouse = false;
|
||||
ctx.sceneViewerFocused = false;
|
||||
|
||||
glfwSetInputMode(ctx.window,
|
||||
GLFW_CURSOR,
|
||||
GLFW_CURSOR_NORMAL);
|
||||
}
|
||||
}
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
void UISceneViewer::bind_fbo() const {
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
|
||||
}
|
||||
|
||||
void UISceneViewer::create_scene_framebuffer() {
|
||||
glGenFramebuffers(1, &fbo);
|
||||
resize_scene_framebuffer(1, 1);
|
||||
}
|
||||
|
||||
void UISceneViewer::resize_scene_framebuffer(int width, int height) {
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
|
||||
|
||||
glGenTextures(1, &sceneRenderTexture);
|
||||
glBindTexture(GL_TEXTURE_2D, sceneRenderTexture);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, sceneRenderTexture, 0);
|
||||
|
||||
glGenRenderbuffers(1, &rbo);
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, rbo);
|
||||
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, width, height);
|
||||
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, rbo);
|
||||
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, 0);
|
||||
}
|
||||
38
src/ui/UISceneViewer.h
Normal file
38
src/ui/UISceneViewer.h
Normal file
@@ -0,0 +1,38 @@
|
||||
//
|
||||
// Created by slinky on 5/11/26.
|
||||
//
|
||||
|
||||
#ifndef B_ENGINE_UISCENEVIEWER_H
|
||||
#define B_ENGINE_UISCENEVIEWER_H
|
||||
|
||||
#include "glad/gl.h"
|
||||
#include "imgui.h"
|
||||
#include "IUIPanel.h"
|
||||
|
||||
#include "Scene.h"
|
||||
#include "Camera.h"
|
||||
|
||||
class UISceneViewer : public IUIPanel {
|
||||
public:
|
||||
UISceneViewer();
|
||||
|
||||
void update(EditorContext& ctx) override;
|
||||
|
||||
void bind_fbo() const;
|
||||
private:
|
||||
ImVec2 sceneViewportSize = { 0.0f, 0.0f };
|
||||
GLuint sceneRenderTexture = 0;
|
||||
GLuint fbo = 0;
|
||||
GLuint rbo = 0;
|
||||
|
||||
Scene* scene = nullptr;
|
||||
Camera* camera = nullptr;
|
||||
|
||||
bool capturedMouse = false;
|
||||
|
||||
void create_scene_framebuffer();
|
||||
void resize_scene_framebuffer(int width, int height);
|
||||
};
|
||||
|
||||
|
||||
#endif //B_ENGINE_UISCENEVIEWER_H
|
||||
Reference in New Issue
Block a user