Merge pull request #1399 from aws-lumberyard-dev/CherryPickNetworkingFixes

Cherry pick networking fixes
main
Gene Walters 5 years ago committed by GitHub
commit 4be9ce56c4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -1,878 +0,0 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates, or
* a third party where indicated.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
/*
* Temporary dynamic tree structure used internally by GridMate.
* To be replaced with a general Vis framework when that becomes available.
*/
#ifndef RR_DYNAMIC_BOUNDING_VOLUME_TREE_H
#define RR_DYNAMIC_BOUNDING_VOLUME_TREE_H
#include <AzCore/Math/Vector3.h>
#include <AzCore/Math/Aabb.h>
#include <AzCore/Math/Plane.h>
#include <GridMate/Containers/vector.h>
#include <AzCore/std/containers/fixed_vector.h>
namespace GridMate
{
namespace Internal
{
/**
*
*/
class DynamicTreeAabb : public AZ::Aabb
{
public:
GM_CLASS_ALLOCATOR(DynamicTreeAabb);
AZ_FORCE_INLINE explicit DynamicTreeAabb() {}
AZ_FORCE_INLINE DynamicTreeAabb(const AZ::Aabb& aabb) : AZ::Aabb(aabb) {}
AZ_FORCE_INLINE explicit DynamicTreeAabb(const AZ::Vector3& min,const AZ::Vector3& max) : AZ::Aabb(AZ::Aabb::CreateFromMinMax(min,max)) {}
AZ_FORCE_INLINE static DynamicTreeAabb CreateFromFacePoints(const AZ::Vector3& a, const AZ::Vector3& b, const AZ::Vector3& c)
{
DynamicTreeAabb vol(a,a);
vol.AddPoint(b);
vol.AddPoint(c);
return vol;
}
AZ_FORCE_INLINE void SignedExpand(const AZ::Vector3& e)
{
AZ::Vector3 zero = AZ::Vector3::CreateZero();
AZ::Vector3 mxE = m_max + e;
AZ::Vector3 miE = m_min + e;
m_max = AZ::Vector3::CreateSelectCmpGreater(e,zero,mxE,m_max );
m_min = AZ::Vector3::CreateSelectCmpGreater(e,zero,m_min,miE);
}
AZ_FORCE_INLINE int Classify(const AZ::Vector3& n,const float o,int s) const
{
AZ::Vector3 pi, px;
switch(s)
{
case (0+0+0): px=m_min;
pi=m_max; break;
case (1+0+0): px=AZ::Vector3(m_max.GetX(),m_min.GetY(),m_min.GetZ());
pi=AZ::Vector3(m_min.GetX(),m_max.GetY(),m_max.GetZ());break;
case (0+2+0): px=AZ::Vector3(m_min.GetX(),m_max.GetY(),m_min.GetZ());
pi=AZ::Vector3(m_max.GetX(),m_min.GetY(),m_max.GetZ());break;
case (1+2+0): px=AZ::Vector3(m_max.GetX(),m_max.GetY(),m_min.GetZ());
pi=AZ::Vector3(m_min.GetX(),m_min.GetY(),m_max.GetZ());break;
case (0+0+4): px=AZ::Vector3(m_min.GetX(),m_min.GetY(),m_max.GetZ());
pi=AZ::Vector3(m_max.GetX(),m_max.GetY(),m_min.GetZ());break;
case (1+0+4): px=AZ::Vector3(m_max.GetX(),m_min.GetY(),m_max.GetZ());
pi=AZ::Vector3(m_min.GetX(),m_max.GetY(),m_min.GetZ());break;
case (0+2+4): px=AZ::Vector3(m_min.GetX(),m_max.GetY(),m_max.GetZ());
pi=AZ::Vector3(m_max.GetX(),m_min.GetY(),m_min.GetZ());break;
case (1+2+4): px=m_max;
pi=m_min;break;
}
if (n.Dot(px) + o < 0.0f)
{
return -1;
}
if (n.Dot(pi) + o > 0.0f)
{
return 1;
}
return 0;
}
AZ_FORCE_INLINE float ProjectMinimum(const AZ::Vector3& v, unsigned signs) const
{
const AZ::Vector3* b[]={&m_max,&m_min};
const AZ::Vector3 p( b[(signs>>0)&1]->GetX(),b[(signs>>1)&1]->GetY(),b[(signs>>2)&1]->GetZ());
return p.Dot(v);
}
// Move the code here
AZ_FORCE_INLINE friend bool IntersectAabbAabb(const DynamicTreeAabb& a,const DynamicTreeAabb& b);
AZ_FORCE_INLINE friend bool IntersectAabbPoint(const DynamicTreeAabb& a, const AZ::Vector3& b);
AZ_FORCE_INLINE friend bool IntersectAabbPlane(const DynamicTreeAabb& a, const AZ::Plane& b);
AZ_FORCE_INLINE friend float Proximity(const DynamicTreeAabb& a, const DynamicTreeAabb& b);
AZ_FORCE_INLINE friend int Select(const DynamicTreeAabb& o, const DynamicTreeAabb& a, const DynamicTreeAabb& b);
AZ_FORCE_INLINE friend void Merge(const DynamicTreeAabb& a, const DynamicTreeAabb& b, DynamicTreeAabb& r);
AZ_FORCE_INLINE friend bool NotEqual(const DynamicTreeAabb& a, const DynamicTreeAabb& b);
private:
AZ_FORCE_INLINE void AddSpan(const AZ::Vector3& d, float& smi, float& smx) const
{
AZ::Vector3 vecZero = AZ::Vector3::CreateZero();
AZ::Vector3 mxD = m_max*d;
AZ::Vector3 miD = m_min*d;
AZ::Vector3 smiAdd = AZ::Vector3::CreateSelectCmpGreater(vecZero,d,mxD,miD);
AZ::Vector3 smxAdd = AZ::Vector3::CreateSelectCmpGreater(vecZero,d,miD,mxD);
AZ::Vector3 vecOne = AZ::Vector3::CreateOne();
// sum components
smi += smiAdd.Dot(vecOne);
smx += smxAdd.Dot(vecOne);
}
};
//
AZ_FORCE_INLINE bool IntersectAabbAabb(const DynamicTreeAabb& a, const DynamicTreeAabb& b)
{
return a.Overlaps(b);
}
AZ_FORCE_INLINE bool IntersectAabbPlane(const DynamicTreeAabb& a, const AZ::Plane& b)
{
//use plane normal to quickly select the nearest corner of the aabb
AZ::Vector3 testPoint = AZ::Vector3::CreateSelectCmpGreater(b.GetNormal(), AZ::Vector3::CreateZero(), a.GetMin(), a.GetMax());
//test if nearest point is inside the plane
return b.GetPointDist(testPoint) <= 0.0f;
}
//
AZ_FORCE_INLINE float Proximity(const DynamicTreeAabb& a, const DynamicTreeAabb& b)
{
const AZ::Vector3 d=(a.m_min+a.m_max)-(b.m_min+b.m_max);
// get abs and sum
return d.GetAbs().Dot(AZ::Vector3::CreateOne());
}
//
AZ_FORCE_INLINE int Select( const DynamicTreeAabb& o, const DynamicTreeAabb& a, const DynamicTreeAabb& b)
{
return Proximity(o,a) < Proximity(o,b);
}
//
AZ_FORCE_INLINE void Merge(const DynamicTreeAabb& a, const DynamicTreeAabb& b, DynamicTreeAabb& r)
{
r.m_min = AZ::Vector3::CreateSelectCmpGreater(b.m_min,a.m_min,a.m_min,b.m_min);
r.m_max = AZ::Vector3::CreateSelectCmpGreater(a.m_max,b.m_max,a.m_max,b.m_max);
}
//
AZ_FORCE_INLINE bool NotEqual( const DynamicTreeAabb& a, const DynamicTreeAabb& b)
{
return (a.m_min != b.m_min || a.m_max != b.m_max);
}
/* NodeType */
struct DynamicTreeNode
{
GM_CLASS_ALLOCATOR(DynamicTreeNode);
DynamicTreeAabb m_volume;
DynamicTreeNode* m_parent;
AZ_FORCE_INLINE bool IsLeaf() const { return(m_childs[1]==0); }
AZ_FORCE_INLINE bool IsInternal() const { return(!IsLeaf()); }
union
{
DynamicTreeNode* m_childs[2];
void* m_data;
int m_dataAsInt;
};
};
}
/**
* Implementation of dynamic aabb tree, based on the bullet dynamic tree (btDbvt).
*
* The BvDynamicTree class implements a fast dynamic bounding volume tree based on axis aligned bounding boxes (aabb tree).
* This BvDynamicTree is used for soft body collision detection and for the btDbvtBroadphase. It has a fast insert, remove and update of nodes.
* Unlike the BvTreeQuantized, nodes can be dynamically moved around, which allows for change in topology of the underlying data structure.
*/
class BvDynamicTree
{
public:
using Ptr = AZStd::intrusive_ptr<BvDynamicTree>;
GM_CLASS_ALLOCATOR(BvDynamicTree);
typedef Internal::DynamicTreeAabb VolumeType;
typedef Internal::DynamicTreeNode NodeType;
typedef vector<NodeType*> NodeArrayType;
typedef vector<const NodeType*> ConstNodeArrayType;
private:
/* Stack element */
struct sStkNN
{
const NodeType* a;
const NodeType* b;
sStkNN() {}
sStkNN(const NodeType* na,const NodeType* nb) : a(na), b(nb) {}
};
struct sStkNP
{
const NodeType* node;
int mask;
sStkNP(const NodeType* n, unsigned m) : node(n), mask(m) {}
};
struct sStkNPS
{
const NodeType* node;
int mask;
float value;
sStkNPS() {}
sStkNPS(const NodeType* n, unsigned m, const float v) : node(n), mask(m), value(v) {}
};
struct sStkCLN
{
const NodeType* node;
NodeType* parent;
sStkCLN(const NodeType* n, NodeType* p) : node(n), parent(p) {}
};
public:
/* ICollideCollector templated collectors should implement this functions or inherit from this class */
struct ICollideCollector
{
void Process(const NodeType*, const NodeType*) {}
void Process(const NodeType*) {}
void Process(const NodeType* n, const float) { Process(n); }
bool Descent(const NodeType*) { return true; }
bool AllLeaves(const NodeType*) { return true; }
};
/* IWriter */
struct IWriter
{
virtual ~IWriter() {}
virtual void Prepare(const NodeType* root,int numnodes) = 0;
virtual void WriteNode(const NodeType*, int index, int parent, int child0, int child1) = 0;
virtual void WriteLeaf(const NodeType*, int index, int parent) = 0;
};
/* IClone */
struct IClone
{
virtual ~IClone() {}
virtual void CloneLeaf(NodeType*) {}
};
// Constants
enum
{
SIMPLE_STACKSIZE = 64,
DOUBLE_STACKSIZE = SIMPLE_STACKSIZE * 2
};
// Methods
BvDynamicTree();
~BvDynamicTree();
NodeType* GetRoot() const { return m_root; }
void Clear();
bool Empty() const { return 0 == m_root; }
int GetNumLeaves() const { return m_leaves; }
void OptimizeBottomUp();
void OptimizeTopDown(int bu_treshold = 128);
void OptimizeIncremental(int passes);
NodeType* Insert(const VolumeType& box,void* data);
void Update(NodeType* leaf, int lookahead=-1);
void Update(NodeType* leaf, VolumeType& volume);
bool Update(NodeType* leaf, VolumeType& volume, const AZ::Vector3& velocity, const float margin);
bool Update(NodeType* leaf, VolumeType& volume, const AZ::Vector3& velocity);
bool Update(NodeType* leaf, VolumeType& volume, const float margin);
void Remove(NodeType* leaf);
void Write(IWriter* iwriter) const;
void Clone(BvDynamicTree& dest, IClone* iclone=0) const;
static int GetMaxDepth(const NodeType* node);
static int CountLeaves(const NodeType* node);
static void ExtractLeaves(const NodeType* node, /*btAlignedObjectArray<const NodeType*>&*/vector<const NodeType*>& leaves);
#if DBVT_ENABLE_BENCHMARK
static void Benchmark();
#else
static void Benchmark(){}
#endif
/**
* Collector should inherit from ICollide
*/
template<class Collector>
static inline void enumNodes( const NodeType* root, Collector& collector)
{
collector.Process(root);
if(root->IsInternal())
{
enumNodes(root->m_childs[0],collector);
enumNodes(root->m_childs[1],collector);
}
}
template<class Collector>
static void enumLeaves( const NodeType* root,Collector& collector)
{
if(root->IsInternal())
{
enumLeaves(root->m_childs[0],collector);
enumLeaves(root->m_childs[1],collector);
}
else
{
collector.Process(root);
}
}
template<class Collector>
void collideTT( const NodeType* root0,const NodeType* root1,Collector& collector) const
{
if(root0&&root1)
{
size_t depth=1;
size_t treshold=DOUBLE_STACKSIZE-4;
vector<sStkNN> stkStack;
stkStack.resize(DOUBLE_STACKSIZE);
stkStack[0]=sStkNN(root0,root1);
do {
sStkNN p=stkStack[--depth];
if(depth>treshold)
{
stkStack.resize(stkStack.size()*2);
treshold=stkStack.size()-4;
}
if(p.a==p.b)
{
if(p.a->IsInternal())
{
stkStack[depth++]=sStkNN(p.a->m_childs[0],p.a->m_childs[0]);
stkStack[depth++]=sStkNN(p.a->m_childs[1],p.a->m_childs[1]);
stkStack[depth++]=sStkNN(p.a->m_childs[0],p.a->m_childs[1]);
}
}
else if(IntersectAabbAabb(p.a->m_volume,p.b->m_volume))
{
if(p.a->IsInternal())
{
if(p.b->IsInternal())
{
stkStack[depth++]=sStkNN(p.a->m_childs[0],p.b->m_childs[0]);
stkStack[depth++]=sStkNN(p.a->m_childs[1],p.b->m_childs[0]);
stkStack[depth++]=sStkNN(p.a->m_childs[0],p.b->m_childs[1]);
stkStack[depth++]=sStkNN(p.a->m_childs[1],p.b->m_childs[1]);
}
else
{
stkStack[depth++]=sStkNN(p.a->m_childs[0],p.b);
stkStack[depth++]=sStkNN(p.a->m_childs[1],p.b);
}
}
else
{
if(p.b->IsInternal())
{
stkStack[depth++]=sStkNN(p.a,p.b->m_childs[0]);
stkStack[depth++]=sStkNN(p.a,p.b->m_childs[1]);
}
else
{
collector.Process(p.a,p.b);
}
}
}
} while(depth);
}
}
template<class Collector>
void collideTTpersistentStack( const NodeType* root0, const NodeType* root1,Collector& collector)
{
if(root0&&root1)
{
size_t depth=1;
size_t treshold=DOUBLE_STACKSIZE-4;
m_stkStack.resize(DOUBLE_STACKSIZE);
m_stkStack[0]=sStkNN(root0,root1);
do
{
sStkNN p=m_stkStack[--depth];
if(depth>treshold)
{
m_stkStack.resize(m_stkStack.size()*2);
treshold=m_stkStack.size()-4;
}
if(p.a==p.b)
{
if(p.a->IsInternal())
{
m_stkStack[depth++]=sStkNN(p.a->m_childs[0],p.a->m_childs[0]);
m_stkStack[depth++]=sStkNN(p.a->m_childs[1],p.a->m_childs[1]);
m_stkStack[depth++]=sStkNN(p.a->m_childs[0],p.a->m_childs[1]);
}
}
else if(IntersectAabbAabb(p.a->m_volume,p.b->m_volume))
{
if(p.a->IsInternal())
{
if(p.b->IsInternal())
{
m_stkStack[depth++]=sStkNN(p.a->m_childs[0],p.b->m_childs[0]);
m_stkStack[depth++]=sStkNN(p.a->m_childs[1],p.b->m_childs[0]);
m_stkStack[depth++]=sStkNN(p.a->m_childs[0],p.b->m_childs[1]);
m_stkStack[depth++]=sStkNN(p.a->m_childs[1],p.b->m_childs[1]);
}
else
{
m_stkStack[depth++]=sStkNN(p.a->m_childs[0],p.b);
m_stkStack[depth++]=sStkNN(p.a->m_childs[1],p.b);
}
}
else
{
if(p.b->IsInternal())
{
m_stkStack[depth++]=sStkNN(p.a,p.b->m_childs[0]);
m_stkStack[depth++]=sStkNN(p.a,p.b->m_childs[1]);
}
else
{
collector.Process(p.a,p.b);
}
}
}
} while(depth);
}
}
template<class Collector>
void collideTV( const NodeType* root, const VolumeType& volume, Collector& collector) const
{
if(root)
{
// ATTRIBUTE_ALIGNED16(VolumeType) volume(vol);
// btAlignedObjectArray<const NodeType*> stack;
AZStd::fixed_vector<const NodeType*,SIMPLE_STACKSIZE> stack;
//stack.reserve(SIMPLE_STACKSIZE);
stack.push_back(root);
do {
const NodeType* n=stack[stack.size()-1];
stack.pop_back();
if(IntersectAabbAabb(n->m_volume,volume))
{
if(n->IsInternal())
{
stack.push_back(n->m_childs[0]);
stack.push_back(n->m_childs[1]);
}
else
{
collector.Process(n);
}
}
} while(!stack.empty());
}
}
template<class Collector>
void collideTP(const NodeType* root, const AZ::Plane& plane, Collector& collector) const
{
if (root)
{
AZStd::fixed_vector<const NodeType*,SIMPLE_STACKSIZE> stack;
stack.push_back(root);
do
{
const NodeType* n=stack[stack.size()-1];
stack.pop_back();
if (IntersectAabbPlane(n->m_volume, plane))
{
if(n->IsInternal())
{
stack.push_back(n->m_childs[0]);
stack.push_back(n->m_childs[1]);
}
else
{
collector.Process(n);
}
}
} while (!stack.empty());
}
}
///rayTest is a re-entrant ray test, and can be called in parallel as long as the btAlignedAlloc is thread-safe (uses locking etc)
///rayTest is slower than rayTestInternal, because it builds a local stack, using memory allocations, and it recomputes signs/rayDirectionInverses each time
template<class Collector>
static void rayTest( const NodeType* root, const AZ::Vector3& rayFrom, const AZ::Vector3& rayTo, Collector& collector)
{
if(root)
{
AZ::Vector3 ray = rayTo-rayFrom;
AZ::Vector3 rayDir = ray.GetNormalized();
///what about division by zero? --> just set rayDirection[i] to INF/1e30
AZ::Vector3 rayDirectionInverse = AZ::Vector3::CreateSelectCmpEqual(rayDir,AZ::Vector3::CreateZero(),AZ::Vector3(1e30),rayDir.GetReciprocal());
unsigned int signs[3];// = { rayDirectionInverse[0] < 0.0f, rayDirectionInverse[1] < 0.0f, rayDirectionInverse[2] < 0.0f };
signs[0] = rayDirectionInverse.GetX() < 0.0f;
signs[1] = rayDirectionInverse.GetY() < 0.0f;
signs[2] = rayDirectionInverse.GetZ() < 0.0f;
//float lambda_max = rayDir.Dot(ray);
AZ::Vector3 resultNormal;
//btAlignedObjectArray<const NodeType*> stack;
vector<const NodeType*> stack;
int depth=1;
int treshold=DOUBLE_STACKSIZE-2;
stack.resize(DOUBLE_STACKSIZE);
stack[0]=root;
AZ::Vector3 bounds[2];
do {
const NodeType* node=stack[--depth];
bounds[0] = node->m_volume.GetMin();
bounds[1] = node->m_volume.GetMax();
//float tmin = 1.0f;
//float lambda_min = 0.0f;
// todo..
unsigned int result1 = /*btRayAabb2(rayFrom,rayDirectionInverse,signs,bounds,tmin,lambda_min,lambda_max)*/0;
#ifdef COMPARE_BTRAY_AABB2
float param = 1.0f;
bool result2 = /*btRayAabb(rayFrom,rayTo,node->volume.GetMin(),node->volume.GetMax(),param,resultNormal)*/0;
AZ_Assert(result1 == result2, "");
#endif //TEST_BTRAY_AABB2
if(result1)
{
if(node->IsInternal())
{
if(depth>treshold)
{
stack.resize(stack.size()*2);
treshold=stack.size()-2;
}
stack[depth++]=node->m_childs[0];
stack[depth++]=node->m_childs[1];
}
else
{
collector.Process(node);
}
}
} while(depth);
}
}
///rayTestInternal is faster than rayTest, because it uses a persistent stack (to reduce dynamic memory allocations to a minimum) and it uses precomputed signs/rayInverseDirections
///rayTestInternal is used by btDbvtBroadphase to accelerate world ray casts
template<class Collector>
void rayTestInternal(const NodeType* root, const AZ::Vector3& rayFrom, const AZ::Vector3& rayTo, const AZ::Vector3& rayDirectionInverse, unsigned int signs[3], const float lambda_max, const AZ::Vector3& aabbMin, const AZ::Vector3& aabbMax, Collector& collector) const
{
(void)rayFrom;(void)rayTo;(void)rayDirectionInverse;(void)signs;(void)lambda_max;
if(root)
{
AZ::Vector3 resultNormal;
int depth=1;
int treshold=DOUBLE_STACKSIZE-2;
vector<const NodeType*> stack;
stack.resize(DOUBLE_STACKSIZE);
stack[0]=root;
AZ::Vector3 bounds[2];
do
{
const NodeType* node=stack[--depth];
bounds[0] = node->m_volume.GetMin()+aabbMin;
bounds[1] = node->m_volume.GetMax()+aabbMax;
//float tmin = 1.0f;
//float lambda_min = 0.0f;
unsigned int result1=false;
// todo...
result1 = /*btRayAabb2(rayFrom,rayDirectionInverse,signs,bounds,tmin,lambda_min,lambda_max)*/false;
if(result1)
{
if(node->IsInternal())
{
if(depth>treshold)
{
stack.resize(stack.size()*2);
treshold=stack.size()-2;
}
stack[depth++]=node->m_childs[0];
stack[depth++]=node->m_childs[1];
}
else
{
collector.Process(node);
}
}
} while(depth);
}
}
template<class Collector>
static void collideKDOP(const NodeType* root, const AZ::Vector3* normals, const float* offsets, int count, Collector& collector)
{
(void)root;(void)normals;(void)offsets;(void)count;(void)collector;
/* if(root)
{
const int inside=(1<<count)-1;
btAlignedObjectArray<sStkNP> stack;
int signs[sizeof(unsigned)*8];
btAssert(count<int (sizeof(signs)/sizeof(signs[0])));
for(int i=0;i<count;++i)
{
signs[i]= ((normals[i].x()>=0)?1:0)+
((normals[i].y()>=0)?2:0)+
((normals[i].z()>=0)?4:0);
}
stack.reserve(SIMPLE_STACKSIZE);
stack.push_back(sStkNP(root,0));
do {
sStkNP se=stack[stack.size()-1];
bool out=false;
stack.pop_back();
for(int i=0,j=1;(!out)&&(i<count);++i,j<<=1)
{
if(0==(se.mask&j))
{
const int side=se.node->volume.Classify(normals[i],offsets[i],signs[i]);
switch(side)
{
case -1: out=true;break;
case +1: se.mask|=j;break;
}
}
}
if(!out)
{
if((se.mask!=inside)&&(se.node->isinternal()))
{
stack.push_back(sStkNP(se.node->childs[0],se.mask));
stack.push_back(sStkNP(se.node->childs[1],se.mask));
}
else
{
if(policy.AllLeaves(se.node)) enumLeaves(se.node,policy);
}
}
} while(!stack.empty());
}*/
}
template<class Collector>
static void collideOCL( const NodeType* root, const AZ::Vector3* normals, const float* offsets, const AZ::Vector3& sortaxis, int count, Collector& collector, bool fullsort=true)
{
(void)root;(void)normals;(void)offsets;(void)sortaxis;(void)count;(void)offsets;(void)collector;(void)fullsort;
/* if(root)
{
const unsigned srtsgns=(sortaxis[0]>=0?1:0)+
(sortaxis[1]>=0?2:0)+
(sortaxis[2]>=0?4:0);
const int inside=(1<<count)-1;
btAlignedObjectArray<sStkNPS> stock;
btAlignedObjectArray<int> ifree;
btAlignedObjectArray<int> stack;
int signs[sizeof(unsigned)*8];
btAssert(count<int (sizeof(signs)/sizeof(signs[0])));
for(int i=0;i<count;++i)
{
signs[i]= ((normals[i].x()>=0)?1:0)+
((normals[i].y()>=0)?2:0)+
((normals[i].z()>=0)?4:0);
}
stock.reserve(SIMPLE_STACKSIZE);
stack.reserve(SIMPLE_STACKSIZE);
ifree.reserve(SIMPLE_STACKSIZE);
stack.push_back(allocate(ifree,stock,sStkNPS(root,0,root->volume.ProjectMinimum(sortaxis,srtsgns))));
do {
const int id=stack[stack.size()-1];
sStkNPS se=stock[id];
stack.pop_back();ifree.push_back(id);
if(se.mask!=inside)
{
bool out=false;
for(int i=0,j=1;(!out)&&(i<count);++i,j<<=1)
{
if(0==(se.mask&j))
{
const int side=se.node->volume.Classify(normals[i],offsets[i],signs[i]);
switch(side)
{
case -1: out=true;break;
case +1: se.mask|=j;break;
}
}
}
if(out) continue;
}
if(policy.Descent(se.node))
{
if(se.node->isinternal())
{
const NodeType* pns[]={ se.node->childs[0],se.node->childs[1]};
sStkNPS nes[]={ sStkNPS(pns[0],se.mask,pns[0]->volume.ProjectMinimum(sortaxis,srtsgns)),
sStkNPS(pns[1],se.mask,pns[1]->volume.ProjectMinimum(sortaxis,srtsgns))};
const int q=nes[0].value<nes[1].value?1:0;
int j=stack.size();
if(fsort&&(j>0))
{
// Insert 0
j=nearest(&stack[0],&stock[0],nes[q].value,0,stack.size());
stack.push_back(0);
#if DBVT_USE_MEMMOVE
memmove(&stack[j+1],&stack[j],sizeof(int)*(stack.size()-j-1));
#else
for(int k=stack.size()-1;k>j;--k) stack[k]=stack[k-1];
#endif
stack[j]=allocate(ifree,stock,nes[q]);
// Insert 1
j=nearest(&stack[0],&stock[0],nes[1-q].value,j,stack.size());
stack.push_back(0);
#if DBVT_USE_MEMMOVE
memmove(&stack[j+1],&stack[j],sizeof(int)*(stack.size()-j-1));
#else
for(int k=stack.size()-1;k>j;--k) stack[k]=stack[k-1];
#endif
stack[j]=allocate(ifree,stock,nes[1-q]);
}
else
{
stack.push_back(allocate(ifree,stock,nes[q]));
stack.push_back(allocate(ifree,stock,nes[1-q]));
}
}
else
{
policy.Process(se.node,se.value);
}
}
} while(stack.size());
}*/
}
template<class Collector>
static void collideTU(const NodeType* root, Collector& collector)
{
(void)root;(void)collector;
/* if(root)
{
btAlignedObjectArray<const NodeType*> stack;
stack.reserve(SIMPLE_STACKSIZE);
stack.push_back(root);
do {
const NodeType* n=stack[stack.size()-1];
stack.pop_back();
if(policy.Descent(n))
{
if(n->isinternal())
{ stack.push_back(n->childs[0]);stack.push_back(n->childs[1]); }
else
{ policy.Process(n); }
}
} while(stack.size()>0);
}*/
}
private:
BvDynamicTree(const BvDynamicTree&) {}
// Helpers
//static AZ_FORCE_INLINE int nearest(const int* i,const BvDynamicTree::sStkNPS* a,const float& v,int l,int h)
//{
// int m=0;
// while(l<h)
// {
// m=(l+h)>>1;
// if(a[i[m]].value>=v) l=m+1; else h=m;
// }
// return h;
//}
//static AZ_FORCE_INLINE int allocate( int_fixed_stack_type& ifree, stknps_fixed_stack_type& stock, const sStkNPS& value)
//{
// int i;
// if( !ifree.empty() )
// {
// i=ifree[ifree.size()-1];
// ifree.pop_back();
// stock[i]=value;
// }
// else
// {
// i=stock.size();
// stock.push_back(value);
// }
// return i;
//}
//
AZ_FORCE_INLINE void deletenode( NodeType* node)
{
//btAlignedFree(pdbvt->m_free);
delete m_free;
m_free=node;
}
void recursedeletenode( NodeType* node)
{
if(!node->IsLeaf())
{
recursedeletenode(node->m_childs[0]);
recursedeletenode(node->m_childs[1]);
}
if( node == m_root ) m_root=0;
deletenode(node);
}
AZ_FORCE_INLINE NodeType* createnode( NodeType* parent, void* data)
{
NodeType* node;
if(m_free)
{ node=m_free;m_free=0; }
else
{ node = aznew NodeType(); }
node->m_parent = parent;
node->m_data = data;
node->m_childs[1] = 0;
return node;
}
AZ_FORCE_INLINE NodeType* createnode( BvDynamicTree::NodeType* parent, const VolumeType& volume, void* data)
{
NodeType* node = createnode(parent,data);
node->m_volume=volume;
return node;
}
//
AZ_FORCE_INLINE NodeType* createnode( BvDynamicTree::NodeType* parent, const VolumeType& volume0, const VolumeType& volume1, void* data)
{
NodeType* node = createnode(parent,data);
Merge(volume0,volume1,node->m_volume);
return node;
}
void insertleaf( NodeType* root, NodeType* leaf);
NodeType* removeleaf( NodeType* leaf);
void fetchleaves(NodeType* root,NodeArrayType& leaves,int depth=-1);
void split(const NodeArrayType& leaves,NodeArrayType& left,NodeArrayType& right,const AZ::Vector3& org,const AZ::Vector3& axis);
VolumeType bounds(const NodeArrayType& leaves);
void bottomup( NodeArrayType& leaves );
NodeType* topdown(NodeArrayType& leaves,int bu_treshold);
AZ_FORCE_INLINE NodeType* sort(NodeType* n,NodeType*& r);
NodeType* m_root;
NodeType* m_free;
int m_lkhd;
int m_leaves;
unsigned m_opath;
//btAlignedObjectArray<sStkNN> m_stkStack;
// Profile and choose static or dynamic vector.
typedef AZStd::fixed_vector<sStkNN,DOUBLE_STACKSIZE> stknn_fixed_stack_type;
typedef AZStd::fixed_vector<int,SIMPLE_STACKSIZE> int_fixed_stack_type;
typedef AZStd::fixed_vector<sStkNPS,SIMPLE_STACKSIZE> stknps_fixed_stack_type;
stknn_fixed_stack_type m_stkStack;
};
}
#endif // RR_DYNAMIC_BOUNDING_VOLUME_TREE_H
#pragma once

