diff --git a/.gitignore b/.gitignore
index 004a819..2a60d2f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -252,4 +252,5 @@ imgui.ini
/ZenRen/lib/imgui/misc/
/ZenRen/data_*
*.tlog
-*.log.txt
\ No newline at end of file
+*.log.txt
+/tmp/
\ No newline at end of file
diff --git a/ZenRen/ZenRen.vcxproj b/ZenRen/ZenRen.vcxproj
index 4c35eff..135c845 100644
--- a/ZenRen/ZenRen.vcxproj
+++ b/ZenRen/ZenRen.vcxproj
@@ -367,6 +367,7 @@
+
Create
Create
@@ -413,6 +414,7 @@
+
diff --git a/ZenRen/ZenRen.vcxproj.filters b/ZenRen/ZenRen.vcxproj.filters
index 7b88e4c..9c8706c 100644
--- a/ZenRen/ZenRen.vcxproj.filters
+++ b/ZenRen/ZenRen.vcxproj.filters
@@ -99,6 +99,7 @@
+
@@ -160,5 +161,6 @@
+
\ No newline at end of file
diff --git a/ZenRen/src/renderer/loader/DebugMeshes.cpp b/ZenRen/src/renderer/loader/DebugMeshes.cpp
index 323ead1..516036a 100644
--- a/ZenRen/src/renderer/loader/DebugMeshes.cpp
+++ b/ZenRen/src/renderer/loader/DebugMeshes.cpp
@@ -94,7 +94,7 @@ namespace renderer::loader
return result;
}
- void loadPointDebugVisual(unordered_map& target, const VEC3& pos, const VEC3& scale)
+ void loadPointDebugVisual(unordered_map& target, const VEC3& pos, const VEC3& scale, const D3DXCOLOR& color)
{
vector facesPos;
vector facesOther;
@@ -107,11 +107,49 @@ namespace renderer::loader
faceNormal,
UV { 0, 0 },
ARRAY_UV { 0, 0, -1 },
- D3DXCOLOR(1, 0, 0, 1)
+ color
});
}
}
const Material defaultMat = { "point.tga" };
insert(target, defaultMat, facesPos, facesOther);
}
+
+ vector> createDebugLineVerts(const VEC3& posStart, const VEC3& posEnd, const float width)
+ {
+ float minWidth = 0.01f;
+ float actualWidth = std::max(minWidth, width);
+ VEC3 posStartTop = { posStart.x, posStart.y + actualWidth, posStart.z };
+ VEC3 posEndTop = { posEnd.x, posEnd.y + actualWidth, posEnd.z };
+
+ vector result = {
+ posToXM4({ posStart, posEnd, posEndTop }),
+ posToXM4({ posStart, posEndTop, posStartTop }),
+ // make double sided
+ posToXM4({ posStart, posStartTop, posEndTop }),
+ posToXM4({ posStart, posEndTop, posEnd }),
+ };
+ return result;
+ }
+
+ void loadLineDebugVisual(unordered_map& target, const VEC3& posStart, VEC3& posEnd, const D3DXCOLOR& color)
+ {
+ vector facesPos;
+ vector facesOther;
+ const auto& bboxFacesXm = createDebugLineVerts(posStart, posEnd, 0.02f);
+ for (auto& posXm : bboxFacesXm) {
+ const auto faceNormal = toVec3(calcFlatFaceNormal(posXm));
+ for (int32_t i = 0; i < 3; i++) {
+ facesPos.push_back(toVec3(posXm[i]));
+ facesOther.push_back({
+ faceNormal,
+ UV { 0, 0 },
+ ARRAY_UV { 0, 0, -1 },
+ color
+ });
+ }
+ }
+ const Material defaultMat = { "line.tga" };
+ insert(target, defaultMat, facesPos, facesOther);
+ }
}
diff --git a/ZenRen/src/renderer/loader/DebugMeshes.h b/ZenRen/src/renderer/loader/DebugMeshes.h
index 16d8a58..a4fecff 100644
--- a/ZenRen/src/renderer/loader/DebugMeshes.h
+++ b/ZenRen/src/renderer/loader/DebugMeshes.h
@@ -5,5 +5,6 @@
namespace renderer::loader
{
void loadInstanceMeshBboxDebugVisual(std::unordered_map& target, const StaticInstance& instance);
- void loadPointDebugVisual(std::unordered_map& target, const VEC3& pos, const VEC3& scale = { 0.f, 0.f, 0.f });
+ void loadPointDebugVisual(std::unordered_map& target, const VEC3& pos, const VEC3& scale = { 0.f, 0.f, 0.f }, const D3DXCOLOR& color = D3DXCOLOR(1, 0, 0, 1));
+ void loadLineDebugVisual(std::unordered_map& target, const VEC3& posStart, VEC3& posEnd, const D3DXCOLOR& color = D3DXCOLOR(1, 0, 0, 1));
}
\ No newline at end of file
diff --git a/ZenRen/src/renderer/loader/MeshFromVdfLoader.cpp b/ZenRen/src/renderer/loader/MeshFromVdfLoader.cpp
index 578af9d..c35325b 100644
--- a/ZenRen/src/renderer/loader/MeshFromVdfLoader.cpp
+++ b/ZenRen/src/renderer/loader/MeshFromVdfLoader.cpp
@@ -107,7 +107,7 @@ namespace renderer::loader {
VERTEX_OTHER other;
other.normal = from(zenVert.Normal);
other.uvDiffuse = from(zenVert.TexCoord);
- other.colLight = D3DXCOLOR(zenVert.Color);
+ other.colLight = fromSRGB(D3DXCOLOR(zenVert.Color));
//other.colLight = D3DXCOLOR(1, 1, 1, 0.f);
if (faceLightmapIndex == -1) {
diff --git a/ZenRen/src/renderer/loader/MeshUtil.cpp b/ZenRen/src/renderer/loader/MeshUtil.cpp
index 5702645..29a8edc 100644
--- a/ZenRen/src/renderer/loader/MeshUtil.cpp
+++ b/ZenRen/src/renderer/loader/MeshUtil.cpp
@@ -50,4 +50,17 @@ namespace renderer::loader
LOG(INFO) << "Vector was not normalized! " << source << " | " << normalized << " | " << nearEqualXm;
}
}
+
+ inline float fromSRGB(const float channel) {
+ return (channel <= 0.04045f) ? (channel / 12.92f) : pow((channel + 0.055f) / 1.055f, 2.4f);
+ }
+
+ D3DXCOLOR fromSRGB(const D3DXCOLOR color) {
+ auto result = color;
+ const float gamma = 2.2f;
+ result.r = fromSRGB(result.r);
+ result.g = fromSRGB(result.g);
+ result.b = fromSRGB(result.b);
+ return result;
+ }
}
\ No newline at end of file
diff --git a/ZenRen/src/renderer/loader/MeshUtil.h b/ZenRen/src/renderer/loader/MeshUtil.h
index d1e2f4a..efbaf2a 100644
--- a/ZenRen/src/renderer/loader/MeshUtil.h
+++ b/ZenRen/src/renderer/loader/MeshUtil.h
@@ -48,4 +48,7 @@ namespace renderer::loader
}
void warnIfNotNormalized(const DirectX::XMVECTOR& source);
+
+ inline float fromSRGB(const float channel);
+ D3DXCOLOR fromSRGB(const D3DXCOLOR color);
}
\ No newline at end of file
diff --git a/ZenRen/src/renderer/loader/StaticLightFromVobLight.cpp b/ZenRen/src/renderer/loader/StaticLightFromVobLight.cpp
new file mode 100644
index 0000000..3516027
--- /dev/null
+++ b/ZenRen/src/renderer/loader/StaticLightFromVobLight.cpp
@@ -0,0 +1,28 @@
+#include "stdafx.h"
+#include "StaticLightFromVobLight.h"
+
+#include "MeshUtil.h"
+
+namespace renderer::loader
+{
+ using namespace DirectX;
+ using ::std::vector;
+ using ::std::unordered_map;
+
+ bool rayIntersectsWorldFaces(XMVECTOR rayStart, XMVECTOR rayEnd, float maxDistance, const unordered_map& meshData, const VertLookupTree& vertLookup)
+ {
+ XMVECTOR direction = DirectX::XMVector3Normalize(rayEnd - rayStart);
+ vector vertKeys = rayIntersected(vertLookup, toVec3(rayStart), toVec3(rayEnd));
+ for (auto& vertKey : vertKeys) {
+ auto& face = vertKey.getPos(meshData);
+ XMVECTOR faceA = toXM4Pos(face[0]);
+ XMVECTOR faceB = toXM4Pos(face[1]);
+ XMVECTOR faceC = toXM4Pos(face[2]);
+ float distance;
+ bool intersects = TriangleTests::Intersects(rayStart, direction, faceA, faceB, faceC, distance);
+ if (intersects && distance <= maxDistance) {
+ return true;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/ZenRen/src/renderer/loader/StaticLightFromVobLight.h b/ZenRen/src/renderer/loader/StaticLightFromVobLight.h
new file mode 100644
index 0000000..22b502e
--- /dev/null
+++ b/ZenRen/src/renderer/loader/StaticLightFromVobLight.h
@@ -0,0 +1,9 @@
+#pragma once
+
+#include "../RendererCommon.h"
+#include "VertPosLookup.h"
+
+namespace renderer::loader
+{
+ bool rayIntersectsWorldFaces(DirectX::XMVECTOR rayStart, DirectX::XMVECTOR rayEnd, float maxDistance, const std::unordered_map& meshData, const VertLookupTree& vertLookup);
+}
\ No newline at end of file
diff --git a/ZenRen/src/renderer/loader/VertPosLookup.cpp b/ZenRen/src/renderer/loader/VertPosLookup.cpp
index 2809743..6f42bbf 100644
--- a/ZenRen/src/renderer/loader/VertPosLookup.cpp
+++ b/ZenRen/src/renderer/loader/VertPosLookup.cpp
@@ -5,15 +5,14 @@ namespace renderer::loader
{
using ::std::vector;
using ::std::unordered_map;
+ using ::std::array;
- float rayIntersectTolerance = 0.1f;
+ const float rayIntersectTolerance = 0.0001f;
OrthoBoundingBox3D createBB3D(const vector& verts, uint32_t vertIndex)
{
float minX = FLT_MAX, minY = FLT_MAX, minZ = FLT_MAX;
float maxX = -FLT_MAX, maxY = -FLT_MAX, maxZ = -FLT_MAX;
- //float minX = FLT_MAX, minZ = FLT_MAX;
- //float maxX = -FLT_MAX, maxZ = -FLT_MAX;
for (uint32_t i = vertIndex; i < vertIndex + 3; i++) {
auto& vert = verts[i];
minX = std::min(minX, vert.x);
@@ -26,6 +25,22 @@ namespace renderer::loader
return OrthoTree::BoundingBoxND<3, float>{ {minX, minY, minZ}, {maxX, maxY, maxZ} };
}
+ OrthoBoundingBox3D createRaySearchBox(const array& posStartEnd)
+ {
+ float minX = FLT_MAX, minY = FLT_MAX, minZ = FLT_MAX;
+ float maxX = -FLT_MAX, maxY = -FLT_MAX, maxZ = -FLT_MAX;
+ for (uint32_t i = 0; i < posStartEnd.size(); i++) {
+ auto& vert = posStartEnd[i];
+ minX = std::min(minX, vert.x);
+ minY = std::min(minY, vert.y);
+ minZ = std::min(minZ, vert.z);
+ maxX = std::max(maxX, vert.x);
+ maxY = std::max(maxY, vert.y);
+ maxZ = std::max(maxZ, vert.z);
+ }
+ return OrthoTree::BoundingBoxND<3, float>{ {minX, minY, minZ}, { maxX, maxY, maxZ } };
+ }
+
VertLookupTree createVertLookup(const unordered_map& meshData)
{
vector bboxes;
@@ -57,17 +72,25 @@ namespace renderer::loader
};
constexpr bool shouldFullyContain = false;
- auto intersectedBoxesSorted = lookup.tree.RangeSearch(searchBox);
+ auto intersectedBoxes = lookup.tree.RangeSearch(searchBox);
// TODO while RayIntersectedAll should be equivalent from what i understand, it is missing most bboxes that RangeSearch finds
- //auto intersectedBoxesSorted = cache.tree.RayIntersectedAll({ pos.x, pos.y, pos.z }, { 0, -1, 0 }, rayIntersectTolerance, searchSizeY);
+ //auto intersectedBoxes = lookup.tree.RayIntersectedAll({ pos.x, pos.y, pos.z }, { 0, -100.f, 0 }, rayIntersectTolerance, searchSizeY);
- vector result;
- for (auto id : intersectedBoxesSorted) {
- const auto& vertKey = lookup.bboxIndexToVert.find(id)->second;
- result.push_back(vertKey);
- }
- return result;
+ return lookup.bboxIdsToVertIds(intersectedBoxes);
+ }
+
+ vector rayIntersected(const VertLookupTree& lookup, const VEC3& rayPosStart, const VEC3& rayPosEnd) {
+ // TODO
+ // RayIntersectedAll is not giving the expected results, until then just get all boxes that intersect the bbox of the ray.
+ // This will be very slow for long rays but we just don't to long rays for now. We could also construct multiple bboxes along the ray.
+
+ auto searchBox = createRaySearchBox({ rayPosStart, rayPosEnd });
+
+ constexpr bool shouldFullyContain = false;
+ auto intersectedBoxes = lookup.tree.RangeSearch(searchBox);
+
+ return lookup.bboxIdsToVertIds(intersectedBoxes);
}
// ##############################################################################################################################
@@ -75,9 +98,8 @@ namespace renderer::loader
// ##############################################################################################################################
__inline bool rayDownIntersectsFaceBB(const VEC3& pos, const vector& verts, const size_t vertIndex, const float searchSizeY) {
- float minX = FLT_MAX, minZ = FLT_MAX;
- float minY = -FLT_MAX, maxY = FLT_MAX;
- float maxX = -FLT_MAX, maxZ = -FLT_MAX;
+ float minX = FLT_MAX, minY = FLT_MAX, minZ = FLT_MAX;
+ float maxX = -FLT_MAX, maxY = -FLT_MAX, maxZ = -FLT_MAX;
for (uint32_t i = vertIndex; i < vertIndex + 3; i++) {
const auto& vert = verts[i];
diff --git a/ZenRen/src/renderer/loader/VertPosLookup.h b/ZenRen/src/renderer/loader/VertPosLookup.h
index 1827b36..8466784 100644
--- a/ZenRen/src/renderer/loader/VertPosLookup.h
+++ b/ZenRen/src/renderer/loader/VertPosLookup.h
@@ -16,10 +16,20 @@ namespace renderer::loader
struct VertLookupTree {
std::unordered_map bboxIndexToVert;
OrthoOctree tree;
+
+ const std::vector bboxIdsToVertIds(const std::vector& bboxIds) const {
+ std::vector result;
+ for (auto id : bboxIds) {
+ const auto& vertKey = this->bboxIndexToVert.find(id)->second;
+ result.push_back(vertKey);
+ }
+ return result;
+ }
};
VertLookupTree createVertLookup(const std::unordered_map& meshData);
std::vector rayDownIntersected(const VertLookupTree& lookup, const VEC3& pos, float searchSizeY);
std::vector rayDownIntersectedNaive(const std::unordered_map& meshData, const VEC3& pos, float searchSizeY);
+ std::vector rayIntersected(const VertLookupTree& lookup, const VEC3& rayPosStart, const VEC3& rayPosEnd);
}
diff --git a/ZenRen/src/renderer/loader/ZenLoader.cpp b/ZenRen/src/renderer/loader/ZenLoader.cpp
index 3753564..b940833 100644
--- a/ZenRen/src/renderer/loader/ZenLoader.cpp
+++ b/ZenRen/src/renderer/loader/ZenLoader.cpp
@@ -14,6 +14,7 @@
#include "TexFromVdfLoader.h"
#include "VertPosLookup.h"
#include "StaticLightFromGroundFace.h"
+#include "StaticLightFromVobLight.h"
#include "DirectXTex.h"
#include "zenload/zCMesh.h"
@@ -36,6 +37,8 @@ namespace renderer::loader {
bool debugInstanceMeshBboxCenter = false;
bool debugTintVobStaticLight = false;
bool debugStaticLights = false;
+ bool debugStaticLightRays = false;
+ float debugStaticLightRaysMaxDist = 50;
XMVECTOR bboxCenter(const array& bbox) {
return 0.5f * (toXM4Pos(bbox[0]) + toXM4Pos(bbox[1]));
@@ -82,7 +85,7 @@ namespace renderer::loader {
if (debugInstanceMeshBboxCenter) {
auto center = toVec3(bboxCenter(instance.bbox));
auto scale = toVec3(0.7f * XMVectorAbs(toXM4Pos(instance.bbox[1]) - toXM4Pos(instance.bbox[0])));
- loadPointDebugVisual(target, center, scale);
+ loadPointDebugVisual(target, center, scale, D3DXCOLOR(0, 0, 1, 1));
}
return true;
}
@@ -124,7 +127,13 @@ namespace renderer::loader {
};
}
- vector loadLights(vector& rootVobs)
+ void multiplyColor(D3DXCOLOR color, const float factor) {
+ color.r *= factor;
+ color.g *= factor;
+ color.b *= factor;
+ }
+
+ vector loadLights(const vector& rootVobs)
{
vector lights;
vector vobs;
@@ -135,12 +144,14 @@ namespace renderer::loader {
Light light = {
toVec3(toXM4Pos(vob.position) * 0.01f),
vob.zCVobLight.lightStatic,
- D3DXCOLOR(vob.zCVobLight.color),
+ fromSRGB(D3DXCOLOR(vob.zCVobLight.color)),
vob.zCVobLight.range * 0.01f,
};
lights.push_back(light);
}
+
+ LOG(INFO) << "VOBs: Loaded " << lights.size() << " static lights.";
return lights;
}
@@ -168,7 +179,17 @@ namespace renderer::loader {
return result;
}
- D3DXCOLOR getLightAtPos(XMVECTOR posXm, const vector& lights, const LightLookupTree& lightLookup) {
+ int32_t vobLightWorldIntersectChecks = 0;
+
+ struct DebugLine {
+ VEC3 posStart;
+ VEC3 posEnd;
+ D3DXCOLOR color;
+ };
+
+ vector lightToVobRays;
+
+ D3DXCOLOR getLightAtPos(XMVECTOR posXm, const vector& lights, const LightLookupTree& lightLookup, const unordered_map& worldMeshData, const VertLookupTree& worldFaceLookup) {
auto pos = toVec3(posXm);
float rayIntersectTolerance = 0.1f;
auto searchBox = OrthoBoundingBox3D{
@@ -179,6 +200,7 @@ namespace renderer::loader {
constexpr bool shouldFullyContain = false;
auto intersectedBoxes = lightLookup.tree.RangeSearch(searchBox);
+ int32_t contributingLightCount = 0;
D3DXCOLOR color = D3DXCOLOR(0.f, 0.f, 0.f, 1.f);
for (auto boxIndex : intersectedBoxes) {
@@ -186,11 +208,26 @@ namespace renderer::loader {
XMVECTOR lightPos = toXM4Pos(light.pos);
float dist = XMVectorGetX(XMVector3Length(lightPos - posXm));
float weight = 0;
- if (dist < light.range) {
- weight = 1.f - (dist / light.range);// TODO this assumes linear falloff for all light types, which is very likely wrong
- color += (light.color * weight);
+ if (dist < (light.range * 1.0f)) {
+ vobLightWorldIntersectChecks++;
+ bool intersectedWorld = rayIntersectsWorldFaces(lightPos, posXm, dist * 0.85f, worldMeshData, worldFaceLookup);
+ if (!intersectedWorld) {
+ contributingLightCount++;
+ weight = 1.f - (dist / (light.range * 1.0f));
+ color += (light.color * fromSRGB(weight));
+ }
+ if (dist < debugStaticLightRaysMaxDist) {
+ lightToVobRays.push_back({ light.pos, pos, intersectedWorld ? D3DXCOLOR(0.f, 0.f, 1.f, 0.5f) : D3DXCOLOR(1.f, 0.f, 0.f, 0.5f) });
+ }
+ }
+ }
+
+ if (debugStaticLightRays) {
+ if (contributingLightCount == 0) {
+ color = D3DXCOLOR(0.f, 1.f, 0.f, 1.f);
}
}
+
return color;
}
@@ -255,7 +292,8 @@ namespace renderer::loader {
}
if (hasLightmap) {
- colLight = getLightAtPos(bboxCenter(instance.bbox), lightsStatic, lightStaticLookup);
+ colLight = getLightAtPos(bboxCenter(instance.bbox), lightsStatic, lightStaticLookup, worldMeshData, worldFaceLookup);
+ multiplyColor(colLight, fromSRGB(0.71f));
resolvedStaticLight++;
}
else {
@@ -264,7 +302,7 @@ namespace renderer::loader {
resolvedStaticLight++;
}
else {
- colLight = D3DXCOLOR(0.63f, 0.63f, 0.63f, 1);// fallback lightness of (160, 160, 160)
+ colLight = fromSRGB(D3DXCOLOR(0.63f, 0.63f, 0.63f, 1));// fallback lightness of (160, 160, 160)
}
}
@@ -286,6 +324,7 @@ namespace renderer::loader {
LOG(INFO) << "VOBs: Loaded " << statics.size() << " instances:";
LOG(INFO) << " " << "Static light: Resolved for " << resolvedStaticLight << " instances.";
LOG(INFO) << " " << "Static light: Duration: " << std::to_string(totalDurationMicros / 1000) << " ms total (" << std::to_string(maxDurationMicros) << " micros worst instance)";
+ LOG(INFO) << " " << "Static light: Checked VobLight visibility " << vobLightWorldIntersectChecks << " times.";
return statics;
}
@@ -339,7 +378,13 @@ namespace renderer::loader {
if (debugStaticLights) {
for (auto& light : lightsStatic) {
- loadPointDebugVisual(staticMeshData, light.pos);
+ float scale = light.range / 10.f;
+ loadPointDebugVisual(staticMeshData, light.pos, { scale, scale, scale });
+ }
+ }
+ if (debugStaticLightRays) {
+ for (auto& ray : lightToVobRays) {
+ loadLineDebugVisual(staticMeshData, ray.posStart, ray.posEnd, ray.color);
}
}
diff --git a/ZenRen/stdafx.h b/ZenRen/stdafx.h
index 11cb827..c051b83 100644
--- a/ZenRen/stdafx.h
+++ b/ZenRen/stdafx.h
@@ -31,5 +31,6 @@
//#define _XM_NO_INTRINSICS_
#include
#include
+#include
#include "g3log/g3log.hpp"
\ No newline at end of file