Kinda week 2
This commit is contained in:
@@ -34,7 +34,7 @@ namespace dae
|
|||||||
Matrix CalculateCameraToWorld()
|
Matrix CalculateCameraToWorld()
|
||||||
{
|
{
|
||||||
//todo: W2
|
//todo: W2
|
||||||
throw std::runtime_error("Not Implemented Yet");
|
// throw std::runtime_error("Not Implemented Yet");
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -103,9 +103,7 @@ namespace dae {
|
|||||||
|
|
||||||
Matrix Matrix::CreateTranslation(float x, float y, float z)
|
Matrix Matrix::CreateTranslation(float x, float y, float z)
|
||||||
{
|
{
|
||||||
//todo W2
|
return { Vector3::UnitX, Vector3::UnitY, Vector3::UnitZ, {x, y, z} };
|
||||||
throw std::runtime_error("Not Implemented Yet");
|
|
||||||
return {};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Matrix Matrix::CreateTranslation(const Vector3& t)
|
Matrix Matrix::CreateTranslation(const Vector3& t)
|
||||||
@@ -115,30 +113,37 @@ namespace dae {
|
|||||||
|
|
||||||
Matrix Matrix::CreateRotationX(float pitch)
|
Matrix Matrix::CreateRotationX(float pitch)
|
||||||
{
|
{
|
||||||
//todo W2
|
return {
|
||||||
throw std::runtime_error("Not Implemented Yet");
|
{1, 0, 0, 0},
|
||||||
return {};
|
{0, cosf(pitch), -sinf(pitch), 0},
|
||||||
|
{0, sinf(pitch), cosf(pitch), 0},
|
||||||
|
{0, 0, 0, 1}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
Matrix Matrix::CreateRotationY(float yaw)
|
Matrix Matrix::CreateRotationY(float yaw)
|
||||||
{
|
{
|
||||||
//todo W2
|
return {
|
||||||
throw std::runtime_error("Not Implemented Yet");
|
{cosf(yaw), 0, sinf(yaw), 0},
|
||||||
return {};
|
{0, 1, 0, 0},
|
||||||
|
{-sinf(yaw), 0, cosf(yaw), 0},
|
||||||
|
{0, 0, 0, 1}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
Matrix Matrix::CreateRotationZ(float roll)
|
Matrix Matrix::CreateRotationZ(float roll)
|
||||||
{
|
{
|
||||||
//todo W2
|
return {
|
||||||
throw std::runtime_error("Not Implemented Yet");
|
{cosf(roll), -sinf(roll), 0, 0},
|
||||||
return {};
|
{sinf(roll), cosf(roll), 0, 0},
|
||||||
|
{0, 0, 1, 0},
|
||||||
|
{0, 0, 0, 1}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
Matrix Matrix::CreateRotation(const Vector3& r)
|
Matrix Matrix::CreateRotation(const Vector3& r)
|
||||||
{
|
{
|
||||||
//todo W2
|
return CreateRotationX(r.x) * CreateRotationY(r.y) * CreateRotationZ(r.z);
|
||||||
throw std::runtime_error("Not Implemented Yet");
|
|
||||||
return {};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Matrix Matrix::CreateRotation(float pitch, float yaw, float roll)
|
Matrix Matrix::CreateRotation(float pitch, float yaw, float roll)
|
||||||
@@ -148,9 +153,12 @@ namespace dae {
|
|||||||
|
|
||||||
Matrix Matrix::CreateScale(float sx, float sy, float sz)
|
Matrix Matrix::CreateScale(float sx, float sy, float sz)
|
||||||
{
|
{
|
||||||
//todo W2
|
return {
|
||||||
throw std::runtime_error("Not Implemented Yet");
|
{sx, 0, 0, 0},
|
||||||
return {};
|
{0, sy, 0, 0},
|
||||||
|
{0, 0, sz, 0},
|
||||||
|
{0, 0, 0, 1}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
Matrix Matrix::CreateScale(const Vector3& s)
|
Matrix Matrix::CreateScale(const Vector3& s)
|
||||||
|
|||||||
@@ -14,44 +14,43 @@ using namespace dae;
|
|||||||
|
|
||||||
Renderer::Renderer(SDL_Window *pWindow) :
|
Renderer::Renderer(SDL_Window *pWindow) :
|
||||||
m_pWindow(pWindow),
|
m_pWindow(pWindow),
|
||||||
m_pBuffer(SDL_GetWindowSurface(pWindow))
|
m_pBuffer(SDL_GetWindowSurface(pWindow)) {
|
||||||
{
|
|
||||||
//Initialize
|
//Initialize
|
||||||
SDL_GetWindowSize(pWindow, &m_Width, &m_Height);
|
SDL_GetWindowSize(pWindow, &m_Width, &m_Height);
|
||||||
m_pBufferPixels = static_cast<uint32_t *>(m_pBuffer->pixels);
|
m_pBufferPixels = static_cast<uint32_t *>(m_pBuffer->pixels);
|
||||||
|
|
||||||
}
|
}
|
||||||
void Renderer::Render(Scene* pScene) const
|
|
||||||
{
|
void Renderer::Render(Scene *pScene) const {
|
||||||
Camera &camera = pScene->GetCamera();
|
Camera &camera = pScene->GetCamera();
|
||||||
auto &materials = pScene->GetMaterials();
|
auto &materials = pScene->GetMaterials();
|
||||||
auto &lights = pScene->GetLights();
|
auto &lights = pScene->GetLights();
|
||||||
|
|
||||||
float apsectRatio = static_cast<float>(m_Width) / m_Height;
|
float apsectRatio = static_cast<float>(m_Width) / m_Height;
|
||||||
|
float fov = tan((camera.fovAngle * (PI / 180.f)) / 2);
|
||||||
|
for (int px{}; px < m_Width; ++px) {
|
||||||
|
for (int py{}; py < m_Height; ++py) {
|
||||||
|
float NDCx = ((2 * ((px + 0.5) / m_Width) - 1) * apsectRatio) * fov;
|
||||||
|
float NDCy = (1 - 2 * ((py + 0.5) / m_Height)) * fov;
|
||||||
|
|
||||||
for (int px{}; px < m_Width; ++px)
|
|
||||||
{
|
|
||||||
for (int py{}; py < m_Height; ++py)
|
|
||||||
{
|
|
||||||
float NDCx = (2 * ((px + 0.5) / m_Width) - 1) * apsectRatio;
|
|
||||||
float NDCy = 1 - 2 * ((py + 0.5) / m_Height);
|
|
||||||
|
|
||||||
Vector3 rayDir = Vector3{NDCx, NDCy, 1};
|
Vector3 rayDir = Vector3{NDCx, NDCy, 1};
|
||||||
Ray ray{ Vector3(0, 0, 0), rayDir };
|
rayDir.Normalize();
|
||||||
|
Ray ray{camera.origin, rayDir};
|
||||||
|
|
||||||
//set the color based on the ray direction
|
//set the color based on the ray direction
|
||||||
ColorRGB finalColor{0.f, 0.f, 0.f};
|
ColorRGB finalColor{0.f, 0.f, 0.f};
|
||||||
|
|
||||||
HitRecord closestHit{};
|
HitRecord closestHit{};
|
||||||
|
Plane testPlane{{0.f, -50.f, 0.f}, {0.f, 1.f, 0.f}, 0};
|
||||||
|
// GeometryUtils::HitTest_Plane(testPlane, ray, closestHit);
|
||||||
pScene->GetClosestHit(ray, closestHit);
|
pScene->GetClosestHit(ray, closestHit);
|
||||||
|
|
||||||
if (closestHit.didHit) {
|
if (closestHit.didHit) {
|
||||||
finalColor = materials[closestHit.materialIndex]->Shade();
|
finalColor = materials[closestHit.materialIndex]->Shade();
|
||||||
// finalColor = ColorRGB(closestHit.normal.x, closestHit.normal.y, closestHit.normal.z);
|
|
||||||
// const float scaled_t = (closestHit.t - 50.f) / 40.f;
|
|
||||||
// finalColor = {scaled_t, scaled_t, scaled_t};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//Update Color in Buffer
|
|
||||||
finalColor.MaxToOne();
|
finalColor.MaxToOne();
|
||||||
|
|
||||||
m_pBufferPixels[px + (py * m_Width)] = SDL_MapRGB(m_pBuffer->format,
|
m_pBufferPixels[px + (py * m_Width)] = SDL_MapRGB(m_pBuffer->format,
|
||||||
@@ -67,7 +66,6 @@ void Renderer::Render(Scene* pScene) const
|
|||||||
SDL_UpdateWindowSurface(m_pWindow);
|
SDL_UpdateWindowSurface(m_pWindow);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Renderer::SaveBufferToImage() const
|
bool Renderer::SaveBufferToImage() const {
|
||||||
{
|
|
||||||
return SDL_SaveBMP(m_pBuffer, "RayTracing_Buffer.bmp");
|
return SDL_SaveBMP(m_pBuffer, "RayTracing_Buffer.bmp");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
#include "Camera.h"
|
||||||
|
|
||||||
struct SDL_Window;
|
struct SDL_Window;
|
||||||
struct SDL_Surface;
|
struct SDL_Surface;
|
||||||
@@ -31,5 +32,10 @@ namespace dae
|
|||||||
|
|
||||||
int m_Width{};
|
int m_Width{};
|
||||||
int m_Height{};
|
int m_Height{};
|
||||||
|
|
||||||
|
float m_FOV{};
|
||||||
|
|
||||||
|
Camera m_Camera{};
|
||||||
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,20 +5,18 @@
|
|||||||
namespace dae {
|
namespace dae {
|
||||||
|
|
||||||
#pragma region Base Scene
|
#pragma region Base Scene
|
||||||
|
|
||||||
//Initialize Scene with Default Solid Color Material (RED)
|
//Initialize Scene with Default Solid Color Material (RED)
|
||||||
Scene::Scene() :
|
Scene::Scene() :
|
||||||
m_Materials({ new Material_SolidColor({1,0,0}) })
|
m_Materials({new Material_SolidColor({1, 0, 0})}) {
|
||||||
{
|
|
||||||
m_SphereGeometries.reserve(32);
|
m_SphereGeometries.reserve(32);
|
||||||
m_PlaneGeometries.reserve(32);
|
m_PlaneGeometries.reserve(32);
|
||||||
m_TriangleMeshGeometries.reserve(32);
|
m_TriangleMeshGeometries.reserve(32);
|
||||||
m_Lights.reserve(32);
|
m_Lights.reserve(32);
|
||||||
}
|
}
|
||||||
|
|
||||||
Scene::~Scene()
|
Scene::~Scene() {
|
||||||
{
|
for (auto &pMaterial: m_Materials) {
|
||||||
for (auto& pMaterial : m_Materials)
|
|
||||||
{
|
|
||||||
delete pMaterial;
|
delete pMaterial;
|
||||||
pMaterial = nullptr;
|
pMaterial = nullptr;
|
||||||
}
|
}
|
||||||
@@ -26,35 +24,37 @@ namespace dae {
|
|||||||
m_Materials.clear();
|
m_Materials.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void dae::Scene::GetClosestHit(const Ray& ray, HitRecord& closestHit) const
|
void dae::Scene::GetClosestHit(const Ray &ray, HitRecord &closestHit) const {
|
||||||
{
|
|
||||||
closestHit.t = FLT_MAX;
|
closestHit.t = FLT_MAX;
|
||||||
|
|
||||||
for (const Sphere& sphere : m_SphereGeometries){
|
|
||||||
HitRecord hitRecord{};
|
|
||||||
if (GeometryUtils::HitTest_Sphere(sphere, ray, hitRecord) && hitRecord.t < closestHit.t){
|
|
||||||
closestHit = hitRecord;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const Plane &plane: m_PlaneGeometries) {
|
for (const Plane &plane: m_PlaneGeometries) {
|
||||||
HitRecord hitRecord{};
|
HitRecord hitRecord{};
|
||||||
if(GeometryUtils::HitTest_Plane(plane, ray, hitRecord) && hitRecord.t < closestHit.t){
|
GeometryUtils::HitTest_Plane(plane, ray, hitRecord);
|
||||||
|
if (hitRecord.t < closestHit.t && hitRecord.didHit) {
|
||||||
closestHit = hitRecord;
|
closestHit = hitRecord;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (const Sphere &sphere: m_SphereGeometries) {
|
||||||
|
HitRecord hitRecord{};
|
||||||
|
GeometryUtils::HitTest_Sphere(sphere, ray, hitRecord);
|
||||||
|
if (hitRecord.t < closestHit.t && hitRecord.didHit) {
|
||||||
|
closestHit = hitRecord;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Scene::DoesHit(const Ray& ray) const
|
|
||||||
{
|
}
|
||||||
|
|
||||||
|
bool Scene::DoesHit(const Ray &ray) const {
|
||||||
//todo W2
|
//todo W2
|
||||||
throw std::runtime_error("Not Implemented Yet");
|
throw std::runtime_error("Not Implemented Yet");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma region Scene Helpers
|
#pragma region Scene Helpers
|
||||||
Sphere* Scene::AddSphere(const Vector3& origin, float radius, unsigned char materialIndex)
|
|
||||||
{
|
Sphere *Scene::AddSphere(const Vector3 &origin, float radius, unsigned char materialIndex) {
|
||||||
Sphere s;
|
Sphere s;
|
||||||
s.origin = origin;
|
s.origin = origin;
|
||||||
s.radius = radius;
|
s.radius = radius;
|
||||||
@@ -64,8 +64,7 @@ namespace dae {
|
|||||||
return &m_SphereGeometries.back();
|
return &m_SphereGeometries.back();
|
||||||
}
|
}
|
||||||
|
|
||||||
Plane* Scene::AddPlane(const Vector3& origin, const Vector3& normal, unsigned char materialIndex)
|
Plane *Scene::AddPlane(const Vector3 &origin, const Vector3 &normal, unsigned char materialIndex) {
|
||||||
{
|
|
||||||
Plane p;
|
Plane p;
|
||||||
p.origin = origin;
|
p.origin = origin;
|
||||||
p.normal = normal;
|
p.normal = normal;
|
||||||
@@ -75,8 +74,7 @@ namespace dae {
|
|||||||
return &m_PlaneGeometries.back();
|
return &m_PlaneGeometries.back();
|
||||||
}
|
}
|
||||||
|
|
||||||
TriangleMesh* Scene::AddTriangleMesh(TriangleCullMode cullMode, unsigned char materialIndex)
|
TriangleMesh *Scene::AddTriangleMesh(TriangleCullMode cullMode, unsigned char materialIndex) {
|
||||||
{
|
|
||||||
TriangleMesh m{};
|
TriangleMesh m{};
|
||||||
m.cullMode = cullMode;
|
m.cullMode = cullMode;
|
||||||
m.materialIndex = materialIndex;
|
m.materialIndex = materialIndex;
|
||||||
@@ -85,8 +83,7 @@ namespace dae {
|
|||||||
return &m_TriangleMeshGeometries.back();
|
return &m_TriangleMeshGeometries.back();
|
||||||
}
|
}
|
||||||
|
|
||||||
Light* Scene::AddPointLight(const Vector3& origin, float intensity, const ColorRGB& color)
|
Light *Scene::AddPointLight(const Vector3 &origin, float intensity, const ColorRGB &color) {
|
||||||
{
|
|
||||||
Light l;
|
Light l;
|
||||||
l.origin = origin;
|
l.origin = origin;
|
||||||
l.intensity = intensity;
|
l.intensity = intensity;
|
||||||
@@ -97,8 +94,7 @@ namespace dae {
|
|||||||
return &m_Lights.back();
|
return &m_Lights.back();
|
||||||
}
|
}
|
||||||
|
|
||||||
Light* Scene::AddDirectionalLight(const Vector3& direction, float intensity, const ColorRGB& color)
|
Light *Scene::AddDirectionalLight(const Vector3 &direction, float intensity, const ColorRGB &color) {
|
||||||
{
|
|
||||||
Light l;
|
Light l;
|
||||||
l.direction = direction;
|
l.direction = direction;
|
||||||
l.intensity = intensity;
|
l.intensity = intensity;
|
||||||
@@ -109,17 +105,17 @@ namespace dae {
|
|||||||
return &m_Lights.back();
|
return &m_Lights.back();
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned char Scene::AddMaterial(Material* pMaterial)
|
unsigned char Scene::AddMaterial(Material *pMaterial) {
|
||||||
{
|
|
||||||
m_Materials.push_back(pMaterial);
|
m_Materials.push_back(pMaterial);
|
||||||
return static_cast<unsigned char>(m_Materials.size() - 1);
|
return static_cast<unsigned char>(m_Materials.size() - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma endregion
|
#pragma endregion
|
||||||
#pragma endregion
|
#pragma endregion
|
||||||
|
|
||||||
#pragma region SCENE W1
|
#pragma region SCENE W1
|
||||||
void Scene_W1::Initialize()
|
|
||||||
{
|
void Scene_W1::Initialize() {
|
||||||
//default: Material id0 >> SolidColor Material (RED)
|
//default: Material id0 >> SolidColor Material (RED)
|
||||||
constexpr unsigned char matId_Solid_Red = 0;
|
constexpr unsigned char matId_Solid_Red = 0;
|
||||||
const unsigned char matId_Solid_Blue = AddMaterial(new Material_SolidColor{colors::Blue});
|
const unsigned char matId_Solid_Blue = AddMaterial(new Material_SolidColor{colors::Blue});
|
||||||
@@ -139,5 +135,35 @@ namespace dae {
|
|||||||
AddPlane({0.f, 75.f, 0.f}, {0.f, -1.f, 0.f}, matId_Solid_Yellow);
|
AddPlane({0.f, 75.f, 0.f}, {0.f, -1.f, 0.f}, matId_Solid_Yellow);
|
||||||
AddPlane({0.f, 0.f, 125.f}, {0.f, 0.f, -1.f}, matId_Solid_Magenta);
|
AddPlane({0.f, 0.f, 125.f}, {0.f, 0.f, -1.f}, matId_Solid_Magenta);
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma endregion
|
#pragma endregion
|
||||||
|
|
||||||
|
void Scene_W2::Initialize() {
|
||||||
|
m_Camera.origin += {0.f, 3.f, -9.f};
|
||||||
|
m_Camera.fovAngle = 45.f;
|
||||||
|
|
||||||
|
constexpr unsigned char matId_Solid_Red = 0;
|
||||||
|
const unsigned char matId_Solid_Blue = AddMaterial(new Material_SolidColor{colors::Blue});
|
||||||
|
|
||||||
|
const unsigned char matId_Solid_Yellow = AddMaterial(new Material_SolidColor{colors::Yellow});
|
||||||
|
const unsigned char matId_Solid_Green = AddMaterial(new Material_SolidColor{colors::Green});
|
||||||
|
const unsigned char matId_Solid_Magenta = AddMaterial(new Material_SolidColor{colors::Magenta});
|
||||||
|
|
||||||
|
//Plane
|
||||||
|
AddPlane({-5.f, 0.f, 0.f}, {1.f, 0.f, 0.f}, matId_Solid_Green);
|
||||||
|
AddPlane({5.f, 0.f, 0.f}, {-1.f, 0.f, 0.f}, matId_Solid_Green);
|
||||||
|
AddPlane({0.f, 0.f, 0.f}, {0.f, 1.f, 0.f}, matId_Solid_Yellow);
|
||||||
|
AddPlane({0.f, 10.f, 0.f}, {0.f, -1.f, 0.f}, matId_Solid_Yellow);
|
||||||
|
AddPlane({0.f, 0.f, 10.f}, {0.f, 0.f, -1.f}, matId_Solid_Magenta);
|
||||||
|
|
||||||
|
//Spheres
|
||||||
|
AddSphere({-1.75f, 1.f, 0.f}, 0.75f, matId_Solid_Red);
|
||||||
|
AddSphere({0.f, 1.f, 0.f}, 0.75f, matId_Solid_Blue);
|
||||||
|
AddSphere({1.75f, 1.f, 0.f}, 0.75f, matId_Solid_Red);
|
||||||
|
AddSphere({-1.75f, 3.f, 0.f}, 0.75f, matId_Solid_Blue);
|
||||||
|
AddSphere({0.f, 3.f, 0.f}, 0.75f, matId_Solid_Red);
|
||||||
|
AddSphere({1.75f, 3.f, 0.f}, 0.75f, matId_Solid_Blue);
|
||||||
|
|
||||||
|
AddPointLight({0.f, 5.f, -5.f}, 70.f, colors::White);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -75,6 +75,22 @@ namespace dae
|
|||||||
Scene_W1& operator=(const Scene_W1&) = delete;
|
Scene_W1& operator=(const Scene_W1&) = delete;
|
||||||
Scene_W1& operator=(Scene_W1&&) noexcept = delete;
|
Scene_W1& operator=(Scene_W1&&) noexcept = delete;
|
||||||
|
|
||||||
|
void Initialize() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
//+++++++++++++++++++++++++++++++++++++++++
|
||||||
|
//WEEK 1 Test Scene
|
||||||
|
class Scene_W2 final : public Scene
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Scene_W2() = default;
|
||||||
|
~Scene_W2() override = default;
|
||||||
|
|
||||||
|
Scene_W2(const Scene_W1&) = delete;
|
||||||
|
Scene_W2(Scene_W1&&) noexcept = delete;
|
||||||
|
Scene_W2& operator=(const Scene_W1&) = delete;
|
||||||
|
Scene_W2& operator=(Scene_W1&&) noexcept = delete;
|
||||||
|
|
||||||
void Initialize() override;
|
void Initialize() override;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,8 +53,8 @@ namespace dae
|
|||||||
{
|
{
|
||||||
float dot = Vector3::Dot(plane.normal, ray.direction);
|
float dot = Vector3::Dot(plane.normal, ray.direction);
|
||||||
if(dot < 0) {
|
if(dot < 0) {
|
||||||
float t = Vector3::Dot(plane.origin - ray.origin, plane.normal) / dot;
|
// float t = Vector3::Dot(plane.origin - ray.origin, plane.normal) / dot;
|
||||||
|
float t = Vector3::Dot(plane.origin - ray.origin, plane.normal) / Vector3::Dot(ray.direction, plane.normal);
|
||||||
bool hit = t >= 0;
|
bool hit = t >= 0;
|
||||||
|
|
||||||
if(hit && !ignoreHitRecord){
|
if(hit && !ignoreHitRecord){
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ int main(int argc, char* args[])
|
|||||||
const auto pTimer = new Timer();
|
const auto pTimer = new Timer();
|
||||||
const auto pRenderer = new Renderer(pWindow);
|
const auto pRenderer = new Renderer(pWindow);
|
||||||
|
|
||||||
const auto pScene = new Scene_W1();
|
const auto pScene = new Scene_W2();
|
||||||
pScene->Initialize();
|
pScene->Initialize();
|
||||||
|
|
||||||
//Start loop
|
//Start loop
|
||||||
|
|||||||
Reference in New Issue
Block a user