[Add] Planet sphere geom in new collider. A little suspect..
This commit is contained in:
parent
20906f1f0a
commit
a6501f28e4
@ -11,6 +11,7 @@ struct CollisionContact {
|
|||||||
vector3d normal;
|
vector3d normal;
|
||||||
double depth;
|
double depth;
|
||||||
int triIdx;
|
int triIdx;
|
||||||
Geom* g1, *g2;
|
void* userData1, *userData2;
|
||||||
|
int geomFlag;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
#include "geom_tree.h"
|
#include "geom_tree.h"
|
||||||
|
|
||||||
CollisionSpace::CollisionSpace(void) {
|
CollisionSpace::CollisionSpace(void) {
|
||||||
|
sphere.radius = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CollisionSpace::AddGeom(Geom* geom) {
|
void CollisionSpace::AddGeom(Geom* geom) {
|
||||||
@ -15,6 +15,35 @@ void CollisionSpace::RemoveGeom(Geom* geom) {
|
|||||||
m_geoms.remove(geom);
|
m_geoms.remove(geom);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CollisionSpace::CollideRaySphere(const vector3d& start, const vector3d& dir, isect_t* isect) {
|
||||||
|
if(sphere.radius != 0) {
|
||||||
|
/* Collide with lovely sphere! */
|
||||||
|
const vector3d v = start - sphere.pos;
|
||||||
|
const double b = -vector3d::Dot(v, dir);
|
||||||
|
double det = (b*b) - vector3d::Dot(v, v) + (sphere.radius*sphere.radius);
|
||||||
|
if(det > 0) {
|
||||||
|
det = sqrt(det);
|
||||||
|
const double i1 = b - det;
|
||||||
|
const double i2 = b + det;
|
||||||
|
if(i2 > 0) {
|
||||||
|
/*if(i1 < 0) {
|
||||||
|
if(i2 < *dist) {
|
||||||
|
*dist = i2;
|
||||||
|
//retval = INPRIM;
|
||||||
|
retval = true;
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
if(i2 > 0) {
|
||||||
|
if(i1 < isect->dist) {
|
||||||
|
isect->dist = i1;
|
||||||
|
isect->triIdx = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void CollisionSpace::TraceRay(const vector3d& start, const vector3d& dir, isect_t* isect) {
|
void CollisionSpace::TraceRay(const vector3d& start, const vector3d& dir, isect_t* isect) {
|
||||||
for(std::list<Geom*>::iterator i = m_geoms.begin(); i != m_geoms.end(); ++i) {
|
for(std::list<Geom*>::iterator i = m_geoms.begin(); i != m_geoms.end(); ++i) {
|
||||||
if((*i)->IsEnabled()) {
|
if((*i)->IsEnabled()) {
|
||||||
@ -26,6 +55,7 @@ void CollisionSpace::TraceRay(const vector3d& start, const vector3d& dir, isect_
|
|||||||
(*i)->GetGeomTree()->TraceRay(modelStart, modelDir, isect);
|
(*i)->GetGeomTree()->TraceRay(modelStart, modelDir, isect);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
CollideRaySphere(start, dir, isect);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CollisionSpace::CollideGeoms(Geom* a, void (*callback)(CollisionContact*)) {
|
void CollisionSpace::CollideGeoms(Geom* a, void (*callback)(CollisionContact*)) {
|
||||||
@ -36,6 +66,11 @@ void CollisionSpace::CollideGeoms(Geom* a, void (*callback)(CollisionContact*))
|
|||||||
bigAabb.min += pos;
|
bigAabb.min += pos;
|
||||||
bigAabb.max += pos;
|
bigAabb.max += pos;
|
||||||
|
|
||||||
|
/* First test this reested sh*t against the planet sphere thing. */
|
||||||
|
if(sphere.radius != 0) {
|
||||||
|
a->CollideSphere(sphere, callback);
|
||||||
|
}
|
||||||
|
|
||||||
for(std::list<Geom*>::iterator i = m_geoms.begin(); i != m_geoms.end(); ++i) {
|
for(std::list<Geom*>::iterator i = m_geoms.begin(); i != m_geoms.end(); ++i) {
|
||||||
if((*i) != a) {
|
if((*i) != a) {
|
||||||
Aabb bigAabb2;
|
Aabb bigAabb2;
|
||||||
|
@ -6,6 +6,14 @@ class Geom;
|
|||||||
struct isect_t;
|
struct isect_t;
|
||||||
class CollisionContact;
|
class CollisionContact;
|
||||||
|
|
||||||
|
struct Sphere {
|
||||||
|
vector3d pos;
|
||||||
|
double radius;
|
||||||
|
void* userData;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Collision spaces have a bunch of geoms and at most one sphere (for a planet). */
|
||||||
|
|
||||||
class CollisionSpace {
|
class CollisionSpace {
|
||||||
public:
|
public:
|
||||||
CollisionSpace(void);
|
CollisionSpace(void);
|
||||||
@ -13,8 +21,15 @@ public:
|
|||||||
void RemoveGeom(Geom*);
|
void RemoveGeom(Geom*);
|
||||||
void TraceRay(const vector3d& start, const vector3d& dir, isect_t* isect);
|
void TraceRay(const vector3d& start, const vector3d& dir, isect_t* isect);
|
||||||
void Collide(void (*callback)(CollisionContact*));
|
void Collide(void (*callback)(CollisionContact*));
|
||||||
|
void SetSphere(const vector3d& pos, double radius, void* user_data) {
|
||||||
|
sphere.pos = pos; sphere.radius = radius; sphere.userData = user_data;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void CollideGeoms(Geom* a, void (*callback)(CollisionContact*));
|
void CollideGeoms(Geom* a, void (*callback)(CollisionContact*));
|
||||||
|
void CollideRaySphere(const vector3d& start, const vector3d& dir, isect_t* isect);
|
||||||
std::list<Geom*> m_geoms;
|
std::list<Geom*> m_geoms;
|
||||||
|
|
||||||
|
Sphere sphere;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -36,6 +36,47 @@ vector3d Geom::GetPosition(void) const {
|
|||||||
m_orient[m_orientIdx][14]);
|
m_orient[m_orientIdx][14]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Geom::CollideSphere(Sphere& sphere, void(*callback)(CollisionContact*)) {
|
||||||
|
for(int i = 0; i < m_geomtree->m_numVertices; i++) {
|
||||||
|
vector3d vtx(&m_geomtree->m_vertices[3*i]);
|
||||||
|
vector3d from = m_orient[!m_orientIdx] * vtx;
|
||||||
|
vector3d to = m_orient[m_orientIdx] * vtx;
|
||||||
|
vector3d dir = to - from;
|
||||||
|
const double len = dir.Length();
|
||||||
|
dir *= 1.0f/len;
|
||||||
|
|
||||||
|
vector3d _from(from.x, from.y, from.z);
|
||||||
|
vector3d _dir(dir.x, dir.y, dir.z);
|
||||||
|
|
||||||
|
_dir.Normalize();
|
||||||
|
|
||||||
|
/* Collide with lovely sphere! */
|
||||||
|
const vector3d v = _from - sphere.pos;
|
||||||
|
const double b = -vector3d::Dot(v, dir);
|
||||||
|
double det = (b*b) - vector3d::Dot(v,v) + (sphere.radius*sphere.radius);
|
||||||
|
if(det > 0) {
|
||||||
|
det = sqrt(det);
|
||||||
|
const double i1 = b - det;
|
||||||
|
const double i2 = b + det;
|
||||||
|
if(i2 > 0) {
|
||||||
|
if(i1 > 0) {
|
||||||
|
if(i1 < len) {
|
||||||
|
CollisionContact c;
|
||||||
|
c.pos = _from + _dir*i1;
|
||||||
|
c.normal = vector3d::Normalize(v);
|
||||||
|
c.depth = len - i1;
|
||||||
|
c.triIdx = 0;
|
||||||
|
c.userData1 = this->m_data;
|
||||||
|
c.userData2 = sphere.userData;
|
||||||
|
c.geomFlag = 0;
|
||||||
|
(*callback)(&c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Geom::Collide(Geom* b, void(*callback)(CollisionContact*)) {
|
void Geom::Collide(Geom* b, void(*callback)(CollisionContact*)) {
|
||||||
m_moved = false;
|
m_moved = false;
|
||||||
for(int i = 0; i < m_geomtree->m_numVertices; i++) {
|
for(int i = 0; i < m_geomtree->m_numVertices; i++) {
|
||||||
@ -66,8 +107,9 @@ void Geom::Collide(Geom* b, void(*callback)(CollisionContact*)) {
|
|||||||
|
|
||||||
c.depth = len - isect.dist;
|
c.depth = len - isect.dist;
|
||||||
c.triIdx = isect.triIdx;
|
c.triIdx = isect.triIdx;
|
||||||
c.g1 = this;
|
c.userData1 = m_data;
|
||||||
c.g2 = b;
|
c.userData2 = b->m_data;
|
||||||
|
c.geomFlag = b->m_geomtree->GetTriFlag(isect.triIdx);
|
||||||
(*callback)(&c);
|
(*callback)(&c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
class GeomTree;
|
class GeomTree;
|
||||||
class isect_t;
|
class isect_t;
|
||||||
class CollisionContact;
|
class CollisionContact;
|
||||||
|
class Sphere;
|
||||||
|
|
||||||
class Geom {
|
class Geom {
|
||||||
public:
|
public:
|
||||||
@ -20,6 +21,7 @@ public:
|
|||||||
bool HasMoved(void) { return m_moved; }
|
bool HasMoved(void) { return m_moved; }
|
||||||
GeomTree* GetGeomTree(void) { return m_geomtree; }
|
GeomTree* GetGeomTree(void) { return m_geomtree; }
|
||||||
void Collide(Geom* b, void(*callback)(CollisionContact*));
|
void Collide(Geom* b, void(*callback)(CollisionContact*));
|
||||||
|
void CollideSphere(Sphere& sphere, void (*callback)(CollisionContact*));
|
||||||
void SetUserData(void* d) { m_data = d; }
|
void SetUserData(void* d) { m_data = d; }
|
||||||
void* GetUserData(void) { return m_data; }
|
void* GetUserData(void) { return m_data; }
|
||||||
private:
|
private:
|
||||||
|
@ -7,10 +7,12 @@
|
|||||||
* You don't need a 'mailbox' as objects only appear once in the hierarchy.
|
* You don't need a 'mailbox' as objects only appear once in the hierarchy.
|
||||||
* single ray traversal performance is comparable to kd-tree and way faster than bvh.
|
* single ray traversal performance is comparable to kd-tree and way faster than bvh.
|
||||||
*/
|
*/
|
||||||
#define MAX_LEAF_PRIMS 2
|
#define MAX_LEAF_PRIMS 1
|
||||||
#define MAX_DEPTH 20
|
#define MAX_DEPTH 20
|
||||||
#define MAX_SPLITPOS_RETRIES 32
|
#define MAX_SPLITPOS_RETRIES 32
|
||||||
#define MIN_SPACE_CUTOFF 0.33
|
#define MIN_SPACE_CUTOFF 0.5
|
||||||
|
//#define DO_SPACE_CUTOFF
|
||||||
|
#define NODE_ALLOC_MULT 8
|
||||||
#define EPSILON 0.00001
|
#define EPSILON 0.00001
|
||||||
|
|
||||||
#include <float.h>
|
#include <float.h>
|
||||||
@ -24,6 +26,7 @@
|
|||||||
#define MAX(a,b) ((a)>(b) ? (a) : (b))
|
#define MAX(a,b) ((a)>(b) ? (a) : (b))
|
||||||
|
|
||||||
class DisplayList;
|
class DisplayList;
|
||||||
|
int GeomTree::stats_rayTriIntersections;
|
||||||
|
|
||||||
struct tri_t {
|
struct tri_t {
|
||||||
int triIdx;
|
int triIdx;
|
||||||
@ -120,8 +123,8 @@ GeomTree::GeomTree(int numVerts, int numTris, float* vertices, int* indices,
|
|||||||
m_maxAabb.max.x,
|
m_maxAabb.max.x,
|
||||||
m_maxAabb.max.y,
|
m_maxAabb.max.y,
|
||||||
m_maxAabb.max.z);
|
m_maxAabb.max.z);
|
||||||
m_nodes = new BIHNode[numTris*4];
|
m_nodes = new BIHNode[numTris*NODE_ALLOC_MULT];
|
||||||
m_nodesAllocSize = numTris*4;
|
m_nodesAllocSize = numTris*NODE_ALLOC_MULT;
|
||||||
m_nodesAllocPos = 0;
|
m_nodesAllocPos = 0;
|
||||||
m_triAllocPos = 0;
|
m_triAllocPos = 0;
|
||||||
|
|
||||||
@ -153,6 +156,7 @@ void GeomTree::BihTreeGhBuild(BIHNode* a_node, Aabb& a_box, Aabb& a_splitBox, in
|
|||||||
int s1count, s2count, splitAxis, attempt;
|
int s1count, s2count, splitAxis, attempt;
|
||||||
attempt = 0;
|
attempt = 0;
|
||||||
|
|
||||||
|
#ifdef DO_SPACE_CUTOFF
|
||||||
Aabb realAabb;
|
Aabb realAabb;
|
||||||
realAabb.min = vector3d(FLT_MAX, FLT_MAX, FLT_MAX);
|
realAabb.min = vector3d(FLT_MAX, FLT_MAX, FLT_MAX);
|
||||||
realAabb.max = vector3d(-FLT_MAX, -FLT_MAX, -FLT_MAX);
|
realAabb.max = vector3d(-FLT_MAX, -FLT_MAX, -FLT_MAX);
|
||||||
@ -227,7 +231,7 @@ void GeomTree::BihTreeGhBuild(BIHNode* a_node, Aabb& a_box, Aabb& a_splitBox, in
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif /* DO_SPACE_CUTOFF */
|
||||||
for(;;) {
|
for(;;) {
|
||||||
splitAxis = 0;
|
splitAxis = 0;
|
||||||
vector3d splitBoxSize = a_splitBox.max - a_splitBox.min;
|
vector3d splitBoxSize = a_splitBox.max - a_splitBox.min;
|
||||||
@ -468,6 +472,7 @@ pop_bstack:
|
|||||||
}
|
}
|
||||||
|
|
||||||
void GeomTree::RayTriIntersect(const vector3f& origin, const vector3f& dir, int triIdx, isect_t* isect) {
|
void GeomTree::RayTriIntersect(const vector3f& origin, const vector3f& dir, int triIdx, isect_t* isect) {
|
||||||
|
stats_rayTriIntersections++;
|
||||||
const vector3f a(&m_vertices[3*m_indices[triIdx]]);
|
const vector3f a(&m_vertices[3*m_indices[triIdx]]);
|
||||||
const vector3f b(&m_vertices[3*m_indices[triIdx+1]]);
|
const vector3f b(&m_vertices[3*m_indices[triIdx+1]]);
|
||||||
const vector3f c(&m_vertices[3*m_indices[triIdx+2]]);
|
const vector3f c(&m_vertices[3*m_indices[triIdx+2]]);
|
||||||
|
@ -27,6 +27,7 @@ public:
|
|||||||
|
|
||||||
const int m_numVertices;
|
const int m_numVertices;
|
||||||
const float* m_vertices;
|
const float* m_vertices;
|
||||||
|
static int stats_rayTriIntersections;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend class BIHNode;
|
friend class BIHNode;
|
||||||
|
@ -91,6 +91,10 @@ Frame::~Frame(void) {
|
|||||||
void Frame::AddGeom(Geom* g) { m_collisionSpace->AddGeom(g); }
|
void Frame::AddGeom(Geom* g) { m_collisionSpace->AddGeom(g); }
|
||||||
void Frame::RemoveGeom(Geom* g) { m_collisionSpace->RemoveGeom(g); }
|
void Frame::RemoveGeom(Geom* g) { m_collisionSpace->RemoveGeom(g); }
|
||||||
|
|
||||||
|
void Frame::SetPlanetGeom(double radius, Body* obj) {
|
||||||
|
m_collisionSpace->SetSphere(vector3d(0,0,0), radius, static_cast<void*>(obj));
|
||||||
|
}
|
||||||
|
|
||||||
void Frame::ApplyLeavingTransform(matrix4x4d& m) const {
|
void Frame::ApplyLeavingTransform(matrix4x4d& m) const {
|
||||||
m = matrix4x4d::Translation(m_pos) * m_orient * m;
|
m = matrix4x4d::Translation(m_pos) * m_orient * m;
|
||||||
}
|
}
|
||||||
|
@ -36,6 +36,7 @@ public:
|
|||||||
void _RemoveGeom(dGeomID g) { dSpaceRemove(m_dSpaceID, g); }
|
void _RemoveGeom(dGeomID g) { dSpaceRemove(m_dSpaceID, g); }
|
||||||
void AddGeom(Geom*);
|
void AddGeom(Geom*);
|
||||||
void RemoveGeom(Geom*);
|
void RemoveGeom(Geom*);
|
||||||
|
void SetPlanetGeom(double radius, Body*);
|
||||||
dSpaceID GetSpaceID(void) const { return m_dSpaceID; }
|
dSpaceID GetSpaceID(void) const { return m_dSpaceID; }
|
||||||
CollisionSpace* GetCollisionSpace(void) const { return m_collisionSpace; }
|
CollisionSpace* GetCollisionSpace(void) const { return m_collisionSpace; }
|
||||||
void RotateInTimestep(double step);
|
void RotateInTimestep(double step);
|
||||||
|
@ -911,8 +911,14 @@ void Planet::Render(const Frame* a_camFrame) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Planet::SetFrame(Frame* f) {
|
void Planet::SetFrame(Frame* f) {
|
||||||
if(GetFrame()) GetFrame()->_RemoveGeom(geom);
|
if(GetFrame()) {
|
||||||
|
GetFrame()->_RemoveGeom(geom);
|
||||||
|
GetFrame()->SetPlanetGeom(0,0);
|
||||||
|
}
|
||||||
Body::SetFrame(f);
|
Body::SetFrame(f);
|
||||||
if(f) f->_AddGeom(geom);
|
if(f) {
|
||||||
|
f->_AddGeom(geom);
|
||||||
|
GetFrame()->SetPlanetGeom(GetRadius(), this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -160,6 +160,7 @@ float foo[TEXSIZE][TEXSIZE];
|
|||||||
float aspectRatio = 1.0;
|
float aspectRatio = 1.0;
|
||||||
float camera_zoom = 1.0;
|
float camera_zoom = 1.0;
|
||||||
float distance = 100;
|
float distance = 100;
|
||||||
|
extern int stat_rayTriIntersections;
|
||||||
static void raytraceCollMesh(vector3d camPos, vector3d camera_up, vector3d camera_forward, CollisionSpace* space) {
|
static void raytraceCollMesh(vector3d camPos, vector3d camera_up, vector3d camera_forward, CollisionSpace* space) {
|
||||||
memset(foo, 0, sizeof(float)*TEXSIZE*TEXSIZE);
|
memset(foo, 0, sizeof(float)*TEXSIZE*TEXSIZE);
|
||||||
|
|
||||||
@ -178,6 +179,7 @@ static void raytraceCollMesh(vector3d camPos, vector3d camera_up, vector3d camer
|
|||||||
float ystep = 1.0f / TEXSIZE;
|
float ystep = 1.0f / TEXSIZE;
|
||||||
float xpos, ypos;
|
float xpos, ypos;
|
||||||
ypos = 0.0f;
|
ypos = 0.0f;
|
||||||
|
GeomTree::stats_rayTriIntersections = 0;
|
||||||
|
|
||||||
Uint32 t = SDL_GetTicks();
|
Uint32 t = SDL_GetTicks();
|
||||||
for(int y = 0; y < TEXSIZE; y++, ypos += ystep) {
|
for(int y = 0; y < TEXSIZE; y++, ypos += ystep) {
|
||||||
@ -198,7 +200,8 @@ static void raytraceCollMesh(vector3d camPos, vector3d camera_up, vector3d camer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
printf("%3f million rays/sec\n", (TEXSIZE*TEXSIZE)/(1000.0*(SDL_GetTicks()-t)));
|
printf("%.3f million rays/sec, %.2f tri isect tests per ray\n", (TEXSIZE*TEXSIZE)/(1000.0*(SDL_GetTicks()-t)),
|
||||||
|
GeomTree::stats_rayTriIntersections/(float)(TEXSIZE*TEXSIZE));
|
||||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, TEXSIZE, TEXSIZE, 0, GL_LUMINANCE, GL_FLOAT, foo);
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, TEXSIZE, TEXSIZE, 0, GL_LUMINANCE, GL_FLOAT, foo);
|
||||||
|
|
||||||
glDisable(GL_DEPTH_TEST);
|
glDisable(GL_DEPTH_TEST);
|
||||||
|
@ -326,7 +326,7 @@ static void dump_contact(const dContact* c) {
|
|||||||
|
|
||||||
static bool _OnCollision2(Object* o1, Object* o2, CollisionContact* c) {
|
static bool _OnCollision2(Object* o1, Object* o2, CollisionContact* c) {
|
||||||
Body* pb1, *pb2;
|
Body* pb1, *pb2;
|
||||||
int flags = c->g2->GetGeomTree()->GetTriFlag(c->triIdx);
|
int flags = c->geomFlag;
|
||||||
//printf("Collision flags %x (triIdx %d)\n", flags, c->triIdx);
|
//printf("Collision flags %x (triIdx %d)\n", flags, c->triIdx);
|
||||||
/* Geom bodies point to their parents. */
|
/* Geom bodies point to their parents. */
|
||||||
if(o1->IsType(Object::GEOM)) {
|
if(o1->IsType(Object::GEOM)) {
|
||||||
@ -369,8 +369,19 @@ static void hitCallback(CollisionContact* c) {
|
|||||||
contact.fdir1[1] = 0;
|
contact.fdir1[1] = 0;
|
||||||
contact.fdir1[2] = 0;
|
contact.fdir1[2] = 0;
|
||||||
|
|
||||||
|
Object* po1 = static_cast<Object*>(c->userData1);
|
||||||
|
Object* po2 = static_cast<Object*>(c->userData2);
|
||||||
|
|
||||||
|
if(!_OnCollision2(po1, po2, c)) return;
|
||||||
|
|
||||||
|
dBodyID b1 = 0;
|
||||||
|
dBodyID b2 = 0;
|
||||||
|
/* Get the dynamics body for each geom. */
|
||||||
|
if(po1->IsType(Object::DYNAMICBODY)) b1 = static_cast<DynamicBody*>(po1)->m_body;
|
||||||
|
if(po2->IsType(Object::DYNAMICBODY)) b2 = static_cast<DynamicBody*>(po2)->m_body;
|
||||||
|
|
||||||
dJointID j = dJointCreateContact(Space::world, _contactgroup, &contact);
|
dJointID j = dJointCreateContact(Space::world, _contactgroup, &contact);
|
||||||
dJointAttach(j, (dBodyID)c->g1->GetUserData(), (dBodyID)c->g2->GetUserData());
|
dJointAttach(j, b1, b2);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
|
Loading…
Reference in New Issue
Block a user