aa
This commit is contained in:
+323
@@ -0,0 +1,323 @@
|
||||
#include <config.hpp>
|
||||
|
||||
#include <json/json.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace openceg {
|
||||
namespace {
|
||||
|
||||
std::string lowercase(std::string value) {
|
||||
std::transform(value.begin(), value.end(), value.begin(), [](unsigned char ch) {
|
||||
return static_cast<char>(std::tolower(ch));
|
||||
});
|
||||
return value;
|
||||
}
|
||||
|
||||
int parse_key_name(const std::string& name) {
|
||||
const std::string key = lowercase(name);
|
||||
if (key == "space") return 32;
|
||||
if (key == "apostrophe" || key == "'") return 39;
|
||||
if (key == "comma" || key == ",") return 44;
|
||||
if (key == "minus" || key == "-") return 45;
|
||||
if (key == "period" || key == ".") return 46;
|
||||
if (key == "slash" || key == "/") return 47;
|
||||
if (key.size() == 1 && key[0] >= '0' && key[0] <= '9') return key[0];
|
||||
if (key.size() == 1 && key[0] >= 'a' && key[0] <= 'z') return key[0] - 'a' + 'A';
|
||||
if (key == "semicolon" || key == ";") return 59;
|
||||
if (key == "equal" || key == "=") return 61;
|
||||
if (key == "leftbracket" || key == "[") return 91;
|
||||
if (key == "backslash" || key == "\\") return 92;
|
||||
if (key == "rightbracket" || key == "]") return 93;
|
||||
if (key == "graveaccent" || key == "`") return 96;
|
||||
if (key == "escape" || key == "esc") return 256;
|
||||
if (key == "enter" || key == "return") return 257;
|
||||
if (key == "tab") return 258;
|
||||
if (key == "backspace") return 259;
|
||||
if (key == "insert") return 260;
|
||||
if (key == "delete" || key == "del") return 261;
|
||||
if (key == "right") return 262;
|
||||
if (key == "left") return 263;
|
||||
if (key == "down") return 264;
|
||||
if (key == "up") return 265;
|
||||
if (key == "pageup" || key == "pgup") return 266;
|
||||
if (key == "pagedown" || key == "pgdown" || key == "pgdn") return 267;
|
||||
if (key == "home") return 268;
|
||||
if (key == "end") return 269;
|
||||
if (key == "capslock" || key == "caps") return 280;
|
||||
if (key == "scrolllock") return 281;
|
||||
if (key == "numlock") return 282;
|
||||
if (key == "printscreen" || key == "prtscr") return 283;
|
||||
if (key == "pause") return 284;
|
||||
if (key.size() > 1 && key[0] == 'f') {
|
||||
try {
|
||||
int number = std::stoi(key.substr(1));
|
||||
if (number >= 1 && number <= 25) return 290 + number - 1;
|
||||
} catch (...) {
|
||||
}
|
||||
}
|
||||
if (key == "kp0" || key == "keypad0") return 320;
|
||||
if (key == "kp1" || key == "keypad1") return 321;
|
||||
if (key == "kp2" || key == "keypad2") return 322;
|
||||
if (key == "kp3" || key == "keypad3") return 323;
|
||||
if (key == "kp4" || key == "keypad4") return 324;
|
||||
if (key == "kp5" || key == "keypad5") return 325;
|
||||
if (key == "kp6" || key == "keypad6") return 326;
|
||||
if (key == "kp7" || key == "keypad7") return 327;
|
||||
if (key == "kp8" || key == "keypad8") return 328;
|
||||
if (key == "kp9" || key == "keypad9") return 329;
|
||||
if (key == "leftshift" || key == "lshift") return 340;
|
||||
if (key == "rightshift" || key == "rshift") return 344;
|
||||
if (key == "leftcontrol" || key == "leftctrl" || key == "lctrl") return 341;
|
||||
if (key == "rightcontrol" || key == "rightctrl" || key == "rctrl") return 345;
|
||||
if (key == "leftalt" || key == "lalt") return 342;
|
||||
if (key == "rightalt" || key == "ralt") return 346;
|
||||
if (key == "leftsuper" || key == "lsuper") return 343;
|
||||
if (key == "rightsuper" || key == "rsuper") return 347;
|
||||
|
||||
try {
|
||||
return std::stoi(name);
|
||||
} catch (...) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
int parse_mouse_button(const std::string& name) {
|
||||
const std::string button = lowercase(name);
|
||||
if (button == "left") return 0;
|
||||
if (button == "right") return 1;
|
||||
if (button == "middle") return 2;
|
||||
try {
|
||||
return std::stoi(name);
|
||||
} catch (...) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
int json_int(const Json::Value& value, int fallback) {
|
||||
if (value.isNumeric()) {
|
||||
return value.asInt();
|
||||
}
|
||||
if (value.isString()) {
|
||||
try {
|
||||
return std::stoi(value.asString());
|
||||
} catch (...) {
|
||||
}
|
||||
}
|
||||
return fallback;
|
||||
}
|
||||
|
||||
float json_float(const Json::Value& value, float fallback) {
|
||||
if (value.isNumeric()) {
|
||||
return value.asFloat();
|
||||
}
|
||||
if (value.isString()) {
|
||||
try {
|
||||
return std::stof(value.asString());
|
||||
} catch (...) {
|
||||
}
|
||||
}
|
||||
return fallback;
|
||||
}
|
||||
|
||||
std::string json_string(const Json::Value& value, const std::string& fallback) {
|
||||
if (value.isString()) {
|
||||
return value.asString();
|
||||
}
|
||||
if (value.isNumeric()) {
|
||||
return std::to_string(value.asInt());
|
||||
}
|
||||
return fallback;
|
||||
}
|
||||
|
||||
void add_binding_code(const Json::Value& value, int type, std::vector<int>& output) {
|
||||
if (value.isArray()) {
|
||||
for (Json::ArrayIndex i = 0; i < value.size(); ++i) {
|
||||
add_binding_code(value[i], type, output);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
int code = -1;
|
||||
if (value.isNumeric()) {
|
||||
code = value.asInt();
|
||||
} else if (value.isString()) {
|
||||
code = type == 0 ? parse_key_name(value.asString())
|
||||
: parse_mouse_button(value.asString());
|
||||
}
|
||||
if (code >= 0) {
|
||||
output.push_back(code);
|
||||
}
|
||||
}
|
||||
|
||||
void parse_window(const Json::Value& root, WindowConfig& window) {
|
||||
if (!root.isMember("window") || !root["window"].isObject()) {
|
||||
return;
|
||||
}
|
||||
const Json::Value& value = root["window"];
|
||||
if (value.isMember("width")) {
|
||||
window.width = json_int(value["width"], window.width);
|
||||
}
|
||||
if (value.isMember("height")) {
|
||||
window.height = json_int(value["height"], window.height);
|
||||
}
|
||||
if (value.isMember("title")) {
|
||||
window.title = json_string(value["title"], window.title);
|
||||
}
|
||||
}
|
||||
|
||||
void parse_input(const Json::Value& root, std::vector<InputBindingConfig>& bindings) {
|
||||
if (!root.isMember("input") || !root["input"].isArray()) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const Json::Value& item : root["input"]) {
|
||||
if (!item.isObject()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
InputBindingConfig binding;
|
||||
binding.action = item.isMember("action")
|
||||
? json_string(item["action"], "")
|
||||
: json_string(item["name"], "");
|
||||
|
||||
if (item.isMember("key")) {
|
||||
add_binding_code(item["key"], 0, binding.keys);
|
||||
}
|
||||
if (item.isMember("keys")) {
|
||||
add_binding_code(item["keys"], 0, binding.keys);
|
||||
}
|
||||
if (item.isMember("mouse")) {
|
||||
add_binding_code(item["mouse"], 1, binding.mouse_buttons);
|
||||
}
|
||||
if (item.isMember("mouse_buttons")) {
|
||||
add_binding_code(item["mouse_buttons"], 1, binding.mouse_buttons);
|
||||
}
|
||||
|
||||
if (!binding.action.empty()) {
|
||||
bindings.push_back(std::move(binding));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void parse_color(const Json::Value& mesh, MeshConfig& mesh_config) {
|
||||
if (!mesh.isObject() || !mesh.isMember("color")) {
|
||||
return;
|
||||
}
|
||||
|
||||
const Json::Value& color = mesh["color"];
|
||||
if (color.isObject()) {
|
||||
if (color.isMember("r")) mesh_config.color[0] = json_float(color["r"], mesh_config.color[0]);
|
||||
if (color.isMember("g")) mesh_config.color[1] = json_float(color["g"], mesh_config.color[1]);
|
||||
if (color.isMember("b")) mesh_config.color[2] = json_float(color["b"], mesh_config.color[2]);
|
||||
if (color.isMember("a")) mesh_config.color[3] = json_float(color["a"], mesh_config.color[3]);
|
||||
} else if (color.isArray()) {
|
||||
for (Json::ArrayIndex i = 0; i < color.size() && i < 4; ++i) {
|
||||
mesh_config.color[i] = json_float(color[i], mesh_config.color[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ActorConfig parse_actor(const Json::Value& value) {
|
||||
ActorConfig actor;
|
||||
|
||||
if (value.isMember("name")) {
|
||||
actor.name = json_string(value["name"], "");
|
||||
}
|
||||
if (value.isMember("init")) {
|
||||
actor.init_function = json_string(value["init"], "");
|
||||
}
|
||||
if (value.isMember("tick")) {
|
||||
actor.tick_function = json_string(value["tick"], "");
|
||||
}
|
||||
|
||||
if (value.isMember("position") && value["position"].isObject()) {
|
||||
const Json::Value& position = value["position"];
|
||||
actor.position[0] = json_float(position["x"], actor.position[0]);
|
||||
actor.position[1] = json_float(position["y"], actor.position[1]);
|
||||
}
|
||||
|
||||
if (value.isMember("facing") && value["facing"].isObject()) {
|
||||
const Json::Value& facing = value["facing"];
|
||||
actor.facing[0] = json_float(facing["x"], actor.facing[0]);
|
||||
actor.facing[1] = json_float(facing["y"], actor.facing[1]);
|
||||
}
|
||||
|
||||
if (value.isMember("mesh") && value["mesh"].isObject()) {
|
||||
const Json::Value& mesh = value["mesh"];
|
||||
if (mesh.isMember("shape")) {
|
||||
actor.mesh.shape = json_string(mesh["shape"], actor.mesh.shape);
|
||||
}
|
||||
parse_color(mesh, actor.mesh);
|
||||
}
|
||||
|
||||
return actor;
|
||||
}
|
||||
|
||||
void parse_actors(const Json::Value& root, std::vector<ActorConfig>& actors) {
|
||||
const Json::Value* list = nullptr;
|
||||
|
||||
if (root.isMember("actors") && root["actors"].isArray()) {
|
||||
list = &root["actors"];
|
||||
} else if (root.isMember("scene")) {
|
||||
const Json::Value& scene = root["scene"];
|
||||
if (scene.isArray()) {
|
||||
list = &scene;
|
||||
} else if (scene.isObject() && scene.isMember("actors") && scene["actors"].isArray()) {
|
||||
list = &scene["actors"];
|
||||
}
|
||||
}
|
||||
|
||||
if (list == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const Json::Value& item : *list) {
|
||||
if (!item.isObject()) {
|
||||
continue;
|
||||
}
|
||||
ActorConfig actor = parse_actor(item);
|
||||
if (!actor.name.empty()) {
|
||||
actors.push_back(std::move(actor));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
bool ConfigLoader::load(const std::string& path) {
|
||||
Json::Value root;
|
||||
Json::CharReaderBuilder builder;
|
||||
std::ifstream file(path);
|
||||
std::string errors;
|
||||
|
||||
if (!Json::parseFromStream(builder, file, &root, &errors)) {
|
||||
last_error_ = "无法解析 JSON 配置文件: " + path + " - " + errors;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!root.isObject()) {
|
||||
last_error_ = "JSON 根节点必须是对象: " + path;
|
||||
return false;
|
||||
}
|
||||
|
||||
config_ = GameConfig{};
|
||||
parse_window(root, config_.window);
|
||||
parse_input(root, config_.input_bindings);
|
||||
parse_actors(root, config_.actors);
|
||||
return true;
|
||||
}
|
||||
|
||||
const GameConfig& ConfigLoader::config() const {
|
||||
return config_;
|
||||
}
|
||||
|
||||
const std::string& ConfigLoader::last_error() const {
|
||||
return last_error_;
|
||||
}
|
||||
|
||||
} // namespace openceg
|
||||
+117
@@ -0,0 +1,117 @@
|
||||
#include <engine.hpp>
|
||||
|
||||
#include <chrono>
|
||||
#include <cstdio>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
#include <openceg.hpp>
|
||||
|
||||
namespace openceg {
|
||||
|
||||
int run_engine(int argc, char** argv, const char* default_config_path) {
|
||||
std::string config_path = default_config_path != nullptr ? default_config_path : "config.ceg";
|
||||
if (argc > 1) {
|
||||
config_path = argv[1];
|
||||
}
|
||||
|
||||
ConfigLoader loader;
|
||||
if (!loader.load(config_path)) {
|
||||
std::fprintf(stderr, "%s\n", loader.last_error().c_str());
|
||||
return 1;
|
||||
}
|
||||
|
||||
const GameConfig& config = loader.config();
|
||||
Game& game = Game::getInstance();
|
||||
BehaviorRegistry& behaviors = BehaviorRegistry::getInstance();
|
||||
|
||||
std::vector<std::shared_ptr<Actor>> actor_storage;
|
||||
std::vector<Actor*> actors;
|
||||
|
||||
for (const ActorConfig& actor_config : config.actors) {
|
||||
auto mesh = std::make_shared<Mesh>();
|
||||
if (actor_config.mesh.shape == "quad") {
|
||||
*mesh = Mesh::quad();
|
||||
} else {
|
||||
// 当前渲染器只提供 quad;未知形状先按 quad 创建,便于后续扩展。
|
||||
*mesh = Mesh::quad();
|
||||
}
|
||||
mesh->set_color(actor_config.mesh.color);
|
||||
|
||||
auto actor = std::make_shared<Actor>(actor_config.name, game, mesh);
|
||||
actor->set_position(actor_config.position);
|
||||
actor->set_facing(actor_config.facing);
|
||||
|
||||
if (!actor_config.init_function.empty()) {
|
||||
auto handler = behaviors.init(actor_config.init_function);
|
||||
if (handler) {
|
||||
actor->set_init_handler(std::move(handler));
|
||||
} else {
|
||||
std::fprintf(stderr, "未找到 init 处理函数: %s\n",
|
||||
actor_config.init_function.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
if (!actor_config.tick_function.empty()) {
|
||||
auto handler = behaviors.tick(actor_config.tick_function);
|
||||
if (handler) {
|
||||
actor->set_tick_handler(std::move(handler));
|
||||
} else {
|
||||
std::fprintf(stderr, "未找到 tick 处理函数: %s\n",
|
||||
actor_config.tick_function.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
actors.push_back(actor.get());
|
||||
actor_storage.push_back(std::move(actor));
|
||||
}
|
||||
|
||||
for (auto& actor : actor_storage) {
|
||||
actor->initialize();
|
||||
}
|
||||
|
||||
render::RenderLoop render_loop;
|
||||
for (const InputBindingConfig& binding : config.input_bindings) {
|
||||
render_loop.input().add_action(binding.action);
|
||||
for (int key : binding.keys) {
|
||||
render_loop.input().bind_key(binding.action, key);
|
||||
}
|
||||
for (int button : binding.mouse_buttons) {
|
||||
render_loop.input().bind_mouse_button(binding.action, button);
|
||||
}
|
||||
}
|
||||
|
||||
// 引擎级默认退出动作,不包含具体游戏逻辑。
|
||||
render_loop.input().add_action("quit");
|
||||
render_loop.input().bind_key("quit", 256);
|
||||
|
||||
render_loop.set_actors(std::move(actors));
|
||||
render_loop.start(config.window.width, config.window.height,
|
||||
config.window.title.c_str());
|
||||
|
||||
auto last = std::chrono::steady_clock::now();
|
||||
while (!render_loop.should_close()) {
|
||||
render_loop.poll_events();
|
||||
if (render_loop.input().is_action_just_pressed("quit")) {
|
||||
break;
|
||||
}
|
||||
|
||||
const auto now = std::chrono::steady_clock::now();
|
||||
const float dt = std::chrono::duration<float>(now - last).count();
|
||||
last = now;
|
||||
|
||||
for (auto& actor : actor_storage) {
|
||||
actor->process(dt, render_loop.input());
|
||||
}
|
||||
|
||||
render_loop.input().end_frame();
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(2));
|
||||
}
|
||||
|
||||
render_loop.stop();
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace openceg
|
||||
@@ -0,0 +1,5 @@
|
||||
#include <engine.hpp>
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
return openceg::run_engine(argc, argv);
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
#include <game.hpp>
|
||||
|
||||
namespace openceg {
|
||||
|
||||
Game::Game() { id_cnt = 0; }
|
||||
Game::~Game() = default;
|
||||
|
||||
Game& Game::getInstance() {
|
||||
static Game instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
void Game::launch() {}
|
||||
|
||||
size_t Game::new_id() { return id_cnt++; }
|
||||
|
||||
}
|
||||
@@ -0,0 +1,415 @@
|
||||
// Minimal GLFW-compatible Win32 backend for cross-compiling the example.
|
||||
// It implements only the subset used by src/render.cpp.
|
||||
#include <windows.h>
|
||||
#include <windowsx.h>
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
#define GLFW_INCLUDE_VULKAN
|
||||
#define VK_USE_PLATFORM_WIN32_KHR
|
||||
#include <GLFW/glfw3.h>
|
||||
#include <vulkan/vulkan.h>
|
||||
#include <vulkan/vulkan_win32.h>
|
||||
|
||||
struct GLFWwindow {
|
||||
HWND handle = nullptr;
|
||||
void* user_pointer = nullptr;
|
||||
bool should_close = false;
|
||||
int width = 0;
|
||||
int height = 0;
|
||||
|
||||
GLFWkeyfun key_callback = nullptr;
|
||||
GLFWmousebuttonfun mouse_button_callback = nullptr;
|
||||
GLFWcursorposfun cursor_pos_callback = nullptr;
|
||||
GLFWscrollfun scroll_callback = nullptr;
|
||||
|
||||
std::array<unsigned char, 1024> key_state{};
|
||||
std::array<unsigned char, 8> mouse_button_state{};
|
||||
double cursor_x = 0.0;
|
||||
double cursor_y = 0.0;
|
||||
};
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr wchar_t kWindowClassName[] = L"OpenCGE_GLFWStub";
|
||||
HINSTANCE g_instance = nullptr;
|
||||
bool g_initialized = false;
|
||||
bool g_resizable = true;
|
||||
|
||||
int map_key(WPARAM vk) {
|
||||
if (vk >= 'A' && vk <= 'Z') {
|
||||
return GLFW_KEY_A + static_cast<int>(vk - 'A');
|
||||
}
|
||||
if (vk >= '0' && vk <= '9') {
|
||||
return GLFW_KEY_0 + static_cast<int>(vk - '0');
|
||||
}
|
||||
switch (vk) {
|
||||
case VK_ESCAPE:
|
||||
return GLFW_KEY_ESCAPE;
|
||||
case VK_LEFT:
|
||||
return GLFW_KEY_LEFT;
|
||||
case VK_RIGHT:
|
||||
return GLFW_KEY_RIGHT;
|
||||
case VK_UP:
|
||||
return GLFW_KEY_UP;
|
||||
case VK_DOWN:
|
||||
return GLFW_KEY_DOWN;
|
||||
case VK_SPACE:
|
||||
return GLFW_KEY_SPACE;
|
||||
case VK_RETURN:
|
||||
return GLFW_KEY_ENTER;
|
||||
case VK_TAB:
|
||||
return GLFW_KEY_TAB;
|
||||
case VK_BACK:
|
||||
return GLFW_KEY_BACKSPACE;
|
||||
case VK_DELETE:
|
||||
return GLFW_KEY_DELETE;
|
||||
case VK_LSHIFT:
|
||||
return GLFW_KEY_LEFT_SHIFT;
|
||||
case VK_RSHIFT:
|
||||
return GLFW_KEY_RIGHT_SHIFT;
|
||||
case VK_LCONTROL:
|
||||
return GLFW_KEY_LEFT_CONTROL;
|
||||
case VK_RCONTROL:
|
||||
return GLFW_KEY_RIGHT_CONTROL;
|
||||
case VK_LMENU:
|
||||
return GLFW_KEY_LEFT_ALT;
|
||||
case VK_RMENU:
|
||||
return GLFW_KEY_RIGHT_ALT;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return GLFW_KEY_UNKNOWN;
|
||||
}
|
||||
|
||||
int current_mods() {
|
||||
int mods = 0;
|
||||
if (GetKeyState(VK_SHIFT) & 0x8000) {
|
||||
mods |= GLFW_MOD_SHIFT;
|
||||
}
|
||||
if (GetKeyState(VK_CONTROL) & 0x8000) {
|
||||
mods |= GLFW_MOD_CONTROL;
|
||||
}
|
||||
if (GetKeyState(VK_MENU) & 0x8000) {
|
||||
mods |= GLFW_MOD_ALT;
|
||||
}
|
||||
if ((GetKeyState(VK_LWIN) & 0x8000) || (GetKeyState(VK_RWIN) & 0x8000)) {
|
||||
mods |= GLFW_MOD_SUPER;
|
||||
}
|
||||
return mods;
|
||||
}
|
||||
|
||||
void handle_key(GLFWwindow* window, UINT message, WPARAM wparam, LPARAM lparam) {
|
||||
if (window == nullptr || window->key_callback == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
const int key = map_key(wparam);
|
||||
const int scancode = static_cast<int>((lparam >> 16) & 0x1ff);
|
||||
const int action = message == WM_KEYDOWN || message == WM_SYSKEYDOWN
|
||||
? (((lparam & (1U << 30)) != 0) ? GLFW_REPEAT : GLFW_PRESS)
|
||||
: GLFW_RELEASE;
|
||||
|
||||
if (key >= 0 && key < static_cast<int>(window->key_state.size())) {
|
||||
window->key_state[key] = action == GLFW_RELEASE ? GLFW_RELEASE : GLFW_PRESS;
|
||||
}
|
||||
|
||||
window->key_callback(window, key, scancode, action, current_mods());
|
||||
}
|
||||
|
||||
int map_mouse_button(UINT message, WPARAM wparam) {
|
||||
switch (message) {
|
||||
case WM_LBUTTONDOWN:
|
||||
case WM_LBUTTONUP:
|
||||
return GLFW_MOUSE_BUTTON_LEFT;
|
||||
case WM_RBUTTONDOWN:
|
||||
case WM_RBUTTONUP:
|
||||
return GLFW_MOUSE_BUTTON_RIGHT;
|
||||
case WM_MBUTTONDOWN:
|
||||
case WM_MBUTTONUP:
|
||||
return GLFW_MOUSE_BUTTON_MIDDLE;
|
||||
case WM_XBUTTONDOWN:
|
||||
case WM_XBUTTONUP:
|
||||
return GET_XBUTTON_WPARAM(wparam) == XBUTTON1 ? GLFW_MOUSE_BUTTON_4
|
||||
: GLFW_MOUSE_BUTTON_5;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
void handle_mouse_button(GLFWwindow* window, UINT message, WPARAM wparam) {
|
||||
if (window == nullptr || window->mouse_button_callback == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
const int button = map_mouse_button(message, wparam);
|
||||
if (button < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const bool is_down = message == WM_LBUTTONDOWN || message == WM_RBUTTONDOWN ||
|
||||
message == WM_MBUTTONDOWN || message == WM_XBUTTONDOWN;
|
||||
window->mouse_button_state[button] = is_down ? GLFW_PRESS : GLFW_RELEASE;
|
||||
window->mouse_button_callback(window, button, is_down ? GLFW_PRESS : GLFW_RELEASE,
|
||||
current_mods());
|
||||
}
|
||||
|
||||
LRESULT CALLBACK window_proc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) {
|
||||
auto* window = reinterpret_cast<GLFWwindow*>(GetWindowLongPtrW(hwnd, GWLP_USERDATA));
|
||||
|
||||
switch (message) {
|
||||
case WM_CLOSE:
|
||||
if (window != nullptr) {
|
||||
window->should_close = true;
|
||||
}
|
||||
return 0;
|
||||
case WM_KEYDOWN:
|
||||
case WM_SYSKEYDOWN:
|
||||
case WM_KEYUP:
|
||||
case WM_SYSKEYUP:
|
||||
handle_key(window, message, wparam, lparam);
|
||||
return 0;
|
||||
case WM_LBUTTONDOWN:
|
||||
case WM_LBUTTONUP:
|
||||
case WM_RBUTTONDOWN:
|
||||
case WM_RBUTTONUP:
|
||||
case WM_MBUTTONDOWN:
|
||||
case WM_MBUTTONUP:
|
||||
case WM_XBUTTONDOWN:
|
||||
case WM_XBUTTONUP:
|
||||
handle_mouse_button(window, message, wparam);
|
||||
return 0;
|
||||
case WM_MOUSEMOVE:
|
||||
if (window != nullptr) {
|
||||
window->cursor_x = static_cast<double>(GET_X_LPARAM(lparam));
|
||||
window->cursor_y = static_cast<double>(GET_Y_LPARAM(lparam));
|
||||
if (window->cursor_pos_callback != nullptr) {
|
||||
window->cursor_pos_callback(window, window->cursor_x, window->cursor_y);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
case WM_MOUSEWHEEL:
|
||||
if (window != nullptr && window->scroll_callback != nullptr) {
|
||||
const double delta = static_cast<double>(GET_WHEEL_DELTA_WPARAM(wparam)) /
|
||||
static_cast<double>(WHEEL_DELTA);
|
||||
window->scroll_callback(window, 0.0, delta);
|
||||
}
|
||||
return 0;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return DefWindowProcW(hwnd, message, wparam, lparam);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
extern "C" {
|
||||
|
||||
int glfwInit(void) {
|
||||
if (g_initialized) {
|
||||
return GLFW_TRUE;
|
||||
}
|
||||
|
||||
g_instance = GetModuleHandleW(nullptr);
|
||||
|
||||
WNDCLASSEXW wc{};
|
||||
wc.cbSize = sizeof(wc);
|
||||
wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
|
||||
wc.lpfnWndProc = window_proc;
|
||||
wc.hInstance = g_instance;
|
||||
wc.hCursor = LoadCursorW(nullptr, MAKEINTRESOURCEW(32512));
|
||||
wc.lpszClassName = kWindowClassName;
|
||||
if (RegisterClassExW(&wc) == 0) {
|
||||
return GLFW_FALSE;
|
||||
}
|
||||
|
||||
g_initialized = true;
|
||||
return GLFW_TRUE;
|
||||
}
|
||||
|
||||
void glfwTerminate(void) {
|
||||
g_initialized = false;
|
||||
}
|
||||
|
||||
void glfwWindowHint(int hint, int value) {
|
||||
if (hint == GLFW_RESIZABLE) {
|
||||
g_resizable = value != GLFW_FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
GLFWwindow* glfwCreateWindow(int width, int height, const char* title, GLFWmonitor*, GLFWwindow*) {
|
||||
if (!g_initialized) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const int wide_length = MultiByteToWideChar(CP_UTF8, 0, title, -1, nullptr, 0);
|
||||
std::vector<wchar_t> wide_title(wide_length);
|
||||
MultiByteToWideChar(CP_UTF8, 0, title, -1, wide_title.data(), wide_length);
|
||||
|
||||
DWORD style = WS_OVERLAPPEDWINDOW;
|
||||
if (!g_resizable) {
|
||||
style &= ~(WS_THICKFRAME | WS_MAXIMIZEBOX);
|
||||
}
|
||||
|
||||
RECT rect{0, 0, width, height};
|
||||
AdjustWindowRectEx(&rect, style, FALSE, 0);
|
||||
|
||||
HWND hwnd = CreateWindowExW(
|
||||
0, kWindowClassName, wide_title.data(), style, CW_USEDEFAULT, CW_USEDEFAULT,
|
||||
rect.right - rect.left, rect.bottom - rect.top, nullptr, nullptr, g_instance, nullptr);
|
||||
if (hwnd == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto* window = new GLFWwindow();
|
||||
window->handle = hwnd;
|
||||
window->width = width;
|
||||
window->height = height;
|
||||
SetWindowLongPtrW(hwnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(window));
|
||||
|
||||
ShowWindow(hwnd, SW_SHOW);
|
||||
UpdateWindow(hwnd);
|
||||
return window;
|
||||
}
|
||||
|
||||
void glfwDestroyWindow(GLFWwindow* window) {
|
||||
if (window == nullptr) {
|
||||
return;
|
||||
}
|
||||
if (window->handle != nullptr) {
|
||||
DestroyWindow(window->handle);
|
||||
}
|
||||
delete window;
|
||||
}
|
||||
|
||||
void glfwSetWindowUserPointer(GLFWwindow* window, void* pointer) {
|
||||
if (window != nullptr) {
|
||||
window->user_pointer = pointer;
|
||||
}
|
||||
}
|
||||
|
||||
void* glfwGetWindowUserPointer(GLFWwindow* window) {
|
||||
return window != nullptr ? window->user_pointer : nullptr;
|
||||
}
|
||||
|
||||
GLFWkeyfun glfwSetKeyCallback(GLFWwindow* window, GLFWkeyfun callback) {
|
||||
if (window == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
GLFWkeyfun previous = window->key_callback;
|
||||
window->key_callback = callback;
|
||||
return previous;
|
||||
}
|
||||
|
||||
GLFWmousebuttonfun glfwSetMouseButtonCallback(GLFWwindow* window, GLFWmousebuttonfun callback) {
|
||||
if (window == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
GLFWmousebuttonfun previous = window->mouse_button_callback;
|
||||
window->mouse_button_callback = callback;
|
||||
return previous;
|
||||
}
|
||||
|
||||
GLFWcursorposfun glfwSetCursorPosCallback(GLFWwindow* window, GLFWcursorposfun callback) {
|
||||
if (window == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
GLFWcursorposfun previous = window->cursor_pos_callback;
|
||||
window->cursor_pos_callback = callback;
|
||||
return previous;
|
||||
}
|
||||
|
||||
GLFWscrollfun glfwSetScrollCallback(GLFWwindow* window, GLFWscrollfun callback) {
|
||||
if (window == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
GLFWscrollfun previous = window->scroll_callback;
|
||||
window->scroll_callback = callback;
|
||||
return previous;
|
||||
}
|
||||
|
||||
void glfwPollEvents(void) {
|
||||
MSG msg{};
|
||||
while (PeekMessageW(&msg, nullptr, 0, 0, PM_REMOVE)) {
|
||||
TranslateMessage(&msg);
|
||||
DispatchMessageW(&msg);
|
||||
}
|
||||
}
|
||||
|
||||
int glfwWindowShouldClose(GLFWwindow* window) {
|
||||
return window != nullptr && window->should_close ? GLFW_TRUE : GLFW_FALSE;
|
||||
}
|
||||
|
||||
int glfwGetKey(GLFWwindow* window, int key) {
|
||||
if (window == nullptr || key < 0 || key >= static_cast<int>(window->key_state.size())) {
|
||||
return GLFW_RELEASE;
|
||||
}
|
||||
return window->key_state[key] != 0 ? GLFW_PRESS : GLFW_RELEASE;
|
||||
}
|
||||
|
||||
int glfwGetMouseButton(GLFWwindow* window, int button) {
|
||||
if (window == nullptr || button < 0 ||
|
||||
button >= static_cast<int>(window->mouse_button_state.size())) {
|
||||
return GLFW_RELEASE;
|
||||
}
|
||||
return window->mouse_button_state[button] != 0 ? GLFW_PRESS : GLFW_RELEASE;
|
||||
}
|
||||
|
||||
void glfwGetCursorPos(GLFWwindow* window, double* xpos, double* ypos) {
|
||||
if (xpos != nullptr) {
|
||||
*xpos = window != nullptr ? window->cursor_x : 0.0;
|
||||
}
|
||||
if (ypos != nullptr) {
|
||||
*ypos = window != nullptr ? window->cursor_y : 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
void glfwGetFramebufferSize(GLFWwindow* window, int* width, int* height) {
|
||||
if (window == nullptr || window->handle == nullptr) {
|
||||
return;
|
||||
}
|
||||
RECT client{};
|
||||
GetClientRect(window->handle, &client);
|
||||
if (width != nullptr) {
|
||||
*width = client.right - client.left;
|
||||
}
|
||||
if (height != nullptr) {
|
||||
*height = client.bottom - client.top;
|
||||
}
|
||||
}
|
||||
|
||||
const char** glfwGetRequiredInstanceExtensions(uint32_t* count) {
|
||||
static const char* extensions[] = {
|
||||
VK_KHR_SURFACE_EXTENSION_NAME,
|
||||
VK_KHR_WIN32_SURFACE_EXTENSION_NAME,
|
||||
};
|
||||
if (count != nullptr) {
|
||||
*count = static_cast<uint32_t>(sizeof(extensions) / sizeof(extensions[0]));
|
||||
}
|
||||
return extensions;
|
||||
}
|
||||
|
||||
VkResult glfwCreateWindowSurface(VkInstance instance, GLFWwindow* window,
|
||||
const VkAllocationCallbacks* allocator,
|
||||
VkSurfaceKHR* surface) {
|
||||
if (instance == VK_NULL_HANDLE || window == nullptr || surface == nullptr) {
|
||||
return VK_ERROR_INITIALIZATION_FAILED;
|
||||
}
|
||||
|
||||
auto create_surface = reinterpret_cast<PFN_vkCreateWin32SurfaceKHR>(
|
||||
vkGetInstanceProcAddr(instance, "vkCreateWin32SurfaceKHR"));
|
||||
if (create_surface == nullptr) {
|
||||
return VK_ERROR_EXTENSION_NOT_PRESENT;
|
||||
}
|
||||
|
||||
VkWin32SurfaceCreateInfoKHR info{};
|
||||
info.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR;
|
||||
info.hinstance = g_instance;
|
||||
info.hwnd = window->handle;
|
||||
return create_surface(instance, &info, allocator, surface);
|
||||
}
|
||||
|
||||
} // extern "C"
|
||||
@@ -0,0 +1 @@
|
||||
#include <openceg.hpp>
|
||||
+1340
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user