mirror of
https://github.com/ZDoom/ZDRay.git
synced 2024-11-22 12:01:09 +00:00
Merge pull request #38 from MrRaveYard/pr_fix_slopes
Initial support for most common slope types
This commit is contained in:
commit
0e3340b807
11 changed files with 1255 additions and 64 deletions
|
@ -142,6 +142,7 @@ set( SOURCES
|
|||
src/level/level.cpp
|
||||
src/level/level_udmf.cpp
|
||||
src/level/level_light.cpp
|
||||
src/level/level_slopes.cpp
|
||||
src/level/doomdata.h
|
||||
src/level/level.h
|
||||
src/level/workdata.h
|
||||
|
|
|
@ -36,6 +36,7 @@
|
|||
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#ifndef __APPLE__
|
||||
#include <malloc.h>
|
||||
#endif
|
||||
|
@ -343,3 +344,686 @@ public:
|
|||
}
|
||||
}
|
||||
};
|
||||
|
||||
// TMap ---------------------------------------------------------------------
|
||||
// An associative array, similar in concept to the STL extension
|
||||
// class hash_map. It is implemented using Lua's table algorithm:
|
||||
/*
|
||||
** Hash uses a mix of chained scatter table with Brent's variation.
|
||||
** A main invariant of these tables is that, if an element is not
|
||||
** in its main position (i.e. the `original' position that its hash gives
|
||||
** to it), then the colliding element is in its own main position.
|
||||
** Hence even when the load factor reaches 100%, performance remains good.
|
||||
*/
|
||||
/******************************************************************************
|
||||
* Copyright (C) 1994-2006 Lua.org, PUC-Rio. All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
******************************************************************************/
|
||||
|
||||
typedef unsigned int hash_t;
|
||||
|
||||
template<class KT> struct THashTraits
|
||||
{
|
||||
// Returns the hash value for a key.
|
||||
hash_t Hash(const KT key) { return (hash_t)(intptr_t)key; }
|
||||
hash_t Hash(double key)
|
||||
{
|
||||
hash_t keyhash[2];
|
||||
memcpy(&keyhash, &key, sizeof(keyhash));
|
||||
return keyhash[0] ^ keyhash[1];
|
||||
}
|
||||
|
||||
// Compares two keys, returning zero if they are the same.
|
||||
int Compare(const KT left, const KT right) { return left != right; }
|
||||
};
|
||||
|
||||
template<> struct THashTraits<float>
|
||||
{
|
||||
// Use all bits when hashing singles instead of converting them to ints.
|
||||
hash_t Hash(float key)
|
||||
{
|
||||
hash_t keyhash;
|
||||
memcpy(&keyhash, &key, sizeof(keyhash));
|
||||
return keyhash;
|
||||
}
|
||||
int Compare(float left, float right) { return left != right; }
|
||||
};
|
||||
|
||||
template<> struct THashTraits<double>
|
||||
{
|
||||
// Use all bits when hashing doubles instead of converting them to ints.
|
||||
hash_t Hash(double key)
|
||||
{
|
||||
hash_t keyhash[2];
|
||||
memcpy(&keyhash, &key, sizeof(keyhash));
|
||||
return keyhash[0] ^ keyhash[1];
|
||||
}
|
||||
int Compare(double left, double right) { return left != right; }
|
||||
};
|
||||
|
||||
template<class VT> struct TValueTraits
|
||||
{
|
||||
// Initializes a value for TMap. If a regular constructor isn't
|
||||
// good enough, you can override it.
|
||||
void Init(VT& value)
|
||||
{
|
||||
::new(&value) VT;
|
||||
}
|
||||
};
|
||||
|
||||
// Must match layout of TMap
|
||||
struct FMap
|
||||
{
|
||||
void* Nodes;
|
||||
void* LastFree;
|
||||
hash_t Size;
|
||||
hash_t NumUsed;
|
||||
};
|
||||
|
||||
|
||||
template<class KT, class VT, class MapType> class TMapIterator;
|
||||
template<class KT, class VT, class MapType> class TMapConstIterator;
|
||||
|
||||
template<class KT, class VT, class HashTraits = THashTraits<KT>, class ValueTraits = TValueTraits<VT> >
|
||||
class TMap
|
||||
{
|
||||
template<class KTa, class VTa, class MTa> friend class TMapIterator;
|
||||
template<class KTb, class VTb, class MTb> friend class TMapConstIterator;
|
||||
|
||||
public:
|
||||
typedef class TMap<KT, VT, HashTraits, ValueTraits> MyType;
|
||||
typedef class TMapIterator<KT, VT, MyType> Iterator;
|
||||
typedef class TMapConstIterator<KT, VT, MyType> ConstIterator;
|
||||
typedef struct { const KT Key; VT Value; } Pair;
|
||||
typedef const Pair ConstPair;
|
||||
|
||||
TMap() { NumUsed = 0; SetNodeVector(1); }
|
||||
TMap(hash_t size) { NumUsed = 0; SetNodeVector(size); }
|
||||
~TMap() { ClearNodeVector(); }
|
||||
|
||||
TMap(const TMap& o)
|
||||
{
|
||||
NumUsed = 0;
|
||||
SetNodeVector(o.CountUsed());
|
||||
CopyNodes(o.Nodes, o.Size);
|
||||
}
|
||||
|
||||
TMap(TMap&& o)
|
||||
{
|
||||
Nodes = o.Nodes;
|
||||
LastFree = o.LastFree; /* any free position is before this position */
|
||||
Size = o.Size; /* must be a power of 2 */
|
||||
NumUsed = o.NumUsed;
|
||||
|
||||
o.Size = 0;
|
||||
o.NumUsed = 0;
|
||||
o.SetNodeVector(1);
|
||||
}
|
||||
|
||||
TMap& operator= (const TMap& o)
|
||||
{
|
||||
NumUsed = 0;
|
||||
ClearNodeVector();
|
||||
SetNodeVector(o.CountUsed());
|
||||
CopyNodes(o.Nodes, o.Size);
|
||||
return *this;
|
||||
}
|
||||
|
||||
TMap& operator= (TMap&& o)
|
||||
{
|
||||
TransferFrom(o);
|
||||
return *this;
|
||||
}
|
||||
|
||||
//=======================================================================
|
||||
//
|
||||
// TransferFrom
|
||||
//
|
||||
// Moves the contents from one TMap to another, leaving the TMap moved
|
||||
// from empty.
|
||||
//
|
||||
//=======================================================================
|
||||
|
||||
void TransferFrom(TMap& o)
|
||||
{
|
||||
// Clear all our nodes.
|
||||
NumUsed = 0;
|
||||
ClearNodeVector();
|
||||
|
||||
// Copy all of o's nodes.
|
||||
Nodes = o.Nodes;
|
||||
LastFree = o.LastFree;
|
||||
Size = o.Size;
|
||||
NumUsed = o.NumUsed;
|
||||
|
||||
// Tell o it doesn't have any nodes.
|
||||
o.Nodes = NULL;
|
||||
o.Size = 0;
|
||||
o.LastFree = NULL;
|
||||
o.NumUsed = 0;
|
||||
|
||||
// Leave o functional with one empty node.
|
||||
o.SetNodeVector(1);
|
||||
}
|
||||
|
||||
//=======================================================================
|
||||
//
|
||||
// Clear
|
||||
//
|
||||
// Empties out the table and resizes it with room for count entries.
|
||||
//
|
||||
//=======================================================================
|
||||
|
||||
void Clear(hash_t count = 1)
|
||||
{
|
||||
ClearNodeVector();
|
||||
SetNodeVector(count);
|
||||
}
|
||||
|
||||
//=======================================================================
|
||||
//
|
||||
// CountUsed
|
||||
//
|
||||
// Returns the number of entries in use in the table.
|
||||
//
|
||||
//=======================================================================
|
||||
|
||||
hash_t CountUsed() const
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
hash_t used = 0;
|
||||
hash_t ct = Size;
|
||||
for (Node* n = Nodes; ct-- > 0; ++n)
|
||||
{
|
||||
if (!n->IsNil())
|
||||
{
|
||||
++used;
|
||||
}
|
||||
}
|
||||
assert(used == NumUsed);
|
||||
#endif
|
||||
return NumUsed;
|
||||
}
|
||||
|
||||
//=======================================================================
|
||||
//
|
||||
// operator[]
|
||||
//
|
||||
// Returns a reference to the value associated with a particular key,
|
||||
// creating the pair if the key isn't already in the table.
|
||||
//
|
||||
//=======================================================================
|
||||
|
||||
VT& operator[] (const KT key)
|
||||
{
|
||||
return GetNode(key)->Pair.Value;
|
||||
}
|
||||
|
||||
const VT& operator[] (const KT key) const
|
||||
{
|
||||
return GetNode(key)->Pair.Value;
|
||||
}
|
||||
|
||||
//=======================================================================
|
||||
//
|
||||
// CheckKey
|
||||
//
|
||||
// Returns a pointer to the value associated with a particular key, or
|
||||
// NULL if the key isn't in the table.
|
||||
//
|
||||
//=======================================================================
|
||||
|
||||
VT* CheckKey(const KT key)
|
||||
{
|
||||
Node* n = FindKey(key);
|
||||
return n != NULL ? &n->Pair.Value : NULL;
|
||||
}
|
||||
|
||||
const VT* CheckKey(const KT key) const
|
||||
{
|
||||
const Node* n = FindKey(key);
|
||||
return n != NULL ? &n->Pair.Value : NULL;
|
||||
}
|
||||
|
||||
//=======================================================================
|
||||
//
|
||||
// Insert
|
||||
//
|
||||
// Adds a key/value pair to the table if key isn't in the table, or
|
||||
// replaces the value for the existing pair if the key is in the table.
|
||||
//
|
||||
// This is functionally equivalent to (*this)[key] = value; but can be
|
||||
// slightly faster if the pair needs to be created because it doesn't run
|
||||
// the constructor on the value part twice.
|
||||
//
|
||||
//=======================================================================
|
||||
|
||||
VT& Insert(const KT key, const VT& value)
|
||||
{
|
||||
Node* n = FindKey(key);
|
||||
if (n != NULL)
|
||||
{
|
||||
n->Pair.Value = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
n = NewKey(key);
|
||||
::new(&n->Pair.Value) VT(value);
|
||||
}
|
||||
return n->Pair.Value;
|
||||
}
|
||||
|
||||
VT& Insert(const KT key, VT&& value)
|
||||
{
|
||||
Node* n = FindKey(key);
|
||||
if (n != NULL)
|
||||
{
|
||||
n->Pair.Value = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
n = NewKey(key);
|
||||
::new(&n->Pair.Value) VT(value);
|
||||
}
|
||||
return n->Pair.Value;
|
||||
}
|
||||
|
||||
VT& InsertNew(const KT key)
|
||||
{
|
||||
Node* n = FindKey(key);
|
||||
if (n != NULL)
|
||||
{
|
||||
n->Pair.Value.~VT();
|
||||
}
|
||||
else
|
||||
{
|
||||
n = NewKey(key);
|
||||
}
|
||||
::new(&n->Pair.Value) VT;
|
||||
return n->Pair.Value;
|
||||
}
|
||||
|
||||
//=======================================================================
|
||||
//
|
||||
// Remove
|
||||
//
|
||||
// Removes the key/value pair for a particular key if it is in the table.
|
||||
//
|
||||
//=======================================================================
|
||||
|
||||
void Remove(const KT key)
|
||||
{
|
||||
DelKey(key);
|
||||
}
|
||||
|
||||
void Swap(MyType& other)
|
||||
{
|
||||
std::swap(Nodes, other.Nodes);
|
||||
std::swap(LastFree, other.LastFree);
|
||||
std::swap(Size, other.Size);
|
||||
std::swap(NumUsed, other.NumUsed);
|
||||
}
|
||||
|
||||
protected:
|
||||
struct IPair // This must be the same as Pair above, but with a
|
||||
{ // non-const Key.
|
||||
KT Key;
|
||||
VT Value;
|
||||
};
|
||||
struct Node
|
||||
{
|
||||
Node* Next;
|
||||
IPair Pair;
|
||||
void SetNil()
|
||||
{
|
||||
Next = (Node*)1;
|
||||
}
|
||||
bool IsNil() const
|
||||
{
|
||||
return Next == (Node*)1;
|
||||
}
|
||||
};
|
||||
|
||||
/* This is used instead of memcpy, because Node is likely to be small,
|
||||
* such that the time spent calling a function would eclipse the time
|
||||
* spent copying. */
|
||||
struct NodeSizedStruct { unsigned char Pads[sizeof(Node)]; };
|
||||
|
||||
Node* Nodes;
|
||||
Node* LastFree; /* any free position is before this position */
|
||||
hash_t Size; /* must be a power of 2 */
|
||||
hash_t NumUsed;
|
||||
|
||||
const Node* MainPosition(const KT k) const
|
||||
{
|
||||
HashTraits Traits;
|
||||
return &Nodes[Traits.Hash(k) & (Size - 1)];
|
||||
}
|
||||
|
||||
Node* MainPosition(const KT k)
|
||||
{
|
||||
HashTraits Traits;
|
||||
return &Nodes[Traits.Hash(k) & (Size - 1)];
|
||||
}
|
||||
|
||||
void SetNodeVector(hash_t size)
|
||||
{
|
||||
// Round size up to nearest power of 2
|
||||
for (Size = 1; Size < size; Size <<= 1)
|
||||
{
|
||||
}
|
||||
Nodes = (Node*)malloc(Size * sizeof(Node));
|
||||
LastFree = &Nodes[Size]; /* all positions are free */
|
||||
for (hash_t i = 0; i < Size; ++i)
|
||||
{
|
||||
Nodes[i].SetNil();
|
||||
}
|
||||
}
|
||||
|
||||
void ClearNodeVector()
|
||||
{
|
||||
for (hash_t i = 0; i < Size; ++i)
|
||||
{
|
||||
if (!Nodes[i].IsNil())
|
||||
{
|
||||
Nodes[i].~Node();
|
||||
}
|
||||
}
|
||||
free(Nodes);
|
||||
Nodes = NULL;
|
||||
Size = 0;
|
||||
LastFree = NULL;
|
||||
NumUsed = 0;
|
||||
}
|
||||
|
||||
void Resize(hash_t nhsize)
|
||||
{
|
||||
hash_t i, oldhsize = Size;
|
||||
Node* nold = Nodes;
|
||||
/* create new hash part with appropriate size */
|
||||
SetNodeVector(nhsize);
|
||||
/* re-insert elements from hash part */
|
||||
NumUsed = 0;
|
||||
for (i = 0; i < oldhsize; ++i)
|
||||
{
|
||||
if (!nold[i].IsNil())
|
||||
{
|
||||
Node* n = NewKey(nold[i].Pair.Key);
|
||||
::new(&n->Pair.Value) VT(std::move(nold[i].Pair.Value));
|
||||
nold[i].~Node();
|
||||
}
|
||||
}
|
||||
free(nold);
|
||||
}
|
||||
|
||||
void Rehash()
|
||||
{
|
||||
Resize(Size << 1);
|
||||
}
|
||||
|
||||
Node* GetFreePos()
|
||||
{
|
||||
while (LastFree-- > Nodes)
|
||||
{
|
||||
if (LastFree->IsNil())
|
||||
{
|
||||
return LastFree;
|
||||
}
|
||||
}
|
||||
return NULL; /* could not find a free place */
|
||||
}
|
||||
|
||||
/*
|
||||
** Inserts a new key into a hash table; first, check whether key's main
|
||||
** position is free. If not, check whether colliding node is in its main
|
||||
** position or not: if it is not, move colliding node to an empty place and
|
||||
** put new key in its main position; otherwise (colliding node is in its main
|
||||
** position), new key goes to an empty position.
|
||||
**
|
||||
** The Value field is left unconstructed.
|
||||
*/
|
||||
Node* NewKey(const KT key)
|
||||
{
|
||||
Node* mp = MainPosition(key);
|
||||
if (!mp->IsNil())
|
||||
{
|
||||
Node* othern;
|
||||
Node* n = GetFreePos(); /* get a free place */
|
||||
if (n == NULL) /* cannot find a free place? */
|
||||
{
|
||||
Rehash(); /* grow table */
|
||||
return NewKey(key); /* re-insert key into grown table */
|
||||
}
|
||||
othern = MainPosition(mp->Pair.Key);
|
||||
if (othern != mp) /* is colliding node out of its main position? */
|
||||
{ /* yes; move colliding node into free position */
|
||||
while (othern->Next != mp) /* find previous */
|
||||
{
|
||||
othern = othern->Next;
|
||||
}
|
||||
othern->Next = n; /* redo the chain with 'n' in place of 'mp' */
|
||||
CopyNode(n, mp); /* copy colliding node into free pos. (mp->Next also goes) */
|
||||
mp->Next = NULL; /* now 'mp' is free */
|
||||
}
|
||||
else /* colliding node is in its own main position */
|
||||
{ /* new node will go into free position */
|
||||
n->Next = mp->Next; /* chain new position */
|
||||
mp->Next = n;
|
||||
mp = n;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
mp->Next = NULL;
|
||||
}
|
||||
++NumUsed;
|
||||
::new(&mp->Pair.Key) KT(key);
|
||||
return mp;
|
||||
}
|
||||
|
||||
void DelKey(const KT key)
|
||||
{
|
||||
Node* mp = MainPosition(key), ** mpp;
|
||||
HashTraits Traits;
|
||||
|
||||
if (mp->IsNil())
|
||||
{
|
||||
/* the key is definitely not present, because there is nothing at its main position */
|
||||
}
|
||||
else if (!Traits.Compare(mp->Pair.Key, key)) /* the key is in its main position */
|
||||
{
|
||||
if (mp->Next != NULL) /* move next node to its main position */
|
||||
{
|
||||
Node* n = mp->Next;
|
||||
mp->~Node(); /* deconstruct old node */
|
||||
CopyNode(mp, n); /* copy next node */
|
||||
n->SetNil(); /* next node is now nil */
|
||||
}
|
||||
else
|
||||
{
|
||||
mp->~Node();
|
||||
mp->SetNil(); /* there is no chain, so main position is nil */
|
||||
}
|
||||
--NumUsed;
|
||||
}
|
||||
else /* the key is either not present or not in its main position */
|
||||
{
|
||||
for (mpp = &mp->Next, mp = *mpp; mp != NULL && Traits.Compare(mp->Pair.Key, key); mpp = &mp->Next, mp = *mpp)
|
||||
{
|
||||
} /* look for the key */
|
||||
if (mp != NULL) /* found it */
|
||||
{
|
||||
*mpp = mp->Next; /* rechain so this node is skipped */
|
||||
mp->~Node();
|
||||
mp->SetNil(); /* because this node is now nil */
|
||||
--NumUsed;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Node* FindKey(const KT key)
|
||||
{
|
||||
HashTraits Traits;
|
||||
Node* n = MainPosition(key);
|
||||
while (n != NULL && !n->IsNil() && Traits.Compare(n->Pair.Key, key))
|
||||
{
|
||||
n = n->Next;
|
||||
}
|
||||
return n == NULL || n->IsNil() ? NULL : n;
|
||||
}
|
||||
|
||||
const Node* FindKey(const KT key) const
|
||||
{
|
||||
HashTraits Traits;
|
||||
const Node* n = MainPosition(key);
|
||||
while (n != NULL && !n->IsNil() && Traits.Compare(n->Pair.Key, key))
|
||||
{
|
||||
n = n->Next;
|
||||
}
|
||||
return n == NULL || n->IsNil() ? NULL : n;
|
||||
}
|
||||
|
||||
Node* GetNode(const KT key)
|
||||
{
|
||||
Node* n = FindKey(key);
|
||||
if (n != NULL)
|
||||
{
|
||||
return n;
|
||||
}
|
||||
n = NewKey(key);
|
||||
ValueTraits traits;
|
||||
traits.Init(n->Pair.Value);
|
||||
return n;
|
||||
}
|
||||
|
||||
/* Perform a bit-wise copy of the node. Used when relocating a node in the table. */
|
||||
void CopyNode(Node* dst, const Node* src)
|
||||
{
|
||||
*(NodeSizedStruct*)dst = *(const NodeSizedStruct*)src;
|
||||
}
|
||||
|
||||
/* Copy all nodes in the node vector to this table. */
|
||||
void CopyNodes(const Node* nodes, hash_t numnodes)
|
||||
{
|
||||
for (; numnodes-- > 0; ++nodes)
|
||||
{
|
||||
if (!nodes->IsNil())
|
||||
{
|
||||
Node* n = NewKey(nodes->Pair.Key);
|
||||
::new(&n->Pair.Value) VT(nodes->Pair.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// TMapIterator -------------------------------------------------------------
|
||||
// A class to iterate over all the pairs in a TMap.
|
||||
|
||||
template<class KT, class VT, class MapType = TMap<KT, VT> >
|
||||
class TMapIterator
|
||||
{
|
||||
public:
|
||||
TMapIterator(MapType& map)
|
||||
: Map(map), Position(0)
|
||||
{
|
||||
}
|
||||
|
||||
//=======================================================================
|
||||
//
|
||||
// NextPair
|
||||
//
|
||||
// Returns false if there are no more entries in the table. Otherwise, it
|
||||
// returns true, and pair is filled with a pointer to the pair in the
|
||||
// table.
|
||||
//
|
||||
//=======================================================================
|
||||
|
||||
bool NextPair(typename MapType::Pair*& pair)
|
||||
{
|
||||
if (Position >= Map.Size)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
do
|
||||
{
|
||||
if (!Map.Nodes[Position].IsNil())
|
||||
{
|
||||
pair = reinterpret_cast<typename MapType::Pair*>(&Map.Nodes[Position].Pair);
|
||||
Position += 1;
|
||||
return true;
|
||||
}
|
||||
} while (++Position < Map.Size);
|
||||
return false;
|
||||
}
|
||||
|
||||
//=======================================================================
|
||||
//
|
||||
// Reset
|
||||
//
|
||||
// Restarts the iteration so you can do it all over again.
|
||||
//
|
||||
//=======================================================================
|
||||
|
||||
void Reset()
|
||||
{
|
||||
Position = 0;
|
||||
}
|
||||
|
||||
protected:
|
||||
MapType& Map;
|
||||
hash_t Position;
|
||||
};
|
||||
|
||||
// TMapConstIterator --------------------------------------------------------
|
||||
// Exactly the same as TMapIterator, but it works with a const TMap.
|
||||
|
||||
template<class KT, class VT, class MapType = TMap<KT, VT> >
|
||||
class TMapConstIterator
|
||||
{
|
||||
public:
|
||||
TMapConstIterator(const MapType& map)
|
||||
: Map(map), Position(0)
|
||||
{
|
||||
}
|
||||
|
||||
bool NextPair(typename MapType::ConstPair*& pair)
|
||||
{
|
||||
if (Position >= Map.Size)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
do
|
||||
{
|
||||
if (!Map.Nodes[Position].IsNil())
|
||||
{
|
||||
pair = reinterpret_cast<typename MapType::Pair*>(&Map.Nodes[Position].Pair);
|
||||
Position += 1;
|
||||
return true;
|
||||
}
|
||||
} while (++Position < Map.Size);
|
||||
return false;
|
||||
}
|
||||
|
||||
protected:
|
||||
const MapType& Map;
|
||||
hash_t Position;
|
||||
};
|
||||
|
||||
|
|
|
@ -92,6 +92,8 @@ struct MapLineDef2
|
|||
uint16_t sidenum[2];
|
||||
};
|
||||
|
||||
struct IntSector;
|
||||
|
||||
struct IntLineDef
|
||||
{
|
||||
uint32_t v1;
|
||||
|
@ -103,6 +105,8 @@ struct IntLineDef
|
|||
|
||||
TArray<UDMFKey> props;
|
||||
TArray<int> ids;
|
||||
|
||||
IntSector *frontsector, *backsector;
|
||||
};
|
||||
|
||||
struct MapSector
|
||||
|
@ -139,6 +143,8 @@ struct IntSector
|
|||
bool skySector;
|
||||
|
||||
TArray<UDMFKey> props;
|
||||
|
||||
TArray<IntLineDef*> lines;
|
||||
};
|
||||
|
||||
struct MapSubsector
|
||||
|
@ -263,6 +269,9 @@ struct IntVertex
|
|||
{
|
||||
TArray<UDMFKey> props;
|
||||
double zfloor = 100000, zceiling = 100000;
|
||||
|
||||
inline bool HasZFloor() const { return zfloor != 100000; }
|
||||
inline bool HasZCeiling() const { return zceiling != 100000; }
|
||||
};
|
||||
|
||||
class BBox;
|
||||
|
@ -416,6 +425,9 @@ struct FLevel
|
|||
void RemoveExtraLines ();
|
||||
void RemoveExtraSides ();
|
||||
void RemoveExtraSectors ();
|
||||
|
||||
void PostLoadInitialization();
|
||||
|
||||
void SetupLights();
|
||||
|
||||
int NumSides() const { return Sides.Size(); }
|
||||
|
@ -433,6 +445,9 @@ struct FLevel
|
|||
|
||||
vec3 GetLightProbePosition(int index);
|
||||
|
||||
int FindFirstSectorFromTag(int tag);
|
||||
|
||||
inline IntSector* PointInSector(const dvec2& pos) { return GetSectorFromSubSector(PointInSubSector(int(pos.x), int(pos.y))); }
|
||||
private:
|
||||
void CheckSkySectors();
|
||||
void CreateLights();
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include "level/level.h"
|
||||
#include "lightmap/cpuraytracer.h"
|
||||
#include "lightmap/gpuraytracer.h"
|
||||
#include "math/vec.h"
|
||||
//#include "rejectbuilder.h"
|
||||
#include <memory>
|
||||
|
||||
|
@ -46,19 +47,6 @@ enum
|
|||
PO_SPAWN_TYPE,
|
||||
PO_SPAWNCRUSH_TYPE,
|
||||
PO_SPAWNHURT_TYPE,
|
||||
|
||||
// Thing numbers used to define slopes
|
||||
SMT_VavoomFloor = 1500,
|
||||
SMT_VavoomCeiling = 1501,
|
||||
SMT_VertexFloorZ = 1504,
|
||||
SMT_VertexCeilingZ = 1505,
|
||||
SMT_SlopeFloorPointLine = 9500,
|
||||
SMT_SlopeCeilingPointLine = 9501,
|
||||
SMT_SetFloorSlope = 9502,
|
||||
SMT_SetCeilingSlope = 9503,
|
||||
SMT_CopyFloorPlane = 9510,
|
||||
SMT_CopyCeilingPlane = 9511,
|
||||
|
||||
};
|
||||
|
||||
FLevel::FLevel ()
|
||||
|
@ -549,6 +537,22 @@ void FLevel::RemoveExtraSectors ()
|
|||
delete[] remap;
|
||||
}
|
||||
|
||||
int FLevel::FindFirstSectorFromTag(int tag)
|
||||
{
|
||||
for (unsigned i = 0; i < Sectors.Size(); ++i)
|
||||
{
|
||||
for (auto& sectorTag : Sectors[i].tags)
|
||||
{
|
||||
if (sectorTag == tag)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
void FProcessor::GetPolySpots ()
|
||||
{
|
||||
if (Extended && CheckPolyobjs)
|
||||
|
@ -602,6 +606,65 @@ void FProcessor::GetPolySpots ()
|
|||
}
|
||||
}
|
||||
|
||||
void FLevel::PostLoadInitialization()
|
||||
{
|
||||
CheckSkySectors();
|
||||
|
||||
for (unsigned int i = 0; i < Sectors.Size(); i++)
|
||||
Sectors[i].controlsector = false;
|
||||
|
||||
for (unsigned int i = 0; i < Sides.Size(); i++)
|
||||
Sides[i].line = nullptr;
|
||||
|
||||
for (unsigned int i = 0; i < Lines.Size(); i++)
|
||||
{
|
||||
IntLineDef* line = &Lines[i];
|
||||
|
||||
// Link sides to lines
|
||||
if (line->sidenum[0] < Sides.Size())
|
||||
Sides[line->sidenum[0]].line = line;
|
||||
if (line->sidenum[1] < Sides.Size())
|
||||
Sides[line->sidenum[1]].line = line;
|
||||
|
||||
if (line->special == Sector_Set3DFloor)
|
||||
{
|
||||
int sectorTag = line->args[0];
|
||||
int type = line->args[1];
|
||||
int opacity = line->args[3];
|
||||
|
||||
if (opacity > 0)
|
||||
{
|
||||
IntSector* controlsector = &Sectors[Sides[Lines[i].sidenum[0]].sector];
|
||||
controlsector->controlsector = true;
|
||||
|
||||
for (unsigned int j = 0; j < Sectors.Size(); j++)
|
||||
{
|
||||
for (unsigned t = 0; t < Sectors[j].tags.Size(); t++)
|
||||
{
|
||||
if (Sectors[j].tags[t] == sectorTag)
|
||||
{
|
||||
Sectors[j].x3dfloors.Push(controlsector);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set front and back sector pointer
|
||||
for (auto& side : Sides)
|
||||
{
|
||||
auto& sector = Sectors[side.sector];
|
||||
auto line = side.line;
|
||||
|
||||
sector.lines.Push(line);
|
||||
|
||||
line->frontsector = (line->sidenum[0] != NO_INDEX) ? &Sectors[Sides[line->sidenum[0]].sector] : nullptr;
|
||||
line->backsector = (line->sidenum[1] != NO_INDEX) ? &Sectors[Sides[line->sidenum[1]].sector] : nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void FProcessor::BuildNodes()
|
||||
{
|
||||
NodesBuilt = true;
|
||||
|
@ -693,7 +756,15 @@ void FProcessor::BuildNodes()
|
|||
|
||||
void FProcessor::BuildLightmaps()
|
||||
{
|
||||
Level.PostLoadInitialization();
|
||||
|
||||
SpawnSlopeMakers(&Level.Things[0], &Level.Things[Level.Things.Size()], nullptr);
|
||||
CopySlopes();
|
||||
|
||||
SetSlopes();
|
||||
|
||||
Level.SetupLights();
|
||||
|
||||
LightmapMesh = std::make_unique<LevelMesh>(Level, Level.DefaultSamples, LMDims);
|
||||
|
||||
std::unique_ptr<GPURaytracer> gpuraytracer;
|
||||
|
|
|
@ -69,6 +69,15 @@ private:
|
|||
void GetPolySpots();
|
||||
void SetLineID(IntLineDef *ld);
|
||||
|
||||
void SetSlopes();
|
||||
void CopySlopes();
|
||||
void AlignPlane(IntSector* sec, IntLineDef* line, int which);
|
||||
void SetSlopesFromVertexHeights(IntThing* firstmt, IntThing* lastmt, const int* oldvertextable);
|
||||
void SpawnSlopeMakers(IntThing* firstmt, IntThing* lastmt, const int* oldvertextable);
|
||||
void CopyPlane(int tag, IntSector* dest, bool copyCeil);
|
||||
void CopyPlane(int tag, const dvec2& pos, bool copyCeil);
|
||||
|
||||
|
||||
MapNodeEx *NodesToEx(const MapNode *nodes, int count);
|
||||
MapSubsectorEx *SubsectorsToEx(const MapSubsector *ssec, int count);
|
||||
MapSegGLEx *SegGLsToEx(const MapSegGL *segs, int count);
|
||||
|
|
|
@ -40,50 +40,6 @@
|
|||
|
||||
void FLevel::SetupLights()
|
||||
{
|
||||
CheckSkySectors();
|
||||
|
||||
for (unsigned int i = 0; i < Sectors.Size(); i++)
|
||||
Sectors[i].controlsector = false;
|
||||
|
||||
for (unsigned int i = 0; i < Sides.Size(); i++)
|
||||
Sides[i].line = nullptr;
|
||||
|
||||
for (unsigned int i = 0; i < Lines.Size(); i++)
|
||||
{
|
||||
IntLineDef *line = &Lines[i];
|
||||
|
||||
// Link sides to lines
|
||||
if (line->sidenum[0] < Sides.Size())
|
||||
Sides[line->sidenum[0]].line = line;
|
||||
if (line->sidenum[1] < Sides.Size())
|
||||
Sides[line->sidenum[1]].line = line;
|
||||
|
||||
if (line->special == Sector_Set3DFloor)
|
||||
{
|
||||
int sectorTag = line->args[0];
|
||||
int type = line->args[1];
|
||||
int opacity = line->args[3];
|
||||
|
||||
if (opacity > 0)
|
||||
{
|
||||
IntSector *controlsector = &Sectors[Sides[Lines[i].sidenum[0]].sector];
|
||||
controlsector->controlsector = true;
|
||||
|
||||
for (unsigned int j = 0; j < Sectors.Size(); j++)
|
||||
{
|
||||
for (unsigned t = 0; t < Sectors[j].tags.Size(); t++)
|
||||
{
|
||||
if (Sectors[j].tags[t] == sectorTag)
|
||||
{
|
||||
Sectors[j].x3dfloors.Push(controlsector);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// GG to whoever memset'ed FLevel
|
||||
defaultSunColor = vec3(1, 1, 1);
|
||||
defaultSunDirection = vec3(0.45f, 0.3f, 0.9f);
|
||||
|
|
414
src/level/level_slopes.cpp
Normal file
414
src/level/level_slopes.cpp
Normal file
|
@ -0,0 +1,414 @@
|
|||
/*
|
||||
** p_slopes.cpp
|
||||
** Slope creation
|
||||
**
|
||||
**---------------------------------------------------------------------------
|
||||
** Copyright 1998-2008 Randy Heit
|
||||
** 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 "level.h"
|
||||
|
||||
#include <map>
|
||||
|
||||
#define EQUAL_EPSILON (1/65536.)
|
||||
|
||||
// convert from fixed point(FRACUNIT) to floating point
|
||||
#define F(x) (((float)(x))/65536.0f)
|
||||
|
||||
inline int P_PointOnLineSidePrecise(const vec2& p, const vec2& v1, const vec2& v2)
|
||||
{
|
||||
return (p.y - v1.y) * (v2.x - v1.x) + (v1.x - p.x) * (v2.y - v1.y) > EQUAL_EPSILON;
|
||||
}
|
||||
|
||||
inline int P_PointOnLineSidePrecise(const vec2& p, const FloatVertex& v1, const FloatVertex& v2)
|
||||
{
|
||||
return P_PointOnLineSidePrecise(p, vec2(v1.x, v1.y), vec2(v2.x, v2.y));
|
||||
}
|
||||
|
||||
enum
|
||||
{
|
||||
// Thing numbers used to define slopes
|
||||
SMT_VavoomFloor = 1500,
|
||||
SMT_VavoomCeiling = 1501,
|
||||
SMT_VertexFloorZ = 1504,
|
||||
SMT_VertexCeilingZ = 1505,
|
||||
SMT_SlopeFloorPointLine = 9500,
|
||||
SMT_SlopeCeilingPointLine = 9501,
|
||||
SMT_SetFloorSlope = 9502,
|
||||
SMT_SetCeilingSlope = 9503,
|
||||
SMT_CopyFloorPlane = 9510,
|
||||
SMT_CopyCeilingPlane = 9511,
|
||||
};
|
||||
|
||||
void FProcessor::SetSlopesFromVertexHeights(IntThing* firstmt, IntThing* lastmt, const int* oldvertextable)
|
||||
{
|
||||
TMap<int, double> vt_heights[2];
|
||||
IntThing* mt;
|
||||
bool vt_found = false;
|
||||
|
||||
for (mt = firstmt; mt < lastmt; ++mt)
|
||||
{
|
||||
if (mt->type == SMT_VertexFloorZ || mt->type == SMT_VertexCeilingZ)
|
||||
{
|
||||
for (int i = 0; i < Level.NumVertices; i++)
|
||||
{
|
||||
auto vertex = Level.GetSegVertex(i);
|
||||
|
||||
if(F(mt->x) == vertex.x && F(mt->y) == vertex.y)
|
||||
{
|
||||
if (mt->type == SMT_VertexFloorZ)
|
||||
{
|
||||
vt_heights[0][i] = F(mt->z);
|
||||
}
|
||||
else
|
||||
{
|
||||
vt_heights[1][i] = F(mt->z);
|
||||
}
|
||||
vt_found = true;
|
||||
}
|
||||
}
|
||||
mt->type = 0;
|
||||
}
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < Level.VertexProps.Size(); i++)
|
||||
{
|
||||
auto& vertex = Level.VertexProps[i];
|
||||
|
||||
int ii = oldvertextable == NULL ? i : oldvertextable[i];
|
||||
|
||||
if (vertex.HasZCeiling())
|
||||
{
|
||||
vt_heights[1][ii] = vertex.zceiling;
|
||||
vt_found = true;
|
||||
}
|
||||
|
||||
if (vertex.HasZFloor())
|
||||
{
|
||||
vt_heights[0][ii] = vertex.zfloor;
|
||||
vt_found = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (vt_found)
|
||||
{
|
||||
for (auto& sec : Level.Sectors)
|
||||
{
|
||||
if (sec.lines.Size() != 3) continue; // only works with triangular sectors
|
||||
|
||||
dvec3 vt1, vt2, vt3;
|
||||
dvec3 vec1, vec2;
|
||||
int vi1, vi2, vi3;
|
||||
|
||||
vi1 = sec.lines[0]->v1;
|
||||
vi2 = sec.lines[0]->v2;
|
||||
vi3 = (sec.lines[1]->v1 == sec.lines[0]->v1 || sec.lines[1]->v1 == sec.lines[0]->v2) ?
|
||||
sec.lines[1]->v2 : sec.lines[1]->v1;
|
||||
|
||||
const auto sv1 = Level.GetSegVertex(vi1);
|
||||
const auto sv2 = Level.GetSegVertex(vi2);
|
||||
const auto sv3 = Level.GetSegVertex(vi3);
|
||||
|
||||
vt1 = dvec3(sv1.x, sv1.y, 0);
|
||||
vt2 = dvec3(sv2.x, sv2.y, 0);
|
||||
vt3 = dvec3(sv3.x, sv3.y, 0);
|
||||
|
||||
for (int j = 0; j < 2; j++)
|
||||
{
|
||||
double* h1 = vt_heights[j].CheckKey(vi1);
|
||||
double* h2 = vt_heights[j].CheckKey(vi2);
|
||||
double* h3 = vt_heights[j].CheckKey(vi3);
|
||||
|
||||
if (h1 == NULL && h2 == NULL && h3 == NULL) continue;
|
||||
|
||||
vt1.z = h1 ? *h1 : j == 0 ? sec.data.floorheight : sec.data.ceilingheight;
|
||||
vt2.z = h2 ? *h2 : j == 0 ? sec.data.floorheight : sec.data.ceilingheight;
|
||||
vt3.z = h3 ? *h3 : j == 0 ? sec.data.floorheight : sec.data.ceilingheight;
|
||||
|
||||
if (P_PointOnLineSidePrecise(::vec2(vt3.xy()), Level.GetSegVertex(sec.lines[0]->v1), Level.GetSegVertex(sec.lines[0]->v2)) == 0)
|
||||
{
|
||||
vec1 = vt2 - vt3;
|
||||
vec2 = vt1 - vt3;
|
||||
}
|
||||
else
|
||||
{
|
||||
vec1 = vt1 - vt3;
|
||||
vec2 = vt2 - vt3;
|
||||
}
|
||||
|
||||
dvec3 cross = ::cross(vec1, vec2);
|
||||
|
||||
double len = length(cross);
|
||||
if (len == 0)
|
||||
{
|
||||
// Only happens when all vertices in this sector are on the same line.
|
||||
// Let's just ignore this case.
|
||||
continue;
|
||||
}
|
||||
cross /= len;
|
||||
|
||||
// Fix backward normals
|
||||
if ((cross.z < 0 && j == 0) || (cross.z > 0 && j == 1))
|
||||
{
|
||||
cross = -cross;
|
||||
}
|
||||
|
||||
Plane* plane = j == 0 ? &sec.floorplane : &sec.ceilingplane;
|
||||
|
||||
double dist = -cross[0] * vt3.x - cross[1] * vt3.y - cross[2] * vt3.z;
|
||||
plane->Set(float(cross[0]), float(cross[1]), float(cross[2]), float(-dist));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FProcessor::SpawnSlopeMakers(IntThing* firstmt, IntThing* lastmt, const int* oldvertextable)
|
||||
{
|
||||
IntThing* mt;
|
||||
|
||||
for (mt = firstmt; mt < lastmt; ++mt)
|
||||
{
|
||||
if (mt->type >= SMT_SlopeFloorPointLine && mt->type <= SMT_VavoomCeiling)
|
||||
{
|
||||
dvec3 pos = dvec3(F(mt->x), F(mt->y), F(mt->z));
|
||||
Plane* refplane;
|
||||
IntSector* sec;
|
||||
bool ceiling;
|
||||
|
||||
sec = Level.PointInSector(pos.xy());
|
||||
if (mt->type == SMT_SlopeCeilingPointLine || mt->type == SMT_VavoomCeiling || mt->type == SMT_SetCeilingSlope)
|
||||
{
|
||||
refplane = &sec->ceilingplane;
|
||||
ceiling = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
refplane = &sec->floorplane;
|
||||
ceiling = false;
|
||||
}
|
||||
|
||||
pos.z = double(refplane->zAt(float(pos.x), float(pos.y))) + pos.z;
|
||||
|
||||
/*if (mt->type <= SMT_SlopeCeilingPointLine)
|
||||
{ // SlopeFloorPointLine and SlopCeilingPointLine
|
||||
SlopeLineToPoint(mt->args[0], pos, ceiling);
|
||||
}
|
||||
else if (mt->type <= SMT_SetCeilingSlope)
|
||||
{ // SetFloorSlope and SetCeilingSlope
|
||||
SetSlope(refplane, ceiling, mt->angle, mt->args[0], pos);
|
||||
}
|
||||
else
|
||||
{ // VavoomFloor and VavoomCeiling (these do not perform any sector height adjustment - z is absolute)
|
||||
VavoomSlope(sec, mt->thingid, mt->pos, ceiling);
|
||||
}*/
|
||||
mt->type = 0;
|
||||
}
|
||||
}
|
||||
SetSlopesFromVertexHeights(firstmt, lastmt, oldvertextable);
|
||||
|
||||
for (mt = firstmt; mt < lastmt; ++mt)
|
||||
{
|
||||
if (mt->type == SMT_CopyFloorPlane || mt->type == SMT_CopyCeilingPlane)
|
||||
{
|
||||
CopyPlane(mt->args[0], dvec2(F(mt->x), F(mt->y)), mt->type == SMT_CopyCeilingPlane);
|
||||
mt->type = 0;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void FProcessor::AlignPlane(IntSector* sec, IntLineDef* line, int which)
|
||||
{
|
||||
IntSector* refsec;
|
||||
double bestdist;
|
||||
FloatVertex refvert = Level.GetSegVertex(sec->lines[0]->v1);
|
||||
|
||||
if (line->backsector == NULL)
|
||||
return;
|
||||
|
||||
auto lv1 = Level.GetSegVertex(line->v1);
|
||||
auto lv2 = Level.GetSegVertex(line->v2);
|
||||
|
||||
// Find furthest vertex from the reference line. It, along with the two ends
|
||||
// of the line, will define the plane.
|
||||
bestdist = 0;
|
||||
for (auto ln : sec->lines)
|
||||
{
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
double dist;
|
||||
FloatVertex vert;
|
||||
|
||||
vert = Level.GetSegVertex(i == 0 ? ln->v1 : ln->v2);
|
||||
|
||||
dist = fabs((lv1.y - vert.y) * (lv2.x - lv1.x) -
|
||||
(lv1.x - vert.x) * (lv2.y - lv1.y));
|
||||
|
||||
if (dist > bestdist)
|
||||
{
|
||||
bestdist = dist;
|
||||
refvert = vert;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
refsec = line->frontsector == sec ? line->backsector : line->frontsector;
|
||||
|
||||
dvec3 p, v1, v2, cross;
|
||||
|
||||
Plane* srcplane;
|
||||
double srcheight, destheight;
|
||||
|
||||
srcplane = (which == 0) ? &sec->floorplane : &sec->ceilingplane;
|
||||
srcheight = (which == 0) ? sec->data.floorheight : sec->data.ceilingheight;
|
||||
destheight = (which == 0) ? refsec->data.floorheight : refsec->data.ceilingheight;
|
||||
|
||||
p[0] = lv1.x;
|
||||
p[1] = lv1.y;
|
||||
p[2] = destheight;
|
||||
v1[0] = double(lv2.x) - double(lv1.x);
|
||||
v1[1] = double(lv2.y) - double(lv1.y);
|
||||
v1[2] = 0;
|
||||
v2[0] = double(refvert.x) - double(lv1.x);
|
||||
v2[1] = double(refvert.y) - double(lv1.y);
|
||||
v2[2] = srcheight - destheight;
|
||||
|
||||
cross = normalize(::cross(v1, v2));
|
||||
|
||||
// Fix backward normals
|
||||
if ((cross.z < 0 && which == 0) || (cross.z > 0 && which == 1))
|
||||
{
|
||||
cross = -cross;
|
||||
}
|
||||
|
||||
double dist = -cross[0] * lv1.x - cross[1] * lv1.y - cross[2] * destheight;
|
||||
srcplane->Set(float(cross[0]), float(cross[1]), float(cross[2]), float(-dist));
|
||||
}
|
||||
|
||||
void FProcessor::SetSlopes()
|
||||
{
|
||||
int s;
|
||||
|
||||
for (auto& line : Level.Lines)
|
||||
{
|
||||
if (line.special == Plane_Align)
|
||||
{
|
||||
line.special = 0;
|
||||
if (line.backsector != nullptr)
|
||||
{
|
||||
// args[0] is for floor, args[1] is for ceiling
|
||||
//
|
||||
// As a special case, if args[1] is 0,
|
||||
// then args[0], bits 2-3 are for ceiling.
|
||||
for (s = 0; s < 2; s++)
|
||||
{
|
||||
int bits = line.args[s] & 3;
|
||||
|
||||
if (s == 1 && bits == 0)
|
||||
bits = (line.args[0] >> 2) & 3;
|
||||
|
||||
if (bits == 1) // align front side to back
|
||||
AlignPlane(line.frontsector, &line, s);
|
||||
else if (bits == 2) // align back side to front
|
||||
AlignPlane(line.backsector, &line, s);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FProcessor::CopySlopes()
|
||||
{
|
||||
for (auto& line : Level.Lines)
|
||||
{
|
||||
if (line.special == Plane_Copy)
|
||||
{
|
||||
// The args are used for the tags of sectors to copy:
|
||||
// args[0]: front floor
|
||||
// args[1]: front ceiling
|
||||
// args[2]: back floor
|
||||
// args[3]: back ceiling
|
||||
// args[4]: copy slopes from one side of the line to the other.
|
||||
line.special = 0;
|
||||
for (int s = 0; s < (line.backsector ? 4 : 2); s++)
|
||||
{
|
||||
if (line.args[s])
|
||||
CopyPlane(line.args[s], (s & 2 ? line.backsector : line.frontsector), s & 1);
|
||||
}
|
||||
|
||||
if (line.backsector != NULL)
|
||||
{
|
||||
if ((line.args[4] & 3) == 1)
|
||||
{
|
||||
line.backsector->floorplane = line.frontsector->floorplane;
|
||||
}
|
||||
else if ((line.args[4] & 3) == 2)
|
||||
{
|
||||
line.frontsector->floorplane = line.backsector->floorplane;
|
||||
}
|
||||
if ((line.args[4] & 12) == 4)
|
||||
{
|
||||
line.backsector->ceilingplane = line.frontsector->ceilingplane;
|
||||
}
|
||||
else if ((line.args[4] & 12) == 8)
|
||||
{
|
||||
line.frontsector->ceilingplane = line.backsector->ceilingplane;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FProcessor::CopyPlane(int tag, IntSector* dest, bool copyCeil)
|
||||
{
|
||||
IntSector* source;
|
||||
int secnum;
|
||||
|
||||
secnum = Level.FindFirstSectorFromTag(tag);
|
||||
if (secnum == -1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
source = &Level.Sectors[secnum];
|
||||
|
||||
if (copyCeil)
|
||||
{
|
||||
dest->ceilingplane = source->ceilingplane;
|
||||
}
|
||||
else
|
||||
{
|
||||
dest->floorplane = source->floorplane;
|
||||
}
|
||||
}
|
||||
|
||||
void FProcessor::CopyPlane(int tag, const dvec2& pos, bool copyCeil)
|
||||
{
|
||||
CopyPlane(tag, Level.PointInSector(pos), copyCeil);
|
||||
}
|
|
@ -613,11 +613,11 @@ void FProcessor::ParseVertex(WideVertex *vt, IntVertex *vtp)
|
|||
}
|
||||
if (!stricmp(key, "zfloor"))
|
||||
{
|
||||
vtp->zfloor = CheckFixed(key);
|
||||
vtp->zfloor = CheckFloat(key);
|
||||
}
|
||||
else if (!stricmp(key, "zceiling"))
|
||||
{
|
||||
vtp->zceiling = CheckFixed(key);
|
||||
vtp->zceiling = CheckFloat(key);
|
||||
}
|
||||
|
||||
// now store the key in its unprocessed form
|
||||
|
|
|
@ -539,7 +539,7 @@ void LevelMesh::CreateSideSurfaces(FLevel &doomMap, IntSideDef *side)
|
|||
surf->verts[1].z = xfloor->floorplane.zAt(v1.x, v1.y);
|
||||
surf->verts[2].z = xfloor->ceilingplane.zAt(v2.x, v2.y);
|
||||
surf->verts[3].z = xfloor->ceilingplane.zAt(v1.x, v1.y);
|
||||
surf->plane.SetNormal(surf->verts[0], surf->verts[1], surf->verts[2]);
|
||||
surf->plane.SetNormal(surf->verts[0], surf->verts[1], surf->verts[2], surf->verts[3]);
|
||||
surf->plane.SetDistance(surf->verts[0]);
|
||||
|
||||
float texZ = surf->verts[0].z;
|
||||
|
@ -589,7 +589,7 @@ void LevelMesh::CreateSideSurfaces(FLevel &doomMap, IntSideDef *side)
|
|||
surf->verts[2].z = v1BottomBack;
|
||||
surf->verts[3].z = v2BottomBack;
|
||||
|
||||
surf->plane.SetNormal(surf->verts[0], surf->verts[1], surf->verts[2]);
|
||||
surf->plane.SetNormal(surf->verts[0], surf->verts[1], surf->verts[2], surf->verts[3]);
|
||||
surf->plane.SetDistance(surf->verts[0]);
|
||||
surf->type = ST_LOWERSIDE;
|
||||
surf->typeIndex = typeIndex;
|
||||
|
@ -647,7 +647,7 @@ void LevelMesh::CreateSideSurfaces(FLevel &doomMap, IntSideDef *side)
|
|||
surf->verts[2].z = v1Top;
|
||||
surf->verts[3].z = v2Top;
|
||||
|
||||
surf->plane.SetNormal(surf->verts[0], surf->verts[1], surf->verts[2]);
|
||||
surf->plane.SetNormal(surf->verts[0], surf->verts[1], surf->verts[2], surf->verts[3]);
|
||||
surf->plane.SetDistance(surf->verts[0]);
|
||||
surf->type = ST_UPPERSIDE;
|
||||
surf->typeIndex = typeIndex;
|
||||
|
@ -695,7 +695,7 @@ void LevelMesh::CreateSideSurfaces(FLevel &doomMap, IntSideDef *side)
|
|||
surf->verts[2].z = v1Top;
|
||||
surf->verts[3].z = v2Top;
|
||||
|
||||
surf->plane.SetNormal(surf->verts[0], surf->verts[1], surf->verts[2]);
|
||||
surf->plane.SetNormal(surf->verts[0], surf->verts[1], surf->verts[2], surf->verts[3]);
|
||||
surf->plane.SetDistance(surf->verts[0]);
|
||||
surf->type = ST_MIDDLESIDE;
|
||||
surf->typeIndex = typeIndex;
|
||||
|
|
|
@ -94,10 +94,12 @@ public:
|
|||
AXIS_XY
|
||||
};
|
||||
|
||||
void Set(float a, float b, float c, float d);
|
||||
const vec3 &Normal() const;
|
||||
vec3 &Normal();
|
||||
Plane &SetNormal(const vec3 &normal);
|
||||
Plane &SetNormal(const vec3 &pt1, const vec3 &pt2, const vec3 &pt3);
|
||||
Plane& SetNormal(const vec3& pt1, const vec3& pt2, const vec3& pt3);
|
||||
Plane& SetNormal(const vec3& v1, const vec3& v2, const vec3& v3, const vec3& v4);
|
||||
float Distance(const vec3 &point);
|
||||
Plane &SetDistance(const vec3 &point);
|
||||
bool IsFacing(const float yaw);
|
||||
|
|
|
@ -66,6 +66,14 @@ Plane::Plane(const Plane &plane)
|
|||
this->d = plane.d;
|
||||
}
|
||||
|
||||
void Plane::Set(float a, float b, float c, float d)
|
||||
{
|
||||
this->a = a;
|
||||
this->b = b;
|
||||
this->c = c;
|
||||
this->d = d;
|
||||
}
|
||||
|
||||
Plane &Plane::SetNormal(const vec3 &normal)
|
||||
{
|
||||
Normal() = normal;
|
||||
|
@ -78,6 +86,37 @@ Plane &Plane::SetNormal(const vec3 &pt1, const vec3 &pt2, const vec3 &pt3)
|
|||
return *this;
|
||||
}
|
||||
|
||||
Plane& Plane::SetNormal(const vec3& v1, const vec3& v2, const vec3& v3, const vec3& v4)
|
||||
{
|
||||
// Handle cases where two vertices share the same position
|
||||
|
||||
// TODO fix this hack
|
||||
// Certain vertex positions are close enough to cause NaN, but aren't equal
|
||||
auto iv1 = ivec3(v1);
|
||||
auto iv2 = ivec3(v2);
|
||||
auto iv3 = ivec3(v3);
|
||||
auto iv4 = ivec3(v4);
|
||||
|
||||
if (iv1 == iv3)
|
||||
{
|
||||
return SetNormal(v1, v2, v4);
|
||||
}
|
||||
else if (iv2 == iv4)
|
||||
{
|
||||
return SetNormal(v1, v2, v3);
|
||||
}
|
||||
else if (iv1 == iv2)
|
||||
{
|
||||
return SetNormal(v2, v3, v4);
|
||||
}
|
||||
else if (iv2 == iv3)
|
||||
{
|
||||
return SetNormal(v1, v2, v4);
|
||||
}
|
||||
|
||||
return SetNormal(v1, v2, v3);
|
||||
}
|
||||
|
||||
vec3 const &Plane::Normal() const
|
||||
{
|
||||
return *reinterpret_cast<const vec3*>(&a);
|
||||
|
|
Loading…
Reference in a new issue