improved movements a bit and also added some infrastructure for UI <3

also removed some deprecated debug functions for map loading since map loading is now fast enough that it doesn't really require terminal loading bars
This commit is contained in:
Safariminer 2025-07-29 15:20:29 -04:00
parent 7d8b306daa
commit d8dc877da6
9 changed files with 332 additions and 220 deletions

3
.gitignore vendored
View File

@ -5,4 +5,5 @@ deps
x64
*/x64
x86
*/x86
*/x86
gameenv/imgui.ini

View File

@ -2,4 +2,10 @@
## focusing on complete id control
## Supported file types
- BSP29 (Quake maps)
- BSP29 (Quake maps)
## Dependencies
- raylib
- boost::asio
- imgui
- rlimgui

View File

@ -0,0 +1,24 @@
<!-- Test window for MPFW IMGUI impl -->
<window>
<title>Test Window</title>
<menubar>
<menu>
<title>File</title>
<item>
<title>Load Test Map</title>
<command mode="1">map "data/maps/testmpfw.bsp"</command>
</item>
<item>
<title>Quit game</title>
<command mode="0">quit</command>
</item>
</menu>
<menu>
<title>E1M1</title>
<item>
<title>Play</title>
<command mode="1">map "data/maps/fullquake/e1m1.bsp"</command>
</item>
</menu>
</menubar>
</window>

View File

@ -2,6 +2,7 @@
#include <iostream>
#include <vector>
#include "MPFW_Quake.h"
#include "MPFW_UI.h"
#include <functional>
#include <map>
@ -18,6 +19,7 @@ namespace MPFW {
struct CommandHandlerResources {
Quake::Maps::MapFile* mapQuake;
OperationMode mode = MPFW;
UI::UIRenderer* ui;
};

95
mpfw/MPFW_UI.cpp Normal file
View File

@ -0,0 +1,95 @@
#include "MPFW_UI.h"
#define NO_FONT_AWESOME
#include <rlImGui.h>
#include <imgui.h>
#include <iostream>
#include <raylib.h>
#include "MPFW_Console.h"
#include <pugixml.hpp>
bool testActive = true;
MPFW::UI::UIRenderer::UIRenderer()
{
rlImGuiSetup(true);
}
void MPFW::UI::UIRenderer::Render()
{
if(rendererIsActive){
rlImGuiBegin();
// wH = windowHandle
for (int wH = 0; wH < windows.size(); wH++) {
ImGui::Begin(windows[wH].windowTitle.c_str(), &windows[wH].active, windows[wH].menuBar.size() > 0 ? ImGuiWindowFlags_MenuBar : 0);
if(windows[wH].menuBar.size() != 0){
if(ImGui::BeginMenuBar()){
// mH = menu handle
for (int mH = 0; mH < windows[wH].menuBar.size(); mH++) {
if(ImGui::BeginMenu(windows[wH].menuBar[mH].text.c_str())){
for (int mIH = 0; mIH < windows[wH].menuBar[mH].children.size(); mIH++){
if (ImGui::MenuItem(windows[wH].menuBar[mH].children[mIH].text.c_str())) {
cmh->Run(std::format("mode {}", windows[wH].menuBar[mH].children[mIH].commandMode), false);
cmh->Run(windows[wH].menuBar[mH].children[mIH].command, false);
}
}
ImGui::EndMenu();
}
}
ImGui::EndMenuBar();
}
}
ImGui::End();
}
rlImGuiEnd();
}
}
MPFW::UI::UIRenderer::~UIRenderer()
{
}
void MPFW::UI::Window::LoadWindow(std::string path)
{
pugi::xml_document doc;
pugi::xml_parse_result result = doc.load_file(path.c_str());
if (result) {
pugi::xml_node root = doc.child("window");
windowTitle = root.child("title").child_value();
pugi::xml_node menubarRoot = root.child("menubar");
if (menubarRoot) {
for (auto const& menu : menubarRoot.children("menu")) {
Menu m;
m.text = menu.child("title").child_value();
for (auto const& menuItem : menu.children("item")) {
Menu mI;
mI.text = menuItem.child("title").child_value();
mI.command = menuItem.child("command").child_value();
if (menuItem.child("command").attribute("mode")) {
mI.commandMode = menuItem.child("command").attribute("mode").as_int();
}
m.children.push_back(mI);
}
menuBar.push_back(m);
}
}
}
else {
throw std::runtime_error("couldn't load " + path);
}
}

View File

@ -1,11 +1,52 @@
#pragma once
#include <iostream>
#include <vector>
namespace MPFW {
namespace Console {
class CommandHandler;
}
namespace UI {
struct WindowElement {
};
struct Menu {
std::string text;
std::string command = ""; int commandMode = 0;
std::vector<Menu> children;
};
class Window {
public:
Window() {}
Window(std::string path) { LoadWindow(path); }
std::string windowTitle;
std::vector<Menu> menuBar;
bool active = false;
void LoadWindow(std::string path);
~Window() { menuBar.clear(); }
};
// main class that does the rendering of the UI y'know
class UIRenderer {
bool cursorState = false; // false = off, true = on
public:
UIRenderer();
bool rendererIsActive = false;
MPFW::Console::CommandHandler* cmh;
std::vector<Window> windows;
void Render();
~UIRenderer();
};

View File

@ -10,7 +10,7 @@
#include <print>
#include <rlgl.h>
#include <thread>
#include "MPFW_UI.h"
// turn quake miptex into rl texture
Texture2D RLMT_QUAKE(MPFW::Quake::Maps::rlMipTex rlmt) {
@ -37,40 +37,6 @@ struct ConsoleSettings {
int height;
};
// this is grossly imprecise because of a lack of direct interfaces
// with the loop, but it's simple and should be performant in a
// thread
//static void __DebugCounter_Quake(MPFW::Quake::Maps::MapFile* mapFile) {
// while (mapFile->ds != MPFW::Quake::Maps::Debug::DONE) {
// switch (mapFile->ds) {
// case MPFW::Quake::Maps::Debug::HEADER:
// std::print("Reading header\n");
// break;
// case MPFW::Quake::Maps::Debug::MODELS:
// std::print("Model {}/{}\n", mapFile->modelsCDBG, mapFile->data.header.models.size / 64);
// break;
// case MPFW::Quake::Maps::Debug::VERTICES:
// std::print("Vertex {}/{}\n", mapFile->verticesCDBG, mapFile->data.header.vertices.size / 12);
// break;
// case MPFW::Quake::Maps::Debug::EDGES:
// std::print("Edge {}/{}\n", mapFile->edgesCDBG, mapFile->data.header.edges.size / 4);
// break;
// case MPFW::Quake::Maps::Debug::TEXINFO:
// std::print("Surface {}/{}\n", mapFile->texInfoCDBG, mapFile->data.header.texinfo.size / 40);
// break;
// case MPFW::Quake::Maps::Debug::LEDGES:
// std::print("Ledge {}/{}\n", mapFile->data.ledges.size(), mapFile->data.header.ledges.size / 2);
// break;
// case MPFW::Quake::Maps::Debug::FACES:
// std::print("Face {}/{}\n", mapFile->data.faces.size(), mapFile->data.header.faces.size / 20);
// break;
// }
//#pragma warning(suppress : 4996)
// _sleep(250);
// }
//}
void Look() {
@ -80,35 +46,57 @@ void Look() {
rotation = Vector3Normalize({ rotation.x, Clamp(rotation.y - GetMouseDelta().y * GetFrameTime(), -0.99, 0.99), rotation.z });
}
void AirAccelerate(bool inAir) {
if (IsKeyDown(KEY_W)) {
velocity += hRotation * GetFrameTime() * accelFactor * (IsKeyDown(KEY_LEFT_SHIFT) ? 4 : 2);
}
if (IsKeyDown(KEY_S)) {
velocity -= hRotation * GetFrameTime() * accelFactor;
}
Vector3 sideRotation = Vector3RotateByAxisAngle(hRotation, { 0,1,0 }, DEG2RAD * 90);
if (IsKeyDown(KEY_A)) {
velocity += sideRotation * GetFrameTime() * accelFactor * (IsKeyDown(KEY_LEFT_SHIFT) ? 4 : 2);
}
if (IsKeyDown(KEY_D)) {
velocity -= sideRotation * GetFrameTime() * accelFactor * (IsKeyDown(KEY_LEFT_SHIFT) ? 4 : 2);
}
if (inAir)velocity.y -= GetFrameTime() * 2.5;
velocity.x *= 80 * GetFrameTime();
velocity.z *= 80 * GetFrameTime();
// because quake can't normalize vectors like a civilized piece of software
struct qNVec {
Vector3 vectorReturn;
float distance;
};
qNVec qNormalize(Vector3 v) {
qNVec retval;
retval.vectorReturn = Vector3Normalize(v);
retval.distance = Vector3Length(v);
return retval;
}
Vector3 wishDir = { 0,0,0 };
double wishSpeed;
void Accelerate() {
if (IsKeyDown(KEY_SPACE)) velocity.y += accelFactor * 0.0025;
AirAccelerate(false);
if (velocity.y < 0) velocity.y = 0;
double addSpeed, accelSpeed, currentSpeed;
currentSpeed = Vector3DotProduct(velocity, wishDir);
addSpeed = wishSpeed - currentSpeed;
if(addSpeed > 0){
accelSpeed = 10 * GetFrameTime() * wishSpeed;
if (accelSpeed > addSpeed) accelSpeed = addSpeed;
velocity = Vector3Add(velocity, wishDir * accelSpeed);
}
}
void AirAccelerate(Vector3 wishVelocity) {
double addSpeed, wishSpeedFunc, accelSpeed, currentSpeed;
qNVec qnv = qNormalize(wishVelocity);
wishSpeedFunc = qnv.distance;
wishVelocity = qnv.vectorReturn;
if (wishSpeedFunc > 30) wishSpeedFunc = 30;
currentSpeed = Vector3DotProduct(velocity, wishVelocity);
addSpeed = wishSpeedFunc - currentSpeed;
if (addSpeed > 0) {
accelSpeed = 10 * wishSpeed * GetFrameTime();
if (accelSpeed > addSpeed) accelSpeed = addSpeed;
velocity = Vector3Add(velocity, wishVelocity * accelSpeed);
}
}
#ifdef _DEBUG
template <typename T>
@ -193,13 +181,15 @@ int main() {
SetConfigFlags(FLAG_WINDOW_RESIZABLE);
InitWindow(1280, 720, TextFormat("mpfw"));
// SetTargetFPS(60);
SetTargetFPS(60);
SetExitKey(0);
DisableCursor();
MPFW::UI::UIRenderer uiRenderer;
MPFW::Console::CommandHandlerResources chr;
chr.mapQuake = &map;
chr.ui = &uiRenderer;
MPFW::Console::CommandHandler cmdH(&chr);
@ -209,7 +199,12 @@ int main() {
cmdH.RunScript("data/cfg/startup.cfg");
uiRenderer.cmh = &cmdH;
MPFW::UI::Window wndw("data/menus/test.wnd");
uiRenderer.windows.push_back(wndw);
// rlSetClipPlanes(1, INFINITY);
camera.position = { 0,5,0 };
@ -222,22 +217,72 @@ int main() {
int indivFace = 0;
bool consoleOn = false;
std::string cmdBuf = "";
int framebuffer = 0;
while (!WindowShouldClose()) {
if (framebuffer < 5) {
DisableCursor();
framebuffer++;
}
if (IsKeyPressed(KEY_APOSTROPHE)) {
consoleOn = !consoleOn;
}
if(!consoleOn){
Look();
if (camera.position.y > 5) AirAccelerate(true);
else Accelerate();
if (camera.position.y < 5) camera.position.y = 5;
Vector3 wishVel = { 0,0,0 };
wishSpeed = 320;
if (IsKeyDown(KEY_W)) {
wishVel += hRotation;
}
if (IsKeyDown(KEY_S)) {
wishVel -= hRotation;
}
if (IsKeyDown(KEY_A)) {
wishVel += Vector3RotateByAxisAngle(hRotation, { 0,1,0 }, 90 * DEG2RAD);
}
if (IsKeyDown(KEY_D)) {
wishVel += Vector3RotateByAxisAngle(hRotation, { 0,1,0 }, -90 * DEG2RAD);
}
wishVel = Vector3Multiply(Vector3Normalize(wishVel), { 320, 320, 320 });
wishDir = Vector3Normalize(wishVel);
if (camera.position.y <= 5) {
camera.position.y = 5;
velocity.y = 0;
if(IsKeyPressed(KEY_SPACE)){
velocity.y += 320*GetFrameTime();
}
Accelerate();
}
else {
AirAccelerate(wishVel);
velocity.y -= 10 * GetFrameTime();
}
if (IsKeyPressed(KEY_LEFT) && indivFace > 0) indivFace--;
if (IsKeyPressed(KEY_RIGHT) && indivFace < map.data.faces.size() - 1) indivFace++;
if (IsKeyPressed(KEY_F)) indivFaceMode = !indivFaceMode;
}
if (IsKeyPressed(KEY_ESCAPE)) {
if (IsCursorHidden()) {
EnableCursor();
uiRenderer.rendererIsActive = true;
}
else {
DisableCursor();
uiRenderer.rendererIsActive = false;
}
}
velocity *= {GetFrameTime(), 1, GetFrameTime()};
camera.position += velocity;
camera.target = camera.position + rotation;
BeginDrawing();
@ -245,177 +290,41 @@ int main() {
DrawRectangleGradientV(0, 0, GetScreenWidth(), GetScreenHeight(), WHITE, LIGHTGRAY);
BeginMode3D(camera);
// DrawGrid(1000, 10);
if (!indivFaceMode) {
for (int i = 0; i < map.data.renderFaces.size(); i++) {
rlColor4ub(255, 255, 255, 255);
rlSetTexture(map.data.renderFaces[i].glTextureId);
DrawGrid(10000, 10);
for (int i = 0; i < map.data.renderFaces.size(); i++) {
rlColor4ub(255, 255, 255, 255);
rlSetTexture(map.data.renderFaces[i].glTextureId);
for (int j = 1; j < map.data.renderFaces[i].vertices.size(); j++) {
rlBegin(RL_QUADS); // for texturing reasons because rlgl
Vector3 a = map.data.renderFaces[i].vertices[0],
b = map.data.renderFaces[i].vertices[j],
c = map.data.renderFaces[i].vertices[j - 1];
for (int j = 1; j < map.data.renderFaces[i].vertices.size(); j++) {
rlBegin(RL_QUADS); // for texturing reasons because rlgl
Vector3 a = map.data.renderFaces[i].vertices[0],
b = map.data.renderFaces[i].vertices[j],
c = map.data.renderFaces[i].vertices[j - 1];
Vector2 at = map.data.renderFaces[i].texCoords[0],
bt = map.data.renderFaces[i].texCoords[j],
ct = map.data.renderFaces[i].texCoords[j - 1];
Vector2 at = map.data.renderFaces[i].texCoords[0],
bt = map.data.renderFaces[i].texCoords[j],
ct = map.data.renderFaces[i].texCoords[j - 1];
rlTexCoord2f(at.x, at.y);
rlVertex3f(a.x, a.y, a.z);
rlTexCoord2f(bt.x, bt.y);
rlVertex3f(b.x, b.y, b.z);
rlTexCoord2f(ct.x, ct.y);
rlVertex3f(c.x, c.y, c.z);
rlVertex3f(c.x, c.y, c.z);
rlTexCoord2f(at.x, at.y);
rlVertex3f(a.x, a.y, a.z);
rlTexCoord2f(bt.x, bt.y);
rlVertex3f(b.x, b.y, b.z);
rlTexCoord2f(ct.x, ct.y);
rlVertex3f(c.x, c.y, c.z);
rlVertex3f(c.x, c.y, c.z);
rlEnd();
}
}
}
else {
int i = indivFace;
std::vector<Vector3> vertices;
rlColor4ub(colors[i].r, colors[i].g, colors[i].b, colors[i].a);
rlBegin(RL_TRIANGLES);
Vector3 a = MPFW::Quake::Maps::qVec2RLVec(map.data.vertices[map.data.edges[abs(map.data.ledges[map.data.faces[i].ledgeId])].vertex0]);
for (int k = 0; k < map.data.faces[i].ledgeNum; k++) {
if (map.data.ledges[map.data.faces[i].ledgeId + k] < 0) {
vertices.push_back(MPFW::Quake::Maps::qVec2RLVec(
map.data.vertices[
map.data.edges[
abs(
map.data.ledges[
map.data.faces[i].ledgeId + k
]
)
].vertex1
]
));
}
else {
vertices.push_back(MPFW::Quake::Maps::qVec2RLVec(
map.data.vertices[
map.data.edges[
abs(
map.data.ledges[
map.data.faces[i].ledgeId + k
]
)
].vertex0
]
));
}
DrawLine3D(MPFW::Quake::Maps::qVec2RLVec(
map.data.vertices[
map.data.edges[
abs(
map.data.ledges[
map.data.faces[i].ledgeId + k
]
)
].vertex0
]
),
MPFW::Quake::Maps::qVec2RLVec(
map.data.vertices[
map.data.edges[
abs(
map.data.ledges[
map.data.faces[i].ledgeId + k
]
)
].vertex1
]
), RED);
rlEnd();
}
rlVertex3f(vertices[0].x, vertices[0].y, vertices[0].z);
rlVertex3f(vertices[2].x, vertices[2].y, vertices[2].z);
rlVertex3f(vertices[1].x, vertices[1].y, vertices[1].z);
rlEnd();
rlColor4f(255, 255, 255, 255);
}
EndMode3D();
DrawFPS(0, 0);
if (indivFaceMode) {
std::vector<Vector3> vertices;
{
int i = indivFace;
rlColor4ub(colors[i].r, colors[i].g, colors[i].b, colors[i].a);
rlBegin(RL_TRIANGLES);
Vector3 a = MPFW::Quake::Maps::qVec2RLVec(map.data.vertices[map.data.edges[abs(map.data.ledges[map.data.faces[i].ledgeId])].vertex0]);
for (int k = 0; k < map.data.faces[i].ledgeNum; k++) {
if (map.data.ledges[map.data.faces[i].ledgeId + k] < 0) {
vertices.push_back(MPFW::Quake::Maps::qVec2RLVec(
map.data.vertices[
map.data.edges[
abs(
map.data.ledges[
map.data.faces[i].ledgeId + k
]
)
].vertex1
]
));
}
else {
vertices.push_back(MPFW::Quake::Maps::qVec2RLVec(
map.data.vertices[
map.data.edges[
abs(
map.data.ledges[
map.data.faces[i].ledgeId + k
]
)
].vertex0
]
));
}
}
}
DrawText(TextFormat("Current face: %i", indivFace), 0, 30, 30, BLACK);
for (int i = 0; i < vertices.size(); i++) {
DrawText(TextFormat("Vertex %i : %f %f %f", i, vertices[i].x, vertices[i].y, vertices[i].z), 0, 60 + 10 * i, 10, BLACK);
}
}
if (consoleOn) {
DrawRectangle(0, 0, GetScreenWidth(), GetScreenHeight() / 2, {0,0,0,200});
@ -440,9 +349,8 @@ int main() {
cmdBuf = "";
}
}
uiRenderer.Render();
EndDrawing();
}

View File

@ -137,11 +137,19 @@
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="..\deps\include\imgui.cpp" />
<ClCompile Include="..\deps\include\imgui_demo.cpp" />
<ClCompile Include="..\deps\include\imgui_draw.cpp" />
<ClCompile Include="..\deps\include\imgui_tables.cpp" />
<ClCompile Include="..\deps\include\imgui_widgets.cpp" />
<ClCompile Include="..\deps\include\pugixml.cpp" />
<ClCompile Include="..\deps\include\rlImGui.cpp" />
<ClCompile Include="main.cpp" />
<ClCompile Include="MPFW_Console.cpp" />
<ClCompile Include="MPFW_CVars.cpp" />
<ClCompile Include="MPFW_HL.cpp" />
<ClCompile Include="MPFW_Quake.cpp" />
<ClCompile Include="MPFW_UI.cpp" />
<ClCompile Include="MPFW_Utils.cpp" />
</ItemGroup>
<ItemGroup>

View File

@ -13,6 +13,9 @@
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
<Filter Include="ext.">
<UniqueIdentifier>{4f8f0e99-b4e5-46ed-8593-5de637bd7032}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="main.cpp">
@ -33,6 +36,30 @@
<ClCompile Include="MPFW_CVars.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\deps\include\pugixml.cpp">
<Filter>ext.</Filter>
</ClCompile>
<ClCompile Include="..\deps\include\imgui.cpp">
<Filter>ext.</Filter>
</ClCompile>
<ClCompile Include="..\deps\include\imgui_demo.cpp">
<Filter>ext.</Filter>
</ClCompile>
<ClCompile Include="..\deps\include\imgui_draw.cpp">
<Filter>ext.</Filter>
</ClCompile>
<ClCompile Include="..\deps\include\imgui_tables.cpp">
<Filter>ext.</Filter>
</ClCompile>
<ClCompile Include="..\deps\include\imgui_widgets.cpp">
<Filter>ext.</Filter>
</ClCompile>
<ClCompile Include="MPFW_UI.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\deps\include\rlImGui.cpp">
<Filter>ext.</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="MPFW_HL.h">