Archived
1
Fork 0

Merge remote-tracking branch 'origin/master'

This commit is contained in:
redstrate 2021-10-12 10:13:33 -04:00
commit 2772553e2c
66 changed files with 1452 additions and 3761 deletions

View file

@ -22,7 +22,9 @@ endif()
if(${CMAKE_SYSTEM_NAME} STREQUAL "Darwin" AND NOT IOS)
message("macOS build detected!")
set(ENABLE_METAL TRUE)
find_package(SDL2 REQUIRED)
set(ENABLE_VULKAN TRUE)
set(ENABLE_DARWIN TRUE)
set(ENABLE_MACOS TRUE)
@ -71,7 +73,9 @@ set(CROSS_LIBS
add_subdirectory(extern)
# enable lto
if (CMAKE_BUILD_TYPE EQUAL "Release")
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE)
endif ()
add_subdirectory(platforms)

View file

@ -2,11 +2,6 @@ macro(set_engine_properties target)
target_compile_features(${target} PUBLIC cxx_std_17)
set_target_properties(${target} PROPERTIES CXX_EXTENSIONS OFF)
if(ENABLE_MACOS OR ENABLE_LINUX)
target_compile_options(${target} PUBLIC
-fpermissive) # ew but required for now TODO: remove and test!
endif()
if(ENABLE_MACOS)
target_compile_definitions(${target} PUBLIC PLATFORM_MACOS)
endif()

View file

