Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ jobs:
python ThirdParty/ConanRecipes/build_recipes.py --export-only

- name: Configure CMake
run: cmake -B ${{github.workspace}}/build -G=Ninja -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCI=ON
run: cmake -B ${{github.workspace}}/build -G=Ninja -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}}

- name: Build
run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} -j ${{env.MAKE_THREAD_NUM}}
Expand Down
2 changes: 1 addition & 1 deletion Editor/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ endif ()
exp_add_executable(
NAME Editor
SRC ${editor_sources} ${imgui_stdlib_path}/imgui_stdlib.cpp
LIB Core RHI Runtime glfw imgui::imgui ${editor_platform_libs}
LIB Core Runtime glfw imgui::imgui ${editor_platform_libs}
INC Include ${imgui_stdlib_path}
DEP_TARGET ${platform_rhi_targets}
REFLECT Include
Expand Down
1 change: 1 addition & 0 deletions Editor/Include/Editor/EditorContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ namespace Editor {
Runtime::Entity CreateEntity(const std::string& inName);
void DestroyEntity(Runtime::Entity inEntity);
void RenameEntity(Runtime::Entity inEntity, const std::string& inName);
void NotifyComponentEdited(Runtime::Entity inEntity, Runtime::CompClass inClass);
void NotifyComponentsChanged(Runtime::Entity inEntity);

private:
Expand Down
1 change: 1 addition & 0 deletions Editor/Include/Editor/EditorWindow.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ namespace Editor {
std::string title;
uint32_t width;
uint32_t height;
bool maximized;
};

class EditorWindow final : public Runtime::Window {
Expand Down
30 changes: 26 additions & 4 deletions Editor/Src/EditorApplication.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,15 @@ namespace Editor::Internal {
constexpr uint32_t projectHubWindowWidth = 560;
constexpr uint32_t projectHubWindowHeight = 720;
constexpr float defaultFontSize = 15.0f;
constexpr float defaultRightColumnRatio = 0.22f;
constexpr float minRightColumnWidth = 360.0f;
constexpr float maxRightColumnWidth = 480.0f;
constexpr float defaultLogRatio = 0.22f;
constexpr float minLogHeight = 200.0f;
constexpr float maxLogHeight = 300.0f;
constexpr float defaultOutlinerRatio = 0.35f;
constexpr float minOutlinerHeight = 280.0f;
constexpr float maxOutlinerHeight = 420.0f;
constexpr uint32_t defaultSceneWidth = 1280;
constexpr uint32_t defaultSceneHeight = 720;
constexpr RHI::PixelFormat sceneColorFormat = RHI::PixelFormat::rgba8Unorm;
Expand Down Expand Up @@ -72,6 +81,15 @@ namespace Editor::Internal {
style.TabRounding = 2.0f;
}

static float CalculatePanelRatio(float inAvailableSize, float inDefaultRatio, float inMinSize, float inMaxSize)
{
if (inAvailableSize <= 0.0f) {
return inDefaultRatio;
}
const float panelSize = std::clamp(inAvailableSize * inDefaultRatio, inMinSize, inMaxSize);
return std::min(panelSize / inAvailableSize, 0.5f);
}

static void BuildInitialEditorDockLayout(ImGuiID inDockSpaceId, const ImVec2& inSize)
{
if (ImGui::DockBuilderGetNode(inDockSpaceId) != nullptr) {
Expand All @@ -86,9 +104,12 @@ namespace Editor::Internal {
ImGuiID outlinerNodeId = 0;
ImGuiID inspectorNodeId = 0;
ImGuiID logNodeId = 0;
ImGui::DockBuilderSplitNode(inDockSpaceId, ImGuiDir_Right, 0.25f, &rightColumnNodeId, &sceneNodeId);
ImGui::DockBuilderSplitNode(sceneNodeId, ImGuiDir_Down, 0.25f, &logNodeId, &sceneNodeId);
ImGui::DockBuilderSplitNode(rightColumnNodeId, ImGuiDir_Down, 0.5f, &inspectorNodeId, &outlinerNodeId);
const float rightColumnRatio = CalculatePanelRatio(inSize.x, defaultRightColumnRatio, minRightColumnWidth, maxRightColumnWidth);
const float logRatio = CalculatePanelRatio(inSize.y, defaultLogRatio, minLogHeight, maxLogHeight);
const float outlinerRatio = CalculatePanelRatio(inSize.y, defaultOutlinerRatio, minOutlinerHeight, maxOutlinerHeight);
ImGui::DockBuilderSplitNode(inDockSpaceId, ImGuiDir_Right, rightColumnRatio, &rightColumnNodeId, &sceneNodeId);
ImGui::DockBuilderSplitNode(sceneNodeId, ImGuiDir_Down, logRatio, &logNodeId, &sceneNodeId);
ImGui::DockBuilderSplitNode(rightColumnNodeId, ImGuiDir_Up, outlinerRatio, &outlinerNodeId, &inspectorNodeId);

ImGui::DockBuilderDockWindow("Scene", sceneNodeId);
ImGui::DockBuilderDockWindow("Outliner", outlinerNodeId);
Expand Down Expand Up @@ -132,7 +153,8 @@ namespace Editor {
EditorWindowDesc {
.title = Internal::MakeWindowTitle(desc),
.width = Internal::GetInitialWindowWidth(desc),
.height = Internal::GetInitialWindowHeight(desc)
.height = Internal::GetInitialWindowHeight(desc),
.maximized = desc.mode == EditorApplicationMode::editor
});
InstallCallbacks();

Expand Down
10 changes: 10 additions & 0 deletions Editor/Src/EditorContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,16 @@ namespace Editor {
worldStructureVersion++;
}

void EditorContext::NotifyComponentEdited(Runtime::Entity inEntity, Runtime::CompClass inClass)
{
auto& registry = sceneClient->GetWorld().GetRegistry();
if (!registry.Valid(inEntity) || !registry.HasDyn(inClass, inEntity)) {
return;
}
registry.NotifyUpdatedDyn(inClass, inEntity);
NotifyComponentsChanged(inEntity);
}

void EditorContext::NotifyComponentsChanged(Runtime::Entity)
{
componentsVersion++;
Expand Down
1 change: 1 addition & 0 deletions Editor/Src/EditorWindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ namespace Editor {
{
glfwInit();
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
glfwWindowHint(GLFW_MAXIMIZED, inDesc.maximized ? GLFW_TRUE : GLFW_FALSE);
window = glfwCreateWindow(
static_cast<int>(inDesc.width),
static_cast<int>(inDesc.height),
Expand Down
65 changes: 55 additions & 10 deletions Editor/Src/Frame/EditorFrame.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,15 @@
#include <Runtime/ECS.h>

namespace Editor::Internal {
static std::string ComponentDisplayName(const Mirror::Class& inClass)
{
const std::string& qualifiedName = inClass.GetName();
const size_t namespaceSeparator = qualifiedName.rfind("::");
return namespaceSeparator == std::string::npos
? qualifiedName
: qualifiedName.substr(namespaceSeparator + 2);
}

static std::string EntityDisplayName(const Runtime::ECRegistry& inRegistry, Runtime::Entity inEntity)
{
const auto* name = inRegistry.Find<Runtime::Name>(inEntity);
Expand Down Expand Up @@ -114,7 +123,7 @@ namespace Editor {

auto& registry = inContext.GetSceneClient().GetWorld().GetRegistry();
registry.Each([&](Runtime::Entity entity) -> void {
if (registry.Has<Runtime::TransientTag>(entity)) {
if (registry.HasTag<Runtime::TransientTag>(entity)) {
return;
}
const std::string label = Internal::EntityDisplayName(registry, entity);
Expand Down Expand Up @@ -152,7 +161,9 @@ namespace Editor {

std::vector<const Mirror::Class*> addableComponents;
for (const auto* clazz : Mirror::Class::GetAll()) {
if (clazz->HasMeta("comp") && !registry.HasDyn(clazz, selectedEntity) && clazz->HasDefaultConstructor()) {
const bool isTag = clazz->HasMeta(Runtime::MetaPresets::tag);
const bool hasType = isTag ? registry.HasTagDyn(clazz, selectedEntity) : registry.HasDyn(clazz, selectedEntity);
if (clazz->HasMeta("comp") && !hasType && (isTag || clazz->HasDefaultConstructor())) {
addableComponents.emplace_back(clazz);
}
}
Expand All @@ -162,54 +173,88 @@ namespace Editor {

if (!addableComponents.empty()) {
selectedAddComponentIndex = std::clamp(selectedAddComponentIndex, 0, static_cast<int>(addableComponents.size() - 1));
if (ImGui::BeginCombo("Add Component", addableComponents[selectedAddComponentIndex]->GetName().c_str())) {
const std::string selectedComponentName = Internal::ComponentDisplayName(*addableComponents[selectedAddComponentIndex]);
if (ImGui::BeginCombo("Add Component", selectedComponentName.c_str())) {
for (int i = 0; i < static_cast<int>(addableComponents.size()); i++) {
const bool selected = i == selectedAddComponentIndex;
if (ImGui::Selectable(addableComponents[i]->GetName().c_str(), selected)) {
const std::string componentName = Internal::ComponentDisplayName(*addableComponents[i]);
ImGui::PushID(addableComponents[i]->GetName().c_str());
if (ImGui::Selectable(componentName.c_str(), selected)) {
selectedAddComponentIndex = i;
}
if (selected) {
ImGui::SetItemDefaultFocus();
}
ImGui::PopID();
}
ImGui::EndCombo();
}
ImGui::SameLine();
if (ImGui::Button("Add")) {
registry.EmplaceDyn(addableComponents[selectedAddComponentIndex], selectedEntity, {});
const auto* selectedClass = addableComponents[selectedAddComponentIndex];
if (selectedClass->HasMeta(Runtime::MetaPresets::tag)) {
registry.AddTagDyn(selectedClass, selectedEntity);
} else {
registry.EmplaceDyn(selectedClass, selectedEntity, {});
}
inContext.NotifyComponentsChanged(selectedEntity);
}
}

Runtime::CompClass componentToRemove = nullptr;
bool tagToRemove = false;
registry.CompEach(selectedEntity, [&](Runtime::CompClass compClass) -> void {
if (compClass->HasMeta("transient")) {
return;
}
ImGui::PushID(compClass->GetName().c_str());
const bool open = ImGui::CollapsingHeader(compClass->GetName().c_str(), ImGuiTreeNodeFlags_DefaultOpen);
const std::string componentName = Internal::ComponentDisplayName(*compClass);
const bool open = ImGui::CollapsingHeader(componentName.c_str(), ImGuiTreeNodeFlags_DefaultOpen);
if (ImGui::BeginPopupContextItem("ComponentMenu")) {
if (ImGui::MenuItem("Remove")) {
componentToRemove = compClass;
tagToRemove = false;
}
ImGui::EndPopup();
}
if (open && componentToRemove != compClass) {
const Mirror::Any compRef = registry.GetDyn(compClass, selectedEntity);
bool componentEdited = false;
for (const auto& memberVariable : compClass->GetMemberVariables() | std::views::values) {
if (memberVariable.IsTransient()) {
continue;
}
Mirror::Any memberRef = memberVariable.GetDyn(compRef);
if (RenderInputWidget(memberVariable.GetName(), memberRef)) {
inContext.NotifyComponentsChanged(selectedEntity);
}
componentEdited |= RenderInputWidget(memberVariable.GetName(), memberRef);
}
if (componentEdited) {
inContext.NotifyComponentEdited(selectedEntity, compClass);
}
}
ImGui::PopID();
});
registry.TagEach(selectedEntity, [&](Runtime::TagClass tagClass) -> void {
if (tagClass->HasMeta("transient")) {
return;
}
ImGui::PushID(tagClass->GetName().c_str());
const std::string tagName = Internal::ComponentDisplayName(*tagClass);
ImGui::TextUnformatted(tagName.c_str());
if (ImGui::BeginPopupContextItem("ComponentMenu")) {
if (ImGui::MenuItem("Remove")) {
componentToRemove = tagClass;
tagToRemove = true;
}
ImGui::EndPopup();
}
ImGui::PopID();
});
if (componentToRemove != nullptr) {
registry.RemoveDyn(componentToRemove, selectedEntity);
if (tagToRemove) {
registry.RemoveTagDyn(componentToRemove, selectedEntity);
} else {
registry.RemoveDyn(componentToRemove, selectedEntity);
}
inContext.NotifyComponentsChanged(selectedEntity);
}
ImGui::End();
Expand Down
7 changes: 6 additions & 1 deletion Editor/Src/SceneClient.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include <array>
#include <algorithm>
#include <cmath>
#include <string>

#include <GLFW/glfw3.h>
#include <Editor/EditorWindow.h>
Expand All @@ -14,6 +15,7 @@
#include <Runtime/Asset/Material.h>
#include <Runtime/Asset/Mesh.h>
#include <Runtime/Component/Camera.h>
#include <Runtime/Component/Name.h>
#include <Runtime/Component/Player.h>
#include <Runtime/Component/Primitive.h>
#include <Runtime/Component/Transform.h>
Expand Down Expand Up @@ -108,6 +110,7 @@ namespace Editor::Internal {

// WorldTransform's reflected constructor takes an FTransform, other argument shapes would not match it
const auto ground = inRegistry.Create();
inRegistry.Emplace<Runtime::Name>(ground, std::string("Ground"));
Common::FTransform groundTransform;
groundTransform.scale = Common::FVec3(10.0f, 10.0f, 0.2f);
groundTransform.translation = Common::FVec3(0.0f, 0.0f, -0.1f);
Expand All @@ -116,13 +119,15 @@ namespace Editor::Internal {
groundPrimitive.mesh = cubeMesh;

const auto cube = inRegistry.Create();
inRegistry.Emplace<Runtime::Name>(cube, std::string("Cube"));
Common::FTransform cubeTransform;
cubeTransform.translation = Common::FVec3(0.0f, 0.0f, 0.5f);
inRegistry.Emplace<Runtime::WorldTransform>(cube, cubeTransform);
auto& cubePrimitive = inRegistry.Emplace<Runtime::StaticPrimitive>(cube);
cubePrimitive.mesh = cubeMesh;

const auto playerStart = inRegistry.Create();
inRegistry.Emplace<Runtime::Name>(playerStart, std::string("Player Start"));
const Common::FTransform playerStartTransform = Common::FTransform::LookAt(Common::FVec3(-5.0f, -6.0f, 4.0f), Common::FVec3(0.0f, 0.0f, 0.5f));
inRegistry.Emplace<Runtime::WorldTransform>(playerStart, playerStartTransform);
inRegistry.Emplace<Runtime::PlayerStart>(playerStart);
Expand Down Expand Up @@ -325,7 +330,7 @@ namespace Editor {
{
auto& registry = world.GetRegistry();
editorCamera = registry.Create();
registry.Emplace<Runtime::TransientTag>(editorCamera);
registry.AddTag<Runtime::TransientTag>(editorCamera);
registry.Emplace<Runtime::Camera>(editorCamera);
// WorldTransform's reflected constructor takes an FTransform, other argument shapes would not match it
const Common::FTransform cameraTransform = Common::FTransform::LookAt(Common::FVec3(-5.0f, -6.0f, 4.0f), Common::FVec3(0.0f, 0.0f, 0.5f));
Expand Down
15 changes: 0 additions & 15 deletions Engine/Source/Common/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,16 +1,3 @@
# Math SIMD baseline. The math types are header-only, so the option is PUBLIC: every consumer instantiates them and must
# share one ISA baseline. The simd backend only relies on the SSE2/NEON baseline (present on every CPU this engine
# targets), so it is always on; this just lifts the compiler's instruction-set baseline where that helps codegen. MSVC
# x64 implies SSE2 and has no /arch:SSE4.2; on gcc/clang lift the baseline to SSE4.2 where supported (the check fails and
# is skipped on non-x86 targets such as aarch64, which already ship NEON).
if (NOT MSVC)
include(CheckCXXCompilerFlag)
check_cxx_compiler_flag("-msse4.2" has_msse42)
if (has_msse42)
set(math_public_compile_opt -msse4.2)
endif ()
endif ()

file(GLOB_RECURSE sources Src/*.cpp)
exp_add_library(
NAME Common
Expand All @@ -19,15 +6,13 @@ exp_add_library(
PUBLIC_INC Include
PUBLIC_LIB rapidjson cityhash::cityhash
PRIVATE_LIB debugbreak::debugbreak
PUBLIC_COMPILE_OPT ${math_public_compile_opt}
)

file(GLOB test_sources Test/*.cpp)
exp_add_test(
NAME Common.Test
INC Test
SRC ${test_sources}
LIB Common
)

# exp_add_benchmark early-returns when BUILD_BENCHMARK is OFF, so this is safe to declare unconditionally.
Expand Down
8 changes: 4 additions & 4 deletions Engine/Source/Common/Include/Common/Serialization.h
Original file line number Diff line number Diff line change
Expand Up @@ -958,7 +958,7 @@ namespace Common {
static size_t SerializeInternal(BinarySerializeStream& stream, const std::tuple<T...>& value, std::index_sequence<I...>)
{
size_t serialized = 0;
std::initializer_list<int> { ([&]() -> void {
(void) std::initializer_list<int> { ([&]() -> void {
serialized += Serializer<T>::Serialize(stream, std::get<I>(value));
}(), 0)... };
return serialized;
Expand All @@ -968,7 +968,7 @@ namespace Common {
static size_t DeserializeInternal(BinaryDeserializeStream& stream, std::tuple<T...>& value, std::index_sequence<I...>)
{
size_t deserialized = 0;
std::initializer_list<int> { ([&]() -> void {
(void) std::initializer_list<int> { ([&]() -> void {
deserialized += Serializer<T>::Deserialize(stream, std::get<I>(value));
}(), 0)... };
return deserialized;
Expand Down Expand Up @@ -1507,7 +1507,7 @@ namespace Common {
template <size_t... I>
static void JsonSerializeInternal(rapidjson::Value& outJsonValue, rapidjson::Document::AllocatorType& inAllocator, const std::tuple<T...>& inValue, std::index_sequence<I...>)
{
std::initializer_list<int> { ([&]() -> void {
(void) std::initializer_list<int> { ([&]() -> void {
const auto key = std::to_string(I);

rapidjson::Value jsonKey;
Expand All @@ -1523,7 +1523,7 @@ namespace Common {
template <size_t... I>
static void JsonDeserializeInternal(const rapidjson::Value& inJsonValue, std::tuple<T...>& outValue, std::index_sequence<I...>)
{
std::initializer_list<int> { ([&]() -> void {
(void) std::initializer_list<int> { ([&]() -> void {
const auto key = std::to_string(I);
if (!inJsonValue.HasMember(key.c_str())) {
return;
Expand Down
2 changes: 1 addition & 1 deletion Engine/Source/Common/Include/Common/String.h
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@ namespace Common {
template <size_t N, typename TupleType, size_t... I>
static void ContactInternal(std::stringstream& stream, const TupleType& tuple, std::index_sequence<I...>)
{
std::initializer_list<int> { ([&]() -> void {
(void) std::initializer_list<int> { ([&]() -> void {
stream << StringConverter<std::tuple_element_t<I, TupleType>>::ToString(std::get<I>(tuple));
if (I != N - 1) {
stream << ", ";
Expand Down
9 changes: 8 additions & 1 deletion Engine/Source/Mirror/Include/Mirror/Meta.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,16 @@ public: \
static const Mirror::Class& GetStaticClass(); \
const Mirror::Class& GetClass() const; \

#define EPolyClassBody(className) \
#define EPolyBaseClassBody(className) \
private: \
static Mirror::Internal::ScopedReleaser _mirrorRegistry; \
public: \
static const Mirror::Class& GetStaticClass(); \
virtual const Mirror::Class& GetClass() const; \

#define EPolyDerivedClassBody(className) \
private: \
static Mirror::Internal::ScopedReleaser _mirrorRegistry; \
public: \
static const Mirror::Class& GetStaticClass(); \
const Mirror::Class& GetClass() const override; \
Loading
Loading