qbsp: remove internal list in face_t
This commit is contained in:
parent
fefc0fc662
commit
6b5b426636
|
|
@ -22,11 +22,12 @@
|
|||
#pragma once
|
||||
|
||||
#include <list>
|
||||
#include <tuple>
|
||||
|
||||
extern int csgmergefaces;
|
||||
|
||||
// build surfaces is also used by GatherNodeFaces
|
||||
std::vector<surface_t> BuildSurfaces(std::map<int, std::list<face_t *>> &planefaces);
|
||||
face_t *NewFaceFromFace(face_t *in);
|
||||
void SplitFace(face_t *in, const qplane3d &split, face_t **front, face_t **back);
|
||||
std::tuple<face_t *, face_t *> SplitFace(face_t *in, const qplane3d &split);
|
||||
void UpdateFaceSphere(face_t *in);
|
||||
|
|
|
|||
|
|
@ -211,8 +211,6 @@ struct face_fragment_t
|
|||
|
||||
struct face_t : face_fragment_t
|
||||
{
|
||||
face_t *next;
|
||||
|
||||
int planenum;
|
||||
int planeside; // which side is the front of the face
|
||||
int texinfo;
|
||||
|
|
@ -301,7 +299,7 @@ struct node_t
|
|||
int firstface; // decision node only
|
||||
int numfaces; // decision node only
|
||||
node_t *children[2]; // children[0] = front side, children[1] = back side of plane. only valid for decision nodes
|
||||
face_t *faces; // decision nodes only, list for both sides
|
||||
std::list<face_t *> facelist; // decision nodes only, list for both sides
|
||||
|
||||
// information for leafs
|
||||
contentflags_t contents; // leaf nodes (0 for decision nodes)
|
||||
|
|
|
|||
|
|
@ -404,21 +404,6 @@ static std::vector<face_t> CreateBrushFaces(const mapentity_t *src, hullbrush_t
|
|||
return { std::make_move_iterator(facelist.begin()), std::make_move_iterator(facelist.end()) };
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
FreeBrushFaces
|
||||
=================
|
||||
*/
|
||||
static void FreeBrushFaces(face_t *facelist)
|
||||
{
|
||||
face_t *face, *next;
|
||||
|
||||
for (face = facelist; face; face = next) {
|
||||
next = face->next;
|
||||
delete face;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
=====================
|
||||
FreeBrushes
|
||||
|
|
|
|||
124
qbsp/csg4.cc
124
qbsp/csg4.cc
|
|
@ -101,10 +101,10 @@ void UpdateFaceSphere(face_t *in)
|
|||
==================
|
||||
SplitFace
|
||||
|
||||
Frees in.
|
||||
Frees in. Returns {front, back}
|
||||
==================
|
||||
*/
|
||||
void SplitFace(face_t *in, const qplane3d &split, face_t **front, face_t **back)
|
||||
std::tuple<face_t *, face_t *> SplitFace(face_t *in, const qplane3d &split)
|
||||
{
|
||||
vec_t *dists = (vec_t *)alloca(sizeof(vec_t) * (in->w.size() + 1));
|
||||
side_t *sides = (side_t *)alloca(sizeof(side_t) * (in->w.size() + 1));
|
||||
|
|
@ -131,18 +131,14 @@ void SplitFace(face_t *in, const qplane3d &split, face_t **front, face_t **back)
|
|||
|
||||
// Plane doesn't split this face after all
|
||||
if (!counts[SIDE_FRONT]) {
|
||||
*front = NULL;
|
||||
*back = in;
|
||||
return;
|
||||
return {nullptr, in};
|
||||
}
|
||||
if (!counts[SIDE_BACK]) {
|
||||
*front = in;
|
||||
*back = NULL;
|
||||
return;
|
||||
return {in, nullptr};
|
||||
}
|
||||
|
||||
*back = newf = NewFaceFromFace(in);
|
||||
*front = new2 = NewFaceFromFace(in);
|
||||
newf = NewFaceFromFace(in);
|
||||
new2 = NewFaceFromFace(in);
|
||||
|
||||
// distribute the points and generate splits
|
||||
for (i = 0; i < in->w.size(); i++) {
|
||||
|
|
@ -187,6 +183,9 @@ void SplitFace(face_t *in, const qplane3d &split, face_t **front, face_t **back)
|
|||
|
||||
/* free the original face now that it is represented by the fragments */
|
||||
delete in;
|
||||
|
||||
// {front, back}
|
||||
return {new2, newf};
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -198,13 +197,14 @@ outside the brush to the outside list, without splitting them. This saves us
|
|||
time in mergefaces later on (and sometimes a lot of memory)
|
||||
=================
|
||||
*/
|
||||
static void RemoveOutsideFaces(const brush_t &brush, face_t **inside, face_t **outside)
|
||||
static void RemoveOutsideFaces(const brush_t &brush, std::list<face_t *> *inside, std::list<face_t *> *outside)
|
||||
{
|
||||
face_t *face = *inside;
|
||||
face_t *next = nullptr;
|
||||
*inside = NULL;
|
||||
while (face) {
|
||||
next = face->next;
|
||||
std::list<face_t *> oldinside;
|
||||
|
||||
// clear `inside`, transfer it to `oldinside`
|
||||
std::swap(*inside, oldinside);
|
||||
|
||||
for (face_t *face : oldinside) {
|
||||
std::optional<winding_t> w = face->w;
|
||||
for (auto &clipface : brush.faces) {
|
||||
qbsp_plane_t clipplane = map.planes[clipface.planenum];
|
||||
|
|
@ -216,14 +216,11 @@ static void RemoveOutsideFaces(const brush_t &brush, face_t **inside, face_t **o
|
|||
break;
|
||||
}
|
||||
if (!w) {
|
||||
/* The face is completely outside this brush */
|
||||
face->next = *outside;
|
||||
*outside = face;
|
||||
/* The face is completely outside this brush */
|
||||
outside->push_front(face);
|
||||
} else {
|
||||
face->next = *inside;
|
||||
*inside = face;
|
||||
inside->push_front(face);
|
||||
}
|
||||
face = next;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -237,17 +234,17 @@ outside list or spliting it into a piece in each list.
|
|||
Faces exactly on the plane will stay inside unless overdrawn by later brush
|
||||
=================
|
||||
*/
|
||||
static void ClipInside(const face_t *clipface, bool precedence, face_t **inside, face_t **outside)
|
||||
static void ClipInside(const face_t *clipface, bool precedence, std::list<face_t *> *inside,
|
||||
std::list<face_t *> *outside)
|
||||
{
|
||||
face_t *face, *next, *frags[2];
|
||||
std::list<face_t *> oldinside;
|
||||
|
||||
// effectively make a copy of `inside`, and clear it
|
||||
std::swap(*inside, oldinside);
|
||||
|
||||
const qbsp_plane_t &splitplane = map.planes[clipface->planenum];
|
||||
|
||||
face = *inside;
|
||||
*inside = NULL;
|
||||
while (face) {
|
||||
next = face->next;
|
||||
|
||||
for (face_t *face : oldinside) {
|
||||
/* HACK: Check for on-plane but not the same planenum
|
||||
( https://github.com/ericwa/ericw-tools/issues/174 )
|
||||
*/
|
||||
|
|
@ -260,6 +257,8 @@ static void ClipInside(const face_t *clipface, bool precedence, face_t **inside,
|
|||
}
|
||||
}
|
||||
|
||||
std::array<face_t *, 2> frags;
|
||||
|
||||
/* Handle exactly on-plane faces */
|
||||
if (face->planenum == clipface->planenum || spurious_onplane) {
|
||||
const qplane3d faceplane = Face_Plane(face);
|
||||
|
|
@ -269,27 +268,24 @@ static void ClipInside(const face_t *clipface, bool precedence, face_t **inside,
|
|||
|
||||
if (opposite || precedence) {
|
||||
/* always clip off opposite facing */
|
||||
frags[clipface->planeside] = NULL;
|
||||
frags[!clipface->planeside] = face;
|
||||
frags[clipface->planeside] = {};
|
||||
frags[!clipface->planeside] = {face};
|
||||
} else {
|
||||
/* leave it on the outside */
|
||||
frags[clipface->planeside] = face;
|
||||
frags[!clipface->planeside] = NULL;
|
||||
frags[clipface->planeside] = {face};
|
||||
frags[!clipface->planeside] = {};
|
||||
}
|
||||
} else {
|
||||
/* proper split */
|
||||
SplitFace(face, splitplane, &frags[0], &frags[1]);
|
||||
std::tie(frags[0], frags[1]) = SplitFace(face, splitplane);
|
||||
}
|
||||
|
||||
if (frags[clipface->planeside]) {
|
||||
frags[clipface->planeside]->next = *outside;
|
||||
*outside = frags[clipface->planeside];
|
||||
outside->push_front(frags[clipface->planeside]);
|
||||
}
|
||||
if (frags[!clipface->planeside]) {
|
||||
frags[!clipface->planeside]->next = *inside;
|
||||
*inside = frags[!clipface->planeside];
|
||||
inside->push_front(frags[!clipface->planeside]);
|
||||
}
|
||||
face = next;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -303,13 +299,10 @@ This plane map is later used to build up the surfaces for creating the BSP.
|
|||
Not parallel.
|
||||
==================
|
||||
*/
|
||||
static void SaveFacesToPlaneList(face_t *facelist, bool mirror, std::map<int, std::list<face_t *>> &planefaces)
|
||||
static void SaveFacesToPlaneList(std::list<face_t *> facelist, bool mirror, std::map<int, std::list<face_t *>> &planefaces)
|
||||
{
|
||||
face_t *face, *next;
|
||||
|
||||
for (face = facelist; face; face = next) {
|
||||
for (face_t *face : facelist) {
|
||||
const int plane = face->planenum;
|
||||
next = face->next;
|
||||
|
||||
// Handy for debugging CSGFaces issues, throw out detail faces and export obj.
|
||||
#if 0
|
||||
|
|
@ -355,39 +348,33 @@ static void SaveFacesToPlaneList(face_t *facelist, bool mirror, std::map<int, st
|
|||
}
|
||||
}
|
||||
|
||||
static void FreeFaces(face_t *face)
|
||||
static void FreeFaces(std::list<face_t *> &facelist)
|
||||
{
|
||||
face_t *next;
|
||||
|
||||
while (face) {
|
||||
next = face->next;
|
||||
for (face_t *face : facelist) {
|
||||
delete face;
|
||||
face = next;
|
||||
}
|
||||
facelist.clear();
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
SaveInsideFaces
|
||||
|
||||
`face` is the list of faces from `brush` (solid) that are inside `clipbrush` (nonsolid)
|
||||
`faces` is the list of faces from `brush` (solid) that are inside `clipbrush` (nonsolid)
|
||||
|
||||
Save the list of faces onto the output list, modifying the outside contents to
|
||||
match given brush. If the inside contents are empty, the given brush's
|
||||
contents override the face inside contents.
|
||||
==================
|
||||
*/
|
||||
static void SaveInsideFaces(face_t *face, const brush_t &clipbrush, face_t **savelist)
|
||||
static void SaveInsideFaces(const std::list<face_t *> &faces, const brush_t &clipbrush, std::list<face_t *> *savelist)
|
||||
{
|
||||
Q_assert(!clipbrush.contents.is_solid(options.target_game));
|
||||
|
||||
face_t *next;
|
||||
|
||||
while (face) {
|
||||
for (face_t *face : faces) {
|
||||
// the back side of `face` is a solid
|
||||
// Q_assert(face->contents[1] == CONTENTS_SOLID);
|
||||
|
||||
next = face->next;
|
||||
face->contents[0] = clipbrush.contents;
|
||||
|
||||
if ((face->contents[1].is_solid(options.target_game) || face->contents[1].is_sky(options.target_game)) &&
|
||||
|
|
@ -434,9 +421,7 @@ static void SaveInsideFaces(face_t *face, const brush_t &clipbrush, face_t **sav
|
|||
}
|
||||
}
|
||||
|
||||
face->next = *savelist;
|
||||
*savelist = face;
|
||||
face = next;
|
||||
savelist->push_front(face);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -484,18 +469,16 @@ std::vector<surface_t> BuildSurfaces(std::map<int, std::list<face_t *>> &planefa
|
|||
CopyBrushFaces
|
||||
==================
|
||||
*/
|
||||
static face_t *CopyBrushFaces(const brush_t &brush)
|
||||
static std::list<face_t *> CopyBrushFaces(const brush_t &brush)
|
||||
{
|
||||
face_t *facelist, *newface;
|
||||
std::list<face_t *> facelist;
|
||||
|
||||
facelist = NULL;
|
||||
for (auto &face : brush.faces) {
|
||||
brushfaces++;
|
||||
newface = new face_t(face);
|
||||
auto *newface = new face_t(face);
|
||||
newface->contents = { options.target_game->create_empty_contents(), brush.contents };
|
||||
newface->lmshift = { brush.lmshift, brush.lmshift };
|
||||
newface->next = facelist;
|
||||
facelist = newface;
|
||||
facelist.push_back(newface);
|
||||
}
|
||||
|
||||
return facelist;
|
||||
|
|
@ -524,7 +507,7 @@ std::vector<surface_t> CSGFaces(const mapentity_t *entity)
|
|||
#endif
|
||||
|
||||
// output vector for the parallel_for
|
||||
std::vector<face_t *> brushvec_outsides;
|
||||
std::vector<std::list<face_t *>> brushvec_outsides;
|
||||
brushvec_outsides.resize(entity->brushes.size());
|
||||
|
||||
/*
|
||||
|
|
@ -537,7 +520,7 @@ std::vector<surface_t> CSGFaces(const mapentity_t *entity)
|
|||
*/
|
||||
tbb::parallel_for(static_cast<size_t>(0), entity->brushes.size(), [entity, &brushvec_outsides](const size_t i) {
|
||||
auto &brush = entity->brushes[i];
|
||||
face_t *outside = CopyBrushFaces(brush);
|
||||
std::list<face_t*> outside = CopyBrushFaces(brush);
|
||||
bool overwrite = false;
|
||||
|
||||
for (auto &clipbrush : entity->brushes) {
|
||||
|
|
@ -586,8 +569,9 @@ std::vector<surface_t> CSGFaces(const mapentity_t *entity)
|
|||
*/
|
||||
|
||||
// divide faces by the planes of the new brush
|
||||
face_t *inside = outside;
|
||||
outside = NULL;
|
||||
std::list<face_t *> inside;
|
||||
|
||||
std::swap(inside, outside);
|
||||
|
||||
RemoveOutsideFaces(clipbrush, &inside, &outside);
|
||||
for (auto &clipface : clipbrush.faces)
|
||||
|
|
@ -632,7 +616,7 @@ std::vector<surface_t> CSGFaces(const mapentity_t *entity)
|
|||
std::map<int, std::list<face_t *>> planefaces;
|
||||
for (size_t i = 0; i < entity->brushes.size(); ++i) {
|
||||
const brush_t &brush = entity->brushes[i];
|
||||
face_t *outside = brushvec_outsides[i];
|
||||
std::list<face_t *> outside = brushvec_outsides[i];
|
||||
|
||||
/*
|
||||
* All of the faces left on the outside list are real surface faces
|
||||
|
|
|
|||
|
|
@ -145,7 +145,7 @@ static void ExportObj_Nodes_r(const node_t *node, std::vector<const face_t *> *d
|
|||
return;
|
||||
}
|
||||
|
||||
for (face_t *face = node->faces; face; face = face->next) {
|
||||
for (face_t *face : node->facelist) {
|
||||
dest->push_back(face);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -324,7 +324,7 @@ static void ResetFacesTouchingOccupiedLeafs(node_t *node)
|
|||
return;
|
||||
}
|
||||
|
||||
for (face_t *face = node->faces; face; face = face->next) {
|
||||
for (face_t *face : node->facelist) {
|
||||
face->touchesOccupiedLeaf = false;
|
||||
}
|
||||
|
||||
|
|
@ -383,7 +383,7 @@ static void ClearOutFaces(node_t *node)
|
|||
}
|
||||
|
||||
// FIXME: Shouldn't be needed here
|
||||
node->faces = NULL;
|
||||
node->facelist = {};
|
||||
}
|
||||
|
||||
static void OutLeafsToSolid_r(node_t *node, int *outleafs_count)
|
||||
|
|
|
|||
|
|
@ -548,16 +548,13 @@ static twosided<std::optional<surface_t>> DividePlane(surface_t &in, const qplan
|
|||
std::list<face_t *> frontlist, backlist;
|
||||
|
||||
for (auto face : in.faces) {
|
||||
face_t *frontfrag = nullptr;
|
||||
face_t *backfrag = nullptr;
|
||||
|
||||
SplitFace(face, split, &frontfrag, &backfrag);
|
||||
auto [frontfrag, backfrag] = SplitFace(face, split);
|
||||
|
||||
if (frontfrag) {
|
||||
frontlist.emplace_back(frontfrag);
|
||||
frontlist.push_back(frontfrag);
|
||||
}
|
||||
if (backfrag) {
|
||||
backlist.emplace_back(backfrag);
|
||||
backlist.push_back(backfrag);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -606,7 +603,7 @@ Called in parallel.
|
|||
*/
|
||||
static void LinkConvexFaces(std::vector<surface_t> &planelist, node_t *leafnode)
|
||||
{
|
||||
leafnode->faces = NULL;
|
||||
leafnode->facelist.clear();
|
||||
leafnode->planenum = PLANENUM_LEAF;
|
||||
|
||||
int count = 0;
|
||||
|
|
@ -688,7 +685,7 @@ is returned here (why?).
|
|||
Called in parallel.
|
||||
==================
|
||||
*/
|
||||
static face_t *LinkNodeFaces(surface_t &surface)
|
||||
static std::list<face_t *> LinkNodeFaces(surface_t &surface)
|
||||
{
|
||||
// subdivide large faces
|
||||
for (auto it = surface.faces.begin(); it != surface.faces.end(); it++) {
|
||||
|
|
@ -699,14 +696,15 @@ static face_t *LinkNodeFaces(surface_t &surface)
|
|||
|
||||
nodefaces += surface.faces.size();
|
||||
|
||||
face_t *list = NULL;
|
||||
|
||||
std::list<face_t *> list;
|
||||
|
||||
// copy
|
||||
for (auto &f : surface.faces) {
|
||||
face_t *newf = new face_t(*f);
|
||||
Q_assert(newf->original == nullptr);
|
||||
|
||||
list.push_front(newf);
|
||||
f->original = newf;
|
||||
newf->next = list;
|
||||
list = newf;
|
||||
}
|
||||
|
||||
return list;
|
||||
|
|
@ -735,7 +733,7 @@ static void PartitionSurfaces(std::vector<surface_t> &surfaces, node_t *node)
|
|||
splitnodes++;
|
||||
LogPercent(splitnodes.load(), csgmergefaces);
|
||||
|
||||
node->faces = LinkNodeFaces(*split);
|
||||
node->facelist = LinkNodeFaces(*split);
|
||||
node->children[0] = new node_t{};
|
||||
node->children[1] = new node_t{};
|
||||
node->planenum = split->planenum;
|
||||
|
|
|
|||
|
|
@ -110,7 +110,7 @@ std::list<face_t *>::iterator SubdivideFace(std::list<face_t *>::iterator it, st
|
|||
// plane.dist = (mins + subdiv) / v;
|
||||
plane.dist = (mins + subdiv - 16) / v;
|
||||
|
||||
SplitFace(f, plane, &front, &back);
|
||||
std::tie(front, back) = SplitFace(f, plane);
|
||||
if (!front || !back) {
|
||||
LogPrintLocked("didn't split\n");
|
||||
break;
|
||||
|
|
@ -128,13 +128,8 @@ std::list<face_t *>::iterator SubdivideFace(std::list<face_t *>::iterator it, st
|
|||
|
||||
static void FreeNode(node_t* node)
|
||||
{
|
||||
if (node->faces) {
|
||||
face_t *f, *next;
|
||||
for (f = node->faces; f; f = next) {
|
||||
next = f->next;
|
||||
delete f;
|
||||
}
|
||||
node->faces = nullptr;
|
||||
for (face_t *f : node->facelist) {
|
||||
delete f;
|
||||
}
|
||||
delete node;
|
||||
}
|
||||
|
|
@ -159,12 +154,9 @@ have inside faces.
|
|||
|
||||
static void GatherNodeFaces_r(node_t *node, std::map<int, std::list<face_t *>> &planefaces)
|
||||
{
|
||||
face_t *f, *next;
|
||||
|
||||
if (node->planenum != PLANENUM_LEAF) {
|
||||
// decision node
|
||||
for (f = node->faces; f; f = next) {
|
||||
next = f->next;
|
||||
for (face_t *f : node->facelist) {
|
||||
if (!f->w.size()) { // face was removed outside
|
||||
delete f;
|
||||
} else {
|
||||
|
|
@ -172,7 +164,7 @@ static void GatherNodeFaces_r(node_t *node, std::map<int, std::list<face_t *>> &
|
|||
}
|
||||
}
|
||||
// don't attempt to free node->faces again as ownership has moved to the planefaces map
|
||||
node->faces = nullptr;
|
||||
node->facelist = {};
|
||||
GatherNodeFaces_r(node->children[0], planefaces);
|
||||
GatherNodeFaces_r(node->children[1], planefaces);
|
||||
}
|
||||
|
|
@ -367,7 +359,7 @@ static int MakeFaceEdges_r(mapentity_t *entity, node_t *node, int progress)
|
|||
if (node->planenum == PLANENUM_LEAF)
|
||||
return progress;
|
||||
|
||||
for (face_t *f = node->faces; f; f = f->next) {
|
||||
for (face_t *f : node->facelist) {
|
||||
FindFaceEdges(entity, f);
|
||||
}
|
||||
|
||||
|
|
@ -443,7 +435,7 @@ static void GrowNodeRegion(mapentity_t *entity, node_t *node)
|
|||
|
||||
node->firstface = static_cast<int>(map.bsp.dfaces.size());
|
||||
|
||||
for (face_t *face = node->faces; face; face = face->next) {
|
||||
for (face_t *face : node->facelist) {
|
||||
Q_assert(face->planenum == node->planenum);
|
||||
|
||||
// emit a region
|
||||
|
|
@ -477,12 +469,10 @@ CountData_r
|
|||
*/
|
||||
static void CountData_r(mapentity_t *entity, node_t *node, size_t &facesCount, size_t &vertexesCount)
|
||||
{
|
||||
face_t *f;
|
||||
|
||||
if (node->planenum == PLANENUM_LEAF)
|
||||
return;
|
||||
|
||||
for (f = node->faces; f; f = f->next) {
|
||||
for (face_t *f : node->facelist) {
|
||||
CountFace(entity, f, facesCount, vertexesCount);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -363,13 +363,12 @@ static void FixFaceEdges(face_t *face)
|
|||
|
||||
static void tjunc_count_r(node_t *node)
|
||||
{
|
||||
face_t *f;
|
||||
|
||||
if (node->planenum == PLANENUM_LEAF)
|
||||
return;
|
||||
|
||||
for (f = node->faces; f; f = f->next)
|
||||
for (face_t *f : node->facelist) {
|
||||
cWVerts += f->w.size();
|
||||
}
|
||||
|
||||
tjunc_count_r(node->children[0]);
|
||||
tjunc_count_r(node->children[1]);
|
||||
|
|
@ -377,13 +376,12 @@ static void tjunc_count_r(node_t *node)
|
|||
|
||||
static void tjunc_find_r(node_t *node)
|
||||
{
|
||||
face_t *f;
|
||||
|
||||
if (node->planenum == PLANENUM_LEAF)
|
||||
return;
|
||||
|
||||
for (f = node->faces; f; f = f->next)
|
||||
for (face_t *f : node->facelist) {
|
||||
AddFaceEdges(f);
|
||||
}
|
||||
|
||||
tjunc_find_r(node->children[0]);
|
||||
tjunc_find_r(node->children[1]);
|
||||
|
|
@ -394,7 +392,7 @@ static void tjunc_fix_r(node_t *node)
|
|||
if (node->planenum == PLANENUM_LEAF)
|
||||
return;
|
||||
|
||||
for (face_t *face = node->faces; face; face = face->next) {
|
||||
for (face_t *face : node->facelist) {
|
||||
FixFaceEdges(face);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue