Final Commit for now
This adds model support, AABB, and other optimisations
This commit is contained in:
10
.gitignore
vendored
10
.gitignore
vendored
@@ -18,6 +18,9 @@ bld/
|
||||
# Visual Studio
|
||||
.vs/
|
||||
|
||||
# Clion
|
||||
.idea
|
||||
|
||||
# Do not ignore libs
|
||||
!project/libs/**
|
||||
|
||||
@@ -27,4 +30,9 @@ bld/
|
||||
*.ipr
|
||||
*.iws
|
||||
*.iws.*
|
||||
cmake-build-debug/**
|
||||
cmake-build-debug/**
|
||||
|
||||
cmake-build-default/**
|
||||
cmake-build-minsizerel/**
|
||||
cmake-build-relwithdebinfo/**
|
||||
cmake-build-release/**
|
||||
@@ -85,9 +85,17 @@ namespace dae
|
||||
Matrix translationTransform{};
|
||||
Matrix scaleTransform{};
|
||||
|
||||
Vector3 minAABB{};
|
||||
Vector3 maxAABB{};
|
||||
|
||||
Vector3 transformedMinAABB{};
|
||||
Vector3 transformedMaxAABB{};
|
||||
|
||||
std::vector<Vector3> transformedPositions{};
|
||||
std::vector<Vector3> transformedNormals{};
|
||||
|
||||
|
||||
|
||||
void Translate(const Vector3& translation)
|
||||
{
|
||||
translationTransform = Matrix::CreateTranslation(translation);
|
||||
@@ -124,21 +132,107 @@ namespace dae
|
||||
|
||||
void CalculateNormals()
|
||||
{
|
||||
throw std::runtime_error("Not Implemented Yet");
|
||||
}
|
||||
constexpr int triSides{ 3 };
|
||||
const size_t nTris{ indices.size() / triSides };
|
||||
normals.resize(nTris);
|
||||
|
||||
for(size_t triIndex{}; triIndex < nTris; ++triIndex)
|
||||
{
|
||||
const Vector3& v0{ positions[indices[triIndex * triSides + 0]] };
|
||||
const Vector3& v1{ positions[indices[triIndex * triSides + 1]] };
|
||||
const Vector3& v2{ positions[indices[triIndex * triSides + 2]] };
|
||||
|
||||
const Vector3 v0v1{ v1 - v0 };
|
||||
const Vector3 v0v2{ v2 - v0 };
|
||||
|
||||
const Vector3 normal{ Vector3::Cross(v0v1, v0v2).Normalized() };
|
||||
|
||||
normals[triIndex] = normal;
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateTransforms()
|
||||
{
|
||||
throw std::runtime_error("Not Implemented Yet");
|
||||
//Calculate Final Transform
|
||||
//const auto finalTransform = ...
|
||||
const size_t vertexCount = positions.size();
|
||||
const size_t normalCount = normals.size();
|
||||
const Matrix finalTransform{ scaleTransform * rotationTransform * translationTransform };
|
||||
transformedPositions.resize(vertexCount);
|
||||
transformedNormals.resize(normalCount);
|
||||
|
||||
//Transform Positions (positions > transformedPositions)
|
||||
//...
|
||||
|
||||
//Transform Normals (normals > transformedNormals)
|
||||
//...
|
||||
for (size_t i = 0; i < vertexCount; ++i)
|
||||
{
|
||||
transformedPositions[i] = finalTransform.TransformPoint(positions[i]);
|
||||
}
|
||||
for(size_t i = 0; i < normalCount; ++i){
|
||||
transformedNormals[i] = finalTransform.TransformVector(normals[i]);
|
||||
}
|
||||
|
||||
UpdateTransformedAABB(finalTransform);
|
||||
}
|
||||
|
||||
void UpdateAABB(){
|
||||
if(positions.size() > 0){
|
||||
minAABB = positions[0];
|
||||
maxAABB = positions[0];
|
||||
for (auto& p : positions)
|
||||
{
|
||||
minAABB = Vector3::Min(minAABB, p);
|
||||
maxAABB = Vector3::Max(maxAABB, p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateTransformedAABB(const Matrix& finalTransform) {
|
||||
// AABB update: be careful -> transform the 8 vertices of the aabb
|
||||
// and calculate new min and max.
|
||||
Vector3 tMinAABB = finalTransform.TransformPoint(minAABB);
|
||||
Vector3 tMaxAABB = tMinAABB;
|
||||
|
||||
// (xmax, ymin, zmin)
|
||||
Vector3 tAABB = finalTransform.TransformPoint(maxAABB.x, minAABB.y, minAABB.z);
|
||||
tMinAABB = Vector3::Min(tAABB, tMinAABB);
|
||||
tMaxAABB = Vector3::Max(tAABB, tMaxAABB);
|
||||
|
||||
// (xmax, ymin, zmax)
|
||||
tAABB = finalTransform.TransformPoint(maxAABB.x, minAABB.y, maxAABB.z);
|
||||
tMinAABB = Vector3::Min(tAABB, tMinAABB);
|
||||
tMaxAABB = Vector3::Max(tAABB, tMaxAABB);
|
||||
|
||||
// (xmin, ymax, zmax)
|
||||
tAABB = finalTransform.TransformPoint(minAABB.x, maxAABB.y, maxAABB.z);
|
||||
tMinAABB = Vector3::Min(tAABB, tMinAABB);
|
||||
tMaxAABB = Vector3::Max(tAABB, tMaxAABB);
|
||||
|
||||
// (xmin, ymin, zmax)
|
||||
tAABB = finalTransform.TransformPoint(minAABB.x, minAABB.y, maxAABB.z);
|
||||
tMinAABB = Vector3::Min(tAABB, tMinAABB);
|
||||
tMaxAABB = Vector3::Max(tAABB, tMaxAABB);
|
||||
|
||||
// (xmax, ymax, zmin)
|
||||
tAABB = finalTransform.TransformPoint(maxAABB.x, maxAABB.y, minAABB.z);
|
||||
tMinAABB = Vector3::Min(tAABB, tMinAABB);
|
||||
tMaxAABB = Vector3::Max(tAABB, tMaxAABB);
|
||||
|
||||
// (xmin, ymax, zmin)
|
||||
tAABB = finalTransform.TransformPoint(minAABB.x, maxAABB.y, minAABB.z);
|
||||
tMinAABB = Vector3::Min(tAABB, tMinAABB);
|
||||
tMaxAABB = Vector3::Max(tAABB, tMaxAABB);
|
||||
|
||||
// (xmax, ymax, zmax)
|
||||
tAABB = finalTransform.TransformPoint(maxAABB.x, maxAABB.y, maxAABB.z);
|
||||
tMinAABB = Vector3::Min(tAABB, tMinAABB);
|
||||
tMaxAABB = Vector3::Max(tAABB, tMaxAABB);
|
||||
|
||||
// (xmin, ymin, zmax)
|
||||
tAABB = finalTransform.TransformPoint(minAABB.x, minAABB.y, maxAABB.z);
|
||||
tMinAABB = Vector3::Min(tAABB, tMinAABB);
|
||||
tMaxAABB = Vector3::Max(tAABB, tMaxAABB);
|
||||
|
||||
transformedMinAABB = tMinAABB;
|
||||
transformedMaxAABB = tMaxAABB;
|
||||
}
|
||||
|
||||
};
|
||||
#pragma endregion
|
||||
#pragma region LIGHT
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
#include <iostream>
|
||||
#include "SDL.h"
|
||||
#include "SDL_surface.h"
|
||||
#include <algorithm>
|
||||
#include <execution>
|
||||
|
||||
//Project includes
|
||||
#include "Renderer.h"
|
||||
@@ -21,10 +23,10 @@ Renderer::Renderer(SDL_Window *pWindow) :
|
||||
m_pBufferPixels = static_cast<uint32_t *>(m_pBuffer->pixels);
|
||||
|
||||
m_xvalues.reserve(m_Width);
|
||||
for (uint16_t x{}; x < m_Width; ++x) {
|
||||
for(uint16_t x{}; x < m_Width; ++x)
|
||||
{
|
||||
m_xvalues.push_back(x);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void Renderer::Render(Scene *pScene) const {
|
||||
@@ -36,168 +38,17 @@ void Renderer::Render(Scene *pScene) const {
|
||||
|
||||
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 += 1) {
|
||||
for (int py{}; py < m_Height; py += 1) {
|
||||
|
||||
int actualPx = px;
|
||||
// if (py % 2 != 0) {
|
||||
// actualPx = px + 1;
|
||||
// }
|
||||
|
||||
float NDCx = ((2 * ((actualPx + 0.5) / m_Width) - 1) * apsectRatio) * fov;
|
||||
float NDCy = (1 - 2 * ((py + 0.5) / m_Height)) * fov;
|
||||
|
||||
|
||||
Vector3 rayDir = Vector3{NDCx, NDCy, 1};
|
||||
rayDir = CameraToWorld.TransformVector((rayDir));
|
||||
rayDir.Normalize();
|
||||
|
||||
Ray ray{camera.origin, rayDir};
|
||||
|
||||
|
||||
//set the color based on the ray direction
|
||||
ColorRGB finalColor{0.f, 0.f, 0.f};
|
||||
|
||||
HitRecord closestHit{};
|
||||
pScene->GetClosestHit(ray, closestHit);
|
||||
|
||||
|
||||
if (closestHit.didHit) {
|
||||
for (const Light &light: lights) {
|
||||
Vector3 lightDirection{LightUtils::GetDirectionToLight(light, closestHit.origin)};
|
||||
float rayMaxValue = lightDirection.Normalize();
|
||||
Ray lightRay = Ray{closestHit.origin + closestHit.normal * 0.01f, lightDirection, 0.0001f,
|
||||
rayMaxValue};
|
||||
bool doesLightHit = pScene->DoesHit(lightRay);
|
||||
|
||||
|
||||
if (m_CurrentLightingMode == LightingMode::Radiance) {
|
||||
if (!m_ShadowsEnabled or (m_ShadowsEnabled and !doesLightHit)) {
|
||||
auto radiance = LightUtils::GetRadiance(light, closestHit.origin);
|
||||
finalColor += radiance;
|
||||
}
|
||||
|
||||
|
||||
} else if (m_CurrentLightingMode == LightingMode::ObservedArea) {
|
||||
if (!m_ShadowsEnabled or (m_ShadowsEnabled and !doesLightHit)) {
|
||||
// float observedArea = std::clamp(
|
||||
// LightUtils::LambertCosineLaw(closestHit.normal, lightDirection), 0.0f, 1.0f);
|
||||
// finalColor.r += observedArea;
|
||||
// finalColor.g += observedArea;
|
||||
// finalColor.b += observedArea;
|
||||
}
|
||||
} else if (m_CurrentLightingMode == LightingMode::Combined) {
|
||||
if (!m_ShadowsEnabled or (m_ShadowsEnabled and !doesLightHit)) {
|
||||
auto radiance = LightUtils::GetRadiance(light, closestHit.origin);
|
||||
float observedArea = std::clamp(
|
||||
LightUtils::LambertCosineLaw(closestHit.normal, lightDirection), 0.0f, 1.0f);
|
||||
|
||||
finalColor += radiance * observedArea *
|
||||
materials[closestHit.materialIndex]->Shade(closestHit, lightDirection,
|
||||
rayDir);
|
||||
}
|
||||
} else if (m_CurrentLightingMode == LightingMode::BRDF) {
|
||||
if (!m_ShadowsEnabled or (m_ShadowsEnabled and !doesLightHit)) {
|
||||
auto radiance = LightUtils::GetRadiance(light, closestHit.origin);
|
||||
float observedArea = std::clamp(
|
||||
LightUtils::LambertCosineLaw(closestHit.normal, lightDirection), 0.0f, 1.0f);
|
||||
|
||||
finalColor += materials[closestHit.materialIndex]->Shade(closestHit, lightDirection, ray.direction);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// if (m_CurrentLightingMode == LightingMode::ObservedArea) {
|
||||
// if (closestHit.didHit) {
|
||||
// for (const Light &light: lights) {
|
||||
// Vector3 lightDirection{LightUtils::GetDirectionToLight(light, closestHit.origin)};
|
||||
// const float lightDistance = lightDirection.Normalize();
|
||||
//
|
||||
// float law = std::clamp(LightUtils::LambertCosineLaw(closestHit.normal, lightDirection), 0.0f, 1.0f);
|
||||
//// finalColor += materials[closestHit.materialIndex]->Shade() * law;
|
||||
// finalColor += ColorRGB{law, law, law};
|
||||
//
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// if (m_ShadowsEnabled) {
|
||||
//
|
||||
// if (closestHit.didHit) {
|
||||
//// finalColor = materials[closestHit.materialIndex]->Shade();
|
||||
// if (m_ShadowsEnabled) {
|
||||
// for (const Light &light: lights) {
|
||||
// Vector3 lightDirection{LightUtils::GetDirectionToLight(light, closestHit.origin)};
|
||||
// const float lightDistance = lightDirection.Normalize();
|
||||
//
|
||||
// const Ray lightRay{
|
||||
// closestHit.origin + closestHit.normal * 0.01f,
|
||||
// lightDirection, 0.0001f, lightDistance
|
||||
// };
|
||||
//
|
||||
// if (pScene->DoesHit(lightRay)) {
|
||||
// finalColor *= 0.5;
|
||||
// continue;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
finalColor.MaxToOne();
|
||||
|
||||
m_pBufferPixels[actualPx + (py * m_Width)] = SDL_MapRGB(m_pBuffer->format,
|
||||
static_cast<uint8_t>(finalColor.r * 255),
|
||||
static_cast<uint8_t>(finalColor.g * 255),
|
||||
static_cast<uint8_t>(finalColor.b * 255));
|
||||
|
||||
}
|
||||
uint32_t amountOfPixels = m_Width * m_Height;
|
||||
std::vector<uint32_t> pixelIndices{};
|
||||
pixelIndices.reserve(amountOfPixels);
|
||||
for (uint32_t i{}; i < amountOfPixels; ++i) {
|
||||
pixelIndices.push_back(i);
|
||||
}
|
||||
|
||||
// go over the pixels and avg the pixels i skipped
|
||||
// for (int px2{1}; px2 < m_Width; px2 += 2) {
|
||||
// for (int py{1}; py < m_Height; py += 1) {
|
||||
// if (px2 == m_Width - 1 || py == m_Height - 1) continue;
|
||||
// if (px2 == 0 || py == 0) continue;
|
||||
//
|
||||
// int px = px2;
|
||||
// if (py % 2 != 0) {
|
||||
// px = px2 + 1;
|
||||
// }
|
||||
//
|
||||
//
|
||||
// uint8_t r = 0;
|
||||
// uint8_t g = 0;
|
||||
// uint8_t b = 0;
|
||||
// SDL_GetRGB(m_pBufferPixels[px + (py * m_Width)], m_pBuffer->format, &r, &g, &b);
|
||||
// ColorRGB currentColor = ColorRGB{r / 255.f, g / 255.f, b / 255.f};
|
||||
//
|
||||
// SDL_GetRGB(m_pBufferPixels[(px - 1) + (py * m_Width)], m_pBuffer->format, &r, &g, &b);
|
||||
// ColorRGB leftColor = ColorRGB{r / 255.f, g / 255.f, b / 255.f};
|
||||
//
|
||||
// SDL_GetRGB(m_pBufferPixels[(px + 1) + (py * m_Width)], m_pBuffer->format, &r, &g, &b);
|
||||
// ColorRGB rightColor = ColorRGB{r / 255.f, g / 255.f, b / 255.f};
|
||||
//
|
||||
// SDL_GetRGB(m_pBufferPixels[px + ((py - 1) * m_Width)], m_pBuffer->format, &r, &g, &b);
|
||||
// ColorRGB upColor = ColorRGB{r / 255.f, g / 255.f, b / 255.f};
|
||||
//
|
||||
// SDL_GetRGB(m_pBufferPixels[px + ((py + 1) * m_Width)], m_pBuffer->format, &r, &g, &b);
|
||||
// ColorRGB downColor = ColorRGB{r / 255.f, g / 255.f, b / 255.f};
|
||||
//
|
||||
// float avgR = (leftColor.r + rightColor.r + upColor.r + downColor.r) / 4;
|
||||
// float avgG = (leftColor.g + rightColor.g + upColor.g + downColor.g) / 4;
|
||||
// float avgB = (leftColor.b + rightColor.b + upColor.b + downColor.b) / 4;
|
||||
// ColorRGB avgColor = ColorRGB{avgR, avgG, avgB};
|
||||
//
|
||||
// m_pBufferPixels[px + (py * m_Width)] = SDL_MapRGB(m_pBuffer->format,
|
||||
// static_cast<uint8_t>(avgColor.r * 255),
|
||||
// static_cast<uint8_t>(avgColor.g * 255),
|
||||
// static_cast<uint8_t>(avgColor.b * 255));
|
||||
// }
|
||||
// }
|
||||
std::for_each(std::execution::par, pixelIndices.begin(), pixelIndices.end(), [&](uint32_t& pixelIndex) {
|
||||
this->RenderPixel(pScene, pixelIndex, fov, apsectRatio, CameraToWorld, camera.origin);
|
||||
});
|
||||
|
||||
//@END
|
||||
//Update SDL Surface
|
||||
@@ -266,3 +117,85 @@ void Renderer::CycleLightingMode() {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void Renderer::RenderPixel(Scene *pScene, uint32_t pixelIndex, float fov, float aspectRatio, const Matrix cameraToWorld,
|
||||
const Vector3 &cameraPosition) const {
|
||||
|
||||
auto materials = pScene->GetMaterials();
|
||||
auto lights = pScene->GetLights();
|
||||
|
||||
const uint32_t px{ pixelIndex % m_Width}, py{ pixelIndex / m_Width };
|
||||
|
||||
float NDCx{px + 0.5f }, NDCy{py + 0.5f };
|
||||
float cx{(2.f * NDCx / m_Width - 1.f) * aspectRatio * fov };
|
||||
float cy{(1.f - 2.f * NDCy / m_Height) * fov };
|
||||
|
||||
Vector3 rayDir{ cx, cy, 1.f };
|
||||
rayDir = cameraToWorld.TransformVector((rayDir));
|
||||
rayDir.Normalize();
|
||||
|
||||
Ray ray{cameraPosition, rayDir};
|
||||
|
||||
|
||||
//set the color based on the ray direction
|
||||
ColorRGB finalColor{0.f, 0.f, 0.f};
|
||||
|
||||
HitRecord closestHit{};
|
||||
pScene->GetClosestHit(ray, closestHit);
|
||||
|
||||
|
||||
if (closestHit.didHit) {
|
||||
for (const Light &light: lights) {
|
||||
Vector3 lightDirection{LightUtils::GetDirectionToLight(light, closestHit.origin)};
|
||||
float rayMaxValue = lightDirection.Normalize();
|
||||
Ray lightRay = Ray{closestHit.origin + closestHit.normal * 0.01f, lightDirection, 0.0001f,
|
||||
rayMaxValue};
|
||||
bool doesLightHit = pScene->DoesHit(lightRay);
|
||||
|
||||
|
||||
if (m_CurrentLightingMode == LightingMode::Radiance) {
|
||||
if (!m_ShadowsEnabled or (m_ShadowsEnabled and !doesLightHit)) {
|
||||
auto radiance = LightUtils::GetRadiance(light, closestHit.origin);
|
||||
finalColor += radiance;
|
||||
}
|
||||
|
||||
|
||||
} else if (m_CurrentLightingMode == LightingMode::ObservedArea) {
|
||||
if (!m_ShadowsEnabled or (m_ShadowsEnabled and !doesLightHit)) {
|
||||
float observedArea = std::clamp(
|
||||
LightUtils::LambertCosineLaw(closestHit.normal, lightDirection), 0.0f, 1.0f);
|
||||
finalColor.r += observedArea;
|
||||
finalColor.g += observedArea;
|
||||
finalColor.b += observedArea;
|
||||
}
|
||||
} else if (m_CurrentLightingMode == LightingMode::Combined) {
|
||||
if (!m_ShadowsEnabled or (m_ShadowsEnabled and !doesLightHit)) {
|
||||
auto radiance = LightUtils::GetRadiance(light, closestHit.origin);
|
||||
float observedArea = std::clamp(
|
||||
LightUtils::LambertCosineLaw(closestHit.normal, lightDirection), 0.0f, 1.0f);
|
||||
|
||||
finalColor += radiance * observedArea *
|
||||
materials[closestHit.materialIndex]->Shade(closestHit, lightDirection,
|
||||
rayDir);
|
||||
}
|
||||
} else if (m_CurrentLightingMode == LightingMode::BRDF) {
|
||||
if (!m_ShadowsEnabled or (m_ShadowsEnabled and !doesLightHit)) {
|
||||
|
||||
finalColor += materials[closestHit.materialIndex]->Shade(closestHit, lightDirection,
|
||||
ray.direction);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
finalColor.MaxToOne();
|
||||
|
||||
m_pBufferPixels[pixelIndex] = SDL_MapRGB(m_pBuffer->format,
|
||||
static_cast<uint8_t>(finalColor.r * 255),
|
||||
static_cast<uint8_t>(finalColor.g * 255),
|
||||
static_cast<uint8_t>(finalColor.b * 255));
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -23,6 +23,9 @@ namespace dae
|
||||
Renderer& operator=(Renderer&&) noexcept = delete;
|
||||
|
||||
void Render(Scene* pScene) const;
|
||||
|
||||
void RenderPixel(Scene* pScene, uint32_t pixelIndex, float fov, float aspectRatio, const Matrix cameraToWorld, const Vector3& cameraPosition) const;
|
||||
|
||||
bool SaveBufferToImage() const;
|
||||
|
||||
void CycleLightingMode();
|
||||
|
||||
@@ -25,7 +25,7 @@ namespace dae {
|
||||
}
|
||||
|
||||
void dae::Scene::GetClosestHit(const Ray &ray, HitRecord &closestHit) const {
|
||||
closestHit.t = FLT_MAX;
|
||||
closestHit.t = std::numeric_limits<float>::max();
|
||||
|
||||
for (const Plane &plane: m_PlaneGeometries) {
|
||||
HitRecord hitRecord{};
|
||||
@@ -43,6 +43,21 @@ namespace dae {
|
||||
}
|
||||
}
|
||||
|
||||
for(const TriangleMesh& mesh : m_TriangleMeshGeometries)
|
||||
{
|
||||
HitRecord hit{};
|
||||
GeometryUtils::HitTest_TriangleMesh(mesh, ray, hit);
|
||||
if(hit.t < closestHit.t)
|
||||
closestHit = hit;
|
||||
}
|
||||
|
||||
// for(const Triangle& mesh : m_Triangles)
|
||||
// {
|
||||
// HitRecord hit{};
|
||||
// GeometryUtils::HitTest_Triangle(mesh, ray, hit);
|
||||
// if(hit.t < closestHit.t)
|
||||
// closestHit = hit;
|
||||
// }
|
||||
|
||||
}
|
||||
|
||||
@@ -61,6 +76,18 @@ namespace dae {
|
||||
if(GeometryUtils::HitTest_Plane(plane, ray, hit, true))
|
||||
return true;
|
||||
}
|
||||
for(const TriangleMesh& mesh : m_TriangleMeshGeometries)
|
||||
{
|
||||
if(GeometryUtils::HitTest_TriangleMesh(mesh, ray, hit, true))
|
||||
return true;
|
||||
}
|
||||
|
||||
// for(const Triangle& mesh : m_Triangles)
|
||||
// {
|
||||
// if(GeometryUtils::HitTest_Triangle(mesh, ray, hit, true))
|
||||
// return true;
|
||||
// }
|
||||
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -214,4 +241,49 @@ namespace dae {
|
||||
AddPointLight(Vector3{ -2.5f, 5.f, -5.f }, 70.f, ColorRGB{ 1.f, .8f, .45f }); //Front Light Left
|
||||
AddPointLight(Vector3{ 2.5f, 2.5f, -5.f }, 50.f, ColorRGB{ .34f, .47f, .68f });
|
||||
}
|
||||
|
||||
void Scene_W4_TestScene::Initialize() {
|
||||
m_Camera.origin = { 0.f, 1.f, -5.f };
|
||||
m_Camera.SetFOV(45.f);
|
||||
|
||||
// Materials
|
||||
const auto matLambert_GrayBlue = AddMaterial(new Material_Lambert({ 0.49f, 0.57f, 0.57f }, 1.f));
|
||||
const auto matLambert_White = AddMaterial(new Material_Lambert(colors::White, 1.f));
|
||||
|
||||
// Planes
|
||||
AddPlane(Vector3{ 0.f, 0.f, 10.f }, Vector3{ 0.f, 0.f, -1.f }, matLambert_GrayBlue); // BACK
|
||||
AddPlane(Vector3{ 0.f, 0.f, 0.f }, Vector3{ 0.f, 1.f, 0.f }, matLambert_GrayBlue); // BOTTOM
|
||||
AddPlane(Vector3{ 0.f, 10.f, 0.f }, Vector3{ 0.f, -1.f, 0.f }, matLambert_GrayBlue); // TOP
|
||||
AddPlane(Vector3{ 5.f, 0.f, 0.f }, Vector3{ -1.f, 0.f, 0.f }, matLambert_GrayBlue); // RIGHT
|
||||
AddPlane(Vector3{ -5.f, 0.f, 0.f }, Vector3{ 1.f, 0.f, 0.f }, matLambert_GrayBlue); // LEFT
|
||||
|
||||
// // Triangle (Temp)
|
||||
auto triangle = Triangle{ { -.75f, .5f, 0.f }, { -.75f, 2.f, 0.f }, { .75f, .5f, 0.f } };
|
||||
triangle.cullMode = TriangleCullMode::NoCulling;
|
||||
triangle.materialIndex = matLambert_White;
|
||||
|
||||
m_Triangles.emplace_back(triangle);
|
||||
|
||||
//
|
||||
pMesh = AddTriangleMesh(TriangleCullMode::NoCulling, matLambert_White);
|
||||
Utils::ParseOBJ("Resources/lowpoly_bunny.obj", pMesh->positions, pMesh->normals, pMesh->indices);
|
||||
// pMesh->Scale({2.f, 2.f, 2.f});
|
||||
// pMesh->Scale({2.f, 2.f, 2.f});
|
||||
pMesh->RotateY(PI_DIV_2);
|
||||
|
||||
pMesh->UpdateAABB();
|
||||
pMesh->UpdateTransforms();
|
||||
|
||||
// Light
|
||||
AddPointLight(Vector3{ 0.f, 5.f, 5.f }, 50.f, ColorRGB{ 1.f, .61f, .45f }); // Backlight
|
||||
AddPointLight(Vector3{ -2.5f, 5.f, -5.f }, 70.f, ColorRGB{ 1.f, .8f, .45f }); // Front Light Left
|
||||
AddPointLight(Vector3{ 2.5f, 5.f, -5.f }, 50.f, ColorRGB{ .34f, .47f, .68f }); // Front Light Right
|
||||
}
|
||||
|
||||
void Scene_W4_TestScene::Update(Timer *timer) {
|
||||
Scene::Update(timer);
|
||||
|
||||
// pMesh->RotateY(PI_DIV_2 * timer->GetTotal());
|
||||
// pMesh->UpdateTransforms();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,6 +51,8 @@ namespace dae
|
||||
std::vector<Light> m_Lights{};
|
||||
std::vector<Material*> m_Materials{};
|
||||
|
||||
std::vector<Triangle> m_Triangles{};
|
||||
|
||||
Camera m_Camera{};
|
||||
|
||||
Sphere* AddSphere(const Vector3& origin, float radius, unsigned char materialIndex = 0);
|
||||
@@ -94,7 +96,6 @@ namespace dae
|
||||
void Initialize() override;
|
||||
};
|
||||
|
||||
|
||||
class Scene_W3 final : public Scene
|
||||
{
|
||||
public:
|
||||
@@ -108,4 +109,24 @@ namespace dae
|
||||
|
||||
void Initialize() override;
|
||||
};
|
||||
|
||||
|
||||
class Scene_W4_TestScene final : public Scene
|
||||
{
|
||||
public:
|
||||
Scene_W4_TestScene() = default;
|
||||
~Scene_W4_TestScene() override = default;
|
||||
|
||||
Scene_W4_TestScene(const Scene_W4_TestScene&) = delete;
|
||||
Scene_W4_TestScene(Scene_W4_TestScene&&) noexcept = delete;
|
||||
Scene_W4_TestScene& operator=(const Scene_W4_TestScene&) = delete;
|
||||
Scene_W4_TestScene& operator=(Scene_W4_TestScene&&) noexcept = delete;
|
||||
|
||||
void Initialize() override;
|
||||
void Update(Timer* timer) override;
|
||||
|
||||
private:
|
||||
TriangleMesh* pMesh{ nullptr };
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -1,24 +1,25 @@
|
||||
#pragma once
|
||||
|
||||
#include <fstream>
|
||||
#include <memory>
|
||||
#include "Maths.h"
|
||||
#include "DataTypes.h"
|
||||
|
||||
namespace dae
|
||||
{
|
||||
namespace GeometryUtils
|
||||
{
|
||||
namespace dae {
|
||||
namespace GeometryUtils {
|
||||
#pragma region Sphere HitTest
|
||||
//SPHERE HIT-TESTS
|
||||
inline bool HitTest_Sphere(const Sphere& sphere, const Ray& ray, HitRecord& hitRecord, bool ignoreHitRecord = false)
|
||||
{
|
||||
|
||||
//SPHERE HIT-TESTS
|
||||
inline bool
|
||||
HitTest_Sphere(const Sphere &sphere, const Ray &ray, HitRecord &hitRecord, bool ignoreHitRecord = false) {
|
||||
const float radius2 = sphere.radius * sphere.radius;
|
||||
const Vector3 CameraToOrigin = sphere.origin - ray.origin;
|
||||
const float tInSphere = Vector3::Dot(CameraToOrigin, ray.direction);
|
||||
|
||||
if(tInSphere < 0) return false; //Looking away from sphere
|
||||
if (tInSphere < 0) return false; //Looking away from sphere
|
||||
|
||||
float originToInsideDist2 = CameraToOrigin.SqrMagnitude() - tInSphere * tInSphere;
|
||||
if(originToInsideDist2 > radius2)
|
||||
if (originToInsideDist2 > radius2)
|
||||
return false; // 'inside' point is outside of sphere
|
||||
|
||||
float tDiff = std::sqrt(radius2 - originToInsideDist2);
|
||||
@@ -27,7 +28,7 @@ namespace dae
|
||||
const bool hit = t > ray.min && t < ray.max;
|
||||
|
||||
|
||||
if (!ignoreHitRecord && hit){
|
||||
if (!ignoreHitRecord && hit) {
|
||||
hitRecord.t = t;
|
||||
hitRecord.origin = ray.origin + ray.direction * t;
|
||||
hitRecord.normal = (hitRecord.origin - sphere.origin).Normalized();
|
||||
@@ -38,27 +39,28 @@ namespace dae
|
||||
|
||||
return hit;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
inline bool HitTest_Sphere(const Sphere &sphere, const Ray &ray) {
|
||||
HitRecord temp{};
|
||||
return HitTest_Sphere(sphere, ray, temp, true);
|
||||
}
|
||||
|
||||
inline bool HitTest_Sphere(const Sphere& sphere, const Ray& ray)
|
||||
{
|
||||
HitRecord temp{};
|
||||
return HitTest_Sphere(sphere, ray, temp, true);
|
||||
}
|
||||
#pragma endregion
|
||||
#pragma region Plane HitTest
|
||||
//PLANE HIT-TESTS
|
||||
inline bool HitTest_Plane(const Plane& plane, const Ray& ray, HitRecord& hitRecord, bool ignoreHitRecord = false)
|
||||
{
|
||||
|
||||
//PLANE HIT-TESTS
|
||||
inline bool
|
||||
HitTest_Plane(const Plane &plane, const Ray &ray, HitRecord &hitRecord, bool ignoreHitRecord = false) {
|
||||
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);
|
||||
t /= dot;
|
||||
|
||||
const bool hit = t > ray.min && t < ray.max;
|
||||
|
||||
if(hit && !ignoreHitRecord){
|
||||
if (hit && !ignoreHitRecord) {
|
||||
hitRecord.t = t;
|
||||
hitRecord.origin = ray.origin + ray.direction * t;
|
||||
hitRecord.normal = plane.normal;
|
||||
@@ -70,142 +72,253 @@ namespace dae
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
inline bool HitTest_Plane(const Plane &plane, const Ray &ray) {
|
||||
HitRecord temp{};
|
||||
return HitTest_Plane(plane, ray, temp, true);
|
||||
}
|
||||
|
||||
inline bool HitTest_Plane(const Plane& plane, const Ray& ray)
|
||||
{
|
||||
HitRecord temp{};
|
||||
return HitTest_Plane(plane, ray, temp, true);
|
||||
}
|
||||
#pragma endregion
|
||||
#pragma region Triangle HitTest
|
||||
//TRIANGLE HIT-TESTS
|
||||
inline bool HitTest_Triangle(const Triangle& triangle, const Ray& ray, HitRecord& hitRecord, bool ignoreHitRecord = false)
|
||||
{
|
||||
//todo W5
|
||||
throw std::runtime_error("Not Implemented Yet");
|
||||
return false;
|
||||
}
|
||||
|
||||
inline bool HitTest_Triangle(const Triangle& triangle, const Ray& ray)
|
||||
{
|
||||
HitRecord temp{};
|
||||
return HitTest_Triangle(triangle, ray, temp, true);
|
||||
}
|
||||
//TRIANGLE HIT-TESTS
|
||||
inline bool
|
||||
HitTest_Triangle(const Triangle &triangle, const Ray &ray, HitRecord &hitRecord, bool ignoreHitRecord = false) {
|
||||
const Vector3 &n = triangle.normal;
|
||||
const float dot = Vector3::Dot(n, ray.direction);
|
||||
|
||||
switch (triangle.cullMode) {
|
||||
case TriangleCullMode::BackFaceCulling: {
|
||||
if (ignoreHitRecord) {
|
||||
if (dot >= FLT_EPSILON) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (dot <= FLT_EPSILON) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case TriangleCullMode::FrontFaceCulling: {
|
||||
if (ignoreHitRecord) {
|
||||
if (dot <= FLT_EPSILON) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (dot >= FLT_EPSILON) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case TriangleCullMode::NoCulling: {
|
||||
if (dot == 0.f)
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const Vector3 L = triangle.v0 - ray.origin;
|
||||
const float t = Vector3::Dot(L, n) / dot;
|
||||
|
||||
if (
|
||||
t < ray.min || t > ray
|
||||
.max)
|
||||
return false;
|
||||
|
||||
const Vector3 hitPoint = ray.origin + ray.direction * t;
|
||||
const Vector3 *verts[3] = {&triangle.v0, &triangle.v1, &triangle.v2};
|
||||
|
||||
for (
|
||||
int index{};
|
||||
index < 3; ++index) {
|
||||
const Vector3 e = *verts[(index + 1) % 3] - *verts[index];
|
||||
const Vector3 p = hitPoint - *verts[index];
|
||||
|
||||
const Vector3 c = Vector3::Cross(e, p);
|
||||
if (
|
||||
Vector3::Dot(Vector3::Cross(e, p), n
|
||||
) < 0)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!ignoreHitRecord) {
|
||||
hitRecord.
|
||||
t = t;
|
||||
hitRecord.
|
||||
origin = hitPoint;
|
||||
hitRecord.
|
||||
normal = triangle.normal;
|
||||
hitRecord.
|
||||
materialIndex = triangle.materialIndex;
|
||||
hitRecord.
|
||||
didHit = true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool HitTest_Triangle(const Triangle &triangle, const Ray &ray) {
|
||||
HitRecord temp{};
|
||||
return HitTest_Triangle(triangle, ray, temp, true);
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
#pragma region TriangeMesh HitTest
|
||||
inline bool HitTest_TriangleMesh(const TriangleMesh& mesh, const Ray& ray, HitRecord& hitRecord, bool ignoreHitRecord = false)
|
||||
{
|
||||
//todo W5
|
||||
throw std::runtime_error("Not Implemented Yet");
|
||||
return false;
|
||||
}
|
||||
|
||||
inline bool HitTest_TriangleMesh(const TriangleMesh& mesh, const Ray& ray)
|
||||
{
|
||||
HitRecord temp{};
|
||||
return HitTest_TriangleMesh(mesh, ray, temp, true);
|
||||
}
|
||||
inline bool SlabTest_TriangleMesh(const TriangleMesh &mesh, const Ray &ray) {
|
||||
float tx1 = (mesh.transformedMinAABB.x - ray.origin.x) / ray.direction.x;
|
||||
float tx2 = (mesh.transformedMaxAABB.x - ray.origin.x) / ray.direction.x;
|
||||
//
|
||||
float tmin = std::min(tx1, tx2);
|
||||
float tmax = std::max(tx1, tx2);
|
||||
//
|
||||
float ty1 = (mesh.transformedMinAABB.y - ray.origin.y) / ray.direction.y;
|
||||
float ty2 = (mesh.transformedMaxAABB.y - ray.origin.y) / ray.direction.y;
|
||||
//
|
||||
tmin = std::max(tmin, std::min(ty1, ty2));
|
||||
tmax = std::min(tmax, std::max(ty1, ty2));
|
||||
|
||||
float tz1 = (mesh.transformedMinAABB.z - ray.origin.z) / ray.direction.z;
|
||||
float tz2 = (mesh.transformedMaxAABB.z - ray.origin.z) / ray.direction.z;
|
||||
|
||||
tmin = std::max(tmin, std::min(tz1, tz2));
|
||||
tmax = std::min(tmax, std::max(tz1, tz2));
|
||||
//
|
||||
return tmax > 0 && tmax >= tmin;
|
||||
}
|
||||
|
||||
inline bool HitTest_TriangleMesh(const TriangleMesh &mesh, const Ray &ray, HitRecord &hitRecord,
|
||||
bool ignoreHitRecord = false) {
|
||||
if (!SlabTest_TriangleMesh(mesh, ray))
|
||||
return false;
|
||||
|
||||
constexpr int triSides{3};
|
||||
const size_t nTris{mesh.indices.size() / triSides};
|
||||
bool hit{false};
|
||||
|
||||
for (int i{}; i < nTris; ++i) {
|
||||
const Vector3 &v0{mesh.transformedPositions[mesh.indices[i * triSides + 0]]};
|
||||
const Vector3 &v1{mesh.transformedPositions[mesh.indices[i * triSides + 1]]};
|
||||
const Vector3 &v2{mesh.transformedPositions[mesh.indices[i * triSides + 2]]};
|
||||
|
||||
const Vector3 norm{mesh.transformedNormals[i]};
|
||||
|
||||
Triangle tri{
|
||||
v0, v1, v2, norm
|
||||
};
|
||||
|
||||
tri.cullMode = mesh.cullMode;
|
||||
tri.materialIndex = mesh.materialIndex;
|
||||
|
||||
HitRecord curHit;
|
||||
if (HitTest_Triangle(tri, ray, curHit, ignoreHitRecord)) {
|
||||
hit = true;
|
||||
|
||||
if (curHit.t < hitRecord.t) {
|
||||
hitRecord = curHit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return hit;
|
||||
}
|
||||
|
||||
inline bool HitTest_TriangleMesh(const TriangleMesh &mesh, const Ray &ray) {
|
||||
HitRecord temp{};
|
||||
return HitTest_TriangleMesh(mesh, ray, temp, true);
|
||||
}
|
||||
|
||||
|
||||
#pragma endregion
|
||||
}
|
||||
}
|
||||
|
||||
namespace LightUtils
|
||||
{
|
||||
//Direction from target to light
|
||||
inline Vector3 GetDirectionToLight(const Light& light, const Vector3 origin)
|
||||
{
|
||||
return { light.origin - origin };
|
||||
}
|
||||
namespace LightUtils {
|
||||
//Direction from target to light
|
||||
inline Vector3 GetDirectionToLight(const Light &light, const Vector3 origin) {
|
||||
return {light.origin - origin};
|
||||
}
|
||||
|
||||
inline ColorRGB GetRadiance(const Light& light, const Vector3& target)
|
||||
{
|
||||
switch(light.type)
|
||||
{
|
||||
inline ColorRGB GetRadiance(const Light &light, const Vector3 &target) {
|
||||
switch (light.type) {
|
||||
case LightType::Point:
|
||||
return light.color * light.intensity / (light.origin - target).SqrMagnitude();
|
||||
case LightType::Directional:
|
||||
return light.color * light.intensity;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline float LambertCosineLaw(const Vector3& normal, const Vector3& lightDirection)
|
||||
{
|
||||
inline float LambertCosineLaw(const Vector3 &normal, const Vector3 &lightDirection) {
|
||||
return Vector3::Dot(normal, lightDirection);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace Utils
|
||||
{
|
||||
//Just parses vertices and indices
|
||||
namespace Utils {
|
||||
//Just parses vertices and indices
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4505) //Warning unreferenced local function
|
||||
static bool ParseOBJ(const std::string& filename, std::vector<Vector3>& positions, std::vector<Vector3>& normals, std::vector<int>& indices)
|
||||
{
|
||||
std::ifstream file(filename);
|
||||
if (!file)
|
||||
return false;
|
||||
|
||||
std::string sCommand;
|
||||
// start a while iteration ending when the end of file is reached (ios::eof)
|
||||
while (!file.eof())
|
||||
{
|
||||
//read the first word of the string, use the >> operator (istream::operator>>)
|
||||
file >> sCommand;
|
||||
//use conditional statements to process the different commands
|
||||
if (sCommand == "#")
|
||||
{
|
||||
// Ignore Comment
|
||||
}
|
||||
else if (sCommand == "v")
|
||||
{
|
||||
//Vertex
|
||||
float x, y, z;
|
||||
file >> x >> y >> z;
|
||||
positions.push_back({ x, y, z });
|
||||
}
|
||||
else if (sCommand == "f")
|
||||
{
|
||||
float i0, i1, i2;
|
||||
file >> i0 >> i1 >> i2;
|
||||
static bool
|
||||
ParseOBJ(const std::string &filename, std::vector<Vector3> &positions, std::vector<Vector3> &normals,
|
||||
std::vector<int> &indices) {
|
||||
std::ifstream file(filename);
|
||||
if (!file)
|
||||
return false;
|
||||
|
||||
indices.push_back((int)i0 - 1);
|
||||
indices.push_back((int)i1 - 1);
|
||||
indices.push_back((int)i2 - 1);
|
||||
}
|
||||
//read till end of line and ignore all remaining chars
|
||||
file.ignore(1000, '\n');
|
||||
std::string sCommand;
|
||||
// start a while iteration ending when the end of file is reached (ios::eof)
|
||||
while (!file.eof()) {
|
||||
//read the first word of the string, use the >> operator (istream::operator>>)
|
||||
file >> sCommand;
|
||||
//use conditional statements to process the different commands
|
||||
if (sCommand == "#") {
|
||||
// Ignore Comment
|
||||
} else if (sCommand == "v") {
|
||||
//Vertex
|
||||
float x, y, z;
|
||||
file >> x >> y >> z;
|
||||
positions.push_back({x, y, z});
|
||||
} else if (sCommand == "f") {
|
||||
float i0, i1, i2;
|
||||
file >> i0 >> i1 >> i2;
|
||||
|
||||
if (file.eof())
|
||||
break;
|
||||
}
|
||||
indices.push_back((int) i0 - 1);
|
||||
indices.push_back((int) i1 - 1);
|
||||
indices.push_back((int) i2 - 1);
|
||||
}
|
||||
//read till end of line and ignore all remaining chars
|
||||
file.ignore(1000, '\n');
|
||||
|
||||
//Precompute normals
|
||||
for (uint64_t index = 0; index < indices.size(); index += 3)
|
||||
{
|
||||
uint32_t i0 = indices[index];
|
||||
uint32_t i1 = indices[index + 1];
|
||||
uint32_t i2 = indices[index + 2];
|
||||
if (file.eof())
|
||||
break;
|
||||
}
|
||||
|
||||
Vector3 edgeV0V1 = positions[i1] - positions[i0];
|
||||
Vector3 edgeV0V2 = positions[i2] - positions[i0];
|
||||
Vector3 normal = Vector3::Cross(edgeV0V1, edgeV0V2);
|
||||
//Precompute normals
|
||||
for (uint64_t index = 0; index < indices.size(); index += 3) {
|
||||
uint32_t i0 = indices[index];
|
||||
uint32_t i1 = indices[index + 1];
|
||||
uint32_t i2 = indices[index + 2];
|
||||
|
||||
if (std::isnan(normal.x))
|
||||
{
|
||||
int k = 0;
|
||||
}
|
||||
Vector3 edgeV0V1 = positions[i1] - positions[i0];
|
||||
Vector3 edgeV0V2 = positions[i2] - positions[i0];
|
||||
Vector3 normal = Vector3::Cross(edgeV0V1, edgeV0V2);
|
||||
|
||||
normal.Normalize();
|
||||
if (std::isnan(normal.x))
|
||||
{
|
||||
int k = 0;
|
||||
}
|
||||
if (std::isnan(normal.x)) {
|
||||
int k = 0;
|
||||
}
|
||||
|
||||
normals.push_back(normal);
|
||||
}
|
||||
normal.Normalize();
|
||||
if (std::isnan(normal.x)) {
|
||||
int k = 0;
|
||||
}
|
||||
|
||||
normals.push_back(normal);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
#pragma warning(pop)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -162,5 +162,14 @@ namespace dae {
|
||||
{
|
||||
return AreEqual(x, v.x) && AreEqual(y, v.y) && AreEqual(z, v.z);
|
||||
}
|
||||
|
||||
Vector3 Vector3::Max(const Vector3 &v1, const Vector3 &v2) {
|
||||
return { std::max(v1.x, v2.x), std::max(v1.y, v2.y), std::max(v1.z, v2.z) };
|
||||
}
|
||||
|
||||
Vector3 Vector3::Min(const Vector3 &v1, const Vector3 &v2) {
|
||||
return { std::min(v1.x, v2.x), std::min(v1.y, v2.y), std::min(v1.z, v2.z) };
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
}
|
||||
@@ -26,6 +26,9 @@ namespace dae
|
||||
static Vector3 Reflect(const Vector3& v1, const Vector3& v2);
|
||||
static Vector3 Lico(float f1, const Vector3& v1, float f2, const Vector3& v2, float f3, const Vector3& v3);
|
||||
|
||||
static Vector3 Max(const Vector3& v1, const Vector3& v2);
|
||||
static Vector3 Min(const Vector3& v1, const Vector3& v2);
|
||||
|
||||
Vector4 ToPoint4() const;
|
||||
Vector4 ToVector4() const;
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@ int main(int argc, char* args[])
|
||||
const auto pTimer = new Timer();
|
||||
const auto pRenderer = new Renderer(pWindow);
|
||||
|
||||
const auto pScene = new Scene_W3();
|
||||
const auto pScene = new Scene_W4_TestScene();
|
||||
pScene->Initialize();
|
||||
|
||||
//Start loop
|
||||
|
||||
Reference in New Issue
Block a user