451 lines
12 KiB
C++
451 lines
12 KiB
C++
#include <iostream>
|
|
#include <raylib.h>
|
|
|
|
#include <raymath.h>
|
|
#include <fstream>
|
|
#include <sstream>
|
|
#include <string>
|
|
#include "MPFW_Quake.h"
|
|
#include "MPFW_Console.h"
|
|
#include <print>
|
|
#include <rlgl.h>
|
|
#include <thread>
|
|
|
|
|
|
// turn quake miptex into rl texture
|
|
Texture2D RLMT_QUAKE(MPFW::Quake::Maps::rlMipTex rlmt) {
|
|
Texture2D retval;
|
|
|
|
|
|
retval.id = rlmt.glTextureID;
|
|
retval.width = rlmt.width;
|
|
retval.height = rlmt.height;
|
|
retval.format = rlmt.type;
|
|
|
|
return retval;
|
|
}
|
|
|
|
|
|
Camera camera = { 0 };
|
|
|
|
Vector3 rotation = { 1,0,0 };
|
|
Vector3 hRotation = { 1,0,0 };
|
|
Vector3 velocity = { 0,0,0 };
|
|
float accelFactor = 300.0f;
|
|
|
|
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() {
|
|
|
|
hRotation = Vector3RotateByAxisAngle(hRotation, { 0,1,0 }, -GetMouseDelta().x * GetFrameTime());
|
|
rotation = Vector3RotateByAxisAngle(rotation, { 0,1,0 }, -GetMouseDelta().x * GetFrameTime());
|
|
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();
|
|
}
|
|
|
|
void Accelerate() {
|
|
if (IsKeyDown(KEY_SPACE)) velocity.y += accelFactor * 0.0025;
|
|
AirAccelerate(false);
|
|
if (velocity.y < 0) velocity.y = 0;
|
|
}
|
|
|
|
|
|
|
|
#ifdef _DEBUG
|
|
|
|
template <typename T>
|
|
void ___test___(T a, T b) {
|
|
if (a != b) throw;
|
|
}
|
|
|
|
#endif
|
|
|
|
int main() {
|
|
|
|
|
|
#ifdef _DEBUG
|
|
|
|
std::print("COMPILED IN DEBUG MODE\n----------------------\n\n");
|
|
|
|
std::print("Unit testing\n\n\n");
|
|
|
|
{ // UT01 : formatting a quake map directory entry
|
|
std::print("UT01 : formatting a quake map directory entry\n");
|
|
MPFW::Quake::Maps::DirectoryEntry dirent;
|
|
|
|
dirent.offset = 1000;
|
|
dirent.size = 2000;
|
|
|
|
std::string retval = std::format("{}", dirent);
|
|
std::print("{}", retval);
|
|
___test___(retval, std::string("([MPFW::Quake::Maps::DirectoryEntry]: at pos 1000, 2000B in size)"));
|
|
|
|
std::println();
|
|
std::print("UT01 Success\n");
|
|
|
|
}
|
|
|
|
std::print("\nEnd of unit testing\n\n\n");
|
|
#endif
|
|
|
|
MPFW::Quake::Maps::MapFile map;
|
|
// std::thread t(__DebugCounter_Quake, &map);
|
|
|
|
// t.detach();
|
|
|
|
std::vector<Color> colors;
|
|
|
|
for (int i = 0; i < 3000000; i++) {
|
|
colors.push_back({ (unsigned char)GetRandomValue(0, 255), (unsigned char)GetRandomValue(0, 255), (unsigned char)GetRandomValue(0, 255), 255 });
|
|
}
|
|
|
|
std::print("MAP DATA:\n---------\n\n");
|
|
std::print("Version: {}\n", map.data.header.version);
|
|
std::print("\n---\nDIRENTS\n-------\n");
|
|
std::print("Entities: {}\n", map.data.header.entities);
|
|
std::print("Planes: {}\n", map.data.header.planes);
|
|
std::print("Wall Textures (miptex): {}\n", map.data.header.miptex);
|
|
std::print("Map Vertices: {}\n", map.data.header.vertices);
|
|
std::print("Leaves Visibility Lists: {}\n", map.data.header.visilist);
|
|
std::print("BSP Nodes: {}\n", map.data.header.nodes);
|
|
std::print("Texture Info for Faces: {}\n", map.data.header.texinfo);
|
|
std::print("Faces of each surface: {}\n", map.data.header.faces);
|
|
std::print("Wall Lightmaps: {}\n", map.data.header.lightmaps);
|
|
std::print("Clip Nodes: {}\n", map.data.header.clipnodes);
|
|
std::print("BSP Leaves: {}\n", map.data.header.leaves);
|
|
std::print("List of Faces: {}\n", map.data.header.lface);
|
|
std::print("Edges of Faces: {}\n", map.data.header.edges);
|
|
std::print("List of Edges: {}\n", map.data.header.ledges);
|
|
std::print("Models: {}\n", map.data.header.models);
|
|
|
|
std::print("---\n\n");
|
|
|
|
std::print("Vertex count: {} (on {} in header)\n", map.data.vertices.size(), map.data.header.vertices.size / sizeof(Vector3));
|
|
std::print("Edge count: {} (on {} in header)\n", map.data.edges.size(), map.data.header.edges.size / 4);
|
|
|
|
std::print("Model count: {} (on {} in header)\n", map.data.models.size(), map.data.header.models.size / sizeof(MPFW::Quake::Maps::qModel));
|
|
for (int i = 0; i < map.data.models.size(); i++) {
|
|
if (map.data.models[i].node_id3 != 0) {
|
|
std::print("node 3 on {} isn't 0 ({})\n", i, map.data.models[i].node_id3);
|
|
}
|
|
}
|
|
|
|
map.pal = new MPFW::Quake::Maps::Palette("data/palette.lmp");
|
|
|
|
SetConfigFlags(FLAG_WINDOW_RESIZABLE);
|
|
|
|
InitWindow(1280, 720, TextFormat("mpfw"));
|
|
// SetTargetFPS(60);
|
|
SetExitKey(0);
|
|
DisableCursor();
|
|
|
|
|
|
MPFW::Console::CommandHandlerResources chr;
|
|
chr.mapQuake = ↦
|
|
|
|
MPFW::Console::CommandHandler cmdH(&chr);
|
|
|
|
Font robotoMonoRegular = LoadFontEx("data/fonts/RobotoMono/RobotoMono-Regular.ttf", 30, NULL, 6000);
|
|
int robotoMonoHeight = MeasureTextEx(robotoMonoRegular, "X", 30, 0).y;
|
|
|
|
|
|
cmdH.RunScript("data/cfg/startup.cfg");
|
|
|
|
|
|
|
|
// rlSetClipPlanes(1, INFINITY);
|
|
camera.position = { 0,5,0 };
|
|
camera.target = { 1,0,0 };
|
|
camera.fovy = 120;
|
|
camera.up = { 0,1,0 };
|
|
camera.projection = CAMERA_PERSPECTIVE;
|
|
|
|
bool indivFaceMode = false;
|
|
int indivFace = 0;
|
|
bool consoleOn = false;
|
|
std::string cmdBuf = "";
|
|
while (!WindowShouldClose()) {
|
|
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;
|
|
|
|
if (IsKeyPressed(KEY_LEFT) && indivFace > 0) indivFace--;
|
|
if (IsKeyPressed(KEY_RIGHT) && indivFace < map.data.faces.size() - 1) indivFace++;
|
|
if (IsKeyPressed(KEY_F)) indivFaceMode = !indivFaceMode;
|
|
|
|
}
|
|
|
|
camera.position += velocity;
|
|
camera.target = camera.position + rotation;
|
|
BeginDrawing();
|
|
ClearBackground(BLACK);
|
|
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);
|
|
|
|
|
|
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];
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
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});
|
|
DrawTextEx(robotoMonoRegular, cmdH.logStr.c_str(), { 0, GetScreenHeight() / 2 - MeasureTextEx(robotoMonoRegular, cmdH.logStr.c_str(), 20, 0).y }, 20, 0, WHITE);
|
|
|
|
DrawRectangle(0, GetScreenHeight() / 2, GetScreenWidth(), 20, { 0,0,0,200 });
|
|
DrawLine(0, GetScreenHeight() / 2, GetScreenWidth(), GetScreenHeight() / 2, GOLD);
|
|
DrawTextEx(robotoMonoRegular, cmdBuf.c_str(), { 0, (float)GetScreenHeight() / 2 }, 20, 0, GRAY);
|
|
|
|
int key = GetCharPressed();
|
|
while (key > 0) {
|
|
if ((key >= 32) && (key <= 126)) {
|
|
cmdBuf += (char)key;
|
|
|
|
}
|
|
key = GetCharPressed();
|
|
}
|
|
if (IsKeyPressed(KEY_BACKSPACE) && cmdBuf != "") cmdBuf.pop_back();
|
|
|
|
if (IsKeyPressed(KEY_ENTER)) {
|
|
cmdH.Run(cmdBuf);
|
|
cmdBuf = "";
|
|
}
|
|
|
|
|
|
|
|
}
|
|
EndDrawing();
|
|
}
|
|
|
|
CloseWindow();
|
|
}
|