mpfw/mpfw/MPFW_Quake.cpp

453 lines
13 KiB
C++
Raw Blame History

#include <raylib.h>
#include "MPFW_Quake.h"
#include "MPFW_Utils.h"
#include <sstream>
#include <fstream>
#include <string>
#include <iostream>
#include <algorithm>
#include <rlgl.h>
void MPFW::Quake::Maps::MapFile::LoadBSPMap(std::string path)
{
std::ifstream fileHandle(path, std::ios::binary); // open the file as bin
std::string buffer;
ds = Debug::NONE;
if (fileHandle.is_open() && fileHandle.good()) {
std::stringstream sstr; // get out of working with streams
sstr << fileHandle.rdbuf(); // as fast as possible
buffer = sstr.str(); // 3 LINES [WR SPEEDRUN]
// operating on files as a string or a char array is simpler
// than writing some fancy << fuckery (even though it is nice
// to see some << fuckery work without any moving parts)
}
else {
fileHandle.close();
std::print("Bad file handle.\n");
ds = Debug::DONE;
throw std::runtime_error("Bad file handle in Quake BSP parsing.\n");
}
fileHandle.close(); // close it as soon as we can get file loaded
// as to not disturb other programs
// we can now start the parsing
// hehehe >:3
// if file is smaller than header
if (buffer.size() < 124) {
std::print("Bad file content size.\n");
throw std::runtime_error("File is smaller than header. Can't parse.");
}
ds = Debug::HEADER;
data.header.version = 0; // initialize version for addition
data.header.version += buffer[0];
data.header.version += buffer[1] << 8;
data.header.version += buffer[2] << 16;
data.header.version += buffer[3] << 24;
std::vector<DirectoryEntry> vecdir;
for (int i = 4; i < 123; i += 8) {
DirectoryEntry nd{0,0}; // new directory;
nd.offset = Utils::Strings::StringIndexToInteger_4b_le(buffer, i);
nd.size = Utils::Strings::StringIndexToInteger_4b_le(buffer, i + 4);
vecdir.push_back(nd);
}
// redistribution of entries
data.header.entities = vecdir[0];
data.header.planes = vecdir[1];
data.header.miptex = vecdir[2];
data.header.vertices = vecdir[3];
data.header.visilist = vecdir[4];
data.header.nodes = vecdir[5];
data.header.texinfo = vecdir[6];
data.header.faces = vecdir[7];
data.header.lightmaps = vecdir[8];
data.header.clipnodes = vecdir[9];
data.header.leaves = vecdir[10];
data.header.lface = vecdir[11];
data.header.edges = vecdir[12];
data.header.ledges = vecdir[13];
data.header.models = vecdir[14];
data.models.clear();
data.edges.clear();
data.faces.clear();
data.ledges.clear();
data.texInfo.clear();
data.vertices.clear();
// Loading Models
// we're loading models first because that's
// what the spec does <3
ds = Debug::MODELS;
#ifdef MPFW_QUAKE_SLOW_LOADING
data.models.resize(data.header.models.size / 64);
modelsCDBG = 0;
for (int i = 0; i < data.header.models.size / 64; i++) {
int base = data.header.models.offset + 64 * i;
qModel qm;
qm.bound.min.x = Utils::Strings::StringIndexToFloat_4b_le(buffer, base);
qm.bound.min.y = Utils::Strings::StringIndexToFloat_4b_le(buffer, base + 4);
qm.bound.min.z = Utils::Strings::StringIndexToFloat_4b_le(buffer, base + 8);
qm.bound.max.x = Utils::Strings::StringIndexToFloat_4b_le(buffer, base + 12);
qm.bound.max.y = Utils::Strings::StringIndexToFloat_4b_le(buffer, base + 16);
qm.bound.max.z = Utils::Strings::StringIndexToFloat_4b_le(buffer, base + 20);
qm.origin.x = Utils::Strings::StringIndexToFloat_4b_le(buffer, base + 24);
qm.origin.y = Utils::Strings::StringIndexToFloat_4b_le(buffer, base + 28);
qm.origin.z = Utils::Strings::StringIndexToFloat_4b_le(buffer, base + 32);
qm.node_id0 = Utils::Strings::StringIndexToInteger_4b_le(buffer, base + 36);
qm.node_id1 = Utils::Strings::StringIndexToInteger_4b_le(buffer, base + 40);
qm.node_id2 = Utils::Strings::StringIndexToInteger_4b_le(buffer, base + 44);
qm.node_id3 = Utils::Strings::StringIndexToInteger_4b_le(buffer, base + 48);
qm.numleafs = Utils::Strings::StringIndexToInteger_4b_le(buffer, base + 52);
qm.face_id = Utils::Strings::StringIndexToInteger_4b_le(buffer, base + 56);
qm.face_num = Utils::Strings::StringIndexToInteger_4b_le(buffer, base + 60);
data.models[i] = qm;
modelsCDBG++;
// std::print("Model {}/{}\n", i, data.header.models.size / 64);
}
#else
std::string modelsStr = Utils::Strings::IterativeStringExcerpt(buffer, data.header.models.offset, data.header.models.size);
data.models.resize(data.header.models.size / 64);
std::memcpy(data.models.data(), modelsStr.data(), data.header.models.size);
#endif
// parsing vertices
ds = Debug::VERTICES;
#ifdef MPFW_QUAKE_SLOW_LOADING
verticesCDBG = 0;
data.vertices.resize(data.header.vertices.size / 12);
for (int i = 0; i < data.header.vertices.size / 12; i++) {
int base = data.header.vertices.offset + (12 * i);
Vector3 v;
v.x = Utils::Strings::StringIndexToFloat_4b_le(buffer, base);
v.y = Utils::Strings::StringIndexToFloat_4b_le(buffer, base + 4);
v.z = Utils::Strings::StringIndexToFloat_4b_le(buffer, base + 8);
data.vertices[i] = v;
// std::print("Vertex {}/{}\n", i, data.header.vertices.size / 12);
verticesCDBG++;
}
#else
std::string verticesStr = Utils::Strings::IterativeStringExcerpt(buffer, data.header.vertices.offset, data.header.vertices.size);
data.vertices.resize(data.header.vertices.size / 12);
std::memcpy(data.vertices.data(), verticesStr.data(), data.header.vertices.size);
#endif
// parsing edges
ds = Debug::EDGES;
#ifdef MPFW_QUAKE_SLOW_LOADING
edgesCDBG = 0;
data.edges.resize(data.header.edges.size / 4);
for (int i = 0; i < data.header.edges.size / 4; i++) {
int base = data.header.edges.offset + (4 * i);
Edge e;
e.vertex0 = Utils::Strings::StringIndexToInteger_2b_le(buffer, base);
e.vertex1 = Utils::Strings::StringIndexToInteger_2b_le(buffer, base+2);
data.edges[i] = e;
edgesCDBG++;
}
#else
std::string edgeStr = Utils::Strings::IterativeStringExcerpt(buffer, data.header.edges.offset, data.header.edges.size);
data.edges.resize(data.header.edges.size / 4);
std::memcpy(data.edges.data(), edgeStr.data(), data.header.edges.size);
#endif
ds = Debug::TEXINFO;
#ifdef MPFW_QUAKE_SLOW_LOADING
texInfoCDBG = 0;
data.texInfo.resize(data.header.texinfo.size / 40);
for (int i = 0; i < data.header.texinfo.size / 40; i++) {
int base = data.header.texinfo.offset + (40 * i);
Surface s;
s.vectorS.x = Utils::Strings::StringIndexToFloat_4b_le(buffer, base);
s.vectorS.y = Utils::Strings::StringIndexToFloat_4b_le(buffer, base + 4);
s.vectorS.z = Utils::Strings::StringIndexToFloat_4b_le(buffer, base + 8);
s.distS = Utils::Strings::StringIndexToFloat_4b_le(buffer, base + 12);
s.vectorT.x = Utils::Strings::StringIndexToFloat_4b_le(buffer, base + 16);
s.vectorT.y = Utils::Strings::StringIndexToFloat_4b_le(buffer, base + 20);
s.vectorT.z = Utils::Strings::StringIndexToFloat_4b_le(buffer, base + 24);
s.distT = Utils::Strings::StringIndexToFloat_4b_le(buffer, base + 28);
s.textureId = Utils::Strings::StringIndexToInteger_4b_le(buffer, base + 32);
s.animated = Utils::Strings::StringIndexToInteger_4b_le(buffer, base + 36);
data.texInfo[i] = s;
texInfoCDBG++;
}
#else
std::string texInfoStr = Utils::Strings::IterativeStringExcerpt(buffer, data.header.texinfo.offset, data.header.texinfo.size);
data.texInfo.resize(data.header.texinfo.size / 40);
std::memcpy(data.texInfo.data(), texInfoStr.data(), data.header.texinfo.size);
#endif
ds = Debug::LEDGES;
#ifdef MPFW_QUAKE_SLOW_LOADING
for (int i = 0; i < data.header.ledges.size / 2; i++) {
int base = data.header.ledges.offset + (2 * i);
data.ledges.push_back(Utils::Strings::StringIndexToInteger_2b_le(buffer, base));
}
#else
std::string ledgesStr = Utils::Strings::IterativeStringExcerpt(buffer, data.header.ledges.offset, data.header.ledges.size);
data.ledges.resize(data.header.ledges.size / 4);
std::memcpy(data.ledges.data(), ledgesStr.data(), data.header.ledges.size);
#endif
// parsing faces
ds = Debug::FACES;
#ifdef MPFW_QUAKE_SLOW_LOADING
for (int i = 0; i < data.header.faces.size / 20; i++) {
int base = data.header.faces.offset + (20 * i);
Face f;
f.planeId = Utils::Strings::StringIndexToInteger_2b_le(buffer, base);
f.side = Utils::Strings::StringIndexToInteger_2b_le(buffer, base + 2);
f.ledgeId = (signed long)(Utils::Strings::StringIndexToInteger_4b_le(buffer, base + 4));
f.ledgeNum = Utils::Strings::StringIndexToInteger_2b_le(buffer, base + 8);
f.texinfoId = Utils::Strings::StringIndexToInteger_2b_le(buffer, base + 10);
f.typelight = buffer[base + 12];
f.baselight = buffer[base + 13];
f.light[0] = buffer[base + 14];
f.light[1] = buffer[base + 15];
f.lightmap = Utils::Strings::StringIndexToInteger_4b_le(buffer, base + 16);
data.faces.push_back(f);
}
#else
std::string facesStr = Utils::Strings::IterativeStringExcerpt(
buffer,
data.header.faces.offset,
data.header.faces.size
);
data.faces.resize(data.header.faces.size / 20);
std::memcpy(data.faces.data(), facesStr.data(), data.header.faces.size);
#endif
std::print("Loading mip textures manually for now\n");
for (int i = 0; i < data.textures.size(); i++) {
rlUnloadTexture(data.textures[i].glTextureID);
}
cMipHeader cmh;
cmh.numtex = Utils::Strings::StringIndexToInteger_4b_le(buffer, data.header.miptex.offset);
std::vector<long> texOffsets;
// std::memcpy(texOffsets.data(), Utils::Strings::IterativeStringExcerpt(buffer, data.header.miptex.offset + 4, data.header.miptex.size - 4).data(), data.header.miptex.size - 4);
for (int i = 0; i < cmh.numtex; i++) {
texOffsets.push_back(Utils::Strings::StringIndexToInteger_4b_le(buffer, data.header.miptex.offset + 4 + 4*i));
}
for (int i = 0; i < cmh.numtex; i++) {
unsigned int base = static_cast<unsigned int>(texOffsets[i]) + (unsigned)data.header.miptex.offset;
// this shit is <20> undocumented! <20>
// fuck the unofficial quake specs
if (texOffsets[i] == -1) {
// is a texoffset of 0xFFFFFFFF a reference to the fact that
// it starts right after the mipheader? probably not, but it
// makes the texture loading code happy so... :shrug:
base = (unsigned)data.header.miptex.offset + 4 + texOffsets.size() * 4;
}
cMiptex t;
std::memcpy(&t, Utils::Strings::IterativeStringExcerpt(buffer, base, 40).data(), 40);
Image img = GenImageColor(t.width, t.height, BLACK);
for (int y = 0; y < t.height; y++) {
for (int x = 0; x < t.width; x++) {
ImageDrawPixel(&img, x, y,
{
pal->data[(unsigned char)buffer[base + t.offset1 + (t.width * y + x)]].r,
pal->data[(unsigned char)buffer[base + t.offset1 + (t.width * y + x)]].g,
pal->data[(unsigned char)buffer[base + t.offset1 + (t.width * y + x)]].b,
255
}
);
}
}
Texture2D temp = LoadTextureFromImage(img);
rlMipTex mt;
mt.glTextureID = temp.id;
mt.height = temp.height;
mt.width = temp.width;
mt.type = temp.format;
UnloadImage(img);
data.textures.push_back(mt);
}
data.renderFaces.clear();
std::print("Precalculating faces and texture coordinates\n");
for (int i = 0; i < data.faces.size(); i++) {
CalculatedFace cface;
for (int k = 0; k < data.faces[i].ledgeNum; k++) {
if (data.ledges[(unsigned int)(data.faces[i].ledgeId) + k] < 0) {
cface.vertices.push_back(MPFW::Quake::Maps::qVec2RLVec(
data.vertices[
data.edges[
abs(
data.ledges[
data.faces[i].ledgeId + k
]
)
].vertex0
]
));
}
else {
cface.vertices.push_back(MPFW::Quake::Maps::qVec2RLVec(
data.vertices[
data.edges[
abs(
data.ledges[
data.faces[i].ledgeId + k
]
)
].vertex1
]
));
}
}
cface.glTextureId = data.textures[data.texInfo[data.faces[i].texinfoId].textureId].glTextureID;
cface.glTextureWidth = data.textures[data.texInfo[data.faces[i].texinfoId].textureId].width;
cface.glTextureHeight = data.textures[data.texInfo[data.faces[i].texinfoId].textureId].height;
for (int j = 0; j < cface.vertices.size(); j++) {
cface.texCoords.push_back(Vector2(
(Vector3DotProduct(MPFW::Quake::Maps::RLVec2qVec(cface.vertices[j]), data.texInfo[data.faces[i].texinfoId].vectorS) + data.texInfo[data.faces[i].texinfoId].distS) / data.textures[data.texInfo[data.faces[i].texinfoId].textureId].width,
(Vector3DotProduct(MPFW::Quake::Maps::RLVec2qVec(cface.vertices[j]), data.texInfo[data.faces[i].texinfoId].vectorT) + data.texInfo[data.faces[i].texinfoId].distT) / data.textures[data.texInfo[data.faces[i].texinfoId].textureId].height
));
}
data.renderFaces.push_back(cface);
}
ds = Debug::DONE;
}
Vector3 MPFW::Quake::Maps::qVec2RLVec(Vector3 q) {
return { q.y / 2, q.z / 2, q.x / 2 };
}
Vector3 MPFW::Quake::Maps::RLVec2qVec(Vector3 q) {
return { q.z * 2, q.x * 2, q.y * 2 };
}
void MPFW::Quake::Maps::Palette::LoadPalette(std::string path)
{
std::ifstream i(path, std::ios::binary);
if (i.is_open() && i.good()) {
data.clear();
data.resize(256);
std::stringstream s;
s << i.rdbuf();
std::string d = s.str();
std::memcpy(data.data(), d.data(), 256 * 3);
}
else throw std::runtime_error("can't open palette file " + path);
}