testbed, tests, ui
This commit is contained in:
@ -64,11 +64,11 @@ public:
|
||||
{
|
||||
Test::Step();
|
||||
|
||||
g_debugDraw->DrawString(b3Color_white, "S - Static");
|
||||
g_debugDraw->DrawString(b3Color_white, "D - Dynamic");
|
||||
g_debugDraw->DrawString(b3Color_white, "K - Kinematic");
|
||||
g_debugDraw->DrawString(b3Color_white, "Space - Throw Bomb");
|
||||
g_debugDraw->DrawString(b3Color_white, "Arrows - Apply Force/Velocity/Position");
|
||||
g_draw->DrawString(b3Color_white, "S - Static");
|
||||
g_draw->DrawString(b3Color_white, "D - Dynamic");
|
||||
g_draw->DrawString(b3Color_white, "K - Kinematic");
|
||||
g_draw->DrawString(b3Color_white, "Space - Throw Bomb");
|
||||
g_draw->DrawString(b3Color_white, "Arrows - Apply Force/Velocity/Position");
|
||||
}
|
||||
|
||||
void KeyDown(int button)
|
||||
|
@ -51,7 +51,7 @@ public:
|
||||
|
||||
void Step()
|
||||
{
|
||||
m_cloth.Step(g_settings->inv_hertz, g_settings->positionIterations);
|
||||
m_cloth.Step(g_testSettings->inv_hertz, g_testSettings->positionIterations);
|
||||
m_cloth.Draw();
|
||||
}
|
||||
|
||||
|
@ -61,15 +61,15 @@ public:
|
||||
|
||||
for (u32 i = 0; i < clusters.Count(); ++i)
|
||||
{
|
||||
g_debugDraw->DrawSegment(b3Vec3(0, 0, 0), clusters[i].centroid, b3Color(1, 1, 1));
|
||||
g_debugDraw->DrawPoint(clusters[i].centroid, 4.0f, m_colors[i]);
|
||||
g_draw->DrawSegment(b3Vec3(0, 0, 0), clusters[i].centroid, b3Color(1, 1, 1));
|
||||
g_draw->DrawPoint(clusters[i].centroid, 4.0f, m_colors[i]);
|
||||
|
||||
for (u32 j = 0; j < observations.Count(); ++j)
|
||||
{
|
||||
b3Observation obs = observations[j];
|
||||
if (obs.cluster == i)
|
||||
{
|
||||
g_debugDraw->DrawPoint(obs.point, 4.0f, m_colors[i]);
|
||||
g_draw->DrawPoint(obs.point, 4.0f, m_colors[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -46,21 +46,15 @@ public:
|
||||
b3Vec3 pw = wm.points[i].point;
|
||||
b3Vec2 ps = g_camera->ConvertWorldToScreen(pw);
|
||||
|
||||
g_debugDraw->DrawPoint(pw, 4.0f, b3Color(0.0f, 1.0f, 0.0f));
|
||||
g_debugDraw->DrawSegment(pw, pw + wm.points[i].normal, b3Color(1.0f, 1.0f, 1.0f));
|
||||
g_draw->DrawPoint(pw, 4.0f, b3Color(0.0f, 1.0f, 0.0f));
|
||||
g_draw->DrawSegment(pw, pw + wm.points[i].normal, b3Color(1.0f, 1.0f, 1.0f));
|
||||
}
|
||||
|
||||
if (g_settings->drawFaces)
|
||||
{
|
||||
g_debugDraw->DrawShape(m_shapeA, b3Color(1.0f, 1.0f, 1.0f, 0.5f), m_xfA);
|
||||
g_debugDraw->DrawShape(m_shapeB, b3Color(1.0f, 1.0f, 1.0f, 0.5f), m_xfB);
|
||||
}
|
||||
|
||||
if (g_settings->drawVerticesEdges)
|
||||
{
|
||||
m_world.DrawShape(m_xfA, m_shapeA);
|
||||
m_world.DrawShape(m_xfB, m_shapeB);
|
||||
}
|
||||
g_draw->DrawSolidShape(m_shapeA, b3Color(1.0f, 1.0f, 1.0f, 0.5f), m_xfA);
|
||||
g_draw->DrawSolidShape(m_shapeB, b3Color(1.0f, 1.0f, 1.0f, 0.5f), m_xfB);
|
||||
|
||||
m_world.DrawShape(m_xfA, m_shapeA);
|
||||
m_world.DrawShape(m_xfB, m_shapeB);
|
||||
}
|
||||
|
||||
virtual void KeyDown(int key)
|
||||
|
@ -16,13 +16,13 @@
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
#ifndef MULTIPLE_SHAPES_H
|
||||
#define MULTIPLE_SHAPES_H
|
||||
#ifndef COMPOUND_BODY_H
|
||||
#define COMPOUND_BODY_H
|
||||
|
||||
class MultipleShapes : public Test
|
||||
class CompoundBody : public Test
|
||||
{
|
||||
public:
|
||||
MultipleShapes()
|
||||
CompoundBody()
|
||||
{
|
||||
{
|
||||
b3BodyDef bd;
|
||||
@ -76,7 +76,7 @@ public:
|
||||
b3BodyDef bd;
|
||||
bd.type = e_dynamicBody;
|
||||
bd.position.Set(0.0f, 0.0f, 0.0f);
|
||||
bd.angularVelocity.Set(0.0f, 200.0f * B3_PI, 0.0f);
|
||||
bd.angularVelocity.Set(0.0f, 2.0f * B3_PI, 0.0f);
|
||||
|
||||
b3Body* body = m_world.CreateBody(bd);
|
||||
|
||||
@ -105,7 +105,7 @@ public:
|
||||
|
||||
static Test* Create()
|
||||
{
|
||||
return new MultipleShapes();
|
||||
return new CompoundBody();
|
||||
}
|
||||
|
||||
b3BoxHull m_box1;
|
@ -51,22 +51,22 @@ public:
|
||||
for (u32 i = 0; i < featurePair.count1; ++i)
|
||||
{
|
||||
u32 index = featurePair.index1[i];
|
||||
g_debugDraw->DrawPoint(m_xfA * m_proxyA.GetVertex(index), 4.0f, b3Color(1.0f, 1.0f, 0.0f));
|
||||
g_draw->DrawPoint(m_xfA * m_proxyA.GetVertex(index), 4.0f, b3Color(1.0f, 1.0f, 0.0f));
|
||||
}
|
||||
|
||||
for (u32 i = 0; i < featurePair.count2; ++i)
|
||||
{
|
||||
u32 index = featurePair.index2[i];
|
||||
g_debugDraw->DrawPoint(m_xfB * m_proxyB.GetVertex(index), 4.0f, b3Color(1.0f, 1.0f, 0.0f));
|
||||
g_draw->DrawPoint(m_xfB * m_proxyB.GetVertex(index), 4.0f, b3Color(1.0f, 1.0f, 0.0f));
|
||||
}
|
||||
}
|
||||
|
||||
g_debugDraw->DrawPoint(out.point1, 4.0f, b3Color(0.0f, 1.0f, 0.0f));
|
||||
g_debugDraw->DrawPoint(out.point2, 4.0f, b3Color(0.0f, 1.0f, 0.0f));
|
||||
g_debugDraw->DrawSegment(out.point1, out.point2, b3Color(1.0f, 1.0f, 1.0f));
|
||||
g_draw->DrawPoint(out.point1, 4.0f, b3Color(0.0f, 1.0f, 0.0f));
|
||||
g_draw->DrawPoint(out.point2, 4.0f, b3Color(0.0f, 1.0f, 0.0f));
|
||||
g_draw->DrawSegment(out.point1, out.point2, b3Color(1.0f, 1.0f, 1.0f));
|
||||
|
||||
g_debugDraw->DrawTransform(m_xfA);
|
||||
g_debugDraw->DrawTransform(m_xfB);
|
||||
g_draw->DrawTransform(m_xfA);
|
||||
g_draw->DrawTransform(m_xfB);
|
||||
|
||||
m_world.DrawShape(m_xfA, &m_shapeA);
|
||||
m_world.DrawShape(m_xfB, &m_shapeB);
|
||||
|
@ -16,15 +16,15 @@
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
#ifndef GYRO_TEST_H
|
||||
#define GYRO_TEST_H
|
||||
#ifndef GYRO_MOTION_H
|
||||
#define GYRO_MOTION_H
|
||||
|
||||
#include <testbed/tests/quickhull_test.h>
|
||||
|
||||
class GyroTest : public Test
|
||||
class GyroMotion : public Test
|
||||
{
|
||||
public:
|
||||
GyroTest()
|
||||
GyroMotion()
|
||||
{
|
||||
{
|
||||
b3StackArray<b3Vec3, 32> points;
|
||||
@ -63,10 +63,7 @@ public:
|
||||
b3Body* body = m_world.CreateBody(bdef);
|
||||
|
||||
{
|
||||
b3Transform xf;
|
||||
xf.position.SetZero();
|
||||
xf.rotation = b3Diagonal(1.0f, 0.5f, 7.0f);
|
||||
m_rotorBox.SetTransform(xf);
|
||||
m_rotorBox.Set(1.0f, 0.5f, 7.0f);
|
||||
|
||||
b3HullShape hull;
|
||||
hull.m_hull = &m_rotorBox;
|
||||
@ -93,7 +90,7 @@ public:
|
||||
m_world.SetGravity(b3Vec3(0.0f, 0.0f, 0.0f));
|
||||
}
|
||||
|
||||
~GyroTest()
|
||||
~GyroMotion()
|
||||
{
|
||||
{
|
||||
b3Free(m_cylinderHull.vertices);
|
||||
@ -105,7 +102,7 @@ public:
|
||||
|
||||
static Test* Create()
|
||||
{
|
||||
return new GyroTest();
|
||||
return new GyroMotion();
|
||||
}
|
||||
|
||||
b3BoxHull m_rotorBox;
|
@ -126,18 +126,18 @@ public:
|
||||
|
||||
void Step()
|
||||
{
|
||||
float32 h = g_settings->inv_hertz;
|
||||
float32 h = g_testSettings->inv_hertz;
|
||||
|
||||
Solve(h);
|
||||
|
||||
g_debugDraw->DrawSolidSphere(m_x, 0.25f, b3Color_white);
|
||||
g_draw->DrawSolidSphere(m_x, 0.25f, b3Color_white);
|
||||
|
||||
g_debugDraw->DrawSegment(b3Vec3_zero, m_x, b3Color_white);
|
||||
g_draw->DrawSegment(b3Vec3_zero, m_x, b3Color_white);
|
||||
|
||||
g_debugDraw->DrawString(b3Color_white, "Iterations = %u", m_iterations);
|
||||
g_draw->DrawString(b3Color_white, "Iterations = %u", m_iterations);
|
||||
|
||||
float32 E = 0.5f * b3Dot(m_v, m_v);
|
||||
g_debugDraw->DrawString(b3Color_white, "E = %f", E);
|
||||
g_draw->DrawString(b3Color_white, "E = %f", E);
|
||||
}
|
||||
|
||||
static Test* Create()
|
||||
|
@ -163,11 +163,11 @@ public:
|
||||
{
|
||||
Test::Step();
|
||||
|
||||
g_debugDraw->DrawString(b3Color_white, "S - Sphere");
|
||||
g_debugDraw->DrawString(b3Color_white, "C - Capsule");
|
||||
g_debugDraw->DrawString(b3Color_white, "H - Hull");
|
||||
g_debugDraw->DrawString(b3Color_white, "G - Grid");
|
||||
g_debugDraw->DrawString(b3Color_white, "T - Terrain");
|
||||
g_draw->DrawString(b3Color_white, "S - Sphere");
|
||||
g_draw->DrawString(b3Color_white, "C - Capsule");
|
||||
g_draw->DrawString(b3Color_white, "H - Hull");
|
||||
g_draw->DrawString(b3Color_white, "G - Grid");
|
||||
g_draw->DrawString(b3Color_white, "T - Terrain");
|
||||
}
|
||||
|
||||
static Test* Create()
|
||||
|
@ -16,13 +16,13 @@
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
#ifndef CHARACTER_H
|
||||
#define CHARACTER_H
|
||||
#ifndef POINT_CLICK_H
|
||||
#define POINT_CLICK_H
|
||||
|
||||
class Character : public Test
|
||||
class PointClick : public Test
|
||||
{
|
||||
public:
|
||||
Character()
|
||||
PointClick()
|
||||
{
|
||||
{
|
||||
b3BodyDef bdef;
|
||||
@ -59,31 +59,28 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
void RayHit()
|
||||
void BeginDragging()
|
||||
{
|
||||
if (m_rayHit.shape)
|
||||
if (m_bodyDragger.GetBody() == m_character)
|
||||
{
|
||||
if (m_rayHit.shape->GetBody() != m_character)
|
||||
{
|
||||
Test::RayHit();
|
||||
}
|
||||
m_bodyDragger.StopDragging();
|
||||
}
|
||||
}
|
||||
|
||||
void Step()
|
||||
{
|
||||
if (m_rayHit.shape)
|
||||
if (m_bodyDragger.IsSelected())
|
||||
{
|
||||
if (m_rayHit.shape->GetBody() != m_character)
|
||||
if (m_bodyDragger.GetBody() != m_character)
|
||||
{
|
||||
b3Vec3 point = m_rayHit.point;
|
||||
b3Vec3 normal = m_rayHit.normal;
|
||||
|
||||
const b3Transform& xf = m_character->GetTransform();
|
||||
b3Vec3 n = point - xf.position;
|
||||
n.Normalize();
|
||||
b3Vec3 p1 = m_character->GetPosition();
|
||||
b3Vec3 p2 = m_bodyDragger.GetPointA();
|
||||
|
||||
m_character->ApplyForceToCenter(1000.0f * n, true);
|
||||
b3Vec3 n = b3Normalize(p2 - p1);
|
||||
const float32 k = 1000.0f;
|
||||
b3Vec3 f = k * n;
|
||||
|
||||
m_character->ApplyForceToCenter(f, true);
|
||||
}
|
||||
}
|
||||
|
||||
@ -92,10 +89,10 @@ public:
|
||||
|
||||
static Test* Create()
|
||||
{
|
||||
return new Character();
|
||||
return new PointClick();
|
||||
}
|
||||
|
||||
b3Body* m_character;
|
||||
};
|
||||
|
||||
#endif
|
||||
#endif
|
@ -317,7 +317,7 @@ public:
|
||||
|
||||
void Step()
|
||||
{
|
||||
m_qhull.Draw(g_debugDraw);
|
||||
m_qhull.Draw(g_draw);
|
||||
}
|
||||
|
||||
static Test* Create()
|
||||
|
@ -181,22 +181,20 @@ public:
|
||||
b3RayCastSingleOutput out;
|
||||
if (m_world.RayCastSingle(&out, p1, p2))
|
||||
{
|
||||
g_debugDraw->DrawSegment(p1, out.point, b3Color_green);
|
||||
g_draw->DrawSegment(p1, out.point, b3Color_green);
|
||||
|
||||
g_debugDraw->DrawPoint(out.point, 4.0f, b3Color_red);
|
||||
g_debugDraw->DrawSegment(out.point, out.point + out.normal, b3Color_white);
|
||||
|
||||
g_debugDraw->DrawSolidCircle(out.normal, out.point + 0.025f * out.normal, 1.0f, b3Color_white);
|
||||
g_draw->DrawPoint(out.point, 4.0f, b3Color_red);
|
||||
g_draw->DrawSegment(out.point, out.point + out.normal, b3Color_white);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_debugDraw->DrawSegment(p1, p2, b3Color_green);
|
||||
g_draw->DrawSegment(p1, p2, b3Color_green);
|
||||
}
|
||||
}
|
||||
|
||||
void Step()
|
||||
{
|
||||
float32 dt = g_settings->inv_hertz;
|
||||
float32 dt = g_testSettings->inv_hertz;
|
||||
b3Quat dq = b3QuatRotationY(0.05f * B3_PI * dt);
|
||||
|
||||
m_p1 = b3Mul(dq, m_p1);
|
||||
|
@ -85,7 +85,7 @@ public:
|
||||
|
||||
void Step()
|
||||
{
|
||||
m_rope.Step(g_settings->inv_hertz);
|
||||
m_rope.Step(g_testSettings->inv_hertz);
|
||||
m_rope.Draw();
|
||||
}
|
||||
|
||||
|
@ -16,10 +16,10 @@
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
#ifndef THIN_H
|
||||
#define THIN_H
|
||||
#ifndef SHEET_STACK_H
|
||||
#define SHEET_STACK_H
|
||||
|
||||
class Thin : public Test
|
||||
class SheetStack : public Test
|
||||
{
|
||||
public:
|
||||
enum
|
||||
@ -29,7 +29,7 @@ public:
|
||||
e_depthCount = 1
|
||||
};
|
||||
|
||||
Thin()
|
||||
SheetStack()
|
||||
{
|
||||
{
|
||||
b3BodyDef bdef;
|
||||
@ -47,22 +47,12 @@ public:
|
||||
b3Shape* shape = body->CreateShape(sdef);
|
||||
}
|
||||
|
||||
static b3BoxHull thinHull;
|
||||
|
||||
{
|
||||
b3Transform xf;
|
||||
xf.position.SetZero();
|
||||
xf.rotation = b3Diagonal(4.05f, 2.0f * B3_LINEAR_SLOP, 4.05f);
|
||||
|
||||
thinHull.SetTransform(xf);
|
||||
}
|
||||
static b3Vec3 sheetExtents(4.05f, 2.0f * B3_LINEAR_SLOP, 4.05f);
|
||||
static b3BoxHull sheetHull(sheetExtents.x, sheetExtents.y, sheetExtents.z);
|
||||
|
||||
b3Vec3 stackOrigin;
|
||||
stackOrigin.Set(0.0f, 4.05f, 0.0f);
|
||||
|
||||
b3Vec3 boxScale;
|
||||
boxScale.Set(4.05f, 2.05f, 4.05f);
|
||||
|
||||
for (u32 i = 0; i < e_rowCount; ++i)
|
||||
{
|
||||
for (u32 j = 0; j < e_columnCount; ++j)
|
||||
@ -72,15 +62,15 @@ public:
|
||||
b3BodyDef bdef;
|
||||
bdef.type = b3BodyType::e_dynamicBody;
|
||||
|
||||
bdef.position.x = float32(i) * boxScale.x;
|
||||
bdef.position.y = 1.5f * float32(j) * boxScale.y;
|
||||
bdef.position.z = float32(k) * boxScale.z;
|
||||
bdef.position.x = float32(i) * sheetExtents.x;
|
||||
bdef.position.y = float32(j) * 50.0f * sheetExtents.y;
|
||||
bdef.position.z = float32(k) * sheetExtents.z;
|
||||
bdef.position += stackOrigin;
|
||||
|
||||
b3Body* body = m_world.CreateBody(bdef);
|
||||
|
||||
b3HullShape hs;
|
||||
hs.m_hull = &thinHull;
|
||||
hs.m_hull = &sheetHull;
|
||||
|
||||
b3ShapeDef sdef;
|
||||
sdef.shape = &hs;
|
||||
@ -95,8 +85,8 @@ public:
|
||||
|
||||
static Test* Create()
|
||||
{
|
||||
return new Thin();
|
||||
return new SheetStack();
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
#endif
|
@ -37,7 +37,7 @@ public:
|
||||
|
||||
void Step()
|
||||
{
|
||||
float32 h = g_settings->inv_hertz;
|
||||
float32 h = g_testSettings->inv_hertz;
|
||||
|
||||
// Solution (acceleration)
|
||||
float32 omega_dot = -m_g / m_r * sin(m_theta);
|
||||
@ -53,11 +53,11 @@ public:
|
||||
c.x = m_r * sin(m_theta);
|
||||
c.y = m_r * cos(m_theta);
|
||||
c.z = 0.0f;
|
||||
g_debugDraw->DrawSolidSphere(c, 1.0f, b3Color_white);
|
||||
g_draw->DrawSolidSphere(c, 1.0f, b3Color_white);
|
||||
|
||||
b3Vec3 pole;
|
||||
pole.SetZero();
|
||||
g_debugDraw->DrawSegment(pole, c, b3Color_white);
|
||||
g_draw->DrawSegment(pole, c, b3Color_white);
|
||||
|
||||
// Kinetic energy
|
||||
float32 T = 0.5f * m_I * m_omega * m_omega;
|
||||
@ -69,7 +69,7 @@ public:
|
||||
float32 L = T - V;
|
||||
|
||||
//
|
||||
g_debugDraw->DrawString(b3Color_white, "T = %f \nV = %f \nL = %f", T, V, L);
|
||||
g_draw->DrawString(b3Color_white, "T = %f \nV = %f \nL = %f", T, V, L);
|
||||
}
|
||||
|
||||
static Test* Create()
|
||||
|
@ -204,8 +204,6 @@ class SpringClothTest : public Test
|
||||
public:
|
||||
SpringClothTest() : m_clothDragger(&m_clothRay, &m_cloth)
|
||||
{
|
||||
g_camera->m_zoom = 25.0f;
|
||||
|
||||
m_clothRay.origin.SetZero();
|
||||
m_clothRay.direction.Set(0.0f, 0.0f, -1.0f);
|
||||
m_clothRay.fraction = g_camera->m_zFar;
|
||||
@ -213,7 +211,7 @@ public:
|
||||
|
||||
void Step()
|
||||
{
|
||||
float32 dt = g_settings->inv_hertz;
|
||||
float32 dt = g_testSettings->inv_hertz;
|
||||
|
||||
m_cloth.Step(dt);
|
||||
m_cloth.Apply();
|
||||
@ -226,18 +224,18 @@ public:
|
||||
b3Transform xf;
|
||||
xf.SetIdentity();
|
||||
|
||||
g_debugDraw->DrawShape(s, b3Color_white, xf);
|
||||
g_draw->DrawSolidShape(s, b3Color_white, xf);
|
||||
}
|
||||
|
||||
m_cloth.Draw();
|
||||
|
||||
b3SpringClothStep step = m_cloth.GetStep();
|
||||
|
||||
g_debugDraw->DrawString(b3Color_white, "Iterations = %u", step.iterations);
|
||||
g_draw->DrawString(b3Color_white, "Iterations = %u", step.iterations);
|
||||
|
||||
if (m_clothDragger.IsSelected() == true)
|
||||
{
|
||||
g_debugDraw->DrawSegment(m_clothDragger.GetPointA(), m_clothDragger.GetPointB(), b3Color_white);
|
||||
g_draw->DrawSegment(m_clothDragger.GetPointA(), m_clothDragger.GetPointB(), b3Color_white);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -84,7 +84,7 @@ public:
|
||||
|
||||
void Step()
|
||||
{
|
||||
float32 dt = g_settings->inv_hertz;
|
||||
float32 dt = g_testSettings->inv_hertz;
|
||||
|
||||
m_cloth.Step(dt);
|
||||
m_cloth.Apply();
|
||||
@ -119,17 +119,17 @@ public:
|
||||
|
||||
b3Vec3 n2 = -n1;
|
||||
|
||||
g_debugDraw->DrawSolidTriangle(n1, v1, c1, v2, c2, v3, c3);
|
||||
g_debugDraw->DrawSolidTriangle(n2, v1, c1, v3, c3, v2, c2);
|
||||
g_draw->DrawSolidTriangle(n1, v1, c1, v2, c2, v3, c3);
|
||||
g_draw->DrawSolidTriangle(n2, v1, c1, v3, c3, v2, c2);
|
||||
}
|
||||
|
||||
b3SpringClothStep step = m_cloth.GetStep();
|
||||
|
||||
g_debugDraw->DrawString(b3Color_white, "Iterations = %u", step.iterations);
|
||||
g_draw->DrawString(b3Color_white, "Iterations = %u", step.iterations);
|
||||
|
||||
if (m_clothDragger.IsSelected() == true)
|
||||
{
|
||||
g_debugDraw->DrawSegment(m_clothDragger.GetPointA(), m_clothDragger.GetPointB(), b3Color_white);
|
||||
g_draw->DrawSegment(m_clothDragger.GetPointA(), m_clothDragger.GetPointB(), b3Color_white);
|
||||
}
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user