Instead of calculating brush extents globally, do it per brush creation
Use portal node bounds for extents
This commit is contained in:
parent
3f37de95a5
commit
069720078f
|
|
@ -97,6 +97,7 @@ struct gamedef_q1_like_t : public gamedef_t
|
|||
gamedef_q1_like_t(const char *base_dir = "ID1") : gamedef_t(base_dir)
|
||||
{
|
||||
this->id = ID;
|
||||
hull_extents = 64;
|
||||
}
|
||||
|
||||
bool surf_is_lightmapped(const surfflags_t &flags) const { return !(flags.native & TEX_SPECIAL); }
|
||||
|
|
@ -347,7 +348,7 @@ struct gamedef_q1_like_t : public gamedef_t
|
|||
|
||||
struct gamedef_h2_t : public gamedef_q1_like_t<GAME_HEXEN_II>
|
||||
{
|
||||
gamedef_h2_t() : gamedef_q1_like_t("DATA1") { }
|
||||
gamedef_h2_t() : gamedef_q1_like_t("DATA1") { hull_extents = 40; }
|
||||
|
||||
const std::initializer_list<aabb3d> &get_hull_sizes() const
|
||||
{
|
||||
|
|
@ -402,7 +403,7 @@ struct gamedef_h2_t : public gamedef_q1_like_t<GAME_HEXEN_II>
|
|||
|
||||
struct gamedef_hl_t : public gamedef_q1_like_t<GAME_HALF_LIFE>
|
||||
{
|
||||
gamedef_hl_t() : gamedef_q1_like_t("VALVE") { has_rgb_lightmap = true; }
|
||||
gamedef_hl_t() : gamedef_q1_like_t("VALVE") { has_rgb_lightmap = true; hull_extents = 36; }
|
||||
|
||||
const std::initializer_list<aabb3d> &get_hull_sizes() const
|
||||
{
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ class aabb
|
|||
{
|
||||
public:
|
||||
using value_type = qvec<V, N>;
|
||||
using value_value_type = typename value_type::value_type;
|
||||
|
||||
class intersection_t
|
||||
{
|
||||
|
|
@ -183,6 +184,20 @@ public:
|
|||
|
||||
constexpr value_type centroid() const { return (m_mins + m_maxs) * 0.5; }
|
||||
|
||||
constexpr value_value_type extents() const
|
||||
{
|
||||
value_value_type extent = -std::numeric_limits<value_value_type>::infinity();
|
||||
|
||||
for (auto &v : m_mins) {
|
||||
extent = max(extent, v);
|
||||
}
|
||||
for (auto &v : m_maxs) {
|
||||
extent = max(extent, v);
|
||||
}
|
||||
|
||||
return extent;
|
||||
}
|
||||
|
||||
// stream support
|
||||
auto stream_data() { return std::tie(m_mins, m_maxs); }
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1766,7 +1766,12 @@ struct gamedef_t
|
|||
size_t max_entity_key = 32;
|
||||
size_t max_entity_value = 128;
|
||||
|
||||
gamedef_t(const char *default_base_dir) : default_base_dir(default_base_dir) { }
|
||||
// maximum extent of hulls; used for world extent calculations
|
||||
vec_t hull_extents = 0;
|
||||
|
||||
gamedef_t(const char *default_base_dir) : default_base_dir(default_base_dir)
|
||||
{
|
||||
}
|
||||
|
||||
virtual bool surf_is_lightmapped(const surfflags_t &flags) const = 0;
|
||||
virtual bool surf_is_subdivided(const surfflags_t &flags) const = 0;
|
||||
|
|
|
|||
|
|
@ -24,16 +24,17 @@
|
|||
#include <qbsp/winding.hh>
|
||||
#include <common/aabb.hh>
|
||||
|
||||
class mapbrush_t;
|
||||
|
||||
struct brush_t
|
||||
{
|
||||
const mapbrush_t *src;
|
||||
aabb3d bounds;
|
||||
std::vector<face_t> faces;
|
||||
contentflags_t contents; /* BSP contents */
|
||||
short lmshift; /* lightmap scaling (qu/lightmap pixel), passed to the light util */
|
||||
};
|
||||
|
||||
class mapbrush_t;
|
||||
|
||||
qplane3d Face_Plane(const face_t *face);
|
||||
|
||||
enum class rotation_t
|
||||
|
|
|
|||
|
|
@ -71,6 +71,7 @@ public:
|
|||
int numfaces = 0;
|
||||
brushformat_t format = brushformat_t::NORMAL;
|
||||
int contents = 0;
|
||||
vec_t extents = 0;
|
||||
|
||||
const mapface_t &face(int i) const;
|
||||
};
|
||||
|
|
@ -158,8 +159,6 @@ struct mapdata_t
|
|||
|
||||
extern mapdata_t map;
|
||||
|
||||
void CalculateWorldExtent(void);
|
||||
|
||||
extern mapentity_t *pWorldEnt();
|
||||
|
||||
bool ParseEntity(parser_t &parser, mapentity_t *entity);
|
||||
|
|
|
|||
|
|
@ -24,5 +24,3 @@
|
|||
#include "common/polylib.hh"
|
||||
|
||||
using winding_t = polylib::winding_base_t<MAXEDGES>;
|
||||
|
||||
winding_t BaseWindingForPlane(const qplane3d &p);
|
||||
|
|
@ -91,10 +91,6 @@ static void CheckFace(face_t *face, const mapface_t &sourceface)
|
|||
const qvec3d &p1 = face->w[i];
|
||||
const qvec3d &p2 = face->w[(i + 1) % face->w.size()];
|
||||
|
||||
for (auto &v : p1)
|
||||
if (v > options.worldExtent || v < -options.worldExtent)
|
||||
FError("line {}: coordinate out of range ({})", sourceface.linenum, v);
|
||||
|
||||
/* check the point is on the face plane */
|
||||
vec_t dist = plane.distance_to(p1);
|
||||
if (dist < -ON_EPSILON || dist > ON_EPSILON)
|
||||
|
|
@ -309,7 +305,7 @@ static std::vector<face_t> CreateBrushFaces(const mapentity_t *src, hullbrush_t
|
|||
continue;
|
||||
}
|
||||
|
||||
w = BaseWindingForPlane(mapface.plane);
|
||||
w = winding_t::from_plane(mapface.plane, hullbrush->srcbrush->extents);
|
||||
|
||||
for (auto &mapface2 : hullbrush->faces) {
|
||||
if (&mapface == &mapface2)
|
||||
|
|
@ -706,6 +702,8 @@ std::optional<brush_t> LoadBrush(const mapentity_t *src, const mapbrush_t *mapbr
|
|||
const qvec3d &rotate_offset, const rotation_t rottype, const int hullnum)
|
||||
{
|
||||
hullbrush_t hullbrush;
|
||||
hullbrush.srcbrush = mapbrush;
|
||||
|
||||
std::vector<face_t> facelist;
|
||||
|
||||
// create the faces
|
||||
|
|
|
|||
158
qbsp/map.cc
158
qbsp/map.cc
|
|
@ -40,6 +40,67 @@
|
|||
|
||||
#include <common/qvec.hh>
|
||||
|
||||
/*
|
||||
=================
|
||||
GetBrushExtents
|
||||
=================
|
||||
*/
|
||||
inline std::optional<qvec3d> GetIntersection(const qbsp_plane_t &p1, const qbsp_plane_t &p2, const qbsp_plane_t &p3)
|
||||
{
|
||||
const vec_t denom = qv::dot(p1.normal, qv::cross(p2.normal, p3.normal));
|
||||
|
||||
if (denom == 0.f) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
return (qv::cross(p2.normal, p3.normal) * p1.dist - qv::cross(p3.normal, p1.normal) * -p2.dist - qv::cross(p1.normal, p2.normal) * -p3.dist) / denom;
|
||||
}
|
||||
|
||||
#include "tbb/parallel_for.h"
|
||||
#include <mutex>
|
||||
|
||||
inline vec_t CalculateBrushExtents(const mapbrush_t &hullbrush)
|
||||
{
|
||||
vec_t extents = -std::numeric_limits<vec_t>::infinity();
|
||||
|
||||
for (int32_t i = 0; i < hullbrush.numfaces - 2; i++) {
|
||||
for (int32_t j = i; j < hullbrush.numfaces - 1; j++) {
|
||||
for (int32_t k = j; k < hullbrush.numfaces; k++) {
|
||||
if (i == j || j == k || k == i) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto &fi = hullbrush.face(i);
|
||||
auto &fj = hullbrush.face(j);
|
||||
auto &fk = hullbrush.face(k);
|
||||
|
||||
bool legal = true;
|
||||
auto vertex = GetIntersection(fi.plane, fj.plane, fk.plane);
|
||||
|
||||
if (!vertex) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int32_t m = 0; m < hullbrush.numfaces; m++) {
|
||||
if (hullbrush.face(m).plane.distance_to(*vertex) > NORMAL_EPSILON) {
|
||||
legal = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (legal) {
|
||||
|
||||
for (auto &p : *vertex) {
|
||||
extents = max(extents, fabs(p));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return extents + options.target_game->hull_extents;
|
||||
}
|
||||
|
||||
#define info_player_start 1
|
||||
#define info_player_deathmatch 2
|
||||
#define info_player_coop 4
|
||||
|
|
@ -1573,6 +1634,13 @@ mapbrush_t ParseBrush(parser_t &parser, const mapentity_t *entity)
|
|||
}
|
||||
// ericw -- end brush primitives
|
||||
|
||||
// calculate extents, if required
|
||||
if (!options.worldExtent) {
|
||||
brush.extents = CalculateBrushExtents(brush);
|
||||
} else {
|
||||
brush.extents = options.worldExtent;
|
||||
}
|
||||
|
||||
return brush;
|
||||
}
|
||||
|
||||
|
|
@ -2161,94 +2229,6 @@ void WriteEntitiesToString()
|
|||
}
|
||||
}
|
||||
|
||||
//====================================================================
|
||||
|
||||
inline std::optional<qvec3d> GetIntersection(const qbsp_plane_t &p1, const qbsp_plane_t &p2, const qbsp_plane_t &p3)
|
||||
{
|
||||
const vec_t denom = qv::dot(p1.normal, qv::cross(p2.normal, p3.normal));
|
||||
|
||||
if (denom == 0.f) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
return (qv::cross(p2.normal, p3.normal) * p1.dist - qv::cross(p3.normal, p1.normal) * -p2.dist - qv::cross(p1.normal, p2.normal) * -p3.dist) / denom;
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
GetBrushExtents
|
||||
=================
|
||||
*/
|
||||
inline vec_t GetBrushExtents(const mapbrush_t &hullbrush)
|
||||
{
|
||||
vec_t extents = -std::numeric_limits<vec_t>::infinity();
|
||||
|
||||
for (int32_t i = 0; i < hullbrush.numfaces - 2; i++) {
|
||||
for (int32_t j = i; j < hullbrush.numfaces - 1; j++) {
|
||||
for (int32_t k = j; k < hullbrush.numfaces; k++) {
|
||||
if (i == j || j == k || k == i) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto &fi = hullbrush.face(i);
|
||||
auto &fj = hullbrush.face(j);
|
||||
auto &fk = hullbrush.face(k);
|
||||
|
||||
bool legal = true;
|
||||
auto vertex = GetIntersection(fi.plane, fj.plane, fk.plane);
|
||||
|
||||
if (!vertex) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int32_t m = 0; m < hullbrush.numfaces; m++) {
|
||||
if (hullbrush.face(m).plane.distance_to(*vertex) > NORMAL_EPSILON) {
|
||||
legal = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (legal) {
|
||||
|
||||
for (auto &p : *vertex) {
|
||||
extents = max(extents, fabs(p));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return extents;
|
||||
}
|
||||
|
||||
#include "tbb/parallel_for.h"
|
||||
#include <mutex>
|
||||
|
||||
void CalculateWorldExtent(void)
|
||||
{
|
||||
LogPrint("Calculating world extents... ");
|
||||
|
||||
std::atomic<vec_t> extents = -std::numeric_limits<vec_t>::infinity();
|
||||
|
||||
tbb::parallel_for(static_cast<size_t>(0), static_cast<size_t>(map.numbrushes()), [&](const size_t &i) {
|
||||
const auto &brush = map.brushes[i];
|
||||
const vec_t brushExtents = max(extents.load(), GetBrushExtents(brush));
|
||||
vec_t currentExtents = extents;
|
||||
while (currentExtents < brushExtents && !extents.compare_exchange_weak(currentExtents, brushExtents));
|
||||
});
|
||||
|
||||
vec_t hull_extents = 0;
|
||||
|
||||
for (auto &hull : options.target_game->get_hull_sizes()) {
|
||||
for (auto &v : hull.size()) {
|
||||
hull_extents = max(hull_extents, fabs(v));
|
||||
}
|
||||
}
|
||||
|
||||
options.worldExtent = extents + hull_extents;
|
||||
LogPrint("{} units\n", options.worldExtent);
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
WriteBspBrushMap
|
||||
|
|
@ -2276,7 +2256,7 @@ void WriteBspBrushMap(const std::filesystem::path &name, const std::vector<brush
|
|||
plane = -plane;
|
||||
}
|
||||
|
||||
winding_t w = BaseWindingForPlane(plane);
|
||||
winding_t w = winding_t::from_plane(plane, brush.src->extents);
|
||||
|
||||
fmt::print(f, "( {} ) ", w[0]);
|
||||
fmt::print(f, "( {} ) ", w[1]);
|
||||
|
|
|
|||
|
|
@ -364,6 +364,7 @@ static void MakeHeadnodePortals(const mapentity_t *entity, node_t *node)
|
|||
|
||||
// pad with some space so there will never be null volume leafs
|
||||
aabb3d bounds = entity->bounds.grow(SIDESPACE);
|
||||
vec_t extents = bounds.extents() + options.target_game->hull_extents;
|
||||
|
||||
outside_node.planenum = PLANENUM_LEAF;
|
||||
outside_node.contents = options.target_game->create_solid_contents();
|
||||
|
|
@ -389,7 +390,7 @@ static void MakeHeadnodePortals(const mapentity_t *entity, node_t *node)
|
|||
}
|
||||
p->planenum = FindPlane(pl, &side);
|
||||
|
||||
p->winding = BaseWindingForPlane(pl);
|
||||
p->winding = winding_t::from_plane(pl, extents);
|
||||
if (side)
|
||||
AddPortalToNodes(p, &outside_node, node);
|
||||
else
|
||||
|
|
@ -527,7 +528,7 @@ static void CutNodePortals_r(node_t *node, portal_state_t *state)
|
|||
new_portal = new portal_t{};
|
||||
new_portal->planenum = node->planenum;
|
||||
|
||||
std::optional<winding_t> winding = BaseWindingForPlane(plane);
|
||||
std::optional<winding_t> winding = winding_t::from_plane(plane, node->bounds.extents() + options.target_game->hull_extents);
|
||||
for (portal = node->portals; portal; portal = portal->next[side]) {
|
||||
clipplane = map.planes[portal->planenum];
|
||||
if (portal->nodes[0] == node)
|
||||
|
|
|
|||
10
qbsp/qbsp.cc
10
qbsp/qbsp.cc
|
|
@ -472,11 +472,6 @@ static void EmitAreaPortals(node_t *headnode)
|
|||
LogPrint(LOG_STAT, "{:5} numareaportals\n", map.bsp.dareaportals.size());
|
||||
}
|
||||
|
||||
winding_t BaseWindingForPlane(const qplane3d &p)
|
||||
{
|
||||
return winding_t::from_plane(p, options.worldExtent);
|
||||
}
|
||||
|
||||
/*
|
||||
===============
|
||||
ProcessEntity
|
||||
|
|
@ -990,11 +985,6 @@ static void ProcessFile(void)
|
|||
log_mask &= ~((1 << LOG_STAT) | (1 << LOG_PROGRESS));
|
||||
}
|
||||
|
||||
// calculate extents, if required
|
||||
if (!options.worldExtent) {
|
||||
CalculateWorldExtent();
|
||||
}
|
||||
|
||||
// create hulls!
|
||||
CreateHulls();
|
||||
|
||||
|
|
|
|||
|
|
@ -29,8 +29,6 @@ static mapentity_t LoadMap(const char *map)
|
|||
// FIXME: adds the brush to the global map...
|
||||
Q_assert(ParseEntity(parser, &worldspawn));
|
||||
|
||||
CalculateWorldExtent();
|
||||
|
||||
return worldspawn;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue