Preliminary work on lightmaps

This commit is contained in:
Safariminer 2025-08-05 01:42:06 -04:00
parent ecce403966
commit 6b39e39fe1
4 changed files with 146 additions and 20 deletions

View File

View File

@ -307,6 +307,11 @@ void MPFW::Quake::Maps::MapFile::LoadBSPMap(std::string path)
for (int i = 0; i < data.lightMaps.size(); i++) {
rlUnloadTexture(data.lightMaps[i].glTextureID);
}
data.lightMaps.clear();
std::print("Loading mip textures manually for now\n");
@ -368,8 +373,22 @@ void MPFW::Quake::Maps::MapFile::LoadBSPMap(std::string path)
data.renderFaces.clear();
data.lightmap.clear();
data.lightmap.resize(data.header.lightmaps.size);
std::memcpy(data.lightmap.data(), Utils::Strings::IterativeStringExcerpt(buffer, data.header.lightmaps.offset, data.header.lightmaps.size).data(), data.header.lightmaps.size);
std::print("Precalculating faces and texture coordinates\n");
data.planes.clear();
data.planes.resize(data.header.planes.size / sizeof(Plane));
std::memcpy(
data.planes.data(),
Utils::Strings::IterativeStringExcerpt(buffer, data.header.planes.offset, data.header.planes.size).data(),
data.header.planes.size
);
for (int i = 0; i < data.faces.size(); i++) {
CalculatedFace cface;
@ -425,20 +444,87 @@ void MPFW::Quake::Maps::MapFile::LoadBSPMap(std::string path)
}
if (data.textures[data.texInfo[data.faces[i].texinfoId].textureId].name == "clip") cface.clipFace = true;
if (data.textures[data.texInfo[data.faces[i].texinfoId].textureId].name == "trigger") cface.triggerFace = true;
int lmbase = data.faces[i].lightmap;
if (lmbase != -1) {
// int lmw = data.textures[data.texInfo[data.faces[i].texinfoId].textureId].width;
// int lmh = data.textures[data.texInfo[data.faces[i].texinfoId].textureId].height;
std::vector<Vector3> polygon;
for (int v = 0; v < cface.vertices.size(); v++) {
polygon.push_back(RLVec2qVec(cface.vertices[v]));
}
BoundingBox faceBB = GenerateBoundingBoxPolygon(polygon);
int planeType = data.planes[data.faces[i].planeId].type;
int valueForgotten =
planeType == 0 || planeType == 3 ? 0 :
planeType == 1 || planeType == 4 ? 1 :
2;
int lmw, lmh;
switch (valueForgotten) {
case 0: // forgetting x
lmw = static_cast<int>((faceBB.max.y - faceBB.min.y) / 16);
lmh = static_cast<int>((faceBB.max.z - faceBB.min.z) / 16);
break;
case 1: // forgetting y
lmw = static_cast<int>((faceBB.max.x - faceBB.min.x) / 16);
lmh = static_cast<int>((faceBB.max.z - faceBB.min.z) / 16);
break;
case 2: // forgetting z
lmw = static_cast<int>((faceBB.max.x - faceBB.min.x) / 16);
lmh = static_cast<int>((faceBB.max.y - faceBB.min.y) / 16);
break;
default:
throw;
}
Image lightmap = GenImageColor(lmw, lmh, WHITE);
cface.hasLightMap = true;
for (int y = 0; y < lmh; y++) {
for (int x = 0; x < lmw; x++) {
unsigned char lightValue = data.lightmap[y*lmw+x + lmbase];
ImageDrawPixel(&lightmap, x, y, {
lightValue,
lightValue,
lightValue,
255
});
}
}
Texture lightmapTexture = LoadTextureFromImage(lightmap);
rlMipTex lightmapRLMipTex;
lightmapRLMipTex.glTextureID = lightmapTexture.id;
lightmapRLMipTex.width = lightmapTexture.width;
lightmapRLMipTex.height = lightmapTexture.height;
lightmapRLMipTex.type = lightmapTexture.format;
data.lightMaps.push_back(lightmapRLMipTex);
cface.glLightmapTextureId = lightmapTexture.id;
UnloadImage(lightmap);
}
data.renderFaces.push_back(cface);
}
data.planes.clear();
data.planes.resize(data.header.planes.size / sizeof(Plane));
std::memcpy(
data.planes.data(),
Utils::Strings::IterativeStringExcerpt(buffer, data.header.planes.offset, data.header.planes.size).data(),
data.header.planes.size
);
@ -455,6 +541,20 @@ Vector3 MPFW::Quake::Maps::RLVec2qVec(Vector3 q) {
return { q.z * 2, q.x * 2, q.y * 2 };
}
BoundingBox MPFW::Quake::Maps::GenerateBoundingBoxPolygon(std::vector<Vector3> poly)
{
std::vector<Vector3> polyCopy = poly;
std::sort(polyCopy.begin(), polyCopy.end(), [](Vector3 a, Vector3 b) {
return Vector3Distance({ 0,0,0 }, a) < Vector3Distance({ 0,0,0 }, b);
});
BoundingBox retval;
retval.min = polyCopy[0];
retval.max = polyCopy[polyCopy.size() - 1];
return retval;
}
void MPFW::Quake::Maps::Palette::LoadPalette(std::string path)
{
std::ifstream i(path, std::ios::binary);

View File

@ -103,11 +103,12 @@ namespace MPFW {
struct CalculatedFace {
std::vector<Vector3> vertices;
std::vector<Vector2> texCoords;
int glTextureId, glTextureWidth, glTextureHeight;
int glTextureId, glTextureWidth, glTextureHeight, glLightmapTextureId;
bool hasLightMap = false;
bool skyFace = false;
bool triggerFace = false;
bool clipFace = false;
unsigned char baselight;
};
@ -172,7 +173,7 @@ namespace MPFW {
struct MapData {
BSPHeader header;
std::vector<unsigned char> lightmap;
std::vector<Vector3> vertices;
std::vector<Edge> edges;
std::vector<qModel> models;
@ -186,7 +187,7 @@ namespace MPFW {
std::vector<CalculatedFace> renderFaces;
std::vector<rlMipTex> lightMaps; // purely for unloading <3
};
class MapFile {
@ -211,6 +212,8 @@ namespace MPFW {
void LoadBSPMap(std::string path);
~MapFile(){}
};
BoundingBox GenerateBoundingBoxPolygon(std::vector<Vector3> poly);
}
}
}

View File

@ -306,22 +306,41 @@ int main() {
if (chr.cvars["vid_2d_background"] == "true") DrawRectangleGradientV(0, 0, GetScreenWidth(), GetScreenHeight(), WHITE, LIGHTGRAY);
if (chr.cvars["vid_3d_renderer"] == "true") {
bool cullSky = chr.cvars["vid_3d_cull_sky"] == "true";
bool cullClips = chr.cvars["vid_3d_cull_clips"] == "true";
bool cullTriggers = chr.cvars["vid_3d_cull_triggers"] == "true";
// Grab booleans once at the start of rendering instead of continuously grabbing them
// 66->536 FPS on Quake start.bsp
// 500->8000 on test pre-lightmapping
bool cullSky = chr.cvars["vid_3d_cull_sky"] == "true"; // if texture name starts by "sky"
bool cullClips = chr.cvars["vid_3d_cull_clips"] == "true"; // if texture name == "clip"
bool cullTriggers = chr.cvars["vid_3d_cull_triggers"] == "true"; // if texture name == "trigger"
BeginMode3D(camera);
if(chr.cvars["vid_3d_grid"] == "true") DrawGrid(10000, 10);
rlColor4ub(255, 255, 255, 255);
for (int i = 0; i < map.data.renderFaces.size(); i++) {
// verify culling parameters before rendering
if (
!(cullSky && map.data.renderFaces[i].skyFace) &&
!(cullClips && map.data.renderFaces[i].clipFace) &&
!(cullTriggers && map.data.renderFaces[i].triggerFace)
){
rlSetTexture(map.data.renderFaces[i].glTextureId);
rlColor4ub(
255,// - (unsigned char)map.data.renderFaces[i].baselight,
255,// - (unsigned char)map.data.renderFaces[i].baselight,
255,// - (unsigned char)map.data.renderFaces[i].baselight,
255);
if (map.data.renderFaces[i].hasLightMap) rlSetTexture(map.data.renderFaces[i].glLightmapTextureId);
else 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
@ -341,7 +360,7 @@ int main() {
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);
rlVertex3f(c.x, c.y, c.z); // why does RLGL fuck up triangles? is there something i'm missing?
rlEnd();
@ -352,7 +371,10 @@ int main() {
}
EndMode3D();
}
DrawFPS(0, 0);
DrawFPS(0, 0); // draw fps behind console for readability
if (consoleOn) {
DrawRectangle(0, 0, GetScreenWidth(), GetScreenHeight() / 2, {0,0,0,200});
@ -379,6 +401,7 @@ int main() {
}
uiRenderer.Render();
EndDrawing();
}