entity editor, scene graph viewer, scene viewer basics done. Can rotate, translate and scale selected models.
This commit is contained in:
@@ -15,7 +15,19 @@ add_subdirectory(_ThirdParty/glfw)
|
|||||||
add_subdirectory(_ThirdParty/spdlog)
|
add_subdirectory(_ThirdParty/spdlog)
|
||||||
|
|
||||||
set (ASSIMP_INSTALL OFF)
|
set (ASSIMP_INSTALL OFF)
|
||||||
|
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
||||||
|
find_path(UNZIP_PATH unzip.h PATHS
|
||||||
|
/usr/include
|
||||||
|
/usr/local/include)
|
||||||
|
|
||||||
|
if(NOT UNZIP_PATH)
|
||||||
|
include_directories(/usr/include/minizip)
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
add_subdirectory(_ThirdParty/assimp)
|
add_subdirectory(_ThirdParty/assimp)
|
||||||
|
target_compile_options(assimp PRIVATE
|
||||||
|
-Wno-unused-but-set-variable
|
||||||
|
)
|
||||||
|
|
||||||
add_subdirectory(_ThirdParty/glm)
|
add_subdirectory(_ThirdParty/glm)
|
||||||
|
|
||||||
@@ -44,3 +56,4 @@ add_custom_target(copy_resources
|
|||||||
|
|
||||||
target_link_libraries(${PROJECT_NAME} glfw spdlog assimp glm EnTT fmt)
|
target_link_libraries(${PROJECT_NAME} glfw spdlog assimp glm EnTT fmt)
|
||||||
target_include_directories(${PROJECT_NAME} PRIVATE ${ASSIMP_INCLUDE_INSTALL_DIR})
|
target_include_directories(${PROJECT_NAME} PRIVATE ${ASSIMP_INCLUDE_INSTALL_DIR})
|
||||||
|
target_include_directories(${PROJECT_NAME} PRIVATE src)
|
||||||
|
|||||||
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};
|
constexpr glm::vec3 WORLD_UP = {0.0f, 1.0f, 0.0f};
|
||||||
|
|
||||||
enum CAMERA_MOVEMENT {
|
class Camera {
|
||||||
FORWARD,
|
|
||||||
BACKWARD,
|
|
||||||
LEFT,
|
|
||||||
RIGHT,
|
|
||||||
UP,
|
|
||||||
DOWN,
|
|
||||||
};
|
|
||||||
|
|
||||||
class FreeCamera {
|
|
||||||
public:
|
public:
|
||||||
FreeCamera() = default;
|
Camera() = default;
|
||||||
FreeCamera(const glm::vec3 &pos, float pitch, float yaw, float aspectRatio);
|
Camera(const glm::vec3 &pos, float pitch, float yaw, float aspectRatio);
|
||||||
|
|
||||||
[[nodiscard]] const glm::mat4& projection() const;
|
[[nodiscard]] const glm::mat4& projection() const;
|
||||||
[[nodiscard]] const glm::mat4& view() const;
|
[[nodiscard]] const glm::mat4& view() const;
|
||||||
[[nodiscard]] const glm::vec3& position() 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 update_aspect_ratio(float aspectRatio);
|
||||||
void rotate_yaw(float offset);
|
void rotate_yaw(float offset);
|
||||||
void rotate_pitch(float offset);
|
void rotate_pitch(float offset);
|
||||||
|
|
||||||
void move(CAMERA_MOVEMENT direction, float offset);
|
void translate(const glm::vec3& direction);
|
||||||
private:
|
private:
|
||||||
void update_camera_vectors();
|
void update_camera_vectors();
|
||||||
void update_projection();
|
void update_projection();
|
||||||
@@ -42,9 +37,9 @@ private:
|
|||||||
|
|
||||||
glm::vec3 _position{};
|
glm::vec3 _position{};
|
||||||
|
|
||||||
glm::vec3 front{};
|
glm::vec3 _front{};
|
||||||
glm::vec3 up{};
|
glm::vec3 _up{};
|
||||||
glm::vec3 right{};
|
glm::vec3 _right{};
|
||||||
|
|
||||||
glm::mat4 _view{};
|
glm::mat4 _view{};
|
||||||
glm::mat4 _projection{};
|
glm::mat4 _projection{};
|
||||||
@@ -17,6 +17,7 @@ namespace Components {
|
|||||||
glm::vec3 rotation{};
|
glm::vec3 rotation{};
|
||||||
glm::vec3 scale{};
|
glm::vec3 scale{};
|
||||||
glm::mat4 model = glm::identity<glm::mat4>();
|
glm::mat4 model = glm::identity<glm::mat4>();
|
||||||
|
bool dirty {true};
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Relationship {
|
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) {
|
entt::entity Scene::create_game_object(entt::entity parent) {
|
||||||
|
if (parent == entt::null) {
|
||||||
|
parent = root;
|
||||||
|
}
|
||||||
|
|
||||||
const entt::entity entity = _registry.create();
|
const entt::entity entity = _registry.create();
|
||||||
attach_component<Components::Transform>(entity);
|
attach_component<Components::Transform>(entity);
|
||||||
attach_component<Components::Relationship>(entity);
|
attach_component<Components::Relationship>(entity);
|
||||||
@@ -30,10 +34,72 @@ entt::entity Scene::create_game_object(entt::entity parent) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Scene::update_transforms() {
|
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>();
|
auto view = _registry.view<Components::Transform, Components::Drawable>();
|
||||||
for (const auto e : view) {
|
for (const auto e : view) {
|
||||||
const auto& transform = view.get<Components::Transform>(e);
|
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.next_sibling = entt::null;
|
||||||
childRelationship.parent = parent;
|
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
|
#ifndef B_ENGINE_SCENE_H
|
||||||
#define B_ENGINE_SCENE_H
|
#define B_ENGINE_SCENE_H
|
||||||
|
|
||||||
|
#include "Camera.h"
|
||||||
#include "ShaderProgram.h"
|
#include "ShaderProgram.h"
|
||||||
#include "entt/entt.hpp"
|
#include "entt/entt.hpp"
|
||||||
|
|
||||||
@@ -31,16 +32,26 @@ public:
|
|||||||
return _registry.get<T>(e);
|
return _registry.get<T>(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
bool has_component(entt::entity e) {
|
||||||
|
return _registry.all_of<T>(e);
|
||||||
|
}
|
||||||
|
|
||||||
void update_transforms();
|
void update_transforms();
|
||||||
void draw_scene(const ShaderProgram* program);
|
void draw_scene(ShaderProgram* program);
|
||||||
|
|
||||||
[[nodiscard]] entt::entity get_root() const;
|
[[nodiscard]] entt::entity get_root() const;
|
||||||
[[nodiscard]] std::vector<entt::entity> get_children(entt::entity parent);
|
[[nodiscard]] std::vector<entt::entity> get_children(entt::entity parent);
|
||||||
|
|
||||||
|
void set_active_camera(Camera *camera);
|
||||||
private:
|
private:
|
||||||
entt::registry _registry{};
|
entt::registry _registry{};
|
||||||
entt::entity root{entt::null};
|
entt::entity root{entt::null};
|
||||||
|
|
||||||
void add_child(entt::entity parent, entt::entity child);
|
void add_child(entt::entity parent, entt::entity child);
|
||||||
|
void update_transforms(entt::entity parent);
|
||||||
|
|
||||||
|
Camera* activeCamera {nullptr};
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif //B_ENGINE_SCENE_H
|
#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/glm.hpp"
|
||||||
#include "glm/ext/matrix_clip_space.hpp"
|
#include "glm/ext/matrix_clip_space.hpp"
|
||||||
#include "glm/ext/matrix_transform.hpp"
|
|
||||||
|
|
||||||
#include "entt/entt.hpp"
|
|
||||||
|
|
||||||
#define STB_IMAGE_IMPLEMENTATION
|
#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 "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 "ModelManager.h"
|
||||||
#include "Scene.h"
|
#include "Scene.h"
|
||||||
#include "ShaderManager.h"
|
#include "ShaderManager.h"
|
||||||
#include "ShaderProgram.h"
|
#include "ShaderProgram.h"
|
||||||
#include "Texture.h"
|
|
||||||
#include "TextureManager.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;
|
GLFWwindow* gWindow = nullptr;
|
||||||
int gWindowWidth = 1920;
|
int gWindowWidth = 1920;
|
||||||
@@ -40,11 +37,10 @@ float gAspectRatio = 0.f;
|
|||||||
|
|
||||||
float gMouseSensitivity = 0.1f;
|
float gMouseSensitivity = 0.1f;
|
||||||
|
|
||||||
FreeCamera gCamera {};
|
Camera gCamera {};
|
||||||
|
|
||||||
Scene gScene{};
|
Scene gScene{};
|
||||||
|
EditorContext gEditorCtx{};
|
||||||
bool gUiFirstRender = true;
|
FreeCameraController gCameraController{};
|
||||||
|
|
||||||
void glfw_error_callback(int error, const char* description);
|
void glfw_error_callback(int error, const char* description);
|
||||||
void glfw_key_callback(GLFWwindow* window, int key, int scancode, int action, int mods);
|
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 init_glfw();
|
||||||
void create_main_window();
|
void create_main_window();
|
||||||
void load_gl();
|
void load_gl();
|
||||||
void load_imgui();
|
|
||||||
void init_camera();
|
void init_camera();
|
||||||
|
void init_scene();
|
||||||
void load_default_textures();
|
void load_default_textures();
|
||||||
void load_shaders();
|
void load_shaders();
|
||||||
void load_inputs();
|
void load_inputs();
|
||||||
void load_default_models();
|
void load_default_models();
|
||||||
void loop();
|
void loop();
|
||||||
void draw_ui();
|
|
||||||
|
void create_entity(entt::entity parent);
|
||||||
|
|
||||||
auto xAxis = glm::vec3{1.f, 0.f, 0.f};
|
auto xAxis = glm::vec3{1.f, 0.f, 0.f};
|
||||||
auto yAxis = glm::vec3{0.f, 1.f, 0.f};
|
auto yAxis = glm::vec3{0.f, 1.f, 0.f};
|
||||||
@@ -75,10 +72,6 @@ int main() {
|
|||||||
|
|
||||||
load_gl();
|
load_gl();
|
||||||
|
|
||||||
load_imgui();
|
|
||||||
|
|
||||||
init_camera();
|
|
||||||
|
|
||||||
stbi_set_flip_vertically_on_load(true);
|
stbi_set_flip_vertically_on_load(true);
|
||||||
|
|
||||||
load_default_textures();
|
load_default_textures();
|
||||||
@@ -89,15 +82,26 @@ int main() {
|
|||||||
|
|
||||||
load_default_models();
|
load_default_models();
|
||||||
|
|
||||||
entt::entity dirLight = gScene.create_game_object();
|
UIManager::init(gWindow);
|
||||||
auto& [direction, ambient, diffuse, specular] = gScene.attach_component<Components::DirectionalLight>(dirLight);
|
UIMenuBar* menuBar = UIManager::add_ui_panel<UIMenuBar>("menu_bar");
|
||||||
direction = {1, -1, 1};
|
menuBar->create_entity = create_entity;
|
||||||
ambient = {0.3f, 0.3f, 0.3f};
|
UIManager::add_ui_panel<UISceneViewer>("scene_viewer");
|
||||||
diffuse = {0.5f, 0.5f, 0.5f};
|
UIManager::add_ui_panel<UISceneGraph>("scene_graph");
|
||||||
specular = {1.0f, 1.0f, 1.0f};
|
UIManager::add_ui_panel<UIEntityInspector>("entity_inspector");
|
||||||
|
|
||||||
auto& tag = gScene.fetch_component<Components::Tag>(dirLight);
|
init_camera();
|
||||||
tag.name = "directional light";
|
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();
|
loop();
|
||||||
|
|
||||||
@@ -174,27 +178,23 @@ void load_gl()
|
|||||||
glEnable(GL_MULTISAMPLE);
|
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() {
|
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_yaw(-135.f);
|
||||||
gCamera.rotate_pitch(-35.f);
|
gCamera.rotate_pitch(-35.f);
|
||||||
|
}
|
||||||
|
|
||||||
InputManager::add_mouse_listener([](float xoff, float yoff) {
|
void init_scene() {
|
||||||
gCamera.rotate_yaw(xoff * gMouseSensitivity);
|
gScene.set_active_camera(&gCamera);
|
||||||
gCamera.rotate_pitch(yoff * gMouseSensitivity);
|
|
||||||
});
|
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() {
|
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_left", {{GLFW_KEY_A, KEY_DOWN}});
|
||||||
InputManager::generate_input_action("move_up", {{GLFW_KEY_SPACE, 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("move_down", {{GLFW_KEY_LEFT_SHIFT, KEY_DOWN}});
|
||||||
InputManager::generate_input_action("exit_application", {{GLFW_KEY_ESCAPE, KEY_DOWN}});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void load_default_models() {
|
void load_default_models() {
|
||||||
@@ -236,147 +235,47 @@ void load_default_models() {
|
|||||||
|
|
||||||
void loop() {
|
void loop() {
|
||||||
while (!glfwWindowShouldClose(gWindow)) {
|
while (!glfwWindowShouldClose(gWindow)) {
|
||||||
|
// input
|
||||||
glfwPollEvents();
|
glfwPollEvents();
|
||||||
|
|
||||||
// check inputs
|
// update
|
||||||
/*if (InputManager::check_action_performed("exit_application")) {
|
UIManager::update(gEditorCtx);
|
||||||
glfwSetWindowShouldClose(gWindow, true);
|
gCameraController.update(gEditorCtx);
|
||||||
continue;
|
|
||||||
}*/
|
|
||||||
|
|
||||||
if (InputManager::check_action_performed("move_forward")) {
|
if (gCamera.aspect_ratio() != gEditorCtx.viewportAspectRatio) {
|
||||||
gCamera.move(FORWARD, .05);
|
gCamera.update_aspect_ratio(gEditorCtx.viewportAspectRatio);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (InputManager::check_action_performed("move_backward")) {
|
gScene.update_transforms();
|
||||||
gCamera.move(BACKWARD, .05);
|
|
||||||
|
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")) {
|
// draw
|
||||||
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
|
|
||||||
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
|
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
|
||||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||||
|
|
||||||
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
|
|
||||||
|
|
||||||
const auto shader = ShaderManager::shaders["phong_shader"];
|
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());
|
gScene.draw_scene(shader.get());
|
||||||
|
|
||||||
glBindVertexArray(0);
|
glBindVertexArray(0);
|
||||||
ShaderProgram::unbind();
|
ShaderProgram::unbind();
|
||||||
|
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||||
|
|
||||||
// gl end frame stuff
|
UIManager::draw();
|
||||||
|
|
||||||
|
// end frame
|
||||||
glfwSwapBuffers(gWindow);
|
glfwSwapBuffers(gWindow);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void draw_ui() {
|
void create_entity(entt::entity parent) {
|
||||||
ImGui_ImplOpenGL3_NewFrame();
|
auto entity = gScene.create_game_object(parent);
|
||||||
ImGui_ImplGlfw_NewFrame();
|
auto& drawable = gScene.attach_component<Components::Drawable>(entity);
|
||||||
ImGui::NewFrame();
|
drawable.model = ModelManager::models["vette"];
|
||||||
{
|
auto& transform = gScene.fetch_component<Components::Transform>(entity);
|
||||||
ImGuiWindowFlags window_flags = ImGuiWindowFlags_MenuBar | ImGuiWindowFlags_NoDocking;
|
transform.scale = {0.1, 0.1, 0.1};
|
||||||
ImGuiViewport* viewport = ImGui::GetMainViewport();
|
transform.dirty = true;
|
||||||
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();
|
|
||||||
}
|
|
||||||
|
|||||||
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