bspinfo: factor out lightmap atlas generation from .obj writer

This commit is contained in:
Eric Wasylishen 2023-05-20 13:05:22 -06:00
parent ce25e9c3d0
commit 698a492822
2 changed files with 85 additions and 24 deletions

View File

@ -235,8 +235,7 @@ static faceextents_t get_face_extents(const mbsp_t &bsp, const bspxentries_t &bs
(float)nth_bit(reinterpret_cast<const char *>(bspx.at("LMSHIFT").data())[&face - bsp.dfaces.data()])};
}
static void export_obj_and_lightmaps(const mbsp_t &bsp, const bspxentries_t &bspx, bool use_bspx, bool use_decoupled,
fs::path obj_path, fs::path lightmaps_path)
full_atlas_t build_lightmap_atlas(const mbsp_t &bsp, const bspxentries_t &bspx, bool use_bspx, bool use_decoupled)
{
struct face_rect
{
@ -306,7 +305,7 @@ static void export_obj_and_lightmaps(const mbsp_t &bsp, const bspxentries_t &bsp
}
if (!rectangles.size()) {
return;
return {};
}
// sort faces
@ -383,6 +382,8 @@ static void export_obj_and_lightmaps(const mbsp_t &bsp, const bspxentries_t &bsp
full_atlas.height = full_atlas.meta.height = trimmed_height;
full_atlas.pixels.resize(full_atlas.width * full_atlas.height);
full_atlas_t result;
// compile all of the styles that are available
// TODO: LMSTYLE16
for (size_t i = 0; i < INVALID_LIGHTSTYLE_OLD - 1; i++) {
@ -434,27 +435,18 @@ static void export_obj_and_lightmaps(const mbsp_t &bsp, const bspxentries_t &bsp
continue;
}
lightmaps_path.replace_filename(lightmaps_path.stem().string() + "_" + std::to_string(i) + ".png");
std::ofstream strm(lightmaps_path, std::ofstream::out | std::ofstream::binary);
stbi_write_png_to_func(
[](void *context, void *data, int size) {
std::ofstream &strm = *((std::ofstream *)context);
strm.write((const char *)data, size);
},
&strm, full_atlas.width, full_atlas.height, 4, full_atlas.pixels.data(), full_atlas.width * 4);
memset(full_atlas.pixels.data(), 0, sizeof(*full_atlas.pixels.data()) * full_atlas.pixels.size());
// copy out the atlas texture
result.style_to_lightmap_atlas[i] = full_atlas;
logging::print("wrote {}\n", lightmaps_path);
memset(full_atlas.pixels.data(), 0, sizeof(*full_atlas.pixels.data()) * full_atlas.pixels.size());
}
auto ExportObjFace = [&full_atlas](std::ostream &f, const mbsp_t *bsp, const face_rect &face, int &vertcount) {
// export the vertices and uvs
auto ExportLightmapUVs = [&full_atlas, &result](const mbsp_t *bsp, const face_rect &face) {
std::vector<qvec2f> face_lightmap_uvs;
for (int i = 0; i < face.face->numedges; i++) {
const int vertnum = Face_VertexAtIndex(bsp, face.face, i);
const qvec3f normal = bsp->dplanes[face.face->planenum].normal;
const qvec3f &pos = bsp->dvertexes[vertnum];
fmt::print(f, "v {:.9} {:.9} {:.9}\n", pos[0], pos[1], pos[2]);
fmt::print(f, "vn {:.9} {:.9} {:.9}\n", normal[0], normal[1], normal[2]);
auto tc = face.extents.worldToLMCoord(pos);
tc[0] += face.x;
@ -467,29 +459,79 @@ static void export_obj_and_lightmaps(const mbsp_t &bsp, const bspxentries_t &bsp
tc[0] /= full_atlas.width;
tc[1] /= full_atlas.height;
face_lightmap_uvs.push_back(tc);
}
result.facenum_to_lightmap_uvs[Face_GetNum(bsp, face.face)] = std::move(face_lightmap_uvs);
};
for (auto &rect : rectangles) {
ExportLightmapUVs(&bsp, rect);
}
return result;
}
static void export_obj_and_lightmaps(const mbsp_t &bsp, const bspxentries_t &bspx, bool use_bspx, bool use_decoupled,
fs::path obj_path, fs::path lightmaps_path)
{
const auto atlas = build_lightmap_atlas(bsp, bspx, use_bspx, use_decoupled);
if (atlas.facenum_to_lightmap_uvs.empty()) {
return;
}
// write .png's, one per style
for (const auto &[i, full_atlas] : atlas.style_to_lightmap_atlas) {
lightmaps_path.replace_filename(lightmaps_path.stem().string() + "_" + std::to_string(i) + ".png");
std::ofstream strm(lightmaps_path, std::ofstream::out | std::ofstream::binary);
stbi_write_png_to_func(
[](void *context, void *data, int size) {
std::ofstream &strm = *((std::ofstream *)context);
strm.write((const char *)data, size);
},
&strm, full_atlas.width, full_atlas.height, 4, full_atlas.pixels.data(), full_atlas.width * 4);
logging::print("wrote {}\n", lightmaps_path);
}
auto ExportObjFace = [&atlas](std::ostream &f, const mbsp_t *bsp, int face_num, int &vertcount) {
const auto *face = BSP_GetFace(bsp, face_num);
const auto &tcs = atlas.facenum_to_lightmap_uvs.at(face_num);
// export the vertices and uvs
for (int i = 0; i < face->numedges; i++) {
const int vertnum = Face_VertexAtIndex(bsp, face, i);
const qvec3f normal = bsp->dplanes[face->planenum].normal;
const qvec3f &pos = bsp->dvertexes[vertnum];
fmt::print(f, "v {:.9} {:.9} {:.9}\n", pos[0], pos[1], pos[2]);
fmt::print(f, "vn {:.9} {:.9} {:.9}\n", normal[0], normal[1], normal[2]);
qvec2f tc = tcs[i];
tc[1] = 1.0 - tc[1];
fmt::print(f, "vt {:.9} {:.9}\n", tc[0], tc[1]);
}
f << "f";
for (int i = 0; i < face.face->numedges; i++) {
for (int i = 0; i < face->numedges; i++) {
// .obj vertexes start from 1
// .obj faces are CCW, quake is CW, so reverse the order
const int vertindex = vertcount + (face.face->numedges - 1 - i) + 1;
const int vertindex = vertcount + (face->numedges - 1 - i) + 1;
fmt::print(f, " {0}/{0}/{0}", vertindex);
}
f << '\n';
vertcount += face.face->numedges;
vertcount += face->numedges;
};
auto ExportObj = [&ExportObjFace, &rectangles, &obj_path](const mbsp_t *bsp) {
auto ExportObj = [&ExportObjFace, &obj_path](const mbsp_t *bsp) {
std::ofstream objstream(obj_path, std::ofstream::out);
int vertcount = 0;
for (auto &rect : rectangles) {
ExportObjFace(objstream, bsp, rect, vertcount);
for (int i = 0; i < bsp->dfaces.size(); ++i) {
ExportObjFace(objstream, bsp, i, vertcount);
}
};

View File

@ -20,8 +20,27 @@
#pragma once
#include "common/fs.hh"
#include "common/bsputils.hh"
#include "common/imglib.hh"
#include "common/qvec.hh"
#include <map>
#include <vector>
struct bspdata_t;
struct mbsp_t;
struct mface_t;
struct full_atlas_t
{
/**
* these are normalized to 0..1
*/
std::map<int, std::vector<qvec2f>> facenum_to_lightmap_uvs;
std::map<int, img::texture> style_to_lightmap_atlas;
};
full_atlas_t build_lightmap_atlas(const mbsp_t &bsp, const bspxentries_t &bspx, bool use_bspx, bool use_decoupled);
void serialize_bsp(const bspdata_t &bspdata, const mbsp_t &bsp, const fs::path &name);