Merge branch 'master' into asmjit

This commit is contained in:
Christoph Oelckers 2018-11-09 22:36:16 +01:00
commit fb7345e470
80 changed files with 3825 additions and 1604 deletions

View File

@ -16,7 +16,7 @@ matrix:
- CMAKE_OPTIONS="-DCMAKE_BUILD_TYPE=Debug -DCMAKE_OSX_DEPLOYMENT_TARGET=10.9"
- os: osx
osx_image: xcode10
osx_image: xcode10.1
env:
- CMAKE_OPTIONS="-DCMAKE_BUILD_TYPE=Release -DCMAKE_OSX_DEPLOYMENT_TARGET=10.9"

View File

@ -1049,6 +1049,7 @@ set (PCH_SOURCES
gl/system/gl_buffers.cpp
gl/textures/gl_hwtexture.cpp
gl/textures/gl_samplers.cpp
hwrenderer/data/hw_vertexbuilder.cpp
hwrenderer/data/flatvertices.cpp
hwrenderer/data/hw_viewpointbuffer.cpp
hwrenderer/dynlights/hw_aabbtree.cpp
@ -1141,6 +1142,7 @@ set (PCH_SOURCES
r_data/renderstyle.cpp
r_data/r_interpolate.cpp
r_data/r_vanillatrans.cpp
r_data/r_sections.cpp
r_data/models/models_md3.cpp
r_data/models/models_md2.cpp
r_data/models/models_voxel.cpp

View File

@ -53,6 +53,7 @@ struct FBlockNode;
struct FPortalGroupArray;
struct visstyle_t;
class FLightDefaults;
struct FSection;
//
// NOTES: AActor
//
@ -1033,6 +1034,10 @@ public:
void AttachLight(unsigned int count, const FLightDefaults *lightdef);
void SetDynamicLights();
// When was this actor spawned? (relative to the current level)
int GetLevelSpawnTime() const;
// How many ticks passed since this actor was spawned?
int GetAge() const;
// info for drawing
// NOTE: The first member variable *must* be snext.
@ -1083,6 +1088,7 @@ public:
FBlockNode *BlockNode; // links in blocks (if needed)
struct sector_t *Sector;
subsector_t * subsector;
FSection * section;
double floorz, ceilingz; // closest together of contacted secs
double dropoffz; // killough 11/98: the lowest floor over all contacted Sectors.
@ -1182,6 +1188,7 @@ public:
AActor *BlockingMobj; // Actor that blocked the last move
line_t *BlockingLine; // Line that blocked the last move
sector_t *Blocking3DFloor; // 3D floor that blocked the last move (if any)
sector_t *BlockingCeiling; // Sector that blocked the last move (ceiling plane slope)
sector_t *BlockingFloor; // Sector that blocked the last move (floor plane slope)
@ -1262,6 +1269,9 @@ public:
int PrevPortalGroup;
TArray<TObjPtr<AActor*> > AttachedLights;
// When was this actor spawned?
int SpawnTime;
// ThingIDs
static void ClearTIDHashes ();
void AddToHash ();

View File

@ -2072,7 +2072,7 @@ void AM_drawSubsectors()
continue;
}
if ((!(subsectors[i].flags & SSECMF_DRAWN) || (subsectors[i].render_sector->MoreFlags & SECMF_HIDDEN)) && am_cheat == 0)
if ((!(subsectors[i].flags & SSECMF_DRAWN) || (subsectors[i].flags & SSECF_HOLE) || (subsectors[i].render_sector->MoreFlags & SECMF_HIDDEN)) && am_cheat == 0)
{
continue;
}

790
src/earcut.hpp Normal file
View File

@ -0,0 +1,790 @@
/*
ISC License
Copyright (c) 2015, Mapbox
Permission to use, copy, modify, and/or distribute this software for any purpose
with or without fee is hereby granted, provided that the above copyright notice
and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
THIS SOFTWARE.
*/
#pragma once
#include <algorithm>
#include <cassert>
#include <cmath>
#include <memory>
#include <vector>
namespace mapbox {
namespace util {
template <std::size_t I, typename T> struct nth {
inline static typename std::tuple_element<I, T>::type
get(const T& t) { return std::get<I>(t); };
};
}
namespace detail {
template <typename N = uint32_t>
class Earcut {
public:
std::vector<N> indices;
std::size_t vertices = 0;
template <typename Polygon>
void operator()(const Polygon& points);
private:
struct Node {
Node(N index, double x_, double y_) : i(index), x(x_), y(y_) {}
Node(const Node&) = delete;
Node& operator=(const Node&) = delete;
Node(Node&&) = delete;
Node& operator=(Node&&) = delete;
const N i;
const double x;
const double y;
// previous and next vertice nodes in a polygon ring
Node* prev = nullptr;
Node* next = nullptr;
// z-order curve value
int32_t z = 0;
// previous and next nodes in z-order
Node* prevZ = nullptr;
Node* nextZ = nullptr;
// indicates whether this is a steiner point
bool steiner = false;
};
template <typename Ring> Node* linkedList(const Ring& points, const bool clockwise);
Node* filterPoints(Node* start, Node* end = nullptr);
void earcutLinked(Node* ear, int pass = 0);
bool isEar(Node* ear);
bool isEarHashed(Node* ear);
Node* cureLocalIntersections(Node* start);
void splitEarcut(Node* start);
template <typename Polygon> Node* eliminateHoles(const Polygon& points, Node* outerNode);
void eliminateHole(Node* hole, Node* outerNode);
Node* findHoleBridge(Node* hole, Node* outerNode);
void indexCurve(Node* start);
Node* sortLinked(Node* list);
int32_t zOrder(const double x_, const double y_);
Node* getLeftmost(Node* start);
bool pointInTriangle(double ax, double ay, double bx, double by, double cx, double cy, double px, double py) const;
bool isValidDiagonal(Node* a, Node* b);
double area(const Node* p, const Node* q, const Node* r) const;
bool equals(const Node* p1, const Node* p2);
bool intersects(const Node* p1, const Node* q1, const Node* p2, const Node* q2);
bool intersectsPolygon(const Node* a, const Node* b);
bool locallyInside(const Node* a, const Node* b);
bool middleInside(const Node* a, const Node* b);
Node* splitPolygon(Node* a, Node* b);
template <typename Point> Node* insertNode(std::size_t i, const Point& p, Node* last);
void removeNode(Node* p);
bool hashing;
double minX, maxX;
double minY, maxY;
double inv_size = 0;
template <typename T, typename Alloc = std::allocator<T>>
class ObjectPool {
public:
ObjectPool() { }
ObjectPool(std::size_t blockSize_) {
reset(blockSize_);
}
~ObjectPool() {
clear();
}
template <typename... Args>
T* construct(Args&&... args) {
if (currentIndex >= blockSize) {
currentBlock = alloc.allocate(blockSize);
allocations.emplace_back(currentBlock);
currentIndex = 0;
}
T* object = &currentBlock[currentIndex++];
alloc.construct(object, std::forward<Args>(args)...);
return object;
}
void reset(std::size_t newBlockSize) {
for (auto allocation : allocations) alloc.deallocate(allocation, blockSize);
allocations.clear();
blockSize = std::max<std::size_t>(1, newBlockSize);
currentBlock = nullptr;
currentIndex = blockSize;
}
void clear() { reset(blockSize); }
private:
T* currentBlock = nullptr;
std::size_t currentIndex = 1;
std::size_t blockSize = 1;
std::vector<T*> allocations;
Alloc alloc;
};
ObjectPool<Node> nodes;
};
template <typename N> template <typename Polygon>
void Earcut<N>::operator()(const Polygon& points) {
// reset
indices.clear();
vertices = 0;
if (points.empty()) return;
double x;
double y;
int threshold = 80;
std::size_t len = 0;
for (size_t i = 0; threshold >= 0 && i < points.size(); i++) {
threshold -= static_cast<int>(points[i].size());
len += points[i].size();
}
//estimate size of nodes and indices
nodes.reset(len * 3 / 2);
indices.reserve(len + points[0].size());
Node* outerNode = linkedList(points[0], true);
if (!outerNode) return;
if (points.size() > 1) outerNode = eliminateHoles(points, outerNode);
// if the shape is not too simple, we'll use z-order curve hash later; calculate polygon bbox
hashing = threshold < 0;
if (hashing) {
Node* p = outerNode->next;
minX = maxX = outerNode->x;
minY = maxY = outerNode->y;
do {
x = p->x;
y = p->y;
minX = std::min<double>(minX, x);
minY = std::min<double>(minY, y);
maxX = std::max<double>(maxX, x);
maxY = std::max<double>(maxY, y);
p = p->next;
} while (p != outerNode);
// minX, minY and size are later used to transform coords into integers for z-order calculation
inv_size = std::max<double>(maxX - minX, maxY - minY);
inv_size = inv_size != .0 ? (1. / inv_size) : .0;
}
earcutLinked(outerNode);
nodes.clear();
}
// create a circular doubly linked list from polygon points in the specified winding order
template <typename N> template <typename Ring>
typename Earcut<N>::Node*
Earcut<N>::linkedList(const Ring& points, const bool clockwise) {
using Point = typename Ring::value_type;
double sum = 0;
const std::size_t len = points.size();
std::size_t i, j;
Node* last = nullptr;
// calculate original winding order of a polygon ring
for (i = 0, j = len > 0 ? len - 1 : 0; i < len; j = i++) {
const auto& p1 = points[i];
const auto& p2 = points[j];
const double p20 = util::nth<0, Point>::get(p2);
const double p10 = util::nth<0, Point>::get(p1);
const double p11 = util::nth<1, Point>::get(p1);
const double p21 = util::nth<1, Point>::get(p2);
sum += (p20 - p10) * (p11 + p21);
}
// link points into circular doubly-linked list in the specified winding order
if (clockwise == (sum > 0)) {
for (i = 0; i < len; i++) last = insertNode(vertices + i, points[i], last);
} else {
for (i = len; i-- > 0;) last = insertNode(vertices + i, points[i], last);
}
if (last && equals(last, last->next)) {
removeNode(last);
last = last->next;
}
vertices += len;
return last;
}
// eliminate colinear or duplicate points
template <typename N>
typename Earcut<N>::Node*
Earcut<N>::filterPoints(Node* start, Node* end) {
if (!end) end = start;
Node* p = start;
bool again;
do {
again = false;
if (!p->steiner && (equals(p, p->next) /*|| area(p->prev, p, p->next) == 0*/))
{
removeNode(p);
p = end = p->prev;
if (p == p->next) break;
again = true;
} else {
p = p->next;
}
} while (again || p != end);
return end;
}
// main ear slicing loop which triangulates a polygon (given as a linked list)
template <typename N>
void Earcut<N>::earcutLinked(Node* ear, int pass) {
if (!ear) return;
// interlink polygon nodes in z-order
if (!pass && hashing) indexCurve(ear);
Node* stop = ear;
Node* prev;
Node* next;
int iterations = 0;
// iterate through ears, slicing them one by one
while (ear->prev != ear->next) {
iterations++;
prev = ear->prev;
next = ear->next;
if (hashing ? isEarHashed(ear) : isEar(ear)) {
// cut off the triangle
indices.emplace_back(prev->i);
indices.emplace_back(ear->i);
indices.emplace_back(next->i);
removeNode(ear);
// skipping the next vertice leads to less sliver triangles
ear = next->next;
stop = next->next;
continue;
}
ear = next;
// if we looped through the whole remaining polygon and can't find any more ears
if (ear == stop) {
// try filtering points and slicing again
if (!pass) earcutLinked(filterPoints(ear), 1);
// if this didn't work, try curing all small self-intersections locally
else if (pass == 1) {
ear = cureLocalIntersections(ear);
earcutLinked(ear, 2);
// as a last resort, try splitting the remaining polygon into two
} else if (pass == 2) splitEarcut(ear);
break;
}
}
}
// check whether a polygon node forms a valid ear with adjacent nodes
template <typename N>
bool Earcut<N>::isEar(Node* ear) {
const Node* a = ear->prev;
const Node* b = ear;
const Node* c = ear->next;
if (area(a, b, c) >= 0) return false; // reflex, can't be an ear
// now make sure we don't have other points inside the potential ear
Node* p = ear->next->next;
while (p != ear->prev) {
if (pointInTriangle(a->x, a->y, b->x, b->y, c->x, c->y, p->x, p->y) &&
area(p->prev, p, p->next) >= 0) return false;
p = p->next;
}
return true;
}
template <typename N>
bool Earcut<N>::isEarHashed(Node* ear) {
const Node* a = ear->prev;
const Node* b = ear;
const Node* c = ear->next;
if (area(a, b, c) >= 0) return false; // reflex, can't be an ear
// triangle bbox; min & max are calculated like this for speed
const double minTX = std::min<double>(a->x, std::min<double>(b->x, c->x));
const double minTY = std::min<double>(a->y, std::min<double>(b->y, c->y));
const double maxTX = std::max<double>(a->x, std::max<double>(b->x, c->x));
const double maxTY = std::max<double>(a->y, std::max<double>(b->y, c->y));
// z-order range for the current triangle bbox;
const int32_t minZ = zOrder(minTX, minTY);
const int32_t maxZ = zOrder(maxTX, maxTY);
// first look for points inside the triangle in increasing z-order
Node* p = ear->nextZ;
while (p && p->z <= maxZ) {
if (p != ear->prev && p != ear->next &&
pointInTriangle(a->x, a->y, b->x, b->y, c->x, c->y, p->x, p->y) &&
area(p->prev, p, p->next) >= 0) return false;
p = p->nextZ;
}
// then look for points in decreasing z-order
p = ear->prevZ;
while (p && p->z >= minZ) {
if (p != ear->prev && p != ear->next &&
pointInTriangle(a->x, a->y, b->x, b->y, c->x, c->y, p->x, p->y) &&
area(p->prev, p, p->next) >= 0) return false;
p = p->prevZ;
}
return true;
}
// go through all polygon nodes and cure small local self-intersections
template <typename N>
typename Earcut<N>::Node*
Earcut<N>::cureLocalIntersections(Node* start) {
Node* p = start;
do {
Node* a = p->prev;
Node* b = p->next->next;
// a self-intersection where edge (v[i-1],v[i]) intersects (v[i+1],v[i+2])
if (!equals(a, b) && intersects(a, p, p->next, b) && locallyInside(a, b) && locallyInside(b, a)) {
indices.emplace_back(a->i);
indices.emplace_back(p->i);
indices.emplace_back(b->i);
// remove two nodes involved
removeNode(p);
removeNode(p->next);
p = start = b;
}
p = p->next;
} while (p != start);
return p;
}
// try splitting polygon into two and triangulate them independently
template <typename N>
void Earcut<N>::splitEarcut(Node* start) {
// look for a valid diagonal that divides the polygon into two
Node* a = start;
do {
Node* b = a->next->next;
while (b != a->prev) {
if (a->i != b->i && isValidDiagonal(a, b)) {
// split the polygon in two by the diagonal
Node* c = splitPolygon(a, b);
// filter colinear points around the cuts
a = filterPoints(a, a->next);
c = filterPoints(c, c->next);
// run earcut on each half
earcutLinked(a);
earcutLinked(c);
return;
}
b = b->next;
}
a = a->next;
} while (a != start);
}
// link every hole into the outer loop, producing a single-ring polygon without holes
template <typename N> template <typename Polygon>
typename Earcut<N>::Node*
Earcut<N>::eliminateHoles(const Polygon& points, Node* outerNode) {
const size_t len = points.size();
std::vector<Node*> queue;
for (size_t i = 1; i < len; i++) {
Node* list = linkedList(points[i], false);
if (list) {
if (list == list->next) list->steiner = true;
queue.push_back(getLeftmost(list));
}
}
std::sort(queue.begin(), queue.end(), [](const Node* a, const Node* b) {
return a->x < b->x;
});
// process holes from left to right
for (size_t i = 0; i < queue.size(); i++) {
eliminateHole(queue[i], outerNode);
outerNode = filterPoints(outerNode, outerNode->next);
}
return outerNode;
}
// find a bridge between vertices that connects hole with an outer ring and and link it
template <typename N>
void Earcut<N>::eliminateHole(Node* hole, Node* outerNode) {
outerNode = findHoleBridge(hole, outerNode);
if (outerNode) {
Node* b = splitPolygon(outerNode, hole);
filterPoints(b, b->next);
}
}
// David Eberly's algorithm for finding a bridge between hole and outer polygon
template <typename N>
typename Earcut<N>::Node*
Earcut<N>::findHoleBridge(Node* hole, Node* outerNode) {
Node* p = outerNode;
double hx = hole->x;
double hy = hole->y;
double qx = -std::numeric_limits<double>::infinity();
Node* m = nullptr;
// find a segment intersected by a ray from the hole's leftmost Vertex to the left;
// segment's endpoint with lesser x will be potential connection Vertex
do {
if (hy <= p->y && hy >= p->next->y && p->next->y != p->y) {
double x = p->x + (hy - p->y) * (p->next->x - p->x) / (p->next->y - p->y);
if (x <= hx && x > qx) {
qx = x;
if (x == hx) {
if (hy == p->y) return p;
if (hy == p->next->y) return p->next;
}
m = p->x < p->next->x ? p : p->next;
}
}
p = p->next;
} while (p != outerNode);
if (!m) return 0;
if (hx == qx) return m->prev;
// look for points inside the triangle of hole Vertex, segment intersection and endpoint;
// if there are no points found, we have a valid connection;
// otherwise choose the Vertex of the minimum angle with the ray as connection Vertex
const Node* stop = m;
double tanMin = std::numeric_limits<double>::infinity();
double tanCur = 0;
p = m->next;
double mx = m->x;
double my = m->y;
while (p != stop) {
if (hx >= p->x && p->x >= mx && hx != p->x &&
pointInTriangle(hy < my ? hx : qx, hy, mx, my, hy < my ? qx : hx, hy, p->x, p->y)) {
tanCur = std::abs(hy - p->y) / (hx - p->x); // tangential
if ((tanCur < tanMin || (tanCur == tanMin && p->x > m->x)) && locallyInside(p, hole)) {
m = p;
tanMin = tanCur;
}
}
p = p->next;
}
return m;
}
// interlink polygon nodes in z-order
template <typename N>
void Earcut<N>::indexCurve(Node* start) {
assert(start);
Node* p = start;
do {
p->z = p->z ? p->z : zOrder(p->x, p->y);
p->prevZ = p->prev;
p->nextZ = p->next;
p = p->next;
} while (p != start);
p->prevZ->nextZ = nullptr;
p->prevZ = nullptr;
sortLinked(p);
}
// Simon Tatham's linked list merge sort algorithm
// http://www.chiark.greenend.org.uk/~sgtatham/algorithms/listsort.html
template <typename N>
typename Earcut<N>::Node*
Earcut<N>::sortLinked(Node* list) {
assert(list);
Node* p;
Node* q;
Node* e;
Node* tail;
int i, numMerges, pSize, qSize;
int inSize = 1;
for (;;) {
p = list;
list = nullptr;
tail = nullptr;
numMerges = 0;
while (p) {
numMerges++;
q = p;
pSize = 0;
for (i = 0; i < inSize; i++) {
pSize++;
q = q->nextZ;
if (!q) break;
}
qSize = inSize;
while (pSize > 0 || (qSize > 0 && q)) {
if (pSize == 0) {
e = q;
q = q->nextZ;
qSize--;
} else if (qSize == 0 || !q) {
e = p;
p = p->nextZ;
pSize--;
} else if (p->z <= q->z) {
e = p;
p = p->nextZ;
pSize--;
} else {
e = q;
q = q->nextZ;
qSize--;
}
if (tail) tail->nextZ = e;
else list = e;
e->prevZ = tail;
tail = e;
}
p = q;
}
tail->nextZ = nullptr;
if (numMerges <= 1) return list;
inSize *= 2;
}
}
// z-order of a Vertex given coords and size of the data bounding box
template <typename N>
int32_t Earcut<N>::zOrder(const double x_, const double y_) {
// coords are transformed into non-negative 15-bit integer range
int32_t x = static_cast<int32_t>(32767.0 * (x_ - minX) * inv_size);
int32_t y = static_cast<int32_t>(32767.0 * (y_ - minY) * inv_size);
x = (x | (x << 8)) & 0x00FF00FF;
x = (x | (x << 4)) & 0x0F0F0F0F;
x = (x | (x << 2)) & 0x33333333;
x = (x | (x << 1)) & 0x55555555;
y = (y | (y << 8)) & 0x00FF00FF;
y = (y | (y << 4)) & 0x0F0F0F0F;
y = (y | (y << 2)) & 0x33333333;
y = (y | (y << 1)) & 0x55555555;
return x | (y << 1);
}
// find the leftmost node of a polygon ring
template <typename N>
typename Earcut<N>::Node*
Earcut<N>::getLeftmost(Node* start) {
Node* p = start;
Node* leftmost = start;
do {
if (p->x < leftmost->x) leftmost = p;
p = p->next;
} while (p != start);
return leftmost;
}
// check if a point lies within a convex triangle
template <typename N>
bool Earcut<N>::pointInTriangle(double ax, double ay, double bx, double by, double cx, double cy, double px, double py) const {
return (cx - px) * (ay - py) - (ax - px) * (cy - py) >= 0 &&
(ax - px) * (by - py) - (bx - px) * (ay - py) >= 0 &&
(bx - px) * (cy - py) - (cx - px) * (by - py) >= 0;
}
// check if a diagonal between two polygon nodes is valid (lies in polygon interior)
template <typename N>
bool Earcut<N>::isValidDiagonal(Node* a, Node* b) {
return a->next->i != b->i && a->prev->i != b->i && !intersectsPolygon(a, b) &&
locallyInside(a, b) && locallyInside(b, a) && middleInside(a, b);
}
// signed area of a triangle
template <typename N>
double Earcut<N>::area(const Node* p, const Node* q, const Node* r) const {
return (q->y - p->y) * (r->x - q->x) - (q->x - p->x) * (r->y - q->y);
}
// check if two points are equal
template <typename N>
bool Earcut<N>::equals(const Node* p1, const Node* p2) {
return p1->x == p2->x && p1->y == p2->y;
}
// check if two segments intersect
template <typename N>
bool Earcut<N>::intersects(const Node* p1, const Node* q1, const Node* p2, const Node* q2) {
if ((equals(p1, q1) && equals(p2, q2)) ||
(equals(p1, q2) && equals(p2, q1))) return true;
return (area(p1, q1, p2) > 0) != (area(p1, q1, q2) > 0) &&
(area(p2, q2, p1) > 0) != (area(p2, q2, q1) > 0);
}
// check if a polygon diagonal intersects any polygon segments
template <typename N>
bool Earcut<N>::intersectsPolygon(const Node* a, const Node* b) {
const Node* p = a;
do {
if (p->i != a->i && p->next->i != a->i && p->i != b->i && p->next->i != b->i &&
intersects(p, p->next, a, b)) return true;
p = p->next;
} while (p != a);
return false;
}
// check if a polygon diagonal is locally inside the polygon
template <typename N>
bool Earcut<N>::locallyInside(const Node* a, const Node* b) {
return area(a->prev, a, a->next) < 0 ?
area(a, b, a->next) >= 0 && area(a, a->prev, b) >= 0 :
area(a, b, a->prev) < 0 || area(a, a->next, b) < 0;
}
// check if the middle Vertex of a polygon diagonal is inside the polygon
template <typename N>
bool Earcut<N>::middleInside(const Node* a, const Node* b) {
const Node* p = a;
bool inside = false;
double px = (a->x + b->x) / 2;
double py = (a->y + b->y) / 2;
do {
if (((p->y > py) != (p->next->y > py)) && p->next->y != p->y &&
(px < (p->next->x - p->x) * (py - p->y) / (p->next->y - p->y) + p->x))
inside = !inside;
p = p->next;
} while (p != a);
return inside;
}
// link two polygon vertices with a bridge; if the vertices belong to the same ring, it splits
// polygon into two; if one belongs to the outer ring and another to a hole, it merges it into a
// single ring
template <typename N>
typename Earcut<N>::Node*
Earcut<N>::splitPolygon(Node* a, Node* b) {
Node* a2 = nodes.construct(a->i, a->x, a->y);
Node* b2 = nodes.construct(b->i, b->x, b->y);
Node* an = a->next;
Node* bp = b->prev;
a->next = b;
b->prev = a;
a2->next = an;
an->prev = a2;
b2->next = a2;
a2->prev = b2;
bp->next = b2;
b2->prev = bp;
return b2;
}
// create a node and util::optionally link it with previous one (in a circular doubly linked list)
template <typename N> template <typename Point>
typename Earcut<N>::Node*
Earcut<N>::insertNode(std::size_t i, const Point& pt, Node* last) {
Node* p = nodes.construct(static_cast<N>(i), util::nth<0, Point>::get(pt), util::nth<1, Point>::get(pt));
if (!last) {
p->prev = p;
p->next = p;
} else {
assert(last);
p->next = last->next;
p->prev = last;
last->next->prev = p;
last->next = p;
}
return p;
}
template <typename N>
void Earcut<N>::removeNode(Node* p) {
p->next->prev = p->prev;
p->prev->next = p->next;
if (p->prevZ) p->prevZ->nextZ = p->nextZ;
if (p->nextZ) p->nextZ->prevZ = p->prevZ;
}
}
template <typename N = uint32_t, typename Polygon>
std::vector<N> earcut(const Polygon& poly) {
mapbox::detail::Earcut<N> earcut;
earcut(poly);
return std::move(earcut.indices);
}
}

View File

@ -416,6 +416,20 @@ void E_WorldLineActivated(line_t* line, AActor* actor, int activationType)
handler->WorldLineActivated(line, actor, activationType);
}
int E_WorldSectorDamaged(sector_t* sector, AActor* source, int damage, FName damagetype, int part, DVector3 position, bool isradius)
{
for (DStaticEventHandler* handler = E_FirstEventHandler; handler; handler = handler->next)
damage = handler->WorldSectorDamaged(sector, source, damage, damagetype, part, position, isradius);
return damage;
}
int E_WorldLineDamaged(line_t* line, AActor* source, int damage, FName damagetype, int side, DVector3 position, bool isradius)
{
for (DStaticEventHandler* handler = E_FirstEventHandler; handler; handler = handler->next)
damage = handler->WorldLineDamaged(line, source, damage, damagetype, side, position, isradius);
return damage;
}
void E_PlayerEntered(int num, bool fromhub)
{
// this event can happen during savegamerestore. make sure that local handlers don't receive it.
@ -570,6 +584,13 @@ DEFINE_FIELD_X(WorldEvent, FWorldEvent, DamageAngle);
DEFINE_FIELD_X(WorldEvent, FWorldEvent, ActivatedLine);
DEFINE_FIELD_X(WorldEvent, FWorldEvent, ActivationType);
DEFINE_FIELD_X(WorldEvent, FWorldEvent, ShouldActivate);
DEFINE_FIELD_X(WorldEvent, FWorldEvent, DamageSectorPart);
DEFINE_FIELD_X(WorldEvent, FWorldEvent, DamageLine);
DEFINE_FIELD_X(WorldEvent, FWorldEvent, DamageSector);
DEFINE_FIELD_X(WorldEvent, FWorldEvent, DamageLineSide);
DEFINE_FIELD_X(WorldEvent, FWorldEvent, DamagePosition);
DEFINE_FIELD_X(WorldEvent, FWorldEvent, DamageIsRadius);
DEFINE_FIELD_X(WorldEvent, FWorldEvent, NewDamage);
DEFINE_FIELD_X(PlayerEvent, FPlayerEvent, PlayerNumber);
DEFINE_FIELD_X(PlayerEvent, FPlayerEvent, IsReturn);
@ -662,6 +683,8 @@ DEFINE_EMPTY_HANDLER(DStaticEventHandler, WorldThingDamaged)
DEFINE_EMPTY_HANDLER(DStaticEventHandler, WorldThingDestroyed)
DEFINE_EMPTY_HANDLER(DStaticEventHandler, WorldLinePreActivated)
DEFINE_EMPTY_HANDLER(DStaticEventHandler, WorldLineActivated)
DEFINE_EMPTY_HANDLER(DStaticEventHandler, WorldSectorDamaged);
DEFINE_EMPTY_HANDLER(DStaticEventHandler, WorldLineDamaged);
DEFINE_EMPTY_HANDLER(DStaticEventHandler, WorldLightning)
DEFINE_EMPTY_HANDLER(DStaticEventHandler, WorldTick)
@ -861,6 +884,54 @@ void DStaticEventHandler::WorldLineActivated(line_t* line, AActor* actor, int ac
}
}
int DStaticEventHandler::WorldSectorDamaged(sector_t* sector, AActor* source, int damage, FName damagetype, int part, DVector3 position, bool isradius)
{
IFVIRTUAL(DStaticEventHandler, WorldSectorDamaged)
{
// don't create excessive DObjects if not going to be processed anyway
if (func == DStaticEventHandler_WorldSectorDamaged_VMPtr)
return damage;
FWorldEvent e = E_SetupWorldEvent();
e.DamageSource = source;
e.DamageSector = sector;
e.NewDamage = e.Damage = damage;
e.DamageType = damagetype;
e.DamageSectorPart = part;
e.DamagePosition = position;
e.DamageIsRadius = isradius;
VMValue params[2] = { (DStaticEventHandler*)this, &e };
VMCall(func, params, 2, nullptr, 0);
return e.NewDamage;
}
return damage;
}
int DStaticEventHandler::WorldLineDamaged(line_t* line, AActor* source, int damage, FName damagetype, int side, DVector3 position, bool isradius)
{
IFVIRTUAL(DStaticEventHandler, WorldLineDamaged)
{
// don't create excessive DObjects if not going to be processed anyway
if (func == DStaticEventHandler_WorldLineDamaged_VMPtr)
return damage;
FWorldEvent e = E_SetupWorldEvent();
e.DamageSource = source;
e.DamageLine = line;
e.NewDamage = e.Damage = damage;
e.DamageType = damagetype;
e.DamageLineSide = side;
e.DamagePosition = position;
e.DamageIsRadius = isradius;
VMValue params[2] = { (DStaticEventHandler*)this, &e };
VMCall(func, params, 2, nullptr, 0);
return e.NewDamage;
}
return damage;
}
void DStaticEventHandler::WorldLightning()
{
IFVIRTUAL(DStaticEventHandler, WorldLightning)

View File

@ -50,6 +50,10 @@ void E_WorldThingDestroyed(AActor* actor);
void E_WorldLinePreActivated(line_t* line, AActor* actor, int activationType, bool* shouldactivate);
// called in P_ActivateLine after successful special execution.
void E_WorldLineActivated(line_t* line, AActor* actor, int activationType);
// called in P_DamageSector and P_DamageLinedef before receiving damage to the sector. returns actual damage
int E_WorldSectorDamaged(sector_t* sector, AActor* source, int damage, FName damagetype, int part, DVector3 position, bool isradius);
// called in P_DamageLinedef before receiving damage to the linedef. returns actual damage
int E_WorldLineDamaged(line_t* line, AActor* source, int damage, FName damagetype, int side, DVector3 position, bool isradius);
// same as ACS SCRIPT_Lightning
void E_WorldLightning();
// this executes on every tick, before everything, only when in valid level and not paused
@ -157,6 +161,8 @@ public:
void WorldThingDestroyed(AActor* actor);
void WorldLinePreActivated(line_t* line, AActor* actor, int activationType, bool* shouldactivate);
void WorldLineActivated(line_t* line, AActor* actor, int activationType);
int WorldSectorDamaged(sector_t* sector, AActor* source, int damage, FName damagetype, int part, DVector3 position, bool isradius);
int WorldLineDamaged(line_t* line, AActor* source, int damage, FName damagetype, int side, DVector3 position, bool isradius);
void WorldLightning();
void WorldTick();
@ -215,14 +221,22 @@ struct FWorldEvent
AActor* Thing = nullptr; // for thingdied
AActor* Inflictor = nullptr; // can be null - for damagemobj
AActor* DamageSource = nullptr; // can be null
int Damage = 0;
FName DamageType = NAME_None;
int DamageFlags = 0;
DAngle DamageAngle;
int Damage = 0; // thingdamaged, sector/line damaged
FName DamageType = NAME_None; // thingdamaged, sector/line damaged
int DamageFlags = 0; // thingdamaged
DAngle DamageAngle; // thingdamaged
// for line(pre)activated
line_t* ActivatedLine = nullptr;
int ActivationType = 0;
bool ShouldActivate = true;
// for line/sector damaged
int DamageSectorPart = 0;
line_t* DamageLine = nullptr;
sector_t* DamageSector = nullptr;
int DamageLineSide = -1;
DVector3 DamagePosition;
bool DamageIsRadius; // radius damage yes/no
int NewDamage = 0; // sector/line damaged. allows modifying damage
};
struct FPlayerEvent

