mirror of
https://github.com/antopilo/Nuake.git
synced 2026-09-15 20:08:54 +03:00
Merge branch 'main' of https://github.com/antopilo/Nuake
This commit is contained in:
BIN
Editor/resources/Images/logo.png
Normal file
BIN
Editor/resources/Images/logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 13 KiB |
BIN
Editor/resources/Images/screenshot.png
Normal file
BIN
Editor/resources/Images/screenshot.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 859 KiB |
@@ -94,7 +94,7 @@ namespace Nuake {
|
||||
float needed = half - used;
|
||||
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0));
|
||||
if (ImGui::Button(ICON_FA_PLAY, ImVec2(30, 30)))
|
||||
if (ImGui::Button(ICON_FA_PLAY, ImVec2(30, 30)) || (Input::IsKeyPressed(GLFW_KEY_F5) && !Engine::IsPlayMode))
|
||||
{
|
||||
SceneSnapshot = Engine::GetCurrentScene()->Copy();
|
||||
Engine::EnterPlayMode();
|
||||
@@ -102,7 +102,7 @@ namespace Nuake {
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
if (ImGui::Button(ICON_FA_STOP, ImVec2(30, 30)) || Input::IsKeyPressed(297))
|
||||
if (ImGui::Button(ICON_FA_STOP, ImVec2(30, 30)) || Input::IsKeyPressed(GLFW_KEY_F8))
|
||||
{
|
||||
Engine::ExitPlayMode();
|
||||
|
||||
@@ -1248,16 +1248,37 @@ namespace Nuake {
|
||||
|
||||
void NewProject()
|
||||
{
|
||||
if (Engine::GetProject())
|
||||
if (Engine::GetProject() && Engine::GetProject()->FileExist())
|
||||
Engine::GetProject()->Save();
|
||||
|
||||
|
||||
std::string selectedProject = FileDialog::SaveFile("Project file\0*.project");
|
||||
if (selectedProject == "") // Hit cancel
|
||||
|
||||
if(!String::EndsWith(selectedProject, ".project"))
|
||||
selectedProject += ".project";
|
||||
|
||||
if (selectedProject.empty()) // Hit cancel
|
||||
return;
|
||||
|
||||
Ref<Project> project = Project::New("Unnamed project", "no description", selectedProject + ".project");
|
||||
auto backslashSplits = String::Split(selectedProject, '\\');
|
||||
auto fileName = backslashSplits[backslashSplits.size() - 1];
|
||||
|
||||
std::string finalPath = String::Split(selectedProject, '.')[0];
|
||||
|
||||
// We need to create a folder
|
||||
if (const auto& dirPath = finalPath;
|
||||
!std::filesystem::create_directory(dirPath))
|
||||
{
|
||||
// Should we continue?
|
||||
Logger::Log("Failed creating project directory: " + dirPath);
|
||||
}
|
||||
|
||||
finalPath += "\\" + fileName;
|
||||
|
||||
Ref<Project> project = Project::New(String::Split(fileName, '.')[0], "no description", finalPath);
|
||||
Engine::LoadProject(project);
|
||||
Engine::LoadScene(Scene::New());
|
||||
|
||||
Engine::GetCurrentWindow()->SetTitle("Nuake Engine - Editing " + project->Name);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -9,35 +9,34 @@
|
||||
#include "src/Rendering/Textures/TextureManager.h"
|
||||
#include "EditorInterface.h"
|
||||
|
||||
const std::string TEMPLATE_SCRIPT_BEGIN = "import \"Nuake:Engine\" for Engine \
|
||||
import \"Nuake:ScriptableEntity\" for ScriptableEntity \
|
||||
import \"Nuake:Input\" for Input \
|
||||
import \"Nuake:Scene\" for Scene \
|
||||
\
|
||||
class ";
|
||||
const std::string TEMPLATE_SCRIPT = R"(import "Nuake:Engine" for Engine
|
||||
import "Nuake:ScriptableEntity" for ScriptableEntity
|
||||
import "Nuake:Input" for Input
|
||||
import "Nuake:Math" for Vector3, Math
|
||||
import "Nuake:Scene" for Scene
|
||||
|
||||
const std::string TEMPLATE_SCRIPT_END = " is ScriptableEntity {\
|
||||
construct new(){\
|
||||
_ReloadSpeed = 0.1\
|
||||
_Intensity = 0.0\
|
||||
}\
|
||||
\
|
||||
init() {\
|
||||
}\
|
||||
\
|
||||
// Updates every frame\
|
||||
update(ts) {\
|
||||
\
|
||||
}\
|
||||
\
|
||||
// Updates every tick\
|
||||
fixedUpdate(ts) {\
|
||||
\
|
||||
}\
|
||||
\
|
||||
exit() {\
|
||||
}\
|
||||
}";
|
||||
class MyEntityScript is ScriptableEntity {
|
||||
construct new() {
|
||||
}
|
||||
|
||||
// Called when the scene gets initialized
|
||||
init() {
|
||||
// Engine.Log("Hello World!")
|
||||
}
|
||||
|
||||
// Called every update
|
||||
update(ts) {
|
||||
}
|
||||
|
||||
// Called 90 times per second
|
||||
fixedUpdate(ts) {
|
||||
}
|
||||
|
||||
// Called on shutdown
|
||||
exit() {
|
||||
}
|
||||
}
|
||||
)";
|
||||
|
||||
#include <src/Rendering/Textures/Material.h>
|
||||
|
||||
@@ -212,9 +211,29 @@ namespace Nuake {
|
||||
FileSystem::BeginWriteFile(path);
|
||||
FileSystem::WriteLine(jsonData.dump(4));
|
||||
FileSystem::EndWriteFile();
|
||||
|
||||
RefreshFileBrowser();
|
||||
}
|
||||
}
|
||||
|
||||
if (ImGui::MenuItem("Wren Script"))
|
||||
{
|
||||
std::string path = FileDialog::SaveFile("*.wren");
|
||||
if (!String::EndsWith(path, ".wren"))
|
||||
{
|
||||
path += ".wren";
|
||||
}
|
||||
|
||||
if (!path.empty())
|
||||
{
|
||||
FileSystem::BeginWriteFile(path);
|
||||
FileSystem::WriteLine(TEMPLATE_SCRIPT);
|
||||
FileSystem::EndWriteFile();
|
||||
|
||||
RefreshFileBrowser();
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
|
||||
@@ -223,6 +242,12 @@ namespace Nuake {
|
||||
|
||||
}
|
||||
|
||||
void FileSystemUI::RefreshFileBrowser()
|
||||
{
|
||||
FileSystem::Scan();
|
||||
m_CurrentDirectory = FileSystem::RootDirectory;
|
||||
}
|
||||
|
||||
float h = 200;
|
||||
static float sz1 = 300;
|
||||
static float sz2 = 300;
|
||||
@@ -285,8 +310,7 @@ namespace Nuake {
|
||||
{
|
||||
if (ImGui::Button("Refresh"))
|
||||
{
|
||||
FileSystem::Scan();
|
||||
m_CurrentDirectory = FileSystem::RootDirectory;
|
||||
RefreshFileBrowser();
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
@@ -322,7 +346,6 @@ namespace Nuake {
|
||||
ImGui::TableNextColumn();
|
||||
if (ImGui::Button("..", ImVec2(100, 100)))
|
||||
m_CurrentDirectory = m_CurrentDirectory->Parent;
|
||||
ImGui::TableNextColumn();
|
||||
i++;
|
||||
}
|
||||
|
||||
@@ -330,13 +353,12 @@ namespace Nuake {
|
||||
{
|
||||
for (Ref<Directory>& d : m_CurrentDirectory->Directories)
|
||||
{
|
||||
DrawDirectory(d);
|
||||
|
||||
if (i + 1 % amount != 0)
|
||||
ImGui::TableNextColumn();
|
||||
else
|
||||
ImGui::TableNextRow();
|
||||
|
||||
DrawDirectory(d);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
@@ -345,12 +367,12 @@ namespace Nuake {
|
||||
{
|
||||
for (auto f : m_CurrentDirectory->Files)
|
||||
{
|
||||
DrawFile(f);
|
||||
|
||||
if (i - 1 % amount != 0)
|
||||
if (i - 1 % amount != 0 || i == 1)
|
||||
ImGui::TableNextColumn();
|
||||
else
|
||||
ImGui::TableNextRow();
|
||||
|
||||
DrawFile(f);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,5 +26,6 @@ namespace Nuake {
|
||||
void DrawFile(Ref<File> file);
|
||||
void DrawDirectoryExplorer();
|
||||
void DrawContextMenu();
|
||||
void RefreshFileBrowser();
|
||||
};
|
||||
}
|
||||
|
||||
@@ -3,20 +3,21 @@
|
||||
#include <src/Vendors/imgui/imgui.h>
|
||||
#include "../Misc/InterfaceFonts.h"
|
||||
|
||||
#include <Engine.h>
|
||||
#include <src/Core/FileSystem.h>
|
||||
#include <src/Resource/Project.h>
|
||||
#include <Engine.h>
|
||||
#include <src/Core/Logger.h>
|
||||
#include <src/Rendering/Textures/TextureManager.h>
|
||||
#include <src/Rendering/Textures/Texture.h>
|
||||
|
||||
#include <json/json.hpp>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "EditorInterface.h"
|
||||
|
||||
#include "FileSystemUI.h"
|
||||
|
||||
#include <future>
|
||||
|
||||
namespace Nuake
|
||||
{
|
||||
ProjectPreview::ProjectPreview(const std::string& path)
|
||||
@@ -33,6 +34,16 @@ namespace Nuake
|
||||
nlohmann::json projectJson = nlohmann::json::parse(projectFile);
|
||||
Name = projectJson["Name"];
|
||||
Description = projectJson["Description"];
|
||||
|
||||
const std::string projectIconPath = Path + "/../icon.png";
|
||||
if (FileSystem::FileExists(projectIconPath, true))
|
||||
{
|
||||
ProjectIcon = TextureManager::Get()->GetTexture(projectIconPath);
|
||||
}
|
||||
else
|
||||
{
|
||||
ProjectIcon = TextureManager::Get()->GetTexture("resources/Images/nuake-logo.png");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -65,10 +76,14 @@ namespace Nuake
|
||||
_Projects = std::vector<ProjectPreview>();
|
||||
|
||||
ParseRecentFile();
|
||||
|
||||
// Load Nuake logo
|
||||
_NuakeLogo = TextureManager::Get()->GetTexture(NUAKE_LOGO_PATH);
|
||||
}
|
||||
|
||||
void WelcomeWindow::Draw()
|
||||
{
|
||||
// Make viewport fullscreen
|
||||
ImGuiViewport* viewport = ImGui::GetMainViewport();
|
||||
ImGui::SetNextWindowPos(viewport->GetWorkPos());
|
||||
ImGui::SetNextWindowSize(viewport->GetWorkSize());
|
||||
@@ -78,171 +93,23 @@ namespace Nuake
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(32.0f, 32.0f));
|
||||
ImGui::Begin("Welcome Screen", 0, ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoResize);
|
||||
{
|
||||
// Draw Nuake logo
|
||||
{
|
||||
UIFont boldfont = UIFont(Fonts::Title);
|
||||
|
||||
std::string text = "Nuake Engine";
|
||||
auto windowWidth = ImGui::GetWindowSize().x;
|
||||
auto textWidth = ImGui::CalcTextSize(text.c_str()).x;
|
||||
|
||||
ImGui::SetCursorPosX((windowWidth - textWidth) * 0.5f);
|
||||
ImGui::Text(text.c_str());
|
||||
const Vector2 logoSize = _NuakeLogo->GetSize();
|
||||
const ImVec2 imguiSize = ImVec2(logoSize.x, logoSize.y);
|
||||
ImGui::Image((ImTextureID)_NuakeLogo->GetID(), imguiSize, ImVec2(0, 1), ImVec2(1, 0));
|
||||
}
|
||||
|
||||
// Add padding under logo
|
||||
ImGui::Dummy(ImVec2(10, 25));
|
||||
{
|
||||
UIFont boldfont = UIFont(Fonts::SubTitle);
|
||||
std::string text = "An IdTech inspired game engine";
|
||||
auto windowWidth = ImGui::GetWindowSize().x;
|
||||
auto textWidth = ImGui::CalcTextSize(text.c_str()).x;
|
||||
|
||||
ImGui::SetCursorPosX((windowWidth - textWidth) * 0.5f);
|
||||
ImGui::Text(text.c_str());
|
||||
}
|
||||
ImGui::Separator();
|
||||
{
|
||||
UIFont boldfont = UIFont(Fonts::SubTitle);
|
||||
ImGui::Text("Projects recently opened");
|
||||
ImGui::Text("Open recent");
|
||||
}
|
||||
|
||||
ImVec2 projectsWindowSize = ImGui::GetContentRegionAvail();
|
||||
projectsWindowSize.x *= 0.6f;
|
||||
|
||||
ImGui::BeginChild("Projects", projectsWindowSize, true);
|
||||
{
|
||||
const uint32_t itemHeight = 120;
|
||||
|
||||
for (uint32_t i = 0; i < std::size(_Projects); i++)
|
||||
{
|
||||
ProjectPreview& project = _Projects[i];
|
||||
|
||||
float cursorYStart = ImGui::GetCursorPosY();
|
||||
|
||||
std::string selectableName = "##" + std::to_string(i);
|
||||
bool isSelected = SelectedProject == i;
|
||||
if (ImGui::Selectable(selectableName.c_str(), isSelected, ImGuiSelectableFlags_AllowItemOverlap, ImVec2(ImGui::GetContentRegionAvailWidth(), itemHeight)))
|
||||
{
|
||||
SelectedProject = i;
|
||||
}
|
||||
|
||||
ImGui::SetCursorPosY(cursorYStart);
|
||||
{
|
||||
UIFont boldfont = UIFont(Fonts::LargeBold);
|
||||
ImGui::Text(project.Name.c_str());
|
||||
}
|
||||
|
||||
{
|
||||
UIFont boldfont = UIFont(Fonts::Bold);
|
||||
ImGui::Text(project.Description.c_str());
|
||||
}
|
||||
|
||||
ImGui::SetCursorPosY(cursorYStart + itemHeight);
|
||||
}
|
||||
|
||||
if (ImGui::Button("Import an existing project", ImVec2(ImGui::GetContentRegionAvailWidth(), itemHeight)))
|
||||
{
|
||||
std::string path = FileDialog::OpenFile("*.project");
|
||||
if (path != "")
|
||||
{
|
||||
bool alreadyContainsProject = false;
|
||||
for (auto& p : _Projects)
|
||||
{
|
||||
if (p.Path == path)
|
||||
alreadyContainsProject = true;
|
||||
}
|
||||
|
||||
if (!alreadyContainsProject)
|
||||
{
|
||||
_Projects.push_back(ProjectPreview(path));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ImGui::EndChild();
|
||||
|
||||
DrawRecentProjectsSection();
|
||||
ImGui::SameLine();
|
||||
|
||||
if (ImGui::BeginChild("Controls", ImGui::GetContentRegionAvail(), true))
|
||||
{
|
||||
ImVec2 buttonSize = ImVec2(ImGui::GetContentRegionAvailWidth(), 58);
|
||||
if (ImGui::Button("New Project", buttonSize))
|
||||
{
|
||||
std::string selectedProject = FileDialog::SaveFile("Project file\0*.project");
|
||||
|
||||
if (!selectedProject.empty())
|
||||
{
|
||||
auto backslashSplits = String::Split(selectedProject, '\\');
|
||||
auto fileName = backslashSplits[backslashSplits.size() - 1];
|
||||
|
||||
std::string finalPath = selectedProject;
|
||||
|
||||
if (String::EndsWith(fileName, ".project"))
|
||||
{
|
||||
// We need to create a folder
|
||||
if (const auto& dirPath = selectedProject;
|
||||
std::filesystem::create_directory(dirPath))
|
||||
{
|
||||
// Should we continue?
|
||||
Logger::Log("Failed creating project directory: " + dirPath);
|
||||
}
|
||||
|
||||
finalPath += "\\" + fileName + ".project";
|
||||
}
|
||||
|
||||
auto project = Project::New(fileName, "no description", finalPath);
|
||||
Engine::LoadProject(project);
|
||||
Engine::LoadScene(Scene::New());
|
||||
project->Save();
|
||||
|
||||
auto projectPreview = ProjectPreview();
|
||||
projectPreview.Name = project->Name;
|
||||
projectPreview.Description = project->Description;
|
||||
projectPreview.Path = project->FullPath;
|
||||
|
||||
_Projects.push_back(projectPreview);
|
||||
}
|
||||
}
|
||||
ImGui::Separator();
|
||||
|
||||
if (SelectedProject != -1)
|
||||
{
|
||||
if (ImGui::Button("Load Project", buttonSize))
|
||||
{
|
||||
assert(SelectedProject < std::size(_Projects));
|
||||
|
||||
using namespace Nuake;
|
||||
|
||||
SaveRecentFile();
|
||||
|
||||
std::string projectPath = _Projects[SelectedProject].Path;
|
||||
FileSystem::SetRootDirectory(projectPath + "/../");
|
||||
|
||||
auto project = Project::New();
|
||||
auto projectFileData = FileSystem::ReadFile(projectPath, true);
|
||||
try
|
||||
{
|
||||
project->Deserialize(projectFileData);
|
||||
project->FullPath = projectPath;
|
||||
|
||||
Engine::LoadProject(project);
|
||||
|
||||
_Editor->filesystem->m_CurrentDirectory = Nuake::FileSystem::RootDirectory;
|
||||
}
|
||||
catch (std::exception exception)
|
||||
{
|
||||
Logger::Log("Error loading project: " + projectPath, CRITICAL);
|
||||
Logger::Log(exception.what());
|
||||
}
|
||||
|
||||
Engine::GetCurrentWindow()->SetTitle("Nuake Engine - Editing " + project->Name);
|
||||
}
|
||||
|
||||
if (ImGui::Button("Remove Project"))
|
||||
{
|
||||
_Projects.erase(_Projects.begin() + SelectedProject);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::EndChild();
|
||||
DrawRightControls();
|
||||
}
|
||||
|
||||
ImGui::End();
|
||||
@@ -250,6 +117,171 @@ namespace Nuake
|
||||
ImGui::PopStyleVar();
|
||||
}
|
||||
|
||||
void WelcomeWindow::DrawRecentProjectsSection()
|
||||
{
|
||||
// Recent projects section takes up 80% of the width
|
||||
ImVec2 projectsWindowSize = ImGui::GetContentRegionAvail();
|
||||
projectsWindowSize.x *= 0.8f;
|
||||
|
||||
ImGui::BeginChild("Projects", projectsWindowSize, true);
|
||||
{
|
||||
for (uint32_t i = 0; i < std::size(_Projects); i++)
|
||||
{
|
||||
DrawProjectItem(i);
|
||||
}
|
||||
|
||||
const float itemHeight = 120.0f;
|
||||
if (ImGui::Button("Import an existing project", ImVec2(ImGui::GetContentRegionAvailWidth(), itemHeight)))
|
||||
{
|
||||
const std::string path = FileDialog::OpenFile("Project file |*.project");
|
||||
if (path != "" && String::EndsWith(path, ".project"))
|
||||
{
|
||||
// Prevent importing the same project twice in the list
|
||||
bool alreadyContainsProject = false;
|
||||
for (auto& p : _Projects)
|
||||
{
|
||||
if (p.Path == path)
|
||||
{
|
||||
alreadyContainsProject = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!alreadyContainsProject)
|
||||
{
|
||||
_Projects.push_back(ProjectPreview(path));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ImGui::EndChild();
|
||||
}
|
||||
|
||||
void WelcomeWindow::DrawProjectItem(const uint32_t itemIndex)
|
||||
{
|
||||
const ProjectPreview& project = _Projects[itemIndex];
|
||||
const uint32_t itemHeight = 120;
|
||||
const float cursorYStart = ImGui::GetCursorPosY();
|
||||
|
||||
const std::string selectableName = "##" + std::to_string(itemIndex);
|
||||
const bool isSelected = SelectedProject == itemIndex;
|
||||
if (ImGui::Selectable(selectableName.c_str(), isSelected, ImGuiSelectableFlags_AllowItemOverlap, ImVec2(ImGui::GetContentRegionAvailWidth(), itemHeight)))
|
||||
{
|
||||
SelectedProject = itemIndex;
|
||||
}
|
||||
|
||||
const ImVec2 padding = ImVec2(25.0f, 20.0f);
|
||||
const ImVec2 iconSize = ImVec2(100, 100);
|
||||
ImGui::SetCursorPos(padding / 2.0 + ImVec2(0, cursorYStart));
|
||||
|
||||
ImGui::Image((ImTextureID)project.ProjectIcon->GetID(), iconSize, ImVec2(0, 1), ImVec2(1, 0));
|
||||
ImGui::SameLine();
|
||||
ImGui::SetCursorPosX(padding.x + iconSize.x + padding.x);
|
||||
|
||||
ImGui::SetCursorPosX(padding.x + iconSize.x + padding.x);
|
||||
ImGui::SetCursorPosY(cursorYStart + padding.y);
|
||||
{
|
||||
UIFont boldfont = UIFont(Fonts::LargeBold);
|
||||
ImGui::Text(project.Name.c_str());
|
||||
}
|
||||
|
||||
ImGui::SetCursorPosY(cursorYStart + padding.y + 35.f);
|
||||
{
|
||||
ImGui::SetCursorPosX(padding.x + iconSize.x + padding.x);
|
||||
UIFont boldfont = UIFont(Fonts::Bold);
|
||||
ImGui::Text(project.Description.c_str());
|
||||
}
|
||||
|
||||
ImGui::SetCursorPosY(cursorYStart + itemHeight);
|
||||
}
|
||||
|
||||
void WelcomeWindow::DrawRightControls()
|
||||
{
|
||||
const float buttonHeight = 58.0f;
|
||||
ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(0, 0, 0, 0));
|
||||
if (ImGui::BeginChild("Controls", ImGui::GetContentRegionAvail(), false))
|
||||
{
|
||||
const ImVec2 buttonSize = ImVec2(ImGui::GetContentRegionAvailWidth(), buttonHeight);
|
||||
if (ImGui::Button("Create a new project", buttonSize))
|
||||
{
|
||||
std::string selectedProject = FileDialog::SaveFile("Project file\0*.project");
|
||||
|
||||
if (!selectedProject.empty())
|
||||
{
|
||||
if(!String::EndsWith(selectedProject, ".project"))
|
||||
selectedProject += ".project";
|
||||
|
||||
auto backslashSplits = String::Split(selectedProject, '\\');
|
||||
auto fileName = backslashSplits[backslashSplits.size() - 1];
|
||||
|
||||
std::string finalPath = String::Split(selectedProject, '.')[0];
|
||||
|
||||
if (String::EndsWith(fileName, ".project"))
|
||||
{
|
||||
// We need to create a folder
|
||||
if (const auto& dirPath = finalPath;
|
||||
!std::filesystem::create_directory(dirPath))
|
||||
{
|
||||
// Should we continue?
|
||||
Logger::Log("Failed creating project directory: " + dirPath);
|
||||
}
|
||||
|
||||
finalPath += "\\" + fileName;
|
||||
}
|
||||
|
||||
auto project = Project::New(String::Split(fileName, '.')[0], "no description", finalPath);
|
||||
Engine::LoadProject(project);
|
||||
Engine::LoadScene(Scene::New());
|
||||
project->Save();
|
||||
|
||||
auto projectPreview = ProjectPreview();
|
||||
projectPreview.Name = project->Name;
|
||||
projectPreview.Description = project->Description;
|
||||
projectPreview.Path = project->FullPath;
|
||||
|
||||
_Projects.push_back(projectPreview);
|
||||
}
|
||||
}
|
||||
ImGui::Separator();
|
||||
|
||||
if (SelectedProject != -1)
|
||||
{
|
||||
if (ImGui::Button("Open an existing Project", buttonSize))
|
||||
{
|
||||
assert(SelectedProject < std::size(_Projects));
|
||||
|
||||
using namespace Nuake;
|
||||
|
||||
SaveRecentFile();
|
||||
|
||||
std::string projectPath = _Projects[SelectedProject].Path;
|
||||
FileSystem::SetRootDirectory(projectPath + "/../");
|
||||
|
||||
auto project = Project::New();
|
||||
auto projectFileData = FileSystem::ReadFile(projectPath, true);
|
||||
try
|
||||
{
|
||||
project->Deserialize(projectFileData);
|
||||
project->FullPath = projectPath;
|
||||
|
||||
Engine::LoadProject(project);
|
||||
|
||||
_Editor->filesystem->m_CurrentDirectory = Nuake::FileSystem::RootDirectory;
|
||||
}
|
||||
catch (std::exception exception)
|
||||
{
|
||||
Logger::Log("Error loading project: " + projectPath, CRITICAL);
|
||||
Logger::Log(exception.what());
|
||||
}
|
||||
|
||||
Engine::GetCurrentWindow()->SetTitle("Nuake Engine - Editing " + project->Name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::EndChild();
|
||||
ImGui::PopStyleColor();
|
||||
}
|
||||
|
||||
void WelcomeWindow::ParseRecentFile()
|
||||
{
|
||||
if (!FileSystem::FileExists(_RecentProjectFilePath, true))
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
#pragma once
|
||||
#include <src/Resource/Serializable.h>
|
||||
#include <src/Core/Core.h>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace Nuake
|
||||
{
|
||||
class Texture;
|
||||
class ProjectPreview : ISerializable
|
||||
{
|
||||
public:
|
||||
@@ -12,6 +15,7 @@ namespace Nuake
|
||||
std::string Path;
|
||||
std::string Description;
|
||||
|
||||
Ref<Texture> ProjectIcon;
|
||||
|
||||
ProjectPreview(const std::string& path);
|
||||
ProjectPreview() = default;
|
||||
@@ -19,6 +23,7 @@ namespace Nuake
|
||||
|
||||
json Serialize() override;
|
||||
bool Deserialize(const std::string& data) override;
|
||||
|
||||
private:
|
||||
void ReadProjectFile();
|
||||
};
|
||||
@@ -27,19 +32,27 @@ namespace Nuake
|
||||
class WelcomeWindow
|
||||
{
|
||||
private:
|
||||
const std::string NUAKE_LOGO_PATH = "resources/Images/logo_white.png";
|
||||
Ref<Texture> _NuakeLogo;
|
||||
EditorInterface* _Editor;
|
||||
|
||||
const std::string _RecentProjectFilePath = "recent.json";
|
||||
const std::string _RecentProjectFileDefaultContent = "{ \"Projects\": [ ] }";
|
||||
std::vector<ProjectPreview> _Projects;
|
||||
|
||||
uint32_t SelectedProject = 0;
|
||||
EditorInterface* _Editor;
|
||||
public:
|
||||
std::vector<ProjectPreview> _Projects;
|
||||
|
||||
public:
|
||||
WelcomeWindow(Nuake::EditorInterface* editor);
|
||||
~WelcomeWindow() = default;
|
||||
|
||||
void Draw();
|
||||
|
||||
private:
|
||||
void DrawRecentProjectsSection();
|
||||
void DrawProjectItem(const uint32_t projectPreview);
|
||||
void DrawRightControls();
|
||||
|
||||
void ParseRecentFile();
|
||||
void SaveRecentFile();
|
||||
};
|
||||
|
||||
@@ -12,6 +12,8 @@ using Ref = std::shared_ptr<T>;
|
||||
template<typename T>
|
||||
using Scope = std::unique_ptr<T>;
|
||||
|
||||
template<typename T>
|
||||
using Weak = std::weak_ptr<T>;
|
||||
|
||||
template<typename T, typename ... Args>
|
||||
constexpr Ref<T> CreateRef(Args&& ... args)
|
||||
@@ -23,4 +25,4 @@ template<typename T, typename ... Args>
|
||||
constexpr Scope<T> CreateScope(Args&& ... args)
|
||||
{
|
||||
return std::make_unique<T>(std::forward<Args>(args)...);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -231,7 +231,8 @@ namespace Nuake
|
||||
// You should definitely not call this every frame or when e.g. streaming in a new level section as it is an expensive operation.
|
||||
// Instead insert all new objects in batches instead of 1 at a time to keep the broad phase efficient.
|
||||
//_JoltPhysicsSystem->OptimizeBroadPhase();
|
||||
_JoltJobSystem = new JPH::JobSystemThreadPool(JPH::cMaxPhysicsJobs, JPH::cMaxPhysicsBarriers, std::thread::hardware_concurrency() - 1);
|
||||
const uint32_t availableThreads = std::thread::hardware_concurrency() - 1;
|
||||
_JoltJobSystem = new JPH::JobSystemThreadPool(JPH::cMaxPhysicsJobs, JPH::cMaxPhysicsBarriers, availableThreads);
|
||||
}
|
||||
|
||||
void DynamicWorld::DrawDebug()
|
||||
@@ -308,6 +309,7 @@ namespace Nuake
|
||||
}
|
||||
|
||||
assert("Entity doesn't have a character controller component.");
|
||||
return false;
|
||||
}
|
||||
|
||||
RaycastResult DynamicWorld::Raycast(glm::vec3 from, glm::vec3 to)
|
||||
@@ -408,11 +410,15 @@ namespace Nuake
|
||||
// Do 1 collision step per 1 / 60th of a second (round up).
|
||||
int collisionSteps = 1;
|
||||
constexpr float minStepDuration = 1.0f / 90.0f;
|
||||
constexpr int maxStepCount = 4;
|
||||
if(ts > minStepDuration)
|
||||
{
|
||||
collisionSteps = static_cast<float>(ts) / minStepDuration;
|
||||
}
|
||||
|
||||
// Prevents having too many steps and running out of jobs
|
||||
collisionSteps = std::min(collisionSteps, maxStepCount);
|
||||
|
||||
// If you want more accurate step results you can do multiple sub steps within a collision step. Usually you would set this to 1.
|
||||
constexpr int subSteps = 1;
|
||||
|
||||
|
||||
@@ -163,9 +163,16 @@ namespace Nuake
|
||||
for (auto& v : j["Vertices"])
|
||||
{
|
||||
Vertex vertex;
|
||||
|
||||
try {
|
||||
DESERIALIZE_VEC2(v["UV"], vertex.uv)
|
||||
}
|
||||
catch(std::exception& e) {
|
||||
vertex.uv = { 0.0, 0.0 };
|
||||
}
|
||||
DESERIALIZE_VEC3(v["Position"], vertex.position)
|
||||
DESERIALIZE_VEC3(v["Normal"], vertex.normal)
|
||||
DESERIALIZE_VEC2(v["UV"], vertex.uv)
|
||||
|
||||
DESERIALIZE_VEC3(v["Tangent"], vertex.tangent)
|
||||
DESERIALIZE_VEC3(v["Bitangent"], vertex.bitangent)
|
||||
|
||||
|
||||
@@ -53,6 +53,11 @@ namespace Nuake
|
||||
projectFile.close();
|
||||
}
|
||||
|
||||
bool Project::FileExist()
|
||||
{
|
||||
return std::filesystem::exists(this->FullPath.c_str());
|
||||
}
|
||||
|
||||
Ref<Project> Project::New(const std::string& Name, const std::string& Description, const std::string& FullPath)
|
||||
{
|
||||
return CreateRef<Project>(Name, Description, FullPath);
|
||||
|
||||
@@ -26,6 +26,7 @@ namespace Nuake
|
||||
|
||||
void Save();
|
||||
void SaveAs(const std::string& FullPath);
|
||||
bool FileExist();
|
||||
|
||||
static Ref<Project> New(const std::string& Name, const std::string& Description, const std::string& FullPath);
|
||||
static Ref<Project> New();
|
||||
|
||||
@@ -102,17 +102,29 @@ namespace Nuake
|
||||
json ProceduralSky::Serialize()
|
||||
{
|
||||
BEGIN_SERIALIZE()
|
||||
SERIALIZE_VAL(SurfaceRadius);
|
||||
SERIALIZE_VAL(SurfaceRadius);
|
||||
SERIALIZE_VAL(AtmosphereRadius);
|
||||
SERIALIZE_VEC3(RayleighScattering);
|
||||
SERIALIZE_VEC3(MieScattering);
|
||||
SERIALIZE_VAL(SunIntensity);
|
||||
SERIALIZE_VEC3(SunDirection);
|
||||
SERIALIZE_VEC3(CenterPoint);
|
||||
END_SERIALIZE();
|
||||
}
|
||||
|
||||
bool ProceduralSky::Deserialize(const std::string& str)
|
||||
{
|
||||
return false;
|
||||
BEGIN_DESERIALIZE()
|
||||
DESERIALIZE_VEC3(j["SunDirection"], SunDirection)
|
||||
DESERIALIZE_VEC3(j["RayleighScattering"], RayleighScattering)
|
||||
DESERIALIZE_VEC3(j["MieScattering"], MieScattering)
|
||||
if (j.contains("CenterPoint"))
|
||||
{
|
||||
DESERIALIZE_VEC3(j["CenterPoint"], CenterPoint)
|
||||
}
|
||||
SurfaceRadius = j["SurfaceRadius"];
|
||||
AtmosphereRadius = j["AtmosphereRadius"];
|
||||
SunIntensity = j["SunIntensity"];
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,6 +68,11 @@ namespace Nuake {
|
||||
VolumetricFog = j["VolumetricFog"];
|
||||
if (j.contains("VolumetricStepCount"))
|
||||
VolumetricStepCount = j["VolumetricStepCount"];
|
||||
if (j.contains("ProceduralSkybox"))
|
||||
{
|
||||
ProceduralSkybox->Deserialize(j["ProceduralSkybox"].dump());
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
71
README.md
71
README.md
@@ -1,44 +1,47 @@
|
||||

|
||||
# Nuake
|
||||
Feel free to join the discord server for updates:
|
||||

|
||||
|
||||
[](https://discord.gg/kuF4efPK7Y)
|
||||
# What is it
|
||||
Nuake is a game engine written from scratch by myself. It is not meant to be a end-all be-all engine and it is not a quakespasm type engine. This is a game engine that focuses on fast level design iteration that integrates with quake level editing software. If you can create quake maps, you can create Nuake levels.
|
||||
`Warning: It is still very early in development and I dont recommend anyone using this to make their games *yet*. `
|
||||
## Quake inspired game engine
|
||||
Nuake is a game engine currently in developement that focuses on fast level design iteration time that integrates with quake level editing software.
|
||||
> It is currently in developement and is not feature complete. We are currently aiming for an alpha release with basic features and a demo level.
|
||||
|
||||
# How to build
|
||||

|
||||
|
||||
|
||||
You can join the discord server for updates and screenshots or if you want to contribute:
|
||||
|
||||
[](https://discord.gg/kuF4efPK7Y)
|
||||
|
||||
|
||||
## Current Features
|
||||
- ECS system
|
||||
- Modern physic engine (Jolt physic)
|
||||
- PBR Renderer
|
||||
- Post processing effects(Bloom, SSAO, SSR, Volumetrics, Procedural Sky)
|
||||
- Wren Scripting
|
||||
- Trenchbroom integration
|
||||
|
||||
## Planned features
|
||||
- C# Scripting
|
||||
- Custom Shaders
|
||||
- Spatialized audio
|
||||
- NuakeUI integration
|
||||
- Dynamic global illumination
|
||||
- Asset packing
|
||||
- Terrain editor
|
||||
- WAD converter
|
||||
|
||||
## Contributing
|
||||
We are currently looking for contributors, feel free to join the discord if you are looking to help the project.
|
||||
|
||||
## Compiling the engine
|
||||
1. Clone the repos using `git clone --recurse-submodules https://github.com/antopilo/Nuake.git`
|
||||
2. Run the `generate.bat` to generate the sln files.
|
||||
3. Open `Nuake.sln`
|
||||
4. Build and run
|
||||
|
||||
# Contributing
|
||||
Feel free to make pull requests and I will look over them myself.
|
||||
# Documentation and demos
|
||||
> The current documentation is not up to date.
|
||||
|
||||
# Documentation
|
||||
You can access the current documentation at [here](https://nuake.readthedocs.io/en/latest/index.html)
|
||||
You can access the current documentation [here](https://nuake.readthedocs.io/en/latest/index.html).
|
||||
|
||||
# Features
|
||||
- Trenchbroom integration with live reload
|
||||
- fast ECS & Scene tree system
|
||||
- Jolt physics
|
||||
- PBR rendering
|
||||
- Volumetric lighting
|
||||
- Parallax mapping
|
||||
- Game in editor
|
||||
- Procedural & HDR skies
|
||||
- Modern UI system
|
||||
- Wren scripting api and module system
|
||||
- Triggers and entity editing in TB
|
||||
|
||||
# Planned
|
||||
- Advanced physics features
|
||||
- Soft bodies, Joints, etc.
|
||||
- Demo level
|
||||
- Cross platform
|
||||
- Optimization
|
||||
- Water simulation
|
||||
- Terrain editing
|
||||
- Exporting
|
||||
- Custom shaders
|
||||
|
||||
65
nuake.4coder
65
nuake.4coder
@@ -1,65 +0,0 @@
|
||||
version(1);
|
||||
project_name = "4coder custom";
|
||||
|
||||
patterns = {
|
||||
"*.c",
|
||||
"*.cpp",
|
||||
"*.h",
|
||||
"*.m",
|
||||
"*.mm",
|
||||
"*.bat",
|
||||
"*.sh",
|
||||
"*.4coder",
|
||||
"*.txt",
|
||||
};
|
||||
blacklist_patterns = {
|
||||
".*",
|
||||
};
|
||||
load_paths_custom = {
|
||||
{"."},
|
||||
};
|
||||
load_paths = {
|
||||
{ load_paths_custom, .os = "win" },
|
||||
{ load_paths_custom, .os = "linux"},
|
||||
{ load_paths_custom, .os = "mac" },
|
||||
};
|
||||
|
||||
build_super_x64_win32 = "custom\\bin\\buildsuper_x64-win.bat";
|
||||
build_super_x86_win32 = "custom\\bin\\buildsuper_x86-win.bat";
|
||||
build_super_x64_linux = "custom/bin/buildsuper_x64-linux.sh";
|
||||
build_super_x86_linux = "custom/bin/buildsuper_x86-linux.sh";
|
||||
build_super_x64_mac = "custom/bin/buildsuper_x64-mac.sh";
|
||||
|
||||
command_list = {
|
||||
{ .name = "build super x64",
|
||||
.out = "*compilation*", .footer_panel = true, .save_dirty_files = true,
|
||||
.cmd = { {build_super_x64_win32, .os ="win" },
|
||||
{build_super_x64_linux , .os ="linux"},
|
||||
{build_super_x64_mac , .os ="mac" }, }, },
|
||||
|
||||
{ .name = "build super x86",
|
||||
.out = "*compilation*", .footer_panel = true, .save_dirty_files = true,
|
||||
.cmd = { {build_super_x86_win32, .os ="win" },
|
||||
{build_super_x86_linux, .os ="linux" }, }, },
|
||||
|
||||
{ .name = "build C++ lexer generator",
|
||||
.out = "*compilation*", .footer_panel = true, .save_dirty_files = true,
|
||||
.cmd = { {"custom\\bin\\build_one_time custom\\languages\\4coder_cpp_lexer_gen.cpp ..\\build", .os ="win" },
|
||||
}, },
|
||||
|
||||
{ .name = "build token tester",
|
||||
.out = "*compilation*", .footer_panel = true, .save_dirty_files = true,
|
||||
.cmd = { {"custom\\bin\\build_one_time custom\\languages\\4coder_cpp_lexer_test.cpp ..\\build", .os = "win" },
|
||||
}, },
|
||||
|
||||
{ .name = "run one time",
|
||||
.out = "*run*", .footer_panel = false, .save_dirty_files = false,
|
||||
.cmd = { {"pushd ..\\build & one_time", .os = "win" },
|
||||
}, },
|
||||
};
|
||||
|
||||
fkey_command[1] = "build super x64";
|
||||
fkey_command[2] = "build C++ lexer generator";
|
||||
fkey_command[3] = "build token tester";
|
||||
fkey_command[4] = "run one time";
|
||||
fkey_command[5] = "build super x86";
|
||||
Reference in New Issue
Block a user