@ -20,7 +20,7 @@ std::unique_ptr<Mesh> load_mesh(const prism::path path) {
auto file = prism::open_file(path, true);
if(!file.has_value()) {
prism::log::error(System::Renderer, "Failed to load mesh from {}!", path);
prism::log("Failed to load mesh from {}!", path.string());
return nullptr;
}
@ -29,7 +29,7 @@ std::unique_ptr<Mesh> load_mesh(const prism::path path) {
if(version == 5 || version == 6) {
} else {
prism::log::error(System::Renderer, "{} failed the mesh version check! reported version = {}", path, std::to_string(version));
prism::log("{} failed the mesh version check! reported version = {}", path.string(), version);
return nullptr;
}
@ -190,7 +190,7 @@ std::unique_ptr<Texture> load_texture(const prism::path path) {
auto file = prism::open_file(path, true);
if(!file.has_value()) {
prism::log::error(System::Renderer, "Failed to load texture from {}!", path);
prism::log("Failed to load texture from {}!", path.string());
return nullptr;
}
@ -202,7 +202,7 @@ std::unique_ptr<Texture> load_texture(const prism::path path) {
int width, height, channels;
unsigned char* data = stbi_load_from_memory(file->cast_data<unsigned char>(), file->size(), &width, &height, &channels, 4);
if(!data) {
prism::log::error(System::Renderer, "Failed to load texture from {}!", path);
prism::log("Failed to load texture from {}!", path.string());
return nullptr;
}
@ -247,7 +247,7 @@ std::unique_ptr<Material> load_material(const prism::path path) {
auto file = prism::open_file(path);
if(!file.has_value()) {
prism::log::error(System::Core, "Failed to load material from {}!", path);
prism::log("Failed to load material from {}!", path.string());
return {};
}
@ -258,7 +258,7 @@ std::unique_ptr<Material> load_material(const prism::path path) {
mat->path = path.string();
if(!j.count("version") || j["version"] != 2) {
prism::log::error(System::Core, "Material {} failed the version check!", path);
prism::log("Material {} failed the version check!", path.string());
return mat;
}

View file

@ -6,8 +6,6 @@ set(SRC
include/physics.hpp
include/scene.hpp
include/imgui_backend.hpp
include/uielement.hpp
include/screen.hpp
include/object.hpp
include/debug.hpp
include/components.hpp
@ -19,7 +17,6 @@ set(SRC
src/input.cpp
src/physics.cpp
src/imgui_backend.cpp
src/screen.cpp
src/scene.cpp
src/debug.cpp
src/console.cpp)

View file

@ -103,17 +103,6 @@ struct Camera {
Matrix4x4 view, perspective;
};
namespace ui {
class Screen;
}
struct UI {
int width = 1920, height = 1080;
std::string ui_path;
ui::Screen* screen = nullptr;
};
struct EnvironmentProbe {
bool is_sized = true;
prism::float3 size = prism::float3(10);

View file

@ -2,6 +2,7 @@
#include <string_view>
void draw_console();
void draw_debug_ui();
void load_debug_options();

View file

@ -10,11 +10,6 @@
#include "path.hpp"
class GFX;
namespace ui {
class Screen;
}
class Scene;
class RenderTarget;
class Physics;
@ -141,22 +136,6 @@ namespace prism {
*/
void save_scene(std::string_view path);
/** Load a UI screen from disk. This will not change the current screen.
@param path The screen file path.
@return Returns a instance of the screen if successful, and nullptr on failure.
*/
ui::Screen* load_screen(const prism::path& path);
/** Set the current screen.
@param screen The screen object to set as current. Can be null.
*/
void set_screen(ui::Screen* screen);
/** Gets the current screen.
@return The current screen. Can be null.
*/
[[nodiscard]] ui::Screen* get_screen() const;
/** Load a prefab from disk.
@param scene The scene to add the prefab to.
@param path The prefab file path.
@ -231,30 +210,11 @@ namespace prism {
*/
void process_mouse_down(int button, prism::Offset offset);
/** Pushes a UI event for the current screen. Does nothing if there is no screen set.
@param name The name of the event.
@param data Data for the event. Defaulted to an empty string.
/**
* Called when text has been inputted. This is only emitted when text input is enabled thru input system
* @param string
*/
void push_event(std::string_view name, std::string_view data = "");
/** Load a localization file from disk. This will change the current localization.
@param path The localization file path.
*/
void load_localization(std::string_view path);
/** Queries whether or not the current localization loaded has a key.
@param id The key to query.
@return Whether or not the locale has the key specified.
@note Having no localization loaded will always return false.
*/
[[nodiscard]] bool has_localization(std::string_view id) const;
/** Localizes a string.
@param id The locale key to use.
@return A localized string if the key is found, or an empty string if not found.
@note Having no localization loaded will always return a empty string.
*/
std::string localize(const std::string& id);
void process_text_input(const std::string_view string);
/** Adds a timer to the list of timers.
@param timer The timer to add.
@ -288,6 +248,10 @@ namespace prism {
*/
void update_scene(Scene& scene);
imgui_backend& get_imgui() {
return *imgui;
}
/// The current cutscene.
std::unique_ptr<Cutscene> cutscene;
@ -328,6 +292,8 @@ namespace prism {
bool debug_enabled = false;
#endif
bool console_enabled = true;
private:
void setup_scene(Scene& scene);
@ -335,8 +301,6 @@ namespace prism {
bool paused = false;
ui::Screen* current_screen = nullptr;
Scene* current_scene = nullptr;
std::vector<std::unique_ptr<Scene>> scenes;
std::map<std::string, Scene*> path_to_scene;

View file

@ -1,5 +1,8 @@
#pragma once
#include <string_view>
#include <filesystem>
namespace prism {
class imgui_backend {
public:
@ -14,7 +17,33 @@ namespace prism {
void process_key_down(unsigned int key_code);
void process_key_up(unsigned int key_code);
void process_text_input(const std::string_view string);
/**Opens a file dialog to select a file. This will not block.
@param existing Whether or not to limit to existing files.
@param returnFunction The callback function when a file is selected or the dialog is cancelled. An empy string is returned when cancelled.
@param openDirectory Whether or not to allow selecting directories as well.
*/
void open_dialog(const bool existing, std::function<void(std::string)> returnFunction, bool openDirectory = false);
/**Opens a file dialog to select a save location for a file. This will not block.
@param returnFunction The callback function when a file is selected or the dialog is cancelled. An empy string is returned when cancelled.
*/
void save_dialog(std::function<void(std::string)> returnFunction);
private:
bool mouse_buttons[3] = {};
bool open_dialog_next_frame = false;
bool save_dialog_next_frame = false;
struct dialog_data {
std::filesystem::path current_path;
std::filesystem::path selected_path;
std::function<void(std::string)> return_function;
};
dialog_data open_dialog_data;
dialog_data save_dialog_data;
};
}

View file

@ -37,7 +37,7 @@ namespace ImGui {
ImGui::ProgressBar(progress_saturated, ImVec2(0.0f, 0.0f), buf);
ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x);
ImGui::Text(label);
ImGui::Text("%s", label);
}
inline bool DragQuat(const char* label, Quaternion* quat) {

View file

@ -81,8 +81,16 @@ namespace prism {
/// Returns all of the bindings registered with this Input system.
[[nodiscard]] std::vector<input_binding> get_bindings() const;
void begin_text_input();
void end_text_input();
bool is_text_input() const;
bool get_allowable_text_button(unsigned int keycode) const;
private:
std::vector<input_binding> _input_bindings;
std::tuple<int, int> _last_cursor_position;
bool _in_text_input = false;
};
}

View file

@ -201,7 +201,7 @@ class GFXFramebuffer;
class GFXTexture;
/// Represents a scene consisting of Objects with varying Components.
class Scene : public ObjectComponents<Data, Transform, Renderable, Light, Camera, Collision, Rigidbody, UI, EnvironmentProbe> {
class Scene : public ObjectComponents<Data, Transform, Renderable, Light, Camera, Collision, Rigidbody, EnvironmentProbe> {
public:
/// If loaded from disk, the path to the scene file this originated from.
std::string path;

View file

@ -1,43 +0,0 @@
#pragma once
#include <string>
#include <vector>
#include <map>
#include <functional>
#include "uielement.hpp"
#include "file.hpp"
#include "common.hpp"
class GFXBuffer;
namespace ui {
class Screen {
public:
Screen() {}
Screen(const prism::path path);
void process_event(const std::string& type, std::string data = "");
void process_mouse(const int x, const int y);
void calculate_sizes();
std::vector<UIElement> elements;
UIElement* find_element(const std::string& id);
using CallbackFunction = std::function<void()>;
std::map<std::string, CallbackFunction> listeners;
bool blurs_background = false;
void add_listener(const std::string& id, std::function<void()> callback);
prism::Extent extent;
bool view_changed = false;
GFXBuffer* glyph_buffer = nullptr;
GFXBuffer* instance_buffer = nullptr;
GFXBuffer* elements_buffer = nullptr;
};
}

View file

@ -1,67 +0,0 @@
#pragma once
#include <string>
#include "assetptr.hpp"
enum class MetricType {
Absolute,
Relative,
Offset
};
struct Color {
Color() : r(1.0f), g(1.0f), b(1.0f), a(1.0f) {}
Color(const float v) : r(v), g(v), b(v), a(1.0f) {}
union {
struct {
float r, g, b, a;
};
float v[4];
};
};
class GFXTexture;
class Texture;
class UIElement {
public:
struct Metrics {
struct Metric {
MetricType type = MetricType::Absolute;
int value = 0;
};
Metric x, y, width, height;
} metrics;
struct Background {
Color color = Color(0.0f);
std::string image;
AssetPtr<Texture> texture;
} background;
enum class TextLocation {
TopLeft,
Center
} text_location = TextLocation::TopLeft;
std::string id, text, parent;
bool wrap_text = false;
float absolute_x = 0.0f, absolute_y = 0.0f, absolute_width = 0.0f, absolute_height = 0.0f;
float text_x = 0.0f, text_y = 0.0f;
bool visible = true;
std::string on_click_script;
};
inline bool operator==(const UIElement& a, const UIElement& b) {
return a.id == b.id;
}

View file

@ -39,7 +39,7 @@ void prism::console::invoke_command(const std::string_view name, const prism::co
invalid_format = true;
if(invalid_format) {
prism::log::info(System::Core, "Invalid command format!");
prism::log("Invalid command format!");
} else {
command_data.function(console::arguments());
}
@ -59,7 +59,7 @@ void prism::console::invoke_command(const std::string_view name, const prism::co
invalid_format = true;
if(invalid_format) {
prism::log::info(System::Core, "Wrong or empty variable type!");
prism::log("Wrong or empty variable type!");
} else {
auto argument = arguments[0];
switch(argument.query_type()) {
@ -73,7 +73,7 @@ void prism::console::invoke_command(const std::string_view name, const prism::co
}
}
prism::log::info(System::Core, "{} is not the name of a valid command or variable!", name.data());
prism::log("{} is not the name of a valid command or variable!", name.data());
}
void prism::console::parse_and_invoke_command(const std::string_view command) {

View file

@ -12,6 +12,8 @@
#include "scene.hpp"
#include "renderer.hpp"
#include "file.hpp"
#include "console.hpp"
#include "imgui_backend.hpp"
struct Options {
std::string shader_source_path;
@ -168,7 +170,7 @@ void draw_shader_editor() {
if(options.shader_source_path.empty()) {
ImGui::Text("You haven't specified a shader source path yet. Please select one below:");
if(ImGui::Button("Select Path")) {
platform::open_dialog(false, [](std::string path) {
engine->get_imgui().open_dialog(false, [](std::string path) {
// open_dialog() can't select folders yet, so use this as a workaround
options.shader_source_path = prism::path(path).parent_path().string();
});
@ -244,3 +246,23 @@ void save_debug_options() {
std::string_view get_shader_source_directory() {
return options.shader_source_path;
}
void draw_console() {
ImGui::Text("%s", prism::log_output.c_str());
static std::string console_input;
ImGui::SetNextItemWidth(-1.0f);
const bool input_return = ImGui::InputText("##console_input", &console_input, ImGuiInputTextFlags_EnterReturnsTrue);
if(input_return)
ImGui::SetKeyboardFocusHere(-1);
const bool button_return = ImGui::Button("Input");
if(input_return || button_return) {
prism::console::parse_and_invoke_command(console_input);
console_input.clear();
}
}

View file

@ -11,7 +11,6 @@
#include "json_conversions.hpp"
#include "app.hpp"
#include "assertions.hpp"
#include "screen.hpp"
#include "renderer.hpp"
#include "gfx.hpp"
#include "imgui_backend.hpp"
@ -27,10 +26,10 @@
using prism::engine;
engine::engine(const int argc, char* argv[]) {
log::info(System::Core, "Prism Engine loading...");
log("Prism Engine loading...");
console::register_command("test_cmd", console::argument_format(0), [](const console::arguments&) {
log::info(System::Core, "Test cmd!");
log("Test cmd!");
});
console::register_variable("rs_dynamic_resolution", render_options.dynamic_resolution);
@ -60,26 +59,12 @@ prism::app* engine::get_app() const {
return app;
}
void engine::load_localization(const std::string_view path) {
Expects(!path.empty());
auto file = prism::open_file(prism::app_domain / path);
if(file.has_value()) {
nlohmann::json j;
file->read_as_stream() >> j;
strings = j["strings"].get<std::map<std::string, std::string>>();
}
}
void engine::pause() {
paused = true;
push_event("engine_pause");
}
void engine::unpause() {
paused = false;
push_event("engine_unpause");
}
bool engine::is_paused() const {
@ -94,6 +79,8 @@ void engine::quit() {
bool engine::is_quitting() const {
if(!windows.empty())
return windows[0]->quit_requested;
return false;
}
void engine::prepare_quit() {
@ -136,7 +123,7 @@ Scene* engine::load_scene(const prism::path& path) {
auto file = prism::open_file(path);
if(!file.has_value()) {
prism::log::error(System::Core, "Failed to load scene from {}!", path);
prism::log("Failed to load scene from {}!", path.string());
return nullptr;
}
@ -191,25 +178,6 @@ void engine::save_scene(const std::string_view path) {
out << j;
}
ui::Screen* engine::load_screen(const prism::path& path) {
Expects(!path.empty());
return new ui::Screen(path);
}
void engine::set_screen(ui::Screen* screen) {
current_screen = screen;
screen->extent = windows[0]->extent;
screen->calculate_sizes();
get_renderer()->set_screen(screen);
}
ui::Screen* engine::get_screen() const {
return current_screen;
}
AnimationChannel engine::load_animation(nlohmann::json a) {
AnimationChannel animation;
@ -229,7 +197,7 @@ Animation engine::load_animation(const prism::path& path) {
auto file = prism::open_file(path, true);
if(!file.has_value()) {
prism::log::error(System::Core, "Failed to load animation from {}!", path);
prism::log("Failed to load animation from {}!", path.string());
return {};
}
@ -289,7 +257,7 @@ void engine::load_cutscene(const prism::path& path) {
auto file = prism::open_file(path);
if(!file.has_value()) {
prism::log::error(System::Core, "Failed to load cutscene from {}!", path);
prism::log("Failed to load cutscene from {}!", path.string());
return;
}
@ -364,7 +332,7 @@ Object engine::add_prefab(Scene& scene, const prism::path& path, const std::stri
auto file = prism::open_file(path);
if(!file.has_value()) {
prism::log::error(System::Core, "Failed to load prefab from {}!", path);
prism::log("Failed to load prefab from {}!", path.string());
return NullObject;
}
@ -452,22 +420,21 @@ void engine::resize(const int identifier, const prism::Extent extent) {
gfx->recreate_view(identifier, drawable_extent.width, drawable_extent.height);
renderer->resize_render_target(*window->render_target, drawable_extent);
if(identifier == 0) {
if(current_screen != nullptr) {
current_screen->extent = extent;
current_screen->calculate_sizes();
}
}
}
void engine::process_key_down(const unsigned int keyCode) {
Expects(keyCode >= 0);
if(input->is_text_input() && !input->get_allowable_text_button(keyCode))
return;
imgui->process_key_down(keyCode);
if(keyCode == platform::get_keycode(debug_button) && !ImGui::GetIO().WantTextInput)
debug_enabled = !debug_enabled;
if(keyCode == platform::get_keycode(InputButton::C))
console_enabled = !console_enabled;
}
void engine::process_key_up(const unsigned int keyCode) {
@ -477,31 +444,9 @@ void engine::process_key_up(const unsigned int keyCode) {
}
void engine::process_mouse_down(const int button, const prism::Offset offset) {
if(current_screen != nullptr && button == 0)
current_screen->process_mouse(offset.x, offset.y);
imgui->process_mouse_down(button);
}
void engine::push_event(const std::string_view name, const std::string_view data) {
Expects(!name.empty());
if(current_screen != nullptr)
current_screen->process_event(name.data(), data.data());
}
bool engine::has_localization(const std::string_view id) const {
Expects(!id.empty());
return strings.count(id.data());
}
std::string engine::localize(const std::string& id) {
Expects(!id.empty());
return strings[id];
}
void engine::calculate_bone(Mesh& mesh, const Mesh::Part& part, Bone& bone, const Bone* parent_bone) {
if(part.offset_matrices.empty())
return;
@ -665,6 +610,9 @@ void engine::begin_frame(const float delta_time) {
if(debug_enabled)
draw_debug_ui();
if(console_enabled)
draw_console();
if(app != nullptr)
app->begin_frame();
}
@ -767,11 +715,6 @@ void engine::render(const int index) {
GFXCommandBuffer* commandbuffer = gfx->acquire_command_buffer(true);
if(index == 0) {
if(current_screen != nullptr && current_screen->view_changed) {
renderer->update_screen();
current_screen->view_changed = false;
}
imgui->render(0);
app->render(commandbuffer);
@ -864,3 +807,7 @@ void engine::setup_scene(Scene& scene) {
scene.reset_shadows();
scene.reset_environment();
}
void prism::engine::process_text_input(const std::string_view string) {
imgui->process_text_input(string);
}

View file

@ -21,7 +21,7 @@ std::optional<prism::file> prism::open_file(const prism::path path, const bool b
auto str = get_file_path(path).string();
FILE* file = fopen(str.c_str(), binary_mode ? "rb" : "r");
if(file == nullptr) {
prism::log::error(System::File, "Failed to open file handle from {}!", str);
prism::log("Failed to open file handle from {}!", str);
return {};
}

View file

@ -6,6 +6,7 @@
#include "engine.hpp"
#include "platform.hpp"
#include "assertions.hpp"
#include "input.hpp"
using prism::imgui_backend;
@ -160,6 +161,17 @@ imgui_backend::imgui_backend() {
void imgui_backend::begin_frame(const float delta_time) {
ImGuiIO& io = ImGui::GetIO();
const bool imgui_wants_text = io.WantTextInput;
const bool input_is_text = ::engine->get_input()->is_text_input();
if(imgui_wants_text != input_is_text) {
if(imgui_wants_text) {
::engine->get_input()->begin_text_input();
} else {
::engine->get_input()->end_text_input();
}
}
const auto [width, height] = platform::get_window_size(0);
const auto [dw, dh] = platform::get_window_drawable_size(0);
@ -192,6 +204,50 @@ void imgui_backend::begin_frame(const float delta_time) {
mouse_buttons[0] = mouse_buttons[1] = mouse_buttons[2] = false;
ImGui::NewFrame();
if(open_dialog_next_frame) {
ImGui::OpenPopup("Open File");
open_dialog_next_frame = false;
}
if(save_dialog_next_frame) {
ImGui::OpenPopup("Save File");
save_dialog_next_frame = false;
}
const auto dialog_function = [](dialog_data& data) {
if(ImGui::Selectable("..", false, ImGuiSelectableFlags_DontClosePopups))
data.current_path = data.current_path.parent_path();
for(auto dir_ent : std::filesystem::directory_iterator(data.current_path)) {
if(ImGui::Selectable(dir_ent.path().c_str(), data.selected_path == dir_ent.path(), ImGuiSelectableFlags_DontClosePopups)) {
if(dir_ent.is_directory())
data.current_path = dir_ent.path();
else
data.selected_path = dir_ent.path();
}
}
ImGui::TextDisabled("Selected: %s", data.selected_path.c_str());
if(ImGui::Button("OK")) {
data.return_function(data.selected_path.string());
ImGui::CloseCurrentPopup();
}
if(ImGui::Button("Cancel"))
ImGui::CloseCurrentPopup();
};
if(ImGui::BeginPopup("Open File")) {
dialog_function(open_dialog_data);
ImGui::EndPopup();
}
if(ImGui::BeginPopup("Save File")) {
dialog_function(save_dialog_data);
ImGui::EndPopup();
}
}
void imgui_backend::render(int index) {
@ -207,7 +263,6 @@ void imgui_backend::process_key_down(unsigned int key_code) {
Expects(key_code >= 0);
ImGuiIO& io = ImGui::GetIO();
io.AddInputCharactersUTF8(platform::translate_keycode(key_code));
io.KeysDown[key_code] = true;
}
@ -223,3 +278,20 @@ void imgui_backend::process_key_up(unsigned int key_code) {
void imgui_backend::process_mouse_down(int button) {
mouse_buttons[button] = true;
}
void prism::imgui_backend::process_text_input(const std::string_view string) {
ImGuiIO& io = ImGui::GetIO();
io.AddInputCharactersUTF8(string.data());
}
void prism::imgui_backend::open_dialog(const bool existing, std::function<void(std::string)> returnFunction, bool openDirectory) {
open_dialog_next_frame = true;
open_dialog_data.current_path = std::filesystem::current_path();
open_dialog_data.return_function = returnFunction;
}
void prism::imgui_backend::save_dialog(std::function<void(std::string)> returnFunction) {
save_dialog_next_frame = true;
save_dialog_data.current_path = std::filesystem::current_path();
save_dialog_data.return_function = returnFunction;
}

View file

@ -155,3 +155,27 @@ bool input_system::is_repeating(const std::string& name) {
std::vector<prism::input_binding> input_system::get_bindings() const {
return _input_bindings;
}
void input_system::begin_text_input() {
_in_text_input = true;
platform::begin_text_input();
}
void input_system::end_text_input() {
_in_text_input = false;
platform::end_text_input();
}
bool input_system::is_text_input() const {
return _in_text_input;
}
bool input_system::get_allowable_text_button(unsigned int keycode) const {
if(platform::get_keycode(InputButton::Backspace) == keycode)
return true;
if(platform::get_keycode(InputButton::Enter) == keycode)
return true;
return false;
}

View file

@ -65,12 +65,6 @@ void load_rigidbody_component(nlohmann::json j, Rigidbody& rigidbody) {
rigidbody.mass = j["mass"];
}
void load_ui_component(nlohmann::json j, UI& ui) {
ui.width = j["width"];
ui.height = j["height"];
ui.ui_path = j["path"];
}
void load_probe_component(nlohmann::json j, EnvironmentProbe& probe) {
if(j.contains("size"))
probe.size = j["size"];
@ -105,9 +99,6 @@ Object load_object(Scene& scene, const nlohmann::json obj) {
if(obj.contains("rigidbody"))
load_rigidbody_component(obj["rigidbody"], scene.add<Rigidbody>(o));
if(obj.contains("ui"))
load_ui_component(obj["ui"], scene.add<UI>(o));
if(obj.contains("environment_probe"))
load_probe_component(obj["environment_probe"], scene.add<EnvironmentProbe>(o));
@ -158,12 +149,6 @@ void save_rigidbody_component(nlohmann::json& j, const Rigidbody& rigidbody) {
j["mass"] = rigidbody.mass;
}
void save_ui_component(nlohmann::json& j, const UI& ui) {
j["width"] = ui.width;
j["height"] = ui.height;
j["path"] = ui.ui_path;
}
void save_probe_component(nlohmann::json& j, const EnvironmentProbe& probe) {
j["size"] = probe.size;
j["is_sized"] = probe.is_sized;
@ -199,9 +184,6 @@ nlohmann::json save_object(Object obj) {
if(scene->has<Rigidbody>(obj))
save_rigidbody_component(j["rigidbody"], scene->get<Rigidbody>(obj));
if(scene->has<UI>(obj))
save_ui_component(j["ui"], scene->get<UI>(obj));
if(scene->has<EnvironmentProbe>(obj))
save_probe_component(j["environment_probe"], scene->get<EnvironmentProbe>(obj));

View file

@ -1,191 +0,0 @@
#include "screen.hpp"
#include <fstream>
#include <string_view>
#include <nlohmann/json.hpp>
#include "file.hpp"
#include "font.hpp"
#include "engine.hpp"
#include "string_utils.hpp"
#include "log.hpp"
#include "assertions.hpp"
#include "uielement.hpp"
void ui::Screen::calculate_sizes() {
unsigned int numChildren = 0;
int actualWidth = extent.width, actualHeight = extent.height;
int offsetX = 0, offsetY = 0;
for(size_t i = 0; i < elements.size(); i++) {
auto& element = elements[i];
auto id = "ui_" + element.id;
//if(engine->has_localization(id))
// element.text = engine->localize(id);
UIElement* parent = nullptr;
if(!element.parent.empty())
parent = find_element(element.parent);
const auto& fillAbsolute = [parent](const auto& metric, auto& absolute, auto base, auto offset) {
switch(metric.type) {
case MetricType::Absolute:
absolute = offset + metric.value;
break;
case MetricType::Relative:
absolute = offset + ((base - offset) * (metric.value / 100.0f));
break;
case MetricType::Offset:
{
if(parent == nullptr)
absolute = base - metric.value;
else
absolute = parent->absolute_height - metric.value;
}
break;
}
};
fillAbsolute(element.metrics.x, element.absolute_x, offsetX + actualWidth, offsetX);
fillAbsolute(element.metrics.y, element.absolute_y, offsetY + actualHeight, offsetY);
fillAbsolute(element.metrics.width, element.absolute_width, actualWidth, 0);
fillAbsolute(element.metrics.height, element.absolute_height, actualHeight, 0);
if(parent) {
const float heightPerRow = actualWidth / (float)elements.size();
element.absolute_x = (float)numChildren * heightPerRow;
element.absolute_y = actualHeight - 50.0f;
element.absolute_width = (float)heightPerRow;
element.absolute_height = (float)50.0f;
numChildren++;
}
if(element.text_location == UIElement::TextLocation::Center) {
if(get_string_width(element.text) < element.absolute_width)
element.text_x = (element.absolute_width / 2.0f) - (get_string_width(element.text) / 2.0f);
if(get_font_height() < element.absolute_height)
element.text_y = (element.absolute_height / 2.0f) - (get_font_height() / 2.0f);
}
}
}
void ui::Screen::process_mouse(const int x, const int y) {
Expects(x >= 0);
Expects(y >= 0);
}
UIElement* ui::Screen::find_element(const std::string& id) {
Expects(!id.empty());
UIElement* foundElement = nullptr;
for(auto& element : elements) {
if(element.id == id)
foundElement = &element;
}
Expects(foundElement != nullptr);
return foundElement;
}
ui::Screen::Screen(const prism::path path) {
auto file = prism::open_file(path);
if(!file.has_value()) {
prism::log::error(System::Core, "Failed to load UI from {}!", path);
return;
}
nlohmann::json j;
file->read_as_stream() >> j;
for(auto& element : j["elements"]) {
UIElement ue;
ue.id = element["id"];
if(element.contains("on_click"))
ue.on_click_script = element["on_click"];
if(element.contains("text"))
ue.text = element["text"];
if(element.contains("parent"))
ue.parent = element["parent"];
if(element.contains("metrics")) {
const auto& metrics = element["metrics"];
const auto parseMetric = [](const std::string& str) {
UIElement::Metrics::Metric m;
if(string_contains(str, "px")) {
auto v = remove_substring(str, "px");
if(string_contains(str, "@")) {
m.type = MetricType::Offset;
m.value = std::stoi(remove_substring(v, "@"));
} else {
m.type = MetricType::Absolute;
m.value = std::stoi(v);
}
} else if(string_contains(str, "%")) {
m.type = MetricType::Relative;
m.value = std::stoi(remove_substring(str, "%"));
}
return m;
};
if(metrics.contains("x"))
ue.metrics.x = parseMetric(metrics["x"]);
if(metrics.contains("y"))
ue.metrics.y = parseMetric(metrics["y"]);
if(metrics.contains("width"))
ue.metrics.width = parseMetric(metrics["width"]);
if(metrics.contains("height"))
ue.metrics.height = parseMetric(metrics["height"]);
}
if(element.contains("background")) {
if(element["background"].contains("color")) {
auto tokens = tokenize(element["background"]["color"].get<std::string_view>(), ",");
ue.background.color.r = std::stof(tokens[0]);
ue.background.color.g = std::stof(tokens[1]);
ue.background.color.b = std::stof(tokens[2]);
ue.background.color.a = std::stof(tokens[3]);
}
if(element["background"].contains("image")) {
ue.background.image = element["background"]["image"];
}
}
if(element.contains("wrap"))
ue.wrap_text = element["wrap"];
if(element.contains("textLocation")) {
if(element["textLocation"] == "topLeft")
ue.text_location = UIElement::TextLocation::TopLeft;
else if(element["textLocation"] == "center")
ue.text_location = UIElement::TextLocation::Center;
}
elements.push_back(ue);
}
}
void ui::Screen::add_listener(const std::string& id, std::function<void()> callback) {
listeners[id] = callback;
}
void ui::Screen::process_event(const std::string& type, const std::string data) {
}

View file

@ -1,29 +0,0 @@
set(SRC
include/gfx_metal.hpp
src/gfx_metal.mm
src/gfx_metal_buffer.hpp
src/gfx_metal_pipeline.hpp
src/gfx_metal_texture.hpp
src/gfx_metal_framebuffer.hpp
src/gfx_metal_renderpass.hpp
src/gfx_metal_sampler.hpp)
add_library(GFXMetal STATIC
${SRC})
set_target_properties(GFXMetal PROPERTIES
FRAMEWORK TRUE
FRAMEWORK_VERSION OBJC
PUBLIC_HEADER "${HEADERS}"
)
target_link_libraries(GFXMetal PUBLIC
GFX
Core
Log
"-framework Metal")
target_include_directories(GFXMetal PUBLIC
include
PRIVATE
src)
set_engine_properties(GFXMetal)

View file

@ -1,70 +0,0 @@
#pragma once
#include <Metal/Metal.h>
#include <MetalKit/MetalKit.h>
#include "gfx.hpp"
class GFXMetal : public GFX {
public:
bool is_supported() override;
GFXContext required_context() override { return GFXContext::Metal; }
ShaderLanguage accepted_shader_language() override { return ShaderLanguage::MSL; }
const char* get_name() override;
bool supports_feature(const GFXFeature feature) override;
bool initialize(const GFXCreateInfo& createInfo) override;
void initialize_view(void* native_handle, const int identifier, const uint32_t width, const uint32_t height) override;
void remove_view(const int identifier) override;
// buffer operations
GFXBuffer* create_buffer(void* data, const GFXSize size, const bool dynamicData, const GFXBufferUsage usage) override;
void copy_buffer(GFXBuffer* buffer, void* data, const GFXSize offset, const GFXSize size) override;
void* get_buffer_contents(GFXBuffer* buffer) override;
// texture operations
GFXTexture* create_texture(const GFXTextureCreateInfo& info) override;
void copy_texture(GFXTexture* texture, void* data, GFXSize size) override;
void copy_texture(GFXTexture* from, GFXTexture* to) override;
void copy_texture(GFXTexture* from, GFXBuffer* to) override;
// sampler opeations
GFXSampler* create_sampler(const GFXSamplerCreateInfo& info) override;
// framebuffer operations
GFXFramebuffer* create_framebuffer(const GFXFramebufferCreateInfo& info) override;
// render pass operations
GFXRenderPass* create_render_pass(const GFXRenderPassCreateInfo& info) override;
// pipeline operations
GFXPipeline* create_graphics_pipeline(const GFXGraphicsPipelineCreateInfo& info) override;
GFXPipeline* create_compute_pipeline(const GFXComputePipelineCreateInfo& info) override;
GFXCommandBuffer* acquire_command_buffer(bool for_presentation_use = false) override;
void submit(GFXCommandBuffer* command_buffer, const int window = -1) override;
private:
struct NativeMTLView {
int identifier = -1;
CAMetalLayer* layer = nullptr;
};
std::vector<NativeMTLView*> nativeViews;
NativeMTLView* getNativeView(int identifier) {
for(auto& view : nativeViews) {
if(view->identifier == identifier)
return view;
}
return nullptr;
}
id<MTLDevice> device = nil;
id<MTLCommandQueue> command_queue = nil;
};

File diff suppressed because it is too large Load diff

View file

@ -1,19 +0,0 @@
#pragma once
#include <Metal/Metal.h>
#include "gfx_buffer.hpp"
class GFXMetalBuffer : public GFXBuffer {
public:
id<MTLBuffer> handles[3] = {nil, nil, nil};
bool dynamicData = false;
id<MTLBuffer> get(int frameIndex) {
if(dynamicData) {
return handles[frameIndex];
} else {
return handles[0];
}
}
};

View file

@ -1,13 +0,0 @@
#pragma once
#include <Metal/Metal.h>
#include <vector>
#include "gfx_framebuffer.hpp"
class GFXMetalTexture;
class GFXMetalFramebuffer : public GFXFramebuffer {
public:
std::vector<GFXMetalTexture*> attachments;
};

View file

@ -1,32 +0,0 @@
#pragma once
#include <Metal/Metal.h>
#include "gfx_pipeline.hpp"
class GFXMetalPipeline : public GFXPipeline {
public:
std::string label;
id<MTLRenderPipelineState> handle = nil;
id<MTLComputePipelineState> compute_handle = nil;
MTLSize threadGroupSize;
id<MTLDepthStencilState> depthStencil = nil;
MTLPrimitiveType primitiveType;
MTLCullMode cullMode;
GFXWindingMode winding_mode;
struct VertexStride {
int location, stride;
};
std::vector<VertexStride> vertexStrides;
int pushConstantSize = 0;
int pushConstantIndex = 0;
bool renderWire = false;
};

View file

@ -1,10 +0,0 @@
#pragma once
#include <Metal/Metal.h>
#include "gfx_renderpass.hpp"
class GFXMetalRenderPass : public GFXRenderPass {
public:
std::vector<MTLPixelFormat> attachments;
};

View file

@ -1,10 +0,0 @@
#pragma once
#include <Metal/Metal.h>
#include "gfx_sampler.hpp"
class GFXMetalSampler : public GFXSampler {
public:
id<MTLSamplerState> handle = nil;
};

View file

@ -1,16 +0,0 @@
#pragma once
#include <Metal/Metal.h>
#include "gfx_texture.hpp"
class GFXMetalTexture : public GFXTexture {
public:
id<MTLTexture> handle = nil;
id<MTLSamplerState> sampler = nil;
int array_length = 1;
bool is_cubemap = false;
MTLPixelFormat format;
};

View file

@ -1,12 +1,5 @@
#pragma once
#ifdef PLATFORM_WINDOWS
#define NOMINMAX // donut define max in windows.h
#define VK_USE_PLATFORM_WIN32_KHR
#else
#define VK_USE_PLATFORM_XCB_KHR
#endif
#include <vulkan/vulkan.h>
#include <map>
@ -18,7 +11,6 @@
struct NativeSurface {
int identifier = -1;
void* windowNativeHandle;
uint32_t surfaceWidth = -1, surfaceHeight = -1;
std::vector<VkSemaphore> imageAvailableSemaphores;
@ -43,28 +35,28 @@ class GFXVulkanPipeline;
class GFXVulkan : public GFX {
public:
bool is_supported() { return true; }
bool is_supported() override { return true; }
ShaderLanguage accepted_shader_language() override { return ShaderLanguage::SPIRV; }
GFXContext required_context() { return GFXContext::Vulkan; }
GFXContext required_context() override { return GFXContext::Vulkan; }
const char* get_name() override;
bool supports_feature(const GFXFeature feature) override;
bool supports_feature(GFXFeature feature) override;
bool initialize(const GFXCreateInfo& info) override;
void initialize_view(void* native_handle, const int identifier, const uint32_t width, const uint32_t height) override;
void recreate_view(const int identifier, const uint32_t width, const uint32_t height) override;
void initialize_view(void* native_handle, int identifier, uint32_t width, uint32_t height) override;
void recreate_view(int identifier, uint32_t width, uint32_t height) override;
// buffer operations
GFXBuffer* create_buffer(void* data, const GFXSize size, const bool dynamic_data, const GFXBufferUsage usage) override;
void copy_buffer(GFXBuffer* buffer, void* data, const GFXSize offset, const GFXSize size) override;
GFXBuffer* create_buffer(void* data, GFXSize size, bool dynamic_data, GFXBufferUsage usage) override;
void copy_buffer(GFXBuffer* buffer, void* data, GFXSize offset, GFXSize size) override;
void* get_buffer_contents(GFXBuffer* buffer) override;
void release_buffer_contents(GFXBuffer* buffer, void* handle) override;
// texture operations
GFXTexture* create_texture(const GFXTextureCreateInfo& info) override;
void copy_texture(GFXTexture* texture, void* data, const GFXSize size) override;
void copy_texture(GFXTexture* texture, void* data, GFXSize size) override;
void copy_texture(GFXTexture* from, GFXTexture* to) override;
void copy_texture(GFXTexture* from, GFXBuffer* to) override;
@ -82,11 +74,11 @@ public:
GFXPipeline* create_compute_pipeline(const GFXComputePipelineCreateInfo& info) override;
// misc operations
GFXSize get_alignment(const GFXSize size) override;
GFXSize get_alignment(GFXSize size) override;
GFXCommandBuffer* acquire_command_buffer(bool for_presentation_use = false) override;
GFXCommandBuffer* acquire_command_buffer(bool for_presentation_use) override;
void submit(GFXCommandBuffer* command_buffer, const int identifier) override;
void submit(GFXCommandBuffer* command_buffer, int identifier) override;
private:
void createInstance(std::vector<const char*> layers, std::vector<const char*> extensions);
@ -111,7 +103,7 @@ private:
VkImageLayout newLayout,
VkPipelineStageFlags src_stage_mask = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
VkPipelineStageFlags dst_stage_mask = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT);
VkShaderModule createShaderModule(const uint32_t* code, const int length);
VkShaderModule createShaderModule(const uint32_t* code, int length);
VkCommandBuffer beginSingleTimeCommands();
void endSingleTimeCommands(VkCommandBuffer commandBuffer);

View file

@ -4,7 +4,6 @@
#include <limits>
#include <cstddef>
#include <array>
#include <sstream>
#include "gfx_vulkan_buffer.hpp"
#include "gfx_vulkan_pipeline.hpp"
@ -20,11 +19,6 @@
#include <platform.hpp>
#ifdef PLATFORM_LINUX
#include <SDL2/SDL.h>
#endif
VkFormat toVkFormat(GFXPixelFormat format) {
switch (format) {
case GFXPixelFormat::R_32F:
@ -82,6 +76,8 @@ VkFormat toVkFormat(GFXVertexFormat format) {
VkBlendFactor toVkFactor(GFXBlendFactor factor) {
switch (factor) {
case GFXBlendFactor::Zero:
return VK_BLEND_FACTOR_ZERO;
case GFXBlendFactor::One:
return VK_BLEND_FACTOR_ONE;
case GFXBlendFactor::OneMinusSrcAlpha:
@ -90,6 +86,12 @@ VkBlendFactor toVkFactor(GFXBlendFactor factor) {
return VK_BLEND_FACTOR_ONE_MINUS_SRC_COLOR;
case GFXBlendFactor::SrcAlpha:
return VK_BLEND_FACTOR_SRC_ALPHA;
case GFXBlendFactor::DstAlpha:
return VK_BLEND_FACTOR_DST_ALPHA;
case GFXBlendFactor::SrcColor:
return VK_BLEND_FACTOR_SRC_COLOR;
case GFXBlendFactor::DstColor:
return VK_BLEND_FACTOR_DST_COLOR;
}
return VK_BLEND_FACTOR_ONE;
@ -165,14 +167,14 @@ VKAPI_ATTR VkBool32 VKAPI_CALL DebugCallback(
const VkDebugUtilsMessengerCallbackDataEXT *pCallbackData,
void *pUserData) {
prism::log::debug(System::GFX, pCallbackData->pMessage);
prism::log("{}", pCallbackData->pMessage);
return VK_FALSE;
}
VkResult name_object(VkDevice device, VkObjectType type, uint64_t object, std::string_view name) {
if(object == 0x0) {
prism::log::error(System::GFX, "Failed to name object {}", name.data());
prism::log("Failed to name object {}", name);
return VK_ERROR_DEVICE_LOST;
}
@ -229,11 +231,10 @@ bool GFXVulkan::initialize(const GFXCreateInfo& info) {
void GFXVulkan::initialize_view(void* native_handle, const int identifier, const uint32_t width, const uint32_t height) {
vkDeviceWaitIdle(device);
NativeSurface* surface = new NativeSurface();
auto surface = new NativeSurface();
surface->identifier = identifier;
surface->surfaceWidth = width;
surface->surfaceHeight = height;
surface->windowNativeHandle = native_handle;
createSwapchain(surface);
createSyncPrimitives(surface);
@ -259,7 +260,7 @@ void GFXVulkan::recreate_view(const int identifier, const uint32_t width, const
}
GFXBuffer* GFXVulkan::create_buffer(void *data, const GFXSize size, const bool dynamic_data, const GFXBufferUsage usage) {
GFXVulkanBuffer* buffer = new GFXVulkanBuffer();
auto buffer = new GFXVulkanBuffer();
vkDeviceWaitIdle(device);
@ -307,7 +308,7 @@ GFXBuffer* GFXVulkan::create_buffer(void *data, const GFXSize size, const bool d
}
void GFXVulkan::copy_buffer(GFXBuffer* buffer, void* data, GFXSize offset, GFXSize size) {
GFXVulkanBuffer* vulkanBuffer = (GFXVulkanBuffer*)buffer;
auto vulkanBuffer = (GFXVulkanBuffer*)buffer;
void* mapped_data = nullptr;
vkMapMemory(device, vulkanBuffer->memory, offset, vulkanBuffer->size - offset, 0, &mapped_data);
@ -327,7 +328,7 @@ void GFXVulkan::copy_buffer(GFXBuffer* buffer, void* data, GFXSize offset, GFXSi
}
void* GFXVulkan::get_buffer_contents(GFXBuffer* buffer) {
GFXVulkanBuffer* vulkanBuffer = (GFXVulkanBuffer*)buffer;
auto vulkanBuffer = (GFXVulkanBuffer*)buffer;
void* mapped_data;
vkMapMemory(device, vulkanBuffer->memory, 0, VK_WHOLE_SIZE, 0, &mapped_data);
@ -336,7 +337,7 @@ void* GFXVulkan::get_buffer_contents(GFXBuffer* buffer) {
}
void GFXVulkan::release_buffer_contents(GFXBuffer* buffer, void* handle) {
GFXVulkanBuffer* vulkanBuffer = (GFXVulkanBuffer*)buffer;
auto vulkanBuffer = (GFXVulkanBuffer*)buffer;
VkMappedMemoryRange range = {};
range.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE;
@ -349,7 +350,7 @@ void GFXVulkan::release_buffer_contents(GFXBuffer* buffer, void* handle) {
}
GFXTexture* GFXVulkan::create_texture(const GFXTextureCreateInfo& info) {
GFXVulkanTexture* texture = new GFXVulkanTexture();
auto texture = new GFXVulkanTexture();
vkDeviceWaitIdle(device);
@ -514,7 +515,7 @@ GFXTexture* GFXVulkan::create_texture(const GFXTextureCreateInfo& info) {
}
void GFXVulkan::copy_texture(GFXTexture* texture, void* data, GFXSize size) {
GFXVulkanTexture* vulkanTexture = (GFXVulkanTexture*)texture;
auto vulkanTexture = (GFXVulkanTexture*)texture;
vkDeviceWaitIdle(device);
@ -579,12 +580,12 @@ void GFXVulkan::copy_texture(GFXTexture* texture, void* data, GFXSize size) {
}
void GFXVulkan::copy_texture(GFXTexture* from, GFXTexture* to) {
prism::log::error(System::GFX, "Copy Texture->Texture unimplemented!");
prism::log("Copy Texture->Texture unimplemented!");
}
void GFXVulkan::copy_texture(GFXTexture* from, GFXBuffer* to) {
GFXVulkanTexture* vulkanTexture = (GFXVulkanTexture*)from;
GFXVulkanBuffer* vulkanBuffer = (GFXVulkanBuffer*)to;
auto vulkanTexture = (GFXVulkanTexture*)from;
auto vulkanBuffer = (GFXVulkanBuffer*)to;
VkCommandBuffer commandBuffer = beginSingleTimeCommands();
@ -605,7 +606,7 @@ void GFXVulkan::copy_texture(GFXTexture* from, GFXBuffer* to) {
}
GFXSampler* GFXVulkan::create_sampler(const GFXSamplerCreateInfo& info) {
GFXVulkanSampler* sampler = new GFXVulkanSampler();
auto sampler = new GFXVulkanSampler();
const VkSamplerAddressMode samplerMode = toSamplerMode(info.samplingMode);
@ -629,15 +630,15 @@ GFXSampler* GFXVulkan::create_sampler(const GFXSamplerCreateInfo& info) {
}
GFXFramebuffer* GFXVulkan::create_framebuffer(const GFXFramebufferCreateInfo& info) {
GFXVulkanFramebuffer* framebuffer = new GFXVulkanFramebuffer();
auto framebuffer = new GFXVulkanFramebuffer();
vkDeviceWaitIdle(device);
GFXVulkanRenderPass* renderPass = (GFXVulkanRenderPass*)info.render_pass;
auto renderPass = (GFXVulkanRenderPass*)info.render_pass;
std::vector<VkImageView> attachments;
for (auto& attachment : info.attachments) {
GFXVulkanTexture* texture = (GFXVulkanTexture*)attachment;
auto texture = (GFXVulkanTexture*)attachment;
attachments.push_back(texture->view);
VkImageLayout expectedLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
@ -673,7 +674,7 @@ GFXFramebuffer* GFXVulkan::create_framebuffer(const GFXFramebufferCreateInfo& in
}
GFXRenderPass* GFXVulkan::create_render_pass(const GFXRenderPassCreateInfo& info) {
GFXVulkanRenderPass* renderPass = new GFXVulkanRenderPass();
auto renderPass = new GFXVulkanRenderPass();
vkDeviceWaitIdle(device);
@ -762,7 +763,7 @@ GFXRenderPass* GFXVulkan::create_render_pass(const GFXRenderPassCreateInfo& info
}
GFXPipeline* GFXVulkan::create_graphics_pipeline(const GFXGraphicsPipelineCreateInfo& info) {
GFXVulkanPipeline* pipeline = new GFXVulkanPipeline();
auto pipeline = new GFXVulkanPipeline();
vkDeviceWaitIdle(device);
@ -935,6 +936,8 @@ GFXPipeline* GFXVulkan::create_graphics_pipeline(const GFXGraphicsPipelineCreate
case GFXDepthMode::Greater:
depthStencil.depthCompareOp = VK_COMPARE_OP_GREATER;
break;
case GFXDepthMode::None:
break;
}
}
@ -988,6 +991,8 @@ GFXPipeline* GFXVulkan::create_graphics_pipeline(const GFXGraphicsPipelineCreate
case GFXBindingType::Sampler:
descriptorType = VK_DESCRIPTOR_TYPE_SAMPLER;
break;
case GFXBindingType::PushConstant:
break;
}
VkDescriptorSetLayoutBinding layoutBinding = {};
@ -1051,7 +1056,7 @@ GFXPipeline* GFXVulkan::create_graphics_pipeline(const GFXGraphicsPipelineCreate
}
GFXPipeline* GFXVulkan::create_compute_pipeline(const GFXComputePipelineCreateInfo& info) {
GFXVulkanPipeline* pipeline = new GFXVulkanPipeline();
auto pipeline = new GFXVulkanPipeline();
vkDeviceWaitIdle(device);
@ -1123,6 +1128,8 @@ GFXPipeline* GFXVulkan::create_compute_pipeline(const GFXComputePipelineCreateIn
case GFXBindingType::Sampler:
descriptorType = VK_DESCRIPTOR_TYPE_SAMPLER;
break;
case GFXBindingType::PushConstant:
break;
}
VkDescriptorSetLayoutBinding layoutBinding = {};
@ -1176,7 +1183,7 @@ GFXSize GFXVulkan::get_alignment(GFXSize size) {
}
GFXCommandBuffer* GFXVulkan::acquire_command_buffer(bool for_presentation_use) {
GFXVulkanCommandBuffer* cmdbuf = new GFXVulkanCommandBuffer();
auto cmdbuf = new GFXVulkanCommandBuffer();
if(!for_presentation_use) {
VkCommandBufferAllocateInfo info = {};
@ -1209,7 +1216,7 @@ void GFXVulkan::submit(GFXCommandBuffer* command_buffer, const int identifier) {
VkCommandBuffer cmd = VK_NULL_HANDLE;
GFXVulkanCommandBuffer* cmdbuf = (GFXVulkanCommandBuffer*)command_buffer;
auto cmdbuf = (GFXVulkanCommandBuffer*)command_buffer;
if(cmdbuf->handle != VK_NULL_HANDLE)
cmd = cmdbuf->handle;
else if(current_surface != nullptr)
@ -1258,8 +1265,8 @@ void GFXVulkan::submit(GFXCommandBuffer* command_buffer, const int identifier) {
vkCmdEndRenderPass(cmd);
}
GFXVulkanRenderPass* renderPass = (GFXVulkanRenderPass*)command.data.set_render_pass.render_pass;
GFXVulkanFramebuffer* framebuffer = (GFXVulkanFramebuffer*)command.data.set_render_pass.framebuffer;
auto renderPass = (GFXVulkanRenderPass*)command.data.set_render_pass.render_pass;
auto framebuffer = (GFXVulkanFramebuffer*)command.data.set_render_pass.framebuffer;
if (renderPass != nullptr) {
currentRenderPass = renderPass->handle;
@ -1429,8 +1436,8 @@ void GFXVulkan::submit(GFXCommandBuffer* command_buffer, const int identifier) {
break;
case GFXCommandType::CopyTexture:
{
GFXVulkanTexture* src = (GFXVulkanTexture*)command.data.copy_texture.src;
GFXVulkanTexture* dst = (GFXVulkanTexture*)command.data.copy_texture.dst;
auto src = (GFXVulkanTexture*)command.data.copy_texture.src;
auto dst = (GFXVulkanTexture*)command.data.copy_texture.dst;
const int slice_offset = command.data.copy_texture.to_slice + command.data.copy_texture.to_layer * 6;
@ -1534,7 +1541,7 @@ void GFXVulkan::submit(GFXCommandBuffer* command_buffer, const int identifier) {
break;
case GFXCommandType::GenerateMipmaps:
{
auto texture = static_cast<GFXVulkanTexture*>(command.data.generate_mipmaps.texture);
auto texture = dynamic_cast<GFXVulkanTexture*>(command.data.generate_mipmaps.texture);
for(int l = 0; l < texture->range.layerCount; l++) {
int mip_width = texture->width;
@ -1634,7 +1641,7 @@ void GFXVulkan::submit(GFXCommandBuffer* command_buffer, const int identifier) {
}
break;
default:
prism::log::error(System::GFX, "Unhandled GFX Command Type {}", utility::enum_to_string(command.type));
prism::log("Unhandled GFX Command Type {}", utility::enum_to_string(command.type));
break;
}
}
@ -1789,6 +1796,32 @@ void GFXVulkan::createLogicalDevice(std::vector<const char*> extensions) {
queueCreateInfo.pQueuePriorities = &queuePriority;
queueCreateInfos.push_back(queueCreateInfo);
} else {
// graphics
{
VkDeviceQueueCreateInfo queueCreateInfo = {};
queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
queueCreateInfo.queueFamilyIndex = graphicsFamilyIndex;
queueCreateInfo.queueCount = 1;
float queuePriority = 1.0f;
queueCreateInfo.pQueuePriorities = &queuePriority;
queueCreateInfos.push_back(queueCreateInfo);
}
// present
{
VkDeviceQueueCreateInfo queueCreateInfo = {};
queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
queueCreateInfo.queueFamilyIndex = presentFamilyIndex;
queueCreateInfo.queueCount = 1;
float queuePriority = 1.0f;
queueCreateInfo.pQueuePriorities = &queuePriority;
queueCreateInfos.push_back(queueCreateInfo);
}
}
VkDeviceCreateInfo createInfo = {};
@ -1823,23 +1856,9 @@ void GFXVulkan::createLogicalDevice(std::vector<const char*> extensions) {
}
void GFXVulkan::createSwapchain(NativeSurface* native_surface, VkSwapchainKHR oldSwapchain) {
#ifdef PLATFORM_WINDOWS_OLD
// create win32 surface
if(native_surface->surface == VK_NULL_HANDLE)
{
VkWin32SurfaceCreateInfoKHR createInfo = {};
createInfo.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR;
createInfo.hwnd = (HWND)native_surface->windowNativeHandle;
createInfo.hinstance = GetModuleHandle(nullptr);
vkCreateWin32SurfaceKHR(instance, &createInfo, nullptr, &native_surface->surface);
}
#else
if(native_surface->surface == VK_NULL_HANDLE) {
native_surface->surface = (VkSurfaceKHR)platform::create_native_surface(native_surface->identifier, (void*)instance);
}
#endif
// TODO: fix this pls
VkBool32 supported;
@ -2073,7 +2092,7 @@ void GFXVulkan::cacheDescriptorState(GFXVulkanPipeline* pipeline, VkDescriptorSe
VkResult error = vkAllocateDescriptorSets(device, &allocInfo, &descriptorSet);
if(error != VK_SUCCESS || descriptorSet == VK_NULL_HANDLE) {
prism::log::error(System::GFX, "ERROR: COULD NOT CACHE BECAUSE OUT OF DESCRIPTOR SETS.");
prism::log("ERROR: COULD NOT CACHE BECAUSE OUT OF DESCRIPTOR SETS.");
return;
}
@ -2083,7 +2102,7 @@ void GFXVulkan::cacheDescriptorState(GFXVulkanPipeline* pipeline, VkDescriptorSe
// update set
for (auto [i, buffer] : utility::enumerate(boundShaderBuffers)) {
if (buffer.buffer != nullptr) {
GFXVulkanBuffer* vulkanBuffer = (GFXVulkanBuffer*)buffer.buffer;
auto vulkanBuffer = (GFXVulkanBuffer*)buffer.buffer;
VkDescriptorBufferInfo bufferInfo = {};
bufferInfo.buffer = vulkanBuffer->handle; // will this break?
@ -2104,7 +2123,7 @@ void GFXVulkan::cacheDescriptorState(GFXVulkanPipeline* pipeline, VkDescriptorSe
for (auto [i, texture] : utility::enumerate(boundTextures)) {
if (texture != nullptr) {
GFXVulkanTexture* vulkanTexture = (GFXVulkanTexture*) texture;
auto vulkanTexture = (GFXVulkanTexture*) texture;
VkDescriptorImageInfo imageInfo = {};
imageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
@ -2140,7 +2159,7 @@ void GFXVulkan::cacheDescriptorState(GFXVulkanPipeline* pipeline, VkDescriptorSe
for (auto [i, sampler] : utility::enumerate(boundSamplers)) {
if (sampler != nullptr) {
GFXVulkanSampler* vulkanSampler = (GFXVulkanSampler*) sampler;
auto vulkanSampler = (GFXVulkanSampler*) sampler;
VkDescriptorImageInfo imageInfo = {};
imageInfo.sampler = vulkanSampler->sampler;

View file

@ -4,5 +4,5 @@ set(SRC
add_library(Log STATIC ${SRC})
target_include_directories(Log PUBLIC include)
target_link_libraries(Log PRIVATE Utility)
target_link_libraries(Log PUBLIC fmt::fmt)
set_engine_properties(Log)

View file

@ -1,66 +1,18 @@
#pragma once
#include <string_view>
#include <string>
#include <vector>
#include <fmt/format.h>
enum class System {
None,
Core,
Renderer,
Game,
File,
GFX
};
namespace prism {
inline std::string log_output;
enum class Level {
Info,
Warning,
Error,
Debug
};
namespace prism::log {
inline void internal_format(std::string &msg, const std::string &arg) {
auto pos = msg.find_first_of("{}");
msg.replace(pos, 2, arg);
inline void vlog(fmt::string_view format, fmt::format_args args) {
auto str = fmt::vformat(format, args);
fmt::print("{}", str);
log_output += str + "\n";
}
inline void internal_format(std::string &msg, const char *&arg) {
auto pos = msg.find_first_of("{}");
msg.replace(pos, 2, arg);
template <typename S, typename... Args>
inline void log(const S& format, Args&&... args) {
vlog(format, fmt::make_args_checked<Args...>(format, args...));
}
void process_message(const Level level, const System system, const std::string_view message);
template<typename... Args>
void internal_print(const Level level, const System system, const std::string_view format, Args &&... args) {
auto msg = std::string(format);
((internal_format(msg, args)), ...);
process_message(level, system, msg);
}
template<typename... Args>
void info(const System system, const std::string_view format, Args &&... args) {
internal_print(Level::Info, system, format, args...);
}
template<typename... Args>
void warning(const System system, const std::string_view format, Args &&... args) {
internal_print(Level::Warning, system, format, args...);
}
template<typename... Args>
void error(const System system, const std::string_view format, Args &&... args) {
internal_print(Level::Error, system, format, args...);
}
template<typename... Args>
void debug(const System system, const std::string_view format, Args &&... args) {
internal_print(Level::Debug, system, format, args...);
}
inline std::vector<std::string> stored_output;
}

View file

@ -1,24 +1 @@
#include "log.hpp"
#include <iostream>
#include <chrono>
#include <iomanip>
#include "string_utils.hpp"
#include "utility.hpp"
void prism::log::process_message(const Level level, const System system, const std::string_view message) {
auto now = std::chrono::system_clock::now();
std::time_t t_c = std::chrono::system_clock::to_time_t(now);
std::string date;
date.resize(30);
std::strftime(&date[0], date.size(), "%Y-%m-%d %H:%M:%S", std::localtime(&t_c));
utility::erase(date, '\0'); // strftime will insert \0 for us, but it's not needed here
std::string s = utility::format("{} {} {}: {}", date, utility::enum_to_string(system), utility::enum_to_string(level), message);
std::cout << s << '\n';
stored_output.push_back(s);
}

View file

@ -8,7 +8,6 @@
#include <filesystem>
#include "log.hpp"
#include "file_utils.hpp"
#include "path.hpp"
namespace prism {

View file

@ -159,17 +159,9 @@ namespace platform {
/// On platforms that support moue capture, this will lock the mouse cursor to the window and hide it.
void capture_mouse(const bool capture);
/**Opens a file dialog to select a file. This will not block.
@param existing Whether or not to limit to existing files.
@param returnFunction The callback function when a file is selected or the dialog is cancelled. An empy string is returned when cancelled.
@param openDirectory Whether or not to allow selecting directories as well.
*/
void open_dialog(const bool existing, std::function<void(std::string)> returnFunction, bool openDirectory = false);
/**Opens a file dialog to select a save location for a file. This will not block.
@param returnFunction The callback function when a file is selected or the dialog is cancelled. An empy string is returned when cancelled.
*/
void save_dialog(std::function<void(std::string)> returnFunction);
// TODO: right now the OS intercepting and saying "We dont want text input anymore" ala software keyboards is NOT supported yet
void begin_text_input();
void end_text_input();
/**Translates a virtual keycode to it's character equivalent.
@note Example: translateKey(0x01) = 'a'; 0x01 in this example is a platform and language specific keycode for a key named 'a'.

View file

@ -55,21 +55,12 @@ namespace prism {
void recreate_all_render_targets();
void set_screen(ui::Screen* screen);
void init_screen(ui::Screen* screen);
void update_screen();
struct controller_continuity {
int elementOffset = 0;
};
void render(GFXCommandBuffer* command_buffer, Scene* scene, RenderTarget& target, int index);
void render_screen(GFXCommandBuffer* commandBuffer, ui::Screen* screen, prism::Extent extent,
controller_continuity& continuity, render_screen_options options = render_screen_options());
void render_camera(GFXCommandBuffer* command_buffer, Scene& scene, Object camera_object, Camera& camera,
prism::Extent extent, RenderTarget& target, controller_continuity &continuity);

View file

@ -162,7 +162,7 @@ void ImGuiPass::load_font(const std::string_view filename) {
io.Fonts->AddFontFromMemoryTTF(font_file->cast_data<unsigned char>(), font_file->size(), 15.0 * platform::get_window_dpi(0));
ImGui::GetIO().FontGlobalScale = 1.0 / platform::get_window_dpi(0);
} else {
prism::log::error(System::Renderer, "Failed to load font file for imgui!");
prism::log("Failed to load font file for imgui!");
return;
}
}

View file

@ -13,7 +13,7 @@
ShaderSource get_shader(std::string filename, bool skinned, bool cubemap) {
auto shader_file = prism::open_file(prism::internal_domain / filename);
if(!shader_file.has_value()) {
prism::log::error(System::Renderer, "Failed to open shader file {}!", filename);
prism::log("Failed to open shader file {}!", filename);
return {};
}

View file

@ -4,7 +4,6 @@
#include "gfx_commandbuffer.hpp"
#include "math.hpp"
#include "screen.hpp"
#include "file.hpp"
#include "scene.hpp"
#include "font.hpp"
@ -185,48 +184,6 @@ void renderer::recreate_all_render_targets() {
}
void renderer::set_screen(ui::Screen* screen) {
Expects(screen != nullptr);
current_screen = screen;
init_screen(screen);
update_screen();
}
void renderer::init_screen(ui::Screen* screen) {
Expects(screen != nullptr);
std::array<GylphMetric, numGlyphs> metrics = {};
for(int i = 0; i < numGlyphs; i++) {
GylphMetric& metric = metrics[i];
metric.x0_y0 = utility::pack_u32(font.sizes[fontSize][i].x0, font.sizes[fontSize][i].y0);
metric.x1_y1 = utility::pack_u32(font.sizes[fontSize][i].x1, font.sizes[fontSize][i].y1);
metric.xoff = font.sizes[fontSize][i].xoff;
metric.yoff = font.sizes[fontSize][i].yoff;
metric.xoff2 = font.sizes[fontSize][i].xoff2;
metric.yoff2 = font.sizes[fontSize][i].yoff2;
}
screen->glyph_buffer = gfx->create_buffer(nullptr, instance_alignment + (sizeof(GylphMetric) * numGlyphs), false, GFXBufferUsage::Storage);
gfx->copy_buffer(screen->glyph_buffer, metrics.data(), instance_alignment, sizeof(GylphMetric) * numGlyphs);
screen->elements_buffer = gfx->create_buffer(nullptr, sizeof(ElementInstance) * 50, true, GFXBufferUsage::Storage);
screen->instance_buffer = gfx->create_buffer(nullptr, sizeof(StringInstance) * 50, true, GFXBufferUsage::Storage);
}
void renderer::update_screen() {
if(current_screen != nullptr) {
for(auto& element : current_screen->elements) {
if(!element.background.image.empty())
element.background.texture = assetm->get<Texture>(prism::app_domain / element.background.image);
}
}
}
void renderer::render(GFXCommandBuffer* commandbuffer, Scene* scene, RenderTarget& target, int index) {
const auto extent = target.extent;
const auto render_extent = target.get_render_extent();
@ -404,9 +361,6 @@ void renderer::render(GFXCommandBuffer* commandbuffer, Scene* scene, RenderTarge
commandbuffer->pop_group();
if(current_screen != nullptr)
render_screen(commandbuffer, current_screen, extent, continuity);
commandbuffer->push_group("Extra Passes");
for(auto& pass : passes)
@ -533,18 +487,6 @@ void renderer::render_camera(GFXCommandBuffer* command_buffer, Scene& scene, Obj
}
}
const auto& screens = scene.get_all<UI>();
for(const auto& [obj, screen] : screens) {
if(!screen.screen)
continue;
render_screen_options options = {};
options.render_world = true;
options.mvp = camera.perspective * camera.view * scene.get<Transform>(obj).model;
render_screen(command_buffer, screen.screen, extent, continuity, options);
}
SkyPushConstant pc;
pc.view = matrix_from_quat(scene.get<Transform>(camera_object).rotation);
pc.aspect = static_cast<float>(extent.width) / static_cast<float>(extent.height);
@ -568,136 +510,6 @@ void renderer::render_camera(GFXCommandBuffer* command_buffer, Scene& scene, Obj
gfx->copy_buffer(target.sceneBuffer, &sceneInfo, 0, sizeof(SceneInformation));
}
void renderer::render_screen(GFXCommandBuffer *commandbuffer, ui::Screen* screen, prism::Extent extent, controller_continuity& continuity, render_screen_options options) {
std::array<GlyphInstance, maxInstances> instances;
std::vector<ElementInstance> elementInstances;
std::array<StringInstance, 50> stringInstances = {};
int stringLen = 0;
int numElements = 0;
for(auto [i, element] : utility::enumerate(screen->elements)) {
if(!element.visible)
continue;
ElementInstance instance;
instance.position = utility::pack_u32(utility::to_fixed(element.absolute_x), utility::to_fixed(element.absolute_y));
instance.size = utility::pack_u32(utility::to_fixed(element.absolute_width), utility::to_fixed(element.absolute_height));
instance.color[0] = element.background.color.r;
instance.color[1] = element.background.color.g;
instance.color[2] = element.background.color.b;
instance.color[3] = element.background.color.a;
elementInstances.push_back(instance);
stringInstances[i].xy = utility::pack_u32(utility::to_fixed(element.absolute_x + element.text_x), utility::to_fixed(element.absolute_y + element.text_y));
float advance = 0.0f;
float yadvance = 0.0f;
bool do_y_advance = false; // so we can not advance the text by a space if we're wrapping per word
for(size_t j = 0; j < element.text.length(); j++) {
auto index = element.text[j] - 32;
if(do_y_advance) {
advance = 0.0f;
yadvance += font.ascentSizes[fontSize];
do_y_advance = false;
}
if(element.wrap_text) {
if(element.text[j] == ' ') {
std::string t;
float temp_advance = 0.0f;
for(size_t k = j + 1; k < element.text.length() + j; k++) {
t += element.text[k];
auto index = element.text[k] - 32;
temp_advance += font.sizes[fontSize][index].xadvance;
if(element.text[k] == ' ')
break;
}
if((temp_advance + advance) >= element.absolute_width)
do_y_advance = true;
}
}
GlyphInstance& instance = instances[stringLen + j];
instance.position = utility::pack_u32(utility::to_fixed(advance), utility::to_fixed(yadvance));
instance.index = utility::pack_u32(index, 0);
instance.instance = i;
advance += font.sizes[fontSize][index].xadvance;
if(element.text[j] == '\n') {
advance = 0.0f;
yadvance += font.ascentSizes[fontSize];
}
}
stringLen += static_cast<int>(element.text.length());
numElements++;
}
gfx->copy_buffer(screen->glyph_buffer, instances.data(), 0, instance_alignment);
gfx->copy_buffer(screen->instance_buffer, stringInstances.data(), 0, sizeof(StringInstance) * 50);
gfx->copy_buffer(screen->elements_buffer, elementInstances.data(), sizeof(ElementInstance) * continuity.elementOffset, sizeof(ElementInstance) * elementInstances.size());
const prism::float2 windowSize = {static_cast<float>(extent.width), static_cast<float>(extent.height)};
for(auto [i, element] : utility::enumerate(screen->elements)) {
if(!element.visible)
continue;
UIPushConstant pc;
pc.screenSize = windowSize;
if(options.render_world) {
commandbuffer->set_graphics_pipeline(world_general_pipeline);
pc.mvp = options.mvp;
} else {
commandbuffer->set_graphics_pipeline(general_pipeline);
}
if (element.background.texture) {
commandbuffer->bind_texture(element.background.texture->handle, 2);
}
else {
commandbuffer->bind_texture(dummy_texture, 2);
}
commandbuffer->set_push_constant(&pc, sizeof(UIPushConstant));
commandbuffer->bind_shader_buffer(screen->elements_buffer, 0, 0, sizeof(ElementInstance) * 50);
commandbuffer->draw(0, 4, i + continuity.elementOffset, 1);
}
UIPushConstant pc;
pc.screenSize = windowSize;
if(options.render_world) {
commandbuffer->set_graphics_pipeline(world_text_pipeline);
pc.mvp = options.mvp;
} else {
commandbuffer->set_graphics_pipeline(text_pipeline);
}
commandbuffer->set_push_constant(&pc, sizeof(UIPushConstant));
commandbuffer->bind_shader_buffer(screen->glyph_buffer, 0, 0, instance_alignment);
commandbuffer->bind_shader_buffer(screen->glyph_buffer, instance_alignment, 1, sizeof(GylphMetric) * numGlyphs);
commandbuffer->bind_shader_buffer(screen->instance_buffer, 0, 2, sizeof(StringInstance) * 50);
commandbuffer->bind_texture(font_texture, 3);
if(stringLen > 0)
commandbuffer->draw(0, 4, 0, stringLen);
continuity.elementOffset += numElements;
}
void renderer::create_mesh_pipeline(Material& material) const {
GFXShaderConstant materials_constant = {};
materials_constant.type = GFXShaderConstant::Type::Integer;
@ -866,7 +678,7 @@ void renderer::create_post_pipelines() {
void renderer::create_font_texture() {
auto file = prism::open_file(prism::app_domain / "font.fp", true);
if(file == std::nullopt) {
prism::log::error(System::Renderer, "Failed to load font file!");
prism::log("Failed to load font file!");
return;
}

View file

@ -53,7 +53,7 @@ std::vector<uint32_t> compile_glsl_to_spv(const std::string_view source_string,
file_includer.pushExternalLocalDirectory(path);
if (!shader.parse(&DefaultTBuiltInResource, 100, false, EShMsgDefault, file_includer)) {
prism::log::error(System::Renderer, "{}", shader.getInfoLog());
prism::log("{}", shader.getInfoLog());
return {};
}
@ -62,7 +62,7 @@ std::vector<uint32_t> compile_glsl_to_spv(const std::string_view source_string,
Program.addShader(&shader);
if(!Program.link(EShMsgDefault)) {
prism::log::error(System::None, "Failed to link shader: {} {} {}", source_string.data(), shader.getInfoLog(), shader.getInfoDebugLog());
prism::log("Failed to link shader: {} {} {}", source_string.data(), shader.getInfoLog(), shader.getInfoDebugLog());
return {};
}
@ -77,7 +77,7 @@ std::vector<uint32_t> compile_glsl_to_spv(const std::string_view source_string,
std::optional<ShaderSource> ShaderCompiler::compile(const ShaderLanguage from_language, const ShaderStage shader_stage, const ShaderSource& shader_source, const ShaderLanguage to_language, const CompileOptions& options) {
if(from_language != ShaderLanguage::GLSL) {
prism::log::error(System::Renderer, "Non-supported input language!");
prism::log("Non-supported input language!");
return std::nullopt;
}
@ -96,7 +96,7 @@ std::optional<ShaderSource> ShaderCompiler::compile(const ShaderLanguage from_la
auto spirv = compile_glsl_to_spv(shader_source.as_string(), lang, options);
if(spirv.empty()) {
prism::log::error(System::Renderer, "SPIRV generation failed!");
prism::log("SPIRV generation failed!");
return std::nullopt;
}

View file

@ -4,7 +4,6 @@ set(SRC
include/string_utils.hpp
include/timer.hpp
include/common.hpp
include/file_utils.hpp
include/assertions.hpp
include/path.hpp

View file

@ -1,13 +0,0 @@
#pragma once
#include <string>
#include <filesystem>
#include "path.hpp"
namespace prism::log {
inline void internal_format(std::string& msg, const prism::path& arg) {
auto pos = msg.find_first_of("{}");
msg.replace(pos, 2, arg.string());
}
}

View file

@ -12,20 +12,3 @@ bool string_starts_with(const std::string_view haystack, const std::string_view
bool is_numeric(const std::string_view string);
std::vector<std::string> tokenize(const std::string_view string, const std::string_view& delimiters = ",");
namespace utility {
template<class Arg>
inline void format_internal_format(std::string& msg, const Arg& arg) {
auto pos = msg.find_first_of("{}");
msg.replace(pos, 2, arg);
}
template<class... Args>
std::string format(const std::string_view format, Args&&... args) {
auto msg = std::string(format);
((format_internal_format<Args>(msg, args)), ...);
return msg;
}
}

View file

@ -8,7 +8,7 @@
#include "path.hpp"
void app_main(prism::engine* engine) {
prism::set_domain_path(prism::domain::app, "data");
prism::set_domain_path(prism::domain::app, "{resource_dir}/data");
prism::set_domain_path(prism::domain::internal, "{resource_dir}/shaders");
platform::open_window("Example", {-1, -1, 1280, 720}, WindowFlags::Resizable);
@ -30,8 +30,8 @@ void ExampleApp::initialize_render() {
auto sphere_obj = scene->add_object();
auto& sphere_render = scene->add<Renderable>(sphere_obj);
sphere_render.mesh = assetm->get<Mesh>(prism::path("data/models/sphere.model"));
sphere_render.materials = { assetm->get<Material>(prism::path("data/materials/Material.material")) };
sphere_render.mesh = assetm->get<Mesh>(prism::path(prism::app_domain / "models/sphere.model"));
sphere_render.materials = { assetm->get<Material>(prism::path(prism::app_domain / "materials/Material.material")) };
auto probe_obj = scene->add_object();
scene->add<EnvironmentProbe>(probe_obj);

View file

@ -107,3 +107,11 @@ if(BUILD_TOOLS)
FetchContent_MakeAvailable(assimp)
endif()
FetchContent_Declare(
fmt
GIT_REPOSITORY https://github.com/fmtlib/fmt
GIT_TAG 8.0.1
)
FetchContent_MakeAvailable(fmt)

1043
extern/magic_enum/include/magic_enum.hpp vendored Executable file → Normal file

File diff suppressed because it is too large Load diff

View file

@ -7,7 +7,7 @@ if(ENABLE_LINUX)
endif()
if(ENABLE_MACOS)
add_subdirectory(mac)
add_subdirectory(sdl)
endif()
if(ENABLE_IOS)

View file

@ -1,29 +0,0 @@
include(../../cmake/AddPlatformExecutable.cmake)
add_platform(
MAIN_FILE
main.mm.in
SRC
${CMAKE_CURRENT_SOURCE_DIR}/file.mm
EXECUTABLE_PROPERTIES
MACOSX_BUNDLE ON
MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/Info.plist.in"
LINK_LIBRARIES
Core
GFXMetal
"-framework Cocoa"
"-framework QuartzCore"
"-framework GameController"
COMPILE_OPTIONS
-std=c++17
)
function(add_platform_commands target)
target_link_options(${target} BEFORE PRIVATE -pagezero_size 10000 -image_base 100000000
)
if(NOT SKIP_DATA)
add_custom_command(TARGET ${target} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/data $<TARGET_FILE_DIR:${target}>/../Resources/data)
endif()
add_custom_command(TARGET ${target} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_BINARY_DIR}/shaders $<TARGET_FILE_DIR:${target}>/../Resources/shaders)
endfunction()

View file

@ -1,42 +0,0 @@
#include "file.hpp"
#import <Foundation/Foundation.h>
#include <array>
#import <AppKit/AppKit.h>
#include "string_utils.hpp"
#include "log.hpp"
inline std::string clean_path(const std::string_view path) {
auto p = replace_substring(path, "%20", " ");
// this is a path returned by an editor, so skip it
// TODO: find a better way to do this!! NOO!!
if(p.find("file:///") != std::string::npos)
return p.substr(7, p.length());
else
return p;
}
void prism::set_domain_path(const prism::domain domain, const prism::path path) {
NSBundle* bundle = [NSBundle mainBundle];
NSString* resourceFolderPath = [bundle resourcePath];
std::string s = std::string(path);
s = replace_substring(s, "{resource_dir}", [resourceFolderPath cStringUsingEncoding:NSUTF8StringEncoding]);
// basically, if we pass a relative path this will convert it to an absolute one, making get_relative_path functional.
NSURL * bundleURL = [[bundle bundleURL] URLByDeletingLastPathComponent];
NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"%s", s.c_str()] relativeToURL:bundleURL];
domain_data[static_cast<int>(domain)] = clean_path([[[url absoluteURL] absoluteString] cStringUsingEncoding:NSUTF8StringEncoding]);
}
prism::path prism::get_writeable_directory() {
NSArray* paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString* resourceFolderPath = paths[0];
return [resourceFolderPath cStringUsingEncoding:NSUTF8StringEncoding];
}

View file

@ -1,634 +0,0 @@
#import <Cocoa/Cocoa.h>
#import <MetalKit/MetalKit.h>
#import <Foundation/Foundation.h>
#include <GameController/GameController.h>
#include <mach/mach_time.h>
#include <gfx_metal.hpp>
#include <engine.hpp>
#include <map>
#include <file.hpp>
#include <string_utils.hpp>
#include <@APP_INCLUDE@>
uint64_t last_time = 0;
bool is_qutting = false;
static std::map<InputButton, int> inputToKeyCode = { {
{InputButton::C, 8},
{InputButton::V, 9},
{InputButton::X, 7},
{InputButton::Q, 12},
{InputButton::Y, 16},
{InputButton::Z, 6},
{InputButton::Backspace, 51},
{InputButton::Enter, 36},
{InputButton::Space, 49},
{InputButton::Shift, 10},
{InputButton::A, 0},
{InputButton::W, 13},
{InputButton::S, 1},
{InputButton::D, 2},
{InputButton::Escape, 53},
{InputButton::Tab, 48},
{InputButton::LeftArrow, 123},
{InputButton::RightArrow, 124}
}};
struct NativeWindow;
const char* platform::get_name() {
return "macOS";
}
int platform::get_keycode(InputButton key) {
if(inputToKeyCode.count(key))
return inputToKeyCode[key];
else
return -1;
}
GFX* interface = nullptr;
@APP_CLASS@* app = nullptr;
float scrollX = 0.0f, scrollY = 0.0f;
std::array<bool, 8> inputKeys;
float rightX = 0.0f, rightY = 0.0f;
float leftX = 0.0f, leftY = 0.0f;
@interface GameView : NSObject<NSWindowDelegate>
@property NativeWindow *native;
@end
struct NativeWindow {
int index = 0;
NSWindow* window;
GameView* delegate;
CAMetalLayer* layer;
bool willCaptureMouse = false;
int currentWidth = 0, currentHeight = 0;
int timeout = 0;
NSPoint currentMousePos;
bool showingCursor = true;
bool inFocus = true;
};
std::vector<NativeWindow*> windows;
NativeWindow* get_window(const int index) {
for(auto& window : windows) {
if(window->index == index)
return window;
}
return nullptr;
}
@implementation GameView
- (void)gameQuit {
engine->prepare_quit();
}
- (void)gameFocus {
if(![self native]->showingCursor)
[NSCursor hide];
[self native]->inFocus = true;
}
- (void)gameLostFocus {
if(![self native]->showingCursor)
[NSCursor unhide];
[self native]->inFocus = false;
}
const double NANOSECONDS_TO_MILLISECONDS = 1.0 / 1000000000.0;
- (void)update {
const bool is_main_window = [self native]->index == 0;
if(is_main_window) {
if([self native]->willCaptureMouse && [self native]->timeout > 5 && [self native]->inFocus) {
NSPoint point;
point.x = [self native]->window.frame.origin.x + ([self native]->currentWidth / 2);
point.y = [NSScreen mainScreen].frame.size.height - ([self native]->window.frame.origin.y + ([self native]->currentHeight / 2));
CGWarpMouseCursorPosition(point);
CGAssociateMouseAndMouseCursorPosition(true);
[self native]->currentMousePos.x = [self native]->currentWidth / 2;
[self native]->currentMousePos.y = [self native]->currentHeight / 2;
[self native]->timeout = 0;
}
[self native]->timeout++;
}
}
- (void)render {
engine->render([self native]->index);
}
- (void)windowDidResize:(NSNotification *)notification {
[self native]->currentWidth = [self native]->window.contentView.frame.size.width;
[self native]->currentHeight = [self native]->window.contentView.frame.size.height;
NSRect bounds = [[self native]->layer bounds];
bounds = [[self native]->window convertRectToBacking:bounds];
[self native]->layer.drawableSize = NSSizeToCGSize(bounds.size);;
engine->resize([self native]->index, {static_cast<uint32_t>([self native]->currentWidth), static_cast<uint32_t>([self native]->currentHeight)});
}
-(void)windowWillClose:(NSNotification *)notification {
if([self native]->index == 0)
is_qutting = app->should_quit();
}
- (void) controllerConnected {
GCController* controller = [GCController controllers][0];
[[controller extendedGamepad] setValueChangedHandler:^(GCExtendedGamepad * _Nonnull gamepad, GCControllerElement * _Nonnull element) {
const auto& handle_element = [element](int index, GCControllerElement* e) {
if(element == e)
inputKeys[index] = [(GCControllerButtonInput*)e value] == 1.0f;
};
handle_element(0, [[controller extendedGamepad] buttonA]);
handle_element(1, [[controller extendedGamepad] buttonB]);
handle_element(2, [[controller extendedGamepad] buttonX]);
handle_element(3, [[controller extendedGamepad] buttonY]);
if(element == [[controller extendedGamepad] dpad]) {
inputKeys[4] = [[[[controller extendedGamepad] dpad] up] value] == 1.0f;
inputKeys[5] = [[[[controller extendedGamepad] dpad] down] value] == 1.0f;
inputKeys[6] = [[[[controller extendedGamepad] dpad] left] value] == 1.0f;
inputKeys[7] = [[[[controller extendedGamepad] dpad] right] value] == 1.0f;
}
if(element == [[controller extendedGamepad] leftThumbstick]) {
leftX = [[[[controller extendedGamepad] leftThumbstick] xAxis] value];
leftY = [[[[controller extendedGamepad] leftThumbstick] yAxis] value];
}
if(element == [[controller extendedGamepad] rightThumbstick]) {
rightX = [[[[controller extendedGamepad] rightThumbstick] xAxis] value];
rightY = [[[[controller extendedGamepad] rightThumbstick] yAxis] value];
}
}];
}
@end
std::map<int, bool> pressed;
NSString* currentCharString = nil;
bool platform::supports_feature(const PlatformFeature feature) {
switch(feature) {
case PlatformFeature::Windowing:
return true;
}
return false;
}
bool platform::get_key_down(const InputButton key) {
if(key == InputButton::ButtonA)
return inputKeys[0];
if(key == InputButton::ButtonB)
return inputKeys[1];
if(key == InputButton::ButtonX)
return inputKeys[2];
if(key == InputButton::ButtonY)
return inputKeys[3];
if(key == InputButton::DPadUp)
return inputKeys[4];
if(key == InputButton::DPadDown)
return inputKeys[5];
if(key == InputButton::DPadLeft)
return inputKeys[6];
if(key == InputButton::DPadRight)
return inputKeys[7];
if(inputToKeyCode.count(key))
return pressed[inputToKeyCode[key]];
else
return false;
}
char* platform::translate_keycode(const unsigned int keycode) {
return const_cast<char*>([currentCharString UTF8String]);
}
inline std::string clean_path(const std::string_view path) {
auto p = replace_substring(path, "%20", " ");
// this is a path returned by an editor, so skip it
// TODO: find a better way to do this!! NOO!!
if(p.find("file:///") != std::string::npos)
return p.substr(7, p.length());
else
return p;
}
void platform::open_dialog(const bool existing, std::function<void(std::string)> returnFunction, bool openDirectory) {
NSOpenPanel* openDlg = [NSOpenPanel openPanel];
[openDlg setCanChooseFiles:YES];
[openDlg setAllowsMultipleSelection:NO];
[openDlg setCanChooseDirectories:openDirectory];
[openDlg setCanChooseFiles:!openDirectory];
if([openDlg runModal] == NSModalResponseOK)
returnFunction(clean_path([openDlg.URLs[0].absoluteString UTF8String]));
}
void platform::save_dialog(std::function<void(std::string)> returnFunction) {
NSSavePanel* openDlg = [NSSavePanel savePanel];
if ([openDlg runModal] == NSModalResponseOK)
returnFunction(clean_path([openDlg.URL.absoluteString UTF8String]));
}
void platform::capture_mouse(const bool capture) {
windows[0]->willCaptureMouse = capture;
if(windows[0]->showingCursor && capture) {
[NSCursor hide];
windows[0]->showingCursor = false;
} else if(!windows[0]->showingCursor && !capture) {
[NSCursor unhide];
windows[0]->showingCursor = true;
}
}
float platform::get_window_dpi(const int index) {
auto window = get_window(index);
return [window->window backingScaleFactor];
}
float platform::get_monitor_dpi() {
return [[NSScreen mainScreen] backingScaleFactor];
}
CGRect toTopLeftSpace(NSRect frame) {
frame.origin.y = NSMaxY([[NSScreen mainScreen] frame]) - NSMaxY(frame);
return NSRectToCGRect(frame);
}
prism::Rectangle platform::get_monitor_resolution() {
auto frame = toTopLeftSpace([[NSScreen mainScreen] frame]);
return {static_cast<int32_t>(frame.origin.x), static_cast<int32_t>(frame.origin.y), static_cast<uint32_t>(frame.size.width), static_cast<uint32_t>(frame.size.height)};
}
prism::Rectangle platform::get_monitor_work_area() {
auto frame = toTopLeftSpace([[NSScreen mainScreen] visibleFrame]);
return {static_cast<int32_t>(frame.origin.x), static_cast<int32_t>(frame.origin.y), static_cast<uint32_t>(frame.size.width), static_cast<uint32_t>(frame.size.height)};
}
NSPoint get_fixed_cursor_point(NSPoint point) {
return {point.x, std::max(windows[0]->currentHeight - point.y, 0.0)};
}
prism::Offset platform::get_cursor_position() {
return {static_cast<int32_t>(windows[0]->currentMousePos.x), static_cast<int32_t>(windows[0]->currentMousePos.y)};
}
prism::Offset platform::get_screen_cursor_position() {
return {static_cast<int32_t>([NSEvent mouseLocation].x), static_cast<int32_t>([[NSScreen mainScreen] frame].size.height - [NSEvent mouseLocation].y)};
}
std::tuple<float, float> platform::get_wheel_delta() {
return {scrollX, scrollY};
}
std::tuple<float, float> platform::get_left_stick_position() {
return {leftX, leftY};
}
std::tuple<float, float> platform::get_right_stick_position() {
return {rightX, rightY};
}
prism::Extent platform::get_window_size(const int index) {
auto window = get_window(index);
return {static_cast<uint32_t>(window->currentWidth), static_cast<uint32_t>(window->currentHeight)};
}
prism::Extent platform::get_window_drawable_size(const int index) {
auto window = get_window(index);
return {static_cast<uint32_t>(window->currentWidth * window->window.backingScaleFactor), static_cast<uint32_t>(window->currentHeight * window->window.backingScaleFactor)};
}
prism::Offset platform::get_window_position(const int index) {
auto window = get_window(index);
auto frame = toTopLeftSpace([window->window contentRectForFrameRect:[window->window frame]]);
return {static_cast<int32_t>(frame.origin.x), static_cast<int32_t>(frame.origin.y)};
}
bool platform::is_window_focused(const int index) {
auto window = get_window(index);
return [window->window isKeyWindow];
}
void platform::set_window_focused(const int index) {
auto window = get_window(index);
[window->window makeKeyWindow];
}
bool platform::get_mouse_button_down(const int button) {
return (button + 1) & [NSEvent pressedMouseButtons];
}
void platform::set_window_position(const int index, const prism::Offset offset) {
auto window = get_window(index);
NSPoint p;
p.x = offset.x;
p.y = [[NSScreen mainScreen] frame].size.height - offset.y;
[window->window setFrameTopLeftPoint:p];
}
void platform::set_window_size(const int index, const prism::Extent extent) {
auto window = get_window(index);
NSSize size;
size.width = extent.width;
size.height = extent.height;
[window->window setContentSize:size];
}
void platform::set_window_title(const int index, const std::string_view title) {
auto window = get_window(index);
[window->window setTitle:[NSString stringWithUTF8String:title.data()]];
}
void platform::mute_output() {}
void platform::unmute_output() {}
PlatformTheme platform::get_theme() {
auto str = [[[NSApplication sharedApplication] effectiveAppearance] name];
if(str == NSAppearanceNameAqua)
return PlatformTheme::Light;
else if(str == NSAppearanceNameDarkAqua)
return PlatformTheme::Dark;
return PlatformTheme::Light;
}
int platform::open_window(const std::string_view title, const prism::Rectangle rect, const WindowFlags flags) {
NativeWindow* native = new NativeWindow();
native->index = windows.size();
GameView* del = [[GameView alloc] init];
native->delegate = del;
windows.push_back(native);
NSRect nrect = NSMakeRect(rect.offset.x, rect.offset.y, rect.extent.width, rect.extent.height);
NSWindowStyleMask windowStyle = NSWindowStyleMaskTitled
| NSWindowStyleMaskClosable
| NSWindowStyleMaskMiniaturizable;
if(flags == WindowFlags::Resizable)
windowStyle |= NSWindowStyleMaskResizable;
else if(flags == WindowFlags::Borderless)
windowStyle = NSWindowStyleMaskBorderless;
native->window = [[NSWindow alloc]
initWithContentRect: nrect
styleMask:windowStyle
backing: NSBackingStoreBuffered
defer: NO];
native->currentWidth = rect.extent.width;
native->currentHeight = rect.extent.height;
native->layer = [CAMetalLayer layer];
NSView* view = [[NSView alloc] init];
view.wantsLayer = YES;
view.layer = native->layer;
native->layer.contentsScale = [native->window backingScaleFactor];
engine->add_window(view.layer, native->index, rect.extent);
[native->window setContentView:view];
if(native->index == 0)
app->initialize_render();
[native->window setTitle:[NSString stringWithUTF8String:title.data()]];
[del setNative:native];
[native->window setDelegate:del];
[native->window orderFrontRegardless];
if(native->index == 0) {
NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
[nc addObserver:del
selector:@selector(gameQuit)
name:NSApplicationWillTerminateNotification
object:nil];
[nc addObserver:del
selector:@selector(gameFocus)
name:NSApplicationDidBecomeActiveNotification
object:nil];
[nc addObserver:del
selector:@selector(gameLostFocus)
name:NSApplicationDidResignActiveNotification
object:nil];
[nc addObserver:del
selector:@selector(controllerConnected)
name:GCControllerDidConnectNotification
object:nil];
}
return native->index;
}
void platform::close_window(const int index) {
auto window = get_window(index);
engine->remove_window(window->index);
[window->window close];
utility::erase(windows, window);
}
void platform::force_quit() {
[NSApp terminate:nil];
}
int main(int argc, char* argv[]) {
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
NSApp = [NSApplication sharedApplication];
engine = new prism::engine(argc, argv);
app = new @APP_CLASS@();
engine->set_app(app);
GFXCreateInfo createInfo = {};
createInfo.api_validation_enabled = true;
interface = new GFXMetal();
if(interface->initialize(createInfo)) {
engine->set_gfx(interface);
} else {
NSLog(@"Failed to create Metal context!");
return -1;
}
app_main(engine);
[NSEvent addLocalMonitorForEventsMatchingMask:NSEventMaskKeyDown handler:^(NSEvent *event) {
pressed[event.keyCode] = true;
currentCharString = event.characters;
engine->process_key_down(event.keyCode);
return (NSEvent*)nil;
}];
[NSEvent addLocalMonitorForEventsMatchingMask:NSEventMaskKeyUp handler:^(NSEvent *event) {
pressed[event.keyCode] = false;
engine->process_key_up(event.keyCode);
return (NSEvent*)nil;
}];
[NSEvent addLocalMonitorForEventsMatchingMask:NSEventMaskMouseMoved handler:^(NSEvent *event) {
windows[0]->currentMousePos = get_fixed_cursor_point(event.locationInWindow);
return event;
}];
[NSEvent addLocalMonitorForEventsMatchingMask:NSEventMaskLeftMouseDown handler:^(NSEvent *event) {
auto point = get_fixed_cursor_point(event.locationInWindow);
engine->process_mouse_down(0, {static_cast<int32_t>(point.x), static_cast<int32_t>(point.y)});
return event;
}];
[NSEvent addLocalMonitorForEventsMatchingMask:NSEventMaskRightMouseDown handler:^(NSEvent *event) {
auto point = get_fixed_cursor_point(event.locationInWindow);
engine->process_mouse_down(1, {static_cast<int32_t>(point.x), static_cast<int32_t>(point.y)});
return event;
}];
[NSEvent addLocalMonitorForEventsMatchingMask:NSEventMaskRightMouseDragged handler:^(NSEvent *event) {
windows[0]->currentMousePos = get_fixed_cursor_point(event.locationInWindow);
return event;
}];
[NSEvent addLocalMonitorForEventsMatchingMask:NSEventMaskLeftMouseDragged handler:^(NSEvent *event) {
windows[0]->currentMousePos = get_fixed_cursor_point(event.locationInWindow);
return event;
}];
[NSEvent addLocalMonitorForEventsMatchingMask:NSEventMaskScrollWheel handler:^(NSEvent *event) {
double wheel_dx = 0.0;
double wheel_dy = 0.0;
wheel_dx = [event scrollingDeltaX];
wheel_dy = [event scrollingDeltaY];
if ([event hasPreciseScrollingDeltas])
{
wheel_dx *= 0.1;
wheel_dy *= 0.1;
}
scrollX += wheel_dx * 0.1f;
scrollY += wheel_dy * 0.1f;
return event;
}];
last_time = clock_gettime_nsec_np(CLOCK_UPTIME_RAW);
while (!is_qutting) {
@autoreleasepool {
NSEvent* event = nullptr;
do {
event = [NSApp nextEventMatchingMask: NSEventMaskAny
untilDate: nil
inMode: NSDefaultRunLoopMode
dequeue: YES];
if(event)
[NSApp sendEvent: event];
} while(event);
const uint64_t current = clock_gettime_nsec_np(CLOCK_UPTIME_RAW);
const uint64_t elapsed = current - last_time;
if(engine->is_quitting())
is_qutting = true;
for(auto& window : windows) {
if(window != nullptr)
[window->delegate update];
}
engine->update(elapsed * NANOSECONDS_TO_MILLISECONDS);
engine->begin_frame(elapsed * NANOSECONDS_TO_MILLISECONDS);
for(auto& window : windows) {
if(window != nullptr)
[window->delegate render];
}
engine->end_frame();
scrollX = 0.0f;
scrollY = 0.0f;
last_time = current;
}
}
app->prepare_quit();
[NSApp release];
[pool release];
return 0;
}

View file

@ -4,6 +4,9 @@ add_platform(
SRC ${CMAKE_CURRENT_SOURCE_DIR}/file.cpp
MAIN_FILE
main.cpp.in
EXECUTABLE_PROPERTIES
MACOSX_BUNDLE ON
MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/Info.plist.in"
LINK_LIBRARIES
SDL2::Main
Core
@ -12,8 +15,12 @@ add_platform(
function(add_platform_commands target)
if(NOT SKIP_DATA)
if(ENABLE_MACOS)
add_custom_command(TARGET ${target} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/data $<TARGET_FILE_DIR:${target}>/../Resources/data)
else()
add_custom_command(TARGET ${target} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/data $<TARGET_FILE_DIR:${target}>/data)
endif()
endif()
# we HAVE to create this dummy target to convince CMake to properly copy over shader files.
# before you ask, we have used POST_BUILD before but that only runs if the TARGET specified is built.
@ -24,10 +31,18 @@ function(add_platform_commands target)
add_custom_target(${DUMMY_NAME} ALL DEPENDS ${CMAKE_BINARY_DIR}/dummy)
if(ENABLE_MACOS)
add_custom_command(OUTPUT ${CMAKE_BINARY_DIR}/dummy
COMMAND ${CMAKE_COMMAND} -E make_directory $<TARGET_FILE_DIR:${target}>/../Resources/shaders
COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_BINARY_DIR}/shaders $<TARGET_FILE_DIR:${target}>/../Resources/shaders
)
else()
add_custom_command(OUTPUT ${CMAKE_BINARY_DIR}/dummy
COMMAND ${CMAKE_COMMAND} -E make_directory $<TARGET_FILE_DIR:${target}>/shaders
COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_BINARY_DIR}/shaders $<TARGET_FILE_DIR:${target}>/shaders
)
endif()
add_dependencies(${target} ${DUMMY_NAME})
endfunction()

View file

@ -3,7 +3,7 @@
<plist version="1.0">
<dict>
<key>CFBundleDisplayName</key>
<string>Fantasy\U00A0app</string>
<string>${MACOSX_BUNDLE_EXECUTABLE_NAME}</string>
<key>CFBundleDevelopmentRegion</key>
<string>English</string>
<key>CFBundleExecutable</key>
@ -19,7 +19,7 @@
<key>CFBundleLongVersionString</key>
<string>${MACOSX_BUNDLE_LONG_VERSION_STRING}</string>
<key>CFBundleName</key>
<string>Project Shopkeep</string>
<string>${MACOSX_BUNDLE_EXECUTABLE_NAME}</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
@ -31,7 +31,7 @@
<key>CSResourcesFileMapped</key>
<true/>
<key>NSHumanReadableCopyright</key>
<string>(c)2020 Blinding Light Games</string>
<string>(c) 2021</string>
<key>NSHighResolutionCapable</key>
<true/>
</dict>

View file

@ -3,7 +3,11 @@
#include "string_utils.hpp"
void prism::set_domain_path(const prism::domain domain, const prism::path path) {
#ifdef PLATFORM_MACOS
domain_data[(int)domain] = replace_substring(path.string(), "{resource_dir}/", "../Resources/");
#else
domain_data[(int)domain] = replace_substring(path.string(), "{resource_dir}/", "");
#endif
}
prism::path prism::get_writeable_directory() {

View file

@ -257,14 +257,16 @@ void platform::capture_mouse(const bool capture) {
SDL_CaptureMouse((SDL_bool)capture);
}
void platform::open_dialog(const bool existing, std::function<void(std::string)> returnFunction, bool openDirectory) {
}
void platform::save_dialog(std::function<void(std::string)> returnFunction) {
}
char* platform::translate_keycode(const unsigned int keycode) {
return const_cast<char*>(SDL_GetKeyName(keycode));
return const_cast<char*>(SDL_GetKeyName(SDL_GetKeyFromScancode((SDL_Scancode)keycode)));
}
void platform::begin_text_input() {
SDL_StartTextInput();
}
void platform::end_text_input() {
SDL_StopTextInput();
}
void platform::mute_output() {}
@ -320,7 +322,13 @@ int main(int argc, char* argv[]) {
engine->process_key_down(event.key.keysym.scancode);
}
break;
case SDL_KEYUP:
{
engine->process_key_up(event.key.keysym.scancode);
}
break;
case SDL_WINDOWEVENT:
{
if (event.window.event == SDL_WINDOWEVENT_RESIZED) {
auto window = get_window_by_sdl_id(event.window.windowID);
if(window != nullptr)
@ -328,6 +336,12 @@ int main(int argc, char* argv[]) {
} else if(event.window.event == SDL_WINDOWEVENT_CLOSE) {
engine->quit();
}
}
break;
case SDL_TEXTINPUT:
{
engine->process_text_input(event.text.text);
}
break;
}
}

View file

@ -21,6 +21,7 @@
#include "asset.hpp"
#include "scene.hpp"
#include "renderer.hpp"
#include "imgui_backend.hpp"
class TransformCommand : public Command {
public:
@ -369,7 +370,7 @@ inline void editPath(const char* label, std::string& path, bool editable = true,
ImGui::SameLine();
if(ImGui::Button("...")) {
platform::open_dialog(false, [&path, &on_selected](std::string p) {
engine->get_imgui().open_dialog(false, [&path, &on_selected](std::string p) {
path = prism::get_relative_path(prism::domain::app, p).string();
if(on_selected != nullptr)

View file

@ -18,7 +18,6 @@
#include "gfx.hpp"
#include "gfx_commandbuffer.hpp"
#include "imgui_utility.hpp"
#include "screen.hpp"
#include "console.hpp"
#include "input.hpp"
#include "scenecapture.hpp"
@ -48,11 +47,7 @@ const std::map<ImGuiKey, InputButton> imToPl = {
};
CommonEditor::CommonEditor(std::string id) : id(id) {
#ifdef PLATFORM_MACOS
prism::set_domain_path(prism::domain::app, "../../../data");
#else
prism::set_domain_path(prism::domain::app, "data");
#endif
prism::set_domain_path(prism::domain::app, "{resource_dir}/data");
prism::set_domain_path(prism::domain::internal, "{resource_dir}/shaders");
ImGuiIO& io = ImGui::GetIO();
@ -465,20 +460,6 @@ void editRigidbody(Rigidbody& rigidbody) {
ImGui::DragInt("Mass", &rigidbody.mass);
}
void editUI(UI& ui) {
ImGui::DragInt("Width", &ui.width);
ImGui::DragInt("Height", &ui.height);
ImGui::InputText("Path", &ui.ui_path);
if(ImGui::Button("Reload")) {
ui.screen = engine->load_screen(prism::app_domain / ui.ui_path);
engine->get_renderer()->init_screen(ui.screen);
ui.screen->extent.width = ui.width;
ui.screen->extent.height = ui.height;
ui.screen->calculate_sizes();
}
}
void editProbe(EnvironmentProbe& probe) {
ImGui::Checkbox("Is Sized", &probe.is_sized);
if(probe.is_sized)
@ -614,11 +595,6 @@ void CommonEditor::drawPropertyEditor() {
ImGui::CloseCurrentPopup();
}
if(ImGui::Selectable("UI")) {
scene->add<UI>(selected_object);
ImGui::CloseCurrentPopup();
}
if(ImGui::Selectable("Environment Probe")) {
scene->add<EnvironmentProbe>(selected_object);
ImGui::CloseCurrentPopup();
@ -645,9 +621,6 @@ void CommonEditor::drawPropertyEditor() {
if(componentHeader<Rigidbody>(*scene, selected_object, "Rigidbody"))
editRigidbody(scene->get<Rigidbody>(selected_object));
if(componentHeader<UI>(*scene, selected_object, "UI"))
editUI(scene->get<UI>(selected_object));
if(componentHeader<EnvironmentProbe>(*scene, selected_object, "Environment Probe"))
editProbe(scene->get<EnvironmentProbe>(selected_object));
}
@ -732,7 +705,7 @@ void CommonEditor::set_undo_stack(UndoStack *stack) {
bool mesh_readable(const prism::path path) {
auto file = prism::open_file(path);
if(!file.has_value()) {
prism::log::error(System::Renderer, "Failed to load mesh from {}!", path);
prism::log("Failed to load mesh from {}!", path.string());
return false;
}
@ -745,7 +718,7 @@ bool mesh_readable(const prism::path path) {
bool material_readable(const prism::path path) {
auto file = prism::open_file(path);
if(!file.has_value()) {
prism::log::error(System::Core, "Failed to load material from {}!", path);
prism::log("Failed to load material from {}!", path.string());
return false;
}
@ -759,6 +732,9 @@ void cacheAssetFilesystem() {
asset_files.clear();
auto data_directory = "data";
if(!std::filesystem::exists(data_directory))
return;
for(auto& p : std::filesystem::recursive_directory_iterator(data_directory)) {
if(p.path().extension() == ".model" && mesh_readable(p.path())) {
asset_files[std::filesystem::relative(p, data_directory)] = AssetType::Mesh;
@ -1046,8 +1022,10 @@ void CommonEditor::drawConsole() {
ImGui::BeginChild("console_output", ImVec2(-1, -1), true);
for(const auto& message : prism::log::stored_output)
ImGui::TextWrapped("%s", message.c_str());
// TODO: implement for new log system
//for(const auto& message : prism::log::stored_output)
// ImGui::TextWrapped("%s", message.c_str());
ImGui::Text("unimplemented :-(");
ImGui::EndChild();
}
@ -1123,7 +1101,7 @@ void CommonEditor::save_thumbnail_cache() {
FILE* file = fopen("thumbnail-cache", "wb");
if(file == nullptr) {
prism::log::error(System::Core, "Failed to write thumbnail cache!");
prism::log("Failed to write thumbnail cache!");
return;
}

View file

@ -2,13 +2,11 @@ set(SRC
include/prismeditor.hpp
include/materialeditor.hpp
include/sceneeditor.hpp
include/uieditor.hpp
include/prefabeditor.hpp
src/prismeditor.cpp
src/materialeditor.cpp
src/sceneeditor.cpp
src/uieditor.cpp
src/prefabeditor.cpp)
add_platform_executable(

View file

@ -1,27 +0,0 @@
#pragma once
#include "prismeditor.hpp"
#include "screen.hpp"
class UIEditor : public Editor {
public:
UIEditor();
ui::Screen* screen = nullptr;
UIElement* current_element = nullptr;
bool has_menubar() const override;
std::string get_title() const override;
Scene* get_scene() const override;
void setup_windows(ImGuiID dockspace) override;
void edit_metric(const char* label, UIElement::Metrics::Metric* metric);
std::string save_metric(UIElement::Metrics::Metric metric);
void save(std::string path);
void draw(CommonEditor* editor) override;
};

View file

@ -131,7 +131,7 @@ void MaterialEditor::draw(CommonEditor* editor) {
if (ImGui::BeginMenu("File")) {
if(ImGui::MenuItem("Save", "CTRL+S")) {
if (path.empty()) {
platform::save_dialog([this](std::string path) {
engine->get_imgui().save_dialog([this](std::string path) {
this->path = path;
save_material(*material, path);
@ -142,7 +142,7 @@ void MaterialEditor::draw(CommonEditor* editor) {
}
if (ImGui::MenuItem("Save as...", "CTRL+S")) {
platform::save_dialog([this](std::string path) {
engine->get_imgui().save_dialog([this](std::string path) {
this->path = path;
save_material(*material, path);

View file

@ -36,7 +36,7 @@ void PrefabEditor::draw(CommonEditor* editor) {
if (ImGui::BeginMenu("File")) {
if(ImGui::MenuItem("Save", "CTRL+S")) {
if (path.empty()) {
platform::save_dialog([this](std::string path) {
engine->get_imgui().save_dialog([this](std::string path) {
this->path = path;
engine->save_prefab(root_object, path);
@ -47,7 +47,7 @@ void PrefabEditor::draw(CommonEditor* editor) {
}
if (ImGui::MenuItem("Save as...", "CTRL+S")) {
platform::save_dialog([this](std::string path) {
engine->get_imgui().save_dialog([this](std::string path) {
this->path = path;
engine->save_prefab(root_object, path);

View file

@ -11,11 +11,9 @@
#include "json_conversions.hpp"
#include "platform.hpp"
#include "string_utils.hpp"
#include "screen.hpp"
#include "sceneeditor.hpp"
#include "materialeditor.hpp"
#include "prefabeditor.hpp"
#include "uieditor.hpp"
#include "log.hpp"
std::string get_filename(const std::string path) {
@ -164,15 +162,6 @@ void PrismEditor::open_asset(const prism::path path) {
editor->scene = engine->get_scene();
editors.push_back(editor);
} else if(path.extension() == ".json") {
UIEditor* editor = new UIEditor();
editor->path = path.string();
setup_editor(editor);
editor->screen = engine->load_screen(path);
editor->screen->calculate_sizes();
editors.push_back(editor);
}
}
@ -233,19 +222,11 @@ void PrismEditor::drawUI() {
editors.push_back(editor);
}
if(ImGui::MenuItem("UI")) {
UIEditor* editor = new UIEditor();
editor->modified = true;
setup_editor(editor);
editors.push_back(editor);
}
ImGui::EndMenu();
}
if(ImGui::MenuItem("Open", "CTRL+O")) {
platform::open_dialog(true, [this](std::string path) {
engine->get_imgui().open_dialog(true, [this](std::string path) {
open_requests.emplace_back(path, false);
addOpenedFile(path);

View file

@ -44,7 +44,7 @@ void SceneEditor::draw(CommonEditor* editor) {
if (ImGui::BeginMenu("File")) {
if(ImGui::MenuItem("Save", "CTRL+S")) {
if (path.empty()) {
platform::save_dialog([this](std::string path) {
engine->get_imgui().save_dialog([this](std::string path) {
this->path = path;
engine->save_scene(path);
@ -55,7 +55,7 @@ void SceneEditor::draw(CommonEditor* editor) {
}
if(ImGui::MenuItem("Save as...", "CTRL+S")) {
platform::save_dialog([this](std::string path) {
engine->get_imgui().save_dialog([this](std::string path) {
this->path = path;
engine->save_scene(path);
@ -107,7 +107,7 @@ void SceneEditor::draw(CommonEditor* editor) {
}
if(ImGui::MenuItem("Prefab")) {
platform::open_dialog(false, [](std::string path) {
engine->get_imgui().open_dialog(false, [](std::string path) {
engine->add_prefab(*engine->get_scene(), path);
});
}

View file

@ -1,218 +0,0 @@
#include "uieditor.hpp"
#include <nlohmann/json.hpp>
#include <imgui.h>
#include <imgui_stdlib.h>
#include <imgui_internal.h>
#include "engine.hpp"
#include "imgui_utility.hpp"
UIEditor::UIEditor() : Editor() {
screen = new ui::Screen();
screen->extent.width = 1280;
screen->extent.height = 720;
}
bool UIEditor::has_menubar() const {
return true;
}
std::string UIEditor::get_title() const {
return path.empty() ? "New UI" : get_filename(path);
}
Scene* UIEditor::get_scene() const {
return nullptr;
}
void UIEditor::setup_windows(ImGuiID dockspace) {
ImGuiID dock_main_id = dockspace;
ImGuiID dock_id_left, dock_id_right;
ImGui::DockBuilderSplitNode(dock_main_id, ImGuiDir_Left, 0.60f, &dock_id_left, &dock_id_right);
ImGui::DockBuilderDockWindow(get_window_name("Properties").c_str(), dock_id_right);
ImGui::DockBuilderSplitNode(dock_id_left, ImGuiDir_Left, 0.30f, &dock_id_left, &dock_id_right);
ImGui::DockBuilderDockWindow(get_window_name("Outliner").c_str(), dock_id_left);
ImGui::DockBuilderDockWindow(get_window_name("Preview").c_str(), dock_id_right);
}
void UIEditor::edit_metric(const char* label, UIElement::Metrics::Metric* metric) {
if(ImGui::DragInt(label, &metric->value, 1.0f, 0, 0, metric->type == MetricType::Absolute ? "%d" : "%d%%"))
screen->calculate_sizes();
ImGui::SameLine();
ImGui::PushID(label);
if(ImGui::ComboEnum("T", &metric->type))
screen->calculate_sizes();
ImGui::PopID();
}
std::string UIEditor::save_metric(UIElement::Metrics::Metric metric) {
return std::to_string(metric.value) + (metric.type == MetricType::Absolute ? "px" : "%");
}
void UIEditor::save(std::string path) {
nlohmann::json j;
for (auto& element : screen->elements) {
nlohmann::json e;
e["id"] = element.id;
e["text"] = element.text;
e["visible"] = element.visible;
e["wrapText"] = element.wrap_text;
nlohmann::json m;
m["x"] = save_metric(element.metrics.x);
m["y"] = save_metric(element.metrics.y);
m["width"] = save_metric(element.metrics.width);
m["height"] = save_metric(element.metrics.height);
e["metrics"] = m;
if(element.text_location == UIElement::TextLocation::Center) {
e["textLocation"] = "center";
} else {
e["textLocation"] = "topLeft";
}
j["elements"].push_back(e);
}
std::ofstream out(path);
out << std::setw(4) << j;
}
void UIEditor::draw(CommonEditor*) {
if (ImGui::BeginMenuBar()) {
if (ImGui::BeginMenu("File")) {
if(ImGui::MenuItem("Save", "CTRL+S")) {
if (path.empty()) {
platform::save_dialog([this](std::string path) {
this->path = path;
save(path);
});
} else {
save(path);
}
}
if (ImGui::MenuItem("Save as...", "CTRL+S")) {
platform::save_dialog([this](std::string path) {
this->path = path;
save(path);
});
}
ImGui::Separator();
if(ImGui::MenuItem("Close"))
wants_to_close = true;
ImGui::EndMenu();
}
ImGui::EndMenuBar();
}
if(begin("Outliner")) {
if(ImGui::Button("Add Element")) {
screen->elements.push_back(UIElement());
current_element = &screen->elements.back();
}
for(auto& element : screen->elements) {
if(ImGui::Selectable(element.id.c_str()))
current_element = &element;
}
}
ImGui::End();
if(begin("Properties")) {
if(current_element != nullptr) {
ImGui::InputText("ID", &current_element->id);
edit_metric("X", &current_element->metrics.x);
edit_metric("Y", &current_element->metrics.y);
edit_metric("Width", &current_element->metrics.width);
edit_metric("Height", &current_element->metrics.height);
ImGui::InputText("Text", &current_element->text);
ImGui::ColorEdit4("Background color", current_element->background.color.v);
ImGui::Checkbox("Visible", &current_element->visible);
ImGui::Checkbox("Wrap text", &current_element->wrap_text);
bool setCenter = current_element->text_location == UIElement::TextLocation::Center;
ImGui::Checkbox("Center Text", &setCenter);
current_element->text_location = setCenter ? UIElement::TextLocation::Center : UIElement::TextLocation::TopLeft;
if(ImGui::Button("Delete")) {
utility::erase(screen->elements, *current_element);
}
if(ImGui::Button("Duplicate")) {
UIElement element = *current_element;
element.id += " (duplicate)";
screen->elements.push_back(element);
current_element = &screen->elements.back();
}
}
}
ImGui::End();
if(begin("Preview")) {
ImDrawList* draw_list = ImGui::GetWindowDrawList();
static int width = 1280, height = 720;
static int scale = 3;
bool c = ImGui::InputInt("Width", &width);
c |= ImGui::InputInt("Height", &height);
c |= ImGui::InputInt("Scale", &scale);
if(c) {
screen->extent.width = width;
screen->extent.height = height;
screen->calculate_sizes();
}
const ImVec2 p = ImGui::GetCursorScreenPos();
draw_list->AddRect(ImVec2(p.x, p.y), ImVec2(p.x + (width / scale), p.y + (height / scale)), IM_COL32_WHITE);
for(auto& element : screen->elements) {
if(element.absolute_width > 0 && element.absolute_height > 0) {
draw_list->AddRect(
ImVec2(p.x + (element.absolute_x / scale), p.y + (element.absolute_y / scale)),
ImVec2(p.x + ((element.absolute_x + element.absolute_width) / scale), p.y + ((element.absolute_y + element.absolute_height) / scale)),
IM_COL32_WHITE
);
}
if(!element.text.empty()) {
if(element.text_location == UIElement::TextLocation::Center) {
auto textSize = ImGui::CalcTextSize(element.text.c_str());
draw_list->AddText(ImVec2(p.x + (element.absolute_x + (element.absolute_width / 2)) / scale - (textSize.x / 2), p.y + (element.absolute_y + (element.absolute_height / 2)) / scale - (textSize.y / 2)), IM_COL32_WHITE, element.text.c_str());
} else {
draw_list->AddText(ImVec2(p.x + (element.absolute_x / scale), p.y + (element.absolute_y / scale)), IM_COL32_WHITE, element.text.c_str());
}
}
}
}
ImGui::End();
}

View file

@ -5,7 +5,6 @@
#include "shadercompiler.hpp"
#include "log.hpp"
#include "string_utils.hpp"
#include "file_utils.hpp"
bool has_extension(const std::filesystem::path path, const std::string_view extension) {
return string_contains(path.filename().string(), extension);
@ -13,7 +12,7 @@ bool has_extension(const std::filesystem::path path, const std::string_view exte
int main(int argc, char* argv[]) {
if(argc < 2) {
prism::log::error(System::Core, "Not enough arguments!");
prism::log("Not enough arguments!");
return -1;
}
@ -42,17 +41,11 @@ int main(int argc, char* argv[]) {
ShaderLanguage language;
CompileOptions options;
#ifdef PLATFORM_MACOS
options.is_apple_mobile = (bool)argv[3];
language = ShaderLanguage::MSL;
#else
language = ShaderLanguage::SPIRV;
#endif
const auto compiled_source = shader_compiler.compile(ShaderLanguage::GLSL, stage, ShaderSource(buffer.str()), language, options);
if(!compiled_source.has_value()) {
prism::log::error(System::Core, "Error when compiling {}!", source_path);
prism::log("Error when compiling {}!", source_path.string());
return -1;
}