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