diff --git a/libraries/dp_rect_pack/dp_rect_pack.h b/libraries/dp_rect_pack/dp_rect_pack.h new file mode 100644 index 000000000..48c07162e --- /dev/null +++ b/libraries/dp_rect_pack/dp_rect_pack.h @@ -0,0 +1,672 @@ +/* + * Rectangle packing library. + * v1.1.3 + * + * Copyright (c) 2017-2021 Daniel Plakhotich + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgement in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + + +#ifndef DP_RECT_PACK_H +#define DP_RECT_PACK_H + +#include +#include +#include + + +#define DP_RECT_PACK_VERSION_MAJOR 1 +#define DP_RECT_PACK_VERSION_MINOR 1 +#define DP_RECT_PACK_VERSION_PATCH 3 + + +namespace dp { +namespace rect_pack { + + +/** + * Status of the RectPacker::InsertResult. + * + * Only InsertStatus::ok indicates a successful insertion; + * all other values are kinds of errors. + */ +struct InsertStatus { + enum Type { + ok, ///< Successful insertion + negativeSize, ///< Width and/or height is negative + zeroSize, ///< Width and/or height is zero + + /** + * Rectangle is too big to fit in a single page. + * + * Width and/or height of the rectangle exceeds the maximum + * size a single page can hold, which is the maximum page size + * minus the padding. + * + * \sa RectPacker::RectPacker() + */ + rectTooBig + }; +}; + + +// A note on the implementation. +// The current algorithm is absolutely the same as in version 1.0.0, +// except that we only keep the leaf nodes of the binary tree. This +// dramatically improves performance and reduces memory usage, but +// growDown() and growRight() methods are harder to understand +// because the leafs insertion order depends on several layers of +// parent branches that don't physically exist. I added comments to +// help you visualize what happens, but it will probably be easier +// to just look at the code of the version 1.0.0. + + +/** + * Rectangle packer. + * + * GeomT is not required to hold negative numbers, and thus can be + * an unsigned integer. It's also possible to use a floating-point + * or a custom numeric type. + * + * A custom type for GeomT should support: + * * Implicit construction from an integer >= 0 + * * Addition and subtraction (including compound assignment) + * * Comparison + * + * \tparam GeomT numeric type to use for geometry + */ +template +class RectPacker { +public: + struct Spacing { + GeomT x; ///< Horizontal spacing + GeomT y; ///< Vertical spacing + + /** + * Construct Spacing with the same spacing for both dimensions. + */ + explicit Spacing(GeomT spacing) + : x(spacing) + , y(spacing) + {} + + Spacing(GeomT x, GeomT y) + : x(x) + , y(y) + {} + }; + + struct Padding { + GeomT top; + GeomT bottom; + GeomT left; + GeomT right; + + /** + * Construct Padding with the same padding for all sides. + */ + explicit Padding(GeomT padding) + : top(padding) + , bottom(padding) + , left(padding) + , right(padding) + {} + + Padding(GeomT top, GeomT bottom, GeomT left, GeomT right) + : top(top) + , bottom(bottom) + , left(left) + , right(right) + {} + }; + + struct Position { + GeomT x; + GeomT y; + + Position() + : x() + , y() + {} + + Position(GeomT x, GeomT y) + : x(x) + , y(y) + {} + }; + + /** + * Result returned by RectPacker::insert(). + */ + struct InsertResult { + /** + * Status of the insertion. + * + * \warning If InsertResult.status is not InsertStatus::ok, + * values of all other fields of InsertResult are undefined. + */ + InsertStatus::Type status; + + /** + * Position of the inserted rectangle within the page. + */ + Position pos; + + /** + * Index of the page in which the rectangle was inserted. + * + * \sa getPageSize() + */ + std::size_t pageIndex; + }; + + /** + * RectPacker constructor. + * + * maxPageWidth and maxPageHeight define the maximum size of + * a single page, including the padding. Depending on this limit + * and the features of GeomT, a RectPacker can work in multipage + * or infinite single-page mode. + * + * To enable infinite single-page mode, you have two choices, + * depending on the properties of GeomT: + * * If GeomT has a physical limit (like any standard integer), + * you can set the maximum size to the maximum positive + * value GeomT can hold. + * * Otherwise, if GeomT is a floating-point type or a custom + * unbounded type, you can set the maximum size to a huge + * value or, if supported by the type, a magic value that + * always bigger than any finite number (like a positive + * infinity for floating-point types). + * + * If GeomT can hold negative values, the maximum page size, spacing, + * and padding will be clamped to 0. Keep in mind that if the + * maximum page size is 0, or if the total padding greater or equal + * to the maximum page size, pages will have no free space for + * rectangles, and all calls to insert() will result in + * InsertStatus::rectTooBig. + * + * \param maxPageWidth maximum width of a page, including + * the horizontal padding + * \param maxPageHeight maximum height of a page, including + * the vertical padding + * \param rectsSpacing space between rectangles + * \param pagePadding space between rectangles and edges of a page + */ + RectPacker( + GeomT maxPageWidth, GeomT maxPageHeight, + const Spacing& rectsSpacing = Spacing(0), + const Padding& pagePadding = Padding(0)) + : ctx(maxPageWidth, maxPageHeight, rectsSpacing, pagePadding) + , pages(1) + {} + + /** + * Return the current number of pages. + * + * \returns number of pages (always > 0) + */ + std::size_t getNumPages() const + { + return pages.size(); + } + + /** + * Return the current size of the page. + * + * \param pageIndex index of the page in range [0..getNumPages()) + * \param[out] width width of the page + * \param[out] height height of the page + * + * \sa getNumPages(), InsertResult::pageIndex + */ + void getPageSize(std::size_t pageIndex, GeomT& width, GeomT& height) const + { + const Size size = pages[pageIndex].getSize(ctx); + width = size.w; + height = size.h; + } + + /** + * Insert a rectangle. + * + * The rectangles you'll feed to insert() should be sorted in + * descending order by comparing first by height, then by width. + * A comparison function for std::sort may look like the following: + * \code + * bool compare(const T& a, const T& b) + * { + * if (a.height != b.height) + * return a.height > b.height; + * else + * return a.width > b.width; + * } + * \endcode + * + * \param width width of the rectangle + * \param height height of the rectangle + * \returns InsertResult + */ + InsertResult insert(GeomT width, GeomT height); +private: + struct Size { + GeomT w; + GeomT h; + + Size(GeomT w, GeomT h) + : w(w) + , h(h) + {} + }; + + struct Context; + class Page { + public: + Page() + : nodes() + , rootSize(0, 0) + , growDownRootBottomIdx(0) + {} + + Size getSize(const Context& ctx) const + { + return Size( + ctx.padding.left + rootSize.w + ctx.padding.right, + ctx.padding.top + rootSize.h + ctx.padding.bottom); + } + + bool insert(Context& ctx, const Size& rect, Position& pos); + private: + struct Node { + Position pos; + Size size; + + Node(GeomT x, GeomT y, GeomT w, GeomT h) + : pos(x, y) + , size(w, h) + {} + }; + + // Leaf nodes of the binary tree in depth-first order + std::vector nodes; + Size rootSize; + // The index of the first leaf bottom node of the new root + // created in growDown(). See the method for more details. + std::size_t growDownRootBottomIdx; + + bool tryInsert(Context& ctx, const Size& rect, Position& pos); + bool findNode( + const Size& rect, + std::size_t& nodeIdx, Position& pos) const; + void subdivideNode( + Context& ctx, std::size_t nodeIdx, const Size& rect); + bool tryGrow(Context& ctx, const Size& rect, Position& pos); + void growDown(Context& ctx, const Size& rect, Position& pos); + void growRight(Context& ctx, const Size& rect, Position& pos); + }; + + struct Context { + Size maxSize; + Spacing spacing; + Padding padding; + + Context( + GeomT maxPageWidth, GeomT maxPageHeight, + const Spacing& rectsSpacing, const Padding& pagePadding); + + static void subtractPadding(GeomT& padding, GeomT& size); + }; + + Context ctx; + std::vector pages; +}; + + +template +typename RectPacker::InsertResult +RectPacker::insert(GeomT width, GeomT height) +{ + InsertResult result; + + if (width < 0 || height < 0) { + result.status = InsertStatus::negativeSize; + return result; + } + + if (width == 0 || height == 0) { + result.status = InsertStatus::zeroSize; + return result; + } + + if (width > ctx.maxSize.w || height > ctx.maxSize.h) { + result.status = InsertStatus::rectTooBig; + return result; + } + + const Size rect(width, height); + + for (std::size_t i = 0; i < pages.size(); ++i) + if (pages[i].insert(ctx, rect, result.pos)) { + result.status = InsertStatus::ok; + result.pageIndex = i; + return result; + } + + pages.push_back(Page()); + Page& page = pages.back(); + page.insert(ctx, rect, result.pos); + result.status = InsertStatus::ok; + result.pageIndex = pages.size() - 1; + + return result; +} + + +template +bool RectPacker::Page::insert( + Context& ctx, const Size& rect, Position& pos) +{ + assert(rect.w > 0); + assert(rect.w <= ctx.maxSize.w); + assert(rect.h > 0); + assert(rect.h <= ctx.maxSize.h); + + // The first insertion should be handled especially since + // growRight() and growDown() add spacing between the root + // and the inserted rectangle. + if (rootSize.w == 0) { + rootSize = rect; + pos.x = ctx.padding.left; + pos.y = ctx.padding.top; + + return true; + } + + return tryInsert(ctx, rect, pos) || tryGrow(ctx, rect, pos); +} + + +template +bool RectPacker::Page::tryInsert( + Context& ctx, const Size& rect, Position& pos) +{ + std::size_t nodeIdx; + if (findNode(rect, nodeIdx, pos)) { + subdivideNode(ctx, nodeIdx, rect); + return true; + } + + return false; +} + + +template +bool RectPacker::Page::findNode( + const Size& rect, std::size_t& nodeIdx, Position& pos) const +{ + for (nodeIdx = 0; nodeIdx < nodes.size(); ++nodeIdx) { + const Node& node = nodes[nodeIdx]; + if (rect.w <= node.size.w && rect.h <= node.size.h) { + pos = node.pos; + return true; + } + } + + return false; +} + + +/** + * Called after a rectangle was inserted in the top left corner of + * a free node to create child nodes from free space, if any. + * + * The node is first cut horizontally along the rect's bottom, + * then vertically along the right edge of the rect. Splitting + * that way is crucial for the algorithm to work correctly. + * + * +---+ + * | | + * +---+---+ + * | | + * +-------+ + */ +template +void RectPacker::Page::subdivideNode( + Context& ctx, std::size_t nodeIdx, const Size& rect) +{ + assert(nodeIdx < nodes.size()); + + Node& node = nodes[nodeIdx]; + + assert(node.size.w >= rect.w); + const GeomT rightW = node.size.w - rect.w; + const bool hasSpaceRight = rightW > ctx.spacing.x; + + assert(node.size.h >= rect.h); + const GeomT bottomH = node.size.h - rect.h; + const bool hasSpaceBelow = bottomH > ctx.spacing.y; + + if (hasSpaceRight) { + // Right node replaces the current + + const GeomT bottomX = node.pos.x; + const GeomT bottomW = node.size.w; + + node.pos.x += rect.w + ctx.spacing.x; + node.size.w = rightW - ctx.spacing.x; + node.size.h = rect.h; + + if (hasSpaceBelow) { + nodes.insert( + nodes.begin() + nodeIdx + 1, + Node( + bottomX, + node.pos.y + rect.h + ctx.spacing.y, + bottomW, + bottomH - ctx.spacing.y)); + + if (nodeIdx <= growDownRootBottomIdx) + ++growDownRootBottomIdx; + } + } else if (hasSpaceBelow) { + // Bottom node replaces the current + node.pos.y += rect.h + ctx.spacing.y; + node.size.h = bottomH - ctx.spacing.y; + } else { + nodes.erase(nodes.begin() + nodeIdx); + if (nodeIdx < growDownRootBottomIdx) + --growDownRootBottomIdx; + } +} + + +template +bool RectPacker::Page::tryGrow( + Context& ctx, const Size& rect, Position& pos) +{ + assert(ctx.maxSize.w >= rootSize.w); + const GeomT freeW = ctx.maxSize.w - rootSize.w; + assert(ctx.maxSize.h >= rootSize.h); + const GeomT freeH = ctx.maxSize.h - rootSize.h; + + const bool canGrowDown = ( + freeH >= rect.h && freeH - rect.h >= ctx.spacing.y); + const bool mustGrowDown = ( + canGrowDown + && freeW >= ctx.spacing.x + && (rootSize.w + ctx.spacing.x + >= rootSize.h + rect.h + ctx.spacing.y)); + if (mustGrowDown) { + growDown(ctx, rect, pos); + return true; + } + + const bool canGrowRight = ( + freeW >= rect.w && freeW - rect.w >= ctx.spacing.x); + if (canGrowRight) { + growRight(ctx, rect, pos); + return true; + } + + if (canGrowDown) { + growDown(ctx, rect, pos); + return true; + } + + return false; +} + + +template +void RectPacker::Page::growDown( + Context& ctx, const Size& rect, Position& pos) +{ + assert(ctx.maxSize.h > rootSize.h); + assert(ctx.maxSize.h - rootSize.h >= rect.h); + assert(ctx.maxSize.h - rootSize.h - rect.h >= ctx.spacing.y); + + pos.x = ctx.padding.left; + pos.y = ctx.padding.top + rootSize.h + ctx.spacing.y; + + if (rootSize.w < rect.w) { + if (rect.w - rootSize.w > ctx.spacing.x) { + // The auxiliary node becomes the right child of the new + // root. It contains the current root (bottom child) and + // free space at the current root's right (right child). + nodes.insert( + nodes.begin(), + Node( + ctx.padding.left + rootSize.w + ctx.spacing.x, + ctx.padding.top, + rect.w - rootSize.w - ctx.spacing.x, + rootSize.h)); + ++growDownRootBottomIdx; + } + + rootSize.w = rect.w; + } else if (rootSize.w - rect.w > ctx.spacing.x) { + // Free space at the right of the inserted rect becomes the + // right child of the rect's node, which in turn is the + // bottom child of the new root. + nodes.insert( + nodes.begin() + growDownRootBottomIdx, + Node( + pos.x + rect.w + ctx.spacing.x, + pos.y, + rootSize.w - rect.w - ctx.spacing.x, + rect.h)); + + // The inserted node is visited before the node from the next + // growDown() since the current new root will be the right + // child of the next root. + ++growDownRootBottomIdx; + } + + rootSize.h += ctx.spacing.y + rect.h; +} + + +template +void RectPacker::Page::growRight( + Context& ctx, const Size& rect, Position& pos) +{ + assert(ctx.maxSize.w > rootSize.w); + assert(ctx.maxSize.w - rootSize.w >= rect.w); + assert(ctx.maxSize.w - rootSize.w - rect.w >= ctx.spacing.x); + + pos.x = ctx.padding.left + rootSize.w + ctx.spacing.x; + pos.y = ctx.padding.top; + + if (rootSize.h < rect.h) { + if (rect.h - rootSize.h > ctx.spacing.y) + // The auxiliary node becomes the bottom child of the + // new root. It contains the current root (right child) + // and free space at the current root's bottom, if any + // (bottom child). + nodes.insert( + nodes.end(), + Node( + ctx.padding.left, + ctx.padding.top + rootSize.h + ctx.spacing.y, + rootSize.w, + rect.h - rootSize.h - ctx.spacing.y)); + + rootSize.h = rect.h; + } else if (rootSize.h - rect.h > ctx.spacing.y) { + // Free space at the bottom of the inserted rect becomes the + // bottom child of the rect's node, which in turn is the + // right child of the new root node. + nodes.insert( + nodes.begin(), + Node( + pos.x, + pos.y + rect.h + ctx.spacing.y, + rect.w, + rootSize.h - rect.h - ctx.spacing.y)); + ++growDownRootBottomIdx; + } + + rootSize.w += ctx.spacing.x + rect.w; +} + + +template +RectPacker::Context::Context( + GeomT maxPageWidth, GeomT maxPageHeight, + const Spacing& rectsSpacing, const Padding& pagePadding) + : maxSize(maxPageWidth, maxPageHeight) + , spacing(rectsSpacing) + , padding(pagePadding) +{ + if (maxSize.w < 0) + maxSize.w = 0; + if (maxSize.h < 0) + maxSize.h = 0; + + if (spacing.x < 0) + spacing.x = 0; + if (spacing.y < 0) + spacing.y = 0; + + subtractPadding(padding.top, maxSize.h); + subtractPadding(padding.bottom, maxSize.h); + subtractPadding(padding.left, maxSize.w); + subtractPadding(padding.right, maxSize.w); +} + + +template +void RectPacker::Context::subtractPadding( + GeomT& padding, GeomT& size) +{ + if (padding < 0) + padding = 0; + else if (padding < size) + size -= padding; + else { + padding = size; + size = 0; + } +} + + +} // namespace rect_pack +} // namespace dp + + +#endif // DP_RECT_PACK_H \ No newline at end of file diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index ed4244f88..eee704df3 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -750,6 +750,8 @@ set (VULKAN_SOURCES common/rendering/vulkan/textures/vk_texture.cpp common/rendering/vulkan/framebuffers/vk_framebuffer.cpp common/rendering/vulkan/accelstructs/vk_raytrace.cpp + common/rendering/vulkan/accelstructs/vk_lightmap.cpp + common/rendering/vulkan/accelstructs/halffloat.cpp ) if (HAVE_VULKAN) @@ -1263,6 +1265,7 @@ include_directories( scripting/zscript rendering ../libraries/ZVulkan/include + ../libraries/dp_rect_pack ${SYSTEM_SOURCES_DIR} ) diff --git a/src/common/rendering/hwrenderer/data/hw_levelmesh.h b/src/common/rendering/hwrenderer/data/hw_levelmesh.h index 8a48c99f3..c230f4a51 100644 --- a/src/common/rendering/hwrenderer/data/hw_levelmesh.h +++ b/src/common/rendering/hwrenderer/data/hw_levelmesh.h @@ -4,10 +4,82 @@ #include "tarray.h" #include "vectors.h" #include "hw_collision.h" +#include namespace hwrenderer { +// Note: this is just the current layout needed to get VkLightmap/GPURaytracer from zdray to compile within this project. +// +// The surface actually class needs to be moved up DoomLevelMesh, since this can't otherwise be accessed in the common part of the codebase shared with Raze. +// Ideally, we'd undoomify it as much as possible, so Raze in theory also would be able to declare an raytracing acceleration structure for dynlights and lightmaps + +class ThingLight +{ +public: + FVector3 Origin; + FVector3 RelativeOrigin; + float Radius; + float Intensity; + float InnerAngleCos; + float OuterAngleCos; + FVector3 SpotDir; + FVector3 Color; +}; + +enum SurfaceType +{ + ST_UNKNOWN, + ST_MIDDLESIDE, + ST_UPPERSIDE, + ST_LOWERSIDE, + ST_CEILING, + ST_FLOOR +}; + +class Surface +{ +public: + // Surface geometry + SurfaceType type = ST_UNKNOWN; + TArray verts; + //Plane plane; + FVector3 boundsMin, boundsMax; + + // Touching light sources + std::vector LightList; + + // Lightmap world coordinates for the texture + FVector3 worldOrigin = { 0.0f, 0.0f, 0.0f }; + FVector3 worldStepX = { 0.0f, 0.0f, 0.0f }; + FVector3 worldStepY = { 0.0f, 0.0f, 0.0f }; + + // Calculate world coordinates to UV coordinates + FVector3 translateWorldToLocal = { 0.0f, 0.0f, 0.0f }; + FVector3 projLocalToU = { 0.0f, 0.0f, 0.0f }; + FVector3 projLocalToV = { 0.0f, 0.0f, 0.0f }; + + // Output lightmap for the surface + int texWidth = 0; + int texHeight = 0; + std::vector texPixels; + + // Placement in final texture atlas + int atlasPageIndex = -1; + int atlasX = 0; + int atlasY = 0; + + // Smoothing group surface is to be rendered with + int smoothingGroupIndex = -1; +}; + +struct SmoothingGroup +{ + FVector4 plane = FVector4(0, 0, 1, 0); + int sectorGroup = 0; + std::vector surfaces; +}; + class LevelMesh { public: @@ -20,6 +92,12 @@ public: std::unique_ptr Collision; + // To do: these are currently not filled + TArray> surfaces; + TArray smoothingGroups; + FVector3 SunDirection; + FVector3 SunColor; + bool Trace(const FVector3& start, FVector3 direction, float maxDist) { FVector3 end = start + direction * std::max(maxDist - 10.0f, 0.0f); diff --git a/src/common/rendering/vulkan/accelstructs/halffloat.cpp b/src/common/rendering/vulkan/accelstructs/halffloat.cpp new file mode 100644 index 000000000..437a35b83 --- /dev/null +++ b/src/common/rendering/vulkan/accelstructs/halffloat.cpp @@ -0,0 +1,3381 @@ +/* +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any damages +** arising from the use of this software. +** +** Permission is granted to anyone to use this software for any purpose, +** including commercial applications, and to alter it and redistribute it +** freely, subject to the following restrictions: +** +** 1. The origin of this software must not be misrepresented; you must not +** claim that you wrote the original software. If you use this software +** in a product, an acknowledgment in the product documentation would be +** appreciated but is not required. +** 2. Altered source versions must be plainly marked as such, and must not be +** misrepresented as being the original software. +** 3. This notice may not be removed or altered from any source distribution. +** +** Note: Some of the libraries UICore may link to may have additional +** requirements or restrictions. +** +** Based on the paper "Fast Half Float Conversions" by Jeroen van der Zijp. +*/ + +#include "halffloat.h" + +namespace HalfFloatTables +{ + unsigned int mantissa_table[2048] = + { + 0, + 864026624, + 872415232, + 876609536, + 880803840, + 882900992, + 884998144, + 887095296, + 889192448, + 890241024, + 891289600, + 892338176, + 893386752, + 894435328, + 895483904, + 896532480, + 897581056, + 898105344, + 898629632, + 899153920, + 899678208, + 900202496, + 900726784, + 901251072, + 901775360, + 902299648, + 902823936, + 903348224, + 903872512, + 904396800, + 904921088, + 905445376, + 905969664, + 906231808, + 906493952, + 906756096, + 907018240, + 907280384, + 907542528, + 907804672, + 908066816, + 908328960, + 908591104, + 908853248, + 909115392, + 909377536, + 909639680, + 909901824, + 910163968, + 910426112, + 910688256, + 910950400, + 911212544, + 911474688, + 911736832, + 911998976, + 912261120, + 912523264, + 912785408, + 913047552, + 913309696, + 913571840, + 913833984, + 914096128, + 914358272, + 914489344, + 914620416, + 914751488, + 914882560, + 915013632, + 915144704, + 915275776, + 915406848, + 915537920, + 915668992, + 915800064, + 915931136, + 916062208, + 916193280, + 916324352, + 916455424, + 916586496, + 916717568, + 916848640, + 916979712, + 917110784, + 917241856, + 917372928, + 917504000, + 917635072, + 917766144, + 917897216, + 918028288, + 918159360, + 918290432, + 918421504, + 918552576, + 918683648, + 918814720, + 918945792, + 919076864, + 919207936, + 919339008, + 919470080, + 919601152, + 919732224, + 919863296, + 919994368, + 920125440, + 920256512, + 920387584, + 920518656, + 920649728, + 920780800, + 920911872, + 921042944, + 921174016, + 921305088, + 921436160, + 921567232, + 921698304, + 921829376, + 921960448, + 922091520, + 922222592, + 922353664, + 922484736, + 922615808, + 922746880, + 922812416, + 922877952, + 922943488, + 923009024, + 923074560, + 923140096, + 923205632, + 923271168, + 923336704, + 923402240, + 923467776, + 923533312, + 923598848, + 923664384, + 923729920, + 923795456, + 923860992, + 923926528, + 923992064, + 924057600, + 924123136, + 924188672, + 924254208, + 924319744, + 924385280, + 924450816, + 924516352, + 924581888, + 924647424, + 924712960, + 924778496, + 924844032, + 924909568, + 924975104, + 925040640, + 925106176, + 925171712, + 925237248, + 925302784, + 925368320, + 925433856, + 925499392, + 925564928, + 925630464, + 925696000, + 925761536, + 925827072, + 925892608, + 925958144, + 926023680, + 926089216, + 926154752, + 926220288, + 926285824, + 926351360, + 926416896, + 926482432, + 926547968, + 926613504, + 926679040, + 926744576, + 926810112, + 926875648, + 926941184, + 927006720, + 927072256, + 927137792, + 927203328, + 927268864, + 927334400, + 927399936, + 927465472, + 927531008, + 927596544, + 927662080, + 927727616, + 927793152, + 927858688, + 927924224, + 927989760, + 928055296, + 928120832, + 928186368, + 928251904, + 928317440, + 928382976, + 928448512, + 928514048, + 928579584, + 928645120, + 928710656, + 928776192, + 928841728, + 928907264, + 928972800, + 929038336, + 929103872, + 929169408, + 929234944, + 929300480, + 929366016, + 929431552, + 929497088, + 929562624, + 929628160, + 929693696, + 929759232, + 929824768, + 929890304, + 929955840, + 930021376, + 930086912, + 930152448, + 930217984, + 930283520, + 930349056, + 930414592, + 930480128, + 930545664, + 930611200, + 930676736, + 930742272, + 930807808, + 930873344, + 930938880, + 931004416, + 931069952, + 931135488, + 931168256, + 931201024, + 931233792, + 931266560, + 931299328, + 931332096, + 931364864, + 931397632, + 931430400, + 931463168, + 931495936, + 931528704, + 931561472, + 931594240, + 931627008, + 931659776, + 931692544, + 931725312, + 931758080, + 931790848, + 931823616, + 931856384, + 931889152, + 931921920, + 931954688, + 931987456, + 932020224, + 932052992, + 932085760, + 932118528, + 932151296, + 932184064, + 932216832, + 932249600, + 932282368, + 932315136, + 932347904, + 932380672, + 932413440, + 932446208, + 932478976, + 932511744, + 932544512, + 932577280, + 932610048, + 932642816, + 932675584, + 932708352, + 932741120, + 932773888, + 932806656, + 932839424, + 932872192, + 932904960, + 932937728, + 932970496, + 933003264, + 933036032, + 933068800, + 933101568, + 933134336, + 933167104, + 933199872, + 933232640, + 933265408, + 933298176, + 933330944, + 933363712, + 933396480, + 933429248, + 933462016, + 933494784, + 933527552, + 933560320, + 933593088, + 933625856, + 933658624, + 933691392, + 933724160, + 933756928, + 933789696, + 933822464, + 933855232, + 933888000, + 933920768, + 933953536, + 933986304, + 934019072, + 934051840, + 934084608, + 934117376, + 934150144, + 934182912, + 934215680, + 934248448, + 934281216, + 934313984, + 934346752, + 934379520, + 934412288, + 934445056, + 934477824, + 934510592, + 934543360, + 934576128, + 934608896, + 934641664, + 934674432, + 934707200, + 934739968, + 934772736, + 934805504, + 934838272, + 934871040, + 934903808, + 934936576, + 934969344, + 935002112, + 935034880, + 935067648, + 935100416, + 935133184, + 935165952, + 935198720, + 935231488, + 935264256, + 935297024, + 935329792, + 935362560, + 935395328, + 935428096, + 935460864, + 935493632, + 935526400, + 935559168, + 935591936, + 935624704, + 935657472, + 935690240, + 935723008, + 935755776, + 935788544, + 935821312, + 935854080, + 935886848, + 935919616, + 935952384, + 935985152, + 936017920, + 936050688, + 936083456, + 936116224, + 936148992, + 936181760, + 936214528, + 936247296, + 936280064, + 936312832, + 936345600, + 936378368, + 936411136, + 936443904, + 936476672, + 936509440, + 936542208, + 936574976, + 936607744, + 936640512, + 936673280, + 936706048, + 936738816, + 936771584, + 936804352, + 936837120, + 936869888, + 936902656, + 936935424, + 936968192, + 937000960, + 937033728, + 937066496, + 937099264, + 937132032, + 937164800, + 937197568, + 937230336, + 937263104, + 937295872, + 937328640, + 937361408, + 937394176, + 937426944, + 937459712, + 937492480, + 937525248, + 937558016, + 937590784, + 937623552, + 937656320, + 937689088, + 937721856, + 937754624, + 937787392, + 937820160, + 937852928, + 937885696, + 937918464, + 937951232, + 937984000, + 938016768, + 938049536, + 938082304, + 938115072, + 938147840, + 938180608, + 938213376, + 938246144, + 938278912, + 938311680, + 938344448, + 938377216, + 938409984, + 938442752, + 938475520, + 938508288, + 938541056, + 938573824, + 938606592, + 938639360, + 938672128, + 938704896, + 938737664, + 938770432, + 938803200, + 938835968, + 938868736, + 938901504, + 938934272, + 938967040, + 938999808, + 939032576, + 939065344, + 939098112, + 939130880, + 939163648, + 939196416, + 939229184, + 939261952, + 939294720, + 939327488, + 939360256, + 939393024, + 939425792, + 939458560, + 939491328, + 939524096, + 939540480, + 939556864, + 939573248, + 939589632, + 939606016, + 939622400, + 939638784, + 939655168, + 939671552, + 939687936, + 939704320, + 939720704, + 939737088, + 939753472, + 939769856, + 939786240, + 939802624, + 939819008, + 939835392, + 939851776, + 939868160, + 939884544, + 939900928, + 939917312, + 939933696, + 939950080, + 939966464, + 939982848, + 939999232, + 940015616, + 940032000, + 940048384, + 940064768, + 940081152, + 940097536, + 940113920, + 940130304, + 940146688, + 940163072, + 940179456, + 940195840, + 940212224, + 940228608, + 940244992, + 940261376, + 940277760, + 940294144, + 940310528, + 940326912, + 940343296, + 940359680, + 940376064, + 940392448, + 940408832, + 940425216, + 940441600, + 940457984, + 940474368, + 940490752, + 940507136, + 940523520, + 940539904, + 940556288, + 940572672, + 940589056, + 940605440, + 940621824, + 940638208, + 940654592, + 940670976, + 940687360, + 940703744, + 940720128, + 940736512, + 940752896, + 940769280, + 940785664, + 940802048, + 940818432, + 940834816, + 940851200, + 940867584, + 940883968, + 940900352, + 940916736, + 940933120, + 940949504, + 940965888, + 940982272, + 940998656, + 941015040, + 941031424, + 941047808, + 941064192, + 941080576, + 941096960, + 941113344, + 941129728, + 941146112, + 941162496, + 941178880, + 941195264, + 941211648, + 941228032, + 941244416, + 941260800, + 941277184, + 941293568, + 941309952, + 941326336, + 941342720, + 941359104, + 941375488, + 941391872, + 941408256, + 941424640, + 941441024, + 941457408, + 941473792, + 941490176, + 941506560, + 941522944, + 941539328, + 941555712, + 941572096, + 941588480, + 941604864, + 941621248, + 941637632, + 941654016, + 941670400, + 941686784, + 941703168, + 941719552, + 941735936, + 941752320, + 941768704, + 941785088, + 941801472, + 941817856, + 941834240, + 941850624, + 941867008, + 941883392, + 941899776, + 941916160, + 941932544, + 941948928, + 941965312, + 941981696, + 941998080, + 942014464, + 942030848, + 942047232, + 942063616, + 942080000, + 942096384, + 942112768, + 942129152, + 942145536, + 942161920, + 942178304, + 942194688, + 942211072, + 942227456, + 942243840, + 942260224, + 942276608, + 942292992, + 942309376, + 942325760, + 942342144, + 942358528, + 942374912, + 942391296, + 942407680, + 942424064, + 942440448, + 942456832, + 942473216, + 942489600, + 942505984, + 942522368, + 942538752, + 942555136, + 942571520, + 942587904, + 942604288, + 942620672, + 942637056, + 942653440, + 942669824, + 942686208, + 942702592, + 942718976, + 942735360, + 942751744, + 942768128, + 942784512, + 942800896, + 942817280, + 942833664, + 942850048, + 942866432, + 942882816, + 942899200, + 942915584, + 942931968, + 942948352, + 942964736, + 942981120, + 942997504, + 943013888, + 943030272, + 943046656, + 943063040, + 943079424, + 943095808, + 943112192, + 943128576, + 943144960, + 943161344, + 943177728, + 943194112, + 943210496, + 943226880, + 943243264, + 943259648, + 943276032, + 943292416, + 943308800, + 943325184, + 943341568, + 943357952, + 943374336, + 943390720, + 943407104, + 943423488, + 943439872, + 943456256, + 943472640, + 943489024, + 943505408, + 943521792, + 943538176, + 943554560, + 943570944, + 943587328, + 943603712, + 943620096, + 943636480, + 943652864, + 943669248, + 943685632, + 943702016, + 943718400, + 943734784, + 943751168, + 943767552, + 943783936, + 943800320, + 943816704, + 943833088, + 943849472, + 943865856, + 943882240, + 943898624, + 943915008, + 943931392, + 943947776, + 943964160, + 943980544, + 943996928, + 944013312, + 944029696, + 944046080, + 944062464, + 944078848, + 944095232, + 944111616, + 944128000, + 944144384, + 944160768, + 944177152, + 944193536, + 944209920, + 944226304, + 944242688, + 944259072, + 944275456, + 944291840, + 944308224, + 944324608, + 944340992, + 944357376, + 944373760, + 944390144, + 944406528, + 944422912, + 944439296, + 944455680, + 944472064, + 944488448, + 944504832, + 944521216, + 944537600, + 944553984, + 944570368, + 944586752, + 944603136, + 944619520, + 944635904, + 944652288, + 944668672, + 944685056, + 944701440, + 944717824, + 944734208, + 944750592, + 944766976, + 944783360, + 944799744, + 944816128, + 944832512, + 944848896, + 944865280, + 944881664, + 944898048, + 944914432, + 944930816, + 944947200, + 944963584, + 944979968, + 944996352, + 945012736, + 945029120, + 945045504, + 945061888, + 945078272, + 945094656, + 945111040, + 945127424, + 945143808, + 945160192, + 945176576, + 945192960, + 945209344, + 945225728, + 945242112, + 945258496, + 945274880, + 945291264, + 945307648, + 945324032, + 945340416, + 945356800, + 945373184, + 945389568, + 945405952, + 945422336, + 945438720, + 945455104, + 945471488, + 945487872, + 945504256, + 945520640, + 945537024, + 945553408, + 945569792, + 945586176, + 945602560, + 945618944, + 945635328, + 945651712, + 945668096, + 945684480, + 945700864, + 945717248, + 945733632, + 945750016, + 945766400, + 945782784, + 945799168, + 945815552, + 945831936, + 945848320, + 945864704, + 945881088, + 945897472, + 945913856, + 945930240, + 945946624, + 945963008, + 945979392, + 945995776, + 946012160, + 946028544, + 946044928, + 946061312, + 946077696, + 946094080, + 946110464, + 946126848, + 946143232, + 946159616, + 946176000, + 946192384, + 946208768, + 946225152, + 946241536, + 946257920, + 946274304, + 946290688, + 946307072, + 946323456, + 946339840, + 946356224, + 946372608, + 946388992, + 946405376, + 946421760, + 946438144, + 946454528, + 946470912, + 946487296, + 946503680, + 946520064, + 946536448, + 946552832, + 946569216, + 946585600, + 946601984, + 946618368, + 946634752, + 946651136, + 946667520, + 946683904, + 946700288, + 946716672, + 946733056, + 946749440, + 946765824, + 946782208, + 946798592, + 946814976, + 946831360, + 946847744, + 946864128, + 946880512, + 946896896, + 946913280, + 946929664, + 946946048, + 946962432, + 946978816, + 946995200, + 947011584, + 947027968, + 947044352, + 947060736, + 947077120, + 947093504, + 947109888, + 947126272, + 947142656, + 947159040, + 947175424, + 947191808, + 947208192, + 947224576, + 947240960, + 947257344, + 947273728, + 947290112, + 947306496, + 947322880, + 947339264, + 947355648, + 947372032, + 947388416, + 947404800, + 947421184, + 947437568, + 947453952, + 947470336, + 947486720, + 947503104, + 947519488, + 947535872, + 947552256, + 947568640, + 947585024, + 947601408, + 947617792, + 947634176, + 947650560, + 947666944, + 947683328, + 947699712, + 947716096, + 947732480, + 947748864, + 947765248, + 947781632, + 947798016, + 947814400, + 947830784, + 947847168, + 947863552, + 947879936, + 947896320, + 939524096, + 939532288, + 939540480, + 939548672, + 939556864, + 939565056, + 939573248, + 939581440, + 939589632, + 939597824, + 939606016, + 939614208, + 939622400, + 939630592, + 939638784, + 939646976, + 939655168, + 939663360, + 939671552, + 939679744, + 939687936, + 939696128, + 939704320, + 939712512, + 939720704, + 939728896, + 939737088, + 939745280, + 939753472, + 939761664, + 939769856, + 939778048, + 939786240, + 939794432, + 939802624, + 939810816, + 939819008, + 939827200, + 939835392, + 939843584, + 939851776, + 939859968, + 939868160, + 939876352, + 939884544, + 939892736, + 939900928, + 939909120, + 939917312, + 939925504, + 939933696, + 939941888, + 939950080, + 939958272, + 939966464, + 939974656, + 939982848, + 939991040, + 939999232, + 940007424, + 940015616, + 940023808, + 940032000, + 940040192, + 940048384, + 940056576, + 940064768, + 940072960, + 940081152, + 940089344, + 940097536, + 940105728, + 940113920, + 940122112, + 940130304, + 940138496, + 940146688, + 940154880, + 940163072, + 940171264, + 940179456, + 940187648, + 940195840, + 940204032, + 940212224, + 940220416, + 940228608, + 940236800, + 940244992, + 940253184, + 940261376, + 940269568, + 940277760, + 940285952, + 940294144, + 940302336, + 940310528, + 940318720, + 940326912, + 940335104, + 940343296, + 940351488, + 940359680, + 940367872, + 940376064, + 940384256, + 940392448, + 940400640, + 940408832, + 940417024, + 940425216, + 940433408, + 940441600, + 940449792, + 940457984, + 940466176, + 940474368, + 940482560, + 940490752, + 940498944, + 940507136, + 940515328, + 940523520, + 940531712, + 940539904, + 940548096, + 940556288, + 940564480, + 940572672, + 940580864, + 940589056, + 940597248, + 940605440, + 940613632, + 940621824, + 940630016, + 940638208, + 940646400, + 940654592, + 940662784, + 940670976, + 940679168, + 940687360, + 940695552, + 940703744, + 940711936, + 940720128, + 940728320, + 940736512, + 940744704, + 940752896, + 940761088, + 940769280, + 940777472, + 940785664, + 940793856, + 940802048, + 940810240, + 940818432, + 940826624, + 940834816, + 940843008, + 940851200, + 940859392, + 940867584, + 940875776, + 940883968, + 940892160, + 940900352, + 940908544, + 940916736, + 940924928, + 940933120, + 940941312, + 940949504, + 940957696, + 940965888, + 940974080, + 940982272, + 940990464, + 940998656, + 941006848, + 941015040, + 941023232, + 941031424, + 941039616, + 941047808, + 941056000, + 941064192, + 941072384, + 941080576, + 941088768, + 941096960, + 941105152, + 941113344, + 941121536, + 941129728, + 941137920, + 941146112, + 941154304, + 941162496, + 941170688, + 941178880, + 941187072, + 941195264, + 941203456, + 941211648, + 941219840, + 941228032, + 941236224, + 941244416, + 941252608, + 941260800, + 941268992, + 941277184, + 941285376, + 941293568, + 941301760, + 941309952, + 941318144, + 941326336, + 941334528, + 941342720, + 941350912, + 941359104, + 941367296, + 941375488, + 941383680, + 941391872, + 941400064, + 941408256, + 941416448, + 941424640, + 941432832, + 941441024, + 941449216, + 941457408, + 941465600, + 941473792, + 941481984, + 941490176, + 941498368, + 941506560, + 941514752, + 941522944, + 941531136, + 941539328, + 941547520, + 941555712, + 941563904, + 941572096, + 941580288, + 941588480, + 941596672, + 941604864, + 941613056, + 941621248, + 941629440, + 941637632, + 941645824, + 941654016, + 941662208, + 941670400, + 941678592, + 941686784, + 941694976, + 941703168, + 941711360, + 941719552, + 941727744, + 941735936, + 941744128, + 941752320, + 941760512, + 941768704, + 941776896, + 941785088, + 941793280, + 941801472, + 941809664, + 941817856, + 941826048, + 941834240, + 941842432, + 941850624, + 941858816, + 941867008, + 941875200, + 941883392, + 941891584, + 941899776, + 941907968, + 941916160, + 941924352, + 941932544, + 941940736, + 941948928, + 941957120, + 941965312, + 941973504, + 941981696, + 941989888, + 941998080, + 942006272, + 942014464, + 942022656, + 942030848, + 942039040, + 942047232, + 942055424, + 942063616, + 942071808, + 942080000, + 942088192, + 942096384, + 942104576, + 942112768, + 942120960, + 942129152, + 942137344, + 942145536, + 942153728, + 942161920, + 942170112, + 942178304, + 942186496, + 942194688, + 942202880, + 942211072, + 942219264, + 942227456, + 942235648, + 942243840, + 942252032, + 942260224, + 942268416, + 942276608, + 942284800, + 942292992, + 942301184, + 942309376, + 942317568, + 942325760, + 942333952, + 942342144, + 942350336, + 942358528, + 942366720, + 942374912, + 942383104, + 942391296, + 942399488, + 942407680, + 942415872, + 942424064, + 942432256, + 942440448, + 942448640, + 942456832, + 942465024, + 942473216, + 942481408, + 942489600, + 942497792, + 942505984, + 942514176, + 942522368, + 942530560, + 942538752, + 942546944, + 942555136, + 942563328, + 942571520, + 942579712, + 942587904, + 942596096, + 942604288, + 942612480, + 942620672, + 942628864, + 942637056, + 942645248, + 942653440, + 942661632, + 942669824, + 942678016, + 942686208, + 942694400, + 942702592, + 942710784, + 942718976, + 942727168, + 942735360, + 942743552, + 942751744, + 942759936, + 942768128, + 942776320, + 942784512, + 942792704, + 942800896, + 942809088, + 942817280, + 942825472, + 942833664, + 942841856, + 942850048, + 942858240, + 942866432, + 942874624, + 942882816, + 942891008, + 942899200, + 942907392, + 942915584, + 942923776, + 942931968, + 942940160, + 942948352, + 942956544, + 942964736, + 942972928, + 942981120, + 942989312, + 942997504, + 943005696, + 943013888, + 943022080, + 943030272, + 943038464, + 943046656, + 943054848, + 943063040, + 943071232, + 943079424, + 943087616, + 943095808, + 943104000, + 943112192, + 943120384, + 943128576, + 943136768, + 943144960, + 943153152, + 943161344, + 943169536, + 943177728, + 943185920, + 943194112, + 943202304, + 943210496, + 943218688, + 943226880, + 943235072, + 943243264, + 943251456, + 943259648, + 943267840, + 943276032, + 943284224, + 943292416, + 943300608, + 943308800, + 943316992, + 943325184, + 943333376, + 943341568, + 943349760, + 943357952, + 943366144, + 943374336, + 943382528, + 943390720, + 943398912, + 943407104, + 943415296, + 943423488, + 943431680, + 943439872, + 943448064, + 943456256, + 943464448, + 943472640, + 943480832, + 943489024, + 943497216, + 943505408, + 943513600, + 943521792, + 943529984, + 943538176, + 943546368, + 943554560, + 943562752, + 943570944, + 943579136, + 943587328, + 943595520, + 943603712, + 943611904, + 943620096, + 943628288, + 943636480, + 943644672, + 943652864, + 943661056, + 943669248, + 943677440, + 943685632, + 943693824, + 943702016, + 943710208, + 943718400, + 943726592, + 943734784, + 943742976, + 943751168, + 943759360, + 943767552, + 943775744, + 943783936, + 943792128, + 943800320, + 943808512, + 943816704, + 943824896, + 943833088, + 943841280, + 943849472, + 943857664, + 943865856, + 943874048, + 943882240, + 943890432, + 943898624, + 943906816, + 943915008, + 943923200, + 943931392, + 943939584, + 943947776, + 943955968, + 943964160, + 943972352, + 943980544, + 943988736, + 943996928, + 944005120, + 944013312, + 944021504, + 944029696, + 944037888, + 944046080, + 944054272, + 944062464, + 944070656, + 944078848, + 944087040, + 944095232, + 944103424, + 944111616, + 944119808, + 944128000, + 944136192, + 944144384, + 944152576, + 944160768, + 944168960, + 944177152, + 944185344, + 944193536, + 944201728, + 944209920, + 944218112, + 944226304, + 944234496, + 944242688, + 944250880, + 944259072, + 944267264, + 944275456, + 944283648, + 944291840, + 944300032, + 944308224, + 944316416, + 944324608, + 944332800, + 944340992, + 944349184, + 944357376, + 944365568, + 944373760, + 944381952, + 944390144, + 944398336, + 944406528, + 944414720, + 944422912, + 944431104, + 944439296, + 944447488, + 944455680, + 944463872, + 944472064, + 944480256, + 944488448, + 944496640, + 944504832, + 944513024, + 944521216, + 944529408, + 944537600, + 944545792, + 944553984, + 944562176, + 944570368, + 944578560, + 944586752, + 944594944, + 944603136, + 944611328, + 944619520, + 944627712, + 944635904, + 944644096, + 944652288, + 944660480, + 944668672, + 944676864, + 944685056, + 944693248, + 944701440, + 944709632, + 944717824, + 944726016, + 944734208, + 944742400, + 944750592, + 944758784, + 944766976, + 944775168, + 944783360, + 944791552, + 944799744, + 944807936, + 944816128, + 944824320, + 944832512, + 944840704, + 944848896, + 944857088, + 944865280, + 944873472, + 944881664, + 944889856, + 944898048, + 944906240, + 944914432, + 944922624, + 944930816, + 944939008, + 944947200, + 944955392, + 944963584, + 944971776, + 944979968, + 944988160, + 944996352, + 945004544, + 945012736, + 945020928, + 945029120, + 945037312, + 945045504, + 945053696, + 945061888, + 945070080, + 945078272, + 945086464, + 945094656, + 945102848, + 945111040, + 945119232, + 945127424, + 945135616, + 945143808, + 945152000, + 945160192, + 945168384, + 945176576, + 945184768, + 945192960, + 945201152, + 945209344, + 945217536, + 945225728, + 945233920, + 945242112, + 945250304, + 945258496, + 945266688, + 945274880, + 945283072, + 945291264, + 945299456, + 945307648, + 945315840, + 945324032, + 945332224, + 945340416, + 945348608, + 945356800, + 945364992, + 945373184, + 945381376, + 945389568, + 945397760, + 945405952, + 945414144, + 945422336, + 945430528, + 945438720, + 945446912, + 945455104, + 945463296, + 945471488, + 945479680, + 945487872, + 945496064, + 945504256, + 945512448, + 945520640, + 945528832, + 945537024, + 945545216, + 945553408, + 945561600, + 945569792, + 945577984, + 945586176, + 945594368, + 945602560, + 945610752, + 945618944, + 945627136, + 945635328, + 945643520, + 945651712, + 945659904, + 945668096, + 945676288, + 945684480, + 945692672, + 945700864, + 945709056, + 945717248, + 945725440, + 945733632, + 945741824, + 945750016, + 945758208, + 945766400, + 945774592, + 945782784, + 945790976, + 945799168, + 945807360, + 945815552, + 945823744, + 945831936, + 945840128, + 945848320, + 945856512, + 945864704, + 945872896, + 945881088, + 945889280, + 945897472, + 945905664, + 945913856, + 945922048, + 945930240, + 945938432, + 945946624, + 945954816, + 945963008, + 945971200, + 945979392, + 945987584, + 945995776, + 946003968, + 946012160, + 946020352, + 946028544, + 946036736, + 946044928, + 946053120, + 946061312, + 946069504, + 946077696, + 946085888, + 946094080, + 946102272, + 946110464, + 946118656, + 946126848, + 946135040, + 946143232, + 946151424, + 946159616, + 946167808, + 946176000, + 946184192, + 946192384, + 946200576, + 946208768, + 946216960, + 946225152, + 946233344, + 946241536, + 946249728, + 946257920, + 946266112, + 946274304, + 946282496, + 946290688, + 946298880, + 946307072, + 946315264, + 946323456, + 946331648, + 946339840, + 946348032, + 946356224, + 946364416, + 946372608, + 946380800, + 946388992, + 946397184, + 946405376, + 946413568, + 946421760, + 946429952, + 946438144, + 946446336, + 946454528, + 946462720, + 946470912, + 946479104, + 946487296, + 946495488, + 946503680, + 946511872, + 946520064, + 946528256, + 946536448, + 946544640, + 946552832, + 946561024, + 946569216, + 946577408, + 946585600, + 946593792, + 946601984, + 946610176, + 946618368, + 946626560, + 946634752, + 946642944, + 946651136, + 946659328, + 946667520, + 946675712, + 946683904, + 946692096, + 946700288, + 946708480, + 946716672, + 946724864, + 946733056, + 946741248, + 946749440, + 946757632, + 946765824, + 946774016, + 946782208, + 946790400, + 946798592, + 946806784, + 946814976, + 946823168, + 946831360, + 946839552, + 946847744, + 946855936, + 946864128, + 946872320, + 946880512, + 946888704, + 946896896, + 946905088, + 946913280, + 946921472, + 946929664, + 946937856, + 946946048, + 946954240, + 946962432, + 946970624, + 946978816, + 946987008, + 946995200, + 947003392, + 947011584, + 947019776, + 947027968, + 947036160, + 947044352, + 947052544, + 947060736, + 947068928, + 947077120, + 947085312, + 947093504, + 947101696, + 947109888, + 947118080, + 947126272, + 947134464, + 947142656, + 947150848, + 947159040, + 947167232, + 947175424, + 947183616, + 947191808, + 947200000, + 947208192, + 947216384, + 947224576, + 947232768, + 947240960, + 947249152, + 947257344, + 947265536, + 947273728, + 947281920, + 947290112, + 947298304, + 947306496, + 947314688, + 947322880, + 947331072, + 947339264, + 947347456, + 947355648, + 947363840, + 947372032, + 947380224, + 947388416, + 947396608, + 947404800, + 947412992, + 947421184, + 947429376, + 947437568, + 947445760, + 947453952, + 947462144, + 947470336, + 947478528, + 947486720, + 947494912, + 947503104, + 947511296, + 947519488, + 947527680, + 947535872, + 947544064, + 947552256, + 947560448, + 947568640, + 947576832, + 947585024, + 947593216, + 947601408, + 947609600, + 947617792, + 947625984, + 947634176, + 947642368, + 947650560, + 947658752, + 947666944, + 947675136, + 947683328, + 947691520, + 947699712, + 947707904, + 947716096, + 947724288, + 947732480, + 947740672, + 947748864, + 947757056, + 947765248, + 947773440, + 947781632, + 947789824, + 947798016, + 947806208, + 947814400, + 947822592, + 947830784, + 947838976, + 947847168, + 947855360, + 947863552, + 947871744, + 947879936, + 947888128, + 947896320, + 947904512, + }; + + unsigned int exponent_table[64] = + { + 0, + 8388608, + 16777216, + 25165824, + 33554432, + 41943040, + 50331648, + 58720256, + 67108864, + 75497472, + 83886080, + 92274688, + 100663296, + 109051904, + 117440512, + 125829120, + 134217728, + 142606336, + 150994944, + 159383552, + 167772160, + 176160768, + 184549376, + 192937984, + 201326592, + 209715200, + 218103808, + 226492416, + 234881024, + 243269632, + 251658240, + 1199570944, + 2147483648, + 2155872256, + 2164260864, + 2172649472, + 2181038080, + 2189426688, + 2197815296, + 2206203904, + 2214592512, + 2222981120, + 2231369728, + 2239758336, + 2248146944, + 2256535552, + 2264924160, + 2273312768, + 2281701376, + 2290089984, + 2298478592, + 2306867200, + 2315255808, + 2323644416, + 2332033024, + 2340421632, + 2348810240, + 2357198848, + 2365587456, + 2373976064, + 2382364672, + 2390753280, + 2399141888, + 3347054592, + }; + + unsigned short offset_table[64] = + { + 0, + 1024, + 1024, + 1024, + 1024, + 1024, + 1024, + 1024, + 1024, + 1024, + 1024, + 1024, + 1024, + 1024, + 1024, + 1024, + 1024, + 1024, + 1024, + 1024, + 1024, + 1024, + 1024, + 1024, + 1024, + 1024, + 1024, + 1024, + 1024, + 1024, + 1024, + 1024, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + }; + + unsigned short base_table[512] = + { + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 2, + 4, + 8, + 16, + 32, + 64, + 128, + 256, + 512, + 1024, + 2048, + 3072, + 4096, + 5120, + 6144, + 7168, + 8192, + 9216, + 10240, + 11264, + 12288, + 13312, + 14336, + 15360, + 16384, + 17408, + 18432, + 19456, + 20480, + 21504, + 22528, + 23552, + 24576, + 25600, + 26624, + 27648, + 28672, + 29696, + 30720, + 31744, + 31744, + 31744, + 31744, + 31744, + 31744, + 31744, + 31744, + 31744, + 31744, + 31744, + 31744, + 31744, + 31744, + 31744, + 31744, + 31744, + 31744, + 31744, + 31744, + 31744, + 31744, + 31744, + 31744, + 31744, + 31744, + 31744, + 31744, + 31744, + 31744, + 31744, + 31744, + 31744, + 31744, + 31744, + 31744, + 31744, + 31744, + 31744, + 31744, + 31744, + 31744, + 31744, + 31744, + 31744, + 31744, + 31744, + 31744, + 31744, + 31744, + 31744, + 31744, + 31744, + 31744, + 31744, + 31744, + 31744, + 31744, + 31744, + 31744, + 31744, + 31744, + 31744, + 31744, + 31744, + 31744, + 31744, + 31744, + 31744, + 31744, + 31744, + 31744, + 31744, + 31744, + 31744, + 31744, + 31744, + 31744, + 31744, + 31744, + 31744, + 31744, + 31744, + 31744, + 31744, + 31744, + 31744, + 31744, + 31744, + 31744, + 31744, + 31744, + 31744, + 31744, + 31744, + 31744, + 31744, + 31744, + 31744, + 31744, + 31744, + 31744, + 31744, + 31744, + 31744, + 31744, + 31744, + 31744, + 31744, + 31744, + 31744, + 31744, + 31744, + 32768, + 32768, + 32768, + 32768, + 32768, + 32768, + 32768, + 32768, + 32768, + 32768, + 32768, + 32768, + 32768, + 32768, + 32768, + 32768, + 32768, + 32768, + 32768, + 32768, + 32768, + 32768, + 32768, + 32768, + 32768, + 32768, + 32768, + 32768, + 32768, + 32768, + 32768, + 32768, + 32768, + 32768, + 32768, + 32768, + 32768, + 32768, + 32768, + 32768, + 32768, + 32768, + 32768, + 32768, + 32768, + 32768, + 32768, + 32768, + 32768, + 32768, + 32768, + 32768, + 32768, + 32768, + 32768, + 32768, + 32768, + 32768, + 32768, + 32768, + 32768, + 32768, + 32768, + 32768, + 32768, + 32768, + 32768, + 32768, + 32768, + 32768, + 32768, + 32768, + 32768, + 32768, + 32768, + 32768, + 32768, + 32768, + 32768, + 32768, + 32768, + 32768, + 32768, + 32768, + 32768, + 32768, + 32768, + 32768, + 32768, + 32768, + 32768, + 32768, + 32768, + 32768, + 32768, + 32768, + 32768, + 32768, + 32768, + 32768, + 32768, + 32768, + 32768, + 32769, + 32770, + 32772, + 32776, + 32784, + 32800, + 32832, + 32896, + 33024, + 33280, + 33792, + 34816, + 35840, + 36864, + 37888, + 38912, + 39936, + 40960, + 41984, + 43008, + 44032, + 45056, + 46080, + 47104, + 48128, + 49152, + 50176, + 51200, + 52224, + 53248, + 54272, + 55296, + 56320, + 57344, + 58368, + 59392, + 60416, + 61440, + 62464, + 63488, + 64512, + 64512, + 64512, + 64512, + 64512, + 64512, + 64512, + 64512, + 64512, + 64512, + 64512, + 64512, + 64512, + 64512, + 64512, + 64512, + 64512, + 64512, + 64512, + 64512, + 64512, + 64512, + 64512, + 64512, + 64512, + 64512, + 64512, + 64512, + 64512, + 64512, + 64512, + 64512, + 64512, + 64512, + 64512, + 64512, + 64512, + 64512, + 64512, + 64512, + 64512, + 64512, + 64512, + 64512, + 64512, + 64512, + 64512, + 64512, + 64512, + 64512, + 64512, + 64512, + 64512, + 64512, + 64512, + 64512, + 64512, + 64512, + 64512, + 64512, + 64512, + 64512, + 64512, + 64512, + 64512, + 64512, + 64512, + 64512, + 64512, + 64512, + 64512, + 64512, + 64512, + 64512, + 64512, + 64512, + 64512, + 64512, + 64512, + 64512, + 64512, + 64512, + 64512, + 64512, + 64512, + 64512, + 64512, + 64512, + 64512, + 64512, + 64512, + 64512, + 64512, + 64512, + 64512, + 64512, + 64512, + 64512, + 64512, + 64512, + 64512, + 64512, + 64512, + 64512, + 64512, + 64512, + 64512, + 64512, + 64512, + 64512, + 64512, + 64512, + 64512, + }; + + unsigned char shift_table[512] = + { + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 23, + 22, + 21, + 20, + 19, + 18, + 17, + 16, + 15, + 14, + 13, + 13, + 13, + 13, + 13, + 13, + 13, + 13, + 13, + 13, + 13, + 13, + 13, + 13, + 13, + 13, + 13, + 13, + 13, + 13, + 13, + 13, + 13, + 13, + 13, + 13, + 13, + 13, + 13, + 13, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 13, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 23, + 22, + 21, + 20, + 19, + 18, + 17, + 16, + 15, + 14, + 13, + 13, + 13, + 13, + 13, + 13, + 13, + 13, + 13, + 13, + 13, + 13, + 13, + 13, + 13, + 13, + 13, + 13, + 13, + 13, + 13, + 13, + 13, + 13, + 13, + 13, + 13, + 13, + 13, + 13, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 13, + }; + + /* + + void generate_tables() + { + unsigned int mantissa_table[2048]; + unsigned int exponent_table[64]; + unsigned short offset_table[64]; + unsigned short base_table[512]; + unsigned char shift_table[512]; + + mantissa_table[0] = 0; + for (int i = 1; i < 1024; i++) + { + unsigned int m=i<<13; // Zero pad mantissa bits + unsigned int e=0; // Zero exponent + while(!(m&0x00800000)) // While not normalized + { + e-=0x00800000; // Decrement exponent (1<<23) + m<<=1; // Shift mantissa + } + m&=~0x00800000; // Clear leading 1 bit + e+=0x38800000; // Adjust bias ((127-14)<<23) + mantissa_table[i] = m | e; + } + for (int i = 1024; i < 2048; i++) + { + mantissa_table[i] = 0x38000000 + ((i - 1024) << 13); + } + + exponent_table[0] = 0; + exponent_table[32] = 0x80000000; + + for (int i = 1; i < 31; i++) + exponent_table[i] = i<<23; + for (int i = 33; i < 63; i++) + exponent_table[i] = 0x80000000 + ((i-32)<<23); + + exponent_table[31] = 0x47800000; + exponent_table[63] = 0xC7800000; + + offset_table[0] = 0; + offset_table[32] = 0; + for (int i = 1; i < 32; i++) + offset_table[i] = 1024; + + for(unsigned int i=0; i<256; ++i) + { + int e=i-127; + if (e<-24) // Very small numbers map to zero + { + base_table[i|0x000]=0x0000; + base_table[i|0x100]=0x8000; + shift_table[i|0x000]=24; + shift_table[i|0x100]=24; + } + else if (e<-14) // Small numbers map to denorms + { + base_table[i|0x000]=(0x0400>>(-e-14)); + base_table[i|0x100]=(0x0400>>(-e-14)) | 0x8000; + shift_table[i|0x000]=-e-1; + shift_table[i|0x100]=-e-1; + } + else if (e<=15) // Normal numbers just lose precision + { + base_table[i|0x000]=((e+15)<<10); + base_table[i|0x100]=((e+15)<<10) | 0x8000; + shift_table[i|0x000]=13; + shift_table[i|0x100]=13; + } + else if (e<128) // Large numbers map to Infinity + { + base_table[i|0x000]=0x7C00; + base_table[i|0x100]=0xFC00; + shift_table[i|0x000]=24; + shift_table[i|0x100]=24; + } + else // Infinity and NaN's stay Infinity and NaN's + { + base_table[i|0x000]=0x7C00; + base_table[i|0x100]=0xFC00; + shift_table[i|0x000]=13; + shift_table[i|0x100]=13; + } + } + + File file("tables.txt", File::create_always, File::access_read_write); + file.write_string_text("unsigned int mantissa_table[2048] ="); + file.write_string_text("{"); + for (int i = 0; i < 2048; i++) + { + file.write_string_text("\t" + Text::to_string(mantissa_table[i]) + ","); + } + file.write_string_text("};"); + file.write_string_text(""); + + file.write_string_text("unsigned int exponent_table[64] ="); + file.write_string_text("{"); + for (int i = 0; i < 64; i++) + { + file.write_string_text("\t" + Text::to_string(exponent_table[i]) + ","); + } + file.write_string_text("};"); + file.write_string_text(""); + + file.write_string_text("unsigned short offset_table[64] ="); + file.write_string_text("{"); + for (int i = 0; i < 64; i++) + { + file.write_string_text("\t" + Text::to_string(offset_table[i]) + ","); + } + file.write_string_text("};"); + file.write_string_text(""); + + file.write_string_text("unsigned short base_table[512] ="); + file.write_string_text("{"); + for (int i = 0; i < 512; i++) + { + file.write_string_text("\t" + Text::to_string(base_table[i]) + ","); + } + file.write_string_text("};"); + file.write_string_text(""); + + file.write_string_text("unsigned char shift_table[512] ="); + file.write_string_text("{"); + for (int i = 0; i < 512; i++) + { + file.write_string_text("\t" + Text::to_string(shift_table[i]) + ","); + } + file.write_string_text("};"); + file.write_string_text(""); + } + */ +} diff --git a/src/common/rendering/vulkan/accelstructs/halffloat.h b/src/common/rendering/vulkan/accelstructs/halffloat.h new file mode 100644 index 000000000..1d3fc4b42 --- /dev/null +++ b/src/common/rendering/vulkan/accelstructs/halffloat.h @@ -0,0 +1,68 @@ +/* +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any damages +** arising from the use of this software. +** +** Permission is granted to anyone to use this software for any purpose, +** including commercial applications, and to alter it and redistribute it +** freely, subject to the following restrictions: +** +** 1. The origin of this software must not be misrepresented; you must not +** claim that you wrote the original software. If you use this software +** in a product, an acknowledgment in the product documentation would be +** appreciated but is not required. +** 2. Altered source versions must be plainly marked as such, and must not be +** misrepresented as being the original software. +** 3. This notice may not be removed or altered from any source distribution. +** +** Note: Some of the libraries UICore may link to may have additional +** requirements or restrictions. +** +** Based on the paper "Fast Half Float Conversions" by Jeroen van der Zijp. +*/ + +#pragma once + +namespace HalfFloatTables +{ + extern unsigned int mantissa_table[2048]; + extern unsigned int exponent_table[64]; + extern unsigned short offset_table[64]; + extern unsigned short base_table[512]; + extern unsigned char shift_table[512]; +}; + +/// Convert half-float to float. Only works for 'normal' half-float values +inline float halfToFloatSimple(unsigned short hf) +{ + unsigned int float_value = ((hf & 0x8000) << 16) | (((hf & 0x7c00) + 0x1C000) << 13) | ((hf & 0x03FF) << 13); + void *ptr = static_cast(&float_value); + return *static_cast(ptr); +} + +/// Convert float to half-float. Only works for 'normal' half-float values +inline unsigned short floatToHalfSimple(float float_value) +{ + void *ptr = static_cast(&float_value); + unsigned int f = *static_cast(ptr); + return ((f >> 16) & 0x8000) | ((((f & 0x7f800000) - 0x38000000) >> 13) & 0x7c00) | ((f >> 13) & 0x03ff); +} + +/// Convert half-float to float +inline float halfToFloat(unsigned short hf) +{ + using namespace HalfFloatTables; + unsigned int float_value = mantissa_table[offset_table[hf >> 10] + (hf & 0x3ff)] + exponent_table[hf >> 10]; + void *ptr = static_cast(&float_value); + return *static_cast(ptr); +} + +/// Convert float to half-float +inline unsigned short floatToHalf(float float_value) +{ + using namespace HalfFloatTables; + void *ptr = static_cast(&float_value); + unsigned int f = *static_cast(ptr); + return base_table[(f >> 23) & 0x1ff] + ((f & 0x007fffff) >> shift_table[(f >> 23) & 0x1ff]); +} diff --git a/src/common/rendering/vulkan/accelstructs/vk_lightmap.cpp b/src/common/rendering/vulkan/accelstructs/vk_lightmap.cpp new file mode 100644 index 000000000..6619d4d1c --- /dev/null +++ b/src/common/rendering/vulkan/accelstructs/vk_lightmap.cpp @@ -0,0 +1,669 @@ + +#include "vk_lightmap.h" +#include "vulkan/vk_renderdevice.h" +#include "zvulkan/vulkanbuilders.h" +#include "halffloat.h" +#include "filesystem.h" + +VkLightmap::VkLightmap(VulkanRenderDevice* fb) : fb(fb) +{ + useRayQuery = fb->GetDevice()->PhysicalDevice.Features.RayQuery.rayQuery; + + submitFence = std::make_unique(fb->GetDevice()); + cmdpool = std::make_unique(fb->GetDevice(), fb->GetDevice()->GraphicsFamily); + + CreateUniformBuffer(); + CreateSceneVertexBuffer(); + CreateSceneLightBuffer(); + + CreateShaders(); + CreateRaytracePipeline(); + CreateResolvePipeline(); +} + +VkLightmap::~VkLightmap() +{ +} + +void VkLightmap::Raytrace(hwrenderer::LevelMesh* level) +{ + mesh = level; + + UpdateAccelStructDescriptors(); // To do: we only need to do this if the accel struct changes. + + BeginCommands(); + UploadUniforms(); + + for (size_t pageIndex = 0; pageIndex < atlasImages.size(); pageIndex++) + { + RenderAtlasImage(pageIndex); + } + + for (size_t pageIndex = 0; pageIndex < atlasImages.size(); pageIndex++) + { + ResolveAtlasImage(pageIndex); + } + + FinishCommands(); +} + +void VkLightmap::BeginCommands() +{ + cmdbuffer = cmdpool->createBuffer(); + cmdbuffer->begin(); +} + +void VkLightmap::FinishCommands() +{ + cmdbuffer->end(); + + QueueSubmit() + .AddCommandBuffer(cmdbuffer.get()) + .Execute(fb->GetDevice(), fb->GetDevice()->GraphicsQueue, submitFence.get()); + + VkResult result = vkWaitForFences(fb->GetDevice()->device, 1, &submitFence->fence, VK_TRUE, std::numeric_limits::max()); + if (result != VK_SUCCESS) + throw std::runtime_error("vkWaitForFences failed"); + result = vkResetFences(fb->GetDevice()->device, 1, &submitFence->fence); + if (result != VK_SUCCESS) + throw std::runtime_error("vkResetFences failed"); + cmdbuffer.reset(); +} + +void VkLightmap::RenderAtlasImage(size_t pageIndex) +{ + LightmapImage& img = atlasImages[pageIndex]; + + const auto beginPass = [&]() { + RenderPassBegin() + .RenderPass(raytrace.renderPass.get()) + .RenderArea(0, 0, atlasImageSize, atlasImageSize) + .Framebuffer(img.raytrace.Framebuffer.get()) + .Execute(cmdbuffer.get()); + + VkDeviceSize offset = 0; + cmdbuffer->bindVertexBuffers(0, 1, &sceneVertexBuffer->buffer, &offset); + cmdbuffer->bindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, raytrace.pipeline.get()); + cmdbuffer->bindDescriptorSet(VK_PIPELINE_BIND_POINT_GRAPHICS, raytrace.pipelineLayout.get(), 0, raytrace.descriptorSet0.get()); + cmdbuffer->bindDescriptorSet(VK_PIPELINE_BIND_POINT_GRAPHICS, raytrace.pipelineLayout.get(), 1, raytrace.descriptorSet1.get()); + }; + beginPass(); + + for (size_t i = 0; i < mesh->surfaces.Size(); i++) + { + hwrenderer::Surface* targetSurface = mesh->surfaces[i].get(); + if (targetSurface->atlasPageIndex != pageIndex) + continue; + + VkViewport viewport = {}; + viewport.maxDepth = 1; + viewport.x = (float)targetSurface->atlasX - 1; + viewport.y = (float)targetSurface->atlasY - 1; + viewport.width = (float)(targetSurface->texWidth + 2); + viewport.height = (float)(targetSurface->texHeight + 2); + cmdbuffer->setViewport(0, 1, &viewport); + + // Paint all surfaces part of the smoothing group into the surface + for (hwrenderer::Surface* surface : mesh->smoothingGroups[targetSurface->smoothingGroupIndex].surfaces) + { + FVector2 minUV = ToUV(surface->boundsMin, targetSurface); + FVector2 maxUV = ToUV(surface->boundsMax, targetSurface); + if (surface != targetSurface && (maxUV.X < 0.0f || maxUV.Y < 0.0f || minUV.X > 1.0f || minUV.Y > 1.0f)) + continue; // Bounding box not visible + + int firstLight = sceneLightPos; + int firstVertex = sceneVertexPos; + int lightCount = (int)surface->LightList.size(); + int vertexCount = (int)surface->verts.Size(); + if (sceneLightPos + lightCount > SceneLightBufferSize || sceneVertexPos + vertexCount > SceneVertexBufferSize) + { + printf("."); + + // Flush scene buffers + FinishCommands(); + sceneLightPos = 0; + sceneVertexPos = 0; + firstLight = 0; + firstVertex = 0; + BeginCommands(); + beginPass(); + + printf("."); + + if (sceneLightPos + lightCount > SceneLightBufferSize) + { + throw std::runtime_error("SceneLightBuffer is too small!"); + } + else if (sceneVertexPos + vertexCount > SceneVertexBufferSize) + { + throw std::runtime_error("SceneVertexBuffer is too small!"); + } + } + sceneLightPos += lightCount; + sceneVertexPos += vertexCount; + + LightInfo* lightinfo = &sceneLights[firstLight]; + for (hwrenderer::ThingLight* light : surface->LightList) + { + lightinfo->Origin = light->Origin; + lightinfo->RelativeOrigin = light->RelativeOrigin; + lightinfo->Radius = light->Radius; + lightinfo->Intensity = light->Intensity; + lightinfo->InnerAngleCos = light->InnerAngleCos; + lightinfo->OuterAngleCos = light->OuterAngleCos; + lightinfo->SpotDir = light->SpotDir; + lightinfo->Color = light->Color; + lightinfo++; + } + + PushConstants pc; + pc.LightStart = firstLight; + pc.LightEnd = firstLight + lightCount; + pc.SurfaceIndex = (int32_t)i; + pc.LightmapOrigin = targetSurface->worldOrigin - targetSurface->worldStepX - targetSurface->worldStepY; + pc.LightmapStepX = targetSurface->worldStepX * viewport.width; + pc.LightmapStepY = targetSurface->worldStepY * viewport.height; + cmdbuffer->pushConstants(raytrace.pipelineLayout.get(), VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, 0, sizeof(PushConstants), &pc); + + SceneVertex* vertex = &sceneVertices[firstVertex]; + + if (surface->type == hwrenderer::ST_FLOOR || surface->type == hwrenderer::ST_CEILING) + { + for (int idx = 0; idx < vertexCount; idx++) + { + (vertex++)->Position = ToUV(surface->verts[idx], targetSurface); + } + } + else + { + (vertex++)->Position = ToUV(surface->verts[0], targetSurface); + (vertex++)->Position = ToUV(surface->verts[2], targetSurface); + (vertex++)->Position = ToUV(surface->verts[3], targetSurface); + (vertex++)->Position = ToUV(surface->verts[1], targetSurface); + } + + cmdbuffer->draw(vertexCount, 1, firstVertex, 0); + } + } + + cmdbuffer->endRenderPass(); +} + +void VkLightmap::CreateAtlasImages() +{ + const int spacing = 3; // Note: the spacing is here to avoid that the resolve sampler finds data from other surface tiles + RectPacker packer(atlasImageSize, atlasImageSize, RectPacker::Spacing(spacing)); + + for (size_t i = 0; i < mesh->surfaces.Size(); i++) + { + hwrenderer::Surface* surface = mesh->surfaces[i].get(); + + auto result = packer.insert(surface->texWidth + 2, surface->texHeight + 2); + surface->atlasX = result.pos.x + 1; + surface->atlasY = result.pos.y + 1; + surface->atlasPageIndex = (int)result.pageIndex; + } + + for (size_t pageIndex = 0; pageIndex < packer.getNumPages(); pageIndex++) + { + atlasImages.push_back(CreateImage(atlasImageSize, atlasImageSize)); + } +} + +void VkLightmap::UploadUniforms() +{ + Uniforms uniforms = {}; + uniforms.SunDir = mesh->SunDirection; + uniforms.SunColor = mesh->SunColor; + uniforms.SunIntensity = 1.0f; + + mappedUniforms = (uint8_t*)uniformTransferBuffer->Map(0, uniformStructs * uniformStructStride); + *reinterpret_cast(mappedUniforms + uniformStructStride * uniformsIndex) = uniforms; + uniformTransferBuffer->Unmap(); + + cmdbuffer->copyBuffer(uniformTransferBuffer.get(), uniformBuffer.get()); + PipelineBarrier() + .AddBuffer(uniformBuffer.get(), VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT) + .Execute(cmdbuffer.get(), VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT); +} + +void VkLightmap::ResolveAtlasImage(size_t i) +{ + LightmapImage& img = atlasImages[i]; + + PipelineBarrier() + .AddImage(img.raytrace.Image.get(), VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT) + .Execute(cmdbuffer.get(), VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT); + + RenderPassBegin() + .RenderPass(resolve.renderPass.get()) + .RenderArea(0, 0, atlasImageSize, atlasImageSize) + .Framebuffer(img.resolve.Framebuffer.get()) + .Execute(cmdbuffer.get()); + + VkDeviceSize offset = 0; + cmdbuffer->bindVertexBuffers(0, 1, &sceneVertexBuffer->buffer, &offset); + cmdbuffer->bindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, resolve.pipeline.get()); + + auto descriptorSet = resolve.descriptorPool->allocate(resolve.descriptorSetLayout.get()); + descriptorSet->SetDebugName("resolve.descriptorSet"); + WriteDescriptors() + .AddCombinedImageSampler(descriptorSet.get(), 0, img.raytrace.View.get(), resolve.sampler.get(), VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) + .Execute(fb->GetDevice()); + cmdbuffer->bindDescriptorSet(VK_PIPELINE_BIND_POINT_GRAPHICS, resolve.pipelineLayout.get(), 0, descriptorSet.get()); + resolve.descriptorSets.push_back(std::move(descriptorSet)); + + VkViewport viewport = {}; + viewport.maxDepth = 1; + viewport.width = (float)atlasImageSize; + viewport.height = (float)atlasImageSize; + cmdbuffer->setViewport(0, 1, &viewport); + + PushConstants pc; + pc.LightStart = 0; + pc.LightEnd = 0; + pc.SurfaceIndex = 0; + pc.LightmapOrigin = FVector3(0.0f, 0.0f, 0.0f); + pc.LightmapStepX = FVector3(0.0f, 0.0f, 0.0f); + pc.LightmapStepY = FVector3(0.0f, 0.0f, 0.0f); + cmdbuffer->pushConstants(resolve.pipelineLayout.get(), VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(PushConstants), &pc); + + int firstVertex = sceneVertexPos; + int vertexCount = 4; + sceneVertexPos += vertexCount; + SceneVertex* vertex = &sceneVertices[firstVertex]; + vertex[0].Position = FVector2(0.0f, 0.0f); + vertex[1].Position = FVector2(1.0f, 0.0f); + vertex[2].Position = FVector2(1.0f, 1.0f); + vertex[3].Position = FVector2(0.0f, 1.0f); + cmdbuffer->draw(vertexCount, 1, firstVertex, 0); + + cmdbuffer->endRenderPass(); + + PipelineBarrier() + .AddImage(img.resolve.Image.get(), VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT) + .Execute(cmdbuffer.get(), VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT); + + VkBufferImageCopy region = {}; + region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + region.imageSubresource.layerCount = 1; + region.imageExtent.width = atlasImageSize; + region.imageExtent.height = atlasImageSize; + region.imageExtent.depth = 1; + cmdbuffer->copyImageToBuffer(img.resolve.Image->image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, img.Transfer->buffer, 1, ®ion); +} + +void VkLightmap::DownloadAtlasImage(size_t pageIndex) +{ + struct hvec4 + { + unsigned short x, y, z, w; + FVector3 xyz() { return FVector3(halfToFloat(x), halfToFloat(y), halfToFloat(z)); } + }; + + hvec4* pixels = (hvec4*)atlasImages[pageIndex].Transfer->Map(0, atlasImageSize * atlasImageSize * sizeof(hvec4)); + + for (size_t i = 0; i < mesh->surfaces.Size(); i++) + { + hwrenderer::Surface* surface = mesh->surfaces[i].get(); + if (surface->atlasPageIndex != pageIndex) + continue; + + int atlasX = surface->atlasX; + int atlasY = surface->atlasY; + int sampleWidth = surface->texWidth; + int sampleHeight = surface->texHeight; + + for (int y = 0; y < sampleHeight; y++) + { + FVector3* dest = &surface->texPixels[y * sampleWidth]; + hvec4* src = &pixels[atlasX + (atlasY + y) * atlasImageSize]; + for (int x = 0; x < sampleWidth; x++) + { + dest[x] = src[x].xyz(); + } + } + } + atlasImages[pageIndex].Transfer->Unmap(); +} + +FVector2 VkLightmap::ToUV(const FVector3& vert, const hwrenderer::Surface* targetSurface) +{ + FVector3 localPos = vert - targetSurface->translateWorldToLocal; + float u = (1.0f + (localPos | targetSurface->projLocalToU)) / (targetSurface->texWidth + 2); + float v = (1.0f + (localPos | targetSurface->projLocalToV)) / (targetSurface->texHeight + 2); + return FVector2(u, v); +} + +void VkLightmap::CreateShaders() +{ + std::string prefix = "#version 460\r\n"; + std::string traceprefix = "#version 460\r\n"; + if (useRayQuery) + { + traceprefix += "#extension GL_EXT_ray_query : require\r\n"; + traceprefix += "#define USE_RAYQUERY\r\n"; + } + + vertShader = ShaderBuilder() + .Type(ShaderType::Vertex) + .AddSource("VersionBlock", prefix) + .AddSource("vert.glsl", LoadPrivateShaderLump("shaders/lightmap/vert.glsl").GetChars()) + .DebugName("VkLightmap.VertShader") + .Create("VkLightmap.VertShader", fb->GetDevice()); + + fragShader = ShaderBuilder() + .Type(ShaderType::Fragment) + .AddSource("VersionBlock", traceprefix) + .AddSource("frag.glsl", LoadPrivateShaderLump("shaders/lightmap/frag.glsl").GetChars()) + .DebugName("VkLightmap.FragShader") + .Create("VkLightmap.FragShader", fb->GetDevice()); + + fragResolveShader = ShaderBuilder() + .Type(ShaderType::Fragment) + .AddSource("VersionBlock", prefix) + .AddSource("frag_resolve.glsl", LoadPrivateShaderLump("shaders/lightmap/frag_resolve.glsl").GetChars()) + .DebugName("VkLightmap.FragResolveShader") + .Create("VkLightmap.FragResolveShader", fb->GetDevice()); +} + +FString VkLightmap::LoadPrivateShaderLump(const char* lumpname) +{ + int lump = fileSystem.CheckNumForFullName(lumpname, 0); + if (lump == -1) I_Error("Unable to load '%s'", lumpname); + FileData data = fileSystem.ReadFile(lump); + return data.GetString(); +} + +void VkLightmap::CreateRaytracePipeline() +{ + raytrace.descriptorSetLayout0 = DescriptorSetLayoutBuilder() + .AddBinding(0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT) + .AddBinding(1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_FRAGMENT_BIT) + .AddBinding(2, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_FRAGMENT_BIT) + .AddBinding(3, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_FRAGMENT_BIT) + .AddBinding(4, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_FRAGMENT_BIT) + .DebugName("raytrace.descriptorSetLayout0") + .Create(fb->GetDevice()); + + if (useRayQuery) + { + raytrace.descriptorSetLayout1 = DescriptorSetLayoutBuilder() + .AddBinding(0, VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR, 1, VK_SHADER_STAGE_FRAGMENT_BIT) + .DebugName("raytrace.descriptorSetLayout1") + .Create(fb->GetDevice()); + } + else + { + raytrace.descriptorSetLayout1 = DescriptorSetLayoutBuilder() + .AddBinding(0, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_FRAGMENT_BIT) + .AddBinding(1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_FRAGMENT_BIT) + .AddBinding(2, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_FRAGMENT_BIT) + .DebugName("raytrace.descriptorSetLayout1") + .Create(fb->GetDevice()); + } + + raytrace.pipelineLayout = PipelineLayoutBuilder() + .AddSetLayout(raytrace.descriptorSetLayout0.get()) + .AddSetLayout(raytrace.descriptorSetLayout1.get()) + .AddPushConstantRange(VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, 0, sizeof(PushConstants)) + .DebugName("raytrace.pipelineLayout") + .Create(fb->GetDevice()); + + raytrace.renderPass = RenderPassBuilder() + .AddAttachment( + VK_FORMAT_R16G16B16A16_SFLOAT, + VK_SAMPLE_COUNT_4_BIT, + VK_ATTACHMENT_LOAD_OP_DONT_CARE, + VK_ATTACHMENT_STORE_OP_STORE, + VK_IMAGE_LAYOUT_UNDEFINED, + VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL) + .AddSubpass() + .AddSubpassColorAttachmentRef(0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL) + .AddExternalSubpassDependency( + VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, + VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, + VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, + VK_ACCESS_COLOR_ATTACHMENT_READ_BIT) + .DebugName("raytrace.renderpass") + .Create(fb->GetDevice()); + + raytrace.pipeline = GraphicsPipelineBuilder() + .Layout(raytrace.pipelineLayout.get()) + .RenderPass(raytrace.renderPass.get()) + .AddVertexShader(vertShader.get()) + .AddFragmentShader(fragShader.get()) + .AddVertexBufferBinding(0, sizeof(SceneVertex)) + .AddVertexAttribute(0, 0, VK_FORMAT_R32G32_SFLOAT, offsetof(SceneVertex, Position)) + .Topology(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN) + .AddDynamicState(VK_DYNAMIC_STATE_VIEWPORT) + .RasterizationSamples(VK_SAMPLE_COUNT_4_BIT) + .Viewport(0.0f, 0.0f, 0.0f, 0.0f) + .Scissor(0, 0, 4096, 4096) + .DebugName("raytrace.pipeline") + .Create(fb->GetDevice()); + + raytrace.descriptorPool0 = DescriptorPoolBuilder() + .AddPoolSize(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1) + .AddPoolSize(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 4) + .MaxSets(1) + .DebugName("raytrace.descriptorPool0") + .Create(fb->GetDevice()); + + if (useRayQuery) + { + raytrace.descriptorPool1 = DescriptorPoolBuilder() + .AddPoolSize(VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR, 1) + .MaxSets(1) + .DebugName("raytrace.descriptorPool1") + .Create(fb->GetDevice()); + } + else + { + raytrace.descriptorPool1 = DescriptorPoolBuilder() + .AddPoolSize(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 3) + .MaxSets(1) + .DebugName("raytrace.descriptorPool1") + .Create(fb->GetDevice()); + } + + raytrace.descriptorSet0 = raytrace.descriptorPool0->allocate(raytrace.descriptorSetLayout0.get()); + raytrace.descriptorSet0->SetDebugName("raytrace.descriptorSet1"); + + raytrace.descriptorSet1 = raytrace.descriptorPool1->allocate(raytrace.descriptorSetLayout1.get()); + raytrace.descriptorSet1->SetDebugName("raytrace.descriptorSet1"); +} + +void VkLightmap::UpdateAccelStructDescriptors() +{ + // To do: fetch this from VkDescriptorSetManager - perhaps manage it all over there? + +#if 0 + if (useRayQuery) + { + WriteDescriptors() + .AddAccelerationStructure(raytrace.descriptorSet1.get(), 0, tlAccelStruct.get()) + .Execute(fb->GetDevice()); + } + else + { + WriteDescriptors() + .AddBuffer(raytrace.descriptorSet1.get(), 0, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, nodesBuffer.get()) + .AddBuffer(raytrace.descriptorSet1.get(), 1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, vertexBuffer.get()) + .AddBuffer(raytrace.descriptorSet1.get(), 2, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, indexBuffer.get()) + .Execute(fb->GetDevice()); + } + + WriteDescriptors() + .AddBuffer(raytrace.descriptorSet0.get(), 0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, uniformBuffer.get(), 0, sizeof(Uniforms)) + .AddBuffer(raytrace.descriptorSet0.get(), 1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, surfaceIndexBuffer.get()) + .AddBuffer(raytrace.descriptorSet0.get(), 2, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, surfaceBuffer.get()) + .AddBuffer(raytrace.descriptorSet0.get(), 3, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, sceneLightBuffer.get()) + .AddBuffer(raytrace.descriptorSet0.get(), 4, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, portalBuffer.get()) + .Execute(fb->GetDevice()); +#endif +} + +void VkLightmap::CreateResolvePipeline() +{ + resolve.descriptorSetLayout = DescriptorSetLayoutBuilder() + .AddBinding(0, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_FRAGMENT_BIT) + .DebugName("resolve.descriptorSetLayout") + .Create(fb->GetDevice()); + + resolve.pipelineLayout = PipelineLayoutBuilder() + .AddSetLayout(resolve.descriptorSetLayout.get()) + .AddPushConstantRange(VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(PushConstants)) + .DebugName("resolve.pipelineLayout") + .Create(fb->GetDevice()); + + resolve.renderPass = RenderPassBuilder() + .AddAttachment( + VK_FORMAT_R16G16B16A16_SFLOAT, + VK_SAMPLE_COUNT_1_BIT, + VK_ATTACHMENT_LOAD_OP_DONT_CARE, + VK_ATTACHMENT_STORE_OP_STORE, + VK_IMAGE_LAYOUT_UNDEFINED, + VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL) + .AddSubpass() + .AddSubpassColorAttachmentRef(0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL) + .AddExternalSubpassDependency( + VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, + VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, + VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, + VK_ACCESS_COLOR_ATTACHMENT_READ_BIT) + .DebugName("resolve.renderpass") + .Create(fb->GetDevice()); + + resolve.pipeline = GraphicsPipelineBuilder() + .Layout(resolve.pipelineLayout.get()) + .RenderPass(resolve.renderPass.get()) + .AddVertexShader(vertShader.get()) + .AddFragmentShader(fragResolveShader.get()) + .AddVertexBufferBinding(0, sizeof(SceneVertex)) + .AddVertexAttribute(0, 0, VK_FORMAT_R32G32_SFLOAT, offsetof(SceneVertex, Position)) + .Topology(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN) + .AddDynamicState(VK_DYNAMIC_STATE_VIEWPORT) + .Viewport(0.0f, 0.0f, 0.0f, 0.0f) + .Scissor(0, 0, 4096, 4096) + .DebugName("resolve.pipeline") + .Create(fb->GetDevice()); + + resolve.descriptorPool = DescriptorPoolBuilder() + .AddPoolSize(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 256) + .MaxSets(256) + .DebugName("resolve.descriptorPool") + .Create(fb->GetDevice()); + + resolve.sampler = SamplerBuilder() + .DebugName("resolve.Sampler") + .Create(fb->GetDevice()); +} + +LightmapImage VkLightmap::CreateImage(int width, int height) +{ + LightmapImage img; + + img.raytrace.Image = ImageBuilder() + .Usage(VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT) + .Format(VK_FORMAT_R16G16B16A16_SFLOAT) + .Size(width, height) + .Samples(VK_SAMPLE_COUNT_4_BIT) + .DebugName("LightmapImage.raytrace.Image") + .Create(fb->GetDevice()); + + img.raytrace.View = ImageViewBuilder() + .Image(img.raytrace.Image.get(), VK_FORMAT_R16G16B16A16_SFLOAT) + .DebugName("LightmapImage.raytrace.View") + .Create(fb->GetDevice()); + + img.raytrace.Framebuffer = FramebufferBuilder() + .RenderPass(raytrace.renderPass.get()) + .Size(width, height) + .AddAttachment(img.raytrace.View.get()) + .DebugName("LightmapImage.raytrace.Framebuffer") + .Create(fb->GetDevice()); + + img.resolve.Image = ImageBuilder() + .Usage(VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT) + .Format(VK_FORMAT_R16G16B16A16_SFLOAT) + .Size(width, height) + .DebugName("LightmapImage.resolve.Image") + .Create(fb->GetDevice()); + + img.resolve.View = ImageViewBuilder() + .Image(img.resolve.Image.get(), VK_FORMAT_R16G16B16A16_SFLOAT) + .DebugName("LightmapImage.resolve.View") + .Create(fb->GetDevice()); + + img.resolve.Framebuffer = FramebufferBuilder() + .RenderPass(resolve.renderPass.get()) + .Size(width, height) + .AddAttachment(img.resolve.View.get()) + .DebugName("LightmapImage.resolve.Framebuffer") + .Create(fb->GetDevice()); + + img.Transfer = BufferBuilder() + .Size(width * height * sizeof(FVector4)) + .Usage(VK_IMAGE_USAGE_TRANSFER_DST_BIT, VMA_MEMORY_USAGE_CPU_ONLY) + .DebugName("LightmapImage.Transfer") + .Create(fb->GetDevice()); + + return img; +} + +void VkLightmap::CreateUniformBuffer() +{ + VkDeviceSize align = fb->GetDevice()->PhysicalDevice.Properties.Properties.limits.minUniformBufferOffsetAlignment; + uniformStructStride = (sizeof(Uniforms) + align - 1) / align * align; + + uniformBuffer = BufferBuilder() + .Usage(VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT) + .Size(uniformStructs * uniformStructStride) + .DebugName("uniformBuffer") + .Create(fb->GetDevice()); + + uniformTransferBuffer = BufferBuilder() + .Usage(VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VMA_MEMORY_USAGE_CPU_TO_GPU) + .Size(uniformStructs * uniformStructStride) + .DebugName("uniformTransferBuffer") + .Create(fb->GetDevice()); +} + +void VkLightmap::CreateSceneVertexBuffer() +{ + size_t size = sizeof(SceneVertex) * SceneVertexBufferSize; + + sceneVertexBuffer = BufferBuilder() + .Usage( + VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, + VMA_MEMORY_USAGE_UNKNOWN, VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT | VMA_ALLOCATION_CREATE_MAPPED_BIT) + .MemoryType( + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) + .Size(size) + .DebugName("SceneVertexBuffer") + .Create(fb->GetDevice()); + + sceneVertices = (SceneVertex*)sceneVertexBuffer->Map(0, size); + sceneVertexPos = 0; +} + +void VkLightmap::CreateSceneLightBuffer() +{ + size_t size = sizeof(LightInfo) * SceneLightBufferSize; + + sceneLightBuffer = BufferBuilder() + .Usage( + VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, + VMA_MEMORY_USAGE_UNKNOWN, VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT | VMA_ALLOCATION_CREATE_MAPPED_BIT) + .MemoryType( + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) + .Size(size) + .DebugName("SceneLightBuffer") + .Create(fb->GetDevice()); + + sceneLights = (LightInfo*)sceneLightBuffer->Map(0, size); + sceneLightPos = 0; +} diff --git a/src/common/rendering/vulkan/accelstructs/vk_lightmap.h b/src/common/rendering/vulkan/accelstructs/vk_lightmap.h new file mode 100644 index 000000000..a4a29c977 --- /dev/null +++ b/src/common/rendering/vulkan/accelstructs/vk_lightmap.h @@ -0,0 +1,166 @@ +#pragma once + +#include "common/rendering/hwrenderer/data/hw_levelmesh.h" +#include "zvulkan/vulkanobjects.h" +#include + +typedef dp::rect_pack::RectPacker RectPacker; + +class VulkanRenderDevice; +class FString; + +struct Uniforms +{ + FVector3 SunDir; + float Padding1; + FVector3 SunColor; + float SunIntensity; +}; + +struct PushConstants +{ + uint32_t LightStart; + uint32_t LightEnd; + int32_t SurfaceIndex; + int32_t PushPadding1; + FVector3 LightmapOrigin; + float PushPadding2; + FVector3 LightmapStepX; + float PushPadding3; + FVector3 LightmapStepY; + float PushPadding4; +}; + +struct LightmapImage +{ + struct + { + std::unique_ptr Image; + std::unique_ptr View; + std::unique_ptr Framebuffer; + } raytrace; + + struct + { + std::unique_ptr Image; + std::unique_ptr View; + std::unique_ptr Framebuffer; + } resolve; + + std::unique_ptr Transfer; +}; + +struct SceneVertex +{ + FVector2 Position; +}; + +struct LightInfo +{ + FVector3 Origin; + float Padding0; + FVector3 RelativeOrigin; + float Padding1; + float Radius; + float Intensity; + float InnerAngleCos; + float OuterAngleCos; + FVector3 SpotDir; + float Padding2; + FVector3 Color; + float Padding3; +}; + +static_assert(sizeof(LightInfo) == sizeof(float) * 20); + +class VkLightmap +{ +public: + VkLightmap(VulkanRenderDevice* fb); + ~VkLightmap(); + + void Raytrace(hwrenderer::LevelMesh* level); + +private: + void UpdateAccelStructDescriptors(); + + void BeginCommands(); + void FinishCommands(); + + void UploadUniforms(); + void CreateAtlasImages(); + void RenderAtlasImage(size_t pageIndex); + void ResolveAtlasImage(size_t pageIndex); + void DownloadAtlasImage(size_t pageIndex); + + LightmapImage CreateImage(int width, int height); + + void CreateShaders(); + void CreateRaytracePipeline(); + void CreateResolvePipeline(); + void CreateUniformBuffer(); + void CreateSceneVertexBuffer(); + void CreateSceneLightBuffer(); + + static FVector2 ToUV(const FVector3& vert, const hwrenderer::Surface* targetSurface); + + static FString LoadPrivateShaderLump(const char* lumpname); + + VulkanRenderDevice* fb = nullptr; + hwrenderer::LevelMesh* mesh = nullptr; + + bool useRayQuery = true; + + uint8_t* mappedUniforms = nullptr; + int uniformsIndex = 0; + int uniformStructs = 256; + VkDeviceSize uniformStructStride = sizeof(Uniforms); + + static const int SceneVertexBufferSize = 1 * 1024 * 1024; + std::unique_ptr sceneVertexBuffer; + SceneVertex* sceneVertices = nullptr; + int sceneVertexPos = 0; + + static const int SceneLightBufferSize = 2 * 1024 * 1024; + std::unique_ptr sceneLightBuffer; + LightInfo* sceneLights = nullptr; + int sceneLightPos = 0; + + std::unique_ptr vertShader; + std::unique_ptr fragShader; + std::unique_ptr fragResolveShader; + + struct + { + std::unique_ptr descriptorSetLayout0; + std::unique_ptr descriptorSetLayout1; + std::unique_ptr pipelineLayout; + std::unique_ptr pipeline; + std::unique_ptr renderPass; + std::unique_ptr descriptorPool0; + std::unique_ptr descriptorPool1; + std::unique_ptr descriptorSet0; + std::unique_ptr descriptorSet1; + } raytrace; + + struct + { + std::unique_ptr descriptorSetLayout; + std::unique_ptr pipelineLayout; + std::unique_ptr pipeline; + std::unique_ptr renderPass; + std::unique_ptr descriptorPool; + std::vector> descriptorSets; + std::unique_ptr sampler; + } resolve; + + std::unique_ptr uniformBuffer; + std::unique_ptr uniformTransferBuffer; + + std::unique_ptr submitFence; + std::unique_ptr cmdpool; + std::unique_ptr cmdbuffer; + + std::vector atlasImages; + static const int atlasImageSize = 2048; +}; diff --git a/wadsrc/static/shaders/lightmap/frag.glsl b/wadsrc/static/shaders/lightmap/frag.glsl new file mode 100644 index 000000000..d3eccd8fe --- /dev/null +++ b/wadsrc/static/shaders/lightmap/frag.glsl @@ -0,0 +1,563 @@ + +#if defined(USE_RAYQUERY) +layout(set = 1, binding = 0) uniform accelerationStructureEXT acc; +#else +struct CollisionNode +{ + vec3 center; + float padding1; + vec3 extents; + float padding2; + int left; + int right; + int element_index; + int padding3; +}; +layout(std430, set = 1, binding = 0) buffer NodeBuffer +{ + int nodesRoot; + int nodebufferPadding1; + int nodebufferPadding2; + int nodebufferPadding3; + CollisionNode nodes[]; +}; +layout(std430, set = 1, binding = 1) buffer VertexBuffer { vec4 vertices[]; }; +layout(std430, set = 1, binding = 2) buffer ElementBuffer { int elements[]; }; +#endif + +layout(set = 0, binding = 0) uniform Uniforms +{ + vec3 SunDir; + float Padding1; + vec3 SunColor; + float SunIntensity; +}; + +struct SurfaceInfo +{ + vec3 Normal; + float Sky; + float SamplingDistance; + uint PortalIndex; + float Padding1, Padding2; +}; + +struct PortalInfo +{ + mat4 Transformation; +}; + +struct LightInfo +{ + vec3 Origin; + float Padding0; + vec3 RelativeOrigin; + float Padding1; + float Radius; + float Intensity; + float InnerAngleCos; + float OuterAngleCos; + vec3 SpotDir; + float Padding2; + vec3 Color; + float Padding3; +}; + +layout(set = 0, binding = 1) buffer SurfaceIndexBuffer { uint surfaceIndices[]; }; +layout(set = 0, binding = 2) buffer SurfaceBuffer { SurfaceInfo surfaces[]; }; +layout(set = 0, binding = 3) buffer LightBuffer { LightInfo lights[]; }; +layout(set = 0, binding = 4) buffer PortalBuffer { PortalInfo portals[]; }; + +layout(push_constant) uniform PushConstants +{ + uint LightStart; + uint LightEnd; + int SurfaceIndex; + int PushPadding1; + vec3 LightmapOrigin; + float PushPadding2; + vec3 LightmapStepX; + float PushPadding3; + vec3 LightmapStepY; + float PushPadding4; +}; + +layout(location = 0) centroid in vec3 worldpos; +layout(location = 0) out vec4 fragcolor; + +vec3 TraceSunLight(vec3 origin); +vec3 TraceLight(vec3 origin, vec3 normal, LightInfo light); +float TraceAmbientOcclusion(vec3 origin, vec3 normal); +vec2 Hammersley(uint i, uint N); +float RadicalInverse_VdC(uint bits); + +bool TraceAnyHit(vec3 origin, float tmin, vec3 dir, float tmax); +bool TracePoint(vec3 origin, vec3 target, float tmin, vec3 dir, float tmax); +int TraceFirstHitTriangle(vec3 origin, float tmin, vec3 dir, float tmax); +int TraceFirstHitTriangleT(vec3 origin, float tmin, vec3 dir, float tmax, out float t); + +void main() +{ + vec3 normal = surfaces[SurfaceIndex].Normal; + vec3 origin = worldpos + normal * 0.1; + + vec3 incoming = TraceSunLight(origin); + + for (uint j = LightStart; j < LightEnd; j++) + { + incoming += TraceLight(origin, normal, lights[j]); + } + +#if defined(USE_RAYQUERY) // The non-rtx version of TraceFirstHitTriangle is too slow to do AO without the shader getting killed ;( + incoming.rgb *= TraceAmbientOcclusion(origin, normal); +#endif + + fragcolor = vec4(incoming, 1.0); +} + +vec3 TraceLight(vec3 origin, vec3 normal, LightInfo light) +{ + const float minDistance = 0.01; + vec3 incoming = vec3(0.0); + float dist = distance(light.RelativeOrigin, origin); + if (dist > minDistance && dist < light.Radius) + { + vec3 dir = normalize(light.RelativeOrigin - origin); + + float distAttenuation = max(1.0 - (dist / light.Radius), 0.0); + float angleAttenuation = 1.0f; + if (SurfaceIndex >= 0) + { + angleAttenuation = max(dot(normal, dir), 0.0); + } + float spotAttenuation = 1.0; + if (light.OuterAngleCos > -1.0) + { + float cosDir = dot(dir, light.SpotDir); + spotAttenuation = smoothstep(light.OuterAngleCos, light.InnerAngleCos, cosDir); + spotAttenuation = max(spotAttenuation, 0.0); + } + + float attenuation = distAttenuation * angleAttenuation * spotAttenuation; + if (attenuation > 0.0) + { + if(TracePoint(origin, light.Origin, minDistance, dir, dist)) + { + incoming.rgb += light.Color * (attenuation * light.Intensity); + } + } + } + + return incoming; +} + +vec3 TraceSunLight(vec3 origin) +{ + const float minDistance = 0.01; + vec3 incoming = vec3(0.0); + const float dist = 32768.0; + + int primitiveID = TraceFirstHitTriangle(origin, minDistance, SunDir, dist); + if (primitiveID != -1) + { + SurfaceInfo surface = surfaces[surfaceIndices[primitiveID]]; + incoming.rgb += SunColor * SunIntensity * surface.Sky; + } + return incoming; +} + +float TraceAmbientOcclusion(vec3 origin, vec3 normal) +{ + const float minDistance = 0.05; + const float aoDistance = 100; + const int SampleCount = 2048; + + vec3 N = normal; + vec3 up = abs(N.x) < abs(N.y) ? vec3(1.0, 0.0, 0.0) : vec3(0.0, 1.0, 0.0); + vec3 tangent = normalize(cross(up, N)); + vec3 bitangent = cross(N, tangent); + + float ambience = 0.0f; + for (uint i = 0; i < SampleCount; i++) + { + vec2 Xi = Hammersley(i, SampleCount); + vec3 H = normalize(vec3(Xi.x * 2.0f - 1.0f, Xi.y * 2.0f - 1.0f, 1.5 - length(Xi))); + vec3 L = H.x * tangent + H.y * bitangent + H.z * N; + + float hitDistance; + int primitiveID = TraceFirstHitTriangleT(origin, minDistance, L, aoDistance, hitDistance); + if (primitiveID != -1) + { + SurfaceInfo surface = surfaces[surfaceIndices[primitiveID]]; + if (surface.Sky == 0.0) + { + ambience += clamp(hitDistance / aoDistance, 0.0, 1.0); + } + } + else + { + ambience += 1.0; + } + } + return ambience / float(SampleCount); +} + +vec2 Hammersley(uint i, uint N) +{ + return vec2(float(i) / float(N), RadicalInverse_VdC(i)); +} + +float RadicalInverse_VdC(uint bits) +{ + bits = (bits << 16u) | (bits >> 16u); + bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u); + bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u); + bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u); + bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u); + return float(bits) * 2.3283064365386963e-10f; // / 0x100000000 +} + +#if defined(USE_RAYQUERY) + +int TraceFirstHitTriangleNoPortal(vec3 origin, float tmin, vec3 dir, float tmax, out float t) +{ + rayQueryEXT rayQuery; + rayQueryInitializeEXT(rayQuery, acc, gl_RayFlagsTerminateOnFirstHitEXT, 0xFF, origin, tmin, dir, tmax); + + while(rayQueryProceedEXT(rayQuery)) + { + if (rayQueryGetIntersectionTypeEXT(rayQuery, false) == gl_RayQueryCommittedIntersectionTriangleEXT) + { + rayQueryConfirmIntersectionEXT(rayQuery); + } + } + + if (rayQueryGetIntersectionTypeEXT(rayQuery, true) == gl_RayQueryCommittedIntersectionTriangleEXT) + { + t = rayQueryGetIntersectionTEXT(rayQuery, true); + return rayQueryGetIntersectionPrimitiveIndexEXT(rayQuery, true); + } + else + { + t = tmax; + return -1; + } +} + +/* +bool TraceAnyHit(vec3 origin, float tmin, vec3 dir, float tmax) +{ + rayQueryEXT rayQuery; + rayQueryInitializeEXT(rayQuery, acc, gl_RayFlagsTerminateOnFirstHitEXT, 0xFF, origin, tmin, dir, tmax); + while(rayQueryProceedEXT(rayQuery)) { } + return rayQueryGetIntersectionTypeEXT(rayQuery, true) != gl_RayQueryCommittedIntersectionNoneEXT; +} +*/ + +#else + +struct RayBBox +{ + vec3 start, end; + vec3 c, w, v; +}; + +RayBBox create_ray(vec3 ray_start, vec3 ray_end) +{ + RayBBox ray; + ray.start = ray_start; + ray.end = ray_end; + ray.c = (ray_start + ray_end) * 0.5; + ray.w = ray_end - ray.c; + ray.v = abs(ray.w); + return ray; +} + +bool overlap_bv_ray(RayBBox ray, int a) +{ + vec3 v = ray.v; + vec3 w = ray.w; + vec3 h = nodes[a].extents; + vec3 c = ray.c - nodes[a].center; + + if (abs(c.x) > v.x + h.x || + abs(c.y) > v.y + h.y || + abs(c.z) > v.z + h.z) + { + return false; + } + + if (abs(c.y * w.z - c.z * w.y) > h.y * v.z + h.z * v.y || + abs(c.x * w.z - c.z * w.x) > h.x * v.z + h.z * v.x || + abs(c.x * w.y - c.y * w.x) > h.x * v.y + h.y * v.x) + { + return false; + } + + return true; +} + +#define FLT_EPSILON 1.192092896e-07F // smallest such that 1.0+FLT_EPSILON != 1.0 + +float intersect_triangle_ray(RayBBox ray, int a, out float barycentricB, out float barycentricC) +{ + int start_element = nodes[a].element_index; + + vec3 p[3]; + p[0] = vertices[elements[start_element]].xyz; + p[1] = vertices[elements[start_element + 1]].xyz; + p[2] = vertices[elements[start_element + 2]].xyz; + + // Moeller-Trumbore ray-triangle intersection algorithm: + + vec3 D = ray.end - ray.start; + + // Find vectors for two edges sharing p[0] + vec3 e1 = p[1] - p[0]; + vec3 e2 = p[2] - p[0]; + + // Begin calculating determinant - also used to calculate u parameter + vec3 P = cross(D, e2); + float det = dot(e1, P); + + // Backface check + //if (det < 0.0f) + // return 1.0f; + + // If determinant is near zero, ray lies in plane of triangle + if (det > -FLT_EPSILON && det < FLT_EPSILON) + return 1.0f; + + float inv_det = 1.0f / det; + + // Calculate distance from p[0] to ray origin + vec3 T = ray.start - p[0]; + + // Calculate u parameter and test bound + float u = dot(T, P) * inv_det; + + // Check if the intersection lies outside of the triangle + if (u < 0.f || u > 1.f) + return 1.0f; + + // Prepare to test v parameter + vec3 Q = cross(T, e1); + + // Calculate V parameter and test bound + float v = dot(D, Q) * inv_det; + + // The intersection lies outside of the triangle + if (v < 0.f || u + v > 1.f) + return 1.0f; + + float t = dot(e2, Q) * inv_det; + if (t <= FLT_EPSILON) + return 1.0f; + + // Return hit location on triangle in barycentric coordinates + barycentricB = u; + barycentricC = v; + + return t; +} + +bool is_leaf(int node_index) +{ + return nodes[node_index].element_index != -1; +} + +/* +bool TraceAnyHit(vec3 origin, float tmin, vec3 dir, float tmax) +{ + if (tmax <= 0.0f) + return false; + + RayBBox ray = create_ray(origin, origin + dir * tmax); + tmin /= tmax; + + int stack[64]; + int stackIndex = 0; + stack[stackIndex++] = nodesRoot; + do + { + int a = stack[--stackIndex]; + if (overlap_bv_ray(ray, a)) + { + if (is_leaf(a)) + { + float baryB, baryC; + float t = intersect_triangle_ray(ray, a, baryB, baryC); + if (t >= tmin && t < 1.0) + { + return true; + } + } + else + { + stack[stackIndex++] = nodes[a].right; + stack[stackIndex++] = nodes[a].left; + } + } + } while (stackIndex > 0); + return false; +} +*/ + +struct TraceHit +{ + float fraction; + int triangle; + float b; + float c; +}; + +TraceHit find_first_hit(RayBBox ray) +{ + TraceHit hit; + hit.fraction = 1.0; + hit.triangle = -1; + hit.b = 0.0; + hit.c = 0.0; + + int stack[64]; + int stackIndex = 0; + stack[stackIndex++] = nodesRoot; + do + { + int a = stack[--stackIndex]; + if (overlap_bv_ray(ray, a)) + { + if (is_leaf(a)) + { + float baryB, baryC; + float t = intersect_triangle_ray(ray, a, baryB, baryC); + if (t < hit.fraction) + { + hit.fraction = t; + hit.triangle = nodes[a].element_index / 3; + hit.b = baryB; + hit.c = baryC; + } + } + else + { + stack[stackIndex++] = nodes[a].right; + stack[stackIndex++] = nodes[a].left; + } + } + } while (stackIndex > 0); + return hit; +} + +int TraceFirstHitTriangleNoPortal(vec3 origin, float tmin, vec3 dir, float tmax, out float tparam) +{ + // Perform segmented tracing to keep the ray AABB box smaller + vec3 ray_start = origin; + vec3 ray_end = origin + dir * tmax; + vec3 ray_dir = dir; + float tracedist = tmax; + float segmentlen = max(200.0, tracedist / 20.0); + for (float t = 0.0; t < tracedist; t += segmentlen) + { + float segstart = t; + float segend = min(t + segmentlen, tracedist); + + RayBBox ray = create_ray(ray_start + ray_dir * segstart, ray_start + ray_dir * segend); + TraceHit hit = find_first_hit(ray); + if (hit.fraction < 1.0) + { + tparam = hit.fraction = segstart * (1.0 - hit.fraction) + segend * hit.fraction; + return hit.triangle; + } + } + + tparam = tracedist; + return -1; +} + + +#endif + +int TraceFirstHitTriangleT(vec3 origin, float tmin, vec3 dir, float tmax, out float t) +{ + int primitiveID; + while(true) + { + primitiveID = TraceFirstHitTriangleNoPortal(origin, tmin, dir, tmax, t); + + if(primitiveID < 0) + { + break; + } + + SurfaceInfo surface = surfaces[surfaceIndices[primitiveID]]; + + if(surface.PortalIndex == 0) + { + break; + } + + // Portal was hit: Apply transformation onto the ray + mat4 transformationMatrix = portals[surface.PortalIndex].Transformation; + + origin = (transformationMatrix * vec4(origin + dir * t, 1.0)).xyz; + dir = (transformationMatrix * vec4(dir, 0.0)).xyz; + tmax -= t; + } + return primitiveID; +} + +int TraceFirstHitTriangle(vec3 origin, float tmin, vec3 dir, float tmax) +{ + float t; + return TraceFirstHitTriangleT(origin, tmin, dir, tmax, t); +} + +bool TraceAnyHit(vec3 origin, float tmin, vec3 dir, float tmax) +{ + return TraceFirstHitTriangle(origin, tmin, dir, tmax) >= 0; +} + +bool TracePoint(vec3 origin, vec3 target, float tmin, vec3 dir, float tmax) +{ + int primitiveID; + float t; + while(true) + { + t = tmax; + primitiveID = TraceFirstHitTriangleNoPortal(origin, tmin, dir, tmax, t); + + origin += dir * t; + tmax -= t; + + if(primitiveID < 0) + { + // We didn't hit anything + break; + } + + SurfaceInfo surface = surfaces[surfaceIndices[primitiveID]]; + + if(surface.PortalIndex == 0) + { + break; + } + + if(dot(surface.Normal, dir) >= 0.0) + { + continue; + } + + mat4 transformationMatrix = portals[surface.PortalIndex].Transformation; + origin = (transformationMatrix * vec4(origin, 1.0)).xyz; + dir = (transformationMatrix * vec4(dir, 0.0)).xyz; + +#if defined(USE_RAYQUERY) +#else + origin += dir * tmin; + tmax -= tmin; +#endif + } + + return distance(origin, target) <= 1.0; +} diff --git a/wadsrc/static/shaders/lightmap/frag_resolve.glsl b/wadsrc/static/shaders/lightmap/frag_resolve.glsl new file mode 100644 index 000000000..f7f270722 --- /dev/null +++ b/wadsrc/static/shaders/lightmap/frag_resolve.glsl @@ -0,0 +1,46 @@ + +layout(set = 0, binding = 0) uniform sampler2DMS tex; + +layout(location = 0) in vec3 worldpos; +layout(location = 0) out vec4 fragcolor; + +vec4 samplePixel(ivec2 pos, int count) +{ + vec4 c = vec4(0.0); + for (int i = 0; i < count; i++) + { + c += texelFetch(tex, pos, i); + } + if (c.a > 0.0) + c /= c.a; + return c; +} + +void main() +{ + int count = textureSamples(tex); + ivec2 size = textureSize(tex); + ivec2 pos = ivec2(gl_FragCoord.xy); + + vec4 c = samplePixel(pos, count); + if (c.a == 0.0) + { + for (int y = -1; y <= 1; y++) + { + for (int x = -1; x <= 1; x++) + { + if (x != 0 || y != 0) + { + ivec2 pos2; + pos2.x = clamp(pos.x + x, 0, size.x - 1); + pos2.y = clamp(pos.y + y, 0, size.y - 1); + c += samplePixel(pos2, count); + } + } + } + if (c.a > 0.0) + c /= c.a; + } + + fragcolor = c; +} diff --git a/wadsrc/static/shaders/lightmap/vert.glsl b/wadsrc/static/shaders/lightmap/vert.glsl new file mode 100644 index 000000000..c70c239b6 --- /dev/null +++ b/wadsrc/static/shaders/lightmap/vert.glsl @@ -0,0 +1,23 @@ + +layout(push_constant) uniform PushConstants +{ + uint LightStart; + uint LightEnd; + int SurfaceIndex; + int PushPadding1; + vec3 LightmapOrigin; + float PushPadding2; + vec3 LightmapStepX; + float PushPadding3; + vec3 LightmapStepY; + float PushPadding4; +}; + +layout(location = 0) in vec2 aPosition; +layout(location = 0) out vec3 worldpos; + +void main() +{ + worldpos = LightmapOrigin + LightmapStepX * aPosition.x + LightmapStepY * aPosition.y; + gl_Position = vec4(aPosition * 2.0 - 1.0, 0.0, 1.0); +}