bsputil: decompiler wip
This commit is contained in:
parent
ba2ffaa6b9
commit
d4aa42f0ab
|
|
@ -1,4 +1,6 @@
|
||||||
bin/
|
bin/
|
||||||
|
cmakebuild/
|
||||||
|
cmake-*/
|
||||||
build*/
|
build*/
|
||||||
CMakeFiles/
|
CMakeFiles/
|
||||||
*.xcodeproj
|
*.xcodeproj
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,8 @@ project (bsputil CXX)
|
||||||
|
|
||||||
set(BSPUTIL_SOURCES
|
set(BSPUTIL_SOURCES
|
||||||
bsputil.cc
|
bsputil.cc
|
||||||
|
decompile.h
|
||||||
|
decompile.cpp
|
||||||
${CMAKE_SOURCE_DIR}/common/cmdlib.cc
|
${CMAKE_SOURCE_DIR}/common/cmdlib.cc
|
||||||
${CMAKE_SOURCE_DIR}/common/bspfile.cc
|
${CMAKE_SOURCE_DIR}/common/bspfile.cc
|
||||||
${CMAKE_SOURCE_DIR}/common/bsputils.cc
|
${CMAKE_SOURCE_DIR}/common/bsputils.cc
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,8 @@
|
||||||
#include <common/bsputils.hh>
|
#include <common/bsputils.hh>
|
||||||
#include <common/mathlib.hh>
|
#include <common/mathlib.hh>
|
||||||
|
|
||||||
|
#include "decompile.h"
|
||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <set>
|
#include <set>
|
||||||
#include <list>
|
#include <list>
|
||||||
|
|
@ -514,6 +516,7 @@ FindFaces(const mbsp_t *bsp, const vec3_t pos, const vec3_t normal)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int
|
int
|
||||||
main(int argc, char **argv)
|
main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
|
|
@ -526,7 +529,7 @@ main(int argc, char **argv)
|
||||||
printf("---- bsputil / ericw-tools " stringify(ERICWTOOLS_VERSION) " ----\n");
|
printf("---- bsputil / ericw-tools " stringify(ERICWTOOLS_VERSION) " ----\n");
|
||||||
if (argc == 1) {
|
if (argc == 1) {
|
||||||
printf("usage: bsputil [--extract-entities] [--extract-textures] [--convert bsp29|bsp2|bsp2rmq|q2bsp] [--check] [--modelinfo]\n"
|
printf("usage: bsputil [--extract-entities] [--extract-textures] [--convert bsp29|bsp2|bsp2rmq|q2bsp] [--check] [--modelinfo]\n"
|
||||||
"[--check] [--compare otherbsp] [--findfaces x y z nx ny nz] [--settexinfo facenum texinfonum] bspfile\n");
|
"[--check] [--compare otherbsp] [--findfaces x y z nx ny nz] [--settexinfo facenum texinfonum] [--decompile] bspfile\n");
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -672,6 +675,19 @@ main(int argc, char **argv)
|
||||||
// Overwrite source bsp!
|
// Overwrite source bsp!
|
||||||
WriteBSPFile(source, &bspdata);
|
WriteBSPFile(source, &bspdata);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
} else if (!strcmp(argv[i], "--decompile")) {
|
||||||
|
StripExtension(source);
|
||||||
|
DefaultExtension(source, "-decompile.map");
|
||||||
|
printf("-> writing %s... ", source);
|
||||||
|
|
||||||
|
f = fopen(source, "w");
|
||||||
|
if (!f)
|
||||||
|
Error("couldn't open %s for writing\n", source);
|
||||||
|
|
||||||
|
DecompileBSP(bsp, f);
|
||||||
|
|
||||||
|
fclose(f);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,221 @@
|
||||||
|
/*
|
||||||
|
Copyright (C) 2021 Eric Wasylishen
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
|
||||||
|
See file, 'COPYING', for details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "decompile.h"
|
||||||
|
|
||||||
|
#include <common/cmdlib.hh>
|
||||||
|
#include <common/bspfile.hh>
|
||||||
|
#include <common/bsputils.hh>
|
||||||
|
#include <common/mathlib.hh>
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <utility>
|
||||||
|
#include <tuple>
|
||||||
|
|
||||||
|
struct decomp_plane_t {
|
||||||
|
// const bsp2_dnode_t* node;
|
||||||
|
// bool front;
|
||||||
|
|
||||||
|
qvec3d normal;
|
||||||
|
double distance;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct planepoints {
|
||||||
|
qvec3d point0;
|
||||||
|
qvec3d point1;
|
||||||
|
qvec3d point2;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::tuple<qvec3d, qvec3d> MakeTangentAndBitangentUnnormalized(const qvec3d& normal)
|
||||||
|
{
|
||||||
|
// 0, 1, or 2
|
||||||
|
const int axis = qv::indexOfLargestMagnitudeComponent(normal);
|
||||||
|
const int otherAxisA = (axis + 1) % 3;
|
||||||
|
const int otherAxisB = (axis + 2) % 3;
|
||||||
|
|
||||||
|
// setup two other vectors that are perpendicular to each other
|
||||||
|
qvec3d otherVecA;
|
||||||
|
otherVecA[otherAxisA] = 1.0;
|
||||||
|
|
||||||
|
qvec3d otherVecB;
|
||||||
|
otherVecB[otherAxisB] = 1.0;
|
||||||
|
|
||||||
|
qvec3d tangent = qv::cross(normal, otherVecA);
|
||||||
|
qvec3d bitangent = qv::cross(normal, otherVecB);
|
||||||
|
|
||||||
|
// We want test to point in the same direction as normal.
|
||||||
|
// Swap the tangent bitangent if we got the direction wrong.
|
||||||
|
qvec3d test = qv::cross(tangent, bitangent);
|
||||||
|
|
||||||
|
if (qv::dot(test, normal) < 0) {
|
||||||
|
std::swap(tangent, bitangent);
|
||||||
|
}
|
||||||
|
|
||||||
|
// debug test
|
||||||
|
if (1) {
|
||||||
|
auto n = qv::normalize(qv::cross(tangent, bitangent));
|
||||||
|
double d = qv::distance(n, normal);
|
||||||
|
|
||||||
|
assert(d < 0.0001);
|
||||||
|
}
|
||||||
|
|
||||||
|
return { tangent, bitangent };
|
||||||
|
}
|
||||||
|
|
||||||
|
static planepoints NormalDistanceToThreePoints(const qvec3d& normal, const double dist) {
|
||||||
|
std::tuple<qvec3d, qvec3d> tanBitan = MakeTangentAndBitangentUnnormalized(normal);
|
||||||
|
|
||||||
|
planepoints result;
|
||||||
|
|
||||||
|
result.point0 = normal * dist;
|
||||||
|
result.point1 = result.point0 + std::get<1>(tanBitan);
|
||||||
|
result.point2 = result.point0 + std::get<0>(tanBitan);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PrintPoint(const qvec3d& v, FILE* file) {
|
||||||
|
fprintf(file, "( %0.17g %0.17g %0.17g )", v[0], v[1], v[2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
PrintPlanePoints(const mbsp_t *bsp, const decomp_plane_t& decompplane, FILE* file)
|
||||||
|
{
|
||||||
|
// we have a plane in (normal, distance) form;
|
||||||
|
const planepoints p = NormalDistanceToThreePoints(decompplane.normal, decompplane.distance);
|
||||||
|
|
||||||
|
PrintPoint(p.point0, file);
|
||||||
|
fprintf(file, " ");
|
||||||
|
PrintPoint(p.point1, file);
|
||||||
|
fprintf(file, " ");
|
||||||
|
PrintPoint(p.point2, file);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Preconditions:
|
||||||
|
* - The existing path of plane side choices have been pushed onto `planestack`
|
||||||
|
* - We've arrived at a
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
DecompileLeaf(const std::vector<decomp_plane_t>* planestack, const mbsp_t *bsp, const mleaf_t *leaf, FILE* file)
|
||||||
|
{
|
||||||
|
//printf("got leaf %d\n", leaf->contents);
|
||||||
|
|
||||||
|
if (leaf->contents == CONTENTS_SOLID) {
|
||||||
|
fprintf(file, "{\n");
|
||||||
|
for (const auto& decompplane : *planestack) {
|
||||||
|
PrintPlanePoints(bsp, decompplane, file);
|
||||||
|
|
||||||
|
fprintf(file, " __TB_empty 0 0 0 1 1\n");
|
||||||
|
}
|
||||||
|
fprintf(file, "}\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
decomp_plane_t MakeDecompPlane(const mbsp_t *bsp, const bsp2_dnode_t *node, const bool front) {
|
||||||
|
decomp_plane_t result;
|
||||||
|
|
||||||
|
const dplane_t *dplane = BSP_GetPlane(bsp, node->planenum);
|
||||||
|
|
||||||
|
result.normal = qvec3d(dplane->normal[0],
|
||||||
|
dplane->normal[1],
|
||||||
|
dplane->normal[2]);
|
||||||
|
result.distance = static_cast<double>(dplane->dist);
|
||||||
|
|
||||||
|
// flip the plane if we went down the front side, since we want the outward-facing plane
|
||||||
|
if (front) {
|
||||||
|
result.normal = result.normal * -1.0;
|
||||||
|
result.distance = result.distance * -1.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Preconditions:
|
||||||
|
* - The existing path of plane side choices have been pushed onto `planestack` (but not `node`)
|
||||||
|
* - We're presented with a new plane, `node`
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
DecompileNode(std::vector<decomp_plane_t>* planestack, const mbsp_t *bsp, const bsp2_dnode_t *node, FILE* file)
|
||||||
|
{
|
||||||
|
auto handleSide = [&](const bool front) {
|
||||||
|
planestack->push_back(MakeDecompPlane(bsp, node, front));
|
||||||
|
|
||||||
|
const int32_t child = node->children[front ? 0 : 1];
|
||||||
|
|
||||||
|
if (child < 0) {
|
||||||
|
// it's a leaf on this side
|
||||||
|
DecompileLeaf(planestack, bsp, BSP_GetLeafFromNodeNum(bsp, child), file);
|
||||||
|
} else {
|
||||||
|
// it's another node - process it recursively
|
||||||
|
DecompileNode(planestack, bsp, BSP_GetNode(bsp, child), file);
|
||||||
|
}
|
||||||
|
|
||||||
|
planestack->pop_back();
|
||||||
|
};
|
||||||
|
|
||||||
|
// handle the front and back
|
||||||
|
handleSide(true);
|
||||||
|
handleSide(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
AddMapBoundsToStack(std::vector<decomp_plane_t>* planestack, const mbsp_t *bsp, const bsp2_dnode_t* headnode)
|
||||||
|
{
|
||||||
|
for (int i=0; i<3; ++i) {
|
||||||
|
for (int sign=0; sign<2; ++sign) {
|
||||||
|
|
||||||
|
qvec3d normal;
|
||||||
|
normal[i] = (sign == 0) ? 1 : -1;
|
||||||
|
|
||||||
|
double dist;
|
||||||
|
if (sign == 0) {
|
||||||
|
// positive
|
||||||
|
dist = headnode->maxs[i];
|
||||||
|
} else {
|
||||||
|
dist = -headnode->mins[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
// we want outward-facing planes
|
||||||
|
planestack->push_back({ normal, dist });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
DecompileBSP(const mbsp_t *bsp, FILE* file)
|
||||||
|
{
|
||||||
|
const dmodelh2_t* model = &bsp->dmodels[0];
|
||||||
|
|
||||||
|
// start with hull0 of the world
|
||||||
|
|
||||||
|
const bsp2_dnode_t* headnode = BSP_GetNode(bsp, model->headnode[0]);
|
||||||
|
|
||||||
|
fprintf(file, "{\n");
|
||||||
|
fprintf(file, "\"classname\" \"worldspawn\"\n");
|
||||||
|
|
||||||
|
std::vector<decomp_plane_t> stack;
|
||||||
|
AddMapBoundsToStack(&stack, bsp, headnode);
|
||||||
|
DecompileNode(&stack, bsp, headnode, file);
|
||||||
|
|
||||||
|
fprintf(file, "}\n");
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdio>
|
||||||
|
|
||||||
|
struct mbsp_t;
|
||||||
|
struct bsp2_dnode_t;
|
||||||
|
|
||||||
|
void DecompileBSP(const mbsp_t *bsp, FILE* file);
|
||||||
|
|
@ -254,41 +254,41 @@ typedef struct {
|
||||||
#define Q2_CONTENTS_TRANSLUCENT 0x10000000 // auto set if any surface has trans
|
#define Q2_CONTENTS_TRANSLUCENT 0x10000000 // auto set if any surface has trans
|
||||||
#define Q2_CONTENTS_LADDER 0x20000000
|
#define Q2_CONTENTS_LADDER 0x20000000
|
||||||
|
|
||||||
typedef struct {
|
struct bsp29_dnode_t {
|
||||||
int32_t planenum;
|
int32_t planenum;
|
||||||
int16_t children[2]; /* negative numbers are -(leafs+1), not nodes. children[0] is front, children[1] is back */
|
int16_t children[2]; /* negative numbers are -(leafs+1), not nodes. children[0] is front, children[1] is back */
|
||||||
int16_t mins[3]; /* for sphere culling */
|
int16_t mins[3]; /* for sphere culling */
|
||||||
int16_t maxs[3];
|
int16_t maxs[3];
|
||||||
uint16_t firstface;
|
uint16_t firstface;
|
||||||
uint16_t numfaces; /* counting both sides */
|
uint16_t numfaces; /* counting both sides */
|
||||||
} bsp29_dnode_t;
|
};
|
||||||
|
|
||||||
typedef struct {
|
struct bsp2rmq_dnode_t {
|
||||||
int32_t planenum;
|
int32_t planenum;
|
||||||
int32_t children[2]; /* negative numbers are -(leafs+1), not nodes */
|
int32_t children[2]; /* negative numbers are -(leafs+1), not nodes */
|
||||||
int16_t mins[3]; /* for sphere culling */
|
int16_t mins[3]; /* for sphere culling */
|
||||||
int16_t maxs[3];
|
int16_t maxs[3];
|
||||||
uint32_t firstface;
|
uint32_t firstface;
|
||||||
uint32_t numfaces; /* counting both sides */
|
uint32_t numfaces; /* counting both sides */
|
||||||
} bsp2rmq_dnode_t;
|
};
|
||||||
|
|
||||||
typedef struct {
|
struct bsp2_dnode_t {
|
||||||
int32_t planenum;
|
int32_t planenum;
|
||||||
int32_t children[2]; /* negative numbers are -(leafs+1), not nodes */
|
int32_t children[2]; /* negative numbers are -(leafs+1), not nodes */
|
||||||
float mins[3]; /* for sphere culling */
|
float mins[3]; /* for sphere culling */
|
||||||
float maxs[3];
|
float maxs[3];
|
||||||
uint32_t firstface;
|
uint32_t firstface;
|
||||||
uint32_t numfaces; /* counting both sides */
|
uint32_t numfaces; /* counting both sides */
|
||||||
} bsp2_dnode_t;
|
};
|
||||||
|
|
||||||
typedef struct {
|
struct q2_dnode_t {
|
||||||
int32_t planenum;
|
int32_t planenum;
|
||||||
int32_t children[2]; // negative numbers are -(leafs+1), not nodes
|
int32_t children[2]; // negative numbers are -(leafs+1), not nodes
|
||||||
int16_t mins[3]; // for frustom culling
|
int16_t mins[3]; // for frustom culling
|
||||||
int16_t maxs[3];
|
int16_t maxs[3];
|
||||||
uint16_t firstface;
|
uint16_t firstface;
|
||||||
uint16_t numfaces; // counting both sides
|
uint16_t numfaces; // counting both sides
|
||||||
} q2_dnode_t;
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Note that children are interpreted as unsigned values now, so that we can
|
* Note that children are interpreted as unsigned values now, so that we can
|
||||||
|
|
@ -742,7 +742,7 @@ typedef struct {
|
||||||
byte dpop[256];
|
byte dpop[256];
|
||||||
} q2bsp_t;
|
} q2bsp_t;
|
||||||
|
|
||||||
typedef struct {
|
struct mbsp_t {
|
||||||
int32_t loadversion;
|
int32_t loadversion;
|
||||||
|
|
||||||
int nummodels;
|
int nummodels;
|
||||||
|
|
@ -810,7 +810,7 @@ typedef struct {
|
||||||
dbrushside_t *dbrushsides;
|
dbrushside_t *dbrushsides;
|
||||||
|
|
||||||
byte dpop[256];
|
byte dpop[256];
|
||||||
} mbsp_t; // "generic" bsp - superset of all other supported types
|
}; // "generic" bsp - superset of all other supported types
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
int32_t version;
|
int32_t version;
|
||||||
|
|
|
||||||
|
|
@ -250,6 +250,24 @@ namespace qv {
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <int N, class T>
|
||||||
|
int indexOfLargestMagnitudeComponent(const qvec<N,T> &v)
|
||||||
|
{
|
||||||
|
int largestIndex = 0;
|
||||||
|
T largestMag = 0;
|
||||||
|
|
||||||
|
for (int i=0; i<N; ++i) {
|
||||||
|
const T currentMag = std::fabs(v[i]);
|
||||||
|
|
||||||
|
if (currentMag > largestMag) {
|
||||||
|
largestMag = currentMag;
|
||||||
|
largestIndex = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return largestIndex;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue