new, simpler qbsp3-esque TJunc code;
- currently uses naive brute force approach to finding vertices on faces - simplify 'face fragments', which now only need to contain vertex indices since they are already emitted
This commit is contained in:
parent
4feb2bd2c7
commit
f98dd05f56
|
|
@ -278,7 +278,7 @@ constexpr int HULL_COLLISION = -1;
|
||||||
void Brush_LoadEntity(mapentity_t *entity, const int hullnum);
|
void Brush_LoadEntity(mapentity_t *entity, const int hullnum);
|
||||||
|
|
||||||
std::list<face_t *> CSGFace(face_t *srcface, const mapentity_t* srcentity, const bspbrush_t *srcbrush, const node_t *srcnode);
|
std::list<face_t *> CSGFace(face_t *srcface, const mapentity_t* srcentity, const bspbrush_t *srcbrush, const node_t *srcnode);
|
||||||
void TJunc(const mapentity_t *entity, node_t *headnode);
|
void TJunc(node_t *headnode);
|
||||||
int MakeFaceEdges(node_t *headnode);
|
int MakeFaceEdges(node_t *headnode);
|
||||||
void EmitVertices(node_t *headnode);
|
void EmitVertices(node_t *headnode);
|
||||||
void ExportClipNodes(mapentity_t *entity, node_t *headnode, const int hullnum);
|
void ExportClipNodes(mapentity_t *entity, node_t *headnode, const int hullnum);
|
||||||
|
|
|
||||||
|
|
@ -325,7 +325,7 @@ class mapentity_t;
|
||||||
|
|
||||||
struct face_fragment_t
|
struct face_fragment_t
|
||||||
{
|
{
|
||||||
winding_t w;
|
std::vector<size_t> output_vertices; // filled in by EmitVertices & TJunc
|
||||||
std::vector<int64_t> edges; // only filled in MakeFaceEdges
|
std::vector<int64_t> edges; // only filled in MakeFaceEdges
|
||||||
std::optional<size_t> outputnumber; // only valid for original faces after
|
std::optional<size_t> outputnumber; // only valid for original faces after
|
||||||
// write surfaces
|
// write surfaces
|
||||||
|
|
@ -340,11 +340,12 @@ struct face_t : face_fragment_t
|
||||||
int texinfo;
|
int texinfo;
|
||||||
contentflags_t contents; // contents on the front of the face
|
contentflags_t contents; // contents on the front of the face
|
||||||
int16_t lmshift;
|
int16_t lmshift;
|
||||||
|
winding_t w;
|
||||||
|
|
||||||
qvec3d origin;
|
qvec3d origin;
|
||||||
vec_t radius;
|
vec_t radius;
|
||||||
|
|
||||||
// filled by TJunc
|
// only valid after tjunction code
|
||||||
std::vector<face_fragment_t> fragments;
|
std::vector<face_fragment_t> fragments;
|
||||||
|
|
||||||
portal_t *portal;
|
portal_t *portal;
|
||||||
|
|
|
||||||
|
|
@ -130,6 +130,8 @@ static void AddBounceLight(const qvec3d &pos, const std::map<int, qvec3d> &color
|
||||||
|
|
||||||
const int lastBounceLightIndex = static_cast<int>(bouncelights.size()) - 1;
|
const int lastBounceLightIndex = static_cast<int>(bouncelights.size()) - 1;
|
||||||
bouncelightsByFacenum[Face_GetNum(bsp, face)].push_back(lastBounceLightIndex);
|
bouncelightsByFacenum[Face_GetNum(bsp, face)].push_back(lastBounceLightIndex);
|
||||||
|
|
||||||
|
logging::print("bounce light added: {}\n", colorByStyle.at(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::vector<bouncelight_t> &BounceLights()
|
const std::vector<bouncelight_t> &BounceLights()
|
||||||
|
|
|
||||||
|
|
@ -57,7 +57,7 @@ EmitVertex
|
||||||
NOTE: modifies input to be rounded!
|
NOTE: modifies input to be rounded!
|
||||||
=============
|
=============
|
||||||
*/
|
*/
|
||||||
inline void EmitVertex(qvec3d &vert)
|
inline void EmitVertex(qvec3d &vert, size_t &vert_id)
|
||||||
{
|
{
|
||||||
// if we're extremely close to an integral point,
|
// if we're extremely close to an integral point,
|
||||||
// snap us to it.
|
// snap us to it.
|
||||||
|
|
@ -70,11 +70,12 @@ inline void EmitVertex(qvec3d &vert)
|
||||||
|
|
||||||
// already added
|
// already added
|
||||||
if (auto v = map.find_emitted_hash_vector(vert)) {
|
if (auto v = map.find_emitted_hash_vector(vert)) {
|
||||||
|
vert_id = *v;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// add new vertex!
|
// add new vertex!
|
||||||
map.add_hash_vector(vert, map.bsp.dvertexes.size());
|
map.add_hash_vector(vert, vert_id = map.bsp.dvertexes.size());
|
||||||
|
|
||||||
map.bsp.dvertexes.emplace_back(vert);
|
map.bsp.dvertexes.emplace_back(vert);
|
||||||
}
|
}
|
||||||
|
|
@ -86,14 +87,10 @@ static void EmitFaceVertices(face_t *f)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto &p : f->w) {
|
f->output_vertices.resize(f->w.size());
|
||||||
EmitVertex(p);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto &frag : f->fragments) {
|
for (size_t i = 0; i < f->w.size(); i++) {
|
||||||
for (auto &p : frag.w) {
|
EmitVertex(f->w[i], f->output_vertices[i]);
|
||||||
EmitVertex(p);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -125,21 +122,11 @@ GetEdge
|
||||||
Returns a global edge number, possibly negative to indicate a backwards edge.
|
Returns a global edge number, possibly negative to indicate a backwards edge.
|
||||||
==================
|
==================
|
||||||
*/
|
*/
|
||||||
inline int64_t GetEdge(const qvec3d &p1, const qvec3d &p2, const face_t *face)
|
inline int64_t GetEdge(const size_t &v1, const size_t &v2, const face_t *face)
|
||||||
{
|
{
|
||||||
if (!face->contents.is_valid(options.target_game, false))
|
if (!face->contents.is_valid(options.target_game, false))
|
||||||
FError("Face with invalid contents");
|
FError("Face with invalid contents");
|
||||||
|
|
||||||
auto v1o = map.find_emitted_hash_vector(p1);
|
|
||||||
auto v2o = map.find_emitted_hash_vector(p2);
|
|
||||||
|
|
||||||
if (!v1o || !v2o) {
|
|
||||||
FError("invalid output vertex");
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t v1 = *v1o;
|
|
||||||
size_t v2 = *v2o;
|
|
||||||
|
|
||||||
// search for existing edges
|
// search for existing edges
|
||||||
if (auto it = map.hashedges.find(std::make_pair(v1, v2)); it != map.hashedges.end()) {
|
if (auto it = map.hashedges.find(std::make_pair(v1, v2)); it != map.hashedges.end()) {
|
||||||
return it->second;
|
return it->second;
|
||||||
|
|
@ -161,15 +148,15 @@ static void FindFaceFragmentEdges(face_t *face, face_fragment_t *fragment)
|
||||||
{
|
{
|
||||||
fragment->outputnumber = std::nullopt;
|
fragment->outputnumber = std::nullopt;
|
||||||
|
|
||||||
if (fragment->w.size() > MAXEDGES) {
|
if (fragment->output_vertices.size() > MAXEDGES) {
|
||||||
FError("Internal error: face->numpoints > MAXEDGES");
|
FError("Internal error: face->numpoints > MAXEDGES");
|
||||||
}
|
}
|
||||||
|
|
||||||
fragment->edges.resize(fragment->w.size());
|
fragment->edges.resize(fragment->output_vertices.size());
|
||||||
|
|
||||||
for (size_t i = 0; i < fragment->w.size(); i++) {
|
for (size_t i = 0; i < fragment->output_vertices.size(); i++) {
|
||||||
const qvec3d &p1 = fragment->w[i];
|
auto &p1 = fragment->output_vertices[i];
|
||||||
const qvec3d &p2 = fragment->w[(i + 1) % fragment->w.size()];
|
auto &p2 = fragment->output_vertices[(i + 1) % fragment->output_vertices.size()];
|
||||||
fragment->edges[i] = GetEdge(p1, p2, face);
|
fragment->edges[i] = GetEdge(p1, p2, face);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -237,7 +224,7 @@ static void EmitFaceFragment(face_t *face, face_fragment_t *fragment)
|
||||||
|
|
||||||
// emit surfedges
|
// emit surfedges
|
||||||
out.firstedge = static_cast<int32_t>(map.bsp.dsurfedges.size());
|
out.firstedge = static_cast<int32_t>(map.bsp.dsurfedges.size());
|
||||||
std::copy(fragment->edges.cbegin(), fragment->edges.cbegin() + fragment->w.size(),
|
std::copy(fragment->edges.cbegin(), fragment->edges.cbegin() + fragment->output_vertices.size(),
|
||||||
std::back_inserter(map.bsp.dsurfedges));
|
std::back_inserter(map.bsp.dsurfedges));
|
||||||
fragment->edges.clear();
|
fragment->edges.clear();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -647,7 +647,7 @@ static void ProcessEntity(mapentity_t *entity, const int hullnum)
|
||||||
EmitVertices(tree->headnode.get());
|
EmitVertices(tree->headnode.get());
|
||||||
|
|
||||||
if (!options.notjunc.value()) {
|
if (!options.notjunc.value()) {
|
||||||
TJunc(entity, tree->headnode.get());
|
TJunc(tree->headnode.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.objexport.value() && entity == map.world_entity()) {
|
if (options.objexport.value() && entity == map.world_entity()) {
|
||||||
|
|
|
||||||
533
qbsp/tjunc.cc
533
qbsp/tjunc.cc
|
|
@ -23,381 +23,213 @@
|
||||||
#include <qbsp/qbsp.hh>
|
#include <qbsp/qbsp.hh>
|
||||||
#include <qbsp/map.hh>
|
#include <qbsp/map.hh>
|
||||||
|
|
||||||
constexpr size_t MAXPOINTS = 60;
|
size_t c_degenerate;
|
||||||
|
size_t c_tjunctions;
|
||||||
struct wvert_t
|
size_t c_faceoverflows;
|
||||||
{
|
size_t c_facecollapse;
|
||||||
vec_t t; /* t-value for parametric equation of edge */
|
size_t c_badstartverts;
|
||||||
wvert_t *prev, *next; /* t-ordered list of vertices on same edge */
|
|
||||||
};
|
|
||||||
|
|
||||||
struct wedge_t
|
|
||||||
{
|
|
||||||
wedge_t *next; /* pointer for hash bucket chain */
|
|
||||||
qvec3d dir; /* direction vector for the edge */
|
|
||||||
qvec3d origin; /* origin (t = 0) in parametric form */
|
|
||||||
wvert_t head; /* linked list of verticies on this edge */
|
|
||||||
};
|
|
||||||
|
|
||||||
static int numwedges, numwverts;
|
|
||||||
static int tjuncs;
|
|
||||||
static int tjuncfaces;
|
|
||||||
|
|
||||||
static int cWVerts;
|
|
||||||
static int cWEdges;
|
|
||||||
|
|
||||||
static std::vector<wvert_t> pWVerts;
|
|
||||||
static std::vector<wedge_t> pWEdges;
|
|
||||||
|
|
||||||
//============================================================================
|
|
||||||
|
|
||||||
constexpr size_t NUM_HASH = 1024;
|
|
||||||
|
|
||||||
static wedge_t *wedge_hash[NUM_HASH];
|
|
||||||
static qvec3d hash_min, hash_scale;
|
|
||||||
|
|
||||||
static void InitHash(const qvec3d &mins, const qvec3d &maxs)
|
|
||||||
{
|
|
||||||
vec_t volume;
|
|
||||||
vec_t scale;
|
|
||||||
int newsize[2];
|
|
||||||
|
|
||||||
hash_min = mins;
|
|
||||||
qvec3d size = maxs - mins;
|
|
||||||
memset(wedge_hash, 0, sizeof(wedge_hash));
|
|
||||||
|
|
||||||
volume = size[0] * size[1];
|
|
||||||
|
|
||||||
scale = sqrt(volume / NUM_HASH);
|
|
||||||
|
|
||||||
newsize[0] = (int)(size[0] / scale);
|
|
||||||
newsize[1] = (int)(size[1] / scale);
|
|
||||||
|
|
||||||
hash_scale[0] = newsize[0] / size[0];
|
|
||||||
hash_scale[1] = newsize[1] / size[1];
|
|
||||||
hash_scale[2] = (vec_t)newsize[1];
|
|
||||||
}
|
|
||||||
|
|
||||||
static unsigned HashVec(const qvec3d &vec)
|
|
||||||
{
|
|
||||||
unsigned h;
|
|
||||||
|
|
||||||
h = (unsigned)(hash_scale[0] * (vec[0] - hash_min[0]) * hash_scale[2] + hash_scale[1] * (vec[1] - hash_min[1]));
|
|
||||||
if (h >= NUM_HASH)
|
|
||||||
return NUM_HASH - 1;
|
|
||||||
return h;
|
|
||||||
}
|
|
||||||
|
|
||||||
//============================================================================
|
|
||||||
|
|
||||||
static void CanonicalVector(const qvec3d &p1, const qvec3d &p2, qvec3d &vec)
|
|
||||||
{
|
|
||||||
vec = p2 - p1;
|
|
||||||
vec_t length = qv::normalizeInPlace(vec);
|
|
||||||
|
|
||||||
for (size_t i = 0; i < 3; i++) {
|
|
||||||
if (vec[i] > EQUAL_EPSILON)
|
|
||||||
return;
|
|
||||||
else if (vec[i] < -EQUAL_EPSILON) {
|
|
||||||
vec = -vec;
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
vec[i] = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// FIXME: Line {}: was here but no line number can be grabbed here?
|
|
||||||
logging::print("WARNING: Healing degenerate edge ({}) at ({:.3})\n", length, p1);
|
|
||||||
}
|
|
||||||
|
|
||||||
static wedge_t *FindEdge(const qvec3d &p1, const qvec3d &p2, vec_t &t1, vec_t &t2)
|
|
||||||
{
|
|
||||||
qvec3d origin, edgevec;
|
|
||||||
wedge_t *edge;
|
|
||||||
int h;
|
|
||||||
|
|
||||||
CanonicalVector(p1, p2, edgevec);
|
|
||||||
|
|
||||||
t1 = qv::dot(p1, edgevec);
|
|
||||||
t2 = qv::dot(p2, edgevec);
|
|
||||||
|
|
||||||
origin = p1 + (edgevec * -t1);
|
|
||||||
|
|
||||||
if (t1 > t2) {
|
|
||||||
std::swap(t1, t2);
|
|
||||||
}
|
|
||||||
|
|
||||||
h = HashVec(origin);
|
|
||||||
|
|
||||||
for (edge = wedge_hash[h]; edge; edge = edge->next) {
|
|
||||||
vec_t temp = edge->origin[0] - origin[0];
|
|
||||||
if (temp < -EQUAL_EPSILON || temp > EQUAL_EPSILON)
|
|
||||||
continue;
|
|
||||||
temp = edge->origin[1] - origin[1];
|
|
||||||
if (temp < -EQUAL_EPSILON || temp > EQUAL_EPSILON)
|
|
||||||
continue;
|
|
||||||
temp = edge->origin[2] - origin[2];
|
|
||||||
if (temp < -EQUAL_EPSILON || temp > EQUAL_EPSILON)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
temp = edge->dir[0] - edgevec[0];
|
|
||||||
if (temp < -EQUAL_EPSILON || temp > EQUAL_EPSILON)
|
|
||||||
continue;
|
|
||||||
temp = edge->dir[1] - edgevec[1];
|
|
||||||
if (temp < -EQUAL_EPSILON || temp > EQUAL_EPSILON)
|
|
||||||
continue;
|
|
||||||
temp = edge->dir[2] - edgevec[2];
|
|
||||||
if (temp < -EQUAL_EPSILON || temp > EQUAL_EPSILON)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
return edge;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (numwedges >= cWEdges)
|
|
||||||
FError("Internal error: didn't allocate enough edges for tjuncs?");
|
|
||||||
edge = &pWEdges[numwedges];
|
|
||||||
numwedges++;
|
|
||||||
|
|
||||||
edge->next = wedge_hash[h];
|
|
||||||
wedge_hash[h] = edge;
|
|
||||||
|
|
||||||
edge->origin = origin;
|
|
||||||
edge->dir = edgevec;
|
|
||||||
edge->head.next = edge->head.prev = &edge->head;
|
|
||||||
edge->head.t = VECT_MAX;
|
|
||||||
|
|
||||||
return edge;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
===============
|
==========
|
||||||
AddVert
|
TestEdge
|
||||||
|
|
||||||
===============
|
Can be recursively reentered
|
||||||
|
==========
|
||||||
*/
|
*/
|
||||||
static void AddVert(wedge_t *edge, vec_t t)
|
inline void TestEdge (vec_t start, vec_t end, size_t p1, size_t p2, size_t startvert, const std::vector<size_t> &edge_verts, const qvec3d &edge_start, const qvec3d &edge_dir, std::vector<size_t> &superface)
|
||||||
{
|
{
|
||||||
wvert_t *v, *newv;
|
if (p1 == p2) {
|
||||||
|
// degenerate edge
|
||||||
|
c_degenerate++;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
v = edge->head.next;
|
for (size_t k = startvert; k < edge_verts.size(); k++) {
|
||||||
do {
|
size_t j = edge_verts[k];
|
||||||
if (fabs(v->t - t) < T_EPSILON)
|
|
||||||
return;
|
|
||||||
if (v->t > t)
|
|
||||||
break;
|
|
||||||
v = v->next;
|
|
||||||
} while (1);
|
|
||||||
|
|
||||||
// insert a new wvert before v
|
if (j == p1 || j == p2) {
|
||||||
if (numwverts >= cWVerts)
|
continue;
|
||||||
FError("Internal error: didn't allocate enough vertices for tjuncs?");
|
}
|
||||||
|
|
||||||
newv = &pWVerts[numwverts];
|
const qvec3d &p = map.bsp.dvertexes[j];
|
||||||
numwverts++;
|
qvec3d delta = p - edge_start;
|
||||||
|
vec_t dist = qv::dot(delta, edge_dir);
|
||||||
|
|
||||||
newv->t = t;
|
// check if off an end
|
||||||
newv->next = v;
|
if (dist <= start || dist >= end) {
|
||||||
newv->prev = v->prev;
|
continue;
|
||||||
v->prev->next = newv;
|
}
|
||||||
v->prev = newv;
|
|
||||||
|
qvec3d exact = edge_start + (edge_dir * dist);
|
||||||
|
qvec3d off = p - exact;
|
||||||
|
vec_t error = qv::length(off);
|
||||||
|
|
||||||
|
// brushbsp-fixme: this was 0.5 in Q2, check?
|
||||||
|
if (fabs(error) > DEFAULT_ON_EPSILON) {
|
||||||
|
continue; // not on the edge
|
||||||
|
}
|
||||||
|
|
||||||
|
// break the edge
|
||||||
|
c_tjunctions++;
|
||||||
|
TestEdge (start, dist, p1, j, k + 1, edge_verts, edge_start, edge_dir, superface);
|
||||||
|
TestEdge (dist, end, j, p2, k + 1, edge_verts, edge_start, edge_dir, superface);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// the edge p1 to p2 is now free of tjunctions
|
||||||
|
superface.push_back(p1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
===============
|
==========
|
||||||
AddEdge
|
FindEdgeVerts
|
||||||
|
|
||||||
===============
|
Forced a dumb check of everything
|
||||||
|
==========
|
||||||
*/
|
*/
|
||||||
static void AddEdge(const qvec3d &p1, const qvec3d &p2)
|
static void FindEdgeVerts(const qvec3d &, const qvec3d &, std::vector<size_t> &verts)
|
||||||
{
|
{
|
||||||
vec_t t1, t2;
|
verts.resize(map.bsp.dvertexes.size() - 1);
|
||||||
wedge_t *edge = FindEdge(p1, p2, t1, t2);
|
|
||||||
AddVert(edge, t1);
|
for (size_t i = 0; i < verts.size(); i++) {
|
||||||
AddVert(edge, t2);
|
verts[i] = i + 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
===============
|
==================
|
||||||
AddFaceEdges
|
FaceFromSuperverts
|
||||||
|
|
||||||
===============
|
The faces vertexes have been added to the superverts[] array,
|
||||||
|
and there may be more there than can be held in a face (MAXEDGES).
|
||||||
|
|
||||||
|
If less, the faces vertexnums[] will be filled in, otherwise
|
||||||
|
face will reference a tree of split[] faces until all of the
|
||||||
|
vertexnums can be added.
|
||||||
|
|
||||||
|
superverts[base] will become face->vertexnums[0], and the others
|
||||||
|
will be circularly filled in.
|
||||||
|
==================
|
||||||
*/
|
*/
|
||||||
static void AddFaceEdges(face_t *f)
|
inline void FaceFromSuperverts(node_t *node, face_t *f, size_t base, const std::vector<size_t> &superface)
|
||||||
{
|
{
|
||||||
for (size_t i = 0; i < f->w.size(); i++) {
|
size_t remaining = superface.size();
|
||||||
size_t j = (i + 1) % f->w.size();
|
|
||||||
AddEdge(f->w[i], f->w[j]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//============================================================================
|
// don't need to do any work
|
||||||
|
if (remaining <= MAXEDGES) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// If we hit this amount of points, there's probably an issue
|
// split into multiple fragments, because of vertex overload
|
||||||
// in the algorithm that is generating endless vertices.
|
while (remaining > MAXEDGES) {
|
||||||
constexpr size_t MAX_TJUNC_POINTS = 8192;
|
c_faceoverflows++;
|
||||||
|
|
||||||
static void SplitFaceForTjunc(face_t *face)
|
auto &newf = f->fragments.emplace_back();
|
||||||
{
|
|
||||||
winding_t &w = face->w;
|
|
||||||
qvec3d edgevec[2];
|
|
||||||
vec_t angle;
|
|
||||||
int i, firstcorner, lastcorner;
|
|
||||||
|
|
||||||
do {
|
newf.output_vertices.resize(MAXEDGES);
|
||||||
if (w.size() <= MAXPOINTS) {
|
|
||||||
// the face is now small enough without more cutting
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
tjuncfaces++;
|
for (size_t i = 0; i < MAXEDGES; i++) {
|
||||||
|
newf.output_vertices[i] = superface[(i + base) % superface.size()];
|
||||||
|
}
|
||||||
|
|
||||||
restart:
|
remaining -= (MAXEDGES - 2);
|
||||||
/* find the last corner */
|
base = (base + MAXEDGES - 1) % superface.size();
|
||||||
edgevec[0] = qv::normalize(w[w.size() - 1] - w[0]);
|
}
|
||||||
for (lastcorner = w.size() - 1; lastcorner > 0; lastcorner--) {
|
|
||||||
const qvec3d &p0 = w[lastcorner - 1];
|
|
||||||
const qvec3d &p1 = w[lastcorner];
|
|
||||||
edgevec[1] = qv::normalize(p0 - p1);
|
|
||||||
angle = qv::dot(edgevec[0], edgevec[1]);
|
|
||||||
if (angle < 1 - ANGLEEPSILON || angle > 1 + ANGLEEPSILON)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* find the first corner */
|
// copy the vertexes back to the face
|
||||||
edgevec[0] = qv::normalize(w[1] - w[0]);
|
f->w.resize(remaining);
|
||||||
for (firstcorner = 1; firstcorner < w.size() - 1; firstcorner++) {
|
|
||||||
const qvec3d &p0 = w[firstcorner + 1];
|
|
||||||
const qvec3d &p1 = w[firstcorner];
|
|
||||||
edgevec[1] = qv::normalize(p0 - p1);
|
|
||||||
angle = qv::dot(edgevec[0], edgevec[1]);
|
|
||||||
if (angle < 1 - ANGLEEPSILON || angle > 1 + ANGLEEPSILON)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (firstcorner + 2 >= MAXPOINTS) {
|
for (size_t i = 0; i < remaining; i++) {
|
||||||
/* rotate the point winding */
|
f->output_vertices[i] = superface[(i + base) % superface.size()];
|
||||||
qvec3d point0 = w[0];
|
}
|
||||||
for (i = 1; i < w.size(); i++)
|
|
||||||
w[i - 1] = w[i];
|
|
||||||
w[w.size() - 1] = point0;
|
|
||||||
goto restart;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* cut off as big a piece as possible, less than MAXPOINTS, and not
|
|
||||||
* past lastcorner
|
|
||||||
*/
|
|
||||||
winding_t neww(face->w);
|
|
||||||
|
|
||||||
if (w.size() - firstcorner <= MAXPOINTS)
|
|
||||||
neww.resize(firstcorner + 2);
|
|
||||||
else if (lastcorner + 2 < MAXPOINTS && w.size() - lastcorner <= MAXPOINTS)
|
|
||||||
neww.resize(lastcorner + 2);
|
|
||||||
else
|
|
||||||
neww.resize(MAXPOINTS);
|
|
||||||
|
|
||||||
for (i = 0; i < neww.size(); i++)
|
|
||||||
Q_assert(qv::equalExact(neww[i], w[i]));
|
|
||||||
for (i = neww.size() - 1; i < w.size(); i++)
|
|
||||||
w[i - (neww.size() - 2)] = w[i];
|
|
||||||
|
|
||||||
w.resize(w.size() - (neww.size() - 2));
|
|
||||||
|
|
||||||
face->fragments.push_back(face_fragment_t{std::move(neww)});
|
|
||||||
} while (1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
===============
|
==================
|
||||||
FixFaceEdges
|
FixFaceEdges
|
||||||
|
==================
|
||||||
===============
|
|
||||||
*/
|
*/
|
||||||
static void FixFaceEdges(face_t *face)
|
static void FixFaceEdges (node_t *node, face_t *f)
|
||||||
{
|
{
|
||||||
int i, j;
|
std::vector<size_t> count, start;
|
||||||
wedge_t *edge;
|
std::vector<size_t> superface;
|
||||||
wvert_t *v;
|
superface.reserve(64);
|
||||||
vec_t t1, t2;
|
std::vector<size_t> edge_verts;
|
||||||
|
|
||||||
for (i = 0; i < face->w.size();) {
|
// brushbsp-fixme
|
||||||
j = (i + 1) % face->w.size();
|
//if (f->merged || f->split[0] || f->split[1])
|
||||||
|
// return;
|
||||||
|
|
||||||
edge = FindEdge(face->w[i], face->w[j], t1, t2);
|
for (size_t i = 0; i < f->w.size(); i++) {
|
||||||
|
auto &p1 = f->w[i];
|
||||||
|
auto &p2 = f->w[(i + 1) % f->w.size()];
|
||||||
|
|
||||||
v = edge->head.next;
|
qvec3d edge_start = p1;
|
||||||
while (v->t < t1 + T_EPSILON)
|
qvec3d e2 = p2;
|
||||||
v = v->next;
|
|
||||||
|
|
||||||
if (v->t < t2 - T_EPSILON) {
|
FindEdgeVerts (edge_start, e2, edge_verts);
|
||||||
/* insert a new vertex here */
|
|
||||||
if (face->w.size() >= MAX_TJUNC_POINTS) {
|
|
||||||
FError("generated too many points (max {})", MAX_TJUNC_POINTS);
|
|
||||||
}
|
|
||||||
|
|
||||||
tjuncs++;
|
vec_t len;
|
||||||
|
qvec3d edge_dir = qv::normalize(e2 - edge_start, len);
|
||||||
|
|
||||||
face->w.resize(face->w.size() + 1);
|
start.push_back(superface.size());
|
||||||
|
TestEdge(0, len, f->output_vertices[i], f->output_vertices[(i + 1) % f->w.size()], 0, edge_verts, edge_start, edge_dir, superface);
|
||||||
|
|
||||||
std::copy_backward(face->w.begin() + j, face->w.end() - 1, face->w.end());
|
count.push_back(superface.size() - start[i]);
|
||||||
|
}
|
||||||
|
|
||||||
face->w[j] = edge->origin + (edge->dir * v->t);
|
if (superface.size() < 3) {
|
||||||
|
// entire face collapsed
|
||||||
|
f->w.clear();
|
||||||
|
c_facecollapse++;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
i = 0;
|
// we want to pick a vertex that doesn't have tjunctions
|
||||||
continue;
|
// on either side, which can cause artifacts on trifans,
|
||||||
}
|
// especially underwater
|
||||||
|
size_t i = 0;
|
||||||
|
|
||||||
i++;
|
for (; i < f->w.size(); i++) {
|
||||||
}
|
if (count[i] == 1 && count[(i + f->w.size() - 1) % f->w.size()] == 1) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// we're good to go!
|
size_t base;
|
||||||
if (face->w.size() <= MAXPOINTS) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Too many edges - needs to be split into multiple faces */
|
if (i == f->w.size()) {
|
||||||
SplitFaceForTjunc(face);
|
c_badstartverts++;
|
||||||
|
base = 0;
|
||||||
|
} else {
|
||||||
|
// rotate the vertex order
|
||||||
|
base = start[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
// this may fragment the face if > MAXEDGES
|
||||||
|
FaceFromSuperverts(node, f, base, superface);
|
||||||
}
|
}
|
||||||
|
|
||||||
//============================================================================
|
/*
|
||||||
|
==================
|
||||||
static void tjunc_count_r(node_t *node)
|
FixEdges_r
|
||||||
|
==================
|
||||||
|
*/
|
||||||
|
static void FixEdges_r(node_t *node)
|
||||||
{
|
{
|
||||||
if (node->planenum == PLANENUM_LEAF)
|
if (node->planenum == PLANENUM_LEAF) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
for (auto &f : node->facelist) {
|
for (auto &f : node->facelist) {
|
||||||
cWVerts += f->w.size();
|
// might have been omitted earlier, so `output_vertices` will be empty
|
||||||
}
|
if (f->output_vertices.size()) {
|
||||||
|
FixFaceEdges(node, f.get());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
tjunc_count_r(node->children[0].get());
|
FixEdges_r(node->children[0].get());
|
||||||
tjunc_count_r(node->children[1].get());
|
FixEdges_r(node->children[1].get());
|
||||||
}
|
|
||||||
|
|
||||||
static void tjunc_find_r(node_t *node)
|
|
||||||
{
|
|
||||||
if (node->planenum == PLANENUM_LEAF)
|
|
||||||
return;
|
|
||||||
|
|
||||||
for (auto &f : node->facelist) {
|
|
||||||
AddFaceEdges(f.get());
|
|
||||||
}
|
|
||||||
|
|
||||||
tjunc_find_r(node->children[0].get());
|
|
||||||
tjunc_find_r(node->children[1].get());
|
|
||||||
}
|
|
||||||
|
|
||||||
static void tjunc_fix_r(node_t *node)
|
|
||||||
{
|
|
||||||
if (node->planenum == PLANENUM_LEAF)
|
|
||||||
return;
|
|
||||||
|
|
||||||
for (auto &face : node->facelist) {
|
|
||||||
FixFaceEdges(face.get());
|
|
||||||
}
|
|
||||||
|
|
||||||
tjunc_fix_r(node->children[0].get());
|
|
||||||
tjunc_fix_r(node->children[1].get());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
@ -405,51 +237,22 @@ static void tjunc_fix_r(node_t *node)
|
||||||
tjunc
|
tjunc
|
||||||
===========
|
===========
|
||||||
*/
|
*/
|
||||||
void TJunc(const mapentity_t *entity, node_t *headnode)
|
void TJunc(node_t *headnode)
|
||||||
{
|
{
|
||||||
logging::print(logging::flag::PROGRESS, "---- {} ----\n", __func__);
|
logging::print(logging::flag::PROGRESS, "---- {} ----\n", __func__);
|
||||||
|
|
||||||
/*
|
// break edges on tjunctions
|
||||||
* Guess edges = 1/2 verts
|
c_degenerate = 0;
|
||||||
* Verts are arbitrarily multiplied by 2 because there appears to
|
c_facecollapse = 0;
|
||||||
* be a need for them to "grow" slightly.
|
c_tjunctions = 0;
|
||||||
*/
|
c_faceoverflows = 0;
|
||||||
cWVerts = 0;
|
c_badstartverts = 0;
|
||||||
tjunc_count_r(headnode);
|
|
||||||
cWEdges = cWVerts;
|
|
||||||
cWVerts *= 2;
|
|
||||||
|
|
||||||
pWVerts.clear();
|
FixEdges_r (headnode);
|
||||||
pWEdges.clear();
|
|
||||||
pWVerts.resize(cWVerts);
|
|
||||||
pWEdges.resize(cWEdges);
|
|
||||||
|
|
||||||
qvec3d maxs;
|
logging::print (logging::flag::STAT, "{:5} edges degenerated\n", c_degenerate);
|
||||||
/*
|
logging::print (logging::flag::STAT, "{:5} faces degenerated\n", c_facecollapse);
|
||||||
* identify all points on common edges
|
logging::print (logging::flag::STAT, "{:5} edges added by tjunctions\n", c_tjunctions);
|
||||||
* origin points won't allways be inside the map, so extend the hash area
|
logging::print (logging::flag::STAT, "{:5} faces added by tjunctions\n", c_faceoverflows);
|
||||||
*/
|
logging::print (logging::flag::STAT, "{:5} bad start verts\n", c_badstartverts);
|
||||||
for (size_t i = 0; i < 3; i++) {
|
|
||||||
if (fabs(entity->bounds.maxs()[i]) > fabs(entity->bounds.mins()[i]))
|
|
||||||
maxs[i] = fabs(entity->bounds.maxs()[i]);
|
|
||||||
else
|
|
||||||
maxs[i] = fabs(entity->bounds.mins()[i]);
|
|
||||||
}
|
|
||||||
qvec3d mins = -maxs;
|
|
||||||
|
|
||||||
InitHash(mins, maxs);
|
|
||||||
|
|
||||||
numwedges = numwverts = 0;
|
|
||||||
|
|
||||||
tjunc_find_r(headnode);
|
|
||||||
|
|
||||||
logging::print(logging::flag::STAT, " {:8} world edges\n", numwedges);
|
|
||||||
logging::print(logging::flag::STAT, " {:8} edge points\n", numwverts);
|
|
||||||
|
|
||||||
/* add extra vertexes on edges where needed */
|
|
||||||
tjuncs = tjuncfaces = 0;
|
|
||||||
tjunc_fix_r(headnode);
|
|
||||||
|
|
||||||
logging::print(logging::flag::STAT, " {:8} edges added by tjunctions\n", tjuncs);
|
|
||||||
logging::print(logging::flag::STAT, " {:8} faces added by tjunctions\n", tjuncfaces);
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue