453 lines
13 KiB
C++
453 lines
13 KiB
C++
|
||
#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);
|
||
}
|