Physic rebuild start
This commit is contained in:
parent
58cafd7682
commit
c707e49561
@ -32,6 +32,13 @@ ApplicationClass::ApplicationClass() : m_ShouldQuit(false)
|
|||||||
|
|
||||||
ApplicationClass::~ApplicationClass()
|
ApplicationClass::~ApplicationClass()
|
||||||
{
|
{
|
||||||
|
m_ShouldQuit = true;
|
||||||
|
|
||||||
|
// wait for the physics thread to finish
|
||||||
|
while (m_Physics)
|
||||||
|
{
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -400,6 +407,11 @@ bool ApplicationClass::Initialize(int screenWidth, int screenHeight, HWND hwnd)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m_Physics = new Physics;
|
||||||
|
|
||||||
|
std::thread physicsThread(&ApplicationClass::PhysicsThreadFunction, this);
|
||||||
|
physicsThread.detach();
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
catch (const std::exception& e)
|
catch (const std::exception& e)
|
||||||
@ -409,7 +421,7 @@ bool ApplicationClass::Initialize(int screenWidth, int screenHeight, HWND hwnd)
|
|||||||
}
|
}
|
||||||
Logger::Get().Log("Application class initialized", __FILE__, __LINE__, Logger::LogLevel::Initialize);
|
Logger::Get().Log("Application class initialized", __FILE__, __LINE__, Logger::LogLevel::Initialize);
|
||||||
|
|
||||||
m_Physics = new Physics;
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -637,7 +649,7 @@ void ApplicationClass::Shutdown()
|
|||||||
bool ApplicationClass::Frame(InputClass* Input)
|
bool ApplicationClass::Frame(InputClass* Input)
|
||||||
{
|
{
|
||||||
int mouseX, mouseY, currentMouseX, currentMouseY;
|
int mouseX, mouseY, currentMouseX, currentMouseY;
|
||||||
bool result, leftMouseDown, rightMouseDown, keyLeft, keyRight, keyUp, keyDown, buttonQ, buttonD, buttonZ, buttonS, buttonA, buttonE, scrollUp, scrollDown;
|
bool result, leftMouseDown, rightMouseDown, buttonQ, buttonD, buttonZ, buttonS, buttonA, buttonE, scrollUp, scrollDown;
|
||||||
float rotationY, rotationX, positionX, positionY, positionZ;
|
float rotationY, rotationX, positionX, positionY, positionZ;
|
||||||
static float textureTranslation = 0.0f;
|
static float textureTranslation = 0.0f;
|
||||||
|
|
||||||
@ -756,104 +768,11 @@ bool ApplicationClass::Frame(InputClass* Input)
|
|||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
//// Update the x position variable each frame.
|
|
||||||
//x -= 0.0174532925f * 0.6f;
|
|
||||||
|
|
||||||
//y -= 0.0174532925f * 0.2f;
|
m_Inputs.m_KeyLeft = Input->IsLeftArrowPressed();
|
||||||
|
m_Inputs.m_KeyRight = Input->IsRightArrowPressed();
|
||||||
//// Update the z position variable each frame.
|
m_Inputs.m_KeyUp = Input->IsUpArrowPressed();
|
||||||
//z -= 0.0174532925f * 0.2f;
|
m_Inputs.m_KeyDown = Input->IsDownArrowPressed();
|
||||||
|
|
||||||
keyLeft = Input->IsLeftArrowPressed();
|
|
||||||
keyRight = Input->IsRightArrowPressed();
|
|
||||||
keyUp = Input->IsUpArrowPressed();
|
|
||||||
keyDown = Input->IsDownArrowPressed();
|
|
||||||
|
|
||||||
for (auto& object : m_object)
|
|
||||||
{
|
|
||||||
if (object != nullptr) // Check if the object is not null
|
|
||||||
{
|
|
||||||
// Reset acceleration for the new frame
|
|
||||||
object->SetAcceleration(XMVectorZero());
|
|
||||||
|
|
||||||
object->SetGrounded(false);
|
|
||||||
|
|
||||||
for (auto& chunk : m_terrainChunk)
|
|
||||||
{
|
|
||||||
if (m_Physics->IsColliding(object, chunk))
|
|
||||||
{
|
|
||||||
|
|
||||||
// Stop vertical movement, like gravity
|
|
||||||
object->SetVelocity(XMVectorSetY(object->GetVelocity(), 0.0f));
|
|
||||||
object->SetAcceleration(XMVectorSetY(object->GetAcceleration(), 0.0f));
|
|
||||||
|
|
||||||
//// Stop movement in any direction
|
|
||||||
//object->SetVelocity(XMVectorZero());
|
|
||||||
//object->SetAcceleration(XMVectorZero());
|
|
||||||
object->SetGrounded(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto& object2 : m_object)
|
|
||||||
{
|
|
||||||
if (object->GetId() != object2->GetId() && object2 != nullptr)
|
|
||||||
{
|
|
||||||
if (m_Physics->IsColliding(object, object2))
|
|
||||||
{
|
|
||||||
// Stop movement in any direction
|
|
||||||
object->SetVelocity(XMVectorZero());
|
|
||||||
object->SetAcceleration(XMVectorZero());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// Apply forces
|
|
||||||
float forceX = 0, forceY = 0, forceZ = 0, forceW = 0;
|
|
||||||
|
|
||||||
if (keyLeft)
|
|
||||||
{
|
|
||||||
forceX = -10.0f;
|
|
||||||
}
|
|
||||||
if (keyRight)
|
|
||||||
{
|
|
||||||
forceX = 10.0f;
|
|
||||||
}
|
|
||||||
if (keyUp)
|
|
||||||
{
|
|
||||||
forceY = 40.0f;
|
|
||||||
}
|
|
||||||
if (keyDown && !object->IsGrounded())
|
|
||||||
{
|
|
||||||
forceY = -40.0f;
|
|
||||||
}
|
|
||||||
|
|
||||||
XMVECTOR force = XMVectorSet(forceX, forceY, forceZ, forceW);
|
|
||||||
m_Physics->AddForce(object, force);
|
|
||||||
|
|
||||||
// Update velocity based on acceleration
|
|
||||||
object->AddVelocity(frameTime);
|
|
||||||
|
|
||||||
// Update position based on velocity
|
|
||||||
XMVECTOR position = object->GetPosition();
|
|
||||||
position = position + object->GetVelocity() * frameTime;
|
|
||||||
object->SetPosition(position);
|
|
||||||
|
|
||||||
m_Physics->ApplyGravity(object, 1.0f);
|
|
||||||
|
|
||||||
// Check if the object has fallen below a certain position
|
|
||||||
if (XMVectorGetY(object->GetPosition()) < -30.0f)
|
|
||||||
{
|
|
||||||
XMVECTOR currentPosition = object->GetPosition(); // Obtain the current position of the object
|
|
||||||
object->SetPosition(XMVectorSetY(currentPosition, 50.0f)); // Define the new position of the object
|
|
||||||
}
|
|
||||||
|
|
||||||
object->m_previousPosition = object->GetPosition();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Render the scene to a render texture.
|
// Render the scene to a render texture.
|
||||||
result = RenderSceneToTexture(rotation);
|
result = RenderSceneToTexture(rotation);
|
||||||
@ -1543,6 +1462,7 @@ void ApplicationClass::AddKobject(WCHAR* filepath)
|
|||||||
newObject->SetTranslateMatrix(XMMatrixTranslation(0.0f, 50.0f, 0.0f));
|
newObject->SetTranslateMatrix(XMMatrixTranslation(0.0f, 50.0f, 0.0f));
|
||||||
newObject->SetName(filename);
|
newObject->SetName(filename);
|
||||||
newObject->SetId(m_ObjectId);
|
newObject->SetId(m_ObjectId);
|
||||||
|
newObject->SetType(ObjectType::Cube);
|
||||||
|
|
||||||
m_ObjectId++;
|
m_ObjectId++;
|
||||||
|
|
||||||
@ -1957,4 +1877,118 @@ void ApplicationClass::ConstructFrustum()
|
|||||||
m_Camera->GetViewMatrix(viewMatrix);
|
m_Camera->GetViewMatrix(viewMatrix);
|
||||||
|
|
||||||
m_FrustumCulling.ConstructFrustum(SCREEN_DEPTH, projectionMatrix, viewMatrix);
|
m_FrustumCulling.ConstructFrustum(SCREEN_DEPTH, projectionMatrix, viewMatrix);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ApplicationClass::RenderPhysics(bool keyLeft, bool keyRight, bool keyUp, bool keyDown, float deltaTime) {
|
||||||
|
|
||||||
|
for (auto& object : m_object)
|
||||||
|
{
|
||||||
|
if (object == nullptr)
|
||||||
|
{
|
||||||
|
Logger::Get().Log("Object is null", __FILE__, __LINE__, Logger::LogLevel::Error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset acceleration for the new frame
|
||||||
|
object->SetAcceleration(XMVectorZero());
|
||||||
|
object->SetGrounded(false);
|
||||||
|
|
||||||
|
for (auto& chunk : m_terrainChunk)
|
||||||
|
{
|
||||||
|
if (!m_Physics->IsColliding(object, chunk))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop vertical movement, like gravity
|
||||||
|
object->SetVelocity(XMVectorSetY(object->GetVelocity(), 0.0f));
|
||||||
|
object->SetAcceleration(XMVectorSetY(object->GetAcceleration(), 0.0f));
|
||||||
|
object->SetGrounded(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto& object2 : m_object)
|
||||||
|
{
|
||||||
|
if (object->GetId() == object2->GetId())
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!m_Physics->IsColliding(object, object2))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop movement in any direction
|
||||||
|
object->SetVelocity(XMVectorZero());
|
||||||
|
object->SetAcceleration(XMVectorZero());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply forces
|
||||||
|
float forceX = 0, forceY = 0, forceZ = 0, forceW = 0;
|
||||||
|
|
||||||
|
if (keyLeft)
|
||||||
|
{
|
||||||
|
forceX = -10.0f;
|
||||||
|
}
|
||||||
|
if (keyRight)
|
||||||
|
{
|
||||||
|
forceX = 10.0f;
|
||||||
|
}
|
||||||
|
if (keyUp)
|
||||||
|
{
|
||||||
|
forceY = 40.0f;
|
||||||
|
}
|
||||||
|
if (keyDown && !object->IsGrounded())
|
||||||
|
{
|
||||||
|
forceY = -40.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
XMVECTOR force = XMVectorSet(forceX, forceY, forceZ, forceW);
|
||||||
|
m_Physics->AddForce(object, force);
|
||||||
|
|
||||||
|
// Update velocity based on acceleration
|
||||||
|
object->AddVelocity(deltaTime);
|
||||||
|
|
||||||
|
// Update position based on velocity
|
||||||
|
XMVECTOR position = object->GetPosition();
|
||||||
|
position = position + object->GetVelocity() * deltaTime;
|
||||||
|
object->SetPosition(position);
|
||||||
|
|
||||||
|
m_Physics->ApplyGravity(object, deltaTime);
|
||||||
|
|
||||||
|
// Check if the object has fallen below a certain position
|
||||||
|
if (XMVectorGetY(object->GetPosition()) < -30.0f)
|
||||||
|
{
|
||||||
|
XMVECTOR currentPosition = object->GetPosition(); // Obtain the current position of the object
|
||||||
|
object->SetPosition(XMVectorSetY(currentPosition, 50.0f)); // Define the new position of the object
|
||||||
|
}
|
||||||
|
|
||||||
|
object->m_previousPosition = object->GetPosition();
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void ApplicationClass::PhysicsThreadFunction()
|
||||||
|
{
|
||||||
|
auto lastTime = std::chrono::high_resolution_clock::now();
|
||||||
|
|
||||||
|
while (!m_ShouldQuit)
|
||||||
|
{
|
||||||
|
auto currentTime = std::chrono::high_resolution_clock::now();
|
||||||
|
std::chrono::duration<float> elapsedTime = currentTime - lastTime;
|
||||||
|
float deltaTime = elapsedTime.count();
|
||||||
|
lastTime = currentTime;
|
||||||
|
|
||||||
|
bool result = RenderPhysics(m_Inputs.m_KeyLeft, m_Inputs.m_KeyRight, m_Inputs.m_KeyUp, m_Inputs.m_KeyDown, deltaTime);
|
||||||
|
if (!result)
|
||||||
|
{
|
||||||
|
Logger::Get().Log("Could not render the physics scene", __FILE__, __LINE__, Logger::LogLevel::Error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attendre 20 millisecondes (50 fois par seconde)
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(20));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -33,6 +33,8 @@
|
|||||||
|
|
||||||
#include <WICTextureLoader.h>
|
#include <WICTextureLoader.h>
|
||||||
#include <comdef.h> // Pour _com_error
|
#include <comdef.h> // Pour _com_error
|
||||||
|
#include <thread>
|
||||||
|
#include <chrono>
|
||||||
|
|
||||||
|
|
||||||
/////////////
|
/////////////
|
||||||
@ -42,6 +44,13 @@ const bool FULL_SCREEN = false;
|
|||||||
const float SCREEN_DEPTH = 1000.0f;
|
const float SCREEN_DEPTH = 1000.0f;
|
||||||
const float SCREEN_NEAR = 0.3f;
|
const float SCREEN_NEAR = 0.3f;
|
||||||
|
|
||||||
|
struct Input
|
||||||
|
{
|
||||||
|
bool m_KeyLeft = false;
|
||||||
|
bool m_KeyRight = false;
|
||||||
|
bool m_KeyUp = false;
|
||||||
|
bool m_KeyDown = false;
|
||||||
|
};
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
// Class name: ApplicationClass
|
// Class name: ApplicationClass
|
||||||
@ -50,13 +59,13 @@ class ApplicationClass
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
ApplicationClass();
|
ApplicationClass();
|
||||||
ApplicationClass(const ApplicationClass&);
|
|
||||||
~ApplicationClass();
|
~ApplicationClass();
|
||||||
D3DClass* GetDirect3D();
|
D3DClass* GetDirect3D();
|
||||||
|
|
||||||
bool Initialize(int, int, HWND);
|
bool Initialize(int, int, HWND);
|
||||||
void Shutdown();
|
void Shutdown();
|
||||||
bool Frame(InputClass*);
|
bool Frame(InputClass*);
|
||||||
|
void PhysicsThreadFunction();
|
||||||
|
|
||||||
int GetScreenWidth() const;
|
int GetScreenWidth() const;
|
||||||
void SetScreenWidth(int screenWidth);
|
void SetScreenWidth(int screenWidth);
|
||||||
@ -121,7 +130,7 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
bool Render(float, float, float, float, float);
|
bool Render(float, float, float, float, float);
|
||||||
bool RenderPhysics(float x, float y, float z);
|
bool RenderPhysics(bool keyLeft, bool keyRight, bool keyUp, bool keyDown, float deltaTime);
|
||||||
bool UpdateMouseStrings(int, int, bool);
|
bool UpdateMouseStrings(int, int, bool);
|
||||||
bool UpdateFps();
|
bool UpdateFps();
|
||||||
bool UpdateRenderCountString(int);
|
bool UpdateRenderCountString(int);
|
||||||
@ -146,6 +155,8 @@ private :
|
|||||||
HWND m_hwnd;
|
HWND m_hwnd;
|
||||||
bool m_windowed;
|
bool m_windowed;
|
||||||
|
|
||||||
|
int m_PhysicTickRate = 50; // physics execute 50 times per second
|
||||||
|
|
||||||
// ------------------------------------- //
|
// ------------------------------------- //
|
||||||
// ------------- RENDERING ------------- //
|
// ------------- RENDERING ------------- //
|
||||||
// ------------------------------------- //
|
// ------------------------------------- //
|
||||||
@ -227,6 +238,12 @@ private :
|
|||||||
Frustum m_FrustumCulling;
|
Frustum m_FrustumCulling;
|
||||||
int m_renderCount;
|
int m_renderCount;
|
||||||
float m_FrustumCullingTolerance = 5.f;
|
float m_FrustumCullingTolerance = 5.f;
|
||||||
|
|
||||||
|
// ------------------------------------------------- //
|
||||||
|
// -------------------- Input ---------------------- //
|
||||||
|
// ------------------------------------------------- //
|
||||||
|
|
||||||
|
Input m_Inputs;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
@ -254,6 +254,11 @@ void imguiManager::WidgetObjectWindow(ApplicationClass* app)
|
|||||||
object->SetActiveShader(Object::SPECULAR_MAPPING);
|
object->SetActiveShader(Object::SPECULAR_MAPPING);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ImGui::Separator();
|
||||||
|
|
||||||
|
// Physics
|
||||||
|
std::string physicsLabel = "Physics##" + std::to_string(index);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
ImGui::Separator();
|
ImGui::Separator();
|
||||||
|
@ -176,9 +176,9 @@ void Object::SetVelocity(XMVECTOR velocity)
|
|||||||
m_velocity = velocity;
|
m_velocity = velocity;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Object::AddVelocity(float frameTime)
|
void Object::AddVelocity(float deltaTime)
|
||||||
{
|
{
|
||||||
m_velocity += m_acceleration * frameTime;
|
m_velocity += m_acceleration * deltaTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
XMVECTOR Object::GetVelocity() const
|
XMVECTOR Object::GetVelocity() const
|
||||||
|
@ -3,6 +3,13 @@
|
|||||||
#include <WICTextureLoader.h>
|
#include <WICTextureLoader.h>
|
||||||
#include <SimpleMath.h>
|
#include <SimpleMath.h>
|
||||||
|
|
||||||
|
enum class ObjectType
|
||||||
|
{
|
||||||
|
Sphere,
|
||||||
|
Cube,
|
||||||
|
Unknown
|
||||||
|
};
|
||||||
|
|
||||||
class Object : public ModelClass
|
class Object : public ModelClass
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@ -30,7 +37,7 @@ public:
|
|||||||
XMVECTOR GetScale();
|
XMVECTOR GetScale();
|
||||||
|
|
||||||
void SetVelocity(XMVECTOR);
|
void SetVelocity(XMVECTOR);
|
||||||
void AddVelocity(float);
|
void AddVelocity(float deltaTime);
|
||||||
XMVECTOR GetVelocity() const;
|
XMVECTOR GetVelocity() const;
|
||||||
void SetAcceleration(XMVECTOR);
|
void SetAcceleration(XMVECTOR);
|
||||||
XMVECTOR GetAcceleration() const;
|
XMVECTOR GetAcceleration() const;
|
||||||
@ -54,6 +61,8 @@ public:
|
|||||||
void SetName(std::string name);
|
void SetName(std::string name);
|
||||||
int SetId(int id);
|
int SetId(int id);
|
||||||
int GetId() const;
|
int GetId() const;
|
||||||
|
void SetType(ObjectType type) { m_type = type; };
|
||||||
|
ObjectType GetType() const { return m_type; };
|
||||||
|
|
||||||
bool LoadTexture(ID3D11Device* device, ID3D11DeviceContext* deviceContext, const std::wstring& filename);
|
bool LoadTexture(ID3D11Device* device, ID3D11DeviceContext* deviceContext, const std::wstring& filename);
|
||||||
|
|
||||||
@ -88,9 +97,10 @@ private:
|
|||||||
XMVECTOR m_acceleration;
|
XMVECTOR m_acceleration;
|
||||||
float m_mass;
|
float m_mass;
|
||||||
bool m_isGrounded;
|
bool m_isGrounded;
|
||||||
bool m_isPhysicsEnabled;
|
bool m_isPhysicsEnabled = false;
|
||||||
|
|
||||||
std::string m_name;
|
std::string m_name;
|
||||||
|
ObjectType m_type = ObjectType::Unknown;
|
||||||
|
|
||||||
ShaderType m_activeShader = LIGHTING;
|
ShaderType m_activeShader = LIGHTING;
|
||||||
|
|
||||||
|
@ -79,21 +79,26 @@ void Physics::AddForce(Object* object, XMVECTOR force)
|
|||||||
|
|
||||||
bool Physics::IsColliding(Object* object1, Object* object2)
|
bool Physics::IsColliding(Object* object1, Object* object2)
|
||||||
{
|
{
|
||||||
|
ObjectType type1 = object1->GetType();
|
||||||
|
ObjectType type2 = object2->GetType();
|
||||||
|
|
||||||
std::string type1 = object1->GetName();
|
if (type1 == ObjectType::Unknown || type2 == ObjectType::Unknown)
|
||||||
std::string type2 = object2->GetName();
|
{
|
||||||
|
return false;
|
||||||
if (type1 == "sphere" && type2 == "sphere")
|
|
||||||
{
|
|
||||||
return SpheresOverlap(object1, object2);
|
|
||||||
}
|
}
|
||||||
if (type1 == "cube" && type2 == "sphere" || (type1 == "sphere" && type2 == "cube"))
|
|
||||||
|
if (type1 == ObjectType::Sphere && type2 == ObjectType::Sphere)
|
||||||
{
|
{
|
||||||
if (type1 == "cube")
|
return SpheresOverlap(object1, object2);
|
||||||
|
}
|
||||||
|
if ((type1 == ObjectType::Cube && type2 == ObjectType::Sphere) ||
|
||||||
|
(type1 == ObjectType::Sphere && type2 == ObjectType::Cube))
|
||||||
|
{
|
||||||
|
if (type1 == ObjectType::Cube)
|
||||||
{
|
{
|
||||||
return SphereCubeOverlap(object1, object2);
|
return SphereCubeOverlap(object1, object2);
|
||||||
}
|
}
|
||||||
else if (type1 == "sphere")
|
else if (type1 == ObjectType::Sphere)
|
||||||
{
|
{
|
||||||
return SphereCubeOverlap(object2, object1);
|
return SphereCubeOverlap(object2, object1);
|
||||||
}
|
}
|
||||||
@ -106,6 +111,7 @@ bool Physics::IsColliding(Object* object1, Object* object2)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/////////////////////////////////////////
|
/////////////////////////////////////////
|
||||||
// AABB method for collision detection //
|
// AABB method for collision detection //
|
||||||
/////////////////////////////////////////
|
/////////////////////////////////////////
|
||||||
|
Loading…
x
Reference in New Issue
Block a user