@ -1,597 +0,0 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <GridMate/Replica/Interest/ProximityInterestHandler.h>
#include <GridMate/Replica/Replica.h>
#include <GridMate/Replica/ReplicaFunctions.h>
#include <GridMate/Replica/ReplicaMgr.h>
#include <GridMate/Replica/Interest/InterestManager.h>
#include <GridMate/Replica/Interest/BvDynamicTree.h>
// for highly verbose internal debugging
//#define INTERNAL_DEBUG_PROXIMITY
namespace GridMate
{
void ProximityInterestChunk::OnReplicaActivate(const ReplicaContext& rc)
{
m_interestHandler = static_cast<ProximityInterestHandler*>(rc.m_rm->GetUserContext(AZ_CRC("ProximityInterestHandler", 0x3a90b3e4)));
AZ_Warning("GridMate", m_interestHandler, "No proximity interest handler in the user context");
if (m_interestHandler)
{
m_interestHandler->OnNewRulesChunk(this, rc.m_peer);
}
}
void ProximityInterestChunk::OnReplicaDeactivate(const ReplicaContext& rc)
{
if (rc.m_peer && m_interestHandler)
{
m_interestHandler->OnDeleteRulesChunk(this, rc.m_peer);
}
}
bool ProximityInterestChunk::AddRuleFn(RuleNetworkId netId, AZ::Aabb bbox, const RpcContext& ctx)
{
if (IsProxy())
{
auto rulePtr = m_interestHandler->CreateRule(ctx.m_sourcePeer);
rulePtr->Set(bbox);
m_rules.insert(AZStd::make_pair(netId, rulePtr));
}
return true;
}
bool ProximityInterestChunk::RemoveRuleFn(RuleNetworkId netId, const RpcContext&)
{
if (IsProxy())
{
m_rules.erase(netId);
}
return true;
}
bool ProximityInterestChunk::UpdateRuleFn(RuleNetworkId netId, AZ::Aabb bbox, const RpcContext&)
{
if (IsProxy())
{
auto it = m_rules.find(netId);
if (it != m_rules.end())
{
it->second->Set(bbox);
}
}
return true;
}
bool ProximityInterestChunk::AddRuleForPeerFn(RuleNetworkId netId, PeerId peerId, AZ::Aabb bbox, const RpcContext&)
{
ProximityInterestChunk* peerChunk = m_interestHandler->FindRulesChunkByPeerId(peerId);
if (peerChunk)
{
auto it = peerChunk->m_rules.find(netId);
if (it == peerChunk->m_rules.end())
{
auto rulePtr = m_interestHandler->CreateRule(peerId);
peerChunk->m_rules.insert(AZStd::make_pair(netId, rulePtr));
rulePtr->Set(bbox);
}
}
return false;
}
///////////////////////////////////////////////////////////////////////////
/*
* ProximityInterest
*/
ProximityInterest::ProximityInterest(ProximityInterestHandler* handler)
: m_handler(handler)
, m_bbox(AZ::Aabb::CreateNull())
{
AZ_Assert(m_handler, "Invalid interest handler");
}
///////////////////////////////////////////////////////////////////////////
/*
* ProximityInterestRule
*/
void ProximityInterestRule::Set(const AZ::Aabb& bbox)
{
m_bbox = bbox;
m_handler->UpdateRule(this);
}
void ProximityInterestRule::Destroy()
{
m_handler->DestroyRule(this);
}
///////////////////////////////////////////////////////////////////////////
/*
* ProximityInterestAttribute
*/
void ProximityInterestAttribute::Set(const AZ::Aabb& bbox)
{
m_bbox = bbox;
m_handler->UpdateAttribute(this);
}
void ProximityInterestAttribute::Destroy()
{
m_handler->DestroyAttribute(this);
}
///////////////////////////////////////////////////////////////////////////
/*
* ProximityInterestHandler
*/
ProximityInterestHandler::ProximityInterestHandler()
: m_im(nullptr)
, m_rm(nullptr)
, m_lastRuleNetId(0)
, m_rulesReplica(nullptr)
{
m_attributeWorld = AZStd::make_unique<SpatialIndex>();
AZ_Assert(m_attributeWorld, "Out of memory");
}
ProximityInterestRule::Ptr ProximityInterestHandler::CreateRule(PeerId peerId)
{
ProximityInterestRule* rulePtr = aznew ProximityInterestRule(this, peerId, GetNewRuleNetId());
if (m_rm && peerId == m_rm->GetLocalPeerId())
{
m_rulesReplica->AddRuleRpc(rulePtr->GetNetworkId(), rulePtr->Get());
}
CreateAndInsertIntoSpatialStructure(rulePtr);
return rulePtr;
}
ProximityInterestAttribute::Ptr ProximityInterestHandler::CreateAttribute(ReplicaId replicaId)
{
auto newAttribute = aznew ProximityInterestAttribute(this, replicaId);
AZ_Assert(newAttribute, "Out of memory");
CreateAndInsertIntoSpatialStructure(newAttribute);
return newAttribute;
}
void ProximityInterestHandler::FreeRule(ProximityInterestRule* rule)
{
//TODO: should be pool-allocated
delete rule;
}
void ProximityInterestHandler::DestroyRule(ProximityInterestRule* rule)
{
if (m_rm && rule->GetPeerId() == m_rm->GetLocalPeerId())
{
m_rulesReplica->RemoveRuleRpc(rule->GetNetworkId());
}
MarkAttributesDirtyInRule(rule);
rule->m_bbox = AZ::Aabb::CreateNull();
m_removedRules.insert(rule);
m_localRules.erase(rule);
}
void ProximityInterestHandler::UpdateRule(ProximityInterestRule* rule)
{
if (m_rm && rule->GetPeerId() == m_rm->GetLocalPeerId())
{
m_rulesReplica->UpdateRuleRpc(rule->GetNetworkId(), rule->Get());
}
m_dirtyRules.insert(rule);
}
void ProximityInterestHandler::FreeAttribute(ProximityInterestAttribute* attrib)
{
delete attrib;
}
void ProximityInterestHandler::DestroyAttribute(ProximityInterestAttribute* attrib)
{
RemoveFromSpatialStructure(attrib);
m_attributes.erase(attrib);
m_removedAttributes.insert(attrib);
}
void ProximityInterestHandler::RemoveFromSpatialStructure(ProximityInterestAttribute* attribute)
{
attribute->m_bbox = AZ::Aabb::CreateNull();
m_attributeWorld->Remove(attribute->GetNode());
attribute->SetNode(nullptr);
}
void ProximityInterestHandler::UpdateAttribute(ProximityInterestAttribute* attrib)
{
auto node = attrib->GetNode();
AZ_Assert(node, "Attribute wasn't created correctly");
node->m_volume = attrib->Get();
m_attributeWorld->Update(node);
m_dirtyAttributes.insert(attrib);
}
void ProximityInterestHandler::OnNewRulesChunk(ProximityInterestChunk* chunk, ReplicaPeer* peer)
{
if (chunk != m_rulesReplica) // non-local
{
m_peerChunks.insert(AZStd::make_pair(peer->GetId(), chunk));
for (auto& rule : m_localRules)
{
chunk->AddRuleForPeerRpc(rule->GetNetworkId(), rule->GetPeerId(), rule->Get());
}
}
}
void ProximityInterestHandler::OnDeleteRulesChunk(ProximityInterestChunk* chunk, ReplicaPeer* peer)
{
(void)chunk;
m_peerChunks.erase(peer->GetId());
}
RuleNetworkId ProximityInterestHandler::GetNewRuleNetId()
{
++m_lastRuleNetId;
if (m_rulesReplica)
{
return m_rulesReplica->GetReplicaId() | (static_cast<AZ::u64>(m_lastRuleNetId) << 32);
}
return (static_cast<AZ::u64>(m_lastRuleNetId) << 32);
}
ProximityInterestChunk* ProximityInterestHandler::FindRulesChunkByPeerId(PeerId peerId)
{
auto it = m_peerChunks.find(peerId);
if (it == m_peerChunks.end())
{
return nullptr;
}
return it->second;
}
const InterestMatchResult& ProximityInterestHandler::GetLastResult()
{
return m_resultCache;
}
ProximityInterestHandler::RuleSet& ProximityInterestHandler::GetAffectedRules()
{
/*
* The expectation that lots of attributes will change frequently,
* so there is no point in trying to optimize cases
* where only a few attributes have changed.
*/
if (m_dirtyAttributes.empty() && !m_dirtyRules.empty())
{
return m_dirtyRules;
}
/*
* Assuming all rules might have been affected.
*
* There is an optimization chance here if the number of rules is large, as in 1,000+ rules.
* To handle such scale we would need another spatial structure for rules.
*/
return m_localRules;
}
void ProximityInterestHandler::GetAttributesWithinRule(ProximityInterestRule* rule, SpatialIndex::NodeCollector& nodes)
{
m_attributeWorld->Query(rule->Get(), nodes);
}
void ProximityInterestHandler::ClearDirtyState()
{
m_dirtyAttributes.clear();
m_dirtyRules.clear();
}
void ProximityInterestHandler::CreateAndInsertIntoSpatialStructure(ProximityInterestAttribute* attribute)
{
m_attributes.insert(attribute);
SpatialIndex::Node* node = m_attributeWorld->Insert(attribute->Get(), attribute);
attribute->SetNode(node);
}
void ProximityInterestHandler::CreateAndInsertIntoSpatialStructure(ProximityInterestRule* rule)
{
m_localRules.insert(rule);
}
void ProximityInterestHandler::UpdateInternal(InterestMatchResult& result)
{
/*
* The goal is to return all dirty attributes that were either dirty because:
* 1) they changed which rules have apply to
* 2) rules have changed and no longer apply to those attributes
* and thus resulted in different peer(s) associated with a given replica.
*/
const RuleSet& rules = GetAffectedRules();
for (auto& dirtyAttribute : m_dirtyAttributes)
{
result.insert(dirtyAttribute->GetReplicaId());
}
/*
* The exectation is to have a lot more attributes than rules.
* The amount of rules should grow linear with amount of peers,
* so it should be OK to iterate through all rules each update.
*/
for (auto& rule : rules)
{
CheckChangesForRule(rule, result);
}
for (auto& removedRule : m_removedRules)
{
FreeRule(removedRule);
}
m_removedRules.clear();
// mark removed attribute as having no peers
for (auto& removedAttribute : m_removedAttributes)
{
result.insert(removedAttribute->GetReplicaId());
FreeAttribute(removedAttribute);
}
m_removedAttributes.clear();
}
void ProximityInterestHandler::CheckChangesForRule(ProximityInterestRule* rule, InterestMatchResult& result)
{
SpatialIndex::NodeCollector collector;
GetAttributesWithinRule(rule, collector);
auto peerId = rule->GetPeerId();
for (ProximityInterestAttribute* attr : collector.GetNodes())
{
AZ_Assert(attr, "bad node?");
auto findIt = result.find(attr->GetReplicaId());
if (findIt != result.end())
{
findIt->second.insert(peerId);
}
else
{
auto resultIt = result.insert(attr->GetReplicaId());
AZ_Assert(resultIt.second, "Successfully inserted");
resultIt.first->second.insert(peerId);
}
}
}
void ProximityInterestHandler::MarkAttributesDirtyInRule(ProximityInterestRule* rule)
{
SpatialIndex::NodeCollector collector;
GetAttributesWithinRule(rule, collector);
for (ProximityInterestAttribute* attr : collector.GetNodes())
{
AZ_Assert(attr, "bad node?");
UpdateAttribute(attr);
}
}
void ProximityInterestHandler::ProduceChanges(const InterestMatchResult& before, const InterestMatchResult& after)
{
m_resultCache.clear();
#if defined(INTERNAL_DEBUG_PROXIMITY)
before.PrintMatchResult("before");
after.PrintMatchResult("after");
#endif
/*
* 'after' contains only the stuff that might have changed
*/
for (auto& possiblyDirty : after)
{
ReplicaId repId = possiblyDirty.first;
const InterestPeerSet& peerSet = possiblyDirty.second;
auto foundInBefore = before.find(repId);
if (foundInBefore != before.end())
{
if (!HasSamePeers(foundInBefore->second, peerSet))
{
// was in the last calculation but has a different peer set now
m_resultCache.insert(AZStd::make_pair(repId, peerSet));
}
}
else
{
// since it wasn't present during last calculation
m_resultCache.insert(AZStd::make_pair(repId, peerSet));
}
}
// Mark attributes (replicas) for removal that have not moved but a rule (clients) no longer sees it
for (auto& possiblyDirty : before)
{
ReplicaId repId = possiblyDirty.first;
const auto foundInAfter = after.find(repId);
/*
* If the prior state was a replica A present on peer X: "A{X}", and now A should no longer be present on any peer: "A{}"
* then by the rules of InterestHandlers interacting with InterestManager, we should return in @m_resultCache the following:
*
* A{} - indicating that replica A must be removed all peers.
*
* On the next pass, the prior state would be: "A{}" and the current state would be "A{}" as well. At that point, we have
* already sent the update to remove A from X, so @m_resultCache should no longer mention A at all.
*/
if (foundInAfter == after.end() && !possiblyDirty.second.empty() /* "not A{}" see the above comment */)
{
m_resultCache.insert(AZStd::make_pair(repId, InterestPeerSet()));
}
}
#if defined(INTERNAL_DEBUG_PROXIMITY)
m_resultCache.PrintMatchResult("changes");
#endif
}
bool ProximityInterestHandler::HasSamePeers(const InterestPeerSet& one, const InterestPeerSet& another)
{
if (one.size() != another.size())
{
return false;
}
for (auto& peerFromOne : one)
{
if (another.find(peerFromOne) == another.end())
{
return false;
}
}
// Safe to assume it's the same sets since all entries are unique in a peer sets
return true;
}
void ProximityInterestHandler::Update()
{
InterestMatchResult newResult;
UpdateInternal(newResult);
ProduceChanges(m_lastResult, newResult);
m_lastResult = std::move(newResult);
ClearDirtyState();
}
void ProximityInterestHandler::OnRulesHandlerRegistered(InterestManager* manager)
{
AZ_Assert(m_im == nullptr, "Handler is already registered with manager %p (%p)\n", m_im, manager);
AZ_Assert(m_rulesReplica == nullptr, "Rules replica is already created\n");
AZ_TracePrintf("GridMate", "Proximity interest handler is registered\n");
m_im = manager;
m_rm = m_im->GetReplicaManager();
m_rm->RegisterUserContext(AZ_CRC("ProximityInterestHandler", 0x3a90b3e4), this);
auto replica = Replica::CreateReplica("ProximityInterestHandlerRules");
m_rulesReplica = CreateAndAttachReplicaChunk<ProximityInterestChunk>(replica);
m_rm->AddPrimary(replica);
}
void ProximityInterestHandler::OnRulesHandlerUnregistered(InterestManager* manager)
{
(void)manager;
AZ_Assert(m_im == manager, "Handler was not registered with manager %p (%p)\n", manager, m_im);
AZ_TracePrintf("GridMate", "Proximity interest handler is unregistered\n");
m_rulesReplica = nullptr;
m_im = nullptr;
m_rm->UnregisterUserContext(AZ_CRC("ProximityInterestHandler", 0x3a90b3e4));
m_rm = nullptr;
for (auto& chunk : m_peerChunks)
{
chunk.second->m_interestHandler = nullptr;
}
m_peerChunks.clear();
ClearDirtyState();
DestroyAll();
m_resultCache.clear();
}
void ProximityInterestHandler::DestroyAll()
{
for (ProximityInterestRule* rule : m_localRules)
{
FreeRule(rule);
}
m_localRules.clear();
for (ProximityInterestAttribute* attr : m_attributes)
{
FreeAttribute(attr);
}
m_attributes.clear();
for (auto& removedRule : m_removedRules)
{
FreeRule(removedRule);
}
m_removedRules.clear();
for (auto& removedAttribute : m_removedAttributes)
{
FreeAttribute(removedAttribute);
}
m_removedAttributes.clear();
}
///////////////////////////////////////////////////////////////////////////
ProximityInterestHandler::~ProximityInterestHandler()
{
/*
* If a handler was registered with a InterestManager, then InterestManager ought to have called OnRulesHandlerUnregistered
* but this is a safety pre-caution.
*/
DestroyAll();
}
SpatialIndex::SpatialIndex()
{
m_tree.reset(aznew GridMate::BvDynamicTree());
}
void SpatialIndex::Remove(Node* node)
{
m_tree->Remove(node);
}
void SpatialIndex::Update(Node* node)
{
m_tree->Update(node);
}
SpatialIndex::Node* SpatialIndex::Insert(const AZ::Aabb& get, ProximityInterestAttribute* attribute)
{
return m_tree->Insert(get, attribute);
}
void SpatialIndex::Query(const AZ::Aabb& shape, NodeCollector& nodes)
{
m_tree->collideTV(m_tree->GetRoot(), shape, nodes);
}
}

