Skip to content

Commit

Permalink
Add 16 bit indices
Browse files Browse the repository at this point in the history
  • Loading branch information
lewa-j committed Mar 23, 2022
1 parent e12edd2 commit ac9f9b4
Show file tree
Hide file tree
Showing 5 changed files with 147 additions and 62 deletions.
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
# hlbsp-converter

A tool to convert bsp maps (Half-Life and other GoldSrc games) into gltf scenes.
A tool to convert bsp maps (Half-Life and other GoldSrc games) into glTF scenes.

## Key features:

* Export embedded texture
* Lightmaps!
* Option to exclude sky polygons
* BSP31 support (Xash3D)

## Usage

Expand All @@ -18,7 +19,8 @@ A tool to convert bsp maps (Half-Life and other GoldSrc games) into gltf scenes.

* `-lm <number>` - set a lightmap atlas size
* `-skip_sky` - exclude polygons with 'sky' texture from export
* `-lstyle <number>|all` - export lightmap with a specified lightstyle index or all lightyles in one
* `-lstyle <number>|all` - export lightmap with a specified lightstyle index or all lightyles in one.
* `-uint16` - sets index buffer type to usigned short. Useful for old mobile GPU without GL_OES_element_index_uint. Will split models into smaller meshes if required.

## Dependencies (already included)

Expand Down
11 changes: 8 additions & 3 deletions src/bsp-converter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@