View File

@ -184,6 +184,14 @@ public:
return mReader->Read(buffer, (long)len);
}
TArray<uint8_t> Read()
{
TArray<uint8_t> buffer(mReader->Length, true);
Size length = mReader->Read(&buffer[0], mReader->Length);
if (length < mReader->Length) buffer.Clear();
return buffer;
}
char *Gets(char *strbuf, Size len)
{
return mReader->Gets(strbuf, (int)len);

View File

@ -85,6 +85,7 @@
#include "g_levellocals.h"
#include "actorinlines.h"
#include "i_time.h"
#include "p_maputl.h"
void STAT_StartNewGame(const char *lev);
void STAT_ChangeLevel(const char *newl);
@ -2004,10 +2005,48 @@ void FLevelLocals::SetMusicVolume(float f)
}
//==========================================================================
// IsPointInMap
//
//
// Checks to see if a point is inside the void or not.
// Made by dpJudas, modified and implemented by Major Cooke
//==========================================================================
bool IsPointInMap(DVector3 p)
{
subsector_t *subsector = R_PointInSubsector(FLOAT2FIXED(p.X), FLOAT2FIXED(p.Y));
if (!subsector) return false;
for (uint32_t i = 0; i < subsector->numlines; i++)
{
// Skip single sided lines.
seg_t *seg = subsector->firstline + i;
if (seg->backsector != nullptr) continue;
divline_t dline;
P_MakeDivline(seg->linedef, &dline);
bool pol = P_PointOnDivlineSide(p.XY(), &dline) < 1;
if (!pol) return false;
}
double ceilingZ = subsector->sector->ceilingplane.ZatPoint(p.X, p.Y);
if (p.Z > ceilingZ) return false;
double floorZ = subsector->sector->floorplane.ZatPoint(p.X, p.Y);
if (p.Z < floorZ) return false;
return true;
}
DEFINE_ACTION_FUNCTION(FLevelLocals, IsPointInMap)
{
PARAM_PROLOGUE;
PARAM_FLOAT(x);
PARAM_FLOAT(y);
PARAM_FLOAT(z);
ACTION_RETURN_BOOL(IsPointInMap(DVector3(x,y,z)));
}
template <typename T>
inline T VecDiff(const T& v1, const T& v2)
{

View File

@ -42,6 +42,7 @@
#include "p_blockmap.h"
#include "p_local.h"
#include "p_destructible.h"
#include "r_data/r_sections.h"
struct FLevelLocals
{
@ -94,6 +95,8 @@ struct FLevelLocals
TArray<FLinePortal*> linkedPortals; // only the linked portals, this is used to speed up looking for them in P_CollectConnectedGroups.
TArray<FSectorPortalGroup *> portalGroups;
TArray<FLinePortalSpan> linePortalSpans;
FSectionContainer sections;
int NumMapSections;
TArray<zone_t> Zones;

View File

@ -80,7 +80,7 @@ DEFINE_CLASS_PROPERTY(type, S, DynamicLight)
{
PROP_STRING_PARM(str, 0);
static const char * ltype_names[]={
"Point","Pulse","Flicker","Sector","RandomFlicker", "ColorPulse", "ColorFlicker", "RandomColorFlicker", NULL};
"Point","Pulse","Flicker","Sector","RandomFlicker", "ColorPulse", "ColorFlicker", "RandomColorFlicker", nullptr};
static const int ltype_values[]={
PointLight, PulseLight, FlickerLight, SectorLight, RandomFlickerLight, ColorPulseLight, ColorFlickerLight, RandomColorFlickerLight };
@ -182,7 +182,7 @@ void ADynamicLight::PostBeginPlay()
if (!(SpawnFlags & MTF_DORMANT))
{
Activate (NULL);
Activate (nullptr);
}
subsector = R_PointInSubsector(Pos());
@ -324,7 +324,7 @@ void ADynamicLight::Tick()
if (scale == 0.f) scale = 1.f;
intensity = Sector->lightlevel * scale;
intensity = clamp<float>(intensity, 0.f, 1024.f);
intensity = clamp<float>(intensity, 0.f, 255.f);
m_currentRadius = intensity;
break;
@ -430,14 +430,14 @@ void ADynamicLight::SetOffset(const DVector3 &pos)
//==========================================================================
//
// The target pointer in dynamic lights should never be substituted unless
// notOld is NULL (which indicates that the object was destroyed by force.)
// notOld is nullptr (which indicates that the object was destroyed by force.)
//
//==========================================================================
size_t ADynamicLight::PointerSubstitution (DObject *old, DObject *notOld)
{
AActor *saved_target = target;
size_t ret = Super::PointerSubstitution(old, notOld);
if (notOld != NULL) target = saved_target;
if (notOld != nullptr) target = saved_target;
return ret;
}
@ -494,7 +494,7 @@ FLightNode * AddLightNode(FLightNode ** thread, void * linkto, ADynamicLight * l
//
// P_DelSecnode() deletes a sector node from the list of
// sectors this object appears in. Returns a pointer to the next node
// on the linked list, or NULL.
// on the linked list, or nullptr.
//
//=============================================================================
@ -516,7 +516,7 @@ static FLightNode * DeleteLightNode(FLightNode * node)
delete node;
return(tn);
}
return(NULL);
return(nullptr);
} // phares 3/13/98
@ -527,20 +527,20 @@ static FLightNode * DeleteLightNode(FLightNode * node)
//
//==========================================================================
double ADynamicLight::DistToSeg(const DVector3 &pos, seg_t *seg)
double ADynamicLight::DistToSeg(const DVector3 &pos, vertex_t *start, vertex_t *end)
{
double u, px, py;
double seg_dx = seg->v2->fX() - seg->v1->fX();
double seg_dy = seg->v2->fY() - seg->v1->fY();
double seg_dx = end->fX() - start->fX();
double seg_dy = end->fY() - start->fY();
double seg_length_sq = seg_dx * seg_dx + seg_dy * seg_dy;
u = (((pos.X - seg->v1->fX()) * seg_dx) + (pos.Y - seg->v1->fY()) * seg_dy) / seg_length_sq;
u = (((pos.X - start->fX()) * seg_dx) + (pos.Y - start->fY()) * seg_dy) / seg_length_sq;
if (u < 0.) u = 0.; // clamp the test point to the line segment
else if (u > 1.) u = 1.;
px = seg->v1->fX() + (u * seg_dx);
py = seg->v1->fY() + (u * seg_dy);
px = start->fX() + (u * seg_dx);
py = start->fY() + (u * seg_dy);
px -= pos.X;
py -= pos.Y;
@ -557,108 +557,123 @@ double ADynamicLight::DistToSeg(const DVector3 &pos, seg_t *seg)
//==========================================================================
struct LightLinkEntry
{
subsector_t *sub;
FSection *sect;
DVector3 pos;
};
static TArray<LightLinkEntry> collected_ss;
void ADynamicLight::CollectWithinRadius(const DVector3 &opos, subsector_t *subSec, float radius)
void ADynamicLight::CollectWithinRadius(const DVector3 &opos, FSection *section, float radius)
{
if (!subSec) return;
if (!section) return;
collected_ss.Clear();
collected_ss.Push({ subSec, opos });
subSec->validcount = ::validcount;
collected_ss.Push({ section, opos });
section->validcount = dl_validcount;
bool hitonesidedback = false;
for (unsigned i = 0; i < collected_ss.Size(); i++)
{
subSec = collected_ss[i].sub;
auto &pos = collected_ss[i].pos;
section = collected_ss[i].sect;
touching_subsectors = AddLightNode(&subSec->lighthead, subSec, this, touching_subsectors);
if (subSec->sector->validcount != ::validcount)
touching_sector = AddLightNode(&section->lighthead, section, this, touching_sector);
auto processSide = [&](side_t *sidedef, const vertex_t *v1, const vertex_t *v2)
{
touching_sector = AddLightNode(&subSec->render_sector->lighthead, subSec->sector, this, touching_sector);
subSec->sector->validcount = ::validcount;
}
for (unsigned int j = 0; j < subSec->numlines; ++j)
{
auto &pos = collected_ss[i].pos;
seg_t *seg = subSec->firstline + j;
// check distance from x/y to seg and if within radius add this seg and, if present the opposing subsector (lather/rinse/repeat)
// If out of range we do not need to bother with this seg.
if (DistToSeg(pos, seg) <= radius)
auto linedef = sidedef->linedef;
if (linedef && linedef->validcount != ::validcount)
{
if (seg->sidedef && seg->linedef && seg->linedef->validcount != ::validcount)
// light is in front of the seg
if ((pos.Y - v1->fY()) * (v2->fX() - v1->fX()) + (v1->fX() - pos.X) * (v2->fY() - v1->fY()) <= 0)
{
// light is in front of the seg
if ((pos.Y - seg->v1->fY()) * (seg->v2->fX() - seg->v1->fX()) + (seg->v1->fX() - pos.X) * (seg->v2->fY() - seg->v1->fY()) <= 0)
{
seg->linedef->validcount = validcount;
touching_sides = AddLightNode(&seg->sidedef->lighthead, seg->sidedef, this, touching_sides);
}
else if (seg->linedef->sidedef[0] == seg->sidedef && seg->linedef->sidedef[1] == nullptr)
{
hitonesidedback = true;
}
linedef->validcount = ::validcount;
touching_sides = AddLightNode(&sidedef->lighthead, sidedef, this, touching_sides);
}
if (seg->linedef)
else if (linedef->sidedef[0] == sidedef && linedef->sidedef[1] == nullptr)
{
FLinePortal *port = seg->linedef->getPortal();
if (port && port->mType == PORTT_LINKED)
hitonesidedback = true;
}
}
if (linedef)
{
FLinePortal *port = linedef->getPortal();
if (port && port->mType == PORTT_LINKED)
{
line_t *other = port->mDestination;
if (other->validcount != ::validcount)
{
line_t *other = port->mDestination;
if (other->validcount != ::validcount)
subsector_t *othersub = R_PointInSubsector(other->v1->fPos() + other->Delta() / 2);
FSection *othersect = othersub->section;
if (othersect->validcount != ::validcount)
{
subsector_t *othersub = R_PointInSubsector(other->v1->fPos() + other->Delta() / 2);
if (othersub->validcount != ::validcount)
{
othersub->validcount = ::validcount;
collected_ss.Push({ othersub, PosRelative(other) });
}
othersect->validcount = ::validcount;
collected_ss.Push({ othersect, PosRelative(other) });
}
}
}
}
};
seg_t *partner = seg->PartnerSeg;
for (auto &segment : section->segments)
{
// check distance from x/y to seg and if within radius add this seg and, if present the opposing subsector (lather/rinse/repeat)
// If out of range we do not need to bother with this seg.
if (DistToSeg(pos, segment.start, segment.end) <= radius)
{
auto sidedef = segment.sidedef;
if (sidedef)
{
processSide(sidedef, segment.start, segment.end);
}
auto partner = segment.partner;
if (partner)
{
subsector_t *sub = partner->Subsector;
if (sub != NULL && sub->validcount != ::validcount)
FSection *sect = partner->section;
if (sect != nullptr && sect->validcount != dl_validcount)
{
sub->validcount = ::validcount;
collected_ss.Push({ sub, pos });
sect->validcount = dl_validcount;
collected_ss.Push({ sect, pos });
}
}
}
}
sector_t *sec = subSec->sector;
for (auto side : section->sides)
{
auto v1 = side->V1(), v2 = side->V2();
if (DistToSeg(pos, v1, v2) <= radius)
{
processSide(side, v1, v2);
}
}
sector_t *sec = section->sector;
if (!sec->PortalBlocksSight(sector_t::ceiling))
{
line_t *other = subSec->firstline->linedef;
line_t *other = section->segments[0].sidedef->linedef;
if (sec->GetPortalPlaneZ(sector_t::ceiling) < Z() + radius)
{
DVector2 refpos = other->v1->fPos() + other->Delta() / 2 + sec->GetPortalDisplacement(sector_t::ceiling);
subsector_t *othersub = R_PointInSubsector(refpos);
if (othersub->validcount != ::validcount)
FSection *othersect = othersub->section;
if (othersect->validcount != dl_validcount)
{
othersub->validcount = ::validcount;
collected_ss.Push({ othersub, PosRelative(othersub->sector) });
othersect->validcount = dl_validcount;
collected_ss.Push({ othersect, PosRelative(othersub->sector) });
}
}
}
if (!sec->PortalBlocksSight(sector_t::floor))
{
line_t *other = subSec->firstline->linedef;
line_t *other = section->segments[0].sidedef->linedef;
if (sec->GetPortalPlaneZ(sector_t::floor) > Z() - radius)
{
DVector2 refpos = other->v1->fPos() + other->Delta() / 2 + sec->GetPortalDisplacement(sector_t::floor);
subsector_t *othersub = R_PointInSubsector(refpos);
if (othersub->validcount != ::validcount)
FSection *othersect = othersub->section;
if (othersect->validcount != dl_validcount)
{
othersub->validcount = ::validcount;
collected_ss.Push({ othersub, PosRelative(othersub->sector) });
othersect->validcount = dl_validcount;
collected_ss.Push({ othersect, PosRelative(othersub->sector) });
}
}
}
@ -680,49 +695,34 @@ void ADynamicLight::LinkLight()
node = touching_sides;
while (node)
{
node->lightsource = NULL;
node = node->nextTarget;
}
node = touching_subsectors;
while (node)
{
node->lightsource = NULL;
node->lightsource = nullptr;
node = node->nextTarget;
}
node = touching_sector;
while (node)
{
node->lightsource = NULL;
node->lightsource = nullptr;
node = node->nextTarget;
}
if (radius>0)
{
// passing in radius*radius allows us to do a distance check without any calls to sqrt
subsector_t * subSec = R_PointInSubsector(Pos());
FSection *sect = R_PointInSubsector(Pos())->section;
dl_validcount++;
::validcount++;
CollectWithinRadius(Pos(), subSec, float(radius*radius));
CollectWithinRadius(Pos(), sect, float(radius*radius));
}
// Now delete any nodes that won't be used. These are the ones where
// m_thing is still NULL.
// m_thing is still nullptr.
node = touching_sides;
while (node)
{
if (node->lightsource == NULL)
{
node = DeleteLightNode(node);
}
else
node = node->nextTarget;
}
node = touching_subsectors;
while (node)
{
if (node->lightsource == NULL)
if (node->lightsource == nullptr)
{
node = DeleteLightNode(node);
}
@ -733,7 +733,7 @@ void ADynamicLight::LinkLight()
node = touching_sector;
while (node)
{
if (node->lightsource == NULL)
if (node->lightsource == nullptr)
{
node = DeleteLightNode(node);
}
@ -750,7 +750,7 @@ void ADynamicLight::LinkLight()
//==========================================================================
void ADynamicLight::UnlinkLight ()
{
if (owned && target != NULL)
if (owned && target != nullptr)
{
// Delete reference in owning actor
for(int c=target->AttachedLights.Size()-1; c>=0; c--)
@ -763,7 +763,6 @@ void ADynamicLight::UnlinkLight ()
}
}
while (touching_sides) touching_sides = DeleteLightNode(touching_sides);
while (touching_subsectors) touching_subsectors = DeleteLightNode(touching_subsectors);
while (touching_sector) touching_sector = DeleteLightNode(touching_sector);
shadowmapped = false;
}
@ -788,7 +787,7 @@ void AActor::AttachLight(unsigned int count, const FLightDefaults *lightdef)
if (count < AttachedLights.Size())
{
light = barrier_cast<ADynamicLight*>(AttachedLights[count]);
assert(light != NULL);
assert(light != nullptr);
}
else
{
@ -815,13 +814,13 @@ void AActor::SetDynamicLights()
TArray<FInternalLightAssociation *> & LightAssociations = GetInfo()->LightAssociations;
unsigned int count = 0;
if (state == NULL) return;
if (state == nullptr) return;
if (LightAssociations.Size() > 0)
{
ADynamicLight *lights, *tmpLight;
unsigned int i;
lights = tmpLight = NULL;
lights = tmpLight = nullptr;
for (i = 0; i < LightAssociations.Size(); i++)
{
@ -834,7 +833,7 @@ void AActor::SetDynamicLights()
}
if (count == 0 && state->Light > 0)
{
for(int i= state->Light; StateLights[i] != NULL; i++)
for(int i= state->Light; StateLights[i] != nullptr; i++)
{
if (StateLights[i] != (FLightDefaults*)-1)
{
@ -918,7 +917,7 @@ void AActor::RecreateAllAttachedLights()
CCMD(listlights)
{
int walls, sectors, subsecs;
int walls, sectors;
int allwalls=0, allsectors=0, allsubsecs = 0;
int i=0, shadowcount = 0;
ADynamicLight * dl;
@ -928,7 +927,6 @@ CCMD(listlights)
{
walls=0;
sectors=0;
subsecs = 0;
Printf("%s at (%f, %f, %f), color = 0x%02x%02x%02x, radius = %f %s %s",
dl->target? dl->target->GetClass()->TypeName.GetChars() : dl->GetClass()->TypeName.GetChars(),
dl->X(), dl->Y(), dl->Z(), dl->args[LIGHT_RED],
@ -954,14 +952,6 @@ CCMD(listlights)
node = node->nextTarget;
}
node=dl->touching_subsectors;
while (node)
{
allsubsecs++;
subsecs++;
node = node->nextTarget;
}
node = dl->touching_sector;
@ -971,27 +961,10 @@ CCMD(listlights)
sectors++;
node = node->nextTarget;
}
Printf("- %d walls, %d subsectors, %d sectors\n", walls, subsecs, sectors);
Printf("- %d walls, %d sectors\n", walls, sectors);
}
Printf("%i dynamic lights, %d shadowmapped, %d walls, %d subsectors, %d sectors\n\n\n", i, shadowcount, allwalls, allsubsecs, allsectors);
}
CCMD(listsublights)
{
for(auto &sub : level.subsectors)
{
int lights = 0;
FLightNode * node = sub.lighthead;
while (node != NULL)
{
lights++;
node = node->nextLight;
}
Printf(PRINT_LOG, "Subsector %d - %d lights\n", sub.Index(), lights);
}
Printf("%i dynamic lights, %d shadowmapped, %d walls, %d sectors\n\n\n", i, shadowcount, allwalls, allsectors);
}

View File

@ -12,6 +12,7 @@ struct seg_t;
class ADynamicLight;
class FSerializer;
struct FSectionLine;
enum ELightType
{
@ -206,12 +207,11 @@ public:
bool IsSpot() { return !!(lightflags & LF_SPOT); }
FState *targetState;
FLightNode * touching_sides;
FLightNode * touching_subsectors;
FLightNode * touching_sector;
private:
double DistToSeg(const DVector3 &pos, seg_t *seg);
void CollectWithinRadius(const DVector3 &pos, subsector_t *subSec, float radius);
double DistToSeg(const DVector3 &pos, vertex_t *start, vertex_t *end);
void CollectWithinRadius(const DVector3 &pos, FSection *section, float radius);
protected:
DVector3 m_off;

View File

@ -35,7 +35,6 @@
#include "hwrenderer/data/buffers.h"
#include "hwrenderer/scene/hw_renderstate.h"
//==========================================================================
//
//
@ -158,55 +157,26 @@ static F3DFloor *Find3DFloor(sector_t *target, sector_t *model)
//
//==========================================================================
int FFlatVertexBuffer::CreateIndexedSubsectorVertices(subsector_t *sub, const secplane_t &plane, int floor, int vi, FFlatVertexBuffer::FIndexGenerationInfo &gen)
int FFlatVertexBuffer::CreateIndexedSectorVertices(sector_t *sec, const secplane_t &plane, int floor, VertexContainer &verts)
{
if (sub->numlines < 3) return -1;
int idx = ibo_data.Reserve((sub->numlines - 2) * 3);
int idxc = idx;
int firstndx = gen.GetIndex(sub->firstline[0].v1);
int secondndx = gen.GetIndex(sub->firstline[1].v1);
for (unsigned int k = 2; k<sub->numlines; k++)
{
auto ndx = gen.GetIndex(sub->firstline[k].v1);
ibo_data[idx++] = vi + firstndx;
ibo_data[idx++] = vi + secondndx;
ibo_data[idx++] = vi + ndx;
secondndx = ndx;
}
return idx;
}
//==========================================================================
//
// Creates the vertices for one plane in one subsector
//
//==========================================================================
int FFlatVertexBuffer::CreateIndexedSectorVertices(sector_t *sec, const secplane_t &plane, int floor, FFlatVertexBuffer::FIndexGenerationInfo &gen)
{
int rt = ibo_data.Size();
int vi = vbo_shadowdata.Reserve(gen.vertices.Size());
unsigned vi = vbo_shadowdata.Reserve(verts.vertices.Size());
float diff;
// Create the actual vertices.
if (sec->transdoor && floor) diff = -1.f;
else diff = 0.f;
for (unsigned i = 0; i < gen.vertices.Size(); i++)
for (unsigned i = 0; i < verts.vertices.Size(); i++)
{
vbo_shadowdata[vi + i].SetFlatVertex(gen.vertices[i], plane);
vbo_shadowdata[vi + i].SetFlatVertex(verts.vertices[i].vertex, plane);
vbo_shadowdata[vi + i].z += diff;
}
// Create the indices for the subsectors
for (int j = 0; j<sec->subsectorcount; j++)
unsigned rt = ibo_data.Reserve(verts.indices.Size());
for (unsigned i = 0; i < verts.indices.Size(); i++)
{
subsector_t *sub = sec->subsectors[j];
CreateIndexedSubsectorVertices(sub, plane, floor, vi, gen);
ibo_data[rt + i] = vi + verts.indices[i];
}
sec->ibocount = ibo_data.Size() - rt;
return rt;
return (int)rt;
}
//==========================================================================
@ -215,19 +185,20 @@ int FFlatVertexBuffer::CreateIndexedSectorVertices(sector_t *sec, const secplane
//
//==========================================================================
int FFlatVertexBuffer::CreateIndexedVertices(int h, sector_t *sec, const secplane_t &plane, int floor, TArray<FFlatVertexBuffer::FIndexGenerationInfo> &gen)
int FFlatVertexBuffer::CreateIndexedVertices(int h, sector_t *sec, const secplane_t &plane, int floor, VertexContainers &verts)
{
sec->vboindex[h] = vbo_shadowdata.Size();
// First calculate the vertices for the sector itself
sec->vboheight[h] = sec->GetPlaneTexZ(h);
sec->iboindex[h] = CreateIndexedSectorVertices(sec, plane, floor, gen[sec->Index()]);
sec->ibocount = verts[sec->Index()].indices.Size();
sec->iboindex[h] = CreateIndexedSectorVertices(sec, plane, floor, verts[sec->Index()]);
// Next are all sectors using this one as heightsec
TArray<sector_t *> &fakes = sec->e->FakeFloor.Sectors;
for (unsigned g = 0; g < fakes.Size(); g++)
{
sector_t *fsec = fakes[g];
fsec->iboindex[2 + h] = CreateIndexedSectorVertices(fsec, plane, false, gen[fsec->Index()]);
fsec->iboindex[2 + h] = CreateIndexedSectorVertices(fsec, plane, false, verts[fsec->Index()]);
}
// and finally all attached 3D floors
@ -244,7 +215,7 @@ int FFlatVertexBuffer::CreateIndexedVertices(int h, sector_t *sec, const secplan
if (dotop || dobottom)
{
auto ndx = CreateIndexedSectorVertices(fsec, plane, false, gen[fsec->Index()]);
auto ndx = CreateIndexedSectorVertices(fsec, plane, false, verts[fsec->Index()]);
if (dotop) ffloor->top.vindex = ndx;
if (dobottom) ffloor->bottom.vindex = ndx;
}
@ -263,26 +234,34 @@ int FFlatVertexBuffer::CreateIndexedVertices(int h, sector_t *sec, const secplan
void FFlatVertexBuffer::CreateIndexedFlatVertices()
{
TArray<FIndexGenerationInfo> gen;
gen.Resize(level.sectors.Size());
// This must be generated up front so that the following code knows how many vertices a sector contains.
for (unsigned i = 0; i < level.sectors.Size(); i++)
auto verts = BuildVertices();
int i = 0;
/*
for (auto &vert : verts)
{
for (int j = 0; j < level.sectors[i].subsectorcount; j++)
Printf(PRINT_LOG, "Sector %d\n", i);
Printf(PRINT_LOG, "%d vertices, %d indices\n", vert.vertices.Size(), vert.indices.Size());
int j = 0;
for (auto &v : vert.vertices)
{
auto sub = level.sectors[i].subsectors[j];
for (unsigned k = 0; k < sub->numlines; k++)
{
auto vert = sub->firstline[k].v1;
gen[i].AddVertex(vert);
}
Printf(PRINT_LOG, " %d: (%2.3f, %2.3f)\n", j++, v.vertex->fX(), v.vertex->fY());
}
for (unsigned i=0;i<vert.indices.Size();i+=3)
{
Printf(PRINT_LOG, " %d, %d, %d\n", vert.indices[i], vert.indices[i + 1], vert.indices[i + 2]);
}
i++;
}
*/
for (int h = sector_t::floor; h <= sector_t::ceiling; h++)
{
for (auto &sec : level.sectors)
{
CreateIndexedVertices(h, &sec, sec.GetSecPlane(h), h == sector_t::floor, gen);
CreateIndexedVertices(h, &sec, sec.GetSecPlane(h), h == sector_t::floor, verts);
}
}

View File

@ -25,6 +25,7 @@
#include "tarray.h"
#include "hwrenderer/data/buffers.h"
#include "hw_vertexbuilder.h"
#include <atomic>
#include <mutex>
@ -62,31 +63,6 @@ class FFlatVertexBuffer
static const unsigned int BUFFER_SIZE = 2000000;
static const unsigned int BUFFER_SIZE_TO_USE = 1999500;
// Temporary data for creating an indexed buffer
struct FIndexGenerationInfo
{
TArray<vertex_t *> vertices;
TMap<vertex_t*, uint32_t> vertexmap;
uint32_t AddVertex(vertex_t *vert)
{
auto check = vertexmap.CheckKey(vert);
if (check != nullptr) return *check;
auto index = vertices.Push(vert);
vertexmap[vert] = index;
return index;
}
uint32_t GetIndex(vertex_t *vert)
{
auto check = vertexmap.CheckKey(vert);
if (check != nullptr) return *check;
return ~0;
}
};
public:
enum
{
@ -140,9 +116,9 @@ public:
}
private:
int CreateIndexedSubsectorVertices(subsector_t *sub, const secplane_t &plane, int floor, int vi, FIndexGenerationInfo &gen);
int CreateIndexedSectorVertices(sector_t *sec, const secplane_t &plane, int floor, FIndexGenerationInfo &gen);
int CreateIndexedVertices(int h, sector_t *sec, const secplane_t &plane, int floor, TArray<FIndexGenerationInfo> &gen);
int CreateIndexedSectionVertices(subsector_t *sub, const secplane_t &plane, int floor, VertexContainer &cont);
int CreateIndexedSectorVertices(sector_t *sec, const secplane_t &plane, int floor, VertexContainer &cont);
int CreateIndexedVertices(int h, sector_t *sec, const secplane_t &plane, int floor, VertexContainers &cont);
void CreateIndexedFlatVertices();
void UpdatePlaneVertices(sector_t *sec, int plane);
@ -154,4 +130,4 @@ public:
};
#endif
#endif

View File

@ -0,0 +1,146 @@
//
//---------------------------------------------------------------------------
//
// Copyright(C) 2015-2018 Christoph Oelckers
// All rights reserved.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 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 Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this program. If not, see http://www.gnu.org/licenses/
//
//--------------------------------------------------------------------------
//
#include "g_levellocals.h"
#include "hw_vertexbuilder.h"
#include "earcut.hpp"
//=============================================================================
//
// Creates vertex meshes for sector planes
//
//=============================================================================
//=============================================================================
//
//
//
//=============================================================================
static void CreateVerticesForSubsector(subsector_t *sub, VertexContainer &gen, int qualifier)
{
if (sub->numlines < 3) return;
uint32_t startindex = gen.indices.Size();
if ((sub->flags & SSECF_HOLE) && sub->numlines > 3)
{
// Hole filling "subsectors" are not necessarily convex so they require real triangulation.
// These things are extremely rare so performance is secondary here.
using Point = std::pair<double, double>;
std::vector<std::vector<Point>> polygon;
std::vector<Point> *curPoly;
for (unsigned i = 0; i < sub->numlines; i++)
{
polygon.resize(1);
curPoly = &polygon.back();
curPoly->push_back({ sub->firstline[i].v1->fX(), sub->firstline[i].v1->fY() });
}
auto indices = mapbox::earcut(polygon);
for (auto vti : indices)
{
gen.AddIndexForVertex(sub->firstline[vti].v1, qualifier);
}
}
else
{
int firstndx = gen.AddVertex(sub->firstline[0].v1, qualifier);
int secondndx = gen.AddVertex(sub->firstline[1].v1, qualifier);
for (unsigned int k = 2; k < sub->numlines; k++)
{
gen.AddIndex(firstndx);
gen.AddIndex(secondndx);
auto ndx = gen.AddVertex(sub->firstline[k].v1, qualifier);
gen.AddIndex(ndx);
secondndx = ndx;
}
}
}
//=============================================================================
//
//
//
//=============================================================================
static void TriangulateSection(FSection &sect, VertexContainer &gen, int qualifier)
{
if (sect.segments.Size() < 3) return;
// todo
}
//=============================================================================
//
//
//
//=============================================================================
static void CreateVerticesForSection(FSection &section, VertexContainer &gen, bool useSubsectors)
{
section.vertexindex = gen.indices.Size();
if (useSubsectors)
{
for (auto sub : section.subsectors)
{
CreateVerticesForSubsector(sub, gen, -1);
}
}
else
{
TriangulateSection(section, gen, -1);
}
section.vertexcount = gen.indices.Size() - section.vertexindex;
}
//==========================================================================
//
// Creates the vertices for one plane in one subsector
//
//==========================================================================
static void CreateVerticesForSector(sector_t *sec, VertexContainer &gen)
{
auto sections = level.sections.SectionsForSector(sec);
for (auto &section :sections)
{
CreateVerticesForSection( section, gen, true);
}
}
TArray<VertexContainer> BuildVertices()
{
TArray<VertexContainer> verticesPerSector(level.sectors.Size(), true);
for (unsigned i=0; i<level.sectors.Size(); i++)
{
CreateVerticesForSector(&level.sectors[i], verticesPerSector[i]);
}
return verticesPerSector;
}

View File

@ -0,0 +1,69 @@
#include "tarray.h"
struct vertex_t;
struct FQualifiedVertex
{
vertex_t *vertex; // index into vertex array
int qualifier; // some index to prevent vertices in different groups from being merged together.
};
template<> struct THashTraits<FQualifiedVertex>
{
hash_t Hash(const FQualifiedVertex &key)
{
return (int)(((intptr_t)key.vertex) >> 4) ^ (key.qualifier << 16);
}
int Compare(const FQualifiedVertex &left, const FQualifiedVertex &right) { return left.vertex != right.vertex || left.qualifier != right.qualifier; }
};
//=============================================================================
//
// One sector's vertex data.
//
//=============================================================================
struct VertexContainer
{
TArray<FQualifiedVertex> vertices;
TMap<FQualifiedVertex, uint32_t> vertexmap;
bool perSubsector = false;
TArray<uint32_t> indices;
uint32_t AddVertex(FQualifiedVertex *vert)
{
auto check = vertexmap.CheckKey(*vert);
if (check != nullptr) return *check;
auto index = vertices.Push(*vert);
vertexmap[*vert] = index;
return index;
}
uint32_t AddVertex(vertex_t *vert, int qualifier)
{
FQualifiedVertex vertx = { vert, qualifier};
return AddVertex(&vertx);
}
uint32_t AddIndexForVertex(FQualifiedVertex *vert)
{
return indices.Push(AddVertex(vert));
}
uint32_t AddIndexForVertex(vertex_t *vert, int qualifier)
{
return indices.Push(AddVertex(vert, qualifier));
}
uint32_t AddIndex(uint32_t indx)
{
return indices.Push(indx);
}
};
using VertexContainers = TArray<VertexContainer>;
VertexContainers BuildVertices();

View File

@ -155,6 +155,7 @@ void HWDrawInfo::WorkerThread()
{
GLFlat flat;
SetupFlat.Clock();
flat.section = job->sub->section;
front = hw_FakeFlat(job->sub->render_sector, &fakefront, in_area, false);
flat.ProcessSector(this, front);
SetupFlat.Unclock();
@ -679,7 +680,7 @@ void HWDrawInfo::DoSubsector(subsector_t * sub)
fakesector = hw_FakeFlat(sector, &fake, in_area, false);
}
uint8_t &srf = sectorrenderflags[sub->render_sector->sectornum];
uint8_t &srf = section_renderflags[level.sections.SectionIndex(sub->section)];
if (!(srf & SSRF_PROCESSED))
{
srf |= SSRF_PROCESSED;
@ -691,6 +692,7 @@ void HWDrawInfo::DoSubsector(subsector_t * sub)
else
{
GLFlat flat;
flat.section = sub->section;
SetupFlat.Clock();
flat.ProcessSector(this, fakesector);
SetupFlat.Unclock();
@ -701,6 +703,7 @@ void HWDrawInfo::DoSubsector(subsector_t * sub)
(sub->numlines > 2) ? SSRF_PROCESSED|SSRF_RENDERALL : SSRF_PROCESSED;
if (sub->hacked & 1) AddHackedSubsector(sub);
// This is for portal coverage.
FSectorPortalGroup *portal;
portal = fakesector->GetPortalGroup(sector_t::ceiling);

View File

@ -56,18 +56,6 @@ sector_t * hw_FakeFlat(sector_t * sec, sector_t * dest, area_t in_area, bool bac
//
//==========================================================================
template<class T>
inline void DeleteLinkedList(T *node)
{
while (node)
{
auto n = node;
node = node->next;
delete n;
}
}
class FDrawInfoList
{
public:
@ -188,17 +176,10 @@ HWDrawInfo *HWDrawInfo::EndDrawInfo()
void HWDrawInfo::ClearBuffers()
{
for (auto node : otherfloorplanes) DeleteLinkedList(node);
otherfloorplanes.Clear();
for (auto node : otherceilingplanes) DeleteLinkedList(node);
otherceilingplanes.Clear();
for (auto node : floodfloorsegs) DeleteLinkedList(node);
floodfloorsegs.Clear();
for (auto node : floodceilingsegs) DeleteLinkedList(node);
floodceilingsegs.Clear();
otherFloorPlanes.Clear();
otherCeilingPlanes.Clear();
floodFloorSegs.Clear();
floodCeilingSegs.Clear();
// clear all the lists that might not have been cleared already
MissingUpperTextures.Clear();
@ -206,19 +187,19 @@ void HWDrawInfo::ClearBuffers()
MissingUpperSegs.Clear();
MissingLowerSegs.Clear();
SubsectorHacks.Clear();
CeilingStacks.Clear();
FloorStacks.Clear();
//CeilingStacks.Clear();
//FloorStacks.Clear();
HandledSubsectors.Clear();
spriteindex = 0;
CurrentMapSections.Resize(level.NumMapSections);
CurrentMapSections.Zero();
sectorrenderflags.Resize(level.sectors.Size());
section_renderflags.Resize(level.sections.allSections.Size());
ss_renderflags.Resize(level.subsectors.Size());
no_renderflags.Resize(level.subsectors.Size());
memset(&sectorrenderflags[0], 0, level.sectors.Size() * sizeof(sectorrenderflags[0]));
memset(&section_renderflags[0], 0, level.sections.allSections.Size() * sizeof(section_renderflags[0]));
memset(&ss_renderflags[0], 0, level.subsectors.Size() * sizeof(ss_renderflags[0]));
memset(&no_renderflags[0], 0, level.nodes.Size() * sizeof(no_renderflags[0]));
@ -460,8 +441,8 @@ void HWDrawInfo::CreateScene()
HandleMissingTextures(in_area); // Missing upper/lower textures
HandleHackedSubsectors(); // open sector hacks for deep water
ProcessSectorStacks(in_area); // merge visplanes of sector stacks
PrepareUnhandledMissingTextures();
DispatchRenderHacks();
screen->mLights->Unmap();
screen->mVertexData->Unmap();
@ -674,3 +655,22 @@ void HWDrawInfo::ProcessScene(bool toscreen, const std::function<void(HWDrawInfo
DrawScene(this, toscreen ? DM_MAINVIEW : DM_OFFSCREEN);
}
//==========================================================================
//
//
//
//==========================================================================
void HWDrawInfo::AddSubsectorToPortal(FSectorPortalGroup *ptg, subsector_t *sub)
{
auto portal = FindPortal(ptg);
if (!portal)
{
portal = new HWScenePortal(screen->mPortalState, new HWSectorStackPortal(ptg));
Portals.Push(portal);
}
auto ptl = static_cast<HWSectorStackPortal*>(static_cast<HWScenePortal*>(portal)->mScene);
ptl->AddSubsector(sub);
}

View File

@ -68,6 +68,8 @@ enum SectorRenderFlags
SSRF_RENDERALL = 7,
SSRF_PROCESSED = 8,
SSRF_SEEN = 16,
SSRF_PLANEHACK = 32,
SSRF_FLOODHACK = 64
};
enum EPortalClip
@ -160,17 +162,17 @@ struct HWDrawInfo
TArray<SubsectorHackInfo> SubsectorHacks;
TArray<gl_subsectorrendernode*> otherfloorplanes;
TArray<gl_subsectorrendernode*> otherceilingplanes;
TArray<gl_floodrendernode*> floodfloorsegs;
TArray<gl_floodrendernode*> floodceilingsegs;
TMap<int, gl_subsectorrendernode*> otherFloorPlanes;
TMap<int, gl_subsectorrendernode*> otherCeilingPlanes;
TMap<int, gl_floodrendernode*> floodFloorSegs;
TMap<int, gl_floodrendernode*> floodCeilingSegs;
TArray<sector_t *> CeilingStacks;
TArray<sector_t *> FloorStacks;
//TArray<sector_t *> CeilingStacks;
//TArray<sector_t *> FloorStacks;
TArray<subsector_t *> HandledSubsectors;
TArray<uint8_t> sectorrenderflags;
TArray<uint8_t> section_renderflags;
TArray<uint8_t> ss_renderflags;
TArray<uint8_t> no_renderflags;
@ -212,32 +214,6 @@ private:
void DrawPSprite(HUDSprite *huds, FRenderState &state);
public:
gl_subsectorrendernode * GetOtherFloorPlanes(unsigned int sector)
{
if (sector<otherfloorplanes.Size()) return otherfloorplanes[sector];
else return nullptr;
}
gl_subsectorrendernode * GetOtherCeilingPlanes(unsigned int sector)
{
if (sector<otherceilingplanes.Size()) return otherceilingplanes[sector];
else return nullptr;
}
gl_floodrendernode * GetFloodFloorSegs(unsigned int sector)
{
if (sector<floodfloorsegs.Size()) return floodfloorsegs[sector];
else return nullptr;
}
gl_floodrendernode * GetFloodCeilingSegs(unsigned int sector)
{
if (sector<floodceilingsegs.Size()) return floodceilingsegs[sector];
else return nullptr;
}
void SetCameraPos(const DVector3 &pos)
{
VPUniforms.mCameraPos = { (float)pos.X, (float)pos.Z, (float)pos.Y, 0.f };
@ -256,11 +232,6 @@ public:
VPUniforms.mClipHeight = 0;
}
bool ClipLineShouldBeActive()
{
return (screen->hwcaps & RFL_NO_CLIP_PLANES) && VPUniforms.mClipLine.X > -1000000.f;
}
HWPortal * FindPortal(const void * src);
void RenderBSPNode(void *node);
void RenderBSP(void *node);
@ -293,6 +264,7 @@ public:
void CollectSectorStacksCeiling(subsector_t * sub, sector_t * anchor, area_t in_area);
void CollectSectorStacksFloor(subsector_t * sub, sector_t * anchor, area_t in_area);
void DispatchRenderHacks();
void AddUpperMissingTexture(side_t * side, subsector_t *sub, float backheight);
void AddLowerMissingTexture(side_t * side, subsector_t *sub, float backheight);
void HandleMissingTextures(area_t in_area);

View File

@ -116,7 +116,7 @@ void HWDrawInfo::AddFlat(GLFlat *flat, bool fog)
list = GLDL_PLAINFLATS;
}
}
else
else //if (flat->hacktype != SSRF_FLOODHACK) // The flood hack may later need different treatment but with the current setup can go into the existing render list.
{
bool masked = flat->gltexture->isMasked() && ((flat->renderflags&SSRF_RENDER3DPLANES) || flat->stack);
list = masked ? GLDL_MASKEDFLATS : GLDL_PLAINFLATS;

View File

@ -27,6 +27,7 @@ struct FSpriteModelFrame;
struct particle_t;
class FRenderState;
struct GLDecal;
struct FSection;
enum area_t : int;
enum HWRenderStyle
@ -293,6 +294,7 @@ class GLFlat
{
public:
sector_t * sector;
FSection *section;
float z; // the z position of the flat (only valid for non-sloped planes)
FMaterial *gltexture;
@ -306,6 +308,7 @@ public:
bool stack;
bool ceiling;
uint8_t renderflags;
uint8_t hacktype;
int iboindex;
//int vboheight;
@ -317,10 +320,13 @@ public:
void PutFlat(HWDrawInfo *di, bool fog = false);
void Process(HWDrawInfo *di, sector_t * model, int whichplane, bool notexture);
void SetFrom3DFloor(F3DFloor *rover, bool top, bool underside);
void ProcessSector(HWDrawInfo *di, sector_t * frontsector);
void ProcessSector(HWDrawInfo *di, sector_t * frontsector, int which = 7 /*SSRF_RENDERALL*/); // cannot use constant due to circular dependencies.
void DrawSubsectors(HWDrawInfo *di, FRenderState &state);
void DrawFlat(HWDrawInfo *di, FRenderState &state, bool translucent);
void DrawOtherPlanes(HWDrawInfo *di, FRenderState &state);
void DrawFloodPlanes(HWDrawInfo *di, FRenderState &state);
};
//==========================================================================

View File

@ -182,104 +182,110 @@ void GLFlat::SetupLights(HWDrawInfo *di, FLightNode * node, FDynLightData &light
void GLFlat::DrawSubsectors(HWDrawInfo *di, FRenderState &state)
{
auto vcount = sector->ibocount;
if (level.HasDynamicLights && screen->BuffersArePersistent())
{
SetupLights(di, sector->lighthead, lightdata, sector->PortalGroup);
SetupLights(di, section->lighthead, lightdata, sector->PortalGroup);
}
state.SetLightIndex(dynlightindex);
if (vcount > 0 && !di->ClipLineShouldBeActive())
{
state.DrawIndexed(DT_Triangles, iboindex, vcount);
flatvertices += vcount;
flatprimitives++;
}
else
{
int index = iboindex;
bool applied = false;
for (int i = 0; i < sector->subsectorcount; i++)
{
subsector_t * sub = sector->subsectors[i];
if (sub->numlines <= 2) continue;
if (di->ss_renderflags[sub->Index()] & renderflags)
{
state.DrawIndexed(DT_Triangles, index, (sub->numlines - 2) * 3, !applied);
applied = true;
flatvertices += sub->numlines;
flatprimitives++;
}
index += (sub->numlines - 2) * 3;
}
}
if (!(renderflags&SSRF_RENDER3DPLANES))
{
// Draw the subsectors assigned to it due to missing textures
gl_subsectorrendernode * node = (renderflags&SSRF_RENDERFLOOR) ?
di->GetOtherFloorPlanes(sector->sectornum) :
di->GetOtherCeilingPlanes(sector->sectornum);
while (node)
{
state.SetLightIndex(node->lightindex);
auto num = node->sub->numlines;
flatvertices += num;
flatprimitives++;
state.Draw(DT_TriangleFan,node->vertexindex, num);
node = node->next;
}
// Flood gaps with the back side's ceiling/floor texture
// This requires a stencil because the projected plane interferes with
// the depth buffer
gl_floodrendernode * fnode = (renderflags&SSRF_RENDERFLOOR) ?
di->GetFloodFloorSegs(sector->sectornum) :
di->GetFloodCeilingSegs(sector->sectornum);
state.SetLightIndex(dynlightindex);
while (fnode)
{
flatvertices += 12;
flatprimitives += 3;
// Push bleeding floor/ceiling textures back a little in the z-buffer
// so they don't interfere with overlapping mid textures.
state.SetDepthBias(1, 128);
// Create stencil
state.SetEffect(EFF_STENCIL);
state.EnableTexture(false);
state.SetStencil(0, SOP_Increment, SF_ColorMaskOff);
state.Draw(DT_TriangleFan,fnode->vertexindex, 4);
// Draw projected plane into stencil
state.EnableTexture(true);
state.SetEffect(EFF_NONE);
state.SetStencil(1, SOP_Keep, SF_DepthMaskOff);
state.EnableDepthTest(false);
state.Draw(DT_TriangleFan,fnode->vertexindex + 4, 4);
// clear stencil
state.SetEffect(EFF_STENCIL);
state.EnableTexture(false);
state.SetStencil(1, SOP_Decrement, SF_ColorMaskOff | SF_DepthMaskOff);
state.Draw(DT_TriangleFan,fnode->vertexindex, 4);
// restore old stencil op.
state.EnableTexture(true);
state.EnableDepthTest(true);
state.SetEffect(EFF_NONE);
state.SetDepthBias(0, 0);
state.SetStencil(0, SOP_Keep, SF_AllOn);
fnode = fnode->next;
}
}
state.DrawIndexed(DT_Triangles, iboindex + section->vertexindex, section->vertexcount);
flatvertices += section->vertexcount;
flatprimitives++;
}
//==========================================================================
//
// Drawer for render hacks
//
//==========================================================================
void GLFlat::DrawOtherPlanes(HWDrawInfo *di, FRenderState &state)
{
state.SetMaterial(gltexture, CLAMP_NONE, 0, -1);
// Draw the subsectors assigned to it due to missing textures
auto pNode = (renderflags&SSRF_RENDERFLOOR) ?
di->otherFloorPlanes.CheckKey(sector->sectornum) : di->otherCeilingPlanes.CheckKey(sector->sectornum);
if (!pNode) return;
auto node = *pNode;
while (node)
{
state.SetLightIndex(node->lightindex);
auto num = node->sub->numlines;
flatvertices += num;
flatprimitives++;
state.Draw(DT_TriangleFan,node->vertexindex, num);
node = node->next;
}
}
//==========================================================================
//
// Drawer for render hacks
//
//==========================================================================
void GLFlat::DrawFloodPlanes(HWDrawInfo *di, FRenderState &state)
{
// Flood gaps with the back side's ceiling/floor texture
// This requires a stencil because the projected plane interferes with
// the depth buffer
state.SetMaterial(gltexture, CLAMP_NONE, 0, -1);
// Draw the subsectors assigned to it due to missing textures
auto pNode = (renderflags&SSRF_RENDERFLOOR) ?
di->floodFloorSegs.CheckKey(sector->sectornum) : di->floodCeilingSegs.CheckKey(sector->sectornum);
if (!pNode) return;
auto fnode = *pNode;
state.SetLightIndex(-1);
while (fnode)
{
flatvertices += 12;
flatprimitives += 3;
// Push bleeding floor/ceiling textures back a little in the z-buffer
// so they don't interfere with overlapping mid textures.
state.SetDepthBias(1, 128);
// Create stencil
state.SetEffect(EFF_STENCIL);
state.EnableTexture(false);
state.SetStencil(0, SOP_Increment, SF_ColorMaskOff);
state.Draw(DT_TriangleFan, fnode->vertexindex, 4);
// Draw projected plane into stencil
state.EnableTexture(true);
state.SetEffect(EFF_NONE);
state.SetStencil(1, SOP_Keep, SF_DepthMaskOff);
state.EnableDepthTest(false);
state.Draw(DT_TriangleFan, fnode->vertexindex + 4, 4);
// clear stencil
state.SetEffect(EFF_STENCIL);
state.EnableTexture(false);
state.SetStencil(1, SOP_Decrement, SF_ColorMaskOff | SF_DepthMaskOff);
state.Draw(DT_TriangleFan, fnode->vertexindex, 4);
// restore old stencil op.
state.EnableTexture(true);
state.EnableDepthTest(true);
state.SetEffect(EFF_NONE);
state.SetDepthBias(0, 0);
state.SetStencil(0, SOP_Keep, SF_AllOn);
fnode = fnode->next;
}
}
//==========================================================================
//
//
@ -300,10 +306,17 @@ void GLFlat::DrawFlat(HWDrawInfo *di, FRenderState &state, bool translucent)
state.SetColor(lightlevel, rel, di->isFullbrightScene(), Colormap, alpha);
state.SetFog(lightlevel, rel, di->isFullbrightScene(), &Colormap, false);
if (!gltexture || !gltexture->tex->isFullbright())
state.SetObjectColor(FlatColor | 0xff000000);
state.SetObjectColor(FlatColor | 0xff000000);
if (!translucent)
if (hacktype & SSRF_PLANEHACK)
{
DrawOtherPlanes(di, state);
}
else if (hacktype & SSRF_FLOODHACK)
{
DrawFloodPlanes(di, state);
}
else if (!translucent)
{
if (sector->special != GLSector_Skybox)
{
@ -312,7 +325,7 @@ void GLFlat::DrawFlat(HWDrawInfo *di, FRenderState &state, bool translucent)
DrawSubsectors(di, state);
state.EnableTextureMatrix(false);
}
else
else if (!hacktype)
{
state.SetMaterial(gltexture, CLAMP_XY, 0, -1);
state.SetLightIndex(dynlightindex);
@ -362,9 +375,9 @@ inline void GLFlat::PutFlat(HWDrawInfo *di, bool fog)
}
else if (!screen->BuffersArePersistent())
{
if (level.HasDynamicLights && gltexture != nullptr)
if (level.HasDynamicLights && gltexture != nullptr && !(hacktype & (SSRF_PLANEHACK|SSRF_FLOODHACK)) )
{
SetupLights(di, sector->lighthead, lightdata, sector->PortalGroup);
SetupLights(di, section->lighthead, lightdata, sector->PortalGroup);
}
}
di->AddFlat(this, fog);
@ -373,8 +386,6 @@ inline void GLFlat::PutFlat(HWDrawInfo *di, bool fog)
//==========================================================================
//
// This draws one flat
// The passed sector does not indicate the area which is rendered.
// It is only used as source for the plane data.
// The whichplane boolean indicates if the flat is a floor(false) or a ceiling(true)
//
//==========================================================================
@ -412,7 +423,7 @@ void GLFlat::Process(HWDrawInfo *di, sector_t * model, int whichplane, bool fog)
iboindex = vert.second;
}
// For hacks this won't go into a render list.
PutFlat(di, fog);
rendered_flats++;
}
@ -456,7 +467,7 @@ void GLFlat::SetFrom3DFloor(F3DFloor *rover, bool top, bool underside)
//
//==========================================================================
void GLFlat::ProcessSector(HWDrawInfo *di, sector_t * frontsector)
void GLFlat::ProcessSector(HWDrawInfo *di, sector_t * frontsector, int which)
{
lightlist_t * light;
FSectorPortal *port;
@ -472,8 +483,10 @@ void GLFlat::ProcessSector(HWDrawInfo *di, sector_t * frontsector)
sector = &level.sectors[frontsector->sectornum];
extsector_t::xfloor &x = sector->e->XFloor;
dynlightindex = -1;
hacktype = (which & (SSRF_PLANEHACK|SSRF_FLOODHACK));
uint8_t &srf = di->sectorrenderflags[sector->sectornum];
uint8_t sink;
uint8_t &srf = hacktype? sink : di->section_renderflags[level.sections.SectionIndex(section)];
const auto &vp = di->Viewpoint;
//
@ -483,7 +496,7 @@ void GLFlat::ProcessSector(HWDrawInfo *di, sector_t * frontsector)
//
//
//
if (frontsector->floorplane.ZatPoint(vp.Pos) <= vp.Pos.Z)
if ((which & SSRF_RENDERFLOOR) && frontsector->floorplane.ZatPoint(vp.Pos) <= vp.Pos.Z)
{
// process the original floor first.
@ -495,10 +508,12 @@ void GLFlat::ProcessSector(HWDrawInfo *di, sector_t * frontsector)
port = frontsector->ValidatePortal(sector_t::floor);
if ((stack = (port != NULL)))
{
/* to be redone in a less invasive manner
if (port->mType == PORTS_STACKEDSECTORTHING)
{
di->AddFloorStack(sector); // stacked sector things require visplane merging.
}
*/
alpha = frontsector->GetAlpha(sector_t::floor);
}
else
@ -536,7 +551,7 @@ void GLFlat::ProcessSector(HWDrawInfo *di, sector_t * frontsector)
//
//
//
if (frontsector->ceilingplane.ZatPoint(vp.Pos) >= vp.Pos.Z)
if ((which & SSRF_RENDERCEILING) && frontsector->ceilingplane.ZatPoint(vp.Pos) >= vp.Pos.Z)
{
// process the original ceiling first.
@ -548,10 +563,12 @@ void GLFlat::ProcessSector(HWDrawInfo *di, sector_t * frontsector)
port = frontsector->ValidatePortal(sector_t::ceiling);
if ((stack = (port != NULL)))
{
/* as above for floors
if (port->mType == PORTS_STACKEDSECTORTHING)
{
di->AddCeilingStack(sector);
}
*/
alpha = frontsector->GetAlpha(sector_t::ceiling);
}
else
@ -590,7 +607,7 @@ void GLFlat::ProcessSector(HWDrawInfo *di, sector_t * frontsector)
//
stack = false;
if (x.ffloors.Size())
if ((which & SSRF_RENDER3DPLANES) && x.ffloors.Size())
{
player_t * player = players[consoleplayer].camera->player;

View File

@ -41,6 +41,65 @@
sector_t * hw_FakeFlat(sector_t * sec, sector_t * dest, area_t in_area, bool back);
//==========================================================================
//
// Create render list entries from the data generated below
//
//==========================================================================
void HWDrawInfo::DispatchRenderHacks()
{
TMap<int, gl_subsectorrendernode*>::Pair *pair;
TMap<int, gl_floodrendernode*>::Pair *fpair;
TMap<int, gl_subsectorrendernode*>::Iterator ofi(otherFloorPlanes);
GLFlat glflat;
sector_t fakesec;
glflat.section = nullptr;
while (ofi.NextPair(pair))
{
auto sec = hw_FakeFlat(&level.sectors[pair->Key], &fakesec, in_area, false);
glflat.ProcessSector(this, sec, SSRF_RENDERFLOOR | SSRF_PLANEHACK);
}
TMap<int, gl_subsectorrendernode*>::Iterator oci(otherCeilingPlanes);
while (ofi.NextPair(pair))
{
auto sec = hw_FakeFlat(&level.sectors[pair->Key], &fakesec, in_area, false);
glflat.ProcessSector(this, sec, SSRF_RENDERCEILING | SSRF_PLANEHACK);
}
TMap<int, gl_floodrendernode*>::Iterator ffi(floodFloorSegs);
while (ffi.NextPair(fpair))
{
auto sec = hw_FakeFlat(&level.sectors[fpair->Key], &fakesec, in_area, false);
glflat.ProcessSector(this, sec, SSRF_RENDERFLOOR | SSRF_FLOODHACK);
}
TMap<int, gl_floodrendernode*>::Iterator fci(floodCeilingSegs);
while (fci.NextPair(fpair))
{
auto sec = hw_FakeFlat(&level.sectors[fpair->Key], &fakesec, in_area, false);
glflat.ProcessSector(this, sec, SSRF_RENDERCEILING | SSRF_FLOODHACK);
}
}
//==========================================================================
//
// Get the nodes from the render data allocator so we don't have to keep track of them ourselves.
//
//==========================================================================
static gl_subsectorrendernode *NewSubsectorRenderNode()
{
return (gl_subsectorrendernode*)RenderDataAllocator.Alloc(sizeof(gl_subsectorrendernode));
}
static gl_floodrendernode *NewFloodRenderNode()
{
return (gl_floodrendernode*)RenderDataAllocator.Alloc(sizeof(gl_floodrendernode));
}
//==========================================================================
//
// light setup for render hacks.
@ -54,7 +113,8 @@ int HWDrawInfo::SetupLightsForOtherPlane(subsector_t * sub, FDynLightData &light
if (level.HasDynamicLights && !isFullbrightScene())
{
Plane p;
FLightNode * node = sub->lighthead;
FLightNode * node = sub->section->lighthead;
lightdata.Clear();
while (node)
@ -109,35 +169,24 @@ int HWDrawInfo::CreateOtherPlaneVertices(subsector_t *sub, const secplane_t *pla
void HWDrawInfo::AddOtherFloorPlane(int sector, gl_subsectorrendernode * node)
{
int oldcnt = otherfloorplanes.Size();
if (oldcnt <= sector)
{
otherfloorplanes.Resize(sector + 1);
for (int i = oldcnt; i <= sector; i++) otherfloorplanes[i] = nullptr;
}
node->next = otherfloorplanes[sector];
auto pNode = otherFloorPlanes.CheckKey(sector);
node->next = pNode? *pNode : nullptr;
node->lightindex = SetupLightsForOtherPlane(node->sub, lightdata, &level.sectors[sector].floorplane);
node->vertexindex = CreateOtherPlaneVertices(node->sub, &level.sectors[sector].floorplane);
otherfloorplanes[sector] = node;
otherFloorPlanes[sector] = node;
}
void HWDrawInfo::AddOtherCeilingPlane(int sector, gl_subsectorrendernode * node)
{
int oldcnt = otherceilingplanes.Size();
if (oldcnt <= sector)
{
otherceilingplanes.Resize(sector + 1);
for (int i = oldcnt; i <= sector; i++) otherceilingplanes[i] = nullptr;
}
node->next = otherceilingplanes[sector];
auto pNode = otherCeilingPlanes.CheckKey(sector);
node->next = pNode? *pNode : nullptr;
node->lightindex = SetupLightsForOtherPlane(node->sub, lightdata, &level.sectors[sector].ceilingplane);
node->vertexindex = CreateOtherPlaneVertices(node->sub, &level.sectors[sector].ceilingplane);
otherceilingplanes[sector] = node;
otherCeilingPlanes[sector] = node;
}
//==========================================================================
//
// Collects all sectors that might need a fake ceiling
@ -505,12 +554,9 @@ void HWDrawInfo::HandleMissingTextures(area_t in_area)
if (DoOneSectorUpper(MissingUpperTextures[i].sub, MissingUpperTextures[i].Planez, in_area))
{
sector_t * sec = MissingUpperTextures[i].seg->backsector;
// The mere fact that this seg has been added to the list means that the back sector
// will be rendered so we can safely assume that it is already in the render list
for (unsigned int j = 0; j < HandledSubsectors.Size(); j++)
{
gl_subsectorrendernode * node = new gl_subsectorrendernode;
gl_subsectorrendernode * node = NewSubsectorRenderNode();
node->sub = HandledSubsectors[j];
AddOtherCeilingPlane(sec->sectornum, node);
@ -549,12 +595,9 @@ void HWDrawInfo::HandleMissingTextures(area_t in_area)
backsub->validcount = validcount;
if (DoFakeCeilingBridge(backsub, planez, in_area))
{
// The mere fact that this seg has been added to the list means that the back sector
// will be rendered so we can safely assume that it is already in the render list
for (unsigned int j = 0; j < HandledSubsectors.Size(); j++)
{
gl_subsectorrendernode * node = new gl_subsectorrendernode;
gl_subsectorrendernode * node = NewSubsectorRenderNode();
node->sub = HandledSubsectors[j];
AddOtherCeilingPlane(fakesector->sectornum, node);
}
@ -577,12 +620,10 @@ void HWDrawInfo::HandleMissingTextures(area_t in_area)
if (DoOneSectorLower(MissingLowerTextures[i].sub, MissingLowerTextures[i].Planez, in_area))
{
sector_t * sec = MissingLowerTextures[i].seg->backsector;
// The mere fact that this seg has been added to the list means that the back sector
// will be rendered so we can safely assume that it is already in the render list
for (unsigned int j = 0; j < HandledSubsectors.Size(); j++)
{
gl_subsectorrendernode * node = new gl_subsectorrendernode;
gl_subsectorrendernode * node = NewSubsectorRenderNode();
node->sub = HandledSubsectors[j];
AddOtherFloorPlane(sec->sectornum, node);
}
@ -620,12 +661,9 @@ void HWDrawInfo::HandleMissingTextures(area_t in_area)
backsub->validcount = validcount;
if (DoFakeBridge(backsub, planez, in_area))
{
// The mere fact that this seg has been added to the list means that the back sector
// will be rendered so we can safely assume that it is already in the render list
for (unsigned int j = 0; j < HandledSubsectors.Size(); j++)
{
gl_subsectorrendernode * node = new gl_subsectorrendernode;
gl_subsectorrendernode * node = NewSubsectorRenderNode();
node->sub = HandledSubsectors[j];
AddOtherFloorPlane(fakesector->sectornum, node);
}
@ -729,19 +767,13 @@ void HWDrawInfo::PrepareUpperGap(seg_t * seg)
CreateFloodStencilPoly(&ws, vertices.first);
CreateFloodPoly(&ws, vertices.first+4, ws.z2, fakebsector, true);
gl_floodrendernode *node = new gl_floodrendernode;
int oldcnt = floodfloorsegs.Size();
auto sector = fakebsector->sectornum;
if (oldcnt <= sector)
{
floodfloorsegs.Resize(sector + 1);
for (int i = oldcnt; i <= sector; i++) floodfloorsegs[i] = nullptr;
}
gl_floodrendernode *node = NewFloodRenderNode();
auto pNode = floodFloorSegs.CheckKey(fakebsector->sectornum);
node->next = floodfloorsegs[sector];
node->next = pNode? *pNode : nullptr;
node->seg = seg;
node->vertexindex = vertices.second;
floodfloorsegs[sector] = node;
floodFloorSegs[fakebsector->sectornum] = node;
}
@ -793,19 +825,14 @@ void HWDrawInfo::PrepareLowerGap(seg_t * seg)
CreateFloodStencilPoly(&ws, vertices.first);
CreateFloodPoly(&ws, vertices.first+4, ws.z1, fakebsector, false);
gl_floodrendernode *node = new gl_floodrendernode;
int oldcnt = floodceilingsegs.Size();
auto sector = fakebsector->sectornum;
if (oldcnt <= sector)
{
floodceilingsegs.Resize(sector + 1);
for (int i = oldcnt; i <= sector; i++) floodceilingsegs[i] = nullptr;
}
gl_floodrendernode *node = NewFloodRenderNode();
auto pNode = floodCeilingSegs.CheckKey(fakebsector->sectornum);
node->next = pNode? *pNode : nullptr;
node->next = floodceilingsegs[sector];
node->seg = seg;
node->vertexindex = vertices.second;
floodceilingsegs[sector] = node;
floodCeilingSegs[fakebsector->sectornum] = node;
}
//==========================================================================
@ -850,7 +877,6 @@ void HWDrawInfo::PrepareUnhandledMissingTextures()
if (seg->linedef->validcount == validcount) continue; // already done
seg->linedef->validcount = validcount;
if (!(sectorrenderflags[seg->backsector->sectornum] & SSRF_RENDERFLOOR)) continue;
if (seg->frontsector->GetPlaneTexZ(sector_t::floor) > Viewpoint.Pos.Z) continue; // out of sight
if (seg->backsector->transdoor) continue;
if (seg->backsector->GetTexture(sector_t::floor) == skyflatnum) continue;
@ -1120,8 +1146,7 @@ void HWDrawInfo::HandleHackedSubsectors()
{
for(unsigned int j=0;j<HandledSubsectors.Size();j++)
{
gl_subsectorrendernode * node = new gl_subsectorrendernode;
gl_subsectorrendernode * node = NewSubsectorRenderNode();
node->sub = HandledSubsectors[j];
AddOtherFloorPlane(sub->render_sector->sectornum, node);
}
@ -1143,8 +1168,7 @@ void HWDrawInfo::HandleHackedSubsectors()
{
for(unsigned int j=0;j<HandledSubsectors.Size();j++)
{
gl_subsectorrendernode * node = new gl_subsectorrendernode;
gl_subsectorrendernode * node = NewSubsectorRenderNode();
node->sub = HandledSubsectors[j];
AddOtherCeilingPlane(sub->render_sector->sectornum, node);
}
@ -1156,6 +1180,11 @@ void HWDrawInfo::HandleHackedSubsectors()
SubsectorHacks.Clear();
}
// This code was meant to fix the portal in KDIZD's Z1M1, but later versions of that mod do not need it anymore.
// I am not aware of other portals ever having been set up so badly as this one so it probably is not needed anymore.
// Even if needed this must be done differently because this code depends on hacking the render data in a bad way.
#if 0
//==========================================================================
//
// This merges visplanes that lie inside a sector stack together
@ -1296,7 +1325,6 @@ void HWDrawInfo::ProcessSectorStacks(area_t in_area)
{
subsector_t *sub = HandledSubsectors[j];
ss_renderflags[sub->Index()] &= ~SSRF_RENDERCEILING;
sub->sector->ibocount = -1; // cannot render this sector in one go.
if (sub->portalcoverage[sector_t::ceiling].subsectors == NULL)
{
@ -1307,7 +1335,7 @@ void HWDrawInfo::ProcessSectorStacks(area_t in_area)
if (sec->GetAlpha(sector_t::ceiling) != 0 && sec->GetTexture(sector_t::ceiling) != skyflatnum)
{
gl_subsectorrendernode * node = new gl_subsectorrendernode;
gl_subsectorrendernode * node = NewSubsectorRenderNode();
node->sub = sub;
AddOtherCeilingPlane(sec->sectornum, node);
}
@ -1342,7 +1370,6 @@ void HWDrawInfo::ProcessSectorStacks(area_t in_area)
{
subsector_t *sub = HandledSubsectors[j];
ss_renderflags[sub->Index()] &= ~SSRF_RENDERFLOOR;
sub->sector->ibocount = -1; // cannot render this sector in one go.
if (sub->portalcoverage[sector_t::floor].subsectors == NULL)
{
@ -1353,7 +1380,7 @@ void HWDrawInfo::ProcessSectorStacks(area_t in_area)
if (sec->GetAlpha(sector_t::floor) != 0 && sec->GetTexture(sector_t::floor) != skyflatnum)
{
gl_subsectorrendernode * node = new gl_subsectorrendernode;
gl_subsectorrendernode * node = NewSubsectorRenderNode();
node->sub = sub;
AddOtherFloorPlane(sec->sectornum, node);
}
@ -1366,21 +1393,4 @@ void HWDrawInfo::ProcessSectorStacks(area_t in_area)
CeilingStacks.Clear();
}
//==========================================================================
//
//
//
//==========================================================================
void HWDrawInfo::AddSubsectorToPortal(FSectorPortalGroup *ptg, subsector_t *sub)
{
auto portal = FindPortal(ptg);
if (!portal)
{
portal = new HWScenePortal(screen->mPortalState, new HWSectorStackPortal(ptg));
Portals.Push(portal);
}
auto ptl = static_cast<HWSectorStackPortal*>(static_cast<HWScenePortal*>(portal)->mScene);
ptl->AddSubsector(sub);
}
#endif

View File

@ -96,6 +96,11 @@ FSkyVertexBuffer::FSkyVertexBuffer()
mVertexBuffer->SetData(mVertices.Size() * sizeof(FSkyVertex), &mVertices[0], true);
}
FSkyVertexBuffer::~FSkyVertexBuffer()
{
delete mVertexBuffer;
}
//-----------------------------------------------------------------------------
//
//

View File

@ -67,6 +67,7 @@ public:
public:
FSkyVertexBuffer();
~FSkyVertexBuffer();
void SetupMatrices(FMaterial *tex, float x_offset, float y_offset, bool mirror, int mode, VSMatrix &modelmatrix, VSMatrix &textureMatrix);
std::pair<IVertexBuffer *, IIndexBuffer *> GetBufferObjects() const
{

View File

@ -131,11 +131,11 @@ void HWDrawInfo::GetDynSpriteLight(AActor *thing, particle_t *particle, float *o
{
if (thing != NULL)
{
GetDynSpriteLight(thing, (float)thing->X(), (float)thing->Y(), (float)thing->Center(), thing->subsector->lighthead, thing->Sector->PortalGroup, out);
GetDynSpriteLight(thing, (float)thing->X(), (float)thing->Y(), (float)thing->Center(), thing->section->lighthead, thing->Sector->PortalGroup, out);
}
else if (particle != NULL)
{
GetDynSpriteLight(NULL, (float)particle->Pos.X, (float)particle->Pos.Y, (float)particle->Pos.Z, particle->subsector->lighthead, particle->subsector->sector->PortalGroup, out);
GetDynSpriteLight(NULL, (float)particle->Pos.X, (float)particle->Pos.Y, (float)particle->Pos.Z, particle->subsector->section->lighthead, particle->subsector->sector->PortalGroup, out);
}
}
@ -157,10 +157,13 @@ void hw_GetDynModelLight(AActor *self, FDynLightData &modellightdata)
float y = (float)self->Y();
float z = (float)self->Center();
float radiusSquared = (float)(self->renderradius * self->renderradius);
dl_validcount++;
BSPWalkCircle(x, y, radiusSquared, [&](subsector_t *subsector) // Iterate through all subsectors potentially touched by actor
{
FLightNode * node = subsector->lighthead;
auto section = subsector->section;
if (section->validcount == dl_validcount) return; // already done from a previous subsector.
FLightNode * node = section->lighthead;
while (node) // check all lights touching a subsector
{
ADynamicLight *light = node->lightsource;

View File

@ -302,7 +302,7 @@ void GLWall::SetupLights(HWDrawInfo *di, FDynLightData &lightdata)
else if (sub)
{
// Polobject segs cannot be checked per sidedef so use the subsector instead.
node = sub->lighthead;
node = sub->section->lighthead;
}
else node = NULL;

View File

@ -670,10 +670,12 @@ xx(Owner)
xx(HealthFloor)
xx(HealthCeiling)
xx(Health3D)
xx(DamageSpecial)
xx(DeathSpecial)
xx(HealthFloorGroup)
xx(HealthCeilingGroup)
xx(Health3DGroup)
xx(HealthGroup)
// USDF keywords

View File

@ -358,6 +358,7 @@ bool P_CheckFor3DFloorHit(AActor * mo, double z, bool trigger)
if (fabs(z - rover->top.plane->ZatPoint(mo)) < EQUAL_EPSILON)
{
mo->BlockingFloor = rover->model;
mo->Blocking3DFloor = rover->model;
if (trigger) rover->model->TriggerSectorActions (mo, SECSPAC_HitFloor);
return true;
}
@ -385,6 +386,7 @@ bool P_CheckFor3DCeilingHit(AActor * mo, double z, bool trigger)
if(fabs(z - rover->bottom.plane->ZatPoint(mo)) < EQUAL_EPSILON)
{
mo->BlockingCeiling = rover->model;
mo->Blocking3DFloor = rover->model;
if (trigger) rover->model->TriggerSectorActions (mo, SECSPAC_HitCeiling);
return true;
}
@ -762,8 +764,10 @@ void P_LineOpening_XFloors (FLineOpening &open, AActor * thing, const line_t *li
FTextureID highestfloorpic;
int highestfloorterrain = -1;
FTextureID lowestceilingpic;
sector_t *lowestceilingsec = NULL, *highestfloorsec = NULL;
sector_t *lowestceilingsec = nullptr, *highestfloorsec = nullptr;
secplane_t *highestfloorplanes[2] = { &open.frontfloorplane, &open.backfloorplane };
F3DFloor *lowestceilingffloor = nullptr;
F3DFloor *highestfloorffloor = nullptr;
highestfloorpic.SetInvalid();
lowestceilingpic.SetInvalid();
@ -788,6 +792,7 @@ void P_LineOpening_XFloors (FLineOpening &open, AActor * thing, const line_t *li
lowestceiling = ff_bottom;
lowestceilingpic = *rover->bottom.texture;
lowestceilingsec = j == 0 ? linedef->frontsector : linedef->backsector;
lowestceilingffloor = rover;
}
if(delta1 <= delta2 && (!restrict || thing->Z() >= ff_top))
@ -798,6 +803,7 @@ void P_LineOpening_XFloors (FLineOpening &open, AActor * thing, const line_t *li
highestfloorpic = *rover->top.texture;
highestfloorterrain = rover->model->GetTerrain(rover->top.isceiling);
highestfloorsec = j == 0 ? linedef->frontsector : linedef->backsector;
highestfloorffloor = rover;
}
if (ff_top > highestfloorplanes[j]->ZatPoint(x, y))
{
@ -814,6 +820,7 @@ void P_LineOpening_XFloors (FLineOpening &open, AActor * thing, const line_t *li
open.floorpic = highestfloorpic;
open.floorterrain = highestfloorterrain;
open.bottomsec = highestfloorsec;
open.bottomffloor = highestfloorffloor;
}
if (highestfloorplanes[0] != &open.frontfloorplane)
{
@ -831,6 +838,7 @@ void P_LineOpening_XFloors (FLineOpening &open, AActor * thing, const line_t *li
open.top = lowestceiling;
open.ceilingpic = lowestceilingpic;
open.topsec = lowestceilingsec;
open.topffloor = lowestceilingffloor;
}
open.lowfloor = MIN(lowestfloor[0], lowestfloor[1]);

View File

@ -6862,6 +6862,11 @@ doplaysound: if (funcIndex == ACSF_PlayActorSound)
return (ss->healthfloorgroup && (grp = P_GetHealthGroup(ss->healthfloorgroup)))
? grp->health : ss->healthfloor;
}
else if (part == SECPART_3D)
{
return (ss->health3dgroup && (grp = P_GetHealthGroup(ss->health3dgroup)))
? grp->health : ss->health3d;
}
return 0;
}

View File

@ -1,3 +1,35 @@
/*
**
**
**---------------------------------------------------------------------------
** Copyright 2018 ZZYZX
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions
** are met:
**
** 1. Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** 2. Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** 3. The name of the author may not be used to endorse or promote products
** derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**---------------------------------------------------------------------------
**
*/
#include "p_spec.h"
#include "g_levellocals.h"
#include "p_destructible.h"
@ -10,15 +42,16 @@
#include "p_maputl.h"
#include "c_cvars.h"
#include "serializer.h"
#include "vm.h"
#include "events.h"
//==========================================================================
//
// [ZZ] Geometry damage logic callbacks
//
//==========================================================================
void P_SetHealthGroupHealth(int group, int health)
void P_SetHealthGroupHealth(FHealthGroup* grp, int health)
{
FHealthGroup* grp = P_GetHealthGroup(group);
if (!grp) return;
grp->health = health;
@ -33,14 +66,21 @@ void P_SetHealthGroupHealth(int group, int health)
for (unsigned i = 0; i < grp->sectors.Size(); i++)
{
sector_t* lsector = grp->sectors[i];
if (lsector->healthceilinggroup == group)
if (lsector->healthceilinggroup == grp->id)
lsector->healthceiling = health;
if (lsector->healthfloorgroup == group)
if (lsector->healthfloorgroup == grp->id)
lsector->healthfloor = health;
if (lsector->health3dgroup == grp->id)
lsector->health3d = health;
}
}
void P_DamageHealthGroup(FHealthGroup* grp, void* object, AActor* source, int damage, FName damagetype, int side, int part, DVector3 position)
void P_SetHealthGroupHealth(int id, int health)
{
P_SetHealthGroupHealth(P_GetHealthGroup(id), health);
}
void P_DamageHealthGroup(FHealthGroup* grp, void* object, AActor* source, int damage, FName damagetype, int side, int part, DVector3 position, bool isradius)
{
if (!grp) return;
int group = grp->id;
@ -52,7 +92,7 @@ void P_DamageHealthGroup(FHealthGroup* grp, void* object, AActor* source, int da
if (lline == object)
continue;
lline->health = grp->health + damage;
P_DamageLinedef(lline, source, damage, damagetype, side, position, false);
P_DamageLinedef(lline, source, damage, damagetype, side, position, isradius, false);
}
//
for (unsigned i = 0; i < grp->sectors.Size(); i++)
@ -62,19 +102,35 @@ void P_DamageHealthGroup(FHealthGroup* grp, void* object, AActor* source, int da
if (lsector->healthceilinggroup == group && (lsector != object || part != SECPART_Ceiling))
{
lsector->healthceiling = grp->health + damage;
P_DamageSector(lsector, source, damage, damagetype, SECPART_Ceiling, position, false);
P_DamageSector(lsector, source, damage, damagetype, SECPART_Ceiling, position, isradius, false);
}
if (lsector->healthfloorgroup == group && (lsector != object || part != SECPART_Floor))
{
lsector->healthfloor = grp->health + damage;
P_DamageSector(lsector, source, damage, damagetype, SECPART_Floor, position, false);
P_DamageSector(lsector, source, damage, damagetype, SECPART_Floor, position, isradius, false);
}
if (lsector->health3dgroup == group && (lsector != object || part != SECPART_3D))
{
lsector->health3d = grp->health + damage;
P_DamageSector(lsector, source, damage, damagetype, SECPART_3D, position, isradius, false);
}
}
}
void P_DamageLinedef(line_t* line, AActor* source, int damage, FName damagetype, int side, DVector3 position, bool dogroups)
void P_DamageLinedef(line_t* line, AActor* source, int damage, FName damagetype, int side, DVector3 position, bool isradius, bool dogroups)
{
if (damage < 0) damage = 0;
if (dogroups)
{
damage = E_WorldLineDamaged(line, source, damage, damagetype, side, position, isradius);
if (damage < 0) damage = 0;
}
if (!damage) return;
line->health -= damage;
if (line->health < 0) line->health = 0;
@ -92,24 +148,58 @@ void P_DamageLinedef(line_t* line, AActor* source, int damage, FName damagetype,
FHealthGroup* grp = P_GetHealthGroup(line->healthgroup);
if (grp)
grp->health = line->health;
P_DamageHealthGroup(grp, line, source, damage, damagetype, side, -1, position);
P_DamageHealthGroup(grp, line, source, damage, damagetype, side, -1, position, isradius);
}
//Printf("P_DamageLinedef: %d damage (type=%s, source=%p), new health = %d\n", damage, damagetype.GetChars(), source, line->health);
}
void P_DamageSector(sector_t* sector, AActor* source, int damage, FName damagetype, int part, DVector3 position, bool dogroups)
void P_DamageSector(sector_t* sector, AActor* source, int damage, FName damagetype, int part, DVector3 position, bool isradius, bool dogroups)
{
int sectorhealth = (part == SECPART_Ceiling) ? sector->healthceiling : sector->healthfloor;
int newhealth = sectorhealth - damage;
if (damage < 0) damage = 0;
if (dogroups)
{
damage = E_WorldSectorDamaged(sector, source, damage, damagetype, part, position, isradius);
if (damage < 0) damage = 0;
}
if (!damage) return;
int* sectorhealth;
int group;
int dmg;
int dth;
switch (part)
{
case SECPART_Ceiling:
sectorhealth = &sector->healthceiling;
group = sector->healthceilinggroup;
dmg = SECSPAC_DamageCeiling;
dth = SECSPAC_DeathCeiling;
break;
case SECPART_Floor:
sectorhealth = &sector->healthfloor;
group = sector->healthfloorgroup;
dmg = SECSPAC_DamageFloor;
dth = SECSPAC_DeathFloor;
break;
case SECPART_3D:
sectorhealth = &sector->health3d;
group = sector->health3dgroup;
dmg = SECSPAC_Damage3D;
dth = SECSPAC_Death3D;
break;
default:
return;
}
int newhealth = *sectorhealth - damage;
if (newhealth < 0) newhealth = 0;
if (part == SECPART_Ceiling)
sector->healthceiling = newhealth;
else sector->healthfloor = newhealth;
*sectorhealth = newhealth;
// callbacks here
int dmg = (part == SECPART_Ceiling) ? SECSPAC_DamageCeiling : SECSPAC_DamageFloor;
int dth = (part == SECPART_Ceiling) ? SECSPAC_DeathCeiling : SECSPAC_DeathFloor;
if (sector->SecActTarget)
{
sector->TriggerSectorActions(source, dmg);
@ -117,13 +207,12 @@ void P_DamageSector(sector_t* sector, AActor* source, int damage, FName damagety
sector->TriggerSectorActions(source, dth);
}
int group = (part == SECPART_Ceiling) ? sector->healthceilinggroup : sector->healthfloorgroup;
if (dogroups && group)
{
FHealthGroup* grp = P_GetHealthGroup(group);
if (grp)
grp->health = newhealth;
P_DamageHealthGroup(grp, sector, source, damage, damagetype, 0, part, position);
P_DamageHealthGroup(grp, sector, source, damage, damagetype, 0, part, position, isradius);
}
//Printf("P_DamageSector: %d damage (type=%s, position=%s, source=%p), new health = %d\n", damage, damagetype.GetChars(), (part == SECPART_Ceiling) ? "ceiling" : "floor", source, newhealth);
@ -222,6 +311,14 @@ void P_GeometryLineAttack(FTraceResults& trace, AActor* thing, int damage, FName
{
// [ZZ] hitscan geometry damage logic
//
// check 3d floor, but still allow the wall to take generic damage
if (trace.HitType == TRACE_HitWall && trace.Tier == TIER_FFloor)
{
if (trace.ffloor && trace.ffloor->model && trace.ffloor->model->health3d)
P_DamageSector(trace.ffloor->model, thing, damage, damageType, SECPART_3D, trace.HitPos, false, true);
}
if (trace.HitType == TRACE_HitWall && P_CheckLinedefVulnerable(trace.Line, trace.Side))
{
if (trace.Tier == TIER_Lower || trace.Tier == TIER_Upper) // process back sector health if any
@ -234,18 +331,45 @@ void P_GeometryLineAttack(FTraceResults& trace, AActor* thing, int damage, FName
sectorhealth = backsector->healthceiling;
if (sectorhealth > 0)
{
P_DamageSector(backsector, thing, damage, damageType, (trace.Tier == TIER_Upper) ? SECPART_Ceiling : SECPART_Floor, trace.HitPos);
P_DamageSector(backsector, thing, damage, damageType, (trace.Tier == TIER_Upper) ? SECPART_Ceiling : SECPART_Floor, trace.HitPos, false, true);
}
}
// always process linedef health if any
if (trace.Line->health > 0)
{
P_DamageLinedef(trace.Line, thing, damage, damageType, trace.Side, trace.HitPos);
P_DamageLinedef(trace.Line, thing, damage, damageType, trace.Side, trace.HitPos, false, true);
}
// fake floors are not handled
}
else if (trace.HitType == TRACE_HitFloor || trace.HitType == TRACE_HitCeiling)
{
// check for 3d floors. if a 3d floor was hit, it'll block any interaction with the sector planes at the same point, if present.
// i.e. if there are 3d floors at the same height as the sector's real floor/ceiling, and they blocked the shot, then it won't damage.
bool hit3dfloors = false;
sector_t* sector = trace.Sector;
for (auto f : sector->e->XFloor.ffloors)
{
if (!(f->flags & FF_EXISTS)) continue;
if (!(f->flags & FF_SOLID) || (f->flags & FF_SHOOTTHROUGH)) continue;
if (!f->model) continue;
if (trace.HitType == TRACE_HitFloor && fabs(f->top.plane->ZatPoint(trace.HitPos.XY())-trace.HitPos.Z) <= EQUAL_EPSILON)
{
if (f->model->health3d)
P_DamageSector(f->model, thing, damage, damageType, SECPART_3D, trace.HitPos, false, true);
hit3dfloors = true;
}
else if (trace.HitType == TRACE_HitCeiling && fabs(f->bottom.plane->ZatPoint(trace.HitPos.XY())-trace.HitPos.Z) <= EQUAL_EPSILON)
{
if (f->model->health3d)
P_DamageSector(f->model, thing, damage, damageType, SECPART_3D, trace.HitPos, false, true);
hit3dfloors = true;
}
}
if (hit3dfloors)
return;
int sectorhealth = 0;
if (trace.HitType == TRACE_HitFloor && trace.Sector->healthfloor > 0 && P_CheckSectorVulnerable(trace.Sector, SECPART_Floor))
sectorhealth = trace.Sector->healthfloor;
@ -253,7 +377,7 @@ void P_GeometryLineAttack(FTraceResults& trace, AActor* thing, int damage, FName
sectorhealth = trace.Sector->healthceiling;
if (sectorhealth > 0)
{
P_DamageSector(trace.Sector, thing, damage, damageType, (trace.HitType == TRACE_HitCeiling) ? SECPART_Ceiling : SECPART_Floor, trace.HitPos);
P_DamageSector(trace.Sector, thing, damage, damageType, (trace.HitType == TRACE_HitCeiling) ? SECPART_Ceiling : SECPART_Floor, trace.HitPos, false, true);
}
}
}
@ -290,16 +414,23 @@ static DVector2 PGRA_ClosestPointOnLine2D(DVector2 x, DVector2 p1, DVector2 p2)
return p1 + p2p1.Unit() * r;
}
static void PGRA_InsertIfCloser(TMap<int, pgra_data_t>& damageGroupPos, int group, DVector3 pt, DVector3 check, sector_t* checksector, sector_t* sector, line_t* line, int secpart)
static bool PGRA_CheckExplosionBlocked(DVector3 pt, DVector3 check, sector_t* checksector)
{
// simple solid geometry sight check between "check" and "pt"
// expected - Trace hits nothing
check.Z += EQUAL_EPSILON; // this is so that floor under the rocket doesn't block explosion
DVector3 ptVec = (pt - check);
double ptDst = ptVec.Length() - 0.5;
ptVec.MakeUnit();
FTraceResults res;
bool isblocked = Trace(check, checksector, ptVec, ptDst, 0, 0xFFFFFFFF, nullptr, res);
if (isblocked) return;
bool isblocked = Trace(check, checksector, ptVec, ptDst, 0, ML_BLOCKEVERYTHING, nullptr, res);
return isblocked;
}
static void PGRA_InsertIfCloser(TMap<int, pgra_data_t>& damageGroupPos, int group, DVector3 pt, DVector3 check, sector_t* checksector, sector_t* sector, line_t* line, int secpart)
{
if (PGRA_CheckExplosionBlocked(pt, check, checksector))
return;
pgra_data_t* existing = damageGroupPos.CheckKey(group);
// not present or distance is closer
@ -337,7 +468,7 @@ void P_GeometryRadiusAttack(AActor* bombspot, AActor* bombsource, int bombdamage
DVector3 spotTo = bombspot->Pos() - srcsector->ceilingplane.Normal() * dstceiling;
int grp = srcsector->healthceilinggroup;
if (grp <= 0)
grp = 0x80000000 | (srcsector->sectornum & 0x7FFFFFFF);
grp = 0x80000000 | (srcsector->sectornum & 0x0FFFFFFF);
PGRA_InsertIfCloser(damageGroupPos, grp, spotTo, bombspot->Pos(), srcsector, srcsector, nullptr, SECPART_Ceiling);
}
@ -347,12 +478,48 @@ void P_GeometryRadiusAttack(AActor* bombspot, AActor* bombsource, int bombdamage
DVector3 spotTo = bombspot->Pos() - srcsector->floorplane.Normal() * dstfloor;
int grp = srcsector->healthfloorgroup;
if (grp <= 0)
grp = 0x40000000 | (srcsector->sectornum & 0x7FFFFFFF);
grp = 0x40000000 | (srcsector->sectornum & 0x0FFFFFFF);
PGRA_InsertIfCloser(damageGroupPos, grp, spotTo, bombspot->Pos(), srcsector, srcsector, nullptr, SECPART_Floor);
}
for (auto f : srcsector->e->XFloor.ffloors)
{
if (!(f->flags & FF_EXISTS)) continue;
if (!(f->flags & FF_SOLID)) continue;
if (!f->model || !f->model->health3d) continue;
double ff_top = f->top.plane->ZatPoint(bombspot->Pos());
double ff_bottom = f->bottom.plane->ZatPoint(bombspot->Pos());
if (ff_top < ff_bottom) // ignore eldritch geometry
continue;
int grp = f->model->health3dgroup;
if (grp <= 0)
grp = 0x20000000 | (f->model->sectornum & 0x0FFFFFFF);
DVector3 spotTo;
if (bombspot->Z() < ff_bottom) // use bottom plane
{
double dst = f->bottom.plane->Normal() | (bombspot->Pos() + f->bottom.plane->Normal()*f->bottom.plane->D);
spotTo = bombspot->Pos() - f->bottom.plane->Normal() * dst;
}
else if (bombspot->Z() > ff_top) // use top plane
{
double dst = f->top.plane->Normal() | (bombspot->Pos() + f->top.plane->Normal()*f->top.plane->D);
spotTo = bombspot->Pos() - f->top.plane->Normal() * dst;
}
else // explosion right inside the floor. do 100% damage
{
spotTo = bombspot->Pos();
}
PGRA_InsertIfCloser(damageGroupPos, grp, spotTo, bombspot->Pos(), srcsector, f->model, nullptr, SECPART_3D);
}
// enumerate all lines around
FBoundingBox bombbox(bombspot->X(), bombspot->Y(), bombdistance * 16);
FBoundingBox bombbox(bombspot->X(), bombspot->Y(), bombdistance);
FBlockLinesIterator it(bombbox);
line_t* ln;
int vc = validcount;
@ -370,7 +537,7 @@ void P_GeometryRadiusAttack(AActor* bombspot, AActor* bombsource, int bombdamage
sector_t* sector = side->sector;
side_t* otherside = ln->sidedef[!sd];
sector_t* othersector = otherside ? otherside->sector : nullptr;
if (!ln->health && (!othersector || (!othersector->healthfloor && !othersector->healthceiling)))
if (!ln->health && (!othersector || (!othersector->healthfloor && !othersector->healthceiling && !othersector->e->XFloor.ffloors.Size())))
continue; // non-interactive geometry
DVector2 to2d = PGRA_ClosestPointOnLine2D(bombspot->Pos().XY(), side->V1()->p, side->V2()->p);
@ -378,13 +545,15 @@ void P_GeometryRadiusAttack(AActor* bombspot, AActor* bombsource, int bombdamage
double distto2d = (to2d - pos2d).Length();
double z_top1, z_top2, z_bottom1, z_bottom2; // here, z_top1 is closest to the ceiling, and z_bottom1 is closest to the floor.
z_top1 = sector->ceilingplane.ZatPoint(to2d);
z_top2 = othersector ? othersector->ceilingplane.ZatPoint(to2d) : z_top1;
z_bottom1 = sector->floorplane.ZatPoint(to2d);
z_bottom2 = othersector ? othersector->floorplane.ZatPoint(to2d) : z_bottom1;
DVector3 to3d_fullheight(to2d.X, to2d.Y, clamp(bombspot->Z(), z_bottom1, z_top1));
if (ln->health && P_CheckLinedefVulnerable(ln, sd))
{
bool cantdamage = false;
bool linefullheight = othersector && !!(ln->flags & (ML_BLOCKEVERYTHING));
bool linefullheight = !othersector || !!(ln->flags & (ML_BLOCKEVERYTHING));
// decide specific position to affect on a line.
if (!linefullheight)
{
@ -410,7 +579,7 @@ void P_GeometryRadiusAttack(AActor* bombspot, AActor* bombsource, int bombdamage
{
PGRA_InsertIfCloser(damageGroupPos, ln->healthgroup, to3d_fullheight, bombspot->Pos(), srcsector, nullptr, ln, -1);
}
else
else if (!PGRA_CheckExplosionBlocked(to3d_fullheight, bombspot->Pos(), srcsector))
{
// otherwise just damage line
double dst = (to3d_fullheight - bombspot->Pos()).Length();
@ -422,36 +591,59 @@ void P_GeometryRadiusAttack(AActor* bombspot, AActor* bombsource, int bombdamage
if (bombsource == bombspot)
damage = (int)(damage * splashfactor);
}
P_DamageLinedef(ln, bombsource, damage, damagetype, sd, to3d_fullheight);
P_DamageLinedef(ln, bombsource, damage, damagetype, sd, to3d_fullheight, true, true);
}
}
}
if (othersector && othersector->healthceiling && P_CheckLinedefVulnerable(ln, sd, SECPART_Ceiling))
{
z_top2 = othersector->ceilingplane.ZatPoint(to2d);
if (z_top2 < z_top1) // we have front side to hit against
{
DVector3 to3d_upper(to2d.X, to2d.Y, clamp(bombspot->Z(), z_top2, z_top1));
int grp = othersector->healthceilinggroup;
if (grp <= 0)
grp = 0x80000000 | (othersector->sectornum & 0x7FFFFFFF);
grp = 0x80000000 | (othersector->sectornum & 0x0FFFFFFF);
PGRA_InsertIfCloser(damageGroupPos, grp, to3d_upper, bombspot->Pos(), srcsector, othersector, nullptr, SECPART_Ceiling);
}
}
if (othersector && othersector->healthfloor && P_CheckLinedefVulnerable(ln, sd, SECPART_Floor))
{
z_bottom2 = othersector->floorplane.ZatPoint(to2d);
if (z_bottom2 > z_bottom1) // we have front side to hit against
{
DVector3 to3d_lower(to2d.X, to2d.Y, clamp(bombspot->Z(), z_bottom1, z_bottom2));
int grp = othersector->healthfloorgroup;
if (grp <= 0)
grp = 0x40000000 | (othersector->sectornum & 0x7FFFFFFF);
grp = 0x40000000 | (othersector->sectornum & 0x0FFFFFFF);
PGRA_InsertIfCloser(damageGroupPos, grp, to3d_lower, bombspot->Pos(), srcsector, othersector, nullptr, SECPART_Floor);
}
}
// check 3d floors
if (othersector)
{
for (auto f : othersector->e->XFloor.ffloors)
{
if (!(f->flags & FF_EXISTS)) continue;
if (!(f->flags & FF_SOLID)) continue;
if (!f->model || !f->model->health3d) continue;
// 3d floors over real ceiling, or under real floor, are ignored
double z_ff_top = clamp(f->top.plane->ZatPoint(to2d), z_bottom2, z_top2);
double z_ff_bottom = clamp(f->bottom.plane->ZatPoint(to2d), z_bottom2, z_top2);
if (z_ff_top < z_ff_bottom)
continue; // also ignore eldritch geometry
DVector3 to3d_ffloor(to2d.X, to2d.Y, clamp(bombspot->Z(), z_ff_bottom, z_ff_top));
int grp = f->model->health3dgroup;
if (grp <= 0)
grp = 0x20000000 | (f->model->sectornum & 0x0FFFFFFF);
PGRA_InsertIfCloser(damageGroupPos, grp, to3d_ffloor, bombspot->Pos(), srcsector, f->model, nullptr, SECPART_3D);
}
}
}
// damage health groups and sectors.
@ -480,23 +672,124 @@ void P_GeometryRadiusAttack(AActor* bombspot, AActor* bombsource, int bombdamage
if (grp & 0x80000000) // sector ceiling
{
assert(damageGroupPair->Value.sector != nullptr);
P_DamageSector(damageGroupPair->Value.sector, bombsource, damage, damagetype, SECPART_Ceiling, pos);
P_DamageSector(damageGroupPair->Value.sector, bombsource, damage, damagetype, SECPART_Ceiling, pos, true, true);
}
else if (grp & 0x40000000) // sector floor
{
assert(damageGroupPair->Value.sector != nullptr);
P_DamageSector(damageGroupPair->Value.sector, bombsource, damage, damagetype, SECPART_Floor, pos);
P_DamageSector(damageGroupPair->Value.sector, bombsource, damage, damagetype, SECPART_Floor, pos, true, true);
}
else if (grp & 0x20000000) // sector 3d
{
assert(damageGroupPair->Value.sector != nullptr);
P_DamageSector(damageGroupPair->Value.sector, bombsource, damage, damagetype, SECPART_3D, pos, true, true);
}
else
{
assert((damageGroupPair->Value.sector != nullptr) != (damageGroupPair->Value.line != nullptr));
if (damageGroupPair->Value.line != nullptr)
P_DamageLinedef(damageGroupPair->Value.line, bombsource, damage, damagetype, P_PointOnLineSide(pos.XY(), damageGroupPair->Value.line), pos);
else P_DamageSector(damageGroupPair->Value.sector, bombsource, damage, damagetype, damageGroupPair->Value.secpart, pos);
P_DamageLinedef(damageGroupPair->Value.line, bombsource, damage, damagetype, P_PointOnLineSide(pos.XY(), damageGroupPair->Value.line), pos, true, true);
else P_DamageSector(damageGroupPair->Value.sector, bombsource, damage, damagetype, damageGroupPair->Value.secpart, pos, true, true);
}
}
}
//==========================================================================
//
// P_ProjectileHitLinedef
//
// Called if P_ExplodeMissile was called against a wall.
//==========================================================================
bool P_ProjectileHitLinedef(AActor* mo, line_t* line)
{
bool washit = false;
// detect 3d floor hit
if (mo->Blocking3DFloor)
{
if (mo->Blocking3DFloor->health3d > 0)
{
P_DamageSector(mo->Blocking3DFloor, mo, mo->GetMissileDamage((mo->flags4 & MF4_STRIFEDAMAGE) ? 3 : 7, 1), mo->DamageType, SECPART_3D, mo->Pos(), false, true);
washit = true;
}
}
int wside = P_PointOnLineSide(mo->Pos(), line);
int oside = !wside;
side_t* otherside = line->sidedef[oside];
// check if hit upper or lower part
if (otherside)
{
sector_t* othersector = otherside->sector;
// find closest pos from line to MO.
// this logic is so that steep slopes work correctly (value at the line is used, instead of value below the rocket actor)
DVector2 moRelPos = line->v1->p - mo->Pos().XY();
DVector2 lineNormal = line->delta.Rotated90CW().Unit();
double moRelDst = lineNormal | moRelPos;
DVector2 moPos = mo->Pos().XY() - lineNormal*fabs(moRelDst);
double otherfloorz = othersector->floorplane.ZatPoint(moPos);
double otherceilingz = othersector->ceilingplane.ZatPoint(moPos);
double zbottom = mo->Pos().Z;
double ztop = mo->Pos().Z + mo->Height;
if (zbottom < (otherfloorz + EQUAL_EPSILON) && othersector->healthfloor > 0 && P_CheckLinedefVulnerable(line, wside, SECPART_Floor))
{
P_DamageSector(othersector, mo, mo->GetMissileDamage((mo->flags4 & MF4_STRIFEDAMAGE) ? 3 : 7, 1), mo->DamageType, SECPART_Floor, mo->Pos(), false, true);
washit = true;
}
if (ztop > (otherceilingz - EQUAL_EPSILON) && othersector->healthceiling > 0 && P_CheckLinedefVulnerable(line, wside, SECPART_Ceiling))
{
P_DamageSector(othersector, mo, mo->GetMissileDamage((mo->flags4 & MF4_STRIFEDAMAGE) ? 3 : 7, 1), mo->DamageType, SECPART_Ceiling, mo->Pos(), false, true);
washit = true;
}
}
if (line->health > 0 && P_CheckLinedefVulnerable(line, wside))
{
P_DamageLinedef(line, mo, mo->GetMissileDamage((mo->flags4 & MF4_STRIFEDAMAGE) ? 3 : 7, 1), mo->DamageType, wside, mo->Pos(), false, true);
washit = true;
}
return washit;
}
// part = -1 means "detect from blocking"
bool P_ProjectileHitPlane(AActor* mo, int part)
{
if (part < 0)
{
if (mo->BlockingCeiling)
part = SECPART_Ceiling;
else if (mo->BlockingFloor)
part = SECPART_Floor;
}
// detect 3d floor hit
if (mo->Blocking3DFloor)
{
if (mo->Blocking3DFloor->health3d > 0)
{
P_DamageSector(mo->Blocking3DFloor, mo, mo->GetMissileDamage((mo->flags4 & MF4_STRIFEDAMAGE) ? 3 : 7, 1), mo->DamageType, SECPART_3D, mo->Pos(), false, true);
return true;
}
return false;
}
if (part == SECPART_Floor && mo->Sector->healthfloor > 0 && P_CheckSectorVulnerable(mo->Sector, SECPART_Floor))
{
P_DamageSector(mo->Sector, mo, mo->GetMissileDamage((mo->flags4 & MF4_STRIFEDAMAGE) ? 3 : 7, 1), mo->DamageType, SECPART_Floor, mo->Pos(), false, true);
return true;
}
else if (part == SECPART_Ceiling && mo->Sector->healthceiling > 0 && P_CheckSectorVulnerable(mo->Sector, SECPART_Ceiling))
{
P_DamageSector(mo->Sector, mo, mo->GetMissileDamage((mo->flags4 & MF4_STRIFEDAMAGE) ? 3 : 7, 1), mo->DamageType, SECPART_Ceiling, mo->Pos(), false, true);
return true;
}
return false;
}
//==========================================================================
//
// P_CheckLinedefVulnerable
@ -525,6 +818,8 @@ bool P_CheckLinedefVulnerable(line_t* line, int side, int sectorpart)
bool P_CheckSectorVulnerable(sector_t* sector, int part)
{
if (part == SECPART_3D)
return true;
FTextureID texture = sector->GetTexture((part == SECPART_Ceiling) ? sector_t::ceiling : sector_t::floor);
secplane_t* plane = (part == SECPART_Ceiling) ? &sector->ceilingplane : &sector->floorplane;
if (texture == skyflatnum)
@ -577,4 +872,206 @@ void P_SerializeHealthGroups(FSerializer& arc)
arc.EndArray();
}
}
// ===================== zscript interface =====================
//
// =============================================================
DEFINE_FIELD_X(HealthGroup, FHealthGroup, id)
DEFINE_FIELD_X(HealthGroup, FHealthGroup, health)
DEFINE_FIELD_X(HealthGroup, FHealthGroup, sectors)
DEFINE_FIELD_X(HealthGroup, FHealthGroup, lines)
DEFINE_ACTION_FUNCTION(FHealthGroup, Find)
{
PARAM_PROLOGUE;
PARAM_INT(id);
FHealthGroup* grp = P_GetHealthGroup(id);
ACTION_RETURN_POINTER(grp);
}
DEFINE_ACTION_FUNCTION(FHealthGroup, SetHealth)
{
PARAM_SELF_STRUCT_PROLOGUE(FHealthGroup);
PARAM_INT(health);
P_SetHealthGroupHealth(self, health);
return 0;
}
// genuine hack. this essentially causes the engine to register a struct called Destructible, and enables use of DEFINE_ACTION_FUNCTION
struct FDestructible { void* none; };
DEFINE_FIELD_X(Destructible, FDestructible, none);
DEFINE_ACTION_FUNCTION(FDestructible, DamageSector)
{
PARAM_PROLOGUE;
PARAM_POINTER(sec, sector_t);
PARAM_OBJECT(source, AActor);
PARAM_INT(damage);
PARAM_NAME(damagetype);
PARAM_INT(part);
PARAM_FLOAT(position_x);
PARAM_FLOAT(position_y);
PARAM_FLOAT(position_z);
PARAM_BOOL(isradius);
P_DamageSector(sec, source, damage, damagetype, part, DVector3(position_x, position_y, position_z), isradius, true);
return 0;
}
DEFINE_ACTION_FUNCTION(FDestructible, DamageLinedef)
{
PARAM_PROLOGUE;
PARAM_POINTER(def, line_t);
PARAM_OBJECT(source, AActor);
PARAM_INT(damage);
PARAM_NAME(damagetype);
PARAM_INT(side);
PARAM_FLOAT(position_x);
PARAM_FLOAT(position_y);
PARAM_FLOAT(position_z);
PARAM_BOOL(isradius);
P_DamageLinedef(def, source, damage, damagetype, side, DVector3(position_x, position_y, position_z), isradius, true);
return 0;
}
DEFINE_ACTION_FUNCTION(FDestructible, GeometryLineAttack)
{
PARAM_PROLOGUE;
PARAM_POINTER(trace, FTraceResults);
PARAM_OBJECT(thing, AActor);
PARAM_INT(damage);
PARAM_NAME(damagetype);
P_GeometryLineAttack(*trace, thing, damage, damagetype);
return 0;
}
DEFINE_ACTION_FUNCTION(FDestructible, GeometryRadiusAttack)
{
PARAM_PROLOGUE;
PARAM_OBJECT(bombspot, AActor);
PARAM_OBJECT(bombsource, AActor);
PARAM_INT(bombdamage);
PARAM_INT(bombdistance);
PARAM_NAME(damagetype);
PARAM_INT(fulldamagedistance);
P_GeometryRadiusAttack(bombspot, bombsource, bombdamage, bombdistance, damagetype, fulldamagedistance);
return 0;
}
DEFINE_ACTION_FUNCTION(FDestructible, ProjectileHitLinedef)
{
PARAM_PROLOGUE;
PARAM_OBJECT(projectile, AActor);
PARAM_POINTER(def, line_t);
ACTION_RETURN_BOOL(P_ProjectileHitLinedef(projectile, def));
}
DEFINE_ACTION_FUNCTION(FDestructible, ProjectileHitPlane)
{
PARAM_PROLOGUE;
PARAM_OBJECT(projectile, AActor);
PARAM_INT(part);
ACTION_RETURN_BOOL(P_ProjectileHitPlane(projectile, part));
}
DEFINE_ACTION_FUNCTION(FDestructible, CheckLinedefVulnerable)
{
PARAM_PROLOGUE;
PARAM_POINTER(def, line_t);
PARAM_INT(side);
PARAM_INT(part);
ACTION_RETURN_BOOL(P_CheckLinedefVulnerable(def, side, part));
}
DEFINE_ACTION_FUNCTION(FDestructible, CheckSectorVulnerable)
{
PARAM_PROLOGUE;
PARAM_POINTER(sec, sector_t);
PARAM_INT(part);
ACTION_RETURN_BOOL(P_CheckSectorVulnerable(sec, part));
}
DEFINE_ACTION_FUNCTION(_Line, GetHealth)
{
PARAM_SELF_STRUCT_PROLOGUE(line_t);
if (self->healthgroup)
{
FHealthGroup* grp = P_GetHealthGroup(self->healthgroup);
if (grp) ACTION_RETURN_INT(grp->health);
}
ACTION_RETURN_INT(self->health);
}
DEFINE_ACTION_FUNCTION(_Line, SetHealth)
{
PARAM_SELF_STRUCT_PROLOGUE(line_t);
PARAM_INT(newhealth);
if (newhealth < 0)
newhealth = 0;
self->health = newhealth;
if (self->healthgroup)
{
FHealthGroup* grp = P_GetHealthGroup(self->healthgroup);
if (grp) P_SetHealthGroupHealth(grp, newhealth);
}
return 0;
}
DEFINE_ACTION_FUNCTION(_Sector, GetHealth)
{
PARAM_SELF_STRUCT_PROLOGUE(sector_t);
PARAM_INT(part);
FHealthGroup* grp;
switch (part)
{
case SECPART_Floor:
ACTION_RETURN_INT((self->healthfloorgroup && (grp = P_GetHealthGroup(self->healthfloorgroup))) ? grp->health : self->healthfloor);
case SECPART_Ceiling:
ACTION_RETURN_INT((self->healthceilinggroup && (grp = P_GetHealthGroup(self->healthceilinggroup))) ? grp->health : self->healthceiling);
case SECPART_3D:
ACTION_RETURN_INT((self->health3dgroup && (grp = P_GetHealthGroup(self->health3dgroup))) ? grp->health : self->health3d);
default:
ACTION_RETURN_INT(0);
}
}
DEFINE_ACTION_FUNCTION(_Sector, SetHealth)
{
PARAM_SELF_STRUCT_PROLOGUE(sector_t);
PARAM_INT(part);
PARAM_INT(newhealth);
if (newhealth < 0)
newhealth = 0;
int group;
int* health;
switch (part)
{
case SECPART_Floor:
group = self->healthfloorgroup;
health = &self->healthfloor;
break;
case SECPART_Ceiling:
group = self->healthceilinggroup;
health = &self->healthceiling;
break;
case SECPART_3D:
group = self->health3dgroup;
health = &self->health3d;
break;
default:
return 0;
}
FHealthGroup* grp = group ? P_GetHealthGroup(group) : nullptr;
*health = newhealth;
if (grp) P_SetHealthGroupHealth(grp, newhealth);
return 0;
}

View File

@ -17,21 +17,25 @@ struct FHealthGroup
enum
{
SECPART_Floor = 0,
SECPART_Ceiling = 1
SECPART_Ceiling = 1,
SECPART_3D = 2
};
void P_InitHealthGroups();
void P_SetHealthGroupHealth(FHealthGroup* group, int health);
void P_SetHealthGroupHealth(int group, int health);
FHealthGroup* P_GetHealthGroup(int id);
FHealthGroup* P_GetHealthGroupOrNew(int id, int startinghealth);
void P_DamageSector(sector_t* sector, AActor* source, int damage, FName damagetype, int part, DVector3 position, bool dogroups = true);
void P_DamageLinedef(line_t* line, AActor* source, int damage, FName damagetype, int side, DVector3 position, bool dogroups = true);
void P_DamageSector(sector_t* sector, AActor* source, int damage, FName damagetype, int part, DVector3 position, bool isradius, bool dogroups);
void P_DamageLinedef(line_t* line, AActor* source, int damage, FName damagetype, int side, DVector3 position, bool isradius, bool dogroups);
void P_GeometryLineAttack(FTraceResults& trace, AActor* thing, int damage, FName damageType);
void P_GeometryRadiusAttack(AActor* bombspot, AActor* bombsource, int bombdamage, int bombdistance, FName damagetype, int fulldamagedistance);
bool P_ProjectileHitLinedef(AActor* projectile, line_t* line);
bool P_ProjectileHitPlane(AActor* projectile, int part);
bool P_CheckLinedefVulnerable(line_t* line, int side, int part = -1);
bool P_CheckSectorVulnerable(sector_t* sector, int part);

View File

@ -3522,6 +3522,12 @@ FUNC(LS_Sector_SetHealth)
if (sector->healthfloorgroup)
P_SetHealthGroupHealth(sector->healthfloorgroup, arg2);
}
else if (arg1 == SECPART_3D)
{
sector->health3d = arg2;
if (sector->health3dgroup)
P_SetHealthGroupHealth(sector->health3dgroup, arg2);
}
}
return true;
}

View File

@ -110,7 +110,8 @@ enum EPuffFlags
PF_MELEERANGE = 2,
PF_TEMPORARY = 4,
PF_HITTHINGBLEED = 8,
PF_NORANDOMZ = 16
PF_NORANDOMZ = 16,
PF_HITSKY = 32
};
AActor *P_SpawnPuff(AActor *source, PClassActor *pufftype, const DVector3 &pos, DAngle hitdir, DAngle particledir, int updown, int flags = 0, AActor *vict = NULL);

View File

@ -1071,6 +1071,8 @@ bool PIT_CheckLine(FMultiBlockLinesIterator &mit, FMultiBlockLinesIterator::Chec
tm.ceilingpic = open.ceilingpic;
tm.ceilingline = ld;
tm.thing->BlockingLine = ld;
if (open.topffloor)
tm.thing->Blocking3DFloor = open.topffloor->model;
}
}
@ -1086,6 +1088,8 @@ bool PIT_CheckLine(FMultiBlockLinesIterator &mit, FMultiBlockLinesIterator::Chec
tm.touchmidtex = open.touchmidtex;
tm.abovemidtex = open.abovemidtex;
tm.thing->BlockingLine = ld;
if (open.bottomffloor)
tm.thing->Blocking3DFloor = open.bottomffloor->model;
}
else if (open.bottom == tm.floorz)
{
@ -1805,6 +1809,10 @@ bool P_CheckPosition(AActor *thing, const DVector2 &pos, FCheckPosition &tm, boo
tm.abovemidtex = false;
validcount++;
// Remove all old entries before returning.
spechit.Clear();
portalhit.Clear();
if ((thing->flags & MF_NOCLIP) && !(thing->flags & MF_SKULLFLY))
return true;
@ -1882,13 +1890,15 @@ bool P_CheckPosition(AActor *thing, const DVector2 &pos, FCheckPosition &tm, boo
// being considered for collision with the player.
validcount++;
// Clear out any residual garbage left behind by PIT_CheckThing induced recursions etc.
spechit.Clear();
portalhit.Clear();
thing->BlockingMobj = NULL;
thing->Height = realHeight;
if (actorsonly || (thing->flags & MF_NOCLIP))
return (thing->BlockingMobj = thingblocker) == NULL;
spechit.Clear();
portalhit.Clear();
FMultiBlockLinesIterator it(pcheck, pos.X, pos.Y, thing->Z(), thing->Height, thing->radius, newsec);
FMultiBlockLinesIterator::CheckResult lcres;
@ -3548,6 +3558,18 @@ bool FSlide::BounceWall(AActor *mo)
return true;
}
// [ZZ] if bouncing missile hits a damageable linedef, it dies
if (P_ProjectileHitLinedef(mo, line) && mo->bouncecount > 0)
{
mo->Vel.Zero();
mo->Speed = 0;
mo->bouncecount = 0;
if (mo->flags & MF_MISSILE)
P_ExplodeMissile(mo, line, nullptr);
else mo->CallDie(nullptr, nullptr);
return true;
}
// The amount of bounces is limited
if (mo->bouncecount>0 && --mo->bouncecount == 0)
{
@ -4623,9 +4645,13 @@ AActor *P_LineAttack(AActor *t1, DAngle angle, double distance,
damageType = puffDefaults->DamageType;
}
int tflags;
if (nointeract || (puffDefaults && puffDefaults->flags6 & MF6_NOTRIGGER)) tflags = TRACE_NoSky;
else tflags = TRACE_NoSky | TRACE_Impact;
uint32_t tflags = TRACE_NoSky | TRACE_Impact;
if (nointeract || (puffDefaults && puffDefaults->flags6 & MF6_NOTRIGGER)) tflags &= ~TRACE_Impact;
if (spawnSky)
{
tflags &= ~TRACE_NoSky;
tflags |= TRACE_HitSky;
}
// [MC] Check the flags and set the position according to what is desired.
// LAF_ABSPOSITION: Treat the offset parameters as direct coordinates.
@ -4683,8 +4709,16 @@ AActor *P_LineAttack(AActor *t1, DAngle angle, double distance,
{
if (trace.HitType != TRACE_HitActor)
{
if (trace.HitType == TRACE_HasHitSky || (trace.HitType == TRACE_HitWall
&& trace.Line->special == Line_Horizon && spawnSky))
{
puffFlags |= PF_HITSKY;
}
P_GeometryLineAttack(trace, t1, damage, damageType);
// position a bit closer for puffs
if (nointeract || trace.HitType != TRACE_HitWall || ((trace.Line->special != Line_Horizon) || spawnSky))
{

View File

@ -242,6 +242,7 @@ void P_LineOpening (FLineOpening &open, AActor *actor, const line_t *linedef, co
open.backfloorplane.SetAtHeight(LINEOPEN_MIN, sector_t::floor);
}
open.topffloor = open.bottomffloor = nullptr;
// Check 3D floors
if (actor != NULL)
{
@ -470,6 +471,7 @@ void AActor::LinkToWorld(FLinkContext *ctx, bool spawningmapthing, sector_t *sec
Sector = sector;
subsector = R_PointInSubsector(Pos()); // this is from the rendering nodes, not the gameplay nodes!
section = subsector->section;
if (!(flags & MF_NOSECTOR))
{

View File

@ -110,6 +110,8 @@ struct FLineOpening
int floorterrain;
bool touchmidtex;
bool abovemidtex;
F3DFloor *topffloor;
F3DFloor *bottomffloor;
};
static const double LINEOPEN_MIN = -FLT_MAX;

View File

@ -296,6 +296,7 @@ DEFINE_FIELD(AActor, lastbump)
DEFINE_FIELD(AActor, DesignatedTeam)
DEFINE_FIELD(AActor, BlockingMobj)
DEFINE_FIELD(AActor, BlockingLine)
DEFINE_FIELD(AActor, Blocking3DFloor)
DEFINE_FIELD(AActor, BlockingCeiling)
DEFINE_FIELD(AActor, BlockingFloor)
DEFINE_FIELD(AActor, PoisonDamage)
@ -348,6 +349,7 @@ DEFINE_FIELD(AActor, BloodTranslation)
DEFINE_FIELD(AActor, RenderHidden)
DEFINE_FIELD(AActor, RenderRequired)
DEFINE_FIELD(AActor, friendlyseeblocks)
DEFINE_FIELD(AActor, SpawnTime)
//==========================================================================
//
@ -480,6 +482,7 @@ void AActor::Serialize(FSerializer &arc)
A("smokecounter", smokecounter)
("blockingmobj", BlockingMobj)
A("blockingline", BlockingLine)
A("blocking3dfloor", Blocking3DFloor)
A("blockingceiling", BlockingCeiling)
A("blockingfloor", BlockingFloor)
A("visibletoteam", VisibleToTeam)
@ -528,8 +531,9 @@ void AActor::Serialize(FSerializer &arc)
A("selfdamagefactor", SelfDamageFactor)
A("stealthalpha", StealthAlpha)
A("renderhidden", RenderHidden)
A("renderrequired", RenderRequired);
A("friendlyseeblocks", friendlyseeblocks);
A("renderrequired", RenderRequired)
A("friendlyseeblocks", friendlyseeblocks)
A("spawntime", SpawnTime);
}
#undef A
@ -1945,26 +1949,7 @@ void P_ExplodeMissile (AActor *mo, line_t *line, AActor *target, bool onsky)
// [ZZ] line damage callback
if (line)
{
int wside = P_PointOnLineSide(mo->Pos(), line);
int oside = !wside;
side_t* otherside = line->sidedef[oside];
// check if hit upper or lower part
if (otherside)
{
sector_t* othersector = otherside->sector;
double otherfloorz = othersector->floorplane.ZatPoint(mo->Pos());
double otherceilingz = othersector->ceilingplane.ZatPoint(mo->Pos());
double actualz = mo->Pos().Z;
if (actualz < otherfloorz && othersector->healthfloor > 0 && P_CheckLinedefVulnerable(line, wside, SECPART_Floor))
P_DamageSector(othersector, mo, mo->GetMissileDamage((mo->flags4 & MF4_STRIFEDAMAGE) ? 3 : 7, 1), mo->DamageType, SECPART_Floor, mo->Pos());
if (actualz > otherceilingz && othersector->healthceiling > 0 && P_CheckLinedefVulnerable(line, wside, SECPART_Ceiling))
P_DamageSector(othersector, mo, mo->GetMissileDamage((mo->flags4 & MF4_STRIFEDAMAGE) ? 3 : 7, 1), mo->DamageType, SECPART_Ceiling, mo->Pos());
}
if (line->health > 0 && P_CheckLinedefVulnerable(line, wside))
{
P_DamageLinedef(line, mo, mo->GetMissileDamage((mo->flags4 & MF4_STRIFEDAMAGE) ? 3 : 7, 1), mo->DamageType, wside, mo->Pos());
}
P_ProjectileHitLinedef(mo, line);
}
if (mo->flags3 & MF3_EXPLOCOUNT)
@ -2142,6 +2127,18 @@ void AActor::PlayBounceSound(bool onfloor)
bool AActor::FloorBounceMissile (secplane_t &plane)
{
// [ZZ] if bouncing missile hits a damageable sector(plane), it dies
if (P_ProjectileHitPlane(this, -1) && bouncecount > 0)
{
Vel.Zero();
Speed = 0;
bouncecount = 0;
if (flags & MF_MISSILE)
P_ExplodeMissile(this, nullptr, nullptr);
else CallDie(nullptr, nullptr);
return true;
}
if (Z() <= floorz && P_HitFloor (this))
{
// Landed in some sort of liquid
@ -2759,13 +2756,11 @@ explode:
}
if (mo->BlockingCeiling) // hit floor or ceiling while XY movement
{
if (mo->BlockingCeiling->healthceiling > 0 && P_CheckSectorVulnerable(mo->BlockingCeiling, SECPART_Ceiling))
P_DamageSector(mo->BlockingCeiling, mo, mo->GetMissileDamage((mo->flags4 & MF4_STRIFEDAMAGE) ? 3 : 7, 1), mo->DamageType, SECPART_Ceiling, mo->Pos());
P_ProjectileHitPlane(mo, SECPART_Ceiling);
}
if (mo->BlockingFloor)
{
if (mo->BlockingFloor->healthfloor > 0 && P_CheckSectorVulnerable(mo->BlockingFloor, SECPART_Floor))
P_DamageSector(mo->BlockingFloor, mo, mo->GetMissileDamage((mo->flags4 & MF4_STRIFEDAMAGE) ? 3 : 7, 1), mo->DamageType, SECPART_Floor, mo->Pos());
P_ProjectileHitPlane(mo, SECPART_Floor);
}
P_ExplodeMissile (mo, mo->BlockingLine, BlockingMobj, onsky);
return Oldfloorz;
@ -3161,8 +3156,7 @@ void P_ZMovement (AActor *mo, double oldfloorz)
}
P_HitFloor (mo);
// hit floor: direct damage callback
if (mo->Sector->healthfloor > 0 && P_CheckSectorVulnerable(mo->Sector, SECPART_Floor))
P_DamageSector(mo->Sector, mo, mo->GetMissileDamage((mo->flags4 & MF4_STRIFEDAMAGE) ? 3 : 7, 1), mo->DamageType, SECPART_Floor, mo->Pos());
P_ProjectileHitPlane(mo, SECPART_Floor);
P_ExplodeMissile (mo, NULL, NULL, onsky);
return;
}
@ -3268,8 +3262,7 @@ void P_ZMovement (AActor *mo, double oldfloorz)
else onsky = true;
}
// hit ceiling: direct damage callback
if (mo->Sector->healthceiling > 0 && P_CheckSectorVulnerable(mo->Sector, SECPART_Ceiling))
P_DamageSector(mo->Sector, mo, mo->GetMissileDamage((mo->flags4 & MF4_STRIFEDAMAGE) ? 3 : 7, 1), mo->DamageType, SECPART_Ceiling, mo->Pos());
P_ProjectileHitPlane(mo, SECPART_Ceiling);
P_ExplodeMissile (mo, NULL, NULL, onsky);
return;
}
@ -4519,6 +4512,7 @@ void AActor::Tick ()
BlockingMobj = nullptr;
sector_t* oldBlockingCeiling = BlockingCeiling;
sector_t* oldBlockingFloor = BlockingFloor;
Blocking3DFloor = nullptr;
BlockingFloor = nullptr;
BlockingCeiling = nullptr;
double oldfloorz = P_XYMovement (this, cumm);
@ -5006,6 +5000,7 @@ AActor *AActor::StaticSpawn (PClassActor *type, const DVector3 &pos, replace_t a
AActor *actor;
actor = static_cast<AActor *>(const_cast<PClassActor *>(type)->CreateNew ());
actor->SpawnTime = level.totaltime;
// Set default dialogue
actor->ConversationRoot = GetConversation(actor->GetClass()->TypeName);
@ -6245,7 +6240,11 @@ AActor *P_SpawnPuff (AActor *source, PClassActor *pufftype, const DVector3 &pos1
// it will enter the crash state. This is used by the StrifeSpark
// and BlasterPuff.
FState *crashstate;
if (!(flags & PF_HITTHING) && (crashstate = puff->FindState(NAME_Crash)) != NULL)
if ((flags & PF_HITSKY) && (crashstate = puff->FindState(NAME_Death, NAME_Sky, true)) != NULL)
{
puff->SetState (crashstate);
}
else if (!(flags & PF_HITTHING) && (crashstate = puff->FindState(NAME_Crash)) != NULL)
{
puff->SetState (crashstate);
}
@ -8059,6 +8058,43 @@ void AActor::SetTranslation(FName trname)
// silently ignore if the name does not exist, this would create some insane message spam otherwise.
}
//==========================================================================
//
// AActor :: GetLevelSpawnTime
//
// Returns the time when this actor was spawned,
// relative to the current level.
//
//==========================================================================
int AActor::GetLevelSpawnTime() const
{
return SpawnTime - level.totaltime + level.time;
}
DEFINE_ACTION_FUNCTION(AActor, GetLevelSpawnTime)
{
PARAM_SELF_PROLOGUE(AActor);
ACTION_RETURN_INT(self->GetLevelSpawnTime());
}
//==========================================================================
//
// AActor :: GetAge
//
// Returns the number of ticks passed since this actor was spawned.
//
//==========================================================================
int AActor::GetAge() const
{
return level.totaltime - SpawnTime;
}
DEFINE_ACTION_FUNCTION(AActor, GetAge)
{
PARAM_SELF_PROLOGUE(AActor);
ACTION_RETURN_INT(self->GetAge());
}
//---------------------------------------------------------------------------
//
// PROP A_RestoreSpecialPosition
@ -8583,5 +8619,8 @@ void PrintMiscActorInfo(AActor *query)
Printf("FriendlySeeBlocks: %d\n", query->friendlyseeblocks);
Printf("Target: %s\n", query->target ? query->target->GetClass()->TypeName.GetChars() : "-");
Printf("Last enemy: %s\n", query->lastenemy ? query->lastenemy->GetClass()->TypeName.GetChars() : "-");
Printf("Spawn time: %d ticks (%f seconds) after game start, %d ticks (%f seconds) after level start\n",
query->SpawnTime, (double) query->SpawnTime / TICRATE,
query->GetLevelSpawnTime(), (double) query->GetLevelSpawnTime() / TICRATE);
}
}

View File

@ -297,6 +297,7 @@ FSerializer &Serialize(FSerializer &arc, const char *key, sector_t &p, sector_t
("scrolls", scroll, nul)
("healthfloor", p.healthfloor, def->healthfloor)
("healthceiling", p.healthceiling, def->healthceiling)
("health3d", p.health3d, def->health3d)
// GZDoom exclusive:
.Array("reflect", p.reflect, def->reflect, 2, true)
.EndObject();

View File

@ -3559,6 +3559,7 @@ void P_FreeLevelData ()
FBehavior::StaticUnloadModules ();
level.sections.Clear();
level.segs.Clear();
level.sectors.Clear();
level.lines.Clear();
@ -4055,6 +4056,8 @@ void P_SetupLevel (const char *lumpname, int position, bool newGame)
if (hasglnodes)
{
P_SetRenderSector();
FixMinisegReferences();
FixHoles();
}
bodyqueslot = 0;
@ -4064,6 +4067,8 @@ void P_SetupLevel (const char *lumpname, int position, bool newGame)
for (i = 0; i < BODYQUESIZE; i++)
bodyque[i] = NULL;
CreateSections(level.sections);
if (!buildmap)
{
// [RH] Spawn slope creating things first.

View File

@ -160,6 +160,9 @@ bool P_LoadGLNodes(MapData * map);
bool P_CheckNodes(MapData * map, bool rebuilt, int buildtime);
bool P_CheckForGLNodes();
void P_SetRenderSector();
void FixMinisegReferences();
void FixHoles();
void ReportUnpairedMinisegs();
struct sidei_t // [RH] Only keep BOOM sidedef init stuff around for init

View File

@ -587,7 +587,7 @@ bool SightCheck::P_SightTraverseIntercepts ()
for (auto rover : lastsector->e->XFloor.ffloors)
{
if ((rover->flags & FF_SOLID) == myseethrough || !(rover->flags & FF_EXISTS)) continue;
if ((rover->flags & FF_SEETHROUGH) == myseethrough || !(rover->flags & FF_EXISTS)) continue;
if ((Flags & SF_IGNOREWATERBOUNDARY) && (rover->flags & FF_SOLID) == 0) continue;
double ff_bottom = rover->bottom.plane->ZatPoint(seeingthing);
@ -637,7 +637,8 @@ bool SightCheck::P_SightPathTraverse ()
for(auto rover : lastsector->e->XFloor.ffloors)
{
if(!(rover->flags & FF_EXISTS)) continue;
if ((Flags & SF_IGNOREWATERBOUNDARY) && (rover->flags & FF_SOLID) == 0) continue;
double ff_bottom=rover->bottom.plane->ZatPoint(sightstart);
double ff_top=rover->top.plane->ZatPoint(sightstart);

View File

@ -938,6 +938,27 @@ bool FTraceInfo::CheckPlane (const secplane_t &plane)
static bool EditTraceResult (uint32_t flags, FTraceResults &res)
{
if (flags & TRACE_HitSky)
{ // Throw away sky hits
if (res.HitType == TRACE_HitFloor || res.HitType == TRACE_HitCeiling)
{
if (res.HitTexture == skyflatnum)
{
res.HitType = TRACE_HasHitSky;
return true;
}
}
else if (res.HitType == TRACE_HitWall)
{
if (res.Tier == TIER_Upper &&
res.Line->frontsector->GetTexture(sector_t::ceiling) == skyflatnum &&
res.Line->backsector->GetTexture(sector_t::ceiling) == skyflatnum)
{
res.HitType = TRACE_HasHitSky;
return true;
}
}
}
if (flags & TRACE_NoSky)
{ // Throw away sky hits
if (res.HitType == TRACE_HitFloor || res.HitType == TRACE_HitCeiling)

View File

@ -52,6 +52,7 @@ enum ETraceResult
TRACE_HitWall,
TRACE_HitActor,
TRACE_CrossingPortal,
TRACE_HasHitSky,
};
enum
@ -98,6 +99,7 @@ enum
TRACE_PortalRestrict= 0x0008, // Cannot go through portals without a static link offset.
TRACE_ReportPortals = 0x0010, // Report any portal crossing to the TraceCallback
TRACE_3DCallback = 0x0020, // [ZZ] use TraceCallback to determine whether we need to go through a line to do 3D floor check, or not. without this, only line flag mask is used
TRACE_HitSky = 0x0040, // Hitting the sky returns TRACE_HasHitSky
};
// return values from callback

View File

@ -1792,6 +1792,10 @@ public:
sec->healthceiling = CheckInt(key);
break;
case NAME_Health3D:
sec->health3d = CheckInt(key);
break;
case NAME_HealthFloorGroup:
sec->healthfloorgroup = CheckInt(key);
break;
@ -1799,6 +1803,10 @@ public:
case NAME_HealthCeilingGroup:
sec->healthceilinggroup = CheckInt(key);
break;
case NAME_Health3DGroup:
sec->health3dgroup = CheckInt(key);
break;
default:
break;

View File

@ -70,7 +70,7 @@ void PolyModelRenderer::AddLights(AActor *actor)
BSPWalkCircle(x, y, radiusSquared, [&](subsector_t *subsector) // Iterate through all subsectors potentially touched by actor
{
FLightNode * node = subsector->lighthead;
FLightNode * node = subsector->section->lighthead;
while (node) // check all lights touching a subsector
{
ADynamicLight *light = node->lightsource;

View File

@ -275,7 +275,7 @@ void RenderPolyPlane::SetDynLights(PolyRenderThread *thread, PolyDrawArgs &args,
return;
}
FLightNode *light_list = sub->lighthead;
FLightNode *light_list = sub->section->lighthead;
auto cameraLight = PolyCameraLight::Instance();
if ((cameraLight->FixedLightLevel() >= 0) || (cameraLight->FixedColormap() != nullptr))

View File

@ -381,7 +381,7 @@ void RenderPolySprite::SetDynlight(AActor *thing, PolyDrawArgs &args)
float lit_red = 0;
float lit_green = 0;
float lit_blue = 0;
auto node = thing->Sector->lighthead;
auto node = thing->section->lighthead;
while (node != nullptr)
{
ADynamicLight *light = node->lightsource;

View File

@ -355,6 +355,9 @@ SystemGLFrameBuffer::SystemGLFrameBuffer(void*, const bool fullscreen)
assert(frameBuffer == nullptr);
frameBuffer = this;
// To be able to use OpenGL functions in SetMode()
ogl_LoadFunctions();
FConsoleWindow::GetInstance().Show(false);
}
@ -527,6 +530,14 @@ void SystemGLFrameBuffer::SetMode(const bool fullscreen, const bool hiDPI)
SetWindowedMode();
}
const NSSize viewSize = I_GetContentViewSize(m_window);
glViewport(0, 0, static_cast<GLsizei>(viewSize.width), static_cast<GLsizei>(viewSize.height));
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
[[NSOpenGLContext currentContext] flushBuffer];
[m_window updateTitle];
if (![m_window isKeyWindow])

797
src/r_data/r_sections.cpp Normal file
View File

@ -0,0 +1,797 @@
//
//---------------------------------------------------------------------------
//
// Copyright(C) 2008-2018 Christoph Oelckers
// All rights reserved.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 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 Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this program. If not, see http://www.gnu.org/licenses/
//
//--------------------------------------------------------------------------
//
#include <future>
#include "i_system.h"
#include "p_local.h"
#include "c_dispatch.h"
#include "r_defs.h"
#include "g_levellocals.h"
#include "r_sections.h"
#include "earcut.hpp"
#include "stats.h"
#include "p_setup.h"
#include "c_dispatch.h"
#include "memarena.h"
using DoublePoint = std::pair<DVector2, DVector2>;
template<> struct THashTraits<DoublePoint>
{
hash_t Hash(const DoublePoint &key)
{
return (hash_t)SuperFastHash((const char*)(const void*)&key, sizeof(key));
}
int Compare(const DoublePoint &left, const DoublePoint &right) { return left != right; }
};
struct WorkSectionLine
{
vertex_t *start;
vertex_t *end;
side_t *sidedef;
seg_t *refseg;
int tempindex;
int mygroup;
WorkSectionLine *partner;
bool flagged;
};
struct WorkSection
{
int sectorindex;
int mapsection;
bool hasminisegs;
TArray<WorkSectionLine*>segments;
TArray<side_t *> originalSides; // The segs will lose some of these while working on them.
TArray<int> subsectors;
};
struct TriangleWorkData
{
BoundingRect boundingBox;
unsigned boundingLoopStart;
};
struct GroupWork
{
WorkSection *section;
TriangleWorkData *work;
int index;
};
struct Group
{
TArray<GroupWork> groupedSections;
TMap<int, bool> sideMap;
TArray<WorkSectionLine*> segments;
TArray<int> subsectors;
};
class FSectionCreator
{
FMemArena allocator;
TArray<WorkSectionLine *>AllAllocatedLines;
bool verbose = false;
TMap<int, TArray<int>> subsectormap;
TArray<WorkSection> sections;
TArray<TriangleWorkData> triangles;
TArray<Group> groups;
TArray<int> groupForSection;
public:
FSectionCreator()
{
// These must be manually destroyed but not deleted.
for (auto line : AllAllocatedLines)
{
line->~WorkSectionLine();
}
}
// Allocate these from a static allocator so that they remain fixed in memory and avoid excessive small allocations.
WorkSectionLine *NewLine()
{
void *mem = allocator.Alloc(sizeof(WorkSectionLine));
auto line = new(mem) WorkSectionLine;
AllAllocatedLines.Push(line);
return line;
}
int IndexOf(WorkSectionLine *line)
{
return AllAllocatedLines.Find(line); // slow, but only used for debugging.
}
int MakeKey(int sector, int mapsection)
{
return sector + (mapsection << 16);
}
int MakeKey(const subsector_t &sub)
{
return MakeKey(sub.render_sector->Index(), sub.mapsection);
}
int SectorFromKey(int key)
{
return key & 65535;
}
int MapsectionFromKey(int key)
{
return key >> 16;
}
//==========================================================================
//
// Groups subsectors by both sectors and mapsection
// The parts of a sector in a single map section is the upper boundary
// for what a sector section may contain.
//
//==========================================================================
void GroupSubsectors()
{
for (auto &sub : level.subsectors)
{
int key = MakeKey(sub);
auto &array = subsectormap[key];
array.Push(sub.Index());
}
}
//==========================================================================
//
// Go through the list and split each element further into
// connected groups of subsectors.
//
//==========================================================================
TArray < TArray<int>> CompileSections()
{
TMap<int, TArray<int>>::Pair *pair;
TMap<int, TArray<int>>::Iterator it(subsectormap);
TArray<TArray<int>> rawsections; // list of unprocessed subsectors. Sector and mapsection can be retrieved from the elements so aren't stored.
while (it.NextPair(pair))
{
CompileSections(pair->Value, rawsections);
}
subsectormap.Clear();
return rawsections;
}
//==========================================================================
//
// Find all groups of connected subsectors and put them into the group list
//
//==========================================================================
void CompileSections(TArray<int> &list, TArray<TArray<int>>&rawsections)
{
TArray<int> sublist;
TArray<seg_t *> seglist;
while (list.Size() > 0)
{
sublist.Clear();
seglist.Clear();
int index;
list.Pop(index);
auto sub = &level.subsectors[index];
auto collect = [&](subsector_t *sub)
{
sublist.Push(sub->Index());
for (unsigned i = 0; i < sub->numlines; i++)
{
if (sub->firstline[i].PartnerSeg && sub->firstline[i].Subsector->render_sector == sub->firstline[i].PartnerSeg->Subsector->render_sector)
{
seglist.Push(sub->firstline[i].PartnerSeg);
}
}
};
collect(sub);
for (unsigned i = 0; i < seglist.Size(); i++)
{
auto subi = seglist[i]->Subsector->Index();
for (unsigned j = 0; j < list.Size(); j++)
{
if (subi == list[j])
{
collect(&level.subsectors[subi]);
list.Delete(j);
j--;
}
}
}
rawsections.Push(std::move(sublist));
}
}
//==========================================================================
//
//
//==========================================================================
void MakeOutlines()
{
auto rawsections = CompileSections();
TArray<WorkSectionLine *> lineForSeg(level.segs.Size(), true);
memset(lineForSeg.Data(), 0, sizeof(WorkSectionLine*) * level.segs.Size());
for (auto &list : rawsections)
{
MakeOutline(list, lineForSeg);
}
rawsections.Clear();
rawsections.ShrinkToFit();
// Assign partners after everything has been collected
for (auto &section : sections)
{
for (auto seg : section.segments)
{
if (seg->refseg && seg->refseg->PartnerSeg)
{
seg->partner = lineForSeg[seg->refseg->PartnerSeg->Index()];
}
}
}
}
//==========================================================================
//
// Creates an outline for a given section
//
//==========================================================================
void MakeOutline(TArray<int> &rawsection, TArray<WorkSectionLine *> &lineForSeg)
{
TArray<side_t *> foundsides;
TArray<seg_t *> outersegs;
TArray<seg_t *> loopedsegs;
bool hasminisegs = false;
// Collect all the segs that make up the outline of this section.
for (auto j : rawsection)
{
auto sub = &level.subsectors[j];
for (unsigned i = 0; i < sub->numlines; i++)
{
if (!sub->firstline[i].PartnerSeg || sub->firstline[i].Subsector->render_sector != sub->firstline[i].PartnerSeg->Subsector->render_sector)
{
outersegs.Push(&sub->firstline[i]);
if (sub->firstline[i].sidedef == nullptr) hasminisegs = true;
}
if (sub->firstline[i].sidedef)
{
foundsides.Push(sub->firstline[i].sidedef); // This may contain duplicate. Let's deal with those later.
}
}
}
// Loop until all segs have been used.
seg_t *seg = nullptr;
unsigned startindex = 0;
while (outersegs.Size() > 0)
{
if (seg == nullptr)
{
for (unsigned i = 0; i < outersegs.Size(); i++)
{
if (outersegs[i]->sidedef != nullptr && outersegs[i]->sidedef->V1() == outersegs[i]->v1)
{
seg = outersegs[i];
outersegs.Delete(i);
break;
}
}
if (seg == nullptr)
{
// There's only minisegs left. Most likely this is just node garbage.
// Todo: Need to check.
seg = outersegs[0];
outersegs.Delete(0);
}
startindex = loopedsegs.Push(seg);
}
// Find the next seg in the loop.
auto segangle = VecToAngle(seg->v2->fPos() - seg->v1->fPos());
seg_t *pick = nullptr;
int pickindex = -1;
for (unsigned i = 0; i < outersegs.Size(); i++)
{
auto secondseg = outersegs[i];
if (secondseg->v1->fPos() == seg->v2->fPos())
{
// This should never choose a miniseg over a real sidedef.
if (pick == nullptr || (pick->sidedef == nullptr && secondseg->sidedef != nullptr))
{
pick = secondseg;
pickindex = i;
}
else if (pick->sidedef == nullptr || secondseg->sidedef != nullptr)
{
// If there's more than one pick the one with the smallest angle.
auto pickangle = deltaangle(segangle, VecToAngle(pick->v2->fPos() - pick->v1->fPos()));
auto secondangle = deltaangle(segangle, VecToAngle(secondseg->v2->fPos() - secondseg->v1->fPos()));
if (secondangle < pickangle)
{
pick = secondseg;
pickindex = i;
}
}
}
}
if (pick)
{
loopedsegs.Push(pick);
outersegs.Delete(pickindex);
seg = pick;
}
else
{
// There was no more seg connecting to the last one. We should have reached the beginning of the loop again.
if (loopedsegs.Last() == nullptr || loopedsegs[startindex]->v1->fPos() != loopedsegs.Last()->v2->fPos())
{
// Did not find another one but have an unclosed loop. This should never happen and would indicate broken nodes.
// Error out and let the calling code deal with it.
I_Error("Unclosed loop in sector %d at position (%d, %d)\n", loopedsegs[0]->Subsector->render_sector->Index(), (int)loopedsegs[0]->v1->fX(), (int)loopedsegs[0]->v1->fY());
}
seg = nullptr;
loopedsegs.Push(nullptr); // A separator is not really needed but useful for debugging.
}
}
if (loopedsegs.Size() > 0)
{
auto sector = loopedsegs[0]->Subsector->render_sector->Index();
auto mapsec = loopedsegs[0]->Subsector->mapsection;
TArray<WorkSectionLine *> sectionlines(loopedsegs.Size(), true);
for (unsigned i = 0; i < loopedsegs.Size(); i++)
{
if (loopedsegs[i])
{
sectionlines[i] = NewLine();
*sectionlines[i] = { loopedsegs[i]->v1, loopedsegs[i]->v2, loopedsegs[i]->sidedef, loopedsegs[i], -1, (int)sections.Size(), nullptr };
lineForSeg[loopedsegs[i]->Index()] = sectionlines[i];
}
else
{
sectionlines[i] = NewLine();
*sectionlines[i] = { nullptr, nullptr, nullptr, nullptr, -1, (int)sections.Size(), nullptr };
}
}
sections.Reserve(1);
auto &section = sections.Last();
section.sectorindex = sector;
section.mapsection = mapsec;
section.hasminisegs = hasminisegs;
section.originalSides = std::move(foundsides);
section.segments = std::move(sectionlines);
section.subsectors = std::move(rawsection);
}
}
//=============================================================================
//
// Tries to merge continuous segs on the same sidedef
//
//=============================================================================
void MergeLines()
{
for (auto &build : sections)
{
for (int i = (int)build.segments.Size() - 1; i > 0; i--)
{
auto ln1 = build.segments[i];
auto ln2 = build.segments[i - 1];
if (ln1->sidedef && ln2->sidedef && ln1->sidedef == ln2->sidedef)
{
if (ln1->partner == nullptr && ln2->partner == nullptr)
{
// identical references. These 2 lines can be merged.
ln2->end = ln1->end;
build.segments.Delete(i);
}
else if (ln1->partner && ln2->partner && ln1->partner->mygroup == ln2->partner->mygroup)
{
auto &section = sections[ln1->partner->mygroup];
auto index1 = section.segments.Find(ln1->partner); // note that ln1 and ln2 are ordered backward, so the partners are ordered forward.
auto index2 = section.segments.Find(ln2->partner);
if (index2 == index1 + 1 && index2 < section.segments.Size())
{
// Merge both sides at once to ensure the data remains consistent.
ln2->partner->start = ln1->partner->start;
section.segments.Delete(index1);
ln2->end = ln1->end;
build.segments.Delete(i);
}
}
}
}
}
}
//=============================================================================
//
//
//
//=============================================================================
void FindOuterLoops()
{
triangles.Resize(sections.Size());
for (unsigned int i = 0; i < sections.Size(); i++)
{
auto &section = sections[i];
auto &work = triangles[i];
BoundingRect bounds = { 1e32, 1e32, -1e32, -1e32 };
BoundingRect loopBounds = { 1e32, 1e32, -1e32, -1e32 };
BoundingRect outermostBounds = { 1e32, 1e32, -1e32, -1e32 };
unsigned outermoststart = ~0u;
unsigned loopstart = 0;
bool ispolyorg = false;
for (unsigned i = 0; i <= section.segments.Size(); i++)
{
if (i < section.segments.Size() && section.segments[i]->refseg)
{
ispolyorg |= !!(section.segments[i]->refseg->Subsector->flags & SSECF_POLYORG);
loopBounds.addVertex(section.segments[i]->start->fX(), section.segments[i]->start->fY());
loopBounds.addVertex(section.segments[i]->end->fX(), section.segments[i]->end->fY());
bounds.addVertex(section.segments[i]->start->fX(), section.segments[i]->start->fY());
bounds.addVertex(section.segments[i]->end->fX(), section.segments[i]->end->fY());
}
else
{
if (outermostBounds.left == 1e32 || loopBounds.contains(outermostBounds))
{
outermostBounds = loopBounds;
outermoststart = loopstart;
loopstart = i + 1;
}
loopBounds = { 1e32, 1e32, -1e32, -1e32 };
}
}
work.boundingLoopStart = outermoststart;
work.boundingBox = outermostBounds;
}
}
//=============================================================================
//
//
//
//=============================================================================
void GroupSections()
{
TArray<GroupWork> workingSet;
GroupWork first = { &sections[0], &triangles[0], 0 };
workingSet.Push(first);
groupForSection.Resize(sections.Size());
for (auto &i : groupForSection) i = -1;
for (unsigned i = 1; i < sections.Size(); i++)
{
auto sect = &sections[i];
if (sect->segments.Size() == 0) continue;
if (sect->sectorindex == first.section->sectorindex && sect->mapsection == first.section->mapsection)
{
workingSet.Push({ sect, &triangles[i], (int)i });
}
else
{
GroupWorkingSet(workingSet);
workingSet.Clear();
first = { &sections[i], &triangles[i], (int)i };
workingSet.Push(first);
}
}
GroupWorkingSet(workingSet);
}
//=============================================================================
//
//
//
//=============================================================================
void GroupWorkingSet(TArray<GroupWork> &workingSet)
{
const double MAX_GROUP_DIST = 256;
TArray<GroupWork> build;
TArray<int> subsectorcopy;
if (workingSet.Size() == 1)
{
groupForSection[workingSet[0].index] = groups.Size();
Group g;
g.subsectors = std::move(workingSet[0].section->subsectors);
g.groupedSections = std::move(workingSet);
groups.Push(std::move(g));
return;
}
while (workingSet.Size() > 0)
{
build.Clear();
build.Push(workingSet[0]);
groupForSection[workingSet[0].index] = groups.Size();
subsectorcopy = std::move(workingSet[0].section->subsectors);
workingSet.Delete(0);
// Don't use iterators here. These arrays are modified inside.
for (unsigned j = 0; j < build.Size(); j++)
{
auto current = build[j];
for (int i = 0; i < (int)workingSet.Size(); i++)
{
// Are both sections close together?
double dist = current.work->boundingBox.distanceTo(workingSet[i].work->boundingBox);
if (dist < MAX_GROUP_DIST)
{
build.Push(workingSet[i]);
groupForSection[workingSet[i].index] = groups.Size();
subsectorcopy.Append(workingSet[i].section->subsectors);
workingSet.Delete(i);
i--;
continue;
}
// Also put in the same group if they share a sidedef.
bool sharing_sd = CheckForSharedSidedef(*current.section, *workingSet[i].section);
if (sharing_sd)
{
build.Push(workingSet[i]);
groupForSection[workingSet[i].index] = groups.Size();
subsectorcopy.Append(workingSet[i].section->subsectors);
workingSet.Delete(i);
i--;
continue;
}
}
}
Group g;
g.groupedSections = std::move(build);
g.subsectors = std::move(subsectorcopy);
groups.Push(std::move(g));
}
}
//=============================================================================
//
//
//
//=============================================================================
bool CheckForSharedSidedef(WorkSection &set1, WorkSection &set2)
{
for (auto seg : set1.segments)
{
if (seg->sidedef != nullptr)
{
for (auto seg2 : set2.segments)
{
if (seg2->sidedef == seg->sidedef) return true;
}
}
}
return false;
}
//=============================================================================
//
//
//
//=============================================================================
void ConstructOutput(FSectionContainer &output)
{
output.allSections.Resize(groups.Size());
output.allIndices.Resize(2*level.sectors.Size());
output.firstSectionForSectorPtr = &output.allIndices[0];
output.numberOfSectionForSectorPtr = &output.allIndices[level.sectors.Size()];
memset(output.firstSectionForSectorPtr, -1, sizeof(int) * level.sectors.Size());
memset(output.numberOfSectionForSectorPtr, 0, sizeof(int) * level.sectors.Size());
unsigned numsegments = 0;
unsigned numsides = 0;
// First index all the line segments here so that we can later do some quick referencing via the global array and count them and the distinct number of sidedefs in the section.
for (auto &group : groups)
{
for (auto &work : group.groupedSections)
{
auto &section = *work.section;
for (auto segment : section.segments)
{
if (segment->refseg)
{
segment->tempindex = numsegments++;
group.segments.Push(segment);
}
}
for (auto side : section.originalSides)
{
// This is only used for attaching lights to those sidedefs which are not part of the outline.
if (side->linedef && side->linedef->sidedef[1] && side->linedef->sidedef[0]->sector == side->linedef->sidedef[1]->sector)
group.sideMap[side->Index()] = true;
}
}
numsides += group.sideMap.CountUsed();
}
output.allLines.Resize(numsegments);
output.allSides.Resize(numsides);
output.allSubsectors.Resize(level.subsectors.Size());
numsegments = 0;
numsides = 0;
unsigned numsubsectors = 0;
// Now piece it all together
unsigned curgroup = 0;
for (auto &group : groups)
{
FSection &dest = output.allSections[curgroup];
dest.sector = &level.sectors[group.groupedSections[0].section->sectorindex];
dest.mapsection = (short)group.groupedSections[0].section->mapsection;
dest.hacked = false;
dest.lighthead = nullptr;
dest.validcount = 0;
dest.segments.Set(&output.allLines[numsegments], group.segments.Size());
dest.sides.Set(&output.allSides[numsides], group.sideMap.CountUsed());
dest.subsectors.Set(&output.allSubsectors[numsubsectors], group.subsectors.Size());
dest.vertexindex = -1;
dest.vertexcount = 0;
dest.bounds = {1e32, 1e32, -1e32, -1e32};
numsegments += group.segments.Size();
if (output.firstSectionForSectorPtr[dest.sector->Index()] == -1)
output.firstSectionForSectorPtr[dest.sector->Index()] = curgroup;
output.numberOfSectionForSectorPtr[dest.sector->Index()]++;
for (auto &segment : group.segments)
{
// Use the indices calculated above to store these elements.
auto &fseg = output.allLines[segment->tempindex];
fseg.start = segment->start;
fseg.end = segment->end;
fseg.partner = segment->partner == nullptr ? nullptr : &output.allLines[segment->partner->tempindex];
fseg.sidedef = segment->sidedef;
fseg.section = &dest;
dest.bounds.addVertex(fseg.start->fX(), fseg.start->fY());
dest.bounds.addVertex(fseg.end->fX(), fseg.end->fY());
}
TMap<int, bool>::Iterator it(group.sideMap);
TMap<int, bool>::Pair *pair;
while (it.NextPair(pair))
{
output.allSides[numsides++] = &level.sides[pair->Key];
}
for (auto ssi : group.subsectors)
{
output.allSubsectors[numsubsectors++] = &level.subsectors[ssi];
level.subsectors[ssi].section = &output.allSections[curgroup];
}
curgroup++;
}
}
};
//=============================================================================
//
//
//
//=============================================================================
void PrintSections(FSectionContainer &container)
{
for (unsigned i = 0; i < container.allSections.Size(); i++)
{
auto &section = container.allSections[i];
Printf(PRINT_LOG, "\n\nStart of section %d sector %d, mapsection %d, bounds=(%2.3f, %2.3f, %2.3f, %2.3f)\n", i, section.sector->Index(), section.mapsection,
section.bounds.left, section.bounds.top, section.bounds.right, section.bounds.bottom);
for (unsigned j = 0; j < section.segments.Size(); j++)
{
auto &seg = section.segments[j];
if (j > 0 && seg.start != section.segments[j - 1].end)
{
Printf(PRINT_LOG, "\n");
}
FString partnerstring;
if (seg.partner)
{
if (seg.partner->sidedef) partnerstring.Format(", partner = %d (line %d)", seg.partner->sidedef->Index(), seg.partner->sidedef->linedef->Index());
else partnerstring = ", partner = seg";
}
else if (seg.sidedef && seg.sidedef->linedef)
{
partnerstring = ", one-sided line";
}
if (seg.sidedef)
{
Printf(PRINT_LOG, "segment for sidedef %d (line %d) from (%2.6f, %2.6f) to (%2.6f, %2.6f)%s\n",
seg.sidedef->Index(), seg.sidedef->linedef->Index(), seg.start->fX(), seg.start->fY(), seg.end->fX(), seg.end->fY(), partnerstring.GetChars());
}
else
{
Printf(PRINT_LOG, "segment for seg from (%2.6f, %2.6f) to (%2.6f, %2.6f)%s\n",
seg.start->fX(), seg.start->fY(), seg.end->fX(), seg.end->fY(), partnerstring.GetChars());
}
}
}
Printf(PRINT_LOG, "%d sectors, %d subsectors, %d sections\n", level.sectors.Size(), level.subsectors.Size(), container.allSections.Size());
}
//=============================================================================
//
//
//
//=============================================================================
void CreateSections(FSectionContainer &container)
{
FSectionCreator creat;
creat.GroupSubsectors();
creat.MakeOutlines();
creat.MergeLines();
creat.FindOuterLoops();
creat.GroupSections();
creat.ConstructOutput(container);
}
CCMD(printsections)
{
PrintSections(level.sections);
}

142
src/r_data/r_sections.h Normal file
View File

@ -0,0 +1,142 @@
#ifndef __GL_SECTIONS_H
#define __GL_SECTIONS_H
#include <algorithm>
#include "tarray.h"
#include "r_defs.h"
// Undefine Windows garbage.
#ifdef min
#undef min
#endif
#ifdef max
#undef max
#endif
//==========================================================================
//
//
//
//==========================================================================
struct BoundingRect
{
double left, top, right, bottom;
bool contains(const BoundingRect & other)
{
return left <= other.left && top <= other.top && right >= other.right && bottom >= other.bottom;
}
bool intersects(const BoundingRect & other)
{
return !(other.left > right ||
other.right < left ||
other.top > bottom ||
other.bottom < top);
}
void Union(const BoundingRect & other)
{
if (other.left < left) left = other.left;
if (other.right > right) right = other.right;
if (other.top < top) top = other.top;
if (other.bottom > bottom) bottom = other.bottom;
}
double distanceTo(const BoundingRect &other)
{
if (intersects(other)) return 0;
return std::max(std::min(fabs(left - other.right), fabs(right - other.left)),
std::min(fabs(top - other.bottom), fabs(bottom - other.top)));
}
void addVertex(double x, double y)
{
if (x < left) left = x;
if (x > right) right = x;
if (y < top) top = y;
if (y > bottom) bottom = y;
}
bool operator == (const BoundingRect &other)
{
return left == other.left && top == other.top && right == other.right && bottom == other.bottom;
}
};
struct FSectionLine
{
vertex_t *start;
vertex_t *end;
FSectionLine *partner;
FSection *section;
side_t *sidedef;
};
struct FSection
{
// tbd: Do we need a list of subsectors here? Ideally the subsectors should not be used anywhere anymore except for finding out where a location is.
TArrayView<FSectionLine> segments;
TArrayView<side_t *> sides; // contains all sidedefs, including the internal ones that do not make up the outer shape.
TArrayView<subsector_t *> subsectors; // contains all subsectors making up this section
sector_t *sector;
FLightNode *lighthead; // Light nodes (blended and additive)
BoundingRect bounds;
int vertexindex; // This is relative to the start of the entire sector's vertex plane data because it needs to be used with different sources.
int vertexcount;
int validcount;
short mapsection;
char hacked; // 1: is part of a render hack
};
class FSectionContainer
{
public:
TArray<FSectionLine> allLines;
TArray<FSection> allSections;
TArray<side_t *> allSides;
TArray<subsector_t *> allSubsectors;
TArray<int> allIndices;
int *firstSectionForSectorPtr; // ditto.
int *numberOfSectionForSectorPtr; // ditto.
TArrayView<FSection> SectionsForSector(sector_t *sec)
{
return SectionsForSector(sec->Index());
}
TArrayView<FSection> SectionsForSector(int sindex)
{
return sindex < 0 ? TArrayView<FSection>(0) : TArrayView<FSection>(&allSections[firstSectionForSectorPtr[sindex]], numberOfSectionForSectorPtr[sindex]);
}
int SectionIndex(const FSection *sect)
{
return int(sect - allSections.Data());
}
void Clear()
{
allLines.Clear();
allSections.Clear();
allIndices.Clear();
allSides.Clear();
allSubsectors.Clear();
}
void Reset()
{
Clear();
allLines.ShrinkToFit();
allSections.ShrinkToFit();
allIndices.ShrinkToFit();
allSides.ShrinkToFit();
allSubsectors.ShrinkToFit();
}
};
void CreateSections(FSectionContainer &container);
#endif

View File

@ -247,7 +247,7 @@ static void PrepareSectorData()
for (auto &sub : level.subsectors)
{
sub.sectorindex = (uint16_t)sub.render_sector->subsectorcount++;
sub.render_sector->subsectorcount++;
}
for (auto &sec : level.sectors)
@ -573,7 +573,244 @@ void InitRenderInfo()
}
//==========================================================================
//
// FixMinisegReferences
//
// Sometimes it can happen that two matching minisegs do not have their partner set.
// Fix that here.
//
//==========================================================================
void FixMinisegReferences()
{
TArray<seg_t *> bogussegs;
for (unsigned i = 0; i < level.segs.Size(); i++)
{
if (level.segs[i].sidedef == nullptr && level.segs[i].PartnerSeg == nullptr)
{
bogussegs.Push(&level.segs[i]);
}
}
for (unsigned i = 0; i < bogussegs.Size(); i++)
{
auto seg1 = bogussegs[i];
seg_t *pick = nullptr;
unsigned int picki = -1;
// Try to fix the reference: If there's exactly one other seg in the set which matches as a partner link those two segs together.
for (unsigned j = i + 1; j < bogussegs.Size(); j++)
{
auto seg2 = bogussegs[j];
if (seg1->v1 == seg2->v2 && seg2->v1 == seg1->v2 && seg1->Subsector->render_sector == seg2->Subsector->render_sector)
{
pick = seg2;
picki = j;
break;
}
}
if (pick)
{
DPrintf(DMSG_NOTIFY, "Linking miniseg pair from (%2.3f, %2.3f) -> (%2.3f, %2.3f) in sector %d\n", pick->v2->fX(), pick->v2->fY(), pick->v1->fX(), pick->v1->fY(), pick->frontsector->Index());
pick->PartnerSeg = seg1;
seg1->PartnerSeg = pick;
assert(seg1->v1 == pick->v2 && pick->v1 == seg1->v2);
bogussegs.Delete(picki);
bogussegs.Delete(i);
i--;
}
}
}
//==========================================================================
//
// FixHoles
//
// ZDBSP can leave holes in the node tree on extremely detailed maps.
// To help out the triangulator these are filled with dummy subsectors
// so that it can process the area correctly.
//
//==========================================================================
void FixHoles()
{
TArray<seg_t *> bogussegs;
TArray<TArray<seg_t *>> segloops;
for (unsigned i = 0; i < level.segs.Size(); i++)
{
if (level.segs[i].sidedef == nullptr && level.segs[i].PartnerSeg == nullptr)
{
bogussegs.Push(&level.segs[i]);
}
}
while (bogussegs.Size() > 0)
{
segloops.Reserve(1);
auto *segloop = &segloops.Last();
seg_t *startseg;
seg_t *checkseg;
while (bogussegs.Size() > 0)
{
bool foundsome = false;
if (segloop->Size() == 0)
{
bogussegs.Pop(startseg);
segloop->Push(startseg);
checkseg = startseg;
}
for (unsigned i = 0; i < bogussegs.Size(); i++)
{
auto seg1 = bogussegs[i];
if (seg1->v1 == checkseg->v2 && seg1->Subsector->render_sector == checkseg->Subsector->render_sector)
{
foundsome = true;
segloop->Push(seg1);
bogussegs.Delete(i);
i--;
checkseg = seg1;
if (seg1->v2 == startseg->v1)
{
// The loop is complete. Start a new one
segloops.Reserve(1);
segloop = &segloops.Last();
}
}
}
if (!foundsome)
{
if ((*segloop)[0]->v1 != segloop->Last()->v2)
{
// There was no connected seg, leaving an unclosed loop.
// Clear this and continue looking.
segloop->Clear();
}
}
}
for (unsigned i = 0; i < segloops.Size(); i++)
{
if (segloops[i].Size() == 0)
{
segloops.Delete(i);
i--;
}
}
// Add dummy entries to the level's seg and subsector arrays
if (segloops.Size() > 0)
{
// cound the number of segs to add.
unsigned segcount = 0;
for (auto &segloop : segloops)
segcount += segloop.Size();
seg_t *oldsegstartptr = &level.segs[0];
subsector_t *oldssstartptr = &level.subsectors[0];
unsigned newsegstart = level.segs.Reserve(segcount);
unsigned newssstart = level.subsectors.Reserve(segloops.Size());
seg_t *newsegstartptr = &level.segs[0];
subsector_t *newssstartptr = &level.subsectors[0];
// Now fix all references to these in the level data.
// Note that the Index() method does not work here due to the reallocation.
for (auto &seg : level.segs)
{
if (seg.PartnerSeg) seg.PartnerSeg = newsegstartptr + (seg.PartnerSeg - oldsegstartptr);
seg.Subsector = newssstartptr + (seg.Subsector - oldssstartptr);
}
for (auto &sub : level.subsectors)
{
sub.firstline = newsegstartptr + (sub.firstline - oldsegstartptr);
}
for (auto &node : level.nodes)
{
// How hideous... :(
for (auto & p : node.children)
{
auto intp = (intptr_t)p;
if (intp & 1)
{
subsector_t *sp = (subsector_t*)(intp - 1);
sp = newssstartptr + (sp - oldssstartptr);
intp = intptr_t(sp) + 1;
p = (void*)intp;
}
}
}
for (auto &segloop : segloops)
{
for (auto &seg : segloop)
{
seg = newsegstartptr + (seg - oldsegstartptr);
}
}
// The seg lists in the sidedefs and the subsector lists in the sectors are not set yet when this gets called.
// Add the new data. This doesn't care about convexity. It is never directly used to generate a primitive.
for (auto &segloop : segloops)
{
DPrintf(DMSG_NOTIFY, "Adding dummy subsector for sector %d\n", segloop[0]->Subsector->render_sector->Index());
subsector_t &sub = level.subsectors[newssstart++];
memset(&sub, 0, sizeof(sub));
sub.sector = segloop[0]->frontsector;
sub.render_sector = segloop[0]->Subsector->render_sector;
sub.numlines = segloop.Size();
sub.firstline = &level.segs[newsegstart];
sub.flags = SSECF_HOLE;
for (auto otherseg : segloop)
{
DPrintf(DMSG_NOTIFY, " Adding seg from (%2.3f, %2.3f) -> (%2.3f, %2.3f)\n", otherseg->v2->fX(), otherseg->v2->fY(), otherseg->v1->fX(), otherseg->v1->fY());
seg_t &seg = level.segs[newsegstart++];
memset(&seg, 0, sizeof(seg));
seg.v1 = otherseg->v2;
seg.v2 = otherseg->v1;
seg.frontsector = seg.backsector = otherseg->backsector = otherseg->frontsector;
seg.PartnerSeg = otherseg;
otherseg->PartnerSeg = &seg;
seg.Subsector = &sub;
}
}
}
}
}
//==========================================================================
//
// ReportUnpairedMinisegs
//
// Debug routine
// reports all unpaired minisegs that couldn't be fixed by either
// explicitly pairing them or combining them to a dummy subsector
//
//==========================================================================
void ReportUnpairedMinisegs()
{
int bogus = 0;
for (unsigned i = 0; i < level.segs.Size(); i++)
{
if (level.segs[i].sidedef == nullptr && level.segs[i].PartnerSeg == nullptr)
{
Printf("Unpaired miniseg %d, sector %d, (%d: %2.6f, %2.6f) -> (%d: %2.6f, %2.6f)\n",
i, level.segs[i].Subsector->render_sector->Index(),
level.segs[i].v1->Index(), level.segs[i].v1->fX(), level.segs[i].v1->fY(),
level.segs[i].v2->Index(), level.segs[i].v2->fX(), level.segs[i].v2->fY());
bogus++;
}
}
if (bogus > 0) Printf("%d unpaired minisegs found\n", bogus);
}
//==========================================================================
//
//
@ -594,3 +831,8 @@ CCMD(listmapsections)
}
}
}
CCMD(listbadminisegs)
{
ReportUnpairedMinisegs();
}

View File

@ -51,6 +51,7 @@ struct FLinePortal;
struct seg_t;
struct sector_t;
class AActor;
struct FSection;
#define MAXWIDTH 12000
#define MAXHEIGHT 5000
@ -281,6 +282,8 @@ enum
SECSPAC_DamageCeiling=1<<12, // Trigger when ceiling is damaged
SECSPAC_DeathFloor = 1<<13, // Trigger when floor has 0 hp
SECSPAC_DeathCeiling= 1<<14, // Trigger when ceiling has 0 hp
SECSPAC_Damage3D = 1<<15, // Trigger when controlled 3d floor is damaged
SECSPAC_Death3D = 1<<16 // Trigger when controlled 3d floor has 0 hp
};
struct secplane_t
@ -1028,7 +1031,6 @@ public:
// killough 3/7/98: support flat heights drawn at another sector's heights
sector_t *heightsec; // other sector, or NULL if no other sector
FLightNode * lighthead;
uint32_t bottommap, midmap, topmap; // killough 4/4/98: dynamic colormaps
// [RH] these can also be blend values if
@ -1098,8 +1100,10 @@ public:
// default is 0, which means no special behavior
int healthfloor;
int healthceiling;
int health3d;
int healthfloorgroup;
int healthceilinggroup;
int health3dgroup;
};
@ -1437,6 +1441,7 @@ enum
SSECF_DEGENERATE = 1,
SSECMF_DRAWN = 2,
SSECF_POLYORG = 4,
SSECF_HOLE = 8,
};
struct FPortalCoverage
@ -1454,14 +1459,13 @@ struct subsector_t
FMiniBSP *BSP;
seg_t *firstline;
sector_t *render_sector;
FSection *section;
uint32_t numlines;
uint16_t flags;
uint16_t sectorindex;
short mapsection;
// subsector related GL data
FLightNode * lighthead; // Light nodes (blended and additive)
int validcount;
short mapsection;
char hacked; // 1: is part of a render hack
void BuildPolyBSP();

View File

@ -142,6 +142,7 @@ bool setsizeneeded;
unsigned int R_OldBlend = ~0;
int validcount = 1; // increment every time a check is made
int dl_validcount = 1; // increment every time a check is made
FCanvasTextureInfo *FCanvasTextureInfo::List;
DVector3a view;

View File

@ -66,6 +66,7 @@ extern FViewWindow r_viewwindow;
extern int setblocks;
extern bool r_NoInterpolate;
extern int validcount;
extern int dl_validcount; // For use with FSection. validcount is in use by the renderer and any quick section exclusion needs another variable.
extern angle_t LocalViewAngle; // [RH] Added to consoleplayer's angle
extern int LocalViewPitch; // [RH] Used directly instead of consoleplayer's pitch

View File

@ -121,8 +121,7 @@ private:
uint32_t *SendCommand (uint32_t *event, TrackInfo *track, uint32_t delay, ptrdiff_t room, bool &sysex_noroom);
TrackInfo *FindNextDue ();
uint8_t *MusHeader;
int SongLen;
TArray<uint8_t> MusHeader;
TrackInfo *Tracks;
TrackInfo *TrackDue;
int NumTracks;
@ -181,8 +180,7 @@ private:
static uint32_t ReadVarLenHMI(TrackInfo *);
static uint32_t ReadVarLenHMP(TrackInfo *);
uint8_t *MusHeader;
int SongLen;
TArray<uint8_t> MusHeader;
int NumTracks;
TrackInfo *Tracks;
TrackInfo *TrackDue;
@ -218,8 +216,7 @@ private:
uint32_t *SendCommand (uint32_t *event, EventSource track, uint32_t delay, ptrdiff_t room, bool &sysex_noroom);
EventSource FindNextDue();
uint8_t *MusHeader;
int SongLen; // length of the entire file
TArray<uint8_t> MusHeader;
int NumSongs;
TrackInfo *Songs;
TrackInfo *CurrSong;

View File

@ -129,19 +129,17 @@ HMISong::HMISong (FileReader &reader)
{ // Way too small to be HMI.
return;
}
MusHeader = new uint8_t[len];
SongLen = len;
MusHeader = reader.Read();
NumTracks = 0;
if (reader.Read(MusHeader, len) != len)
if (MusHeader.Size() == 0)
return;
// Do some validation of the MIDI file
if (memcmp(MusHeader, HMI_SONG_MAGIC, sizeof(HMI_SONG_MAGIC)) == 0)
if (memcmp(&MusHeader[0], HMI_SONG_MAGIC, sizeof(HMI_SONG_MAGIC)) == 0)
{
SetupForHMI(len);
}
else if (((uint32_t *)MusHeader)[0] == MAKE_ID('H','M','I','M') &&
((uint32_t *)MusHeader)[1] == MAKE_ID('I','D','I','P'))
else if (memcmp(&MusHeader[0], "HMIMIDIP", 8) == 0)
{
SetupForHMP(len);
}
@ -155,14 +153,10 @@ HMISong::HMISong (FileReader &reader)
HMISong::~HMISong()
{
if (Tracks != NULL)
if (Tracks != nullptr)
{
delete[] Tracks;
}
if (MusHeader != NULL)
{
delete[] MusHeader;
}
}
//==========================================================================
@ -175,8 +169,10 @@ void HMISong::SetupForHMI(int len)
{
int i, p;
auto MusPtr = &MusHeader[0];
ReadVarLen = ReadVarLenHMI;
NumTracks = GetShort(MusHeader + HMI_TRACK_COUNT_OFFSET);
NumTracks = GetShort(MusPtr + HMI_TRACK_COUNT_OFFSET);
if (NumTracks <= 0)
{
@ -187,16 +183,16 @@ void HMISong::SetupForHMI(int len)
// HMI files have two values here, a full value and a quarter value. Some games,
// notably Quarantines, have identical values for some reason, so it's safer to
// use the quarter value and multiply it by four than to trust the full value.
Division = GetShort(MusHeader + HMI_DIVISION_OFFSET) << 2;
Division = GetShort(MusPtr + HMI_DIVISION_OFFSET) << 2;
Tempo = InitialTempo = 4000000;
Tracks = new TrackInfo[NumTracks + 1];
int track_dir = GetInt(MusHeader + HMI_TRACK_DIR_PTR_OFFSET);
int track_dir = GetInt(MusPtr + HMI_TRACK_DIR_PTR_OFFSET);
// Gather information about each track
for (i = 0, p = 0; i < NumTracks; ++i)
{
int start = GetInt(MusHeader + track_dir + i*4);
int start = GetInt(MusPtr + track_dir + i*4);
int tracklen, datastart;
if (start > len - HMITRACK_DESIGNATION_OFFSET - 4)
@ -205,7 +201,7 @@ void HMISong::SetupForHMI(int len)
}
// BTW, HMI does not actually check the track header.
if (memcmp(MusHeader + start, TRACK_MAGIC, 13) != 0)
if (memcmp(MusPtr + start, TRACK_MAGIC, 13) != 0)
{
continue;
}
@ -218,7 +214,7 @@ void HMISong::SetupForHMI(int len)
}
else
{
tracklen = GetInt(MusHeader + track_dir + i*4 + 4) - start;
tracklen = GetInt(MusPtr + track_dir + i*4 + 4) - start;
}
// Clamp incomplete tracks to the end of the file.
tracklen = MIN(tracklen, len - start);
@ -228,7 +224,7 @@ void HMISong::SetupForHMI(int len)
}
// Offset to actual MIDI events.
datastart = GetInt(MusHeader + start + HMITRACK_DATA_PTR_OFFSET);
datastart = GetInt(MusPtr + start + HMITRACK_DATA_PTR_OFFSET);
tracklen -= datastart;
if (tracklen <= 0)
{
@ -236,7 +232,7 @@ void HMISong::SetupForHMI(int len)
}
// Store track information
Tracks[p].TrackBegin = MusHeader + start + datastart;
Tracks[p].TrackBegin = MusPtr + start + datastart;
Tracks[p].TrackP = 0;
Tracks[p].MaxTrackP = tracklen;
@ -244,7 +240,7 @@ void HMISong::SetupForHMI(int len)
// connected to the MIDI device.
for (int ii = 0; ii < NUM_HMI_DESIGNATIONS; ++ii)
{
Tracks[p].Designation[ii] = GetShort(MusHeader + start + HMITRACK_DESIGNATION_OFFSET + ii*2);
Tracks[p].Designation[ii] = GetShort(MusPtr + start + HMITRACK_DESIGNATION_OFFSET + ii*2);
}
p++;
@ -266,12 +262,14 @@ void HMISong::SetupForHMP(int len)
int track_data;
int i, p;
auto MusPtr = &MusHeader[0];
ReadVarLen = ReadVarLenHMP;
if (MusHeader[8] == 0)
if (MusPtr[8] == 0)
{
track_data = HMP_TRACK_OFFSET_0;
}
else if (memcmp(MusHeader + 8, HMP_NEW_DATE, sizeof(HMP_NEW_DATE)) == 0)
else if (memcmp(MusPtr + 8, HMP_NEW_DATE, sizeof(HMP_NEW_DATE)) == 0)
{
track_data = HMP_TRACK_OFFSET_1;
}
@ -280,7 +278,7 @@ void HMISong::SetupForHMP(int len)
return;
}
NumTracks = GetInt(MusHeader + HMP_TRACK_COUNT_OFFSET);
NumTracks = GetInt(MusPtr + HMP_TRACK_COUNT_OFFSET);
if (NumTracks <= 0)
{
@ -288,7 +286,7 @@ void HMISong::SetupForHMP(int len)
}
// The division is the number of pulses per quarter note (PPQN).
Division = GetInt(MusHeader + HMP_DIVISION_OFFSET);
Division = GetInt(MusPtr + HMP_DIVISION_OFFSET);
Tempo = InitialTempo = 1000000;
Tracks = new TrackInfo[NumTracks + 1];
@ -304,7 +302,7 @@ void HMISong::SetupForHMP(int len)
break;
}
tracklen = GetInt(MusHeader + start + HMPTRACK_LEN_OFFSET);
tracklen = GetInt(MusPtr + start + HMPTRACK_LEN_OFFSET);
track_data += tracklen;
// Clamp incomplete tracks to the end of the file.
@ -322,7 +320,7 @@ void HMISong::SetupForHMP(int len)
}
// Store track information
Tracks[p].TrackBegin = MusHeader + start + HMPTRACK_MIDI_DATA_OFFSET;
Tracks[p].TrackBegin = MusPtr + start + HMPTRACK_MIDI_DATA_OFFSET;
Tracks[p].TrackP = 0;
Tracks[p].MaxTrackP = tracklen;
@ -333,14 +331,14 @@ void HMISong::SetupForHMP(int len)
// HMI files. Some songs contain nothing but zeroes for this data, so I'd rather
// not go around using it without confirmation.
Printf("Track %d: %d %08x %d: \034I", i, GetInt(MusHeader + start),
GetInt(MusHeader + start + 4), GetInt(MusHeader + start + 8));
Printf("Track %d: %d %08x %d: \034I", i, GetInt(MusPtr + start),
GetInt(MusPtr + start + 4), GetInt(MusPtr + start + 8));
int designations = HMP_DESIGNATIONS_OFFSET +
GetInt(MusHeader + start + HMPTRACK_DESIGNATION_OFFSET) * 4 * NUM_HMP_DESIGNATIONS;
GetInt(MusPtr + start + HMPTRACK_DESIGNATION_OFFSET) * 4 * NUM_HMP_DESIGNATIONS;
for (int ii = 0; ii < NUM_HMP_DESIGNATIONS; ++ii)
{
Printf(" %04x", GetInt(MusHeader + designations + ii*4));
Printf(" %04x", GetInt(MusPtr + designations + ii*4));
}
Printf("\n");
#endif
@ -474,7 +472,7 @@ void HMISong :: DoRestart()
bool HMISong::CheckDone()
{
return TrackDue == NULL;
return TrackDue == nullptr;
}
//==========================================================================
@ -964,7 +962,7 @@ void NoteOffQueue::Heapify()
//
// HMISong :: FindNextDue
//
// Scans every track for the next event to play. Returns NULL if all events
// Scans every track for the next event to play. Returns nullptr if all events
// have been consumed.
//
//==========================================================================
@ -987,7 +985,7 @@ HMISong::TrackInfo *HMISong::FindNextDue ()
}
// Check regular tracks.
track = NULL;
track = nullptr;
best = 0xFFFFFFFF;
for (i = 0; i < NumTracks; ++i)
{

View File

@ -98,13 +98,11 @@ struct MIDISong2::TrackInfo
MIDISong2::MIDISong2 (FileReader &reader)
: MusHeader(0), Tracks(0)
{
int p;
unsigned p;
int i;
SongLen = (int)reader.GetLength();
MusHeader = new uint8_t[SongLen];
if (reader.Read(MusHeader, SongLen) != SongLen)
return;
MusHeader = reader.Read();
if (MusHeader.Size() == 0) return;
// Do some validation of the MIDI file
if (MusHeader[4] != 0 || MusHeader[5] != 0 || MusHeader[6] != 0 || MusHeader[7] != 6)
@ -134,7 +132,7 @@ MIDISong2::MIDISong2 (FileReader &reader)
Tracks = new TrackInfo[NumTracks];
// Gather information about each track
for (i = 0, p = 14; i < NumTracks && p < SongLen + 8; ++i)
for (i = 0, p = 14; i < NumTracks && p < MusHeader.Size() + 8; ++i)
{
uint32_t chunkLen =
(MusHeader[p+4]<<24) |
@ -142,9 +140,9 @@ MIDISong2::MIDISong2 (FileReader &reader)
(MusHeader[p+6]<<8) |
(MusHeader[p+7]);
if (chunkLen + p + 8 > (uint32_t)SongLen)
if (chunkLen + p + 8 > MusHeader.Size())
{ // Track too long, so truncate it
chunkLen = SongLen - p - 8;
chunkLen = MusHeader.Size() - p - 8;
}
if (MusHeader[p+0] == 'M' &&
@ -152,7 +150,7 @@ MIDISong2::MIDISong2 (FileReader &reader)
MusHeader[p+2] == 'r' &&
MusHeader[p+3] == 'k')
{
Tracks[i].TrackBegin = MusHeader + p + 8;
Tracks[i].TrackBegin = &MusHeader[p + 8];
Tracks[i].TrackP = 0;
Tracks[i].MaxTrackP = chunkLen;
}
@ -178,14 +176,10 @@ MIDISong2::MIDISong2 (FileReader &reader)
MIDISong2::~MIDISong2 ()
{
if (Tracks != NULL)
if (Tracks != nullptr)
{
delete[] Tracks;
}
if (MusHeader != NULL)
{
delete[] MusHeader;
}
}
//==========================================================================
@ -273,7 +267,7 @@ void MIDISong2 :: DoRestart()
bool MIDISong2::CheckDone()
{
return TrackDue == NULL;
return TrackDue == nullptr;
}
//==========================================================================
@ -750,7 +744,7 @@ uint32_t MIDISong2::TrackInfo::ReadVarLen ()
//
// MIDISong2 :: FindNextDue
//
// Scans every track for the next event to play. Returns NULL if all events
// Scans every track for the next event to play. Returns nullptr if all events
// have been consumed.
//
//==========================================================================
@ -770,10 +764,10 @@ MIDISong2::TrackInfo *MIDISong2::FindNextDue ()
switch (Format)
{
case 0:
return Tracks[0].Finished ? NULL : Tracks;
return Tracks[0].Finished ? nullptr : Tracks;
case 1:
track = NULL;
track = nullptr;
best = 0xFFFFFFFF;
for (i = 0; i < NumTracks; ++i)
{
@ -794,9 +788,9 @@ MIDISong2::TrackInfo *MIDISong2::FindNextDue ()
{
track++;
}
return track < &Tracks[NumTracks] ? track : NULL;
return track < &Tracks[NumTracks] ? track : nullptr;
}
return NULL;
return nullptr;
}

View File

@ -104,13 +104,11 @@ struct XMISong::TrackInfo
XMISong::XMISong (FileReader &reader)
: MusHeader(0), Songs(0)
{
SongLen = (int)reader.GetLength();
MusHeader = new uint8_t[SongLen];
if (reader.Read(MusHeader, SongLen) != SongLen)
return;
MusHeader = reader.Read();
if (MusHeader.Size() == 0) return;
// Find all the songs in this file.
NumSongs = FindXMIDforms(MusHeader, SongLen, NULL);
NumSongs = FindXMIDforms(&MusHeader[0], MusHeader.Size(), nullptr);
if (NumSongs == 0)
{
return;
@ -128,7 +126,7 @@ XMISong::XMISong (FileReader &reader)
Songs = new TrackInfo[NumSongs];
memset(Songs, 0, sizeof(*Songs) * NumSongs);
FindXMIDforms(MusHeader, SongLen, Songs);
FindXMIDforms(&MusHeader[0], MusHeader.Size(), Songs);
CurrSong = Songs;
DPrintf(DMSG_SPAMMY, "XMI song count: %d\n", NumSongs);
}
@ -141,14 +139,10 @@ XMISong::XMISong (FileReader &reader)
XMISong::~XMISong ()
{
if (Songs != NULL)
if (Songs != nullptr)
{
delete[] Songs;
}
if (MusHeader != NULL)
{
delete[] MusHeader;
}
}
//==========================================================================
@ -172,7 +166,7 @@ int XMISong::FindXMIDforms(const uint8_t *chunk, int len, TrackInfo *songs) cons
{
if (GetNativeInt(chunk + p + 8) == MAKE_ID('X','M','I','D'))
{
if (songs != NULL)
if (songs != nullptr)
{
FoundXMID(chunk + p + 12, chunklen - 4, songs + count);
}

View File

@ -558,7 +558,7 @@ namespace swrenderer
Fake3DOpaque::Normal,
0);
ceilingplane->AddLights(Thread, frontsector->lighthead);
ceilingplane->AddLights(Thread, sub->section->lighthead);
}
int adjusted_floorlightlevel = floorlightlevel;
@ -598,7 +598,7 @@ namespace swrenderer
Fake3DOpaque::Normal,
0);
floorplane->AddLights(Thread, frontsector->lighthead);
floorplane->AddLights(Thread, sub->section->lighthead);
}
Add3DFloorPlanes(sub, frontsector, basecolormap, foggy, adjusted_ceilinglightlevel, adjusted_floorlightlevel);
@ -742,7 +742,7 @@ namespace swrenderer
Fake3DOpaque::FakeFloor,
fakeAlpha);
floorplane3d->AddLights(Thread, tempsec.lighthead);
floorplane3d->AddLights(Thread, sub->section->lighthead);
FakeDrawLoop(sub, &tempsec, floorplane3d, nullptr, foggy, basecolormap, Fake3DOpaque::FakeFloor);
}
@ -810,7 +810,7 @@ namespace swrenderer
Fake3DOpaque::FakeCeiling,
fakeAlpha);
ceilingplane3d->AddLights(Thread, tempsec.lighthead);
ceilingplane3d->AddLights(Thread, sub->section->lighthead);
FakeDrawLoop(sub, &tempsec, nullptr, ceilingplane3d, foggy, basecolormap, Fake3DOpaque::FakeCeiling);
}

View File

@ -111,7 +111,7 @@ namespace swrenderer
BSPWalkCircle(x, y, radiusSquared, [&](subsector_t *subsector) // Iterate through all subsectors potentially touched by actor
{
FLightNode * node = subsector->lighthead;
FLightNode * node = subsector->section->lighthead;
while (node) // check all lights touching a subsector
{
ADynamicLight *light = node->lightsource;

View File

@ -202,6 +202,7 @@ namespace swrenderer
// killough 3/27/98: save sector for special clipping later
vis->heightsec = heightsec;
vis->sector = thing->Sector;
vis->section = thing->section;
vis->depth = (float)tz;
vis->gpos = { (float)pos.X, (float)pos.Y, (float)pos.Z };
@ -251,7 +252,7 @@ namespace swrenderer
float lit_red = 0;
float lit_green = 0;
float lit_blue = 0;
auto node = vis->sector->lighthead;
auto node = vis->section->lighthead;
while (node != nullptr)
{
ADynamicLight *light = node->lightsource;

View File

@ -73,6 +73,7 @@ namespace swrenderer
FVector3 gpos = { 0.0f, 0.0f, 0.0f }; // origin in world coordinates
sector_t *sector = nullptr; // sector this sprite is in
FSection *section;
ColormapLight Light;
float Alpha = 0.0f;

View File

@ -162,6 +162,10 @@ public:
Most = max;
Count = reserve? max : 0;
Array = (T *)M_Malloc (sizeof(T)*max);
if (reserve)
{
for (unsigned i = 0; i < Count; i++) ::new(&Array[i]) T();
}
}
TArray (const TArray<T,TT> &other)
{
@ -250,6 +254,12 @@ public:
return Array[Count-1];
}
// returns address of first element
T *Data() const
{
return &Array[0];
}
unsigned int Find(const T& item) const
{
unsigned int i;
@ -267,6 +277,7 @@ public:
::new((void*)&Array[Count]) T(item);
return Count++;
}
void Append(const TArray<T> &item)
{
unsigned start = Reserve(item.Size());
@ -350,6 +361,18 @@ public:
::new ((void *)&Array[index]) T(item);
}
}
// Reserves a range of entries in the middle of the array, shifting elements as needed
void ReserveAt(unsigned int index, unsigned int amount)
{
Grow(amount);
memmove(&Array[index + amount], &Array[index], sizeof(T)*(Count - index - amount));
for (unsigned i = 0; i < amount; i++)
{
::new ((void *)&Array[index + i]) T();
}
}
void ShrinkToFit ()
{
if (Most > Count)
@ -513,8 +536,9 @@ public:
}
};
// This is not a real dynamic array but just a wrapper around a pointer reference.
// Used for wrapping some memory allocated elsewhere into a VM compatible data structure.
// This is only used for exposing the sector's Lines array to ZScript.
// This also must be trivial so that sector_t remains trivial.
// For other uses TArrayView should be preferred.
template <class T>
class TStaticPointedArray
@ -1332,3 +1356,125 @@ public:
memset(&bytes[0], 0, bytes.Size());
}
};
// A wrapper to externally stored data.
// I would have expected something for this in the stl, but std::span is only in C++20.
template <class T>
class TArrayView
{
public:
typedef TIterator<T> iterator;
typedef TIterator<const T> const_iterator;
iterator begin()
{
return &Array[0];
}
const_iterator begin() const
{
return &Array[0];
}
const_iterator cbegin() const
{
return &Array[0];
}
iterator end()
{
return &Array[Count];
}
const_iterator end() const
{
return &Array[Count];
}
const_iterator cend() const
{
return &Array[Count];
}
////////
TArrayView() = default; // intended to keep this type trivial.
TArrayView(T *data, unsigned count = 0)
{
Count = count;
Array = data;
}
TArrayView(const TArrayView<T> &other)
{
Count = other.Count;
Array = other.Array;
}
TArrayView<T> &operator= (const TArrayView<T> &other)
{
Count = other.Count;
Array = other.Array;
return *this;
}
// Check equality of two arrays
bool operator==(const TArrayView<T> &other) const
{
if (Count != other.Count)
{
return false;
}
for (unsigned int i = 0; i < Count; ++i)
{
if (Array[i] != other.Array[i])
{
return false;
}
}
return true;
}
// Return a reference to an element
T &operator[] (size_t index) const
{
return Array[index];
}
// Returns a reference to the last element
T &Last() const
{
return Array[Count - 1];
}
// returns address of first element
T *Data() const
{
return &Array[0];
}
unsigned Size() const
{
return Count;
}
unsigned int Find(const T& item) const
{
unsigned int i;
for (i = 0; i < Count; ++i)
{
if (Array[i] == item)
break;
}
return i;
}
void Set(T *data, unsigned count)
{
Array = data;
Count = count;
}
void Clear()
{
Count = 0;
Array = nullptr;
}
private:
T *Array;
unsigned int Count;
};

View File

@ -101,10 +101,6 @@ void uppercopy (char *to, const char *from)
}
FWadCollection::FWadCollection ()
: FirstLumpIndex(NULL), NextLumpIndex(NULL),
FirstLumpIndex_FullName(NULL), NextLumpIndex_FullName(NULL),
FirstLumpIndex_NoExt(NULL), NextLumpIndex_NoExt(NULL),
NumLumps(0)
{
}
@ -115,37 +111,6 @@ FWadCollection::~FWadCollection ()
void FWadCollection::DeleteAll ()
{
if (FirstLumpIndex != NULL)
{
delete[] FirstLumpIndex;
FirstLumpIndex = NULL;
}
if (NextLumpIndex != NULL)
{
delete[] NextLumpIndex;
NextLumpIndex = NULL;
}
if (FirstLumpIndex_FullName != NULL)
{
delete[] FirstLumpIndex_FullName;
FirstLumpIndex_FullName = NULL;
}
if (NextLumpIndex_FullName != NULL)
{
delete[] NextLumpIndex_FullName;
NextLumpIndex_FullName = NULL;
}
if (FirstLumpIndex_NoExt != NULL)
{
delete[] FirstLumpIndex_NoExt;
FirstLumpIndex_NoExt = NULL;
}
if (NextLumpIndex_NoExt != NULL)
{
delete[] NextLumpIndex_NoExt;
NextLumpIndex_NoExt = NULL;
}
LumpInfo.Clear();
NumLumps = 0;
@ -193,12 +158,13 @@ void FWadCollection::InitMultipleFiles (TArray<FString> &filenames)
FixMacHexen();
// [RH] Set up hash table
FirstLumpIndex = new uint32_t[NumLumps];
NextLumpIndex = new uint32_t[NumLumps];
FirstLumpIndex_FullName = new uint32_t[NumLumps];
NextLumpIndex_FullName = new uint32_t[NumLumps];
FirstLumpIndex_NoExt = new uint32_t[NumLumps];
NextLumpIndex_NoExt = new uint32_t[NumLumps];
Hashes.Resize(6 * NumLumps);
FirstLumpIndex = &Hashes[0];
NextLumpIndex = &Hashes[NumLumps];
FirstLumpIndex_FullName = &Hashes[NumLumps*2];
NextLumpIndex_FullName = &Hashes[NumLumps*3];
FirstLumpIndex_NoExt = &Hashes[NumLumps*4];
NextLumpIndex_NoExt = &Hashes[NumLumps*5];
InitHashChains ();
LumpInfo.ShrinkToFit();
Files.ShrinkToFit();

View File

@ -188,6 +188,7 @@ protected:
TArray<FResourceFile *> Files;
TArray<LumpRecord> LumpInfo;
TArray<uint32_t> Hashes; // one allocation for all hash lists.
uint32_t *FirstLumpIndex; // [RH] Hashing stuff moved out of lumpinfo structure
uint32_t *NextLumpIndex;
@ -197,12 +198,11 @@ protected:
uint32_t *FirstLumpIndex_NoExt; // The same information for fully qualified paths from .zips
uint32_t *NextLumpIndex_NoExt;
uint32_t NumLumps; // Not necessarily the same as LumpInfo.Size()
uint32_t NumLumps = 0; // Not necessarily the same as LumpInfo.Size()
uint32_t NumWads;
int IwadIndex;
void SkinHack (int baselump);
void InitHashChains (); // [RH] Set up the lumpinfo hashing
private:

View File

@ -49,7 +49,7 @@
#include "v_text.h"
#include "m_argv.h"
#include "doomerrors.h"
#include "Win32BaseVideo.h"
#include "win32basevideo.h"
#include "gl/system/gl_framebuffer.h"

View File

@ -1,845 +0,0 @@
/*
** gl_sections.cpp
** Splits sectors into continuous separate parts
**
**---------------------------------------------------------------------------
** Copyright 2008 Christoph Oelckers
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions
** are met:
**
** 1. Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** 2. Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** 3. The name of the author may not be used to endorse or promote products
** derived from this software without specific prior written permission.
** 4. When not used as part of GZDoom or a GZDoom derivative, this code will be
** covered by the terms of the GNU Lesser General Public License as published
** by the Free Software Foundation; either version 2.1 of the License, or (at
** your option) any later version.
** 5. Full disclosure of the entire project's source code, except for third
** party libraries is mandatory. (NOTE: This clause is non-negotiable!)
**
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**---------------------------------------------------------------------------
**
*/
#include "gl/system/gl_system.h"
#include <GL/GLU.h>
#include "i_system.h"
#include "p_local.h"
#include "c_dispatch.h"
#include "gl/data/gl_sections.h"
typedef void (CALLBACK *tessFunc)();
TArray<FGLSectionLine> SectionLines;
TArray<FGLSectionLoop> SectionLoops;
TArray<FGLSection> Sections;
TArray<int> SectionForSubsector;
CVAR (Bool, dumpsections, false, 0)
#define ISDONE(no, p) (p[(no)>>3] & (1 << ((no)&7)))
#define SETDONE(no, p) p[(no)>>3] |= (1 << ((no)&7))
inline vertex_t *V1(side_t *s)
{
line_t *ln = s->linedef;
return s == ln->sidedef[0]? ln->v1: ln->v2;
}
inline vertex_t *V2(side_t *s)
{
line_t *ln = s->linedef;
return s == ln->sidedef[0]? ln->v2: ln->v1;
}
//==========================================================================
//
//
//
//==========================================================================
class FSectionCreator
{
static FSectionCreator *creator;
uint8_t *processed_segs;
uint8_t *processed_subsectors;
int *section_for_segs;
vertex_t *v1_l1, *v2_l1;
FGLSectionLoop *loop;
FGLSection *section; // current working section
public:
//==========================================================================
//
//
//
//==========================================================================
FSectionCreator()
{
processed_segs = new uint8_t[(numsegs+7)/8];
processed_subsectors = new uint8_t[(numsubsectors+7)/8];
memset(processed_segs, 0, (numsegs+7)/8);
memset(processed_subsectors, 0, (numsubsectors+7)/8);
section_for_segs = new int[numsegs];
memset(section_for_segs, -1, numsegs * sizeof(int));
}
//==========================================================================
//
//
//
//==========================================================================
~FSectionCreator()
{
delete [] processed_segs;
delete [] processed_subsectors;
delete [] section_for_segs;
}
//==========================================================================
//
//
//
//==========================================================================
void NewLoop()
{
section->numloops++;
loop = &SectionLoops[SectionLoops.Reserve(1)];
loop->startline = SectionLines.Size();
loop->numlines = 0 ;
}
void NewSection(sector_t *sec)
{
section = &Sections[Sections.Reserve(1)];
section->sector = sec;
section->subsectors.Clear();
section->numloops = 0;
section->startloop = SectionLoops.Size();
section->validcount = -1;
NewLoop();
}
void FinalizeSection()
{
}
//==========================================================================
//
//
//
//==========================================================================
bool AddSeg(seg_t *seg)
{
FGLSectionLine &line = SectionLines[SectionLines.Reserve(1)];
bool firstline = loop->numlines == 0;
if (ISDONE(seg-segs, processed_segs))
{
// should never happen!
DPrintf("Tried to add seg %d to Sections twice. Cannot create Sections.\n", seg-segs);
return false;
}
SETDONE(seg-segs, processed_segs);
section_for_segs[seg-segs] = Sections.Size()-1;
line.start = seg->v1;
line.end = seg->v2;
line.sidedef = seg->sidedef;
line.linedef = seg->linedef;
line.refseg = seg;
line.polysub = NULL;
line.otherside = -1;
if (loop->numlines == 0)
{
v1_l1 = seg->v1;
v2_l1 = seg->v2;
}
loop->numlines++;
return true;
}
//==========================================================================
//
// Utility stuff
//
//==========================================================================
sector_t *FrontRenderSector(seg_t *seg)
{
return seg->Subsector->render_sector;
}
sector_t *BackRenderSector(seg_t *seg)
{
if (seg->PartnerSeg == NULL) return NULL;
return seg->PartnerSeg->Subsector->render_sector;
}
bool IntraSectorSeg(seg_t *seg)
{
return FrontRenderSector(seg) == BackRenderSector(seg);
}
//==========================================================================
//
// returns the seg whose partner seg determines where this
// section continues
//
//==========================================================================
bool AddSubSector(subsector_t *subsec, vertex_t *startpt, seg_t **pNextSeg)
{
unsigned i = 0;
if (startpt != NULL)
{
// find the seg in this subsector that starts at the given vertex
for(i = 0; i < subsec->numlines; i++)
{
if (subsec->firstline[i].v1 == startpt) break;
}
if (i == subsec->numlines)
{
DPrintf("Vertex not found in subsector %d. Cannot create Sections.\n", subsec-subsectors);
return false; // Nodes are bad
}
}
else
{
// Find the first unprocessed non-miniseg
for(i = 0; i < subsec->numlines; i++)
{
seg_t *seg = subsec->firstline + i;
if (seg->sidedef == NULL) continue;
if (IntraSectorSeg(seg)) continue;
if (ISDONE(seg-segs, processed_segs)) continue;
break;
}
if (i == subsec->numlines)
{
DPrintf("Unable to find a start seg. Cannot create Sections.\n");
return false; // Nodes are bad
}
startpt = subsec->firstline[i].v1;
}
seg_t *thisseg = subsec->firstline + i;
if (IntraSectorSeg(thisseg))
{
SETDONE(thisseg-segs, processed_segs);
// continue with the loop in the adjoining subsector
*pNextSeg = thisseg;
return true;
}
while(1)
{
if (loop->numlines > 0 && thisseg->v1 == v1_l1 && thisseg->v2 == v2_l1)
{
// This loop is complete
*pNextSeg = NULL;
return true;
}
if (!AddSeg(thisseg)) return NULL;
i = (i+1) % subsec->numlines;
seg_t *nextseg = subsec->firstline + i;
if (thisseg->v2 != nextseg->v1)
{
DPrintf("Segs in subsector %d are not continuous. Cannot create Sections.\n", subsec-subsectors);
return false; // Nodes are bad
}
if (IntraSectorSeg(nextseg))
{
SETDONE(nextseg-segs, processed_segs);
// continue with the loop in the adjoining subsector
*pNextSeg = nextseg;
return true;
}
thisseg = nextseg;
}
}
//=============================================================================
//
//
//
//=============================================================================
bool FindNextSeg(seg_t **pSeg)
{
// find an unprocessed non-miniseg or a miniseg with an unprocessed
// partner subsector that belongs to the same rendersector
for (unsigned i = 0; i < section->subsectors.Size(); i++)
{
for(unsigned j = 0; j < section->subsectors[i]->numlines; j++)
{
seg_t *seg = section->subsectors[i]->firstline + j;
bool intra = IntraSectorSeg(seg);
if (!intra && !ISDONE(seg-segs, processed_segs))
{
*pSeg = seg;
return true;
}
else if (intra &&
!ISDONE(seg->PartnerSeg->Subsector-subsectors, processed_subsectors))
{
*pSeg = seg->PartnerSeg;
return true;
}
}
}
*pSeg = NULL;
return true;
}
//=============================================================================
//
// all segs and subsectors must be grouped into Sections
//
//=============================================================================
bool CheckSections()
{
bool res = true;
for (int i = 0; i < numsegs; i++)
{
if (segs[i].sidedef != NULL && !ISDONE(i, processed_segs) && !IntraSectorSeg(&segs[i]))
{
Printf("Seg %d (Linedef %d) not processed during section creation\n", i, segs[i].linedef-lines);
res = false;
}
}
for (int i = 0; i < numsubsectors; i++)
{
if (!ISDONE(i, processed_subsectors))
{
Printf("Subsector %d (Sector %d) not processed during section creation\n", i, subsectors[i].sector-sectors);
res = false;
}
}
return res;
}
//=============================================================================
//
//
//
//=============================================================================
void DeleteLine(int i)
{
SectionLines.Delete(i);
for(int i = SectionLoops.Size() - 1; i >= 0; i--)
{
FGLSectionLoop *loop = &SectionLoops[i];
if (loop->startline > i) loop->startline--;
}
}
//=============================================================================
//
//
//
//=============================================================================
void MergeLines(FGLSectionLoop *loop)
{
int i;
int deleted = 0;
FGLSectionLine *ln1;
FGLSectionLine *ln2;
// Merge identical lines in the list
for(i = loop->numlines - 1; i > 0; i--)
{
ln1 = loop->GetLine(i);
ln2 = loop->GetLine(i-1);
if (ln1->sidedef == ln2->sidedef && ln1->otherside == ln2->otherside)
{
// identical references. These 2 lines can be merged.
ln2->end = ln1->end;
SectionLines.Delete(loop->startline + i);
loop->numlines--;
deleted++;
}
}
// If we started in the middle of a sidedef the first and last lines
// may reference the same sidedef. check that, too.
int loopstart = 0;
ln1 = loop->GetLine(0);
for(i = loop->numlines - 1; i > 0; i--)
{
ln2 = loop->GetLine(i);
if (ln1->sidedef != ln2->sidedef || ln1->otherside != ln2->otherside)
break;
}
if (i < loop->numlines-1)
{
i++;
ln2 = loop->GetLine(i);
ln1->start = ln2->start;
SectionLines.Delete(loop->startline + i, loop->numlines - i);
deleted += loop->numlines - i;
loop->numlines = i;
}
// Adjust all following loops
for(unsigned ii = unsigned(loop - &SectionLoops[0]) + 1; ii < SectionLoops.Size(); ii++)
{
SectionLoops[ii].startline -= deleted;
}
}
//=============================================================================
//
//
//
//=============================================================================
void SetReferences()
{
for(unsigned i = 0; i < SectionLines.Size(); i++)
{
FGLSectionLine *ln = &SectionLines[i];
seg_t *seg = ln->refseg;
if (seg != NULL)
{
seg_t *partner = seg->PartnerSeg;
if (seg->PartnerSeg == NULL)
{
ln->otherside = -1;
}
else
{
ln->otherside = section_for_segs[partner-segs];
}
}
else
{
ln->otherside = -1;
}
}
for(unsigned i = 0; i < SectionLoops.Size(); i++)
{
MergeLines(&SectionLoops[i]);
}
}
//=============================================================================
//
// cbTessBegin
//
// called when the tesselation of a new loop starts
//
//=============================================================================
static void CALLBACK cbTessBegin(GLenum type, void *section)
{
FGLSection *sect = (FGLSection*)section;
sect->primitives.Push(type);
sect->primitives.Push(sect->vertices.Size());
}
//=============================================================================
//
// cbTessError
//
// called when the tesselation failed
//
//=============================================================================
static void CALLBACK cbTessError(GLenum error, void *section)
{
}
//=============================================================================
//
// cbTessCombine
//
// called when the two or more vertexes are on the same coordinate
//
//=============================================================================
static void CALLBACK cbTessCombine( GLdouble coords[3], void *vert[4], GLfloat w[4], void **dataOut )
{
*dataOut = vert[0];
}
//=============================================================================
//
// cbTessVertex
//
// called when a vertex is found
//
//=============================================================================
static void CALLBACK cbTessVertex( void *vert, void *section )
{
FGLSection *sect = (FGLSection*)section;
sect->vertices.Push(int(intptr_t(vert)));
}
//=============================================================================
//
// cbTessEnd
//
// called when the tesselation of a the current loop ends
//
//=============================================================================
static void CALLBACK cbTessEnd(void *section)
{
}
//=============================================================================
//
//
//
//=============================================================================
void tesselateSections()
{
// init tesselator
GLUtesselator *tess = gluNewTess();
if (!tess)
{
return;
}
// set callbacks
gluTessCallback(tess, GLU_TESS_BEGIN_DATA, (tessFunc)cbTessBegin);
gluTessCallback(tess, GLU_TESS_VERTEX_DATA, (tessFunc)cbTessVertex);
gluTessCallback(tess, GLU_TESS_ERROR_DATA, (tessFunc)cbTessError);
gluTessCallback(tess, GLU_TESS_COMBINE, (tessFunc)cbTessCombine);
gluTessCallback(tess, GLU_TESS_END_DATA, (tessFunc)cbTessEnd);
for(unsigned int i=0;i<Sections.Size(); i++)
{
FGLSection *sect = &Sections[i];
gluTessBeginPolygon(tess, sect);
for(int j=0; j< sect->numloops; j++)
{
gluTessBeginContour(tess);
FGLSectionLoop *loop = sect->GetLoop(j);
for(int k=0; k<loop->numlines; k++)
{
FGLSectionLine *line = loop->GetLine(k);
vertex_t *vert = line->start;
GLdouble v[3] = {
-(double)vert->x/(double)FRACUNIT, // negate to get proper winding
0.0,
(double)vert->y/(double)FRACUNIT
};
gluTessVertex(tess, v, (void*)(vert - vertexes));
}
gluTessEndContour(tess);
}
gluTessEndPolygon(tess);
sect->vertices.Push(-1337);
sect->vertices.ShrinkToFit();
}
gluDeleteTess(tess);
}
//=============================================================================
//
// First mark all subsectors that have no outside boundaries as processed
// No line in such a subsector will ever be part of a section's border
//
//=============================================================================
void MarkInternalSubsectors()
{
for(int i=0; i < numsubsectors; i++)
{
subsector_t *sub = &subsectors[i];
int j;
for(j=0; j < sub->numlines; j++)
{
seg_t *seg = sub->firstline + j;
if (!IntraSectorSeg(seg)) break;
}
if (j==sub->numlines)
{
// All lines are intra-sector so mark this as processed
SETDONE(i, processed_subsectors);
for(j=0; j < sub->numlines; j++)
{
seg_t *seg = sub->firstline + j;
SETDONE((sub->firstline-segs)+j, processed_segs);
if (seg->PartnerSeg != NULL)
{
SETDONE(int(seg->PartnerSeg - segs), processed_segs);
}
}
}
}
}
//=============================================================================
//
//
//
//=============================================================================
bool CreateSections()
{
int pick = 0;
MarkInternalSubsectors();
while (pick < numsubsectors)
{
if (ISDONE(pick, processed_subsectors))
{
pick++;
continue;
}
subsector_t *subsector = &subsectors[pick];
seg_t *workseg = NULL;
vertex_t *startpt = NULL;
NewSection(subsector->render_sector);
while (1)
{
if (!ISDONE(subsector-subsectors, processed_subsectors))
{
SETDONE(subsector-subsectors, processed_subsectors);
section->subsectors.Push(subsector);
SectionForSubsector[subsector - subsectors] = int(section - &Sections[0]);
}
bool result = AddSubSector(subsector, startpt, &workseg);
if (!result)
{
return false; // couldn't create Sections
}
else if (workseg != NULL)
{
// crossing into another subsector
seg_t *partner = workseg->PartnerSeg;
if (workseg->v2 != partner->v1)
{
DPrintf("Inconsistent subsector references in seg %d. Cannot create Sections.\n", workseg-segs);
return false;
}
subsector = partner->Subsector;
startpt = workseg->v1;
}
else
{
// loop complete. Check adjoining subsectors for other loops to
// be added to this section
if (!FindNextSeg(&workseg))
{
return false;
}
else if (workseg == NULL)
{
// No more subsectors found. This section is complete!
FinalizeSection();
break;
}
else
{
subsector = workseg->Subsector;
// If this is a regular seg, start there, otherwise start
// at the subsector's first seg
startpt = workseg->sidedef == NULL? NULL : workseg->v1;
NewLoop();
}
}
}
}
if (!CheckSections()) return false;
SetReferences();
Sections.ShrinkToFit();
SectionLoops.ShrinkToFit();
SectionLines.ShrinkToFit();
tesselateSections();
return true;
}
};
FSectionCreator *FSectionCreator::creator;
//=============================================================================
//
//
//
//=============================================================================
void DumpSection(int no, FGLSection *sect)
{
Printf(PRINT_LOG, "Section %d, sector %d\n{\n", no, sect->sector->sectornum);
for(int i = 0; i < sect->numloops; i++)
{
Printf(PRINT_LOG, "\tLoop %d\n\t{\n", i);
FGLSectionLoop *loop = sect->GetLoop(i);
for(int i = 0; i < loop->numlines; i++)
{
FGLSectionLine *ln = loop->GetLine(i);
if (ln->sidedef != NULL)
{
vertex_t *v1 = V1(ln->sidedef);
vertex_t *v2 = V2(ln->sidedef);
double dx = FIXED2DBL(v2->x-v1->x);
double dy = FIXED2DBL(v2->y-v1->y);
double dx1 = FIXED2DBL(ln->start->x-v1->x);
double dy1 = FIXED2DBL(ln->start->y-v1->y);
double dx2 = FIXED2DBL(ln->end->x-v1->x);
double dy2 = FIXED2DBL(ln->end->y-v1->y);
double d = sqrt(dx*dx+dy*dy);
double d1 = sqrt(dx1*dx1+dy1*dy1);
double d2 = sqrt(dx2*dx2+dy2*dy2);
Printf(PRINT_LOG, "\t\tLinedef %d, %s: Start (%1.2f, %1.2f), End (%1.2f, %1.2f)",
ln->linedef - lines, ln->sidedef == ln->linedef->sidedef[0]? "front":"back",
ln->start->x/65536.f, ln->start->y/65536.f,
ln->end->x/65536.f, ln->end->y/65536.f);
if (ln->otherside != -1)
{
Printf (PRINT_LOG, ", other side = %d", ln->otherside);
}
if (d1 > 0.005 || d2 < 0.995)
{
Printf(PRINT_LOG, ", Range = %1.3f, %1.3f", d1/d, d2/d);
}
}
else
{
Printf(PRINT_LOG, "\t\tMiniseg: Start (%1.3f, %1.3f), End (%1.3f, %1.3f)\n",
ln->start->x/65536.f, ln->start->y/65536.f, ln->end->x/65536.f, ln->end->y/65536.f);
if (ln->otherside != -1)
{
Printf (PRINT_LOG, ", other side = %d", ln->otherside);
}
}
Printf(PRINT_LOG, "\n");
}
Printf(PRINT_LOG, "\t}\n");
}
int prim = 1;
for(unsigned i = 0; i < sect->vertices.Size(); i++)
{
int v = sect->vertices[i];
if (v < 0)
{
if (i > 0)
{
Printf(PRINT_LOG, "\t}\n");
}
switch (v)
{
case -GL_TRIANGLE_FAN:
Printf(PRINT_LOG, "\t%d: Triangle fan\n\t{\n", prim);
break;
case -GL_TRIANGLE_STRIP:
Printf(PRINT_LOG, "\t%d: Triangle strip\n\t{\n", prim);
break;
case -GL_TRIANGLES:
Printf(PRINT_LOG, "\t%d: Triangles\n\t{\n", prim);
break;
default:
break;
}
prim++;
}
else
{
Printf(PRINT_LOG, "\t\tVertex %d: (%1.2f, %1.2f)\n",
v, vertexes[v].x/65536.f, vertexes[v].y/65536.f);
}
}
Printf(PRINT_LOG, "}\n\n");
}
//=============================================================================
//
//
//
//=============================================================================
void DumpSections()
{
for(unsigned i = 0; i < Sections.Size(); i++)
{
DumpSection(i, &Sections[i]);
}
}
//=============================================================================
//
//
//
//=============================================================================
void gl_CreateSections()
{
SectionLines.Clear();
SectionLoops.Clear();
Sections.Clear();
SectionForSubsector.Resize(numsubsectors);
memset(&SectionForSubsector[0], -1, numsubsectors * sizeof(SectionForSubsector[0]));
FSectionCreator creat;
creat.CreateSections();
if (dumpsections) DumpSections();
}

View File

@ -1,57 +0,0 @@
#ifndef __GL_SECTIONS_H
#define __GL_SECTIONS_H
#include "tarray.h"
#include "r_defs.h"
struct FGLSectionLine
{
vertex_t *start;
vertex_t *end;
side_t *sidedef;
line_t *linedef;
seg_t *refseg; // we need to reference at least one seg for each line.
subsector_t *polysub; // If this is part of a polyobject we need a reference to the containing subsector
int otherside;
};
struct FGLSectionLoop
{
int startline;
int numlines;
FGLSectionLine *GetLine(int no);
};
struct FGLSection
{
sector_t *sector;
TArray<subsector_t *> subsectors;
TArray<int> primitives; // each primitive has 2 entries: Primitive type and vertex count
TArray<int> vertices;
int startloop;
int numloops;
int validcount;
FGLSectionLoop *GetLoop(int no);
};
extern TArray<FGLSectionLine> SectionLines;
extern TArray<FGLSectionLoop> SectionLoops;
extern TArray<FGLSection> Sections;
extern TArray<int> SectionForSubsector;
inline FGLSectionLine *FGLSectionLoop::GetLine(int no)
{
return &SectionLines[startline + no];
}
inline FGLSectionLoop *FGLSection::GetLoop(int no)
{
return &SectionLoops[startloop + no];
}
void gl_CreateSections();
#endif

View File

@ -92,6 +92,8 @@ DoomEdNums
9601 = SecActDamageCeiling
9602 = SecActDeathFloor
9603 = SecActDeathCeiling
9604 = SecActDamage3D
9605 = SecActDeath3D
9800 = PointLight
9801 = PointLightPulse
9802 = PointLightFlicker

View File

@ -7,6 +7,7 @@ version "3.7"
#include "zscript/actor.txt"
#include "zscript/actor_checks.txt"
#include "zscript/events.txt"
#include "zscript/destructible.txt"
#include "zscript/level_compatibility.txt"
#include "zscript/menu/menuitembase.txt"

View File

@ -38,7 +38,8 @@ struct FLineTraceData
TRACE_HitCeiling,
TRACE_HitWall,
TRACE_HitActor,
TRACE_CrossingPortal
TRACE_CrossingPortal,
TRACE_HasHitSky
};
Actor HitActor;
@ -183,6 +184,7 @@ class Actor : Thinker native
native int DesignatedTeam;
native Actor BlockingMobj;
native Line BlockingLine;
native Sector Blocking3DFloor;
native Sector BlockingCeiling;
native Sector BlockingFloor;
native int PoisonDamage;
@ -233,6 +235,7 @@ class Actor : Thinker native
native int RenderHidden;
native int RenderRequired;
native readonly int FriendlySeeBlocks;
native readonly int SpawnTime;
meta String Obituary; // Player was killed by this actor
meta String HitObituary; // Player was killed by this actor in melee
@ -707,6 +710,8 @@ class Actor : Thinker native
native void GiveSecret(bool printmsg = true, bool playsound = true);
native clearscope double GetCameraHeight() const;
native clearscope double GetGravity() const;
native clearscope int GetLevelSpawnTime() const;
native clearscope int GetAge() const;
native bool CheckClass(class<Actor> checkclass, int ptr_select = AAPTR_DEFAULT, bool match_superclass = false);
native void AddInventory(Inventory inv);

View File

@ -503,6 +503,7 @@ enum ETraceFlags
TRACE_PortalRestrict = 0x0008, // Cannot go through portals without a static link offset.
TRACE_ReportPortals = 0x0010, // Report any portal crossing to the TraceCallback
//TRACE_3DCallback = 0x0020, // [ZZ] use TraceCallback to determine whether we need to go through a line to do 3D floor check, or not. without this, only line flag mask is used
TRACE_HitSky = 0x0040 // Hitting the sky returns TRACE_HasHitSky
}
@ -513,7 +514,8 @@ enum ETraceResult
TRACE_HitCeiling,
TRACE_HitWall,
TRACE_HitActor,
TRACE_CrossingPortal
TRACE_CrossingPortal,
TRACE_HasHitSky
}
enum ELineTier
@ -668,6 +670,8 @@ struct LevelLocals native
native bool IsCrouchingAllowed() const;
native bool IsFreelookAllowed() const;
native static clearscope bool IsPointInMap(vector3 p);
native static clearscope vector2 Vec2Diff(vector2 v1, vector2 v2);
native static clearscope vector3 Vec3Diff(vector3 v1, vector3 v2);
native static clearscope vector3 SphericalCoords(vector3 viewpoint, vector3 targetPos, vector2 viewAngles = (0, 0), bool absolute = false);

View File

@ -1122,7 +1122,8 @@ enum EPuffFlags
PF_MELEERANGE = 2,
PF_TEMPORARY = 4,
PF_HITTHINGBLEED = 8,
PF_NORANDOMZ = 16
PF_NORANDOMZ = 16,
PF_HITSKY = 32
};
enum EPlayerCheats

View File

@ -0,0 +1,34 @@
struct HealthGroup native play
{
static clearscope native HealthGroup Find(int id);
readonly int id;
readonly int health;
readonly Array<Sector> sectors;
readonly Array<Line> lines;
native void SetHealth(int newhealth);
}
enum SectorPart
{
SECPART_None = -1,
SECPART_Floor = 0,
SECPART_Ceiling = 1,
SECPART_3D = 2
}
struct Destructible native play
{
static native void DamageSector(Sector sec, Actor source, int damage, Name damagetype, SectorPart part, vector3 position, bool isradius);
static native void DamageLinedef(Line def, Actor source, int damage, Name damagetype, int side, vector3 position, bool isradius);
static native void GeometryLineAttack(TraceResults trace, Actor thing, int damage, Name damagetype);
static native void GeometryRadiusAttack(Actor bombspot, Actor bombsource, int bombdamage, int bombdistance, Name damagetype, int fulldamagedistance);
static native bool ProjectileHitLinedef(Actor projectile, Line def);
static native bool ProjectileHitPlane(Actor projectile, SectorPart part);
static clearscope native bool CheckLinedefVulnerable(Line def, int side, SectorPart part);
static clearscope native bool CheckSectorVulnerable(Sector sec, SectorPart part);
}

View File

@ -323,7 +323,7 @@ extend class Actor
A_PlaySound(AttackSound, CHAN_WEAPON);
A_FaceTarget();
double slope = AimLineAttack(angle, MISSILERANGE);
double ang = angle + Random2[SPosAttack]() * (22.5/256);
double ang = angle + Random2[CPosAttack]() * (22.5/256);
int damage = Random[CPosAttack](1, 5) * 3;
LineAttack(ang, MISSILERANGE, slope, damage, "Hitscan", "Bulletpuff");
}

View File

@ -19,7 +19,7 @@ struct WorldEvent native play version("2.4")
native readonly Actor Thing;
// for thingdied. can be null
native readonly Actor Inflictor;
// for thingdamaged.
// for thingdamaged, line/sector damaged
native readonly int Damage;
native readonly Actor DamageSource;
native readonly Name DamageType;
@ -29,6 +29,14 @@ struct WorldEvent native play version("2.4")
native readonly Line ActivatedLine;
native readonly int ActivationType;
native bool ShouldActivate;
// for line/sector damaged
native readonly SectorPart DamageSectorPart;
native readonly Line DamageLine;
native readonly Sector DamageSector;
native readonly int DamageLineSide;
native readonly vector3 DamagePosition;
native readonly bool DamageIsRadius;
native int NewDamage;
}
struct PlayerEvent native play version("2.4")
@ -313,6 +321,8 @@ class StaticEventHandler : Object native play version("2.4")
virtual native void WorldThingDestroyed(WorldEvent e);
virtual native void WorldLinePreActivated(WorldEvent e);
virtual native void WorldLineActivated(WorldEvent e);
virtual native void WorldSectorDamaged(WorldEvent e);
virtual native void WorldLineDamaged(WorldEvent e);
virtual native void WorldLightning(WorldEvent e); // for the sake of completeness.
virtual native void WorldTick();

View File

@ -180,6 +180,9 @@ struct Line native play
{
return Level.GetUDMFString(LevelLocals.UDMF_Line, Index(), nm);
}
native clearscope int GetHealth();
native void SetHealth(int newhealth);
}
struct SecPlane native play
@ -479,6 +482,8 @@ struct Sector native play
return Level.GetUDMFString(LevelLocals.UDMF_Sector, Index(), nm);
}
native clearscope int GetHealth(SectorPart part);
native void SetHealth(SectorPart part, int newhealth);
}
class SectorTagIterator : Object native

View File

@ -148,6 +148,7 @@ class FastProjectile : Actor
SetZ(floorz);
HitFloor ();
Destructible.ProjectileHitPlane(self, SECPART_Floor);
ExplodeMissile (NULL, NULL);
return;
}
@ -161,6 +162,7 @@ class FastProjectile : Actor
}
SetZ(ceilingz - Height);
Destructible.ProjectileHitPlane(self, SECPART_Ceiling);
ExplodeMissile (NULL, NULL);
return;
}

View File

@ -18,7 +18,9 @@ class SectorAction : Actor
SECSPAC_DamageFloor = 1<<11,
SECSPAC_DamageCeiling = 1<<12,
SECSPAC_DeathFloor = 1<<13,
SECSPAC_DeathCeiling = 1<<14
SECSPAC_DeathCeiling = 1<<14,
SECSPAC_Damage3D = 1<<15,
SECSPAC_Death3D = 1<<16
};
default
@ -252,6 +254,30 @@ class SecActDeathCeiling : SectorAction
override bool CanTrigger (Actor triggerer) { return !!special; }
}
// Triggered when controlled 3d floor is damaged ----------------------------------
class SecActDamage3D : SectorAction
{
Default
{
Health SECSPAC_Damage3D;
}
// [ZZ] damage is unconditional, so this as well
override bool CanTrigger (Actor triggerer) { return !!special; }
}
// Triggered when controlled 3d floor is reduced to 0 hp ----------------------------------
class SecActDeath3D : SectorAction
{
Default
{
Health SECSPAC_Death3D;
}
// [ZZ] damage is unconditional, so this as well
override bool CanTrigger (Actor triggerer) { return !!special; }
}
//==========================================================================
//
// Music changer. Uses the sector action class to do its job