@ -1,314 +0,0 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#ifndef GM_REPLICA_PROXIMITYINTERESTHANDLER_H
#define GM_REPLICA_PROXIMITYINTERESTHANDLER_H
#include <GridMate/Replica/RemoteProcedureCall.h>
#include <GridMate/Replica/ReplicaChunk.h>
#include <GridMate/Replica/Interest/RulesHandler.h>
#include <GridMate/Replica/Interest/BvDynamicTree.h>
#include <GridMate/Serialize/UtilityMarshal.h>
#include <AzCore/Math/Aabb.h>
#include <AzCore/std/smart_ptr/unique_ptr.h>
namespace GridMate
{
class ProximityInterestHandler;
class ProximityInterestAttribute;
/*
* Base interest
*/
class ProximityInterest
{
friend class ProximityInterestHandler;
public:
const AZ::Aabb& Get() const { return m_bbox; }
protected:
explicit ProximityInterest(ProximityInterestHandler* handler);
ProximityInterestHandler* m_handler;
AZ::Aabb m_bbox;
};
///////////////////////////////////////////////////////////////////////////
/*
* Proximity rule
*/
class ProximityInterestRule
: public InterestRule
, public ProximityInterest
{
friend class ProximityInterestHandler;
public:
using Ptr = AZStd::intrusive_ptr<ProximityInterestRule>;
GM_CLASS_ALLOCATOR(ProximityInterestRule);
void Set(const AZ::Aabb& bbox);
private:
// Intrusive ptr
template<class T>
friend struct AZStd::IntrusivePtrCountPolicy;
unsigned int m_refCount = 0;
AZ_FORCE_INLINE void add_ref() { ++m_refCount; }
AZ_FORCE_INLINE void release() { --m_refCount; if (!m_refCount) Destroy(); }
AZ_FORCE_INLINE bool IsDeleted() const { return m_refCount == 0; }
///////////////////////////////////////////////////////////////////////////
ProximityInterestRule(ProximityInterestHandler* handler, PeerId peerId, RuleNetworkId netId)
: InterestRule(peerId, netId)
, ProximityInterest(handler)
{}
void Destroy();
};
///////////////////////////////////////////////////////////////////////////
class SpatialIndex
{
public:
typedef Internal::DynamicTreeNode Node;
class NodeCollector
{
typedef AZStd::vector<ProximityInterestAttribute*> Type;
public:
void Process(const Internal::DynamicTreeNode* node)
{
m_nodes.push_back(reinterpret_cast<ProximityInterestAttribute*>(node->m_data));
}
const Type& GetNodes() const
{
return m_nodes;
}
private:
Type m_nodes;
};
SpatialIndex();
~SpatialIndex() = default;
AZ_FORCE_INLINE void Remove(Node* node);
AZ_FORCE_INLINE void Update(Node* node);
AZ_FORCE_INLINE Node* Insert(const AZ::Aabb& get, ProximityInterestAttribute* attribute);
AZ_FORCE_INLINE void Query(const AZ::Aabb& get, NodeCollector& nodes);
private:
AZStd::unique_ptr<BvDynamicTree> m_tree;
};
/*
* Proximity attribute
*/
class ProximityInterestAttribute
: public InterestAttribute
, public ProximityInterest
{
friend class ProximityInterestHandler;
template<class T> friend class InterestPtr;
public:
using Ptr = AZStd::intrusive_ptr<ProximityInterestAttribute>;
GM_CLASS_ALLOCATOR(ProximityInterestAttribute);
void Set(const AZ::Aabb& bbox);
private:
// Intrusive ptr
template<class T>
friend struct AZStd::IntrusivePtrCountPolicy;
unsigned int m_refCount = 0;
AZ_FORCE_INLINE void add_ref() { ++m_refCount; }
AZ_FORCE_INLINE void release() { Destroy(); }
AZ_FORCE_INLINE bool IsDeleted() const { return m_refCount == 0; }
///////////////////////////////////////////////////////////////////////////
ProximityInterestAttribute(ProximityInterestHandler* handler, ReplicaId repId)
: InterestAttribute(repId)
, ProximityInterest(handler)
, m_worldNode(nullptr)
{}
void Destroy();
void SetNode(SpatialIndex::Node* node) { m_worldNode = node; }
SpatialIndex::Node* GetNode() const { return m_worldNode; }
SpatialIndex::Node* m_worldNode; ///< non-owning pointer
};
///////////////////////////////////////////////////////////////////////////
class ProximityInterestChunk
: public ReplicaChunk
{
public:
GM_CLASS_ALLOCATOR(ProximityInterestChunk);
// ReplicaChunk
typedef AZStd::intrusive_ptr<ProximityInterestChunk> Ptr;
bool IsReplicaMigratable() override { return false; }
bool IsBroadcast() override { return true; }
static const char* GetChunkName() { return "ProximityInterestChunk"; }
ProximityInterestChunk()
: AddRuleRpc("AddRule")
, RemoveRuleRpc("RemoveRule")
, UpdateRuleRpc("UpdateRule")
, AddRuleForPeerRpc("AddRuleForPeerRpc")
, m_interestHandler(nullptr)
{
}
void OnReplicaActivate(const ReplicaContext& rc) override;
void OnReplicaDeactivate(const ReplicaContext& rc) override;
bool AddRuleFn(RuleNetworkId netId, AZ::Aabb bbox, const RpcContext& ctx);
bool RemoveRuleFn(RuleNetworkId netId, const RpcContext&);
bool UpdateRuleFn(RuleNetworkId netId, AZ::Aabb bbox, const RpcContext&);
bool AddRuleForPeerFn(RuleNetworkId netId, PeerId peerId, AZ::Aabb bbox, const RpcContext&);
Rpc<RpcArg<RuleNetworkId>, RpcArg<AZ::Aabb>>::BindInterface<ProximityInterestChunk, &ProximityInterestChunk::AddRuleFn> AddRuleRpc;
Rpc<RpcArg<RuleNetworkId>>::BindInterface<ProximityInterestChunk, &ProximityInterestChunk::RemoveRuleFn> RemoveRuleRpc;
Rpc<RpcArg<RuleNetworkId>, RpcArg<AZ::Aabb>>::BindInterface<ProximityInterestChunk, &ProximityInterestChunk::UpdateRuleFn> UpdateRuleRpc;
Rpc<RpcArg<RuleNetworkId>, RpcArg<PeerId>, RpcArg<AZ::Aabb>>::BindInterface<ProximityInterestChunk, &ProximityInterestChunk::AddRuleForPeerFn> AddRuleForPeerRpc;
unordered_map<RuleNetworkId, ProximityInterestRule::Ptr> m_rules;
ProximityInterestHandler* m_interestHandler;
};
/*
* Rules handler
*/
class ProximityInterestHandler
: public BaseRulesHandler
{
friend class ProximityInterestRule;
friend class ProximityInterestAttribute;
friend class ProximityInterestChunk;
public:
typedef unordered_set<ProximityInterestAttribute*> AttributeSet;
typedef unordered_set<ProximityInterestRule*> RuleSet;
GM_CLASS_ALLOCATOR(ProximityInterestHandler);
ProximityInterestHandler();
~ProximityInterestHandler();
/*
* Creates new proximity rule and binds it to the peer.
* Note: the lifetime of the created rule is tied to the lifetime of this handler.
*/
ProximityInterestRule::Ptr CreateRule(PeerId peerId);
/*
* Creates new proximity attribute and binds it to the replica.
* Note: the lifetime of the created attribute is tied to the lifetime of this handler.
*/
ProximityInterestAttribute::Ptr CreateAttribute(ReplicaId replicaId);
// Calculates rules and attributes matches
void Update() override;
// Returns last recalculated results
const InterestMatchResult& GetLastResult() override;
// Returns the manager it's bound to
InterestManager* GetManager() override { return m_im; }
// Rules that this handler is aware of
const RuleSet& GetLocalRules() const { return m_localRules; }
private:
// BaseRulesHandler
void OnRulesHandlerRegistered(InterestManager* manager) override;
void OnRulesHandlerUnregistered(InterestManager* manager) override;
void DestroyRule(ProximityInterestRule* rule);
void FreeRule(ProximityInterestRule* rule);
void UpdateRule(ProximityInterestRule* rule);
void DestroyAttribute(ProximityInterestAttribute* attrib);
void FreeAttribute(ProximityInterestAttribute* attrib);
void UpdateAttribute(ProximityInterestAttribute* attrib);
void OnNewRulesChunk(ProximityInterestChunk* chunk, ReplicaPeer* peer);
void OnDeleteRulesChunk(ProximityInterestChunk* chunk, ReplicaPeer* peer);
RuleNetworkId GetNewRuleNetId();
ProximityInterestChunk* FindRulesChunkByPeerId(PeerId peerId);
void DestroyAll();
InterestManager* m_im;
ReplicaManager* m_rm;
AZ::u32 m_lastRuleNetId;
unordered_map<PeerId, ProximityInterestChunk*> m_peerChunks;
RuleSet m_localRules;
RuleSet m_removedRules;
RuleSet m_dirtyRules;
AttributeSet m_attributes;
AttributeSet m_removedAttributes;
AttributeSet m_dirtyAttributes;
ProximityInterestChunk* m_rulesReplica;
// collection of all known attributes
AZStd::unique_ptr<SpatialIndex> m_attributeWorld;
InterestMatchResult m_resultCache;
///////////////////////////////////////////////////////////////////////////////////////////////////
// internal processing helpers
AZ_FORCE_INLINE RuleSet& GetAffectedRules();
AZ_FORCE_INLINE void GetAttributesWithinRule(ProximityInterestRule* rule, SpatialIndex::NodeCollector& nodes);
AZ_FORCE_INLINE void ClearDirtyState();
AZ_FORCE_INLINE void CreateAndInsertIntoSpatialStructure(ProximityInterestAttribute* attribute);
AZ_FORCE_INLINE void RemoveFromSpatialStructure(ProximityInterestAttribute* attribute);
AZ_FORCE_INLINE void CreateAndInsertIntoSpatialStructure(ProximityInterestRule* rule);
void UpdateInternal(InterestMatchResult& result);
void CheckChangesForRule(ProximityInterestRule* rule, InterestMatchResult& result);
void MarkAttributesDirtyInRule(ProximityInterestRule* rule);
static bool HasSamePeers(const InterestPeerSet& one, const InterestPeerSet& another);
void ProduceChanges(const InterestMatchResult& before, const InterestMatchResult& after);
InterestMatchResult m_lastResult;
///////////////////////////////////////////////////////////////////////////////////////////////////
};
///////////////////////////////////////////////////////////////////////////
}
#endif // GM_REPLICA_PROXIMITYINTERESTHANDLER_H

@ -100,14 +100,10 @@ set(FILES
Replica/Tasks/ReplicaPriorityPolicy.h
Replica/Interest/BitmaskInterestHandler.cpp
Replica/Interest/BitmaskInterestHandler.h
Replica/Interest/ProximityInterestHandler.cpp
Replica/Interest/ProximityInterestHandler.h
Replica/Interest/InterestDefs.h
Replica/Interest/InterestManager.cpp
Replica/Interest/InterestManager.h
Replica/Interest/InterestQueryResult.h
Replica/Interest/BvDynamicTree.cpp
Replica/Interest/BvDynamicTree.h
Replica/Interest/RulesHandler.h
Serialize/Buffer.cpp
Serialize/Buffer.h

File diff suppressed because it is too large Load Diff

@ -24,5 +24,4 @@ set(FILES
StreamSocketDriverTests.cpp
CarrierStreamSocketDriverTests.cpp
Carrier.cpp
Interest.cpp
)

@ -123,6 +123,8 @@ if (PAL_TRAIT_BUILD_HOST_TOOLS)
BUILD_DEPENDENCIES
PRIVATE
Gem::Multiplayer.Builders.Static
RUNTIME_DEPENDENCIES
Gem::Multiplayer.Editor
)
ly_add_target(

@ -130,6 +130,7 @@ namespace Multiplayer
// Instance container for net entities
AZStd::unique_ptr<Instance> networkInstance(aznew Instance());
networkInstance->SetTemplateSourcePath(AZ::IO::PathView(uniqueName));
// Create an asset for our future network spawnable: this allows us to put references to the asset in the components
AZ::Data::Asset<AzFramework::Spawnable> networkSpawnableAsset;

Loading…
Cancel
Save