int main(int argc, const char *argv[])
{
printf("bsp-converter by lewa_j v0.8 - 2022\n");
printf("bsp-converter by lewa_j v0.9 - 2022\n");
if (argc < 2 || !strcmp(argv[1], "-h"))
{
printf("Usage: bsp-converter map.bsp [-lm <lightmap atlas size>] [-skip_sky] [-lstyles <light style index>|all]\n");
printf("Usage: bsp-converter map.bsp [-lm <lightmap atlas size>] [-lstyles <light style index>|all] [-skip_sky] [-uint16]\n");
return -1;
}

Expand Down Expand Up @@ -41,7 +41,7 @@ int main(int argc, const char *argv[])
printf("Warning: '-lm' parameter requires a number - lightmap atlas resolution\n");
}
}
if (!strcmp(argv[i], "-lstyle"))
else if (!strcmp(argv[i], "-lstyle"))
{
if (argc > i + 1)
{
Expand All @@ -61,6 +61,11 @@ int main(int argc, const char *argv[])
config.skipSky = true;
printf("Sky polygons will be excluded from export\n");
}
else if (!strcmp(argv[i], "-uint16"))
{
config.uint16Inds = true;
printf("Set indices type to uint16\n");
}
else
{
printf("Warning: unknown parameter \"%s\"\n", argv[i]);
Expand Down
117 changes: 78 additions & 39 deletions src/gltf_export.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,44 +26,73 @@ bool ExportMap(const std::string &name, hlbsp::Map &map)
int accessorId = 0;
int modelAccessorId = 0;
int bufferViewId = 0;
int meshId = 0;
int nodeId = 1;
const int indsBufferOffset = map.vertices.size() * sizeof(map.vertices[0]);
const int indSize = map.indices16.size() ? sizeof(uint16_t) : sizeof(uint32_t);
const int indType = map.indices16.size() ? UNSIGNED_SHORT : UNSIGNED_INT;
for (int i = 0; i < map.models.size(); i++)
{
nodes[0]["children"].push_back(i + 1);
nodes[i + 1] = { {"mesh", i}, {"name",std::string("*") + std::to_string(i)} };

bufferViews[bufferViewId + 0] = { {"buffer", 0}, {"byteOffset", indsBufferOffset + map.models[i].offset * sizeof(map.indices[0])}, {"byteLength", map.models[i].count * sizeof(map.indices[0])}, {"target", ELEMENT_ARRAY_BUFFER} };
bufferViews[bufferViewId + 1] = { {"buffer", 0}, {"byteOffset", map.models[i].vertOffset * sizeof(map.vertices[0])}, {"byteLength", map.models[i].vertCount * sizeof(map.vertices[0])},{"byteStride", 28}, {"target", ARRAY_BUFFER} };
hlbsp::vec3_t bmin{ FLT_MAX, FLT_MAX, FLT_MAX };
hlbsp::vec3_t bmax{ -FLT_MAX, -FLT_MAX, -FLT_MAX };
for (int j = 0; j < map.models[i].vertCount; j++)
int modelNodeId = nodeId;
nodeId++;
nodes[modelNodeId] = { {"name",std::string("*") + std::to_string(i)} };
nodes[0]["children"].push_back(modelNodeId);

if (map.models[i].size() == 1)
{
hlbsp::vec3_t v = map.vertices[map.models[i].vertOffset + j].pos;
bmin.x = fmin(bmin.x, v.x);
bmin.y = fmin(bmin.y, v.y);
bmin.z = fmin(bmin.z, v.z);
bmax.x = fmax(bmax.x, v.x);
bmax.y = fmax(bmax.y, v.y);
bmax.z = fmax(bmax.z, v.z);
nodes[modelNodeId]["mesh"] = meshId;
}
accessors[accessorId + 0] = { {"bufferView",bufferViewId + 1},{"byteOffset",0},{"componentType",FLOAT},{"count",map.models[i].vertCount},{"type","VEC3"}, {"min",{bmin.x, bmin.y, bmin.z}}, {"max",{bmax.x, bmax.y, bmax.z}} };
accessors[accessorId + 1] = { {"bufferView",bufferViewId + 1},{"byteOffset",12},{"componentType",FLOAT},{"count",map.models[i].vertCount},{"type","VEC2"} };
accessors[accessorId + 2] = { {"bufferView",bufferViewId + 1},{"byteOffset",20},{"componentType",FLOAT},{"count",map.models[i].vertCount},{"type","VEC2"} };
modelAccessorId = accessorId;
accessorId += 3;

meshes[i] = { {"name", name + "_mesh" + std::to_string(i)}, {"primitives",json::array()} };
for (int j = 0; j < map.models[i].submeshes.size(); j++)

for (int mmi = 0; mmi < map.models[i].size(); mmi++)
{
meshes[i]["primitives"][j] = {
{"attributes", {{"POSITION",modelAccessorId + 0}, {"TEXCOORD_0",modelAccessorId + 1}, {"TEXCOORD_1",modelAccessorId + 2}}},
{"indices", accessorId},
{"material", map.models[i].submeshes[j].material}
};
accessors[accessorId] = { {"bufferView", bufferViewId}, { "byteOffset", (map.models[i].submeshes[j].offset - map.models[i].offset) * sizeof(map.indices[0]) }, { "componentType", UNSIGNED_INT }, { "count", map.models[i].submeshes[j].count }, { "type","SCALAR" } };
accessorId++;
meshes[meshId] = { {"primitives",json::array()} };
if (map.models[i].size() != 1)
{
std::string meshName = name + "_mesh" + std::to_string(i) + "_" + std::to_string(mmi);
nodes[nodeId] = { {"name", meshName}, {"mesh", meshId} };
nodes[modelNodeId]["children"].push_back(nodeId);
nodeId++;
meshes[meshId]["name"] = meshName;
}
else
{
meshes[meshId]["name"] = name + "_mesh" + std::to_string(i);
}
const hlbsp::Map::model_t &model = map.models[i][mmi];

bufferViews[bufferViewId + 0] = { {"buffer", 0}, {"byteOffset", indsBufferOffset + model.offset * indSize}, {"byteLength", model.count * indSize}, {"target", ELEMENT_ARRAY_BUFFER} };
bufferViews[bufferViewId + 1] = { {"buffer", 0}, {"byteOffset", model.vertOffset * sizeof(map.vertices[0])}, {"byteLength", model.vertCount * sizeof(map.vertices[0])},{"byteStride", 28}, {"target", ARRAY_BUFFER} };
hlbsp::vec3_t bmin{ FLT_MAX, FLT_MAX, FLT_MAX };
hlbsp::vec3_t bmax{ -FLT_MAX, -FLT_MAX, -FLT_MAX };
for (int j = 0; j < model.vertCount; j++)
{
hlbsp::vec3_t v = map.vertices[model.vertOffset + j].pos;
bmin.x = fmin(bmin.x, v.x);
bmin.y = fmin(bmin.y, v.y);
bmin.z = fmin(bmin.z, v.z);
bmax.x = fmax(bmax.x, v.x);
bmax.y = fmax(bmax.y, v.y);
bmax.z = fmax(bmax.z, v.z);
}
accessors[accessorId + 0] = { {"bufferView",bufferViewId + 1},{"byteOffset",0},{"componentType",FLOAT},{"count",model.vertCount},{"type","VEC3"}, {"min",{bmin.x, bmin.y, bmin.z}}, {"max",{bmax.x, bmax.y, bmax.z}} };
accessors[accessorId + 1] = { {"bufferView",bufferViewId + 1},{"byteOffset",12},{"componentType",FLOAT},{"count",model.vertCount},{"type","VEC2"} };
accessors[accessorId + 2] = { {"bufferView",bufferViewId + 1},{"byteOffset",20},{"componentType",FLOAT},{"count",model.vertCount},{"type","VEC2"} };
modelAccessorId = accessorId;
accessorId += 3;

for (int j = 0; j < model.submeshes.size(); j++)
{
meshes[meshId]["primitives"][j] = {
{"attributes", {{"POSITION",modelAccessorId + 0}, {"TEXCOORD_0",modelAccessorId + 1}, {"TEXCOORD_1",modelAccessorId + 2}}},
{"indices", accessorId},
{"material", model.submeshes[j].material}
};
accessors[accessorId] = { {"bufferView", bufferViewId}, { "byteOffset", (model.submeshes[j].offset - model.offset) * indSize }, { "componentType", indType }, { "count", model.submeshes[j].count }, { "type","SCALAR" } };
accessorId++;
}
meshId++;
bufferViewId += 2;
}
bufferViewId += 2;
}

_mkdir("textures");
Expand Down Expand Up @@ -95,17 +124,27 @@ bool ExportMap(const std::string &name, hlbsp::Map &map)
images[lmapTexIndex] = { {"uri", name + "_lightmap0.png"} };
textures[lmapTexIndex] = { {"source", lmapTexIndex} };

std::string bufferName = name + ".bin";

int vertsLen = map.vertices.size() * sizeof(map.vertices[0]);
int indsLen = map.indices.size() * sizeof(map.indices[0]);
j["buffers"] = { { {"uri", bufferName}, {"byteLength", vertsLen + indsLen} } };
int indsLen = 0;

std::string bufferName = name + ".bin";

printf("Writing: %s\n", bufferName.c_str());
std::ofstream verts(bufferName, std::ios_base::binary);
verts.write((char *)&map.vertices[0], vertsLen);
verts.write((char *)&map.indices[0], indsLen);
verts.close();
std::ofstream bufferFile(bufferName, std::ios_base::binary);
bufferFile.write((char *)&map.vertices[0], vertsLen);
if (map.indices16.size())
{
indsLen = map.indices16.size() * sizeof(map.indices16[0]);
bufferFile.write((char *)&map.indices16[0], indsLen);
}
else
{
indsLen = map.indices32.size() * sizeof(map.indices32[0]);
bufferFile.write((char *)&map.indices32[0], indsLen);
}
bufferFile.close();

j["buffers"] = { { {"uri", bufferName}, {"byteLength", vertsLen + indsLen} } };

printf("Writing: %s.gltf\n", name.c_str());
std::ofstream o(name + ".gltf");
Expand Down
68 changes: 52 additions & 16 deletions src/hlbsp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -110,23 +110,48 @@ bool Map::load(const char *path, const char *name, LoadConfig *config)
materialFaces[ti.miptex].push_back(fi);
}

models[mi].submeshes.resize(materialFaces.size());
models[mi].count = 0;
models[mi].offset = indicesOffset;
models[mi].vertOffset = vertices.size();
models[mi].push_back({});
model_t *model = &models[mi].back();

model->count = 0;
model->offset = indicesOffset;
model->vertOffset = vertices.size();
int indVertOffset = 0;
int submeshId = 0;
for (auto &mat : materialFaces)
{
models[mi].submeshes[submeshId].material = mat.first;
models[mi].submeshes[submeshId].offset = indicesOffset;
models[mi].submeshes[submeshId].count = 0;
submesh_t submesh;
submesh.material = mat.first;
submesh.offset = indicesOffset;
submesh.count = 0;

for (int i = 0; i < mat.second.size(); i++)
{
const auto &f = faces[mat.second[i]];
const auto &ti = texinfos[f.texinfo];

if (config->uint16Inds && (indVertOffset + f.numedges >= UINT16_MAX - 1))
{
model->vertCount = vertices.size() - model->vertOffset;
if (submesh.count)
{
indicesOffset += submesh.count;
model->count += submesh.count;
model->submeshes.push_back(submesh);
}

models[mi].push_back({});
model = &models[mi].back();

model->count = 0;
model->offset = indicesOffset;
model->vertOffset = vertices.size();
indVertOffset = 0;

submesh.material = mat.first;
submesh.offset = indicesOffset;
submesh.count = 0;
}

int faceVertOffset = vertices.size();

float min_uv[2]{ FLT_MAX, FLT_MAX };
Expand Down Expand Up @@ -203,18 +228,29 @@ bool Map::load(const char *path, const char *name, LoadConfig *config)
// turn TRIANGLE_FAN into TRIANGLES
for (int j = 1; j < f.numedges - 1; j++)
{
indices.push_back(indVertOffset);
indices.push_back(indVertOffset + j + 1);
indices.push_back(indVertOffset + j);
if (config->uint16Inds)
{
indices16.push_back(indVertOffset);
indices16.push_back(indVertOffset + j + 1);
indices16.push_back(indVertOffset + j);
}
else
{
indices32.push_back(indVertOffset);
indices32.push_back(indVertOffset + j + 1);
indices32.push_back(indVertOffset + j);
}
}
models[mi].submeshes[submeshId].count += (f.numedges - 2) * 3;
submesh.count += (f.numedges - 2) * 3;
indVertOffset += f.numedges;
}
models[mi].count += models[mi].submeshes[submeshId].count;
indicesOffset += models[mi].submeshes[submeshId].count;
submeshId++;
if (submesh.count)
model->submeshes.push_back(submesh);

model->count += submesh.count;
indicesOffset += submesh.count;
}
models[mi].vertCount = vertices.size() - models[mi].vertOffset;
model->vertCount = vertices.size() - model->vertOffset;
}
lightmap.uploadBlock(name);

Expand Down
7 changes: 5 additions & 2 deletions src/hlbsp.h
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,7 @@ struct LoadConfig
int lightmapSize = 1024;
int lstyle = -1;
bool lstylesAll = false;
bool uint16Inds = false;
};

class Map
Expand Down Expand Up @@ -189,8 +190,10 @@ class Map
};

std::vector<vert_t> vertices;
std::vector<uint32_t> indices;
std::vector<model_t> models;
std::vector<uint16_t> indices16;
std::vector<uint32_t> indices32;
// model can contain multiple meshes
std::vector<std::vector<model_t> > models;

std::vector<dface_t> faces;
std::vector<int> surfedges;
Expand Down

0 comments on commit ac9f9b4

Please # to comment.