Compare commits

...

73 commits

Author SHA1 Message Date
Magnus Norddahl
41bd91e44c
Update README.md 2024-12-08 12:21:51 +01:00
nashmuhandes
5fd4056750 Use a light_ prefix for the soft shadows, in consideration for potential future expansions with regards to lights. 2024-10-06 22:04:02 +08:00
nashmuhandes
3750bdd79a Rename SourceRadius to SoftShadowRadius 2024-06-15 18:08:20 +08:00
nashmuhandes
6bdb4c28ca Specs fix 2024-04-22 22:42:15 +08:00
nashmuhandes
f0a1402ab1 Fix wrong default value of lm_sampledist in the readme 2024-04-18 01:23:24 +08:00
Magnus Norddahl
ea222f8ec2 Locate resources automatically if the map file is in a maps folder inside the resources folder 2024-03-25 01:13:36 +01:00
Magnus Norddahl
a6e4290431 Hook up texture loading 2024-03-24 07:09:44 +01:00
Magnus Norddahl
e1fe4658b5 Fix compile error on macOS 2024-03-24 05:25:02 +01:00
Magnus Norddahl
11e2931548 Fix issue with rays passing through 3d floor sides 2024-03-24 05:23:58 +01:00
dpjudas
ff91b34939 Add a bit more of the texture loading 2024-03-23 04:17:28 +01:00
Magnus Norddahl
4d37a89070 Remove call to Sleep 2024-03-11 16:23:57 +01:00
Magnus Norddahl
2af5bc8611 Fix compiler error 2024-03-11 04:25:56 +01:00
Magnus Norddahl
e28879187c Add a level mesh viewer (--viewer cmdline arg) for better debugging 2024-03-11 04:25:30 +01:00
nashmuhandes
6f779c0351 Highlight ZDRayInfo being a requirement for lightmaps, add VKDoom to the readme 2024-03-08 04:28:17 +08:00
Magnus Norddahl
522833c591 Remove lm prefix from source radius as dynlights support them too 2024-03-05 16:12:13 +01:00
Magnus Norddahl
4a7e2f1dcf Add lm_sourceradius property to lights 2024-03-05 15:39:50 +01:00
Magnus Norddahl
f1bbe515bc Create a dummy texture as the lightmapper assumes we have textures 2024-03-05 00:16:44 +01:00
Magnus Norddahl
6ccc1fbc82 Sync shaders from vkdoom 2024-03-04 23:43:30 +01:00
Magnus Norddahl
85f1687769 Sync shader code from vkdoom 2024-03-04 21:41:56 +01:00
Magnus Norddahl
f2ecc9c4fb Fix 3d floors and backface culling for flats 2024-03-03 14:13:45 +01:00
Magnus Norddahl
02c2cf7c78 Forgot to add the filesystem files 2024-03-01 13:28:30 +01:00
Magnus Norddahl
973512ba06 Create a very basic filesystem implementation for zip, folder and wad 2024-03-01 13:28:07 +01:00
Magnus Norddahl
8e1787450b Drop code that is problematic on Crapple and isn't used anyway 2024-03-01 12:12:31 +01:00
Magnus Norddahl
0c42722e20 More build problems fun 2024-03-01 12:10:11 +01:00
Magnus Norddahl
6191412720 Fix PATH_MAX define 2024-03-01 12:01:43 +01:00
Magnus Norddahl
3b23f4efd5 File case sensitivity fixes 2024-02-29 21:15:59 +01:00
Magnus Norddahl
ffca8b5b6c Add misc things needed for loading textures 2024-02-29 21:09:28 +01:00
Magnus Norddahl
847ffe030b Fix sky in zdrayland 2024-02-29 20:38:36 +01:00
Magnus Norddahl
2b5ae842da Fix wall facing the wrong way 2024-02-29 13:08:53 +01:00
Magnus Norddahl
a6d3958ad2 Upgrade lightmapper to latest internal version 2024-02-28 22:32:43 +01:00
Magnus Norddahl
a9da46d00d Fix package path 2024-02-19 14:43:05 +01:00
Magnus Norddahl
9dd302aec1 Set up latest successful build release 2024-02-19 14:39:03 +01:00
nashmuhandes
6219935714 Clarify documentation for lm_dynamic 2024-02-10 09:49:12 +08:00
Magnus Norddahl
3d1fe65485 Fix sunlight not working 2024-01-17 19:42:40 +01:00
Magnus Norddahl
500ddfebe9 Fix creating tiles for sky surfaces 2024-01-17 15:15:00 +01:00
Magnus Norddahl
3d9cf5bc06 Fix shadows not working for non-rt render path 2024-01-17 13:27:01 +01:00
Magnus Norddahl
bfc00410c4 Flip which sidedef the 3d floor walls belong to 2024-01-17 01:44:37 +01:00
Magnus Norddahl
13df9b2b32 Skip sides that have no valid texture 2024-01-17 00:04:27 +01:00
Magnus Norddahl
01cd022fd6 Fix include error for clang 2024-01-16 12:56:43 +01:00
Magnus Norddahl
2c786e0718 Remove original lightmapper implementation 2024-01-16 12:47:24 +01:00
Magnus Norddahl
f689ea3640 Upgrade lightmapper to latest vkdoom code 2024-01-16 12:43:37 +01:00
deviant-syndrome
08f280b54a Fixed DISABLE_SSE config flag in CMakelists. Added <algorithm> to vectors.h, <cstdint> to hw_collision.cpp
Made include statement case-compatible for Linux
2023-12-31 08:08:39 +01:00
dpjudas
38cdc880d1 Fix another descriptor pool out of memory error 2023-10-18 02:27:47 +02:00
Magnus Norddahl
920935bd5f Add lm_dynamic to UDMF properties 2023-10-17 19:52:42 +02:00
Magnus Norddahl
0a7108c3b1 Add lights 2023-10-17 18:29:27 +02:00
Magnus Norddahl
2d1c0acff2 Fix out of descriptor pool error 2023-10-17 18:28:45 +02:00
Magnus Norddahl
526110e188 Fix UV coordinates 2023-10-16 18:30:18 +02:00
Magnus Norddahl
9ed4723e6e Fix misc bugs 2023-10-16 17:37:26 +02:00
Magnus Norddahl
156ef319af Hook up the raytracing 2023-10-16 17:03:40 +02:00
Magnus Norddahl
7c1acb5075 Save lightmap lump 2023-10-16 16:46:35 +02:00
Magnus Norddahl
df010a5643 Add back BinFile 2023-10-16 16:46:16 +02:00
Magnus Norddahl
e9758d8d5f Add back lump writing code 2023-10-16 15:14:10 +02:00
Magnus Norddahl
4dd6b6977b Misc mesh build fixes 2023-10-11 19:44:09 +02:00
Magnus Norddahl
53d73780dd Move textureid to framework 2023-10-10 22:15:00 +02:00
Magnus Norddahl
2c6d4f6f22 Port over doom level mesh 2023-10-10 22:11:30 +02:00
Magnus Norddahl
1391957a42 More doom level mesh porting 2023-10-10 01:33:17 +02:00
Magnus Norddahl
0655d45025 Upgrade tarray.h to latest version from GZD 2023-10-10 00:51:57 +02:00
Magnus Norddahl
f905d37cd7 Port over more of DoomLevelMesh 2023-10-10 00:46:23 +02:00
Magnus Norddahl
3fa88a1447 More setup code 2023-10-06 00:43:13 +02:00
Magnus Norddahl
f8b8005d4a Add the shaders 2023-10-06 00:34:04 +02:00
Magnus Norddahl
0b09fa9793 Implement a basic VulkanRenderDevice 2023-10-06 00:08:43 +02:00
Magnus Norddahl
3572d81010 Minor adjustments 2023-10-05 23:08:54 +02:00
Magnus Norddahl
a9d1999b46 Init vulkan 2023-10-05 23:03:28 +02:00
Magnus Norddahl
642794e678 Replace mathlib with gzdoom equivalents for easier code exchanges between vkdoom and zdray
Import the lightmapper from vkdoom and bring in just enough infrastructure for it to build
2023-10-04 22:22:54 +02:00
Magnus Norddahl
1a4d2a783f Update zvulkan 2023-10-04 19:08:50 +02:00
nashmuhandes
baa1ead6e9 - Unify the custom sample distance variable names
- Fix wrong type for the sun color
2023-10-03 18:36:50 +08:00
Nash Muhandes
9b9663e3d0
Update README.md
- Update Marisa's name in the credits
- Get rid of the line breaks
- Update info about GPU tracing
2023-09-24 20:02:29 +08:00
nashmuhandes
3443793f79 Rename "static lights" to "lightmap lights" to accurately reflect its usage. 2023-09-24 19:51:02 +08:00
Magnus Norddahl
7af7737ffb Revert the revert of "Fix not clearing texture in the raytrace render pass" so one bug doesn't just replace another bug 2023-09-15 11:07:22 +02:00
RaveYard
37a6ccc799 Revert "Fix not clearing texture in the raytrace render pass"
This reverts commit bbd246bdc9.
2023-09-15 10:44:36 +02:00
RaveYard
5c0e9e0db2 Minor possible fixes 2023-09-15 10:44:36 +02:00
RaveYard
13178635b7 V2 format export initial implementation 2023-09-15 10:44:36 +02:00
nashmuhandes
c68e56ada0 Update README to include per-sidedef sample distance properties 2023-09-11 08:36:17 +08:00
111 changed files with 14127 additions and 7879 deletions

View file

@ -42,9 +42,57 @@ jobs:
export MAKEFLAGS=--keep-going
cmake --build build --config Release --parallel 3
- name: Create Package
if: runner.os == 'Windows' # Remove to make packages of all targets
shell: bash
run: |
cd build
mkdir package
if [[ "${{ runner.os }}" == 'Windows' ]]; then
cp Release/zdray.exe package
elif [[ "${{ runner.os }}" == 'macOS' ]]; then
cp zdray package
elif [[ "${{ runner.os }}" == 'Linux' ]]; then
cp zdray package
fi
- name: Upload Package
if: runner.os == 'Windows' # Remove to store packages of all targets
uses: actions/upload-artifact@v4
with:
path: build/package
name: ${{ matrix.config.name }}
- name: List Build Directory
if: always()
shell: bash
run: |
git status
ls -lR build
deploy:
name: Update Latest successful build
runs-on: ubuntu-latest
needs: build
if: github.event_name == 'push' && github.ref == 'refs/heads/master'
steps:
- name: Download artifact
uses: actions/download-artifact@v4
with:
name: Windows
path: build/zdray-prerelease
- name: Zip artifacts
shell: bash
run: |
cd build
zip -r zdray-prerelease.zip zdray-prerelease
- name: Update nightly release
uses: pyTooling/Actions/releaser@r0
with:
tag: nightly
rm: true
token: ${{ secrets.GITHUB_TOKEN }}
files: build/zdray-prerelease.zip

View file

@ -11,8 +11,9 @@ set(ZDRAY_SOURCES
src/commandline/getopt.c
src/commandline/getopt1.c
src/commandline/getopt.h
src/framework/halffloat.cpp
src/framework/binfile.cpp
src/framework/binfile.h
src/framework/halffloat.cpp
src/framework/zstring.cpp
src/framework/zstrformat.cpp
src/framework/utf8.cpp
@ -22,7 +23,21 @@ set(ZDRAY_SOURCES
src/framework/zdray.h
src/framework/xs_Float.h
src/framework/halffloat.h
src/framework/binfile.h
src/framework/vectors.h
src/framework/matrix.cpp
src/framework/matrix.h
src/framework/bounds.cpp
src/framework/bounds.h
src/framework/textureid.cpp
src/framework/textureid.h
src/framework/zipreader.cpp
src/framework/zipreader.h
src/framework/file.cpp
src/framework/file.h
src/framework/utf16.cpp
src/framework/utf16.h
src/framework/filesystem.cpp
src/framework/filesystem.h
src/blockmapbuilder/blockmapbuilder.cpp
src/blockmapbuilder/blockmapbuilder.h
src/level/level.cpp
@ -43,47 +58,60 @@ set(ZDRAY_SOURCES
src/nodebuilder/nodebuild_utility.cpp
src/nodebuilder/nodebuild_classify_nosse2.cpp
src/nodebuilder/nodebuild.h
src/lightmap/pngwriter.cpp
src/lightmap/pngwriter.h
src/lightmap/levelmesh.cpp
src/lightmap/levelmesh.h
src/lightmap/lightmaptexture.cpp
src/lightmap/lightmaptexture.h
src/lightmap/collision.cpp
src/lightmap/collision.h
src/lightmap/delauneytriangulator.cpp
src/lightmap/delauneytriangulator.h
src/lightmap/stacktrace.cpp
src/lightmap/stacktrace.h
src/lightmap/surfaceclip.cpp
src/lightmap/surfaceclip.h
src/lightmap/gpuraytracer.cpp
src/lightmap/gpuraytracer.h
src/lightmap/glsl_frag.h
src/lightmap/glsl_vert.h
src/lightmap/glsl_frag_resolve.h
src/lightmap/renderdoc_app.h
src/lightmap/portal.h
src/math/mat.cpp
src/math/plane.cpp
src/math/angle.cpp
src/math/bounds.cpp
src/math/mathlib.cpp
src/math/mat.h
src/math/quaternion.h
src/math/vec.h
src/math/mathlib.h
src/lightmapper/hw_levelmesh.cpp
src/lightmapper/hw_levelmesh.h
src/lightmapper/hw_levelmeshlight.h
src/lightmapper/hw_levelmeshportal.h
src/lightmapper/hw_levelmeshsurface.h
src/lightmapper/hw_lightmaptile.h
src/lightmapper/flatvertices.h
src/lightmapper/hw_materialstate.h
src/lightmapper/hw_surfaceuniforms.h
src/lightmapper/hw_collision.cpp
src/lightmapper/hw_collision.h
src/lightmapper/vk_renderdevice.cpp
src/lightmapper/vk_renderdevice.h
src/lightmapper/vk_levelmesh.cpp
src/lightmapper/vk_levelmesh.h
src/lightmapper/vk_lightmapper.cpp
src/lightmapper/vk_lightmapper.h
src/lightmapper/doom_levelmesh.cpp
src/lightmapper/doom_levelmesh.h
src/lightmapper/gpuraytracer.cpp
src/lightmapper/gpuraytracer.h
src/lightmapper/stacktrace.cpp
src/lightmapper/stacktrace.h
src/lightmapper/levelmeshviewer.cpp
src/lightmapper/levelmeshviewer.h
src/lightmapper/glsl/binding_lightmapper.glsl.h
src/lightmapper/glsl/binding_raytrace.glsl.h
src/lightmapper/glsl/binding_textures.glsl.h
src/lightmapper/glsl/binding_viewer.glsl.h
src/lightmapper/glsl/frag_blur.glsl.h
src/lightmapper/glsl/frag_copy.glsl.h
src/lightmapper/glsl/frag_raytrace.glsl.h
src/lightmapper/glsl/frag_resolve.glsl.h
src/lightmapper/glsl/frag_viewer.glsl.h
src/lightmapper/glsl/montecarlo.glsl.h
src/lightmapper/glsl/polyfill_rayquery.glsl.h
src/lightmapper/glsl/trace_ambient_occlusion.glsl.h
src/lightmapper/glsl/trace_bounce.glsl.h
src/lightmapper/glsl/trace_levelmesh.glsl.h
src/lightmapper/glsl/trace_light.glsl.h
src/lightmapper/glsl/trace_sunlight.glsl.h
src/lightmapper/glsl/vert_copy.glsl.h
src/lightmapper/glsl/vert_raytrace.glsl.h
src/lightmapper/glsl/vert_screenquad.glsl.h
src/lightmapper/glsl/vert_viewer.glsl.h
src/models/model.cpp
src/models/model.h
src/models/model_md2.h
src/models/model_md3.h
src/models/model_obj.h
src/models/model_ue1.h
src/models/modelrenderer.h
src/models/models_md2.cpp
src/models/models_md3.cpp
src/models/models_obj.cpp
src/models/models_ue1.cpp
src/models/tab_anorms.h
src/platform/windows/resource.h
)
@ -92,6 +120,8 @@ set(THIRDPARTY_SOURCES
${CMAKE_SOURCE_DIR}/thirdparty/dp_rect_pack/dp_rect_pack.h
${CMAKE_SOURCE_DIR}/thirdparty/miniz/miniz.h
${CMAKE_SOURCE_DIR}/thirdparty/miniz/miniz.c
${CMAKE_SOURCE_DIR}/thirdparty/picopng/picopng.cpp
${CMAKE_SOURCE_DIR}/thirdparty/picopng/picopng.h
)
set(ZDRAY_LIBS
@ -108,13 +138,14 @@ source_group("src\\Parse" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/src/p
source_group("src\\Platform" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/src/platform/.+")
source_group("src\\Platform\\Windows" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/src/platform/windows/.+")
source_group("src\\Wad" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/src/wad/.+")
source_group("src\\Math" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/src/math/.+")
source_group("src\\Lightmap" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/src/lightmap/.+")
source_group("src\\Lightmapper" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/src/lightmapper/.+")
source_group("src\\Lightmapper\\glsl" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/src/lightmapper/glsl/.+")
source_group("src\\Models" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/src/models/.+")
source_group("thirdparty" REGULAR_EXPRESSION "${CMAKE_CURRENT_SOURCE_DIR}/thirdparty/.+")
source_group("thirdparty\\dp_rect_pack" REGULAR_EXPRESSION "${CMAKE_CURRENT_SOURCE_DIR}/thirdparty/dp_rect_pack/.+")
source_group("thirdparty\\miniz" REGULAR_EXPRESSION "${CMAKE_CURRENT_SOURCE_DIR}/thirdparty/miniz/.+")
source_group("thirdparty\\picopng" REGULAR_EXPRESSION "${CMAKE_CURRENT_SOURCE_DIR}/thirdparty/picopng/.+")
include_directories( src "${CMAKE_CURRENT_SOURCE_DIR}/thirdparty/ZVulkan/include" "${CMAKE_CURRENT_SOURCE_DIR}/thirdparty" )
@ -129,7 +160,7 @@ if(${TARGET_ARCHITECTURE} MATCHES "x86_64")
set_source_files_properties(src/nodebuilder/nodebuild_classify_sse1.cpp PROPERTIES COMPILE_FLAGS "${SSE1_ENABLE}")
set_source_files_properties(src/nodebuilder/nodebuild_classify_sse2.cpp PROPERTIES COMPILE_FLAGS "${SSE2_ENABLE}")
else()
add_definitions(-DNO_SSE)
add_definitions(-DDISABLE_SSE)
endif()
CHECK_FUNCTION_EXISTS(stricmp STRICMP_EXISTS)

View file

@ -1,17 +1,18 @@
# ZDRay baking utility for GZDoom
Note: This project has been deprecated. Use the lightmap save command in VKDoom instead.
ZDRay is a node and lightmap generator for GZDoom. ZDRay is intended as a drop-in replacement for zdbsp, with the additional feature
that it can also bake lights. Once ZDRay has processed the level WAD it is ready to be used by GZDoom.
# ZDRay baking utility for VKDoom/GZDoom
ZDRay is based on zdbsp for the node generation and originally used dlight for the lightmap generation. Special thanks to Randi Heit,
Samuel Villarreal, Christoph Oelckers and anyone else involved in creating or maintaining those tools.
ZDRay is a node and lightmap generator for GZDoom. ZDRay is intended as a drop-in replacement for zdbsp, with the additional feature that it can also bake lightmap lights. Once ZDRay has processed the level WAD it is ready to be used by GZDoom.
The ray tracing code has been completely rewritten since. It now does the ray tracing on the GPU. GPU ray tracing requires a graphics
card that has support for the Vulkan ray query API (for example, nvidia 20 series and higher). ZDRay no longer supports CPU tracing.
ZDRay is based on zdbsp for the node generation and originally used dlight for the lightmap generation. Special thanks to Marisa Heit, Samuel Villarreal, Christoph Oelckers and anyone else involved in creating or maintaining those tools.
The ray tracing code has been completely rewritten since. ZDRay has two paths for lightmap baking - either the Vulkan ray query API path for raytracing-enabled cards, or a shader-based path utilizing storage buffers for non-RT cards. It defaults to the ray query path but will automatically fall back to the shader path if no RT card is found on the system.
## ZDRay Usage
**ATTENTION: You MUST place down a ZDRayInfo actor somewhere in the map for the lightmaps to appear at all!**
<pre>
Usage: zdray [options] sourcefile.wad
-m, --map=MAP Only affect the specified map
@ -51,24 +52,35 @@ Usage: zdray [options] sourcefile.wad
thing // ZDRayInfo (ZDRay properties for the map)
{
type = 9890;
lm_suncolor = &lt;string&gt; (default: "FFFFFF", hex color value of the sun)
lm_sampledistance = &lt;int&gt; (default: 16, map units each lightmap texel covers, must be in powers of two)
lm_suncolor = &lt;int&gt; (default: 16777215, color value of the sun)
lm_sampledist = &lt;int&gt; (default: 8, map units each lightmap texel covers, must be in powers of two)
}
thing // Static point light (Light color and distance properties use the same args as dynamic lights)
thing // Lightmap point light (Light color and distance properties use the same args as dynamic lights)
{
type = 9876;
light_softshadowradius = &lt;float&gt; (default: 5, radius of the light source in map units; controls the shadow softness. Note that dynamic raytraced lights can also use this feature)
}
thing // Static spotlight (Light color, distance and angle properties use the same args as dynamic lights)
thing // Lightmap spotlight (Light color, distance and angle properties use the same args as dynamic lights)
{
type = 9881;
light_softshadowradius = &lt;float&gt; (default: 5, radius of the light source in map units; controls the shadow softness. Note that dynamic raytraced lights can also use this feature)
}
linedef
{
// Customizable sampling distance per line surface. Will use the value from the ZDRayInfo actor by default.
lm_sampledist_line = &lt;int&gt; (default: 0)
lm_sampledist = &lt;int&gt; (default: 0)
lm_sampledist_top = &lt;int&gt; (default: 0)
lm_sampledist_mid = &lt;int&gt; (default: 0)
lm_sampledist_bot = &lt;int&gt; (default: 0)
}
sidedef
{
// Customizable sampling distance per sidedef. Will use the value from the ZDRayInfo actor by default.
lm_sampledist = &lt;int&gt; (default: 0)
lm_sampledist_top = &lt;int&gt; (default: 0)
lm_sampledist_mid = &lt;int&gt; (default: 0)
lm_sampledist_bot = &lt;int&gt; (default: 0)
@ -79,5 +91,10 @@ sector
// Customizable sampling distance for floors and ceilings.
lm_sampledist_floor = &lt;int&gt; (default: 0)
lm_sampledist_ceiling = &lt;int&gt; (default: 0)
// Update the lightmap for the sector every frame when visible in the game.
// All sides belonging to the sector will also be affected.
// Note that this is computationally expensive, but it allows animated and moving lights.
lm_dynamic = &lt;bool&gt; (default: false)
}
</pre>

View file

@ -65,13 +65,13 @@ float BinFile::ReadFloat()
return fi.f;
}
vec3 BinFile::ReadVector()
FVector3 BinFile::ReadVector()
{
vec3 vec;
FVector3 vec;
vec.x = ReadFloat();
vec.y = ReadFloat();
vec.z = ReadFloat();
vec.X = ReadFloat();
vec.Y = ReadFloat();
vec.Z = ReadFloat();
return vec;
}
@ -121,11 +121,11 @@ void BinFile::WriteFloat(const float val)
Write32(fi.i);
}
void BinFile::WriteVector(const vec3 &val)
void BinFile::WriteVector(const FVector3 &val)
{
WriteFloat(val.x);
WriteFloat(val.y);
WriteFloat(val.z);
WriteFloat(val.X);
WriteFloat(val.Y);
WriteFloat(val.Z);
}
void BinFile::WriteString(const std::string &val)

View file

@ -27,7 +27,7 @@
#pragma once
#include "math/mathlib.h"
#include "vectors.h"
#include <string>
class BinFile
@ -37,14 +37,14 @@ public:
short Read16();
int Read32();
float ReadFloat();
vec3 ReadVector();
FVector3 ReadVector();
std::string ReadString();
void Write8(const uint8_t val);
void Write16(const short val);
void Write32(const int val);
void WriteFloat(const float val);
void WriteVector(const vec3 &val);
void WriteVector(const FVector3 &val);
void WriteString(const std::string &val);
int GetOffsetValue(int id);

View file

@ -26,15 +26,18 @@
//
#include <math.h>
#include "mathlib.h"
#include <assert.h>
#include "bounds.h"
#define M_INFINITY 1e30f
BBox::BBox()
{
Clear();
}
BBox::BBox(const vec3 &vMin, const vec3 &vMax)
BBox::BBox(const FVector3 &vMin, const FVector3 &vMax)
{
this->min = vMin;
this->max = vMax;
@ -42,28 +45,28 @@ BBox::BBox(const vec3 &vMin, const vec3 &vMax)
void BBox::Clear()
{
min = vec3(M_INFINITY, M_INFINITY, M_INFINITY);
max = vec3(-M_INFINITY, -M_INFINITY, -M_INFINITY);
min = FVector3(M_INFINITY, M_INFINITY, M_INFINITY);
max = FVector3(-M_INFINITY, -M_INFINITY, -M_INFINITY);
}
void BBox::AddPoint(const vec3 &vec)
void BBox::AddPoint(const FVector3 &vec)
{
float lowx = min.x;
float lowy = min.y;
float lowz = min.z;
float hix = max.x;
float hiy = max.y;
float hiz = max.z;
float lowx = min.X;
float lowy = min.Y;
float lowz = min.Z;
float hix = max.X;
float hiy = max.Y;
float hiz = max.Z;
if (vec.x < lowx) { lowx = vec.x; }
if (vec.y < lowy) { lowy = vec.y; }
if (vec.z < lowz) { lowz = vec.z; }
if (vec.x > hix) { hix = vec.x; }
if (vec.y > hiy) { hiy = vec.y; }
if (vec.z > hiz) { hiz = vec.z; }
if (vec.X < lowx) { lowx = vec.X; }
if (vec.Y < lowy) { lowy = vec.Y; }
if (vec.Z < lowz) { lowz = vec.Z; }
if (vec.X > hix) { hix = vec.X; }
if (vec.Y > hiy) { hiy = vec.Y; }
if (vec.Z > hiz) { hiz = vec.Z; }
min = vec3(lowx, lowy, lowz);
max = vec3(hix, hiy, hiz);
min = FVector3(lowx, lowy, lowz);
max = FVector3(hix, hiy, hiz);
}
float BBox::Radius() const
@ -75,8 +78,8 @@ float BBox::Radius() const
for (i = 0; i < 3; i++)
{
r1 = Math::Fabs(min[i]);
r2 = Math::Fabs(max[i]);
r1 = fabsf(min[i]);
r2 = fabsf(max[i]);
if (r1 > r2)
{
@ -88,10 +91,10 @@ float BBox::Radius() const
}
}
return Math::Sqrt(r);
return sqrtf(r);
}
bool BBox::PointInside(const vec3 &vec) const
bool BBox::PointInside(const FVector3 &vec) const
{
return !(vec[0] < min[0] || vec[1] < min[1] || vec[2] < min[2] ||
vec[0] > max[0] || vec[1] > max[1] || vec[2] > max[2]);
@ -111,7 +114,9 @@ bool BBox::IntersectingBox2D(const BBox &box) const
float BBox::DistanceToPlane(Plane &plane)
{
vec3 c;
throw "unimplemented";
return 0;
/*FVector3 c;
float distStart;
float distEnd;
float dist = 0;
@ -119,9 +124,9 @@ float BBox::DistanceToPlane(Plane &plane)
c = Center();
distStart = plane.Distance(c);
distEnd = Math::Fabs((max.x - c.x) * plane.a) +
Math::Fabs((max.y - c.y) * plane.b) +
Math::Fabs((max.z - c.z) * plane.c);
distEnd = fabs((max.X - c.X) * plane.a) +
fabs((max.Y - c.Y) * plane.b) +
fabs((max.Z - c.Z) * plane.c);
dist = distStart - distEnd;
@ -139,117 +144,117 @@ float BBox::DistanceToPlane(Plane &plane)
return dist;
}
return 0;
return 0;*/
}
BBox BBox::operator+(const float radius) const
{
vec3 vmin = min;
vec3 vmax = max;
FVector3 vmin = min;
FVector3 vmax = max;
vmin.x -= radius;
vmin.y -= radius;
vmin.z -= radius;
vmin.X -= radius;
vmin.Y -= radius;
vmin.Z -= radius;
vmax.x += radius;
vmax.y += radius;
vmax.z += radius;
vmax.X += radius;
vmax.Y += radius;
vmax.Z += radius;
return BBox(vmin, vmax);
}
BBox &BBox::operator+=(const float radius)
{
min.x -= radius;
min.y -= radius;
min.z -= radius;
max.x += radius;
max.y += radius;
max.z += radius;
min.X -= radius;
min.Y -= radius;
min.Z -= radius;
max.X += radius;
max.Y += radius;
max.Z += radius;
return *this;
}
BBox BBox::operator+(const vec3 &vec) const
BBox BBox::operator+(const FVector3 &vec) const
{
vec3 vmin = min;
vec3 vmax = max;
FVector3 vmin = min;
FVector3 vmax = max;
vmin.x += vec.x;
vmin.y += vec.y;
vmin.z += vec.z;
vmin.X += vec.X;
vmin.Y += vec.Y;
vmin.Z += vec.Z;
vmax.x += vec.x;
vmax.y += vec.y;
vmax.z += vec.z;
vmax.X += vec.X;
vmax.Y += vec.Y;
vmax.Z += vec.Z;
return BBox(vmin, vmax);
}
BBox BBox::operator-(const float radius) const
{
vec3 vmin = min;
vec3 vmax = max;
FVector3 vmin = min;
FVector3 vmax = max;
vmin.x += radius;
vmin.y += radius;
vmin.z += radius;
vmin.X += radius;
vmin.Y += radius;
vmin.Z += radius;
vmax.x -= radius;
vmax.y -= radius;
vmax.z -= radius;
vmax.X -= radius;
vmax.Y -= radius;
vmax.Z -= radius;
return BBox(vmin, vmax);
}
BBox BBox::operator-(const vec3 &vec) const
BBox BBox::operator-(const FVector3 &vec) const
{
vec3 vmin = min;
vec3 vmax = max;
FVector3 vmin = min;
FVector3 vmax = max;
vmin.x -= vec.x;
vmin.y -= vec.y;
vmin.z -= vec.z;
vmin.X -= vec.X;
vmin.Y -= vec.Y;
vmin.Z -= vec.Z;
vmax.x -= vec.x;
vmax.y -= vec.y;
vmax.z -= vec.z;
vmax.X -= vec.X;
vmax.Y -= vec.Y;
vmax.Z -= vec.Z;
return BBox(vmin, vmax);
}
BBox &BBox::operator-=(const float radius)
{
min.x += radius;
min.y += radius;
min.z += radius;
max.x -= radius;
max.y -= radius;
max.z -= radius;
min.X += radius;
min.Y += radius;
min.Z += radius;
max.X -= radius;
max.Y -= radius;
max.Z -= radius;
return *this;
}
BBox BBox::operator*(const vec3 &vec) const
BBox BBox::operator*(const FVector3 &vec) const
{
BBox box = *this;
if (vec.x < 0) { box.min.x += (vec.x - 1); }
else { box.max.x += (vec.x + 1); }
if (vec.y < 0) { box.min.y += (vec.y - 1); }
else { box.max.y += (vec.y + 1); }
if (vec.z < 0) { box.min.z += (vec.z - 1); }
else { box.max.z += (vec.z + 1); }
if (vec.X < 0) { box.min.X += (vec.X - 1); }
else { box.max.X += (vec.X + 1); }
if (vec.Y < 0) { box.min.Y += (vec.Y - 1); }
else { box.max.Y += (vec.Y + 1); }
if (vec.Z < 0) { box.min.Z += (vec.Z - 1); }
else { box.max.Z += (vec.Z + 1); }
return box;
}
BBox &BBox::operator*=(const vec3 &vec)
BBox &BBox::operator*=(const FVector3 &vec)
{
if (vec.x < 0) { min.x += (vec.x - 1); }
else { max.x += (vec.x + 1); }
if (vec.y < 0) { min.y += (vec.y - 1); }
else { max.y += (vec.y + 1); }
if (vec.z < 0) { min.z += (vec.z - 1); }
else { max.z += (vec.z + 1); }
if (vec.X < 0) { min.X += (vec.X - 1); }
else { max.X += (vec.X + 1); }
if (vec.Y < 0) { min.Y += (vec.Y - 1); }
else { max.Y += (vec.Y + 1); }
if (vec.Z < 0) { min.Z += (vec.Z - 1); }
else { max.Z += (vec.Z + 1); }
return *this;
}
@ -262,39 +267,39 @@ BBox &BBox::operator=(const BBox &bbox)
return *this;
}
vec3 BBox::operator[](int index) const
FVector3 BBox::operator[](int index) const
{
assert(index >= 0 && index < 2);
return index == 0 ? min : max;
}
vec3 &BBox::operator[](int index)
FVector3 &BBox::operator[](int index)
{
assert(index >= 0 && index < 2);
return index == 0 ? min : max;
}
bool BBox::LineIntersect(const vec3 &start, const vec3 &end)
bool BBox::LineIntersect(const FVector3 &start, const FVector3 &end)
{
float ld[3];
vec3 center = Center();
vec3 extents = max - center;
vec3 lineDir = (end - start) * 0.5f;
vec3 lineCenter = lineDir + start;
vec3 dir = lineCenter - center;
FVector3 center = Center();
FVector3 extents = max - center;
FVector3 lineDir = (end - start) * 0.5f;
FVector3 lineCenter = lineDir + start;
FVector3 dir = lineCenter - center;
ld[0] = Math::Fabs(lineDir.x);
if (Math::Fabs(dir.x) > extents.x + ld[0]) { return false; }
ld[1] = Math::Fabs(lineDir.y);
if (Math::Fabs(dir.y) > extents.y + ld[1]) { return false; }
ld[2] = Math::Fabs(lineDir.z);
if (Math::Fabs(dir.z) > extents.z + ld[2]) { return false; }
ld[0] = (float)fabs(lineDir.X);
if ((float)fabs(dir.X) > extents.X + ld[0]) { return false; }
ld[1] = (float)fabs(lineDir.Y);
if ((float)fabs(dir.Y) > extents.Y + ld[1]) { return false; }
ld[2] = (float)fabs(lineDir.Z);
if ((float)fabs(dir.Z) > extents.Z + ld[2]) { return false; }
vec3 crossprod = cross(lineDir, dir);
FVector3 crossprod = lineDir ^ dir;
if (Math::Fabs(crossprod.x) > extents.y * ld[2] + extents.z * ld[1]) { return false; }
if (Math::Fabs(crossprod.y) > extents.x * ld[2] + extents.z * ld[0]) { return false; }
if (Math::Fabs(crossprod.z) > extents.x * ld[1] + extents.y * ld[0]) { return false; }
if (fabs(crossprod.X) > extents.Y * ld[2] + extents.Z * ld[1]) { return false; }
if (fabs(crossprod.Y) > extents.X * ld[2] + extents.Z * ld[0]) { return false; }
if (fabs(crossprod.Z) > extents.X * ld[1] + extents.Y * ld[0]) { return false; }
return true;
}
@ -329,7 +334,7 @@ void BBox::ToPoints(float *points) const
}
// Assumes vectors is an array of 8
void BBox::ToVectors(vec3 *vectors) const
void BBox::ToVectors(FVector3 *vectors) const
{
vectors[0][0] = max[0];
vectors[0][1] = min[1];
@ -356,3 +361,19 @@ void BBox::ToVectors(vec3 *vectors) const
vectors[7][1] = max[1];
vectors[7][2] = min[2];
}
inline FVector3 BBox::Center() const
{
return FVector3(
(max.X + min.X) * 0.5f,
(max.Y + min.Y) * 0.5f,
(max.Z + min.Z) * 0.5f);
}
inline FVector3 BBox::Extents() const
{
return FVector3(
(max.X - min.X) * 0.5f,
(max.Y - min.Y) * 0.5f,
(max.Z - min.Z) * 0.5f);
}

View file

@ -1,5 +1,5 @@
//-----------------------------------------------------------------------------
// Note: this is a modified version of dlight. It is not the original software.
// Note: this is from ZDRay
//-----------------------------------------------------------------------------
//
// Copyright (c) 2013-2014 Samuel Villarreal
@ -25,55 +25,41 @@
// distribution.
//
#include "mathlib.h"
#pragma once
int Math::RoundPowerOfTwo(int x)
#include "vectors.h"
class BBox
{
int mask = 1;
public:
BBox();
BBox(const FVector3& vMin, const FVector3& vMax);
while (mask < 0x40000000)
{
if (x == mask || (x & (mask - 1)) == x)
{
return mask;
}
void Clear();
FVector3 Center() const;
FVector3 Extents() const;
float Radius() const;
void AddPoint(const FVector3& vec);
bool PointInside(const FVector3& vec) const;
bool IntersectingBox(const BBox& box) const;
bool IntersectingBox2D(const BBox& box) const;
float DistanceToPlane(Plane& plane);
bool LineIntersect(const FVector3& start, const FVector3& end);
void ToPoints(float* points) const;
void ToVectors(FVector3* vectors) const;
mask <<= 1;
}
return x;
}
void Math::CubicCurve(const vec3 &start, const vec3 &end, const float time, const vec3 &point, vec3 *vec)
{
int i;
float xyz[3];
for (i = 0; i < 3; i++)
{
xyz[i] = Math::Pow(1 - time, 2) * start[i] +
(2 * (1 - time)) * time * point[i] + Math::Pow(time, 2) * end[i];
}
vec->x = xyz[0];
vec->y = xyz[1];
vec->z = xyz[2];
}
void Math::QuadraticCurve(const vec3 &start, const vec3 &end, const float time, const vec3 &pt1, const vec3 &pt2, vec3 *vec)
{
int i;
float xyz[3];
for (i = 0; i < 3; i++)
{
xyz[i] = Math::Pow(1 - time, 3) * start[i] + (3 * Math::Pow(1 - time, 2)) *
time * pt1[i] + (3 * (1 - time)) * Math::Pow(time, 2) * pt2[i] +
Math::Pow(time, 3) * end[i];
}
vec->x = xyz[0];
vec->y = xyz[1];
vec->z = xyz[2];
}
BBox operator+(const float radius) const;
BBox& operator+=(const float radius);
BBox operator+(const FVector3& vec) const;
BBox operator-(const float radius) const;
BBox operator-(const FVector3& vec) const;
BBox& operator-=(const float radius);
BBox operator*(const FVector3& vec) const;
BBox& operator*=(const FVector3& vec);
BBox& operator=(const BBox& bbox);
FVector3 operator[](int index) const;
FVector3& operator[](int index);
FVector3 min;
FVector3 max;
};

503
src/framework/file.cpp Normal file
View file

@ -0,0 +1,503 @@
#include "file.h"
#include "utf16.h"
#include <stdexcept>
#ifndef WIN32
#include <string.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <libgen.h>
#ifndef PATH_MAX
#define PATH_MAX 1024
#endif
#endif
#ifdef WIN32
#define NOMINMAX
#include <Windows.h>
class FileImpl : public File
{
public:
FileImpl(HANDLE handle) : handle(handle)
{
}
~FileImpl()
{
CloseHandle(handle);
}
void write(const void* data, size_t size) override
{
size_t pos = 0;
while (pos < size)
{
size_t writesize = std::min(size, (size_t)0xffffffff);
BOOL result = WriteFile(handle, (const uint8_t*)data + pos, (DWORD)writesize, nullptr, nullptr);
if (result == FALSE)
throw std::runtime_error("WriteFile failed");
pos += writesize;
}
}
void read(void* data, size_t size) override
{
size_t pos = 0;
while (pos < size)
{
size_t readsize = std::min(size, (size_t)0xffffffff);
DWORD bytesRead = 0;
BOOL result = ReadFile(handle, (uint8_t*)data + pos, (DWORD)readsize, &bytesRead, nullptr);
if (result == FALSE || bytesRead != readsize)
throw std::runtime_error("ReadFile failed");
pos += readsize;
}
}
int64_t size() override
{
LARGE_INTEGER fileSize;
BOOL result = GetFileSizeEx(handle, &fileSize);
if (result == FALSE)
throw std::runtime_error("GetFileSizeEx failed");
return fileSize.QuadPart;
}
void seek(int64_t offset, SeekPoint origin) override
{
LARGE_INTEGER off, newoff;
off.QuadPart = offset;
DWORD moveMethod = FILE_BEGIN;
if (origin == SeekPoint::current) moveMethod = FILE_CURRENT;
else if (origin == SeekPoint::end) moveMethod = FILE_END;
BOOL result = SetFilePointerEx(handle, off, &newoff, moveMethod);
if (result == FALSE)
throw std::runtime_error("SetFilePointerEx failed");
}
uint64_t tell() override
{
LARGE_INTEGER offset, delta;
delta.QuadPart = 0;
BOOL result = SetFilePointerEx(handle, delta, &offset, FILE_CURRENT);
if (result == FALSE)
throw std::runtime_error("SetFilePointerEx failed");
return offset.QuadPart;
}
HANDLE handle = INVALID_HANDLE_VALUE;
};
std::shared_ptr<File> File::create_always(const std::string& filename)
{
HANDLE handle = CreateFileW(to_utf16(filename).c_str(), FILE_WRITE_ACCESS, 0, 0, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
if (handle == INVALID_HANDLE_VALUE)
throw std::runtime_error("Could not create " + filename);
return std::make_shared<FileImpl>(handle);
}
std::shared_ptr<File> File::open_existing(const std::string& filename)
{
HANDLE handle = CreateFileW(to_utf16(filename).c_str(), FILE_READ_ACCESS, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
if (handle == INVALID_HANDLE_VALUE)
throw std::runtime_error("Could not open " + filename);
return std::make_shared<FileImpl>(handle);
}
#else
#include <stdio.h>
class FileImpl : public File
{
public:
FileImpl(FILE* handle) : handle(handle)
{
}
~FileImpl()
{
fclose(handle);
}
void write(const void* data, size_t size) override
{
size_t result = fwrite(data, 1, size, handle);
if (result != size)
throw std::runtime_error("File write failed");
}
void read(void* data, size_t size) override
{
size_t result = fread(data, 1, size, handle);
if (result != size)
throw std::runtime_error("File read failed");
}
int64_t size() override
{
auto pos = ftell(handle);
auto result = fseek(handle, 0, SEEK_END);
if (result == -1)
throw std::runtime_error("File seek failed");
auto length = ftell(handle);
fseek(handle, pos, SEEK_SET);
if (length == -1)
throw std::runtime_error("File tell failed");
return length;
}
void seek(int64_t offset, SeekPoint origin) override
{
if (origin == SeekPoint::current)
{
auto result = fseek(handle, offset, SEEK_CUR);
if (result == -1)
throw std::runtime_error("File seek failed");
}
else if (origin == SeekPoint::end)
{
auto result = fseek(handle, offset, SEEK_END);
if (result == -1)
throw std::runtime_error("File seek failed");
}
else
{
auto result = fseek(handle, offset, SEEK_SET);
if (result == -1)
throw std::runtime_error("File seek failed");
}
}
uint64_t tell() override
{
auto result = ftell(handle);
if (result == -1)
throw std::runtime_error("File tell failed");
return result;
}
FILE* handle = nullptr;
};
std::shared_ptr<File> File::create_always(const std::string& filename)
{
FILE* handle = fopen(filename.c_str(), "wb");
if (!handle)
throw std::runtime_error("Could not create " + filename);
return std::make_shared<FileImpl>(handle);
}
std::shared_ptr<File> File::open_existing(const std::string& filename)
{
FILE* handle = fopen(filename.c_str(), "rb");
if (!handle)
throw std::runtime_error("Could not open " + filename);
return std::make_shared<FileImpl>(handle);
}
#endif
void File::write_all_bytes(const std::string& filename, const void* data, size_t size)
{
auto file = create_always(filename);
file->write(data, size);
}
void File::write_all_text(const std::string& filename, const std::string& text)
{
auto file = create_always(filename);
file->write(text.data(), text.size());
}
std::vector<uint8_t> File::read_all_bytes(const std::string& filename)
{
auto file = open_existing(filename);
std::vector<uint8_t> buffer(file->size());
file->read(buffer.data(), buffer.size());
return buffer;
}
std::string File::read_all_text(const std::string& filename)
{
auto file = open_existing(filename);
auto size = file->size();
if (size == 0) return {};
std::string buffer;
buffer.resize(size);
file->read(&buffer[0], buffer.size());
return buffer;
}
int64_t File::get_last_write_time(const std::string& filename)
{
#ifdef WIN32
HANDLE handle = CreateFileW(to_utf16(filename).c_str(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
if (handle == INVALID_HANDLE_VALUE)
throw std::runtime_error("Could not open " + filename);
FILETIME filetime = {};
BOOL result = GetFileTime(handle, nullptr, nullptr, &filetime);
CloseHandle(handle);
if (result == FALSE)
throw std::runtime_error("GetFileTime failed for " + filename);
LARGE_INTEGER li;
li.LowPart = filetime.dwLowDateTime;
li.HighPart = filetime.dwHighDateTime;
return li.QuadPart;
#else
throw std::runtime_error("File::get_last_write_time not implemented");
#endif
}
bool File::try_remove(const std::string& filename)
{
#ifdef WIN32
return DeleteFileW(to_utf16(filename).c_str()) == TRUE;
#else
throw std::runtime_error("File::try_remove not implemented");
#endif
}
/////////////////////////////////////////////////////////////////////////////
std::vector<std::string> Directory::files(const std::string& filename)
{
#ifdef WIN32
std::vector<std::string> files;
WIN32_FIND_DATAW fileinfo;
HANDLE handle = FindFirstFileW(to_utf16(filename).c_str(), &fileinfo);
if (handle == INVALID_HANDLE_VALUE)
return {};
try
{
do
{
bool is_directory = !!(fileinfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);
if (!is_directory)
files.push_back(from_utf16(fileinfo.cFileName));
} while (FindNextFileW(handle, &fileinfo) == TRUE);
FindClose(handle);
}
catch (...)
{
FindClose(handle);
throw;
}
return files;
#else
throw std::runtime_error("Directory::files not implemented");
#endif
}
std::vector<std::string> Directory::folders(const std::string& filename)
{
#ifdef WIN32
std::vector<std::string> files;
WIN32_FIND_DATAW fileinfo;
HANDLE handle = FindFirstFileW(to_utf16(filename).c_str(), &fileinfo);
if (handle == INVALID_HANDLE_VALUE)
return {};
try
{
do
{
bool is_directory = !!(fileinfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);
if (is_directory)
{
files.push_back(from_utf16(fileinfo.cFileName));
if (files.back() == "." || files.back() == "..")
files.pop_back();
}
} while (FindNextFileW(handle, &fileinfo) == TRUE);
FindClose(handle);
}
catch (...)
{
FindClose(handle);
throw;
}
return files;
#else
throw std::runtime_error("Directory::folders not implemented");
#endif
}
void Directory::create(const std::string& path)
{
#ifdef WIN32
BOOL result = CreateDirectoryW(to_utf16(path).c_str(), nullptr);
if (result == FALSE)
{
DWORD error = GetLastError();
if (error == ERROR_ALREADY_EXISTS)
{
return;
}
else if (error == ERROR_PATH_NOT_FOUND)
{
try
{
std::string parent = FilePath::remove_last_component(path);
if (!parent.empty())
{
Directory::create(parent);
if (CreateDirectoryW(to_utf16(path).c_str(), nullptr) == TRUE)
return;
}
}
catch (...)
{
}
}
throw std::runtime_error("Could not create directory for path " + path);
}
#else
throw std::runtime_error("Directory::create not implemented");
#endif
}
/////////////////////////////////////////////////////////////////////////////
bool FilePath::has_extension(const std::string &filename, const char *checkext)
{
auto fileext = extension(filename);
#ifdef WIN32
return _stricmp(fileext.c_str(), checkext) == 0;
#else
return strcasecmp(fileext.c_str(), checkext) == 0;
#endif
}
std::string FilePath::extension(const std::string &filename)
{
std::string file = last_component(filename);
std::string::size_type pos = file.find_last_of('.');
if (pos == std::string::npos)
return std::string();
#ifndef WIN32
// Files beginning with a dot is not a filename extension in Unix.
// This is different from Windows where it is considered the extension.
if (pos == 0)
return std::string();
#endif
return file.substr(pos + 1);
}
std::string FilePath::remove_extension(const std::string &filename)
{
std::string file = last_component(filename);
std::string::size_type pos = file.find_last_of('.');
if (pos == std::string::npos)
return filename;
else
return filename.substr(0, filename.length() - file.length() + pos);
}
std::string FilePath::last_component(const std::string &path)
{
#ifdef WIN32
auto last_slash = path.find_last_of("/\\");
if (last_slash != std::string::npos)
return path.substr(last_slash + 1);
else
return path;
#else
auto last_slash = path.find_last_of('/');
if (last_slash != std::string::npos)
return path.substr(last_slash + 1);
else
return path;
#endif
}
std::string FilePath::remove_last_component(const std::string &path)
{
#ifdef WIN32
auto last_slash = path.find_last_of("/\\");
if (last_slash != std::string::npos)
return path.substr(0, last_slash);
else
return std::string();
#else
auto last_slash = path.find_last_of('/');
if (last_slash != std::string::npos)
return path.substr(0, last_slash + 1);
else
return std::string();
#endif
}
std::string FilePath::combine(const std::string &path1, const std::string &path2)
{
#ifdef WIN32
if (path1.empty())
return path2;
else if (path2.empty())
return path1;
else if (path2.front() == '/' || path2.front() == '\\')
return path2;
else if (path1.back() != '/' && path1.back() != '\\')
return path1 + "\\" + path2;
else
return path1 + path2;
#else
if (path1.empty())
return path2;
else if (path2.empty())
return path1;
else if (path2.front() == '/')
return path2;
else if (path1.back() != '/')
return path1 + "/" + path2;
else
return path1 + path2;
#endif
}
std::string FilePath::force_filesys_slash(std::string path)
{
#ifdef WIN32
return force_backslash(std::move(path));
#else
return force_slash(std::move(path));
#endif
}
std::string FilePath::force_slash(std::string path)
{
for (char& c : path)
{
if (c == '\\') c = '/';
}
return path;
}
std::string FilePath::force_backslash(std::string path)
{
for (char& c : path)
{
if (c == '/') c = '\\';
}
return path;
}

66
src/framework/file.h Normal file
View file

@ -0,0 +1,66 @@
#pragma once
#include <memory>
#include <string>
#include <vector>
#include <cstdint>
enum class SeekPoint
{
begin,
current,
end
};
class File
{
public:
static std::shared_ptr<File> create_always(const std::string &filename);
static std::shared_ptr<File> open_existing(const std::string &filename);
static void write_all_bytes(const std::string& filename, const void* data, size_t size);
static void write_all_text(const std::string& filename, const std::string& text);
static std::vector<uint8_t> read_all_bytes(const std::string& filename);
static std::string read_all_text(const std::string& filename);
static int64_t get_last_write_time(const std::string& filename);
static bool try_remove(const std::string& filename);
uint8_t read_uint8() { uint8_t v; read(&v, sizeof(uint8_t)); return v; }
int8_t read_int8() { int8_t v; read(&v, sizeof(int8_t)); return v; }
uint16_t read_uint16() { uint16_t v; read(&v, sizeof(uint16_t)); return v; }
int16_t read_int16() { int16_t v; read(&v, sizeof(int16_t)); return v; }
uint32_t read_uint32() { uint32_t v; read(&v, sizeof(uint32_t)); return v; }
int32_t read_int32() { int32_t v; read(&v, sizeof(int32_t)); return v; }
uint64_t read_uint64() { uint64_t v; read(&v, sizeof(uint64_t)); return v; }
int64_t read_int64() { int64_t v; read(&v, sizeof(int64_t)); return v; }
virtual ~File() = default;
virtual int64_t size() = 0;
virtual void read(void *data, size_t size) = 0;
virtual void write(const void *data, size_t size) = 0;
virtual void seek(int64_t offset, SeekPoint origin = SeekPoint::begin) = 0;
virtual uint64_t tell() = 0;
};
class Directory
{
public:
static std::vector<std::string> files(const std::string &filename);
static std::vector<std::string> folders(const std::string& filename);
static void create(const std::string& path);
};
class FilePath
{
public:
static bool has_extension(const std::string &filename, const char *extension);
static std::string extension(const std::string &filename);
static std::string remove_extension(const std::string &filename);
static std::string last_component(const std::string &path);
static std::string remove_last_component(const std::string &path);
static std::string combine(const std::string &path1, const std::string &path2);
static std::string force_filesys_slash(std::string path);
static std::string force_slash(std::string path);
static std::string force_backslash(std::string path);
};

View file

@ -0,0 +1,288 @@
#include "filesystem.h"
#include "zipreader.h"
#include "file.h"
#include <map>
#include <stdexcept>
FFileSystem fileSystem;
/////////////////////////////////////////////////////////////////////////////
class ZipFileSystemSource : public IFileSystemSource
{
public:
ZipFileSystemSource(const FString& filename)
{
zip = ZipReader::open(filename.GetChars());
}
int GetLumpCount() override
{
return zip->get_num_files();
}
int CheckNumForFullName(const FString& fullname) override
{
return zip->locate_file(fullname.GetChars());
}
int FileLength(int lump) override
{
uint64_t size = zip->get_uncompressed_size(lump);
// For safety. The filesystem API clearly wasn't designed to handle large files.
if (size >= 0x7fffffff) throw std::runtime_error("File is too big");
return (int)size;
}
FileData ReadFile(int lump) override
{
FileData data;
data.Buffer = zip->read_all_bytes(lump);
return data;
}
const char* GetFileFullName(int lump, bool returnshort) override
{
static std::string tempstring;
tempstring = zip->get_filename(lump);
return tempstring.c_str();
}
std::unique_ptr<ZipReader> zip;
};
/////////////////////////////////////////////////////////////////////////////
class FolderFileSystemSource : public IFileSystemSource
{
public:
FolderFileSystemSource(const FString& foldername)
{
Basepath = foldername;
ScanFolder(foldername.GetChars());
}
int GetLumpCount() override
{
return (int)Filenames.size();
}
int CheckNumForFullName(const FString& fullname) override
{
auto it = FullnameKeyToIndex.find(ToFullnameKey(fullname.GetChars()));
if (it == FullnameKeyToIndex.end())
return -1;
return (int)it->second;
}
int FileLength(int lump) override
{
int64_t size = File::open_existing(FilePath::combine(Basepath, Filenames[lump]))->size();
// For safety. The filesystem API clearly wasn't designed to handle large files.
if (size >= 0x7fffffff) throw std::runtime_error("File is too big");
return (int)size;
}
FileData ReadFile(int lump) override
{
FileData data;
data.Buffer = File::read_all_bytes(FilePath::combine(Basepath, Filenames[lump]));
return data;
}
const char* GetFileFullName(int lump, bool returnshort) override
{
return Filenames[lump].c_str();
}
static std::string ToFullnameKey(std::string name)
{
for (char& c : name)
{
if (c == '\\')
c = '/';
if (c >= 'A' && c <= 'Z')
c = c - 'A' + 'a';
}
return name;
}
void ScanFolder(const std::string& foldername, const std::string& relativefoldername = {}, int depth = 0)
{
std::string search = FilePath::combine(foldername, "*");
for (const std::string& filename : Directory::files(search))
{
std::string fullname = FilePath::combine(relativefoldername, filename);
FullnameKeyToIndex[ToFullnameKey(fullname)] = Filenames.size();
Filenames.push_back(fullname);
}
if (depth < 16)
{
for (const std::string& subfolder : Directory::folders(search))
{
ScanFolder(FilePath::combine(foldername, subfolder), FilePath::combine(relativefoldername, subfolder), depth + 1);
}
}
}
std::string Basepath;
std::vector<std::string> Filenames;
std::map<std::string, size_t> FullnameKeyToIndex;
};
/////////////////////////////////////////////////////////////////////////////
class WadFileEntry
{
public:
uint32_t offset = 0;
uint32_t size = 0;
std::string name;
void *data = nullptr;
};
class WadFileSystemSource : public IFileSystemSource
{
public:
WadFileSystemSource(const FString& filename)
{
file = File::open_existing(filename.GetChars());
char magic[4];
file->read(magic, 4);
pwad = memcmp(magic, "PWAD", 4) == 0;
iwad = memcmp(magic, "IWAD", 4) == 0;
if (!pwad && !iwad)
throw std::runtime_error("Not a valid WAD file");
uint32_t num_entries = file->read_uint32();
uint32_t directory_offset = file->read_uint32();
file->seek(directory_offset);
for (uint32_t i = 0; i < num_entries; i++)
{
WadFileEntry entry;
entry.offset = file->read_uint32();
entry.size = file->read_uint32();
char name[9];
name[8] = 0;
file->read(name, 8);
entry.name = name;
FilenameKeyToIndex[entry.name] = entries.size();
entries.push_back(entry);
}
}
int GetLumpCount() override
{
return (int)entries.size();
}
int CheckNumForFullName(const FString& fullname) override
{
auto it = FilenameKeyToIndex.find(fullname.GetChars());
if (it == FilenameKeyToIndex.end())
return -1;
return (int)it->second;
}
int FileLength(int lump) override
{
return entries[lump].size;
}
FileData ReadFile(int lump) override
{
FileData data;
data.Buffer.resize(entries[lump].size);
file->seek(entries[lump].offset);
file->read(data.Buffer.data(), data.Buffer.size());
return data;
}
const char* GetFileFullName(int lump, bool returnshort) override
{
return entries[lump].name.c_str();
}
std::shared_ptr<File> file;
bool pwad = false;
bool iwad = false;
std::vector<WadFileEntry> entries;
std::map<std::string, size_t> FilenameKeyToIndex;
};
/////////////////////////////////////////////////////////////////////////////
void FFileSystem::AddZipSource(const FString& filename)
{
Sources.push_back(std::make_unique<ZipFileSystemSource>(filename));
}
void FFileSystem::AddFolderSource(const FString& foldername)
{
Sources.push_back(std::make_unique<FolderFileSystemSource>(foldername));
}
void FFileSystem::AddWadSource(const FString& filename)
{
Sources.push_back(std::make_unique<WadFileSystemSource>(filename));
}
int FFileSystem::CheckNumForFullName(const FString& fullname)
{
int pos = 0;
for (auto& source : Sources)
{
int index = source->CheckNumForFullName(fullname);
if (index != -1)
return pos + index;
pos += source->GetLumpCount();
}
return -1;
}
int FFileSystem::FileLength(int lump)
{
int pos = 0;
for (auto& source : Sources)
{
if (lump - pos < source->GetLumpCount())
{
return source->FileLength(lump - pos);
}
pos += source->GetLumpCount();
}
return 0;
}
FileData FFileSystem::ReadFile(int lump)
{
int pos = 0;
for (auto& source : Sources)
{
if (lump - pos < source->GetLumpCount())
{
return source->ReadFile(lump - pos);
}
pos += source->GetLumpCount();
}
return {};
}
const char* FFileSystem::GetFileFullName(int lump, bool returnshort) const
{
int pos = 0;
for (auto& source : Sources)
{
if (lump - pos < source->GetLumpCount())
{
return source->GetFileFullName(lump - pos, returnshort);
}
pos += source->GetLumpCount();
}
return {};
}

View file

@ -0,0 +1,42 @@
#pragma once
#include "framework/tarray.h"
#include "framework/templates.h"
#include "framework/zstring.h"
#include <memory>
#include <vector>
struct FileData
{
char* GetMem() { return (char*)Buffer.data(); }
std::vector<uint8_t> Buffer;
};
class IFileSystemSource
{
public:
virtual ~IFileSystemSource() = default;
virtual int GetLumpCount() = 0;
virtual int CheckNumForFullName(const FString& fullname) = 0;
virtual int FileLength(int lump) = 0;
virtual FileData ReadFile(int lump) = 0;
virtual const char* GetFileFullName(int lump, bool returnshort) = 0;
};
class FFileSystem
{
public:
void AddZipSource(const FString& filename);
void AddFolderSource(const FString& foldername);
void AddWadSource(const FString& filename);
int CheckNumForFullName(const FString& fullname);
int FileLength(int lump);
FileData ReadFile(int lump);
const char* GetFileFullName(int lump, bool returnshort = true) const;
private:
std::vector<std::unique_ptr<IFileSystemSource>> Sources;
};
extern FFileSystem fileSystem;

710
src/framework/matrix.cpp Normal file
View file

@ -0,0 +1,710 @@
/* --------------------------------------------------
Lighthouse3D
VSMatrix - Very Simple Matrix Library
http://www.lighthouse3d.com/very-simple-libs
This is a simplified version of VSMatrix that has been adjusted for GZDoom's needs.
----------------------------------------------------*/
#include <algorithm>
#include <math.h>
#include "matrix.h"
#ifdef _MSC_VER
#pragma warning(disable : 4244) // truncate from double to float
#endif
static inline FLOATTYPE
DegToRad(FLOATTYPE degrees)
{
return (FLOATTYPE)(degrees * (pi::pif() / 180.0f));
};
// sets the square matrix mat to the identity matrix,
// size refers to the number of rows (or columns)
void
VSMatrix::setIdentityMatrix( FLOATTYPE *mat, int size) {
// fill matrix with 0s
for (int i = 0; i < size * size; ++i)
mat[i] = 0.0f;
// fill diagonal with 1s
for (int i = 0; i < size; ++i)
mat[i + i * size] = 1.0f;
}
// gl LoadIdentity implementation
void
VSMatrix::loadIdentity()
{
// fill matrix with 0s
for (int i = 0; i < 16; ++i)
mMatrix[i] = 0.0f;
// fill diagonal with 1s
for (int i = 0; i < 4; ++i)
mMatrix[i + i * 4] = 1.0f;
}
// gl MultMatrix implementation
void
VSMatrix::multMatrix(const FLOATTYPE *aMatrix)
{
FLOATTYPE res[16];
for (int i = 0; i < 4; ++i)
{
for (int j = 0; j < 4; ++j)
{
res[j*4 + i] = 0.0f;
for (int k = 0; k < 4; ++k)
{
res[j*4 + i] += mMatrix[k*4 + i] * aMatrix[j*4 + k];
}
}
}
memcpy(mMatrix, res, 16 * sizeof(FLOATTYPE));
}
#ifdef USE_DOUBLE
// gl MultMatrix implementation
void
VSMatrix::multMatrix(const float *aMatrix)
{
FLOATTYPE res[16];
for (int i = 0; i < 4; ++i)
{
for (int j = 0; j < 4; ++j)
{
res[j * 4 + i] = 0.0f;
for (int k = 0; k < 4; ++k)
{
res[j*4 + i] += mMatrix[k*4 + i] * aMatrix[j*4 + k];
}
}
}
memcpy(mMatrix, res, 16 * sizeof(FLOATTYPE));
}
#endif
void VSMatrix::multQuaternion(const TVector4<FLOATTYPE>& q)
{
FLOATTYPE m[16] = { FLOATTYPE(0.0) };
m[0 * 4 + 0] = FLOATTYPE(1.0) - FLOATTYPE(2.0) * q.Y * q.Y - FLOATTYPE(2.0) * q.Z * q.Z;
m[1 * 4 + 0] = FLOATTYPE(2.0) * q.X * q.Y - FLOATTYPE(2.0) * q.W * q.Z;
m[2 * 4 + 0] = FLOATTYPE(2.0) * q.X * q.Z + FLOATTYPE(2.0) * q.W * q.Y;
m[0 * 4 + 1] = FLOATTYPE(2.0) * q.X * q.Y + FLOATTYPE(2.0) * q.W * q.Z;
m[1 * 4 + 1] = FLOATTYPE(1.0) - FLOATTYPE(2.0) * q.X * q.X - FLOATTYPE(2.0) * q.Z * q.Z;
m[2 * 4 + 1] = FLOATTYPE(2.0) * q.Y * q.Z - FLOATTYPE(2.0) * q.W * q.X;
m[0 * 4 + 2] = FLOATTYPE(2.0) * q.X * q.Z - FLOATTYPE(2.0) * q.W * q.Y;
m[1 * 4 + 2] = FLOATTYPE(2.0) * q.Y * q.Z + FLOATTYPE(2.0) * q.W * q.X;
m[2 * 4 + 2] = FLOATTYPE(1.0) - FLOATTYPE(2.0) * q.X * q.X - FLOATTYPE(2.0) * q.Y * q.Y;
m[3 * 4 + 3] = FLOATTYPE(1.0);
multMatrix(m);
}
// gl LoadMatrix implementation
void
VSMatrix::loadMatrix(const FLOATTYPE *aMatrix)
{
memcpy(mMatrix, aMatrix, 16 * sizeof(FLOATTYPE));
}
#ifdef USE_DOUBLE
// gl LoadMatrix implementation
void
VSMatrix::loadMatrix(const float *aMatrix)
{
for (int i = 0; i < 16; ++i)
{
mMatrix[i] = aMatrix[i];
}
}
#endif
// gl Translate implementation
void
VSMatrix::translate(FLOATTYPE x, FLOATTYPE y, FLOATTYPE z)
{
mMatrix[12] = mMatrix[0] * x + mMatrix[4] * y + mMatrix[8] * z + mMatrix[12];
mMatrix[13] = mMatrix[1] * x + mMatrix[5] * y + mMatrix[9] * z + mMatrix[13];
mMatrix[14] = mMatrix[2] * x + mMatrix[6] * y + mMatrix[10] * z + mMatrix[14];
}
void VSMatrix::transpose()
{
FLOATTYPE original[16];
for (int cnt = 0; cnt < 16; cnt++)
original[cnt] = mMatrix[cnt];
mMatrix[0] = original[0];
mMatrix[1] = original[4];
mMatrix[2] = original[8];
mMatrix[3] = original[12];
mMatrix[4] = original[1];
mMatrix[5] = original[5];
mMatrix[6] = original[9];
mMatrix[7] = original[13];
mMatrix[8] = original[2];
mMatrix[9] = original[6];
mMatrix[10] = original[10];
mMatrix[11] = original[14];
mMatrix[12] = original[3];
mMatrix[13] = original[7];
mMatrix[14] = original[11];
mMatrix[15] = original[15];
}
// gl Scale implementation
void
VSMatrix::scale(FLOATTYPE x, FLOATTYPE y, FLOATTYPE z)
{
mMatrix[0] *= x; mMatrix[1] *= x; mMatrix[2] *= x; mMatrix[3] *= x;
mMatrix[4] *= y; mMatrix[5] *= y; mMatrix[6] *= y; mMatrix[7] *= y;
mMatrix[8] *= z; mMatrix[9] *= z; mMatrix[10] *= z; mMatrix[11] *= z;
}
// gl Rotate implementation
void
VSMatrix::rotate(FLOATTYPE angle, FLOATTYPE x, FLOATTYPE y, FLOATTYPE z)
{
FLOATTYPE mat[16];
FLOATTYPE v[3];
v[0] = x;
v[1] = y;
v[2] = z;
FLOATTYPE radAngle = DegToRad(angle);
FLOATTYPE co = cos(radAngle);
FLOATTYPE si = sin(radAngle);
normalize(v);
FLOATTYPE x2 = v[0]*v[0];
FLOATTYPE y2 = v[1]*v[1];
FLOATTYPE z2 = v[2]*v[2];
// mat[0] = x2 + (y2 + z2) * co;
mat[0] = co + x2 * (1 - co);// + (y2 + z2) * co;
mat[4] = v[0] * v[1] * (1 - co) - v[2] * si;
mat[8] = v[0] * v[2] * (1 - co) + v[1] * si;
mat[12]= 0.0f;
mat[1] = v[0] * v[1] * (1 - co) + v[2] * si;
// mat[5] = y2 + (x2 + z2) * co;
mat[5] = co + y2 * (1 - co);
mat[9] = v[1] * v[2] * (1 - co) - v[0] * si;
mat[13]= 0.0f;
mat[2] = v[0] * v[2] * (1 - co) - v[1] * si;
mat[6] = v[1] * v[2] * (1 - co) + v[0] * si;
// mat[10]= z2 + (x2 + y2) * co;
mat[10]= co + z2 * (1 - co);
mat[14]= 0.0f;
mat[3] = 0.0f;
mat[7] = 0.0f;
mat[11]= 0.0f;
mat[15]= 1.0f;
multMatrix(mat);
}
// gluLookAt implementation
void
VSMatrix::lookAt(FLOATTYPE xPos, FLOATTYPE yPos, FLOATTYPE zPos,
FLOATTYPE xLook, FLOATTYPE yLook, FLOATTYPE zLook,
FLOATTYPE xUp, FLOATTYPE yUp, FLOATTYPE zUp)
{
FLOATTYPE dir[3], right[3], up[3];
up[0] = xUp; up[1] = yUp; up[2] = zUp;
dir[0] = (xLook - xPos);
dir[1] = (yLook - yPos);
dir[2] = (zLook - zPos);
normalize(dir);
crossProduct(dir,up,right);
normalize(right);
crossProduct(right,dir,up);
normalize(up);
FLOATTYPE m1[16],m2[16];
m1[0] = right[0];
m1[4] = right[1];
m1[8] = right[2];
m1[12] = 0.0f;
m1[1] = up[0];
m1[5] = up[1];
m1[9] = up[2];
m1[13] = 0.0f;
m1[2] = -dir[0];
m1[6] = -dir[1];
m1[10] = -dir[2];
m1[14] = 0.0f;
m1[3] = 0.0f;
m1[7] = 0.0f;
m1[11] = 0.0f;
m1[15] = 1.0f;
setIdentityMatrix(m2,4);
m2[12] = -xPos;
m2[13] = -yPos;
m2[14] = -zPos;
multMatrix(m1);
multMatrix(m2);
}
// gluPerspective implementation
void
VSMatrix::perspective(FLOATTYPE fov, FLOATTYPE ratio, FLOATTYPE nearp, FLOATTYPE farp)
{
FLOATTYPE f = 1.0f / tan (fov * (pi::pif() / 360.0f));
loadIdentity();
mMatrix[0] = f / ratio;
mMatrix[1 * 4 + 1] = f;
mMatrix[2 * 4 + 2] = (farp + nearp) / (nearp - farp);
mMatrix[3 * 4 + 2] = (2.0f * farp * nearp) / (nearp - farp);
mMatrix[2 * 4 + 3] = -1.0f;
mMatrix[3 * 4 + 3] = 0.0f;
}
// gl Ortho implementation
void
VSMatrix::ortho(FLOATTYPE left, FLOATTYPE right,
FLOATTYPE bottom, FLOATTYPE top,
FLOATTYPE nearp, FLOATTYPE farp)
{
loadIdentity();
mMatrix[0 * 4 + 0] = 2 / (right - left);
mMatrix[1 * 4 + 1] = 2 / (top - bottom);
mMatrix[2 * 4 + 2] = -2 / (farp - nearp);
mMatrix[3 * 4 + 0] = -(right + left) / (right - left);
mMatrix[3 * 4 + 1] = -(top + bottom) / (top - bottom);
mMatrix[3 * 4 + 2] = -(farp + nearp) / (farp - nearp);
}
// gl Frustum implementation
void
VSMatrix::frustum(FLOATTYPE left, FLOATTYPE right,
FLOATTYPE bottom, FLOATTYPE top,
FLOATTYPE nearp, FLOATTYPE farp)
{
FLOATTYPE m[16];
setIdentityMatrix(m,4);
m[0 * 4 + 0] = 2 * nearp / (right-left);
m[1 * 4 + 1] = 2 * nearp / (top - bottom);
m[2 * 4 + 0] = (right + left) / (right - left);
m[2 * 4 + 1] = (top + bottom) / (top - bottom);
m[2 * 4 + 2] = - (farp + nearp) / (farp - nearp);
m[2 * 4 + 3] = -1.0f;
m[3 * 4 + 2] = - 2 * farp * nearp / (farp-nearp);
m[3 * 4 + 3] = 0.0f;
multMatrix(m);
}
/*
// returns a pointer to the requested matrix
FLOATTYPE *
VSMatrix::get(MatrixTypes aType)
{
return mMatrix[aType];
}
*/
/* -----------------------------------------------------
SEND MATRICES TO OPENGL
------------------------------------------------------*/
// -----------------------------------------------------
// AUX functions
// -----------------------------------------------------
// Compute res = M * point
void
VSMatrix::multMatrixPoint(const FLOATTYPE *point, FLOATTYPE *res)
{
for (int i = 0; i < 4; ++i)
{
res[i] = 0.0f;
for (int j = 0; j < 4; j++) {
res[i] += point[j] * mMatrix[j*4 + i];
}
}
}
// res = a cross b;
void
VSMatrix::crossProduct(const FLOATTYPE *a, const FLOATTYPE *b, FLOATTYPE *res) {
res[0] = a[1] * b[2] - b[1] * a[2];
res[1] = a[2] * b[0] - b[2] * a[0];
res[2] = a[0] * b[1] - b[0] * a[1];
}
// returns a . b
FLOATTYPE
VSMatrix::dotProduct(const FLOATTYPE *a, const FLOATTYPE *b) {
FLOATTYPE res = a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
return res;
}
// Normalize a vec3
void
VSMatrix::normalize(FLOATTYPE *a) {
FLOATTYPE mag = sqrt(a[0] * a[0] + a[1] * a[1] + a[2] * a[2]);
a[0] /= mag;
a[1] /= mag;
a[2] /= mag;
}
// res = b - a
void
VSMatrix::subtract(const FLOATTYPE *a, const FLOATTYPE *b, FLOATTYPE *res) {
res[0] = b[0] - a[0];
res[1] = b[1] - a[1];
res[2] = b[2] - a[2];
}
// res = a + b
void
VSMatrix::add(const FLOATTYPE *a, const FLOATTYPE *b, FLOATTYPE *res) {
res[0] = b[0] + a[0];
res[1] = b[1] + a[1];
res[2] = b[2] + a[2];
}
// returns |a|
FLOATTYPE
VSMatrix::length(const FLOATTYPE *a) {
return(sqrt(a[0] * a[0] + a[1] * a[1] + a[2] * a[2]));
}
// computes the derived normal matrix for the view matrix
void
VSMatrix::computeNormalMatrix(const FLOATTYPE *aMatrix)
{
double mMat3x3[9];
mMat3x3[0] = aMatrix[0];
mMat3x3[1] = aMatrix[1];
mMat3x3[2] = aMatrix[2];
mMat3x3[3] = aMatrix[4];
mMat3x3[4] = aMatrix[5];
mMat3x3[5] = aMatrix[6];
mMat3x3[6] = aMatrix[8];
mMat3x3[7] = aMatrix[9];
mMat3x3[8] = aMatrix[10];
double det, invDet;
det = mMat3x3[0] * (mMat3x3[4] * mMat3x3[8] - mMat3x3[5] * mMat3x3[7]) +
mMat3x3[1] * (mMat3x3[5] * mMat3x3[6] - mMat3x3[8] * mMat3x3[3]) +
mMat3x3[2] * (mMat3x3[3] * mMat3x3[7] - mMat3x3[4] * mMat3x3[6]);
invDet = 1.0/det;
mMatrix[0] = (mMat3x3[4] * mMat3x3[8] - mMat3x3[5] * mMat3x3[7]) * invDet;
mMatrix[1] = (mMat3x3[5] * mMat3x3[6] - mMat3x3[8] * mMat3x3[3]) * invDet;
mMatrix[2] = (mMat3x3[3] * mMat3x3[7] - mMat3x3[4] * mMat3x3[6]) * invDet;
mMatrix[3] = 0.0f;
mMatrix[4] = (mMat3x3[2] * mMat3x3[7] - mMat3x3[1] * mMat3x3[8]) * invDet;
mMatrix[5] = (mMat3x3[0] * mMat3x3[8] - mMat3x3[2] * mMat3x3[6]) * invDet;
mMatrix[6] = (mMat3x3[1] * mMat3x3[6] - mMat3x3[7] * mMat3x3[0]) * invDet;
mMatrix[7] = 0.0f;
mMatrix[8] = (mMat3x3[1] * mMat3x3[5] - mMat3x3[4] * mMat3x3[2]) * invDet;
mMatrix[9] = (mMat3x3[2] * mMat3x3[3] - mMat3x3[0] * mMat3x3[5]) * invDet;
mMatrix[10] =(mMat3x3[0] * mMat3x3[4] - mMat3x3[3] * mMat3x3[1]) * invDet;
mMatrix[11] = 0.0;
mMatrix[12] = 0.0;
mMatrix[13] = 0.0;
mMatrix[14] = 0.0;
mMatrix[15] = 1.0;
}
// aux function resMat = resMat * aMatrix
void
VSMatrix::multMatrix(FLOATTYPE *resMat, const FLOATTYPE *aMatrix)
{
FLOATTYPE res[16];
for (int i = 0; i < 4; ++i)
{
for (int j = 0; j < 4; ++j)
{
res[j*4 + i] = 0.0f;
for (int k = 0; k < 4; ++k)
{
res[j*4 + i] += resMat[k*4 + i] * aMatrix[j*4 + k];
}
}
}
memcpy(resMat, res, 16 * sizeof(FLOATTYPE));
}
static double mat3Determinant(const FLOATTYPE *mMat3x3)
{
return mMat3x3[0] * (mMat3x3[4] * mMat3x3[8] - mMat3x3[5] * mMat3x3[7]) +
mMat3x3[1] * (mMat3x3[5] * mMat3x3[6] - mMat3x3[8] * mMat3x3[3]) +
mMat3x3[2] * (mMat3x3[3] * mMat3x3[7] - mMat3x3[4] * mMat3x3[6]);
}
static double mat4Determinant(const FLOATTYPE *matrix)
{
FLOATTYPE mMat3x3_a[9] =
{
matrix[1 * 4 + 1], matrix[2 * 4 + 1], matrix[3 * 4 + 1],
matrix[1 * 4 + 2], matrix[2 * 4 + 2], matrix[3 * 4 + 2],
matrix[1 * 4 + 3], matrix[2 * 4 + 3], matrix[3 * 4 + 3]
};
FLOATTYPE mMat3x3_b[9] =
{
matrix[1 * 4 + 0], matrix[2 * 4 + 0], matrix[3 * 4 + 0],
matrix[1 * 4 + 2], matrix[2 * 4 + 2], matrix[3 * 4 + 2],
matrix[1 * 4 + 3], matrix[2 * 4 + 3], matrix[3 * 4 + 3]
};
FLOATTYPE mMat3x3_c[9] =
{
matrix[1 * 4 + 0], matrix[2 * 4 + 0], matrix[3 * 4 + 0],
matrix[1 * 4 + 1], matrix[2 * 4 + 1], matrix[3 * 4 + 1],
matrix[1 * 4 + 3], matrix[2 * 4 + 3], matrix[3 * 4 + 3]
};
FLOATTYPE mMat3x3_d[9] =
{
matrix[1 * 4 + 0], matrix[2 * 4 + 0], matrix[3 * 4 + 0],
matrix[1 * 4 + 1], matrix[2 * 4 + 1], matrix[3 * 4 + 1],
matrix[1 * 4 + 2], matrix[2 * 4 + 2], matrix[3 * 4 + 2]
};
FLOATTYPE a, b, c, d;
FLOATTYPE value;
a = mat3Determinant(mMat3x3_a);
b = mat3Determinant(mMat3x3_b);
c = mat3Determinant(mMat3x3_c);
d = mat3Determinant(mMat3x3_d);
value = matrix[0 * 4 + 0] * a;
value -= matrix[0 * 4 + 1] * b;
value += matrix[0 * 4 + 2] * c;
value -= matrix[0 * 4 + 3] * d;
return value;
}
static void mat4Adjoint(const FLOATTYPE *matrix, FLOATTYPE *result)
{
FLOATTYPE mMat3x3_a[9] =
{
matrix[1 * 4 + 1], matrix[2 * 4 + 1], matrix[3 * 4 + 1],
matrix[1 * 4 + 2], matrix[2 * 4 + 2], matrix[3 * 4 + 2],
matrix[1 * 4 + 3], matrix[2 * 4 + 3], matrix[3 * 4 + 3]
};
FLOATTYPE mMat3x3_b[9] =
{
matrix[1 * 4 + 0], matrix[2 * 4 + 0], matrix[3 * 4 + 0],
matrix[1 * 4 + 2], matrix[2 * 4 + 2], matrix[3 * 4 + 2],
matrix[1 * 4 + 3], matrix[2 * 4 + 3], matrix[3 * 4 + 3]
};
FLOATTYPE mMat3x3_c[9] =
{
matrix[1 * 4 + 0], matrix[2 * 4 + 0], matrix[3 * 4 + 0],
matrix[1 * 4 + 1], matrix[2 * 4 + 1], matrix[3 * 4 + 1],
matrix[1 * 4 + 3], matrix[2 * 4 + 3], matrix[3 * 4 + 3]
};
FLOATTYPE mMat3x3_d[9] =
{
matrix[1 * 4 + 0], matrix[2 * 4 + 0], matrix[3 * 4 + 0],
matrix[1 * 4 + 1], matrix[2 * 4 + 1], matrix[3 * 4 + 1],
matrix[1 * 4 + 2], matrix[2 * 4 + 2], matrix[3 * 4 + 2]
};
FLOATTYPE mMat3x3_e[9] =
{
matrix[0 * 4 + 1], matrix[2 * 4 + 1], matrix[3 * 4 + 1],
matrix[0 * 4 + 2], matrix[2 * 4 + 2], matrix[3 * 4 + 2],
matrix[0 * 4 + 3], matrix[2 * 4 + 3], matrix[3 * 4 + 3]
};
FLOATTYPE mMat3x3_f[9] =
{
matrix[0 * 4 + 0], matrix[2 * 4 + 0], matrix[3 * 4 + 0],
matrix[0 * 4 + 2], matrix[2 * 4 + 2], matrix[3 * 4 + 2],
matrix[0 * 4 + 3], matrix[2 * 4 + 3], matrix[3 * 4 + 3]
};
FLOATTYPE mMat3x3_g[9] =
{
matrix[0 * 4 + 0], matrix[2 * 4 + 0], matrix[3 * 4 + 0],
matrix[0 * 4 + 1], matrix[2 * 4 + 1], matrix[3 * 4 + 1],
matrix[0 * 4 + 3], matrix[2 * 4 + 3], matrix[3 * 4 + 3]
};
FLOATTYPE mMat3x3_h[9] =
{
matrix[0 * 4 + 0], matrix[2 * 4 + 0], matrix[3 * 4 + 0],
matrix[0 * 4 + 1], matrix[2 * 4 + 1], matrix[3 * 4 + 1],
matrix[0 * 4 + 2], matrix[2 * 4 + 2], matrix[3 * 4 + 2]
};
FLOATTYPE mMat3x3_i[9] =
{
matrix[0 * 4 + 1], matrix[1 * 4 + 1], matrix[3 * 4 + 1],
matrix[0 * 4 + 2], matrix[1 * 4 + 2], matrix[3 * 4 + 2],
matrix[0 * 4 + 3], matrix[1 * 4 + 3], matrix[3 * 4 + 3]
};
FLOATTYPE mMat3x3_j[9] =
{
matrix[0 * 4 + 0], matrix[1 * 4 + 0], matrix[3 * 4 + 0],
matrix[0 * 4 + 2], matrix[1 * 4 + 2], matrix[3 * 4 + 2],
matrix[0 * 4 + 3], matrix[1 * 4 + 3], matrix[3 * 4 + 3]
};
FLOATTYPE mMat3x3_k[9] =
{
matrix[0 * 4 + 0], matrix[1 * 4 + 0], matrix[3 * 4 + 0],
matrix[0 * 4 + 1], matrix[1 * 4 + 1], matrix[3 * 4 + 1],
matrix[0 * 4 + 3], matrix[1 * 4 + 3], matrix[3 * 4 + 3]
};
FLOATTYPE mMat3x3_l[9] =
{
matrix[0 * 4 + 0], matrix[1 * 4 + 0], matrix[3 * 4 + 0],
matrix[0 * 4 + 1], matrix[1 * 4 + 1], matrix[3 * 4 + 1],
matrix[0 * 4 + 2], matrix[1 * 4 + 2], matrix[3 * 4 + 2]
};
FLOATTYPE mMat3x3_m[9] =
{
matrix[0 * 4 + 1], matrix[1 * 4 + 1], matrix[2 * 4 + 1],
matrix[0 * 4 + 2], matrix[1 * 4 + 2], matrix[2 * 4 + 2],
matrix[0 * 4 + 3], matrix[1 * 4 + 3], matrix[2 * 4 + 3]
};
FLOATTYPE mMat3x3_n[9] =
{
matrix[0 * 4 + 0], matrix[1 * 4 + 0], matrix[2 * 4 + 0],
matrix[0 * 4 + 2], matrix[1 * 4 + 2], matrix[2 * 4 + 2],
matrix[0 * 4 + 3], matrix[1 * 4 + 3], matrix[2 * 4 + 3]
};
FLOATTYPE mMat3x3_o[9] =
{
matrix[0 * 4 + 0], matrix[1 * 4 + 0], matrix[2 * 4 + 0],
matrix[0 * 4 + 1], matrix[1 * 4 + 1], matrix[2 * 4 + 1],
matrix[0 * 4 + 3], matrix[1 * 4 + 3], matrix[2 * 4 + 3]
};
FLOATTYPE mMat3x3_p[9] =
{
matrix[0 * 4 + 0], matrix[1 * 4 + 0], matrix[2 * 4 + 0],
matrix[0 * 4 + 1], matrix[1 * 4 + 1], matrix[2 * 4 + 1],
matrix[0 * 4 + 2], matrix[1 * 4 + 2], matrix[2 * 4 + 2]
};
result[0 * 4 + 0] = mat3Determinant(mMat3x3_a);
result[1 * 4 + 0] = -mat3Determinant(mMat3x3_b);
result[2 * 4 + 0] = mat3Determinant(mMat3x3_c);
result[3 * 4 + 0] = -mat3Determinant(mMat3x3_d);
result[0 * 4 + 1] = -mat3Determinant(mMat3x3_e);
result[1 * 4 + 1] = mat3Determinant(mMat3x3_f);
result[2 * 4 + 1] = -mat3Determinant(mMat3x3_g);
result[3 * 4 + 1] = mat3Determinant(mMat3x3_h);
result[0 * 4 + 2] = mat3Determinant(mMat3x3_i);
result[1 * 4 + 2] = -mat3Determinant(mMat3x3_j);
result[2 * 4 + 2] = mat3Determinant(mMat3x3_k);
result[3 * 4 + 2] = -mat3Determinant(mMat3x3_l);
result[0 * 4 + 3] = -mat3Determinant(mMat3x3_m);
result[1 * 4 + 3] = mat3Determinant(mMat3x3_n);
result[2 * 4 + 3] = -mat3Determinant(mMat3x3_o);
result[3 * 4 + 3] = mat3Determinant(mMat3x3_p);
}
bool VSMatrix::inverseMatrix(VSMatrix &result)
{
// Calculate mat4 determinant
FLOATTYPE det = mat4Determinant(mMatrix);
// Inverse unknown when determinant is close to zero
if (fabs(det) < 1e-15)
{
for (int i = 0; i < 16; i++)
result.mMatrix[i] = FLOATTYPE(0.0);
return false;
}
else
{
mat4Adjoint(mMatrix, result.mMatrix);
FLOATTYPE invDet = FLOATTYPE(1.0) / det;
for (int i = 0; i < 16; i++)
{
result.mMatrix[i] = result.mMatrix[i] * invDet;
}
}
return true;
}

250
src/framework/matrix.h Normal file
View file

@ -0,0 +1,250 @@
// Matrix class based on code from VSML:
/** ----------------------------------------------------------
* \class VSMathLib
*
* Lighthouse3D
*
* VSMathLib - Very Simple Matrix Library
*
* Full documentation at
* http://www.lighthouse3d.com/very-simple-libs
*
* This class aims at easing geometric transforms, camera
* placement and projection definition for programmers
* working with OpenGL core versions.
*
*
---------------------------------------------------------------*/
#ifndef __VSMatrix__
#define __VSMatrix__
#include <stdlib.h>
#include "vectors.h"
#ifdef USE_DOUBLE
typedef double FLOATTYPE;
#else
typedef float FLOATTYPE;
#endif
#ifndef DISABLE_SSE
#include <emmintrin.h>
#endif
class VSMatrix {
public:
VSMatrix() = default;
VSMatrix(int)
{
loadIdentity();
}
static VSMatrix identity()
{
VSMatrix m;
m.loadIdentity();
return m;
}
void translate(FLOATTYPE x, FLOATTYPE y, FLOATTYPE z);
void scale(FLOATTYPE x, FLOATTYPE y, FLOATTYPE z);
void rotate(FLOATTYPE angle, FLOATTYPE x, FLOATTYPE y, FLOATTYPE z);
void loadIdentity();
#ifdef USE_DOUBLE
void multMatrix(const float *aMatrix);
#endif
void multVector(FLOATTYPE *aVector);
void multMatrix(const FLOATTYPE *aMatrix);
void multMatrix(const VSMatrix &aMatrix)
{
multMatrix(aMatrix.mMatrix);
}
void multQuaternion(const TVector4<FLOATTYPE>& q);
void loadMatrix(const FLOATTYPE *aMatrix);
#ifdef USE_DOUBLE
void loadMatrix(const float *aMatrix);
#endif
void lookAt(FLOATTYPE xPos, FLOATTYPE yPos, FLOATTYPE zPos, FLOATTYPE xLook, FLOATTYPE yLook, FLOATTYPE zLook, FLOATTYPE xUp, FLOATTYPE yUp, FLOATTYPE zUp);
void perspective(FLOATTYPE fov, FLOATTYPE ratio, FLOATTYPE nearp, FLOATTYPE farp);
void ortho(FLOATTYPE left, FLOATTYPE right, FLOATTYPE bottom, FLOATTYPE top, FLOATTYPE nearp=-1.0f, FLOATTYPE farp=1.0f);
void frustum(FLOATTYPE left, FLOATTYPE right, FLOATTYPE bottom, FLOATTYPE top, FLOATTYPE nearp, FLOATTYPE farp);
void copy(FLOATTYPE * pDest)
{
memcpy(pDest, mMatrix, 16 * sizeof(FLOATTYPE));
}
#ifdef USE_DOUBLE
void copy(float * pDest)
{
for (int i = 0; i < 16; i++)
{
pDest[i] = (float)mMatrix[i];
}
}
#endif
const FLOATTYPE *get() const
{
return mMatrix;
}
void multMatrixPoint(const FLOATTYPE *point, FLOATTYPE *res);
#ifdef USE_DOUBLE
void computeNormalMatrix(const float *aMatrix);
#endif
void computeNormalMatrix(const FLOATTYPE *aMatrix);
void computeNormalMatrix(const VSMatrix &aMatrix)
{
computeNormalMatrix(aMatrix.mMatrix);
}
bool inverseMatrix(VSMatrix &result);
void transpose();
FVector4 operator *(const FVector4& v) const
{
#ifdef DISABLE_SSE
FVector4 result;
result.X = mMatrix[0 * 4 + 0] * v.X + mMatrix[1 * 4 + 0] * v.Y + mMatrix[2 * 4 + 0] * v.Z + mMatrix[3 * 4 + 0] * v.W;
result.Y = mMatrix[0 * 4 + 1] * v.X + mMatrix[1 * 4 + 1] * v.Y + mMatrix[2 * 4 + 1] * v.Z + mMatrix[3 * 4 + 1] * v.W;
result.Z = mMatrix[0 * 4 + 2] * v.X + mMatrix[1 * 4 + 2] * v.Y + mMatrix[2 * 4 + 2] * v.Z + mMatrix[3 * 4 + 2] * v.W;
result.W = mMatrix[0 * 4 + 3] * v.X + mMatrix[1 * 4 + 3] * v.Y + mMatrix[2 * 4 + 3] * v.Z + mMatrix[3 * 4 + 3] * v.W;
return result;
#else
__m128 m0 = _mm_loadu_ps(mMatrix);
__m128 m1 = _mm_loadu_ps(mMatrix + 4);
__m128 m2 = _mm_loadu_ps(mMatrix + 8);
__m128 m3 = _mm_loadu_ps(mMatrix + 12);
__m128 mv = _mm_loadu_ps(&v.X);
m0 = _mm_mul_ps(m0, _mm_shuffle_ps(mv, mv, _MM_SHUFFLE(0, 0, 0, 0)));
m1 = _mm_mul_ps(m1, _mm_shuffle_ps(mv, mv, _MM_SHUFFLE(1, 1, 1, 1)));
m2 = _mm_mul_ps(m2, _mm_shuffle_ps(mv, mv, _MM_SHUFFLE(2, 2, 2, 2)));
m3 = _mm_mul_ps(m3, _mm_shuffle_ps(mv, mv, _MM_SHUFFLE(3, 3, 3, 3)));
mv = _mm_add_ps(_mm_add_ps(_mm_add_ps(m0, m1), m2), m3);
FVector4 result;
_mm_storeu_ps(&result.X, mv);
return result;
#endif
}
protected:
static void crossProduct(const FLOATTYPE *a, const FLOATTYPE *b, FLOATTYPE *res);
static FLOATTYPE dotProduct(const FLOATTYPE *a, const FLOATTYPE * b);
static void normalize(FLOATTYPE *a);
static void subtract(const FLOATTYPE *a, const FLOATTYPE *b, FLOATTYPE *res);
static void add(const FLOATTYPE *a, const FLOATTYPE *b, FLOATTYPE *res);
static FLOATTYPE length(const FLOATTYPE *a);
static void multMatrix(FLOATTYPE *resMatrix, const FLOATTYPE *aMatrix);
static void setIdentityMatrix(FLOATTYPE *mat, int size = 4);
/// The storage for matrices
FLOATTYPE mMatrix[16];
};
class Matrix3x4 // used like a 4x4 matrix with the last row always being (0,0,0,1)
{
float m[3][4];
public:
void MakeIdentity()
{
memset(m, 0, sizeof(m));
m[0][0] = m[1][1] = m[2][2] = 1.f;
}
void Translate(float x, float y, float z)
{
m[0][3] = m[0][0]*x + m[0][1]*y + m[0][2]*z + m[0][3];
m[1][3] = m[1][0]*x + m[1][1]*y + m[1][2]*z + m[1][3];
m[2][3] = m[2][0]*x + m[2][1]*y + m[2][2]*z + m[2][3];
}
void Scale(float x, float y, float z)
{
m[0][0] *=x;
m[1][0] *=x;
m[2][0] *=x;
m[0][1] *=y;
m[1][1] *=y;
m[2][1] *=y;
m[0][2] *=z;
m[1][2] *=z;
m[2][2] *=z;
}
void Rotate(float ax, float ay, float az, float angle)
{
Matrix3x4 m1;
FVector3 axis(ax, ay, az);
axis.MakeUnit();
double c = cos(angle * pi::pi()/180.), s = sin(angle * pi::pi()/180.), t = 1 - c;
double sx = s*axis.X, sy = s*axis.Y, sz = s*axis.Z;
double tx, ty, txx, tyy, u, v;
tx = t*axis.X;
m1.m[0][0] = float( (txx=tx*axis.X) + c );
m1.m[0][1] = float( (u=tx*axis.Y) - sz);
m1.m[0][2] = float( (v=tx*axis.Z) + sy);
ty = t*axis.Y;
m1.m[1][0] = float( u + sz);
m1.m[1][1] = float( (tyy=ty*axis.Y) + c );
m1.m[1][2] = float( (u=ty*axis.Z) - sx);
m1.m[2][0] = float( v - sy);
m1.m[2][1] = float( u + sx);
m1.m[2][2] = float( (t-txx-tyy) + c );
m1.m[0][3] = 0.f;
m1.m[1][3] = 0.f;
m1.m[2][3] = 0.f;
*this = (*this) * m1;
}
Matrix3x4 operator *(const Matrix3x4 &other) const
{
Matrix3x4 result;
result.m[0][0] = m[0][0]*other.m[0][0] + m[0][1]*other.m[1][0] + m[0][2]*other.m[2][0];
result.m[0][1] = m[0][0]*other.m[0][1] + m[0][1]*other.m[1][1] + m[0][2]*other.m[2][1];
result.m[0][2] = m[0][0]*other.m[0][2] + m[0][1]*other.m[1][2] + m[0][2]*other.m[2][2];
result.m[0][3] = m[0][0]*other.m[0][3] + m[0][1]*other.m[1][3] + m[0][2]*other.m[2][3] + m[0][3];
result.m[1][0] = m[1][0]*other.m[0][0] + m[1][1]*other.m[1][0] + m[1][2]*other.m[2][0];
result.m[1][1] = m[1][0]*other.m[0][1] + m[1][1]*other.m[1][1] + m[1][2]*other.m[2][1];
result.m[1][2] = m[1][0]*other.m[0][2] + m[1][1]*other.m[1][2] + m[1][2]*other.m[2][2];
result.m[1][3] = m[1][0]*other.m[0][3] + m[1][1]*other.m[1][3] + m[1][2]*other.m[2][3] + m[1][3];
result.m[2][0] = m[2][0]*other.m[0][0] + m[2][1]*other.m[1][0] + m[2][2]*other.m[2][0];
result.m[2][1] = m[2][0]*other.m[0][1] + m[2][1]*other.m[1][1] + m[2][2]*other.m[2][1];
result.m[2][2] = m[2][0]*other.m[0][2] + m[2][1]*other.m[1][2] + m[2][2]*other.m[2][2];
result.m[2][3] = m[2][0]*other.m[0][3] + m[2][1]*other.m[1][3] + m[2][2]*other.m[2][3] + m[2][3];
return result;
}
FVector3 operator *(const FVector3 &vec) const
{
FVector3 result;
result.X = vec.X*m[0][0] + vec.Y*m[0][1] + vec.Z*m[0][2] + m[0][3];
result.Y = vec.X*m[1][0] + vec.Y*m[1][1] + vec.Z*m[1][2] + m[1][3];
result.Z = vec.X*m[2][0] + vec.Y*m[2][1] + vec.Z*m[2][2] + m[2][3];
return result;
}
};
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,41 @@
#include "textureid.h"
#include "filesystem.h"
#include "picopng/picopng.h"
FTextureManager TexMan;
FGameTexture::FGameTexture(FString name) : Name(name)
{
// To do: improve this to support subfolders?
// To do: figure out what type of data we got here instead of assuming it is a png.
int lump = fileSystem.CheckNumForFullName(name);
if (lump < 0)
lump = fileSystem.CheckNumForFullName("textures/" + name + ".png");
if (lump < 0)
lump = fileSystem.CheckNumForFullName("flats/" + name + ".png");
if (lump < 0)
{
// Not found - should we mark it as invalid or use a dummy texture?
DisplayWidth = 64.0f;
DisplayHeight = 64.0f;
}
else
{
FileData filedata = fileSystem.ReadFile(lump);
int result = decodePNG(Pixels, Width, Height, (const unsigned char*)filedata.GetMem(), fileSystem.FileLength(lump), true);
if (result == 0)
{
DisplayWidth = (float)Width;
DisplayHeight = (float)Height;
}
else
{
// Not a png.
DisplayWidth = 64.0f;
DisplayHeight = 64.0f;
}
}
}

174
src/framework/textureid.h Normal file
View file

@ -0,0 +1,174 @@
#pragma once
#include "framework/tarray.h"
#include "framework/templates.h"
#include "framework/zstring.h"
#include "framework/filesystem.h"
#include <map>
#include <memory>
class FGameTexture;
enum class ETextureType : uint8_t
{
Any,
Wall,
Flat,
Sprite,
WallPatch,
Build, // no longer used but needs to remain for ZScript
SkinSprite,
Decal,
MiscPatch,
FontChar,
Override, // For patches between TX_START/TX_END
Autopage, // Automap background - used to enable the use of FAutomapTexture
SkinGraphic,
Null,
FirstDefined,
Special,
SWCanvas,
};
class FTextureID
{
friend class FTextureManager;
friend void R_InitSpriteDefs();
public:
FTextureID() = default;
bool isNull() const { return texnum == 0; }
bool isValid() const { return texnum > 0; }
bool Exists() const { return texnum >= 0; }
void SetInvalid() { texnum = -1; }
void SetNull() { texnum = 0; }
bool operator ==(const FTextureID &other) const { return texnum == other.texnum; }
bool operator !=(const FTextureID &other) const { return texnum != other.texnum; }
FTextureID operator +(int offset) const noexcept(true);
int GetIndex() const { return texnum; } // Use this only if you absolutely need the index!
void SetIndex(int index) { texnum = index; } // Use this only if you absolutely need the index!
// The switch list needs these to sort the switches by texture index
int operator -(FTextureID other) const { return texnum - other.texnum; }
bool operator < (FTextureID other) const { return texnum < other.texnum; }
bool operator > (FTextureID other) const { return texnum > other.texnum; }
protected:
constexpr FTextureID(int num) : texnum(num) { }
private:
int texnum;
};
class FNullTextureID : public FTextureID
{
public:
constexpr FNullTextureID() : FTextureID(0) {}
};
// This is for the script interface which needs to do casts from int to texture.
class FSetTextureID : public FTextureID
{
public:
constexpr FSetTextureID(int v) : FTextureID(v) {}
};
class FGameTexture
{
public:
FGameTexture() { Name = "-"; Valid = false; }
FGameTexture(FString name);
bool isValid() const { return Valid; }
float GetDisplayWidth() const { return DisplayWidth; }
float GetDisplayHeight() const { return DisplayHeight; }
float GetScaleX() const { return ScaleX; }
float GetScaleY() const { return ScaleY; }
const void* GetImagePixels() const { return Pixels.data(); }
int GetImageWidth() const { return Width; }
int GetImageHeight() const { return Height; }
int GetTexelWidth() const { return GetImageWidth(); }
int GetTexelHeight() const { return GetImageHeight(); }
bool isHardwareCanvas() const { return false; }
bool useWorldPanning() const { return false; }
private:
FString Name;
bool Valid = true;
float DisplayWidth = 0.0f;
float DisplayHeight = 0.0f;
float ScaleX = 1.0f;
float ScaleY = 1.0f;
std::vector<unsigned char> Pixels;
unsigned long Width = 0;
unsigned long Height = 0;
friend class FTextureManager;
};
class FTextureManager
{
public:
FTextureManager()
{
Textures.Push(std::make_unique<FGameTexture>());
}
FGameTexture* GetGameTexture(FTextureID texnum, bool animate = false)
{
if (!texnum.isValid() || texnum.isNull())
return Textures[0].get();
return Textures[texnum.GetIndex()].get();
}
enum
{
TEXMAN_TryAny = 1,
TEXMAN_Overridable = 2,
TEXMAN_ReturnFirst = 4,
TEXMAN_AllowSkins = 8,
TEXMAN_ShortNameOnly = 16,
TEXMAN_DontCreate = 32,
TEXMAN_Localize = 64,
TEXMAN_ForceLookup = 128,
TEXMAN_NoAlias = 256,
};
enum
{
HIT_Wall = 1,
HIT_Flat = 2,
HIT_Sky = 4,
HIT_Sprite = 8,
HIT_Columnmode = HIT_Wall | HIT_Sky | HIT_Sprite
};
FTextureID CheckForTexture(const char* name, ETextureType usetype, uint32_t flags = TEXMAN_TryAny)
{
if (name == nullptr || name[0] == '\0')
return FTextureID(-1);
if (name[0] == '-' && name[1] == '\0')
return FTextureID(0);
auto it = NameToID.find(name);
if (it != NameToID.end())
return FTextureID(it->second);
int id = Textures.Size();
Textures.Push(std::make_unique<FGameTexture>(name));
NameToID[name] = id;
return FTextureID(id);
}
std::map<FString, int> NameToID;
TArray<std::unique_ptr<FGameTexture>> Textures;
};
extern FTextureManager TexMan;

54
src/framework/utf16.cpp Normal file
View file

@ -0,0 +1,54 @@
#include "utf16.h"
#include <stdexcept>
#ifdef WIN32
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <Windows.h>
#undef min
#undef max
std::wstring to_utf16(const std::string& str)
{
if (str.empty()) return {};
int needed = MultiByteToWideChar(CP_UTF8, 0, str.data(), (int)str.size(), nullptr, 0);
if (needed == 0)
throw std::runtime_error("MultiByteToWideChar failed");
std::wstring result;
result.resize(needed);
needed = MultiByteToWideChar(CP_UTF8, 0, str.data(), (int)str.size(), &result[0], (int)result.size());
if (needed == 0)
throw std::runtime_error("MultiByteToWideChar failed");
return result;
}
std::string from_utf16(const std::wstring& str)
{
if (str.empty()) return {};
int needed = WideCharToMultiByte(CP_UTF8, 0, str.data(), (int)str.size(), nullptr, 0, nullptr, nullptr);
if (needed == 0)
throw std::runtime_error("WideCharToMultiByte failed");
std::string result;
result.resize(needed);
needed = WideCharToMultiByte(CP_UTF8, 0, str.data(), (int)str.size(), &result[0], (int)result.size(), nullptr, nullptr);
if (needed == 0)
throw std::runtime_error("WideCharToMultiByte failed");
return result;
}
#else
std::wstring to_utf16(const std::string& str)
{
throw std::runtime_error("to_utf16 not implemented on this platform");
}
std::string from_utf16(const std::wstring& str)
{
throw std::runtime_error("from_utf16 not implemented on this platform");
}
#endif

6
src/framework/utf16.h Normal file
View file

@ -0,0 +1,6 @@
#pragma once
#include <string>
std::wstring to_utf16(const std::string& str);
std::string from_utf16(const std::wstring& str);

1848
src/framework/vectors.h Normal file

File diff suppressed because it is too large Load diff

View file

@ -37,6 +37,10 @@
#define finline __forceinline
#endif
typedef double real64;
typedef unsigned int uint32;
typedef signed int int32;
union _xs_doubleints
{
real64 val;

150
src/framework/zipreader.cpp Normal file
View file

@ -0,0 +1,150 @@
#include "zipreader.h"
#include "file.h"
#include <miniz/miniz.h>
#include <stdexcept>
class ZipReaderImpl : public ZipReader
{
public:
ZipReaderImpl(std::string filename)
{
file = File::open_existing(std::move(filename));
zip.m_pIO_opaque = this;
zip.m_pRead = &ZipReaderImpl::read;
mz_bool result = mz_zip_reader_init(&zip, file->size(), 0);
if (result == MZ_FALSE)
throw std::runtime_error("mz_zip_reader_init failed");
}
~ZipReaderImpl()
{
mz_zip_reader_end(&zip);
}
int get_num_files() override
{
return mz_zip_reader_get_num_files(&zip);
}
std::string get_filename(int file_index) override
{
std::string filename;
filename.resize((size_t)mz_zip_reader_get_filename(&zip, (mz_uint)file_index, nullptr, 0));
mz_zip_reader_get_filename(&zip, file_index, filename.data(), (mz_uint)filename.size());
return filename;
}
bool file_exists(const std::string& filename) override
{
mz_uint32 fileIndex;
mz_bool result = mz_zip_reader_locate_file_v2(&zip, filename.c_str(), nullptr, 0, &fileIndex);
return result;
}
uint64_t get_uncompressed_size(int file_index) override
{
mz_zip_archive_file_stat stat;
mz_bool result = mz_zip_reader_file_stat(&zip, file_index, &stat);
if (result == MZ_FALSE)
throw std::runtime_error("mz_zip_reader_file_stat failed");
return (uint64_t)stat.m_uncomp_size;
}
uint32_t get_crc32(const std::string& filename) override
{
int file_index = mz_zip_reader_locate_file(&zip, filename.c_str(), nullptr, 0);
if (file_index == -1)
throw std::runtime_error("File " + filename + " not found in archive");
return get_crc32(file_index);
}
uint32_t get_crc32(int file_index) override
{
mz_zip_archive_file_stat stat;
mz_bool result = mz_zip_reader_file_stat(&zip, file_index, &stat);
if (result == MZ_FALSE)
throw std::runtime_error("mz_zip_reader_file_stat failed");
return stat.m_crc32;
}
std::vector<uint8_t> read_all_bytes(int file_index) override
{
mz_zip_archive_file_stat stat;
mz_bool result = mz_zip_reader_file_stat(&zip, file_index, &stat);
if (result == MZ_FALSE)
throw std::runtime_error("mz_zip_reader_file_stat failed");
std::vector<uint8_t> buffer(stat.m_uncomp_size);
if (!buffer.empty())
{
mz_bool result = mz_zip_reader_extract_to_mem(&zip, file_index, buffer.data(), buffer.size(), 0);
if (result == MZ_FALSE)
throw std::runtime_error("mz_zip_reader_extract_file_to_mem failed");
}
return buffer;
}
std::string read_all_text(int file_index) override
{
mz_zip_archive_file_stat stat;
mz_bool result = mz_zip_reader_file_stat(&zip, file_index, &stat);
if (result == MZ_FALSE)
throw std::runtime_error("mz_zip_reader_file_stat failed");
std::string buffer;
buffer.resize(stat.m_uncomp_size);
if (!buffer.empty())
{
mz_bool result = mz_zip_reader_extract_to_mem(&zip, file_index, &buffer[0], buffer.size(), 0);
if (result == MZ_FALSE)
throw std::runtime_error("mz_zip_reader_extract_file_to_mem failed");
}
return buffer;
}
int locate_file(const std::string& filename) override
{
return mz_zip_reader_locate_file(&zip, filename.c_str(), nullptr, 0);
}
std::vector<uint8_t> read_all_bytes(const std::string& filename) override
{
int file_index = mz_zip_reader_locate_file(&zip, filename.c_str(), nullptr, 0);
if (file_index == -1)
throw std::runtime_error("File " + filename + " not found in archive");
return read_all_bytes(file_index);
}
std::string read_all_text(const std::string& filename) override
{
int file_index = mz_zip_reader_locate_file(&zip, filename.c_str(), nullptr, 0);
if (file_index == -1)
throw std::runtime_error("File " + filename + " not found in archive");
return read_all_text(file_index);
}
static size_t read(void* pOpaque, mz_uint64 file_ofs, void* pBuf, size_t n)
{
ZipReaderImpl* impl = static_cast<ZipReaderImpl*>(pOpaque);
if (file_ofs != impl->offset)
{
impl->file->seek(file_ofs);
impl->offset = file_ofs;
}
impl->file->read(pBuf, n);
impl->offset += n;
return n;
}
std::shared_ptr<File> file;
mz_uint64 offset = 0;
mz_zip_archive zip = {};
};
std::unique_ptr<ZipReader> ZipReader::open(std::string filename)
{
return std::make_unique<ZipReaderImpl>(std::move(filename));
}

27
src/framework/zipreader.h Normal file
View file

@ -0,0 +1,27 @@
#pragma once
#include <memory>
#include <string>
#include <vector>
#include <cstdint>
class ZipReader
{
public:
static std::unique_ptr<ZipReader> open(std::string filename);
virtual ~ZipReader() = default;
virtual int get_num_files() = 0;
virtual std::string get_filename(int file_index) = 0;
virtual uint64_t get_uncompressed_size(int file_index) = 0;
virtual uint32_t get_crc32(int file_index) = 0;
virtual std::vector<uint8_t> read_all_bytes(int file_index) = 0;
virtual std::string read_all_text(int file_index) = 0;
virtual int locate_file(const std::string& filename) = 0;
virtual bool file_exists(const std::string& filename) = 0;
virtual uint32_t get_crc32(const std::string& filename) = 0;
virtual std::vector<uint8_t> read_all_bytes(const std::string& filename) = 0;
virtual std::string read_all_text(const std::string& filename) = 0;
};

View file

@ -1,10 +1,12 @@
#pragma once
#include "framework/zdray.h"
#include "framework/tarray.h"
#include "framework/templates.h"
#include "framework/zstring.h"
#include "math/mathlib.h"
#include "framework/vectors.h"
#include "framework/textureid.h"
#include <memory>
#include <cmath>
#include <optional>
@ -13,6 +15,9 @@
#undef min
#undef max
struct FLevel;
struct DoomLevelMeshSurface;
enum
{
BOXTOP, BOXBOTTOM, BOXLEFT, BOXRIGHT
@ -73,6 +78,8 @@ struct SideDefSampleProps
};
struct IntLineDef;
struct IntSector;
class FTextureID;
struct IntSideDef
{
@ -86,13 +93,41 @@ struct IntSideDef
int sector;
int lightdef;
IntSector* sectordef;
IntLineDef *line;
SideDefSampleProps sampling;
TArray<UDMFKey> props;
FTextureID GetTexture(WallPart part)
{
const char* names[3] = { toptexture, midtexture, bottomtexture };
return TexMan.CheckForTexture(names[(int)part], ETextureType::Wall);
}
float GetTextureXOffset(WallPart part) { return 0.0f; }
float GetTextureXScale(WallPart part) { return 1.0f; }
float GetTextureYOffset(WallPart part) { return 0.0f; }
float GetTextureYScale(WallPart part) { return 1.0f; }
inline int GetSampleDistance(WallPart part) const;
inline int GetSectorGroup() const;
int Index(const FLevel& level) const;
FVector2 V1(const FLevel& level) const;
FVector2 V2(const FLevel& level) const;
float GetTexelLength(const FLevel& level) const
{
FVector2 v1 = V1(level);
FVector2 v2 = V2(level);
double dx = v2.X - v1.X;
double dy = v2.Y - v1.Y;
int len = (int)(g_sqrt(dx * dx + dy * dy) + 0.5f);
return (float)len;
}
};
struct MapLineDef
@ -129,11 +164,14 @@ struct IntLineDef
TArray<UDMFKey> props;
TArray<int> ids;
IntSideDef* sidedef[2] = { nullptr, nullptr };
IntSector *frontsector = nullptr, *backsector = nullptr;
SideDefSampleProps sampling;
inline int GetSectorGroup() const;
int Index(const FLevel& level) const;
};
struct MapSector
@ -153,6 +191,12 @@ enum SecPlaneType
PLANE_CEILING,
};
struct X3DFloor
{
IntSector* Sector = nullptr;
IntLineDef* Line = nullptr;
};
struct IntSector
{
// none of the sector properties are used by the node builder
@ -165,6 +209,9 @@ struct IntSector
Plane ceilingplane;
Plane floorplane;
double floorTexZ;
double ceilingTexZ;
int sampleDistanceCeiling;
int sampleDistanceFloor;
@ -172,7 +219,9 @@ struct IntSector
int ceilinglightdef;
bool controlsector;
TArray<IntSector*> x3dfloors;
TArray<X3DFloor> x3dfloors;
bool HasLightmaps = false;
union
{
@ -187,6 +236,11 @@ struct IntSector
int group = 0;
FTextureID GetTexture(SecPlaneType plane)
{
return TexMan.CheckForTexture(GetTextureName(plane), ETextureType::Flat);
}
// Utility functions
inline const char* GetTextureName(int plane) const { return plane != PLANE_FLOOR ? data.ceilingpic : data.floorpic; }
@ -200,6 +254,8 @@ struct IntSector
}
return false;
}
int Index(const FLevel& level) const;
};
inline int IntLineDef::GetSectorGroup() const
@ -224,10 +280,16 @@ struct MapSubsector
uint16_t firstline;
};
struct MapSegGLEx;
struct IntSector;
struct MapSubsectorEx
{
uint32_t numlines;
uint32_t firstline;
MapSegGLEx* GetFirstLine(const FLevel& level) const;
IntSector* GetSector(const FLevel& level) const;
};
struct MapSeg
@ -356,30 +418,39 @@ struct FloatVertex
float y;
};
#define THING_POINTLIGHT_STATIC 9876
#define THING_SPOTLIGHT_STATIC 9881
#define THING_ZDRAYINFO 9890
#define THING_POINTLIGHT_LM 9876
#define THING_SPOTLIGHT_LM 9881
#define THING_ZDRAYINFO 9890
struct ThingLight
{
IntThing *mapThing;
vec2 origin;
vec3 rgb;
FVector2 origin;
FVector3 rgb;
float intensity;
float innerAngleCos;
float outerAngleCos;
float height;
float radius;
float softShadowRadius;
bool bCeiling;
IntSector *sector;
MapSubsectorEx *ssect;
// Locations in the level mesh light list. Ends with index = 0 or all entries used
enum { max_levelmesh_entries = 4 };
struct
{
int index = 0;
int portalgroup = 0;
} levelmesh[max_levelmesh_entries];
// Portal related functionality
std::optional<vec3> relativePosition;
std::optional<FVector3> relativePosition;
int sectorGroup = 0;
// Portal aware position
vec3 LightRelativeOrigin() const
FVector3 LightRelativeOrigin() const
{
if (relativePosition)
{
@ -389,14 +460,14 @@ struct ThingLight
}
// Absolute X, Y, Z position of the light
vec3 LightOrigin() const
FVector3 LightOrigin() const
{
float originZ;
if (!bCeiling)
originZ = sector->floorplane.zAt(origin.x, origin.y) + height;
originZ = sector->floorplane.ZatPoint(origin.X, origin.Y) + height;
else
originZ = sector->ceilingplane.zAt(origin.x, origin.y) - height;
return vec3(origin.x, origin.y, originZ);
originZ = sector->ceilingplane.ZatPoint(origin.X, origin.Y) - height;
return FVector3(origin.X, origin.Y, originZ);
}
float LightRadius() const
@ -404,39 +475,39 @@ struct ThingLight
return radius + radius; // 2.0 because gzdoom's dynlights do this and we want them to match
}
float SpotAttenuation(const vec3& dir) const
float SpotAttenuation(const FVector3& dir) const
{
float spotAttenuation = 1.0f;
if (outerAngleCos > -1.0f)
{
float negPitch = -radians(mapThing->pitch);
float xyLen = std::cos(negPitch);
vec3 spotDir;
spotDir.x = -std::cos(radians(mapThing->angle)) * xyLen;
spotDir.y = -std::sin(radians(mapThing->angle)) * xyLen;
spotDir.z = -std::sin(negPitch);
float cosDir = dot(dir, spotDir);
FVector3 spotDir;
spotDir.X = -std::cos(radians(mapThing->angle)) * xyLen;
spotDir.Y = -std::sin(radians(mapThing->angle)) * xyLen;
spotDir.Z = -std::sin(negPitch);
float cosDir = (dir | spotDir);
spotAttenuation = smoothstep(outerAngleCos, innerAngleCos, cosDir);
spotAttenuation = std::max(spotAttenuation, 0.0f);
}
return spotAttenuation;
}
vec3 SpotDir() const
FVector3 SpotDir() const
{
if (outerAngleCos > -1.0f)
{
float negPitch = -radians(mapThing->pitch);
float xyLen = std::cos(negPitch);
vec3 spotDir;
spotDir.x = -std::cos(radians(mapThing->angle)) * xyLen;
spotDir.y = -std::sin(radians(mapThing->angle)) * xyLen;
spotDir.z = -std::sin(negPitch);
FVector3 spotDir;
spotDir.X = -std::cos(radians(mapThing->angle)) * xyLen;
spotDir.Y = -std::sin(radians(mapThing->angle)) * xyLen;
spotDir.Z = -std::sin(negPitch);
return spotDir;
}
else
{
return vec3(0.0f);
return FVector3(0.0f, 0.0f, 0.0f);
}
}
@ -448,11 +519,57 @@ struct ThingLight
enum mapFlags_t
{
ML_BLOCKING = 1, // Solid, is an obstacle.
ML_BLOCKMONSTERS = 2, // Blocks monsters only.
ML_TWOSIDED = 4, // Backside will not be present at all if not two sided.
ML_TRANSPARENT1 = 2048, // 25% or 75% transcluency?
ML_TRANSPARENT2 = 4096 // 25% or 75% transcluency?
ML_TRANSPARENT2 = 4096, // 25% or 75% transcluency?
ML_BLOCKING = 0x00000001, // solid, is an obstacle
ML_BLOCKMONSTERS = 0x00000002, // blocks monsters only
ML_TWOSIDED = 0x00000004, // backside will not be present at all if not two sided
// If a texture is pegged, the texture will have
// the end exposed to air held constant at the
// top or bottom of the texture (stairs or pulled
// down things) and will move with a height change
// of one of the neighbor sectors.
// Unpegged textures always have the first row of
// the texture at the top pixel of the line for both
// top and bottom textures (use next to windows).
ML_DONTPEGTOP = 0x00000008, // upper texture unpegged
ML_DONTPEGBOTTOM = 0x00000010, // lower texture unpegged
ML_SECRET = 0x00000020, // don't map as two sided: IT'S A SECRET!
ML_SOUNDBLOCK = 0x00000040, // don't let sound cross two of these
ML_DONTDRAW = 0x00000080, // don't draw on the automap
ML_MAPPED = 0x00000100, // set if already drawn in automap
ML_REPEAT_SPECIAL = 0x00000200, // special is repeatable
// 0x400, 0x800 and 0x1000 are ML_SPAC_MASK, they can be used for internal things but not for real map flags.
ML_ADDTRANS = 0x00000400, // additive translucency (can only be set internally)
ML_COMPATSIDE = 0x00000800, // for compatible PointOnLineSide checks. Using the global compatibility check would be a bit expensive for this check.
// Extended flags
ML_NOSKYWALLS = 0x00001000, // Don't draw sky above or below walls
ML_MONSTERSCANACTIVATE = 0x00002000, // [RH] Monsters (as well as players) can activate the line
ML_BLOCK_PLAYERS = 0x00004000,
ML_BLOCKEVERYTHING = 0x00008000, // [RH] Line blocks everything
ML_ZONEBOUNDARY = 0x00010000,
ML_RAILING = 0x00020000,
ML_BLOCK_FLOATERS = 0x00040000,
ML_CLIP_MIDTEX = 0x00080000, // Automatic for every Strife line
ML_WRAP_MIDTEX = 0x00100000,
ML_3DMIDTEX = 0x00200000,
ML_CHECKSWITCHRANGE = 0x00400000,
ML_FIRSTSIDEONLY = 0x00800000, // activated only when crossed from front side
ML_BLOCKPROJECTILE = 0x01000000,
ML_BLOCKUSE = 0x02000000, // blocks all use actions through this line
ML_BLOCKSIGHT = 0x04000000, // blocks monster line of sight
ML_BLOCKHITSCAN = 0x08000000, // blocks hitscan attacks
ML_3DMIDTEX_IMPASS = 0x10000000, // [TP] if 3D midtex, behaves like a height-restricted ML_BLOCKING
ML_REVEALED = 0x20000000, // set if revealed in automap
ML_DRAWFULLHEIGHT = 0x40000000, // Draw the full height of the upper/lower sections
ML_PORTALCONNECT = 0x80000000, // for internal use only: This line connects to a sector with a linked portal (used to speed up sight checks.)
// Flag words may not exceed 32 bit due to VM limitations.
ML2_BLOCKLANDMONSTERS = 0x1, // MBF21
};
#define NO_SIDE_INDEX -1
@ -491,8 +608,8 @@ struct FLevel
TArray<ThingLight> ThingLights;
vec3 defaultSunColor;
vec3 defaultSunDirection;
FVector3 defaultSunColor;
FVector3 defaultSunDirection;
int DefaultSamples;
void FindMapBounds ();
@ -509,18 +626,18 @@ struct FLevel
int NumSectors() const { return Sectors.Size(); }
int NumThings() const { return Things.Size(); }
const vec3 &GetSunColor() const;
const vec3 &GetSunDirection() const;
IntSector *GetFrontSector(const IntSideDef *side);
IntSector *GetBackSector(const IntSideDef *side);
IntSector *GetSectorFromSubSector(const MapSubsectorEx *sub);
const FVector3 &GetSunColor() const;
const FVector3 &GetSunDirection() const;
IntSector* GetFrontSector(const IntSideDef* side) const;
IntSector* GetBackSector(const IntSideDef* side) const;
IntSector* GetSectorFromSubSector(const MapSubsectorEx* sub) const;
MapSubsectorEx *PointInSubSector(const int x, const int y);
FloatVertex GetSegVertex(unsigned int index);
FloatVertex GetSegVertex(unsigned int index) const;
int FindFirstSectorFromTag(int tag);
unsigned FindFirstLineId(int lineId);
inline IntSector* PointInSector(const dvec2& pos) { return GetSectorFromSubSector(PointInSubSector(int(pos.x), int(pos.y))); }
inline IntSector* PointInSector(const DVector2& pos) { return GetSectorFromSubSector(PointInSubSector(int(pos.X), int(pos.Y))); }
private:
void CheckSkySectors();
void CreateLights();
@ -530,3 +647,13 @@ const int BLOCKSIZE = 128;
const int BLOCKFRACSIZE = BLOCKSIZE<<FRACBITS;
const int BLOCKBITS = 7;
const int BLOCKFRACBITS = FRACBITS+7;
inline int IntSector::Index(const FLevel& level) const { return (int)(ptrdiff_t)(this - level.Sectors.Data()); }
inline int IntSideDef::Index(const FLevel& level) const { return (int)(ptrdiff_t)(this - level.Sides.Data()); }
inline int IntLineDef::Index(const FLevel& level) const { return (int)(ptrdiff_t)(this - level.Lines.Data()); }
inline MapSegGLEx* MapSubsectorEx::GetFirstLine(const FLevel& level) const { return firstline != NO_INDEX ? &level.GLSegs[firstline] : nullptr; }
inline IntSector* MapSubsectorEx::GetSector(const FLevel& level) const { return level.GetSectorFromSubSector(this); }
inline FVector2 IntSideDef::V1(const FLevel& level) const { auto v = level.GetSegVertex(sectordef == line->frontsector ? line->v1 : line->v2); return FVector2(v.x, v.y); }
inline FVector2 IntSideDef::V2(const FLevel& level) const { auto v = level.GetSegVertex(sectordef == line->frontsector ? line->v2 : line->v1); return FVector2(v.x, v.y); }

View file

@ -19,8 +19,7 @@
*/
#include "level/level.h"
#include "lightmap/gpuraytracer.h"
#include "math/vec.h"
#include "lightmapper/gpuraytracer.h"
//#include "rejectbuilder.h"
#include <memory>
@ -625,7 +624,10 @@ void FLevel::PostLoadInitialization()
Sectors[i].controlsector = false;
for (unsigned int i = 0; i < Sides.Size(); i++)
{
Sides[i].line = nullptr;
Sides[i].sectordef = &Sectors[Sides[i].sector];
}
for (unsigned int i = 0; i < Lines.Size(); i++)
{
@ -654,7 +656,7 @@ void FLevel::PostLoadInitialization()
{
if (Sectors[j].tags[t] == sectorTag)
{
Sectors[j].x3dfloors.Push(controlsector);
Sectors[j].x3dfloors.Push({ controlsector, line });
break;
}
}
@ -671,8 +673,11 @@ void FLevel::PostLoadInitialization()
sector.lines.Push(line);
line->frontsector = (line->sidenum[0] != NO_INDEX) ? &Sectors[Sides[line->sidenum[0]].sector] : nullptr;
line->backsector = (line->sidenum[1] != NO_INDEX) ? &Sectors[Sides[line->sidenum[1]].sector] : nullptr;
line->sidedef[0] = (line->sidenum[0] != NO_INDEX) ? &Sides[line->sidenum[0]] : nullptr;
line->sidedef[1] = (line->sidenum[1] != NO_INDEX) ? &Sides[line->sidenum[1]] : nullptr;
line->frontsector = line->sidedef[0] ? &Sectors[line->sidedef[0]->sector] : nullptr;
line->backsector = line->sidedef[1] ? &Sectors[line->sidedef[1]->sector] : nullptr;
}
// Find plane portals
@ -834,19 +839,23 @@ void FProcessor::BuildLightmaps()
Level.SetupLights();
LightmapMesh = std::make_unique<LevelMesh>(Level, Level.DefaultSamples, LMDims);
printf(" Creating level mesh\n");
LightmapMesh = std::make_unique<DoomLevelMesh>(Level);
LightmapMesh->SetupTileTransforms();
LightmapMesh->PackLightmapAtlas(0);
LightmapMesh->BeginFrame(Level);
printf(" Surfaces: %d\n", LightmapMesh->GetSurfaceCount());
printf(" Tiles: %d\n", (int)LightmapMesh->LightmapTiles.Size());
std::unique_ptr<GPURaytracer> gpuraytracer = std::make_unique<GPURaytracer>();
gpuraytracer->Raytrace(LightmapMesh.get());
LightmapMesh->CreateTextures();
}
void FProcessor::DumpMesh()
{
if (LightmapMesh)
{
LightmapMesh->Export("levelmesh.obj");
LightmapMesh->DumpMesh("levelmesh.obj", "levelmesh.mtl");
}
else
{

View file

@ -7,7 +7,7 @@
#include "framework/tarray.h"
#include "nodebuilder/nodebuild.h"
#include "blockmapbuilder/blockmapbuilder.h"
#include "lightmap/levelmesh.h"
#include "lightmapper/doom_levelmesh.h"
#include <miniz/miniz.h>
#define DEFINE_SPECIAL(name, num, min, max, map) name = num,
@ -77,7 +77,7 @@ private:
void SetSlopesFromVertexHeights(IntThing* firstmt, IntThing* lastmt, const int* oldvertextable);
void SpawnSlopeMakers(IntThing* firstmt, IntThing* lastmt, const int* oldvertextable);
void CopyPlane(int tag, IntSector* dest, bool copyCeil);
void CopyPlane(int tag, const dvec2& pos, bool copyCeil);
void CopyPlane(int tag, const DVector2& pos, bool copyCeil);
MapNodeEx *NodesToEx(const MapNode *nodes, int count);
@ -158,5 +158,5 @@ private:
int Lump;
bool NodesBuilt = false;
std::unique_ptr<LevelMesh> LightmapMesh;
std::unique_ptr<DoomLevelMesh> LightmapMesh;
};

View file

@ -25,7 +25,7 @@
// distribution.
//
#include "math/mathlib.h"
#include "framework/vectors.h"
#include "level/level.h"
#include <algorithm>
#include <memory>
@ -38,11 +38,28 @@
// convert from fixed point(FRACUNIT) to floating point
#define F(x) (((float)(x))/65536.0f)
static int RoundPowerOfTwo(int x)
{
int mask = 1;
while (mask < 0x40000000)
{
if (x == mask || (x & (mask - 1)) == x)
{
return mask;
}
mask <<= 1;
}
return x;
}
void FLevel::SetupLights()
{
// GG to whoever memset'ed FLevel
defaultSunColor = vec3(1, 1, 1);
defaultSunDirection = vec3(0.45f, 0.3f, 0.9f);
defaultSunColor = FVector3(1, 1, 1);
defaultSunDirection = FVector3(0.45f, 0.3f, 0.9f);
DefaultSamples = 16;
for (int i = 0; i < (int)Things.Size(); ++i)
@ -51,18 +68,18 @@ void FLevel::SetupLights()
if (thing->type == THING_ZDRAYINFO)
{
uint32_t lightcolor = 0xffffff;
vec3 sundir(0.0f, 0.0f, 0.0f);
vec3 suncolor(1.0f, 1.0f, 1.0f);
FVector3 sundir(0.0f, 0.0f, 0.0f);
FVector3 suncolor(1.0f, 1.0f, 1.0f);
// to do: is the math here correct?
float sdx = (float)std::cos(radians(thing->angle)) * (float)std::cos(radians(thing->pitch));
float sdy = (float)std::sin(radians(thing->angle)) * (float)std::cos(radians(thing->pitch));
float sdz = (float)-std::sin(radians(thing->pitch));
sundir.x = -sdx;
sundir.y = -sdy;
sundir.z = -sdz;
sundir.X = -sdx;
sundir.Y = -sdy;
sundir.Z = -sdz;
printf(" Sun vector: %f, %f, %f\n", sundir.x, sundir.y, sundir.z);
printf(" Sun vector: %f, %f, %f\n", sundir.X, sundir.Y, sundir.Z);
for (unsigned int propIndex = 0; propIndex < thing->props.Size(); propIndex++)
{
@ -73,21 +90,21 @@ void FLevel::SetupLights()
lightcolor = atoi(key.value);
printf(" Sun color: %d (%X)\n", lightcolor, lightcolor);
}
else if (!stricmp(key.key, "lm_sampledistance"))
else if (!stricmp(key.key, "lm_sampledist"))
{
DefaultSamples = atoi(key.value);
if (DefaultSamples < 8) DefaultSamples = 8;
if (DefaultSamples > 128) DefaultSamples = 128;
DefaultSamples = Math::RoundPowerOfTwo(DefaultSamples);
DefaultSamples = RoundPowerOfTwo(DefaultSamples);
}
}
if (dot(sundir, sundir) > 0.01f)
if ((sundir | sundir) > 0.01f)
{
sundir = normalize(sundir);
suncolor.x = ((lightcolor >> 16) & 0xff) / 255.0f;
suncolor.y = ((lightcolor >> 8) & 0xff) / 255.0f;
suncolor.z = (lightcolor & 0xff) / 255.0f;
sundir.MakeUnit();
suncolor.X = ((lightcolor >> 16) & 0xff) / 255.0f;
suncolor.Y = ((lightcolor >> 8) & 0xff) / 255.0f;
suncolor.Z = (lightcolor & 0xff) / 255.0f;
defaultSunColor = suncolor;
defaultSunDirection = sundir;
@ -119,22 +136,22 @@ void FLevel::CheckSkySectors()
}
}
const vec3 &FLevel::GetSunColor() const
const FVector3 &FLevel::GetSunColor() const
{
return defaultSunColor;
}
const vec3 &FLevel::GetSunDirection() const
const FVector3 &FLevel::GetSunDirection() const
{
return defaultSunDirection;
}
IntSector *FLevel::GetFrontSector(const IntSideDef *side)
IntSector *FLevel::GetFrontSector(const IntSideDef *side) const
{
return &Sectors[side->sector];
}
IntSector *FLevel::GetBackSector(const IntSideDef *side)
IntSector *FLevel::GetBackSector(const IntSideDef *side) const
{
IntLineDef *line = side->line;
if (!(line->flags & ML_TWOSIDED))
@ -152,7 +169,7 @@ IntSector *FLevel::GetBackSector(const IntSideDef *side)
return GetFrontSector(&Sides[sidenum]);
}
IntSector *FLevel::GetSectorFromSubSector(const MapSubsectorEx *sub)
IntSector *FLevel::GetSectorFromSubSector(const MapSubsectorEx *sub) const
{
for (int i = 0; i < (int)sub->numlines; i++)
{
@ -171,8 +188,8 @@ MapSubsectorEx *FLevel::PointInSubSector(const int x, const int y)
MapNodeEx *node;
int side;
int nodenum;
vec3 dp1;
vec3 dp2;
FVector3 dp1;
FVector3 dp2;
float d;
// single subsector is a special case
@ -187,16 +204,17 @@ MapSubsectorEx *FLevel::PointInSubSector(const int x, const int y)
{
node = &GLNodes[nodenum];
vec3 pt1(F(node->x), F(node->y), 0);
vec3 pt2(F(node->dx), F(node->dy), 0);
//vec3 pt1(F(node->x << 16), F(node->y << 16), 0);
//vec3 pt2(F(node->dx << 16), F(node->dy << 16), 0);
vec3 pos(F(x << 16), F(y << 16), 0);
FVector3 pt1(F(node->x), F(node->y), 0);
FVector3 pt2(F(node->dx), F(node->dy), 0);
//FVector3 pt1(F(node->x << 16), F(node->y << 16), 0);
//FVector3 pt2(F(node->dx << 16), F(node->dy << 16), 0);
FVector3 pos(F(x << 16), F(y << 16), 0);
dp1 = pt1 - pos;
dp2 = (pt2 + pt1) - pos;
d = cross(dp1, dp2).z;
d = (dp1 ^ dp2).Z;
#define FLOATSIGNBIT(f) (reinterpret_cast<const unsigned int&>(f) >> 31)
side = FLOATSIGNBIT(d);
nodenum = node->children[side ^ 1];
@ -205,7 +223,7 @@ MapSubsectorEx *FLevel::PointInSubSector(const int x, const int y)
return &GLSubsectors[nodenum & ~NFX_SUBSECTOR];
}
FloatVertex FLevel::GetSegVertex(unsigned int index)
FloatVertex FLevel::GetSegVertex(unsigned int index) const
{
FloatVertex v;
v.x = F(GLVertices[index].x);
@ -221,37 +239,39 @@ void FLevel::CreateLights()
IntThing *thing = &Things[i];
// skip things that aren't actually static point lights or static spotlights
if (thing->type != THING_POINTLIGHT_STATIC && thing->type != THING_SPOTLIGHT_STATIC)
if (thing->type != THING_POINTLIGHT_LM && thing->type != THING_SPOTLIGHT_LM)
continue;
vec3 lightColor(0, 0, 0);
FVector3 lightColor(0, 0, 0);
float lightIntensity = 1.0f;
float lightDistance = 0.0f;
float innerAngleCos = -1.0f;
float outerAngleCos = -1.0f;
float softshadowradius = 5.0f;
// need to process point lights and spot lights differently due to their
// inconsistent arg usage...
if (thing->type == THING_POINTLIGHT_STATIC)
if (thing->type == THING_POINTLIGHT_LM)
{
int r = thing->args[0];
int g = thing->args[1];
int b = thing->args[2];
lightColor = vec3(r / 255.0, g / 255.0, b / 255.0);
lightColor = FVector3(r / 255.0, g / 255.0, b / 255.0);
}
else if (thing->type == THING_SPOTLIGHT_STATIC)
else if (thing->type == THING_SPOTLIGHT_LM)
{
auto rgb = (uint32_t)thing->args[0];
// UDB's color picker will assign the color as a hex string, stored
// in the arg0str field. detect this, so that it can be converted into an int
// this behavior is unique to spotlights.
if (thing->arg0str.Len() > 0)
{
FString hex = "0x" + thing->arg0str;
rgb = (uint32_t)hex.ToULong();
}
lightColor = vec3(
lightColor = FVector3(
((rgb >> 16) & 0xFF) / 255.0,
((rgb >> 8) & 0xFF) / 255.0,
((rgb) & 0xFF) / 255.0
@ -261,13 +281,21 @@ void FLevel::CreateLights()
outerAngleCos = std::cos((float)thing->args[2] * 3.14159265359f / 180.0f);
}
// this is known as "intensity" on dynamic lights (and in UDB)
for (const auto& prop : thing->props)
{
if (!stricmp(prop.key, "light_softshadowradius"))
{
softshadowradius = atof(prop.value);
}
}
// this is known as "intensity" on dynamic lights (and in UDB) - what it actually is though, is the radius
lightDistance = thing->args[3];
// static light intensity (not to be confused with dynamic lights' intensity, which is actually static light distance
// light intensity is how bright or dark a light is. can go below or above 1.0.
lightIntensity = thing->alpha;
if (lightDistance > 0.0f && lightIntensity > 0.0f && lightColor != vec3(0, 0, 0))
if (lightDistance > 0.0f && lightIntensity > 0.0f && lightColor != FVector3(0, 0, 0))
{
int x = thing->x >> FRACBITS;
int y = thing->y >> FRACBITS;
@ -283,9 +311,10 @@ void FLevel::CreateLights()
thingLight.bCeiling = false;
thingLight.ssect = PointInSubSector(x, y);
thingLight.sector = GetSectorFromSubSector(thingLight.ssect);
thingLight.origin.x = x;
thingLight.origin.y = y;
thingLight.origin.X = x;
thingLight.origin.Y = y;
thingLight.sectorGroup = thingLight.sector->group;
thingLight.softShadowRadius = softshadowradius;
ThingLights.Push(thingLight);
}

View file

@ -41,14 +41,14 @@
// convert from fixed point(FRACUNIT) to floating point
#define F(x) (((float)(x))/65536.0f)
inline int P_PointOnLineSidePrecise(const vec2& p, const vec2& v1, const vec2& v2)
inline int P_PointOnLineSidePrecise(const FVector2& p, const FVector2& v1, const FVector2& v2)
{
return (p.y - v1.y) * (v2.x - v1.x) + (v1.x - p.x) * (v2.y - v1.y) > EQUAL_EPSILON;
return (p.Y - v1.Y) * (v2.X - v1.X) + (v1.X - p.X) * (v2.Y - v1.Y) > EQUAL_EPSILON;
}
inline int P_PointOnLineSidePrecise(const vec2& p, const FloatVertex& v1, const FloatVertex& v2)
inline int P_PointOnLineSidePrecise(const FVector2& p, const FloatVertex& v1, const FloatVertex& v2)
{
return P_PointOnLineSidePrecise(p, vec2(v1.x, v1.y), vec2(v2.x, v2.y));
return P_PointOnLineSidePrecise(p, FVector2(v1.x, v1.y), FVector2(v2.x, v2.y));
}
enum
@ -122,8 +122,8 @@ void FProcessor::SetSlopesFromVertexHeights(IntThing* firstmt, IntThing* lastmt,
{
if (sec.lines.Size() != 3) continue; // only works with triangular sectors
dvec3 vt1, vt2, vt3;
dvec3 vec1, vec2;
DVector3 vt1, vt2, vt3;
DVector3 vec1, vec2;
int vi1, vi2, vi3;
vi1 = sec.lines[0]->v1;
@ -135,9 +135,9 @@ void FProcessor::SetSlopesFromVertexHeights(IntThing* firstmt, IntThing* lastmt,
const auto sv2 = Level.GetSegVertex(vi2);
const auto sv3 = Level.GetSegVertex(vi3);
vt1 = dvec3(sv1.x, sv1.y, 0);
vt2 = dvec3(sv2.x, sv2.y, 0);
vt3 = dvec3(sv3.x, sv3.y, 0);
vt1 = DVector3(sv1.x, sv1.y, 0);
vt2 = DVector3(sv2.x, sv2.y, 0);
vt3 = DVector3(sv3.x, sv3.y, 0);
for (int j = 0; j < 2; j++)
{
@ -147,11 +147,11 @@ void FProcessor::SetSlopesFromVertexHeights(IntThing* firstmt, IntThing* lastmt,
if (h1 == NULL && h2 == NULL && h3 == NULL) continue;
vt1.z = h1 ? *h1 : j == 0 ? sec.data.floorheight : sec.data.ceilingheight;
vt2.z = h2 ? *h2 : j == 0 ? sec.data.floorheight : sec.data.ceilingheight;
vt3.z = h3 ? *h3 : j == 0 ? sec.data.floorheight : sec.data.ceilingheight;
vt1.Z = h1 ? *h1 : j == 0 ? sec.data.floorheight : sec.data.ceilingheight;
vt2.Z = h2 ? *h2 : j == 0 ? sec.data.floorheight : sec.data.ceilingheight;
vt3.Z = h3 ? *h3 : j == 0 ? sec.data.floorheight : sec.data.ceilingheight;
if (P_PointOnLineSidePrecise(::vec2(vt3.xy()), Level.GetSegVertex(sec.lines[0]->v1), Level.GetSegVertex(sec.lines[0]->v2)) == 0)
if (P_PointOnLineSidePrecise(FVector2(vt3.XY()), Level.GetSegVertex(sec.lines[0]->v1), Level.GetSegVertex(sec.lines[0]->v2)) == 0)
{
vec1 = vt2 - vt3;
vec2 = vt1 - vt3;
@ -162,9 +162,9 @@ void FProcessor::SetSlopesFromVertexHeights(IntThing* firstmt, IntThing* lastmt,
vec2 = vt2 - vt3;
}
dvec3 cross = ::cross(vec1, vec2);
DVector3 cross = (vec1 ^ vec2);
double len = length(cross);
double len = cross.Length();
if (len == 0)
{
// Only happens when all vertices in this sector are on the same line.
@ -174,14 +174,14 @@ void FProcessor::SetSlopesFromVertexHeights(IntThing* firstmt, IntThing* lastmt,
cross /= len;
// Fix backward normals
if ((cross.z < 0 && j == 0) || (cross.z > 0 && j == 1))
if ((cross.Z < 0 && j == 0) || (cross.Z > 0 && j == 1))
{
cross = -cross;
}
Plane* plane = j == 0 ? &sec.floorplane : &sec.ceilingplane;
double dist = -cross[0] * vt3.x - cross[1] * vt3.y - cross[2] * vt3.z;
double dist = -cross[0] * vt3.X - cross[1] * vt3.Y - cross[2] * vt3.Z;
plane->Set(float(cross[0]), float(cross[1]), float(cross[2]), float(-dist));
}
}
@ -196,12 +196,12 @@ void FProcessor::SpawnSlopeMakers(IntThing* firstmt, IntThing* lastmt, const int
{
if (mt->type >= SMT_SlopeFloorPointLine && mt->type <= SMT_VavoomCeiling)
{
dvec3 pos = dvec3(F(mt->x), F(mt->y), F(mt->z));
DVector3 pos = DVector3(F(mt->x), F(mt->y), F(mt->z));
Plane* refplane;
IntSector* sec;
bool ceiling;
sec = Level.PointInSector(pos.xy());
sec = Level.PointInSector(pos.XY());
if (mt->type == SMT_SlopeCeilingPointLine || mt->type == SMT_VavoomCeiling || mt->type == SMT_SetCeilingSlope)
{
refplane = &sec->ceilingplane;
@ -213,7 +213,7 @@ void FProcessor::SpawnSlopeMakers(IntThing* firstmt, IntThing* lastmt, const int
ceiling = false;
}
pos.z = double(refplane->zAt(float(pos.x), float(pos.y))) + pos.z;
pos.Z = double(refplane->ZatPoint(pos)) + pos.Z;
/*if (mt->type <= SMT_SlopeCeilingPointLine)
{ // SlopeFloorPointLine and SlopCeilingPointLine
@ -236,7 +236,7 @@ void FProcessor::SpawnSlopeMakers(IntThing* firstmt, IntThing* lastmt, const int
{
if (mt->type == SMT_CopyFloorPlane || mt->type == SMT_CopyCeilingPlane)
{
CopyPlane(mt->args[0], dvec2(F(mt->x), F(mt->y)), mt->type == SMT_CopyCeilingPlane);
CopyPlane(mt->args[0], DVector2(F(mt->x), F(mt->y)), mt->type == SMT_CopyCeilingPlane);
mt->type = 0;
}
}
@ -280,7 +280,7 @@ void FProcessor::AlignPlane(IntSector* sec, IntLineDef* line, int which)
refsec = line->frontsector == sec ? line->backsector : line->frontsector;
dvec3 p, v1, v2, cross;
DVector3 p, v1, v2, cross;
Plane* srcplane;
double srcheight, destheight;
@ -299,10 +299,10 @@ void FProcessor::AlignPlane(IntSector* sec, IntLineDef* line, int which)
v2[1] = double(refvert.y) - double(lv1.y);
v2[2] = srcheight - destheight;
cross = normalize(::cross(v1, v2));
cross = (v1 ^ v2).Unit();
// Fix backward normals
if ((cross.z < 0 && which == 0) || (cross.z > 0 && which == 1))
if ((cross.Z < 0 && which == 0) || (cross.Z > 0 && which == 1))
{
cross = -cross;
}
@ -408,7 +408,7 @@ void FProcessor::CopyPlane(int tag, IntSector* dest, bool copyCeil)
}
}
void FProcessor::CopyPlane(int tag, const dvec2& pos, bool copyCeil)
void FProcessor::CopyPlane(int tag, const DVector2& pos, bool copyCeil)
{
CopyPlane(tag, Level.PointInSector(pos), copyCeil);
}

View file

@ -23,9 +23,6 @@
#include "level/level.h"
#include "parse/sc_man.h"
typedef double real64;
typedef unsigned int uint32;
typedef signed int int32;
#include "framework/xs_Float.h"
#ifdef _MSC_VER
@ -325,7 +322,7 @@ void FProcessor::ParseLinedef(IntLineDef *ld)
ld->ids.Clear();
if (id != -1) ld->ids.Push(id);
}
else if (stricmp(key, "lm_sampledist_line") == 0)
else if (stricmp(key, "lm_sampledist") == 0)
{
ld->sampling.SetGeneralSampleDistance(CheckInt(key));
}
@ -415,7 +412,7 @@ void FProcessor::ParseSidedef(IntSideDef *sd)
{
sd->rowoffset = CheckInt(key);
}
else if (stricmp(key, "lm_sampledist_line") == 0)
else if (stricmp(key, "lm_sampledist") == 0)
{
sd->sampling.SetGeneralSampleDistance(CheckInt(key));
}
@ -453,6 +450,8 @@ void FProcessor::ParseSector(IntSector *sec)
sec->sampleDistanceFloor = 0;
int ceilingplane = 0, floorplane = 0;
bool floorTexZSet = false;
bool ceilingTexZSet = false;
SC_MustGetStringName("{");
while (!SC_CheckString("}"))
@ -460,6 +459,16 @@ void FProcessor::ParseSector(IntSector *sec)
const char *value;
const char *key = ParseKey(value);
if (stricmp(key, "heightfloor") == 0)
{
sec->floorTexZ = CheckFloat(key);
floorTexZSet = true;
}
else if (stricmp(key, "heightceiling") == 0)
{
sec->ceilingTexZ = CheckFloat(key);
ceilingTexZSet = true;
}
if (stricmp(key, "textureceiling") == 0)
{
CopyUDMFString(sec->data.ceilingpic, 64, value);
@ -471,10 +480,14 @@ void FProcessor::ParseSector(IntSector *sec)
else if (stricmp(key, "heightceiling") == 0)
{
sec->data.ceilingheight = CheckFloat(key);
if (!ceilingTexZSet)
sec->ceilingTexZ = sec->data.ceilingheight;
}
else if (stricmp(key, "heightfloor") == 0)
{
sec->data.floorheight = CheckFloat(key);
if (!floorTexZSet)
sec->floorTexZ = sec->data.floorheight;
}
else if (stricmp(key, "lightlevel") == 0)
{
@ -574,7 +587,7 @@ void FProcessor::ParseSector(IntSector *sec)
}
else
{
float scale = 1.0f / length(sec->ceilingplane.Normal());
float scale = 1.0f / sec->ceilingplane.Normal().Length();
sec->ceilingplane.a *= scale;
sec->ceilingplane.b *= scale;
sec->ceilingplane.c *= scale;
@ -591,7 +604,7 @@ void FProcessor::ParseSector(IntSector *sec)
}
else
{
float scale = 1.0f / length(sec->floorplane.Normal());
float scale = 1.0f / sec->floorplane.Normal().Length();
sec->floorplane.a *= scale;
sec->floorplane.b *= scale;
sec->floorplane.c *= scale;
@ -944,8 +957,7 @@ void FProcessor::WriteUDMF(FWadWriter &out)
if (LightmapMesh)
{
LightmapMesh->AddLightmapLump(out);
//LightmapMesh->Export("level.obj");
LightmapMesh->AddLightmapLump(Level, out);
}
out.CreateLabel("ENDMAP");

View file

@ -1,250 +0,0 @@
#include "delauneytriangulator.h"
void DelauneyTriangulator::triangulate()
{
std::vector<Vertex*> ordered_vertices = remove_duplicates(create_ordered_vertex_list());
Vertex super_A, super_B, super_C;
Triangle super_triangle;
super_triangle.A = &super_A;
super_triangle.B = &super_B;
super_triangle.C = &super_C;
calculate_supertriangle(ordered_vertices, super_triangle);
triangles = perform_delauney_triangulation(ordered_vertices, super_triangle);
}
std::vector<DelauneyTriangulator::Vertex*> DelauneyTriangulator::create_ordered_vertex_list()
{
if (vertices.empty())
return {};
std::vector<Vertex*> ordered_vertices;
size_t num_vertices = vertices.size();
for (size_t index_vertices = 0; index_vertices < num_vertices; index_vertices++)
{
ordered_vertices.push_back(&vertices[index_vertices]);
}
std::sort(ordered_vertices.begin(), ordered_vertices.end(), [](Vertex* a, Vertex* b) { return a->x == b->x ? a->y < b->y : a->x < b->x; });
return ordered_vertices;
}
std::vector<DelauneyTriangulator::Vertex*> DelauneyTriangulator::remove_duplicates(const std::vector<Vertex*>& ordered_vertices)
{
// Link duplicates and remove them from the ordered list:
std::vector<Vertex*> filtered_vertices;
Vertex* prev = nullptr;
for (Vertex* v : ordered_vertices)
{
v->next = nullptr; // clear for all vertices just in case triangulate is ever called twice
if (prev && prev->x == v->x && prev->y == v->y)
{
prev->next = v;
}
else
{
filtered_vertices.push_back(v);
}
prev = v;
}
return filtered_vertices;
}
void DelauneyTriangulator::calculate_supertriangle(std::vector<Vertex*>& vertices, Triangle& super_triangle)
{
// Find min and max values:
size_t num_vertices = vertices.size();
float min_x = 0.0f;
float max_x = 0.0f;
float min_y = 0.0f;
float max_y = 0.0f;
if (num_vertices > 0)
{
min_x = vertices[0]->x;
max_x = vertices[0]->x;
min_y = vertices[0]->y;
max_y = vertices[0]->y;
}
for (size_t index_vertices = 1; index_vertices < num_vertices; index_vertices++)
{
Vertex* cur_vertex = vertices[index_vertices];
min_x = std::min(min_x, cur_vertex->x);
max_x = std::max(max_x, cur_vertex->x);
min_y = std::min(min_y, cur_vertex->y);
max_y = std::max(max_y, cur_vertex->y);
}
// Setup super triangle based on min/max values:
float dx = max_x - min_x;
float dy = max_y - min_y;
float dmax = (dx > dy) ? dx : dy;
float xmid = (max_x + min_x) * 0.5f;
float ymid = (max_y + min_y) * 0.5f;
super_triangle.A->x = xmid - 20.0f * dmax;
super_triangle.A->y = ymid - dmax;
super_triangle.A->data = nullptr;
super_triangle.B->x = xmid;
super_triangle.B->y = ymid + 20.0f * dmax;
super_triangle.B->data = nullptr;
super_triangle.C->x = xmid + 20.0f * dmax;
super_triangle.C->y = ymid - dmax;
super_triangle.C->data = nullptr;
calc_cirumcenter(super_triangle);
}
std::vector<DelauneyTriangulator::Triangle> DelauneyTriangulator::perform_delauney_triangulation(const std::vector<Vertex*>& vertices, const Triangle& super_triangle)
{
std::vector<Triangle> triangles;
// add supertriangle vertices to the end of the vertex list
triangles.push_back(super_triangle);
std::vector<Triangle_Edge> edges;
// for each sample point in the vertex list:
size_t num_vertices = vertices.size();
for (size_t index_vertices = 0; index_vertices < num_vertices; index_vertices++)
{
Vertex* insertion_point = vertices[index_vertices];
edges.clear();
// For each triangle currently in the triangle list
std::vector<Triangle>::size_type index_triangles, num_triangles;
num_triangles = triangles.size();
for (index_triangles = 0; index_triangles < num_triangles; index_triangles++)
{
Triangle& cur_triangle = triangles[index_triangles];
// Check if the point lies in the triangle circumcircle:
float dist_x = insertion_point->x - cur_triangle.circumcenter_x;
float dist_y = insertion_point->y - cur_triangle.circumcenter_y;
float dist2 = dist_x * dist_x + dist_y * dist_y;
if (dist2 < cur_triangle.radius2)
{
// Add triangle edges to edge buffer:
edges.push_back(Triangle_Edge(cur_triangle.A, cur_triangle.B));
edges.push_back(Triangle_Edge(cur_triangle.B, cur_triangle.C));
edges.push_back(Triangle_Edge(cur_triangle.C, cur_triangle.A));
// Remove triange from triangle list:
triangles.erase(triangles.begin() + index_triangles);
index_triangles--;
num_triangles--;
}
}
// Delete all doubly specified edges from the edge buffer. This leaves the edges of the enclosing polygon only
int64_t index_edges1, index_edges2, num_edges; // intentionally integer to allow index to be negative when deleting index.
num_edges = (int64_t)edges.size();
for (index_edges1 = 0; index_edges1 < num_edges; index_edges1++)
{
Triangle_Edge& edge1 = edges[index_edges1];
for (index_edges2 = 0/*index_edges1+1*/; index_edges2 < num_edges; index_edges2++)
{
if (index_edges1 == index_edges2) continue;
Triangle_Edge& edge2 = edges[index_edges2];
if ((edge1.first == edge2.first && edge1.second == edge2.second) ||
(edge1.second == edge2.first && edge1.first == edge2.second))
{
// Same edges, delete both:
if (index_edges1 < index_edges2)
{
edges.erase(edges.begin() + index_edges2);
edges.erase(edges.begin() + index_edges1);
}
else
{
edges.erase(edges.begin() + index_edges1);
edges.erase(edges.begin() + index_edges2);
}
num_edges -= 2;
index_edges1--;
break;
}
}
}
// add to the triangle list all triangles formed between the point and the edges of the enclosing polygon
for (index_edges1 = 0; index_edges1 < num_edges; index_edges1++)
{
Triangle triangle;
triangle.A = edges[index_edges1].first;
triangle.B = edges[index_edges1].second;
triangle.C = insertion_point;
calc_cirumcenter(triangle);
triangles.push_back(triangle);
}
}
// remove any triangles from the triangle list that use the supertriangle vertices
size_t num_triangles = triangles.size();
for (size_t index_triangles = 0; index_triangles < num_triangles; index_triangles++)
{
Triangle& cur_triangle = triangles[index_triangles];
if (
cur_triangle.A == super_triangle.A ||
cur_triangle.A == super_triangle.B ||
cur_triangle.A == super_triangle.C ||
cur_triangle.B == super_triangle.A ||
cur_triangle.B == super_triangle.B ||
cur_triangle.B == super_triangle.C ||
cur_triangle.C == super_triangle.A ||
cur_triangle.C == super_triangle.B ||
cur_triangle.C == super_triangle.C)
{
// triangle shares one or more points with supertriangle, remove it:
triangles.erase(triangles.begin() + index_triangles);
index_triangles--;
num_triangles--;
}
}
return triangles;
}
void DelauneyTriangulator::calc_cirumcenter(Triangle& triangle)
{
float a_0 = triangle.A->x;
float a_1 = triangle.A->y;
float b_0 = triangle.B->x;
float b_1 = triangle.B->y;
float c_0 = triangle.C->x;
float c_1 = triangle.C->y;
float A = b_0 - a_0;
float B = b_1 - a_1;
float C = c_0 - a_0;
float D = c_1 - a_1;
float E = A * (a_0 + b_0) + B * (a_1 + b_1);
float F = C * (a_0 + c_0) + D * (a_1 + c_1);
float G = 2.0f * (A * (c_1 - b_1) - B * (c_0 - b_0));
float p_0 = (D * E - B * F) / G;
float p_1 = (A * F - C * E) / G;
triangle.circumcenter_x = p_0;
triangle.circumcenter_y = p_1;
float radius_x = triangle.A->x - triangle.circumcenter_x;
float radius_y = triangle.A->y - triangle.circumcenter_y;
triangle.radius2 = radius_x * radius_x + radius_y * radius_y;
}

View file

@ -1,45 +0,0 @@
#pragma once
#include <vector>
#include <algorithm>
// Bowyer-Watson delauney triangulator (http://paulbourke.net/papers/triangulate/)
class DelauneyTriangulator
{
public:
struct Vertex
{
Vertex() = default;
Vertex(float x, float y, void* data = nullptr) : x(x), y(y), data(data) { }
float x = 0.0f;
float y = 0.0f;
void* data = nullptr;
Vertex* next = nullptr;
};
struct Triangle
{
Vertex* A;
Vertex* B;
Vertex* C;
float circumcenter_x;
float circumcenter_y;
float radius2;
};
typedef std::pair<Vertex*, Vertex*> Triangle_Edge;
std::vector<Vertex> vertices;
std::vector<Triangle> triangles;
void triangulate();
private:
std::vector<Vertex*> create_ordered_vertex_list();
static std::vector<Vertex*> remove_duplicates(const std::vector<Vertex*>& ordered_vertices);
static void calculate_supertriangle(std::vector<Vertex*>& vertices, Triangle& super_triangle);
static void calc_cirumcenter(Triangle& triangle);
static std::vector<Triangle> perform_delauney_triangulation(const std::vector<Vertex*>& vertices, const Triangle& super_triangle);
};

View file

@ -1,566 +0,0 @@
static const char* glsl_frag = R"glsl(
#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;
}
)glsl";

View file

@ -1,26 +0,0 @@
static const char* glsl_vert = R"glsl(
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);
}
)glsl";

File diff suppressed because it is too large Load diff

View file

@ -1,247 +0,0 @@
#pragma once
#include <zvulkan/vulkandevice.h>
#include <zvulkan/vulkanobjects.h>
class LevelMesh;
struct Uniforms
{
vec3 SunDir;
float Padding1;
vec3 SunColor;
float SunIntensity;
};
struct PushConstants
{
uint32_t LightStart;
uint32_t LightEnd;
int32_t SurfaceIndex;
int32_t PushPadding1;
vec3 LightmapOrigin;
float PushPadding2;
vec3 LightmapStepX;
float PushPadding3;
vec3 LightmapStepY;
float PushPadding4;
};
struct SurfaceInfo
{
vec3 Normal;
float Sky;
float SamplingDistance;
uint32_t PortalIndex;
float Padding1, Padding2;
};
static_assert(sizeof(SurfaceInfo) == sizeof(float) * 8);
struct PortalInfo
{
mat4 Transformation;
};
static_assert(sizeof(PortalInfo) == sizeof(float) * 16);
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;
};
static_assert(sizeof(LightInfo) == sizeof(float) * 20);
struct CollisionNodeBufferHeader
{
int root;
int padding1;
int padding2;
int padding3;
};
struct CollisionNode
{
vec3 center;
float padding1;
vec3 extents;
float padding2;
int left;
int right;
int element_index;
int padding3;
};
struct LightmapImage
{
struct
{
std::unique_ptr<VulkanImage> Image;
std::unique_ptr<VulkanImageView> View;
std::unique_ptr<VulkanFramebuffer> Framebuffer;
} raytrace;
struct
{
std::unique_ptr<VulkanImage> Image;
std::unique_ptr<VulkanImageView> View;
std::unique_ptr<VulkanFramebuffer> Framebuffer;
} resolve;
std::unique_ptr<VulkanBuffer> Transfer;
};
struct SceneVertex
{
vec2 Position;
};
class GPURaytracer
{
public:
GPURaytracer();
~GPURaytracer();
void Raytrace(LevelMesh* level);
private:
void CreateVulkanObjects();
void CreateVertexAndIndexBuffers();
void CreateBottomLevelAccelerationStructure();
void CreateTopLevelAccelerationStructure();
void CreateShaders();
void CreateRaytracePipeline();
void CreateResolvePipeline();
void CreateUniformBuffer();
void CreateSceneVertexBuffer();
void CreateSceneLightBuffer();
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 BeginCommands();
void FinishCommands();
void LoadRenderDoc();
void PrintVulkanInfo();
std::vector<SurfaceInfo> CreateSurfaceInfo();
std::vector<PortalInfo> CreatePortalInfo();
std::vector<CollisionNode> CreateCollisionNodes();
static vec2 ToUV(const vec3& vert, const Surface* targetSurface);
LevelMesh* mesh = nullptr;
uint8_t* mappedUniforms = nullptr;
int uniformsIndex = 0;
int uniformStructs = 256;
VkDeviceSize uniformStructStride = sizeof(Uniforms);
std::shared_ptr<VulkanDevice> device;
bool useRayQuery = true;
static const int SceneVertexBufferSize = 1 * 1024 * 1024;
std::unique_ptr<VulkanBuffer> sceneVertexBuffer;
SceneVertex* sceneVertices = nullptr;
int sceneVertexPos = 0;
static const int SceneLightBufferSize = 2 * 1024 * 1024;
std::unique_ptr<VulkanBuffer> sceneLightBuffer;
LightInfo* sceneLights = nullptr;
int sceneLightPos = 0;
std::unique_ptr<VulkanBuffer> vertexBuffer;
std::unique_ptr<VulkanBuffer> indexBuffer;
std::unique_ptr<VulkanBuffer> transferBuffer;
std::unique_ptr<VulkanBuffer> surfaceIndexBuffer;
std::unique_ptr<VulkanBuffer> surfaceBuffer;
std::unique_ptr<VulkanBuffer> portalBuffer;
std::unique_ptr<VulkanBuffer> nodesBuffer;
std::unique_ptr<VulkanBuffer> blScratchBuffer;
std::unique_ptr<VulkanBuffer> blAccelStructBuffer;
std::unique_ptr<VulkanAccelerationStructure> blAccelStruct;
std::unique_ptr<VulkanBuffer> tlTransferBuffer;
std::unique_ptr<VulkanBuffer> tlScratchBuffer;
std::unique_ptr<VulkanBuffer> tlInstanceBuffer;
std::unique_ptr<VulkanBuffer> tlAccelStructBuffer;
std::unique_ptr<VulkanAccelerationStructure> tlAccelStruct;
std::unique_ptr<VulkanShader> vertShader;
std::unique_ptr<VulkanShader> fragShader;
std::unique_ptr<VulkanShader> fragResolveShader;
struct
{
std::unique_ptr<VulkanDescriptorSetLayout> descriptorSetLayout0;
std::unique_ptr<VulkanDescriptorSetLayout> descriptorSetLayout1;
std::unique_ptr<VulkanPipelineLayout> pipelineLayout;
std::unique_ptr<VulkanPipeline> pipeline;
std::unique_ptr<VulkanRenderPass> renderPassBegin;
std::unique_ptr<VulkanRenderPass> renderPassContinue;
std::unique_ptr<VulkanDescriptorPool> descriptorPool0;
std::unique_ptr<VulkanDescriptorPool> descriptorPool1;
std::unique_ptr<VulkanDescriptorSet> descriptorSet0;
std::unique_ptr<VulkanDescriptorSet> descriptorSet1;
} raytrace;
struct
{
std::unique_ptr<VulkanDescriptorSetLayout> descriptorSetLayout;
std::unique_ptr<VulkanPipelineLayout> pipelineLayout;
std::unique_ptr<VulkanPipeline> pipeline;
std::unique_ptr<VulkanRenderPass> renderPass;
std::unique_ptr<VulkanDescriptorPool> descriptorPool;
std::vector<std::unique_ptr<VulkanDescriptorSet>> descriptorSets;
std::unique_ptr<VulkanSampler> sampler;
} resolve;
std::unique_ptr<VulkanBuffer> uniformBuffer;
std::unique_ptr<VulkanBuffer> uniformTransferBuffer;
std::unique_ptr<VulkanFence> submitFence;
std::unique_ptr<VulkanCommandPool> cmdpool;
std::unique_ptr<VulkanCommandBuffer> cmdbuffer;
std::vector<LightmapImage> atlasImages;
static const int atlasImageSize = 2048;
};
class BufferTransfer
{
public:
BufferTransfer& AddBuffer(VulkanBuffer* buffer, const void* data, size_t size);
BufferTransfer& AddBuffer(VulkanBuffer* buffer, const void* data0, size_t size0, const void* data1, size_t size1);
std::unique_ptr<VulkanBuffer> Execute(VulkanDevice* device, VulkanCommandBuffer* cmdbuffer);
private:
struct BufferCopy
{
VulkanBuffer* buffer;
const void* data0;
size_t size0;
const void* data1;
size_t size1;
};
std::vector<BufferCopy> bufferCopies;
};

File diff suppressed because it is too large Load diff

View file

@ -1,193 +0,0 @@
//-----------------------------------------------------------------------------
// Note: this is a modified version of dlight. It is not the original software.
//-----------------------------------------------------------------------------
//
// Copyright (c) 2013-2014 Samuel Villarreal
// svkaiser@gmail.com
//
// 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.
//
#pragma once
#include <vector>
#include <memory>
#include <string>
#include <cstring>
#include <map>
#include <set>
#include "framework/tarray.h"
#include "framework/halffloat.h"
#include "lightmaptexture.h"
#include "math/mathlib.h"
#include "collision.h"
#include "portal.h"
#include "dp_rect_pack/dp_rect_pack.h"
typedef dp::rect_pack::RectPacker<int> RectPacker;
struct MapSubsectorEx;
struct IntSector;
struct IntSideDef;
struct IntLineDef;
struct FLevel;
struct ThingLight;
class FWadWriter;
enum class WallPart;
enum SurfaceType
{
ST_UNKNOWN,
ST_MIDDLESIDE,
ST_UPPERSIDE,
ST_LOWERSIDE,
ST_CEILING,
ST_FLOOR
};
struct Surface
{
// Surface geometry
SurfaceType type = ST_UNKNOWN;
std::vector<vec3> verts;
Plane plane;
BBox bounds;
// Surface material
std::string material;
std::vector<vec2> texUV;
// Surface properties
int typeIndex = 0;
IntSector* controlSector = nullptr;
int sampleDimension = 0;
bool bSky = false;
// Portal
int portalDestinationIndex = -1; // line or sector index
int portalIndex = -1;
// Sector group
int sectorGroup = 0;
// Touching light sources
std::vector<ThingLight*> LightList;
// Lightmap world coordinates for the texture
vec3 worldOrigin = { 0.0f };
vec3 worldStepX = { 0.0f };
vec3 worldStepY = { 0.0f };
// Calculate world coordinates to UV coordinates
vec3 translateWorldToLocal = { 0.0f };
vec3 projLocalToU = { 0.0f };
vec3 projLocalToV = { 0.0f };
// Output lightmap for the surface
int texWidth = 0;
int texHeight = 0;
std::vector<vec3> texPixels;
// UV coordinates for the vertices
std::vector<vec2> lightUV;
// 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
{
Plane plane = Plane(0, 0, 1, 0);
int sectorGroup = 0;
std::vector<Surface*> surfaces;
};
class LevelMesh
{
public:
LevelMesh(FLevel &doomMap, int sampleDistance, int textureSize);
void CreateTextures();
void AddLightmapLump(FWadWriter& wadFile);
void Export(std::string filename);
FLevel* map = nullptr;
std::vector<std::unique_ptr<Surface>> surfaces;
std::vector<std::unique_ptr<LightmapTexture>> textures;
std::vector<SmoothingGroup> smoothingGroups;
std::vector<std::unique_ptr<Portal>> portals;
int defaultSamples = 16;
int textureWidth = 128;
int textureHeight = 128;
TArray<vec3> MeshVertices;
TArray<int> MeshUVIndex;
TArray<unsigned int> MeshElements;
TArray<int> MeshSurfaces;
std::unique_ptr<TriangleMeshShape> Collision;
private:
// Portal to portals[] index
std::map<Portal, int, IdenticalPortalComparator> portalCache;
// Portal lights
std::vector<std::unique_ptr<ThingLight>> portalLights;
std::set<Portal, RecursivePortalComparator> touchedPortals;
int lightPropagationRecursiveDepth = 0;
void CreateSubsectorSurfaces(FLevel &doomMap);
void CreateCeilingSurface(FLevel &doomMap, MapSubsectorEx *sub, IntSector *sector, int typeIndex, bool is3DFloor);
void CreateFloorSurface(FLevel &doomMap, MapSubsectorEx *sub, IntSector *sector, int typeIndex, bool is3DFloor);
void CreateSideSurfaces(FLevel &doomMap, IntSideDef *side);
void BuildSurfaceParams(Surface* surface);
BBox GetBoundsFromSurface(const Surface* surface);
void BuildLightLists(FLevel &doomMap);
void PropagateLight(FLevel& doomMap, ThingLight* thing);
void BuildSmoothingGroups(FLevel& doomMap);
void BlurSurfaces();
void FinishSurface(RectPacker& packer, Surface* surface);
static bool IsDegenerate(const vec3 &v0, const vec3 &v1, const vec3 &v2);
int CheckAndMakePortal(FLevel& doomMap, MapSubsectorEx* sub, IntSector* sector, int typeIndex, int plane);
int CreateLinePortal(FLevel &doomMap, const IntLineDef& srcLine, const IntLineDef& dstLine);
int CreatePlanePortal(FLevel &doomMap, const IntLineDef& srcLine, const IntLineDef& dstLine);
int GetSampleDistance(const IntSideDef& sidedef, WallPart part) const;
};

View file

@ -1,13 +0,0 @@
#include "lightmaptexture.h"
#include "framework/halffloat.h"
#include <algorithm>
LightmapTexture::LightmapTexture(int width, int height) : textureWidth(width), textureHeight(height)
{
#ifdef _DEBUG
mPixels.resize(width * height * 3, floatToHalf(0.5f));
#else
mPixels.resize(width * height * 3, 0);
#endif
}

View file

@ -1,20 +0,0 @@
#pragma once
#include <cstdint>
#include <vector>
class LightmapTexture
{
public:
LightmapTexture(int width, int height);
int Width() const { return textureWidth; }
int Height() const { return textureHeight; }
uint16_t* Pixels() { return mPixels.data(); }
private:
int textureWidth;
int textureHeight;
std::vector<uint16_t> mPixels;
};

View file

@ -1,218 +0,0 @@
#include "math/mathlib.h"
#include "pngwriter.h"
#include "framework/binfile.h"
#include "framework/templates.h"
#include "framework/halffloat.h"
#include <map>
#include <vector>
#include <algorithm>
#include <miniz/miniz.h>
#include <stdexcept>
#include <memory>
#include <cmath>
#include <string.h>
void PNGWriter::save(const std::string& filename, int width, int height, int bytes_per_pixel, void* pixels)
{
PNGImage image;
image.width = width;
image.height = height;
image.bytes_per_pixel = bytes_per_pixel;
image.pixel_ratio = 1.0f;
image.data = pixels;
FILE *file = fopen(filename.c_str(), "wb");
if (file)
{
PNGWriter writer;
writer.file = file;
writer.image = &image;
writer.write_magic();
writer.write_headers();
writer.write_data();
writer.write_chunk("IEND", nullptr, 0);
fclose(file);
}
}
void PNGWriter::write_magic()
{
unsigned char png_magic[8] = { 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A };
write(png_magic, 8);
}
void PNGWriter::write_headers()
{
int ppm = (int)std::round(3800 * image->pixel_ratio);
int ppm_x = ppm;
int ppm_y = ppm;
int width = image->width;
int height = image->height;
int bit_depth = image->bytes_per_pixel == 8 ? 16 : 8;
int color_type = 6;
int compression_method = 0;
int filter_method = 0;
int interlace_method = 0;
unsigned char idhr[13];
idhr[0] = (width >> 24) & 0xff;
idhr[1] = (width >> 16) & 0xff;
idhr[2] = (width >> 8) & 0xff;
idhr[3] = width & 0xff;
idhr[4] = (height >> 24) & 0xff;
idhr[5] = (height >> 16) & 0xff;
idhr[6] = (height >> 8) & 0xff;
idhr[7] = height & 0xff;
idhr[8] = bit_depth;
idhr[9] = color_type;
idhr[10] = compression_method;
idhr[11] = filter_method;
idhr[12] = interlace_method;
//unsigned char srgb[1];
//srgb[0] = 0;
unsigned char phys[9];
phys[0] = (ppm_x >> 24) & 0xff;
phys[1] = (ppm_x >> 16) & 0xff;
phys[2] = (ppm_x >> 8) & 0xff;
phys[3] = ppm_x & 0xff;
phys[4] = (ppm_y >> 24) & 0xff;
phys[5] = (ppm_y >> 16) & 0xff;
phys[6] = (ppm_y >> 8) & 0xff;
phys[7] = ppm_y & 0xff;
phys[8] = 1; // pixels per meter
write_chunk("IHDR", idhr, 13);
if (ppm != 0)
write_chunk("pHYs", phys, 9);
//write_chunk("sRGB", srgb, 1);
}
void PNGWriter::write_data()
{
//int width = image->width;
int height = image->height;
int bytes_per_pixel = image->bytes_per_pixel;
int pitch = image->width * bytes_per_pixel;
std::vector<unsigned char> scanline_orig;
std::vector<unsigned char> scanline_filtered;
scanline_orig.resize((image->width + 1) * bytes_per_pixel);
scanline_filtered.resize(image->width * bytes_per_pixel + 1);
auto idat_uncompressed = std::make_shared<DataBuffer>((int)(height * scanline_filtered.size()));
for (int y = 0; y < height; y++)
{
// Grab scanline
memcpy(scanline_orig.data() + bytes_per_pixel, (uint8_t*)image->data + y * pitch, scanline_orig.size() - bytes_per_pixel);
// Convert to big endian for 16 bit
if (bytes_per_pixel == 8)
{
for (size_t x = 0; x < scanline_orig.size(); x += 2)
{
std::swap(scanline_orig[x], scanline_orig[x + 1]);
}
}
// Filter scanline
/*
scanline_filtered[0] = 0; // None filter type
for (int i = bytes_per_pixel; i < scanline_orig.size(); i++)
{
scanline_filtered[i - bytes_per_pixel + 1] = scanline_orig[i];
}
*/
scanline_filtered[0] = 1; // Sub filter type
for (int i = bytes_per_pixel; i < scanline_orig.size(); i++)
{
unsigned char a = scanline_orig[i - bytes_per_pixel];
unsigned char x = scanline_orig[i];
scanline_filtered[i - bytes_per_pixel + 1] = x - a;
}
// Output scanline
memcpy((uint8_t*)idat_uncompressed->data + y * scanline_filtered.size(), scanline_filtered.data(), scanline_filtered.size());
}
auto idat = std::make_unique<DataBuffer>(idat_uncompressed->size * 125 / 100);
idat->size = (int)compressdata(idat.get(), idat_uncompressed.get(), false);
write_chunk("IDAT", idat->data, (int)idat->size);
}
void PNGWriter::write_chunk(const char name[4], const void *data, int size)
{
unsigned char size_data[4];
size_data[0] = (size >> 24) & 0xff;
size_data[1] = (size >> 16) & 0xff;
size_data[2] = (size >> 8) & 0xff;
size_data[3] = size & 0xff;
write(size_data, 4);
write(name, 4);
write(data, size);
unsigned int crc32 = PNGCRC32::crc(name, data, size);
unsigned char crc32_data[4];
crc32_data[0] = (crc32 >> 24) & 0xff;
crc32_data[1] = (crc32 >> 16) & 0xff;
crc32_data[2] = (crc32 >> 8) & 0xff;
crc32_data[3] = crc32 & 0xff;
write(crc32_data, 4);
}
void PNGWriter::write(const void *data, int size)
{
fwrite(data, size, 1, file);
}
size_t PNGWriter::compressdata(DataBuffer *out, const DataBuffer *data, bool raw)
{
if (data->size > (size_t)0xffffffff || out->size > (size_t)0xffffffff)
throw std::runtime_error("Data is too big");
const int window_bits = 15;
int compression_level = 6;
int strategy = Z_DEFAULT_STRATEGY;
z_stream zs;
memset(&zs, 0, sizeof(z_stream));
int result = deflateInit2(&zs, compression_level, Z_DEFLATED, raw ? -window_bits : window_bits, 8, strategy); // Undocumented: if wbits is negative, zlib skips header check
if (result != Z_OK)
throw std::runtime_error("Zlib deflateInit failed");
zs.next_in = (unsigned char *)data->data;
zs.avail_in = (unsigned int)data->size;
zs.next_out = (unsigned char *)out->data;
zs.avail_out = (unsigned int)out->size;
size_t outSize = 0;
try
{
int result = deflate(&zs, Z_FINISH);
if (result == Z_NEED_DICT) throw std::runtime_error("Zlib deflate wants a dictionary!");
if (result == Z_DATA_ERROR) throw std::runtime_error("Zip data stream is corrupted");
if (result == Z_STREAM_ERROR) throw std::runtime_error("Zip stream structure was inconsistent!");
if (result == Z_MEM_ERROR) throw std::runtime_error("Zlib did not have enough memory to compress file!");
if (result == Z_BUF_ERROR) throw std::runtime_error("Not enough data in buffer when Z_FINISH was used");
if (result != Z_STREAM_END) throw std::runtime_error("Zlib deflate failed while compressing zip file!");
outSize = zs.total_out;
}
catch (...)
{
deflateEnd(&zs);
throw;
}
deflateEnd(&zs);
return outSize;
}

View file

@ -1,78 +0,0 @@
#pragma once
#include <string>
class PNGWriter
{
public:
static void save(const std::string& filename, int width, int height, int bytes_per_pixel, void* pixels);
struct DataBuffer
{
DataBuffer(int size) : size(size) { data = new uint8_t[size]; }
~DataBuffer() { delete[] data; }
int size;
uint8_t* data;
};
private:
struct PNGImage
{
int width;
int height;
int bytes_per_pixel;
void* data;
float pixel_ratio;
};
const PNGImage* image;
FILE* file;
class PNGCRC32
{
public:
static unsigned long crc(const char name[4], const void* data, int len)
{
static PNGCRC32 impl;
const unsigned char* buf = reinterpret_cast<const unsigned char*>(data);
unsigned int c = 0xffffffff;
for (int n = 0; n < 4; n++)
c = impl.crc_table[(c ^ name[n]) & 0xff] ^ (c >> 8);
for (int n = 0; n < len; n++)
c = impl.crc_table[(c ^ buf[n]) & 0xff] ^ (c >> 8);
return c ^ 0xffffffff;
}
private:
unsigned int crc_table[256];
PNGCRC32()
{
for (unsigned int n = 0; n < 256; n++)
{
unsigned int c = n;
for (unsigned int k = 0; k < 8; k++)
{
if ((c & 1) == 1)
c = 0xedb88320 ^ (c >> 1);
else
c = c >> 1;
}
crc_table[n] = c;
}
}
};
void write_magic();
void write_headers();
void write_data();
void write_chunk(const char name[4], const void* data, int size);
void write(const void* data, int size);
size_t compressdata(DataBuffer* out, const DataBuffer* data, bool raw);
};

View file

@ -1,73 +0,0 @@
#pragma once
#include "math/mathlib.h"
struct Portal
{
mat4 transformation = mat4::identity();
int sourceSectorGroup = 0;
int targetSectorGroup = 0;
inline vec3 TransformPosition(const vec3& pos) const
{
auto v = transformation * vec4(pos, 1.0);
return vec3(v.x, v.y, v.z);
}
inline vec3 TransformRotation(const vec3& dir) const
{
auto v = transformation * vec4(dir, 0.0);
return vec3(v.x, v.y, v.z);
}
// Checks only transformation
inline bool IsInverseTransformationPortal(const Portal& portal) const
{
auto diff = portal.TransformPosition(TransformPosition(vec3(0)));
return abs(diff.x) < 0.001 && abs(diff.y) < 0.001 && abs(diff.z) < 0.001;
}
// Checks only transformation
inline bool IsEqualTransformationPortal(const Portal& portal) const
{
auto diff = portal.TransformPosition(vec3(0)) - TransformPosition(vec3(0));
return (abs(diff.x) < 0.001 && abs(diff.y) < 0.001 && abs(diff.z) < 0.001);
}
// Checks transformation, source and destiantion sector groups
inline bool IsEqualPortal(const Portal& portal) const
{
return sourceSectorGroup == portal.sourceSectorGroup && targetSectorGroup == portal.targetSectorGroup && IsEqualTransformationPortal(portal);
}
// Checks transformation, source and destiantion sector groups
inline bool IsInversePortal(const Portal& portal) const
{
return sourceSectorGroup == portal.targetSectorGroup && targetSectorGroup == portal.sourceSectorGroup && IsInverseTransformationPortal(portal);
}
inline void DumpInfo()
{
auto v = TransformPosition(vec3(0));
printf("Portal offset: %.3f %.3f %.3f\n\tsource group:\t%d\n\ttarget group:\t%d", v.x, v.y, v.z, sourceSectorGroup, targetSectorGroup);
}
};
// for use with std::set to recursively go through portals and skip returning portals
struct RecursivePortalComparator
{
bool operator()(const Portal& a, const Portal& b) const
{
return !a.IsInversePortal(b) && std::memcmp(&a.transformation, &b.transformation, sizeof(mat4)) < 0;
}
};
// for use with std::map to reject portals which have the same effect for light rays
struct IdenticalPortalComparator
{
bool operator()(const Portal& a, const Portal& b) const
{
return !a.IsEqualPortal(b) && std::memcmp(&a.transformation, &b.transformation, sizeof(mat4)) < 0;
}
};

View file

@ -1,108 +0,0 @@
#include "surfaceclip.h"
inline bool PointOnSide(const vec2& p, const vec2& v1, const vec2& v2, float tolerance)
{
vec2 p2 = p + normalize(vec2(-(v2.y - v1.y), v2.x - v1.x)) * tolerance;
return (p2.y - v1.y) * (v2.x - v1.x) + (v1.x - p2.x) * (v2.y - v1.y) >= 0;
}
inline bool PointBeyondSide(const vec2& p, const vec2& v1, const vec2& v2)
{
vec2 p2 = p - normalize(vec2(-(v2.y - v1.y), v2.x - v1.x)); // What a hack!
return (p2.y - v1.y) * (v2.x - v1.x) + (v1.x - p2.x) * (v2.y - v1.y) < 0;
}
SurfaceClip::SurfaceClip(Surface* surface)
{
sampleWidth = float(surface->texWidth);
sampleHeight = float(surface->texHeight);
// Transformation matrix
mat3 base;
base[0] = surface->worldStepX.x;
base[1] = surface->worldStepX.y;
base[2] = surface->worldStepX.z;
base[3] = surface->worldStepY.x;
base[4] = surface->worldStepY.y;
base[5] = surface->worldStepY.z;
base[6] = surface->plane.a;
base[7] = surface->plane.b;
base[8] = surface->plane.c;
mat3 inverseProjection = mat3::inverse(base);
// Transform vertices to XY and triangulate
vertices.reserve(surface->verts.size());
for (const auto& vertex : surface->verts)
{
auto flattenedVertex = inverseProjection * vertex;
vertices.emplace_back(flattenedVertex.x, flattenedVertex.y);
if (vertices.empty())
{
bounds = BBox(flattenedVertex, flattenedVertex);
}
else
{
bounds.AddPoint(flattenedVertex);
}
}
// Walls have "Z" like pattern for vertices
if (surface->type != ST_CEILING && surface->type != ST_FLOOR)
{
if (vertices.size() == 4)
{
std::swap(vertices[vertices.size() - 2], vertices[vertices.size() - 1]);
}
}
auto isConvex = [&]() {
for (size_t i = 2; i < vertices.size(); ++i)
{
if (!PointBeyondSide(vertices[i - 1], vertices[i - 2], vertices[i]))
{
return false;
}
}
return PointBeyondSide(vertices[vertices.size() - 1], vertices[vertices.size() - 2], vertices[0]) && PointBeyondSide(vertices[0], vertices[vertices.size() - 1], vertices[1]);
};
// Fix vertex order
if (!isConvex())
{
for (size_t i = 0; i < vertices.size() / 2; ++i)
{
std::swap(vertices[i], vertices[vertices.size() - 1 - i]);
}
}
// Init misc. variables
boundsWidth = bounds.max.x - bounds.min.x;
boundsHeight = bounds.max.y - bounds.min.y;
offsetW = boundsWidth / sampleWidth;
offsetH = boundsHeight / sampleHeight;
tolerance = (offsetH > offsetW ? offsetH : offsetW) * 2.0f;
}
bool SurfaceClip::PointInBounds(const vec2& p, float tolerance) const
{
for (size_t i = 1; i < vertices.size(); ++i)
{
if (!PointOnSide(p, vertices[i - 1], vertices[i], tolerance))
{
return false;
}
}
return PointOnSide(p, vertices[vertices.size() - 1], vertices[0], tolerance);
}
bool SurfaceClip::SampleIsInBounds(float x, float y) const
{
return PointInBounds(vec2((x / float(sampleWidth)) * boundsWidth + bounds.min.x + offsetW, (y / float(sampleHeight)) * boundsHeight + bounds.min.y + offsetH), tolerance);
}

View file

@ -1,28 +0,0 @@
#pragma once
#include "lightmap/levelmesh.h"
#include "math/mathlib.h"
#include "delauneytriangulator.h"
class SurfaceClip
{
std::vector<vec2> vertices;
float sampleWidth;
float sampleHeight;
BBox bounds;
float boundsWidth;
float boundsHeight;
float offsetW;
float offsetH;
float tolerance;
// Local space
bool PointInBounds(const vec2& p, float tolerance) const;
public:
SurfaceClip(Surface* surface);
// Task XY space. Tolerates points close enough to the surface to avoid missing used samples
bool SampleIsInBounds(float x, float y) const;
};

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,146 @@
#pragma once
#include "framework/zstring.h"
#include "hw_levelmesh.h"
#include "hw_lightmaptile.h"
#include "level/doomdata.h"
#include <map>
struct FLevel;
class FWadWriter;
struct FPolyObj;
struct HWWallDispatcher;
class DoomLevelMesh;
class MeshBuilder;
enum DoomLevelMeshSurfaceType
{
ST_NONE,
ST_MIDDLESIDE,
ST_UPPERSIDE,
ST_LOWERSIDE,
ST_CEILING,
ST_FLOOR
};
struct DoomLevelMeshSurface : public LevelMeshSurface
{
DoomLevelMeshSurfaceType Type = ST_NONE;
int TypeIndex = 0;
MapSubsectorEx* Subsector = nullptr;
IntSideDef* Side = nullptr;
IntSector* ControlSector = nullptr;
int PipelineID = 0;
};
struct SideSurfaceRange
{
int StartSurface = 0;
int SurfaceCount = 0;
};
struct FlatSurfaceRange
{
int StartSurface = 0;
int SurfaceCount = 0;
};
class DoomLevelMesh : public LevelMesh
{
public:
DoomLevelMesh(FLevel& doomMap);
LevelMeshSurface* GetSurface(int index) override { return &Surfaces[index]; }
unsigned int GetSurfaceIndex(const LevelMeshSurface* surface) const override { return (unsigned int)(ptrdiff_t)(static_cast<const DoomLevelMeshSurface*>(surface) - Surfaces.Data()); }
int GetSurfaceCount() override { return Surfaces.Size(); }
void BeginFrame(FLevel& doomMap);
bool TraceSky(const FVector3& start, FVector3 direction, float dist);
void DumpMesh(const FString& objFilename, const FString& mtlFilename) const;
void BuildSectorGroups(const FLevel& doomMap);
void AddLightmapLump(FLevel& doomMap, FWadWriter& wadFile);
TArray<DoomLevelMeshSurface> Surfaces;
TArray<int> sectorGroup; // index is sector, value is sectorGroup
TArray<int> sectorPortals[2]; // index is sector+plane, value is index into the portal list
TArray<int> linePortals; // index is linedef, value is index into the portal list
void CreateLights(FLevel& doomMap);
private:
void CreateSurfaces(FLevel& doomMap);
void CreateSideSurfaces(FLevel& doomMap, IntSideDef* side);
void CreateLineHorizonSurface(FLevel& doomMap, IntSideDef* side);
void CreateFrontWallSurface(FLevel& doomMap, IntSideDef* side);
void CreateMidWallSurface(FLevel& doomMap, IntSideDef* side);
void Create3DFloorWallSurfaces(FLevel& doomMap, IntSideDef* side);
void CreateTopWallSurface(FLevel& doomMap, IntSideDef* side);
void CreateBottomWallSurface(FLevel& doomMap, IntSideDef* side);
void AddWallVertices(DoomLevelMeshSurface& surf, FFlatVertex* verts);
void SetSideTextureUVs(FLevel& doomMap, DoomLevelMeshSurface& surface, IntSideDef* side, WallPart texpart, float v1TopZ, float v1BottomZ, float v2TopZ, float v2BottomZ);
void CreateFloorSurface(FLevel& doomMap, MapSubsectorEx* sub, IntSector* sector, X3DFloor* controlSector, int typeIndex);
void CreateCeilingSurface(FLevel& doomMap, MapSubsectorEx* sub, IntSector* sector, X3DFloor* controlSector, int typeIndex);
void AddSurfaceToTile(DoomLevelMeshSurface& surf, FLevel& doomMap, uint16_t sampleDimension);
int GetSampleDimension(const DoomLevelMeshSurface& surf, uint16_t sampleDimension);
static bool IsTopSideSky(IntSector* frontsector, IntSector* backsector, IntSideDef* side);
static bool IsTopSideVisible(IntSideDef* side);
static bool IsBottomSideVisible(IntSideDef* side);
static bool IsSkySector(IntSector* sector, SecPlaneType plane);
static bool IsDegenerate(const FVector3& v0, const FVector3& v1, const FVector3& v2);
void SortIndexes();
BBox GetBoundsFromSurface(const LevelMeshSurface& surface) const;
int AddSurfaceToTile(const DoomLevelMeshSurface& surf);
int GetSampleDimension(const DoomLevelMeshSurface& surf);
void CreatePortals(FLevel& doomMap);
void PropagateLight(FLevel& doomMap, ThingLight* light, int recursiveDepth);
int GetLightIndex(ThingLight* light, int portalgroup);
static FVector4 ToPlane(const FFlatVertex& pt1, const FFlatVertex& pt2, const FFlatVertex& pt3)
{
return ToPlane(FVector3(pt1.x, pt1.y, pt1.z), FVector3(pt2.x, pt2.y, pt2.z), FVector3(pt3.x, pt3.y, pt3.z));
}
static FVector4 ToPlane(const FFlatVertex& pt1, const FFlatVertex& pt2, const FFlatVertex& pt3, const FFlatVertex& pt4)
{
return ToPlane(FVector3(pt1.x, pt1.y, pt1.z), FVector3(pt2.x, pt2.y, pt2.z), FVector3(pt3.x, pt3.y, pt3.z), FVector3(pt4.x, pt4.y, pt4.z));
}
static FVector4 ToPlane(const FVector3& pt1, const FVector3& pt2, const FVector3& pt3)
{
FVector3 n = ((pt2 - pt1) ^ (pt3 - pt2)).Unit();
float d = pt1 | n;
return FVector4(n.X, n.Y, n.Z, d);
}
static FVector4 ToPlane(const FVector3& pt1, const FVector3& pt2, const FVector3& pt3, const FVector3& pt4)
{
if (pt1.ApproximatelyEquals(pt3))
{
return ToPlane(pt1, pt2, pt4);
}
else if (pt1.ApproximatelyEquals(pt2) || pt2.ApproximatelyEquals(pt3))
{
return ToPlane(pt1, pt3, pt4);
}
return ToPlane(pt1, pt2, pt3);
}
TArray<SideSurfaceRange> Sides;
TArray<FlatSurfaceRange> Flats;
std::map<LightmapTileBinding, int> bindings;
};

View file

@ -0,0 +1,46 @@
#pragma once
struct FFlatVertex // Note: this must always match the SurfaceVertex struct in shaders (std430 layout rules apply)
{
float x, z, y; // world position
float lindex; // lightmap texture index
float u, v; // texture coordinates
float lu, lv; // lightmap texture coordinates
void Set(float xx, float zz, float yy, float uu, float vv)
{
x = xx;
z = zz;
y = yy;
u = uu;
v = vv;
lindex = -1.0f;
}
void Set(float xx, float zz, float yy, float uu, float vv, float llu, float llv, float llindex)
{
x = xx;
z = zz;
y = yy;
lindex = llindex;
u = uu;
v = vv;
lu = llu;
lv = llv;
}
void SetVertex(float _x, float _y, float _z = 0)
{
x = _x;
z = _y;
y = _z;
}
void SetTexCoord(float _u = 0, float _v = 0)
{
u = _u;
v = _v;
}
FVector3 fPos() const { return FVector3(x, y, z); }
};

View file

@ -0,0 +1,72 @@
static const char* binding_lightmapper_glsl = R"glsl(
layout(set = 0, binding = 0) uniform Uniforms
{
vec3 SunDir;
float Padding1;
vec3 SunColor;
float SunIntensity;
};
struct SurfaceInfo
{
vec3 Normal;
float Sky;
uint PortalIndex;
int TextureIndex;
float Alpha;
float Padding0;
uint LightStart;
uint LightEnd;
uint Padding1;
uint 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 SoftShadowRadius;
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 LightIndexBuffer { int lightIndexes[]; };
layout(set = 0, binding = 5) buffer PortalBuffer { PortalInfo portals[]; };
struct LightmapRaytracePC
{
int SurfaceIndex;
int Padding0;
int Padding1;
int Padding2;
vec3 WorldToLocal;
float TextureSize;
vec3 ProjLocalToU;
float Padding3;
vec3 ProjLocalToV;
float Padding4;
float TileX;
float TileY;
float TileWidth;
float TileHeight;
};
layout(std430, set = 0, binding = 6) buffer ConstantsBuffer { LightmapRaytracePC constants[]; };
)glsl";

View file

@ -0,0 +1,43 @@
static const char* binding_raytrace_glsl = R"glsl(
#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[];
};
#endif
struct SurfaceVertex // Note: this must always match the FFlatVertex struct
{
vec3 pos;
float lindex;
vec2 uv;
vec2 luv;
};
layout(std430, set = 1, binding = 1) buffer VertexBuffer { SurfaceVertex vertices[]; };
layout(std430, set = 1, binding = 2) buffer ElementBuffer { int elements[]; };
)glsl";

View file

@ -0,0 +1,5 @@
static const char* binding_textures_glsl = R"glsl(
layout(set = 2, binding = 0) uniform sampler2D textures[];
)glsl";

View file

@ -0,0 +1,97 @@
static const char* binding_viewer_glsl = R"glsl(
struct SurfaceInfo
{
vec3 Normal;
float Sky;
uint PortalIndex;
int TextureIndex;
float Alpha;
float Padding0;
uint LightStart;
uint LightEnd;
uint Padding1;
uint 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 SoftShadowRadius;
vec3 Color;
float Padding3;
};
layout(set = 0, binding = 0, std430) buffer SurfaceIndexBuffer { uint surfaceIndices[]; };
layout(set = 0, binding = 1, std430) buffer SurfaceBuffer { SurfaceInfo surfaces[]; };
layout(set = 0, binding = 2, std430) buffer LightBuffer { LightInfo lights[]; };
layout(set = 0, binding = 3, std430) buffer LightIndexBuffer { int lightIndexes[]; };
layout(set = 0, binding = 4, std430) buffer PortalBuffer { PortalInfo portals[]; };
#if defined(USE_RAYQUERY)
layout(set = 0, binding = 5) uniform accelerationStructureEXT acc;
#else
struct CollisionNode
{
vec3 center;
float padding1;
vec3 extents;
float padding2;
int left;
int right;
int element_index;
int padding3;
};
layout(set = 0, binding = 5, std430) buffer NodeBuffer
{
int nodesRoot;
int nodebufferPadding1;
int nodebufferPadding2;
int nodebufferPadding3;
CollisionNode nodes[];
};
#endif
struct SurfaceVertex // Note: this must always match the FFlatVertex struct
{
vec3 pos;
float lindex;
vec2 uv;
vec2 luv;
};
layout(set = 0, binding = 6, std430) buffer VertexBuffer { SurfaceVertex vertices[]; };
layout(set = 0, binding = 7, std430) buffer ElementBuffer { int elements[]; };
layout(set = 1, binding = 0) uniform sampler2D textures[];
layout(push_constant) uniform PushConstants
{
mat4 ViewToWorld;
vec3 CameraPos;
float ProjX;
vec3 SunDir;
float ProjY;
vec3 SunColor;
float SunIntensity;
};
)glsl";

View file

@ -0,0 +1,35 @@
static const char* frag_blur_glsl = R"glsl(
layout(set = 0, binding = 0) uniform sampler2D tex;
layout(location = 0) in vec2 TexCoord;
layout(location = 0) out vec4 fragcolor;
vec4 centerFragColor;
vec4 clampedSample(vec4 f)
{
return f != vec4(0, 0, 0, 0) ? f : centerFragColor;
}
void main()
{
ivec2 size = textureSize(tex, 0);
vec2 texCoord = gl_FragCoord.xy / vec2(size);
centerFragColor = textureOffset(tex, texCoord, ivec2(0, 0));
#if defined(BLUR_HORIZONTAL)
fragcolor =
centerFragColor * 0.5 +
clampedSample(textureOffset(tex, texCoord, ivec2( 1, 0))) * 0.25 +
clampedSample(textureOffset(tex, texCoord, ivec2(-1, 0))) * 0.25;
#else
fragcolor =
centerFragColor * 0.5 +
clampedSample(textureOffset(tex, texCoord, ivec2(0, 1))) * 0.25 +
clampedSample(textureOffset(tex, texCoord, ivec2(0,-1))) * 0.25;
#endif
}
)glsl";

View file

@ -0,0 +1,13 @@
static const char* frag_copy_glsl = R"glsl(
layout(set = 0, binding = 0) uniform sampler2D Tex;
layout(location = 0) in vec2 TexCoord;
layout(location = 0) out vec4 FragColor;
void main()
{
FragColor = texture(Tex, TexCoord);
}
)glsl";

View file

@ -0,0 +1,49 @@
static const char* frag_raytrace_glsl = R"glsl(
#include <shaders/lightmap/binding_lightmapper.glsl>
#include <shaders/lightmap/binding_raytrace.glsl>
#include <shaders/lightmap/binding_textures.glsl>
#include <shaders/lightmap/polyfill_rayquery.glsl>
#include <shaders/lightmap/trace_levelmesh.glsl>
#include <shaders/lightmap/trace_sunlight.glsl>
#include <shaders/lightmap/trace_light.glsl>
#include <shaders/lightmap/trace_ambient_occlusion.glsl>
#include <shaders/lightmap/trace_bounce.glsl>
layout(location = 0) centroid in vec3 worldpos;
layout(location = 1) in flat int InstanceIndex;
layout(location = 0) out vec4 fragcolor;
void main()
{
int SurfaceIndex = constants[InstanceIndex].SurfaceIndex;
uint LightStart = surfaces[SurfaceIndex].LightStart;
uint LightEnd = surfaces[SurfaceIndex].LightEnd;
vec3 normal = surfaces[SurfaceIndex].Normal;
vec3 origin = worldpos;
#if defined(USE_SUNLIGHT)
vec3 incoming = TraceSunLight(origin, normal);
#else
vec3 incoming = vec3(0.0);
#endif
for (uint j = LightStart; j < LightEnd; j++)
{
incoming += TraceLight(origin, normal, lights[lightIndexes[j]], 0.0);
}
#if defined(USE_BOUNCE)
incoming += TraceBounceLight(origin, normal);
#endif
#if defined(USE_AO)
incoming.rgb *= TraceAmbientOcclusion(origin, normal);
#endif
fragcolor = vec4(incoming, 1.0);
}
)glsl";

View file

@ -1,8 +1,8 @@
static const char* glsl_frag_resolve = R"glsl(
static const char* frag_resolve_glsl = R"glsl(
layout(set = 0, binding = 0) uniform sampler2DMS tex;
layout(location = 0) in vec3 worldpos;
layout(location = 0) in vec2 TexCoord;
layout(location = 0) out vec4 fragcolor;
vec4 samplePixel(ivec2 pos, int count)

View file

@ -0,0 +1,62 @@
static const char* frag_viewer_glsl = R"glsl(
#include <shaders/lightmap/binding_viewer.glsl>
#include <shaders/lightmap/polyfill_rayquery.glsl>
#include <shaders/lightmap/trace_levelmesh.glsl>
#include <shaders/lightmap/trace_sunlight.glsl>
#include <shaders/lightmap/trace_light.glsl>
#include <shaders/lightmap/trace_ambient_occlusion.glsl>
layout(location = 0) in vec3 FragRay;
layout(location = 0) out vec4 fragcolor;
void main()
{
vec3 incoming = vec3(0.1);
vec3 origin = CameraPos;
vec3 L = normalize(FragRay);
TraceResult result = TraceFirstHit(origin, 0.0, L, 10000.0);
if (result.primitiveIndex != -1)
{
SurfaceInfo surface = GetSurface(result.primitiveIndex);
vec3 surfacepos = origin + L * result.t;
if (surface.Sky == 0.0)
{
incoming += TraceSunLight(surfacepos, surface.Normal);
uint LightStart = surface.LightStart;
uint LightEnd = surface.LightEnd;
for (uint j = LightStart; j < LightEnd; j++)
{
incoming += TraceLight(surfacepos, surface.Normal, lights[lightIndexes[j]], 0.0);
}
// incoming *= TraceAmbientOcclusion(surfacepos, surface.Normal);
}
else
{
incoming = SunColor;
}
if (surface.TextureIndex != 0)
{
vec2 uv = GetSurfaceUV(result.primitiveIndex, result.primitiveWeights);
vec4 color = texture(textures[surface.TextureIndex], uv);
incoming *= color.rgb;
}
else
{
incoming = vec3(0.0, 0.0, 1.0);
}
}
else
{
incoming = vec3(1.0, 0.0, 0.0);
}
fragcolor = vec4(incoming, 1.0);
}
)glsl";

View file

@ -0,0 +1,33 @@
static const char* montecarlo_glsl = R"glsl(
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
}
vec2 Hammersley(uint i, uint N)
{
return vec2(float(i) / float(N), RadicalInverse_VdC(i));
}
vec2 getVogelDiskSample(int sampleIndex, int sampleCount, float phi)
{
const float goldenAngle = radians(180.0) * (3.0 - sqrt(5.0));
float sampleIndexF = float(sampleIndex);
float sampleCountF = float(sampleCount);
float r = sqrt((sampleIndexF + 0.5) / sampleCountF); // Assuming index and count are positive
float theta = sampleIndexF * goldenAngle + phi;
float sine = sin(theta);
float cosine = cos(theta);
return vec2(cosine, sine) * r;
}
)glsl";

View file

@ -0,0 +1,284 @@
static const char* polyfill_rayquery_glsl = R"glsl(
struct TraceResult
{
float t;
vec3 primitiveWeights;
int primitiveIndex;
};
#if defined(USE_RAYQUERY)
TraceResult TraceFirstHit(vec3 origin, float tmin, vec3 dir, float tmax)
{
TraceResult result;
rayQueryEXT rayQuery;
rayQueryInitializeEXT(rayQuery, acc, gl_RayFlagsCullBackFacingTrianglesEXT, 0xFF, origin, tmin, dir, tmax);
while(rayQueryProceedEXT(rayQuery))
{
if (rayQueryGetIntersectionTypeEXT(rayQuery, false) == gl_RayQueryCommittedIntersectionTriangleEXT)
{
rayQueryConfirmIntersectionEXT(rayQuery);
}
}
if (rayQueryGetIntersectionTypeEXT(rayQuery, true) == gl_RayQueryCommittedIntersectionTriangleEXT)
{
result.t = rayQueryGetIntersectionTEXT(rayQuery, true);
result.primitiveWeights.xy = rayQueryGetIntersectionBarycentricsEXT(rayQuery, true);
result.primitiveWeights.z = 1.0 - result.primitiveWeights.x - result.primitiveWeights.y;
result.primitiveIndex = rayQueryGetIntersectionPrimitiveIndexEXT(rayQuery, true);
}
else
{
result.t = tmax;
result.primitiveIndex = -1;
}
return result;
}
/*
bool TraceAnyHit(vec3 origin, float tmin, vec3 dir, float tmax)
{
rayQueryEXT rayQuery;
rayQueryInitializeEXT(rayQuery, acc, gl_RayFlagsTerminateOnFirstHitEXT | gl_RayFlagsCullBackFacingTrianglesEXT, 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]].pos.xyz;
p[1] = vertices[elements[start_element + 1]].pos.xyz;
p[2] = vertices[elements[start_element + 2]].pos.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;
}
TraceResult TraceFirstHit(vec3 origin, float tmin, vec3 dir, float tmax)
{
TraceResult result;
// 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)
{
result.t = mix(segstart, segend, hit.fraction);
result.primitiveWeights.x = hit.b;
result.primitiveWeights.y = hit.c;
result.primitiveWeights.z = 1.0 - hit.b - hit.c;
result.primitiveIndex = hit.triangle;
return result;
}
}
result.t = tracedist;
result.primitiveIndex = -1;
return result;
}
#endif
)glsl";

View file

@ -0,0 +1,62 @@
static const char* trace_ambient_occlusion_glsl = R"glsl(
#include <shaders/lightmap/montecarlo.glsl>
float TraceAORay(vec3 origin, float tmin, vec3 dir, float tmax);
float TraceAmbientOcclusion(vec3 origin, vec3 normal)
{
const float minDistance = 0.01;
const float aoDistance = 100;
const int SampleCount = 128;
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;
ambience += clamp(TraceAORay(origin, minDistance, L, aoDistance) / aoDistance, 0.0, 1.0);
}
return ambience / float(SampleCount);
}
float TraceAORay(vec3 origin, float tmin, vec3 dir, float tmax)
{
float tcur = 0.0;
for (int i = 0; i < 3; i++)
{
TraceResult result = TraceFirstHit(origin, tmin, dir, tmax - tcur);
if (result.primitiveIndex == -1)
return tmax;
SurfaceInfo surface = GetSurface(result.primitiveIndex);
// Stop if hit sky portal
if (surface.Sky > 0.0)
return tmax;
// Stop if opaque surface
if (surface.PortalIndex == 0 /*surface.TextureIndex == 0*/)
{
return tcur + result.t;
}
// Move to surface hit point
origin += dir * result.t;
tcur += result.t;
if (tcur >= tmax)
return tmax;
// Move through the portal, if any
TransformRay(surface.PortalIndex, origin, dir);
}
return tmax;
}
)glsl";

View file

@ -0,0 +1,48 @@
static const char* trace_bounce_glsl = R"glsl(
#include <shaders/lightmap/montecarlo.glsl>
vec3 TraceBounceLight(vec3 origin, vec3 normal)
{
const float minDistance = 0.01;
const float maxDistance = 1000.0;
const int SampleCount = 8;
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);
vec3 incoming = vec3(0.0);
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;
TraceResult result = TraceFirstHit(origin, minDistance, L, maxDistance);
// We hit nothing.
if (result.primitiveIndex == -1)
continue;
SurfaceInfo surface = GetSurface(result.primitiveIndex);
uint LightStart = surface.LightStart;
uint LightEnd = surface.LightEnd;
vec3 surfacepos = origin + L * result.t;
float angleAttenuation = max(dot(normal, L), 0.0);
#if defined(USE_SUNLIGHT)
incoming += TraceSunLight(surfacepos, surface.Normal) * angleAttenuation;
#endif
for (uint j = LightStart; j < LightEnd; j++)
{
incoming += TraceLight(surfacepos, surface.Normal, lights[lightIndexes[j]], result.t) * angleAttenuation;
}
}
return incoming / float(SampleCount);
}
)glsl";

View file

@ -0,0 +1,53 @@
static const char* trace_levelmesh_glsl = R"glsl(
vec3 BeerLambertSimple(vec3 medium, float depth, vec3 ray_color);
SurfaceInfo GetSurface(int primitiveIndex)
{
return surfaces[surfaceIndices[primitiveIndex]];
}
vec2 GetSurfaceUV(int primitiveIndex, vec3 primitiveWeights)
{
int index = primitiveIndex * 3;
return
vertices[elements[index + 1]].uv * primitiveWeights.x +
vertices[elements[index + 2]].uv * primitiveWeights.y +
vertices[elements[index + 0]].uv * primitiveWeights.z;
}
vec3 PassRayThroughSurface(SurfaceInfo surface, vec2 uv, vec3 rayColor)
{
if (surface.TextureIndex == 0)
{
return rayColor;
}
else
{
vec4 color = texture(textures[surface.TextureIndex], uv);
// To do: currently we do not know the material/renderstyle of the surface.
//
// This means we can't apply translucency and we can't do something like BeerLambertSimple.
// In order to improve this SurfaceInfo needs additional info.
//
// return BeerLambertSimple(1.0 - color.rgb, color.a * surface.Alpha, rayColor);
// Assume the renderstyle is basic alpha blend for now.
return rayColor * (1.0 - color.a * surface.Alpha);
}
}
void TransformRay(uint portalIndex, inout vec3 origin, inout vec3 dir)
{
mat4 transformationMatrix = portals[portalIndex].Transformation;
origin = (transformationMatrix * vec4(origin, 1.0)).xyz;
dir = (transformationMatrix * vec4(dir, 0.0)).xyz;
}
vec3 BeerLambertSimple(vec3 medium, float depth, vec3 ray_color) // based on Beer-Lambert law
{
return ray_color * exp(-medium * depth);
}
)glsl";

View file

@ -0,0 +1,95 @@
static const char* trace_light_glsl = R"glsl(
#include <shaders/lightmap/montecarlo.glsl>
vec3 TracePointLightRay(vec3 origin, vec3 lightpos, float tmin, vec3 rayColor);
vec3 TraceLight(vec3 origin, vec3 normal, LightInfo light, float extraDistance)
{
const float minDistance = 0.01;
vec3 incoming = vec3(0.0);
float dist = distance(light.RelativeOrigin, origin) + extraDistance;
if (dist > minDistance && dist < light.Radius)
{
vec3 dir = normalize(light.RelativeOrigin - origin);
float distAttenuation = max(1.0 - (dist / light.Radius), 0.0);
float 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)
{
vec3 rayColor = light.Color.rgb * (attenuation * light.Intensity);
#if defined(USE_SOFTSHADOWS)
if (light.SoftShadowRadius != 0.0)
{
vec3 v = (abs(dir.x) > abs(dir.y)) ? vec3(0.0, 1.0, 0.0) : vec3(1.0, 0.0, 0.0);
vec3 xdir = normalize(cross(dir, v));
vec3 ydir = cross(dir, xdir);
float lightsize = light.SoftShadowRadius;
int step_count = 10;
for (int i = 0; i < step_count; i++)
{
vec2 gridoffset = getVogelDiskSample(i, step_count, gl_FragCoord.x + gl_FragCoord.y * 13.37) * lightsize;
vec3 pos = light.Origin + xdir * gridoffset.x + ydir * gridoffset.y;
incoming += TracePointLightRay(origin, pos, minDistance, rayColor) / float(step_count);
}
}
else
{
incoming += TracePointLightRay(origin, light.Origin, minDistance, rayColor);
}
#else
incoming += TracePointLightRay(origin, light.Origin, minDistance, rayColor);
#endif
}
}
return incoming;
}
vec3 TracePointLightRay(vec3 origin, vec3 lightpos, float tmin, vec3 rayColor)
{
vec3 dir = normalize(lightpos - origin);
float tmax = distance(origin, lightpos);
for (int i = 0; i < 3; i++)
{
TraceResult result = TraceFirstHit(origin, tmin, dir, tmax);
// Stop if we hit nothing - the point light is visible.
if (result.primitiveIndex == -1)
return rayColor;
SurfaceInfo surface = GetSurface(result.primitiveIndex);
// Pass through surface texture
rayColor = PassRayThroughSurface(surface, GetSurfaceUV(result.primitiveIndex, result.primitiveWeights), rayColor);
// Stop if there is no light left
if (rayColor.r + rayColor.g + rayColor.b <= 0.0)
return vec3(0.0);
// Move to surface hit point
origin += dir * result.t;
tmax -= result.t;
// Move through the portal, if any
TransformRay(surface.PortalIndex, origin, dir);
}
return vec3(0.0);
}
)glsl";

View file

@ -0,0 +1,80 @@
static const char* trace_sunlight_glsl = R"glsl(
#include <shaders/lightmap/montecarlo.glsl>
vec3 TraceSunRay(vec3 origin, float tmin, vec3 dir, float tmax, vec3 rayColor);
vec3 TraceSunLight(vec3 origin, vec3 normal)
{
float angleAttenuation = max(dot(normal, SunDir), 0.0);
if (angleAttenuation == 0.0)
return vec3(0.0);
const float minDistance = 0.01;
vec3 incoming = vec3(0.0);
const float dist = 65536.0;
vec3 rayColor = SunColor.rgb * SunIntensity;
#if defined(USE_SOFTSHADOWS)
vec3 target = origin + SunDir * dist;
vec3 dir = SunDir;
vec3 v = (abs(dir.x) > abs(dir.y)) ? vec3(0.0, 1.0, 0.0) : vec3(1.0, 0.0, 0.0);
vec3 xdir = normalize(cross(dir, v));
vec3 ydir = cross(dir, xdir);
float lightsize = 100;
int step_count = 10;
for (int i = 0; i < step_count; i++)
{
vec2 gridoffset = getVogelDiskSample(i, step_count, gl_FragCoord.x + gl_FragCoord.y * 13.37) * lightsize;
vec3 pos = target + xdir * gridoffset.x + ydir * gridoffset.y;
incoming += TraceSunRay(origin, minDistance, normalize(pos - origin), dist, rayColor) / float(step_count);
}
#else
incoming = TraceSunRay(origin, minDistance, SunDir, dist, rayColor);
#endif
return incoming * angleAttenuation;
}
vec3 TraceSunRay(vec3 origin, float tmin, vec3 dir, float tmax, vec3 rayColor)
{
for (int i = 0; i < 3; i++)
{
TraceResult result = TraceFirstHit(origin, tmin, dir, tmax);
// Stop if we hit nothing. We have to hit a sky surface to hit the sky.
if (result.primitiveIndex == -1)
return vec3(0.0);
SurfaceInfo surface = GetSurface(result.primitiveIndex);
// Stop if we hit the sky.
if (surface.Sky > 0.0)
return rayColor;
// Pass through surface texture
rayColor = PassRayThroughSurface(surface, GetSurfaceUV(result.primitiveIndex, result.primitiveWeights), rayColor);
// Stop if there is no light left
if (rayColor.r + rayColor.g + rayColor.b <= 0.0)
return vec3(0.0);
// Move to surface hit point
origin += dir * result.t;
tmax -= result.t;
if (tmax <= tmin)
return vec3(0.0);
// Move through the portal, if any
TransformRay(surface.PortalIndex, origin, dir);
}
return vec3(0.0);
}
)glsl";

View file

@ -0,0 +1,44 @@
static const char* vert_copy_glsl = R"glsl(
layout(push_constant) uniform PushConstants
{
int SrcTexSize;
int DestTexSize;
int Padding1;
int Padding2;
};
struct TileCopy
{
ivec2 SrcPos;
ivec2 DestPos;
ivec2 TileSize;
int Padding1, Padding2;
};
layout(std430, set = 0, binding = 1) buffer CopyBuffer
{
TileCopy tiles[];
};
layout(location = 0) out vec2 TexCoord;
vec2 positions[4] = vec2[](
vec2(0.0, 0.0),
vec2(1.0, 0.0),
vec2(0.0, 1.0),
vec2(1.0, 1.0)
);
void main()
{
TileCopy tile = tiles[gl_InstanceIndex];
vec2 uv = positions[gl_VertexIndex];
vec2 src = (vec2(tile.SrcPos) + uv * vec2(tile.TileSize)) / float(SrcTexSize);
vec2 dest = (vec2(tile.DestPos) + uv * vec2(tile.TileSize)) / float(DestTexSize);
gl_Position = vec4(dest * 2.0 - 1.0, 0.0, 1.0);
TexCoord = src;
}
)glsl";

View file

@ -0,0 +1,39 @@
static const char* vert_raytrace_glsl = R"glsl(
#include <shaders/lightmap/binding_lightmapper.glsl>
layout(location = 0) in vec3 aPosition;
layout(location = 0) out vec3 worldpos;
layout(location = 1) out flat int InstanceIndex;
void main()
{
vec3 WorldToLocal = constants[gl_InstanceIndex].WorldToLocal;
float TextureSize = constants[gl_InstanceIndex].TextureSize;
vec3 ProjLocalToU = constants[gl_InstanceIndex].ProjLocalToU;
vec3 ProjLocalToV = constants[gl_InstanceIndex].ProjLocalToV;
float TileX = constants[gl_InstanceIndex].TileX;
float TileY = constants[gl_InstanceIndex].TileY;
float TileWidth = constants[gl_InstanceIndex].TileWidth;
float TileHeight = constants[gl_InstanceIndex].TileHeight;
InstanceIndex = gl_InstanceIndex;
worldpos = aPosition;
// Project to position relative to tile
vec3 localPos = aPosition - WorldToLocal;
float x = dot(localPos, ProjLocalToU);
float y = dot(localPos, ProjLocalToV);
// Find the position in the output texture
gl_Position = vec4(vec2(TileX + x, TileY + y) / TextureSize * 2.0 - 1.0, 0.0, 1.0);
// Clip all surfaces to the edge of the tile (effectly we are applying a viewport/scissor to the tile)
gl_ClipDistance[0] = x;
gl_ClipDistance[1] = y;
gl_ClipDistance[2] = TileWidth - x;
gl_ClipDistance[3] = TileHeight - y;
}
)glsl";

View file

@ -0,0 +1,23 @@
static const char* vert_screenquad_glsl = R"glsl(
layout(location = 0) out vec2 TexCoord;
vec2 positions[3] = vec2[](
vec2(-1.0, -1.0),
vec2( 3.0, -1.0),
vec2(-1.0, 3.0)
);
vec2 uvs[3] = vec2[](
vec2(0.0, 0.0),
vec2(2.0, 0.0),
vec2(0.0, 2.0)
);
void main()
{
gl_Position = vec4(positions[gl_VertexIndex], 0.0, 1.0);
TexCoord = uvs[gl_VertexIndex];
}
)glsl";

View file

@ -0,0 +1,24 @@
static const char* vert_viewer_glsl = R"glsl(
#include <shaders/lightmap/binding_viewer.glsl>
layout(location = 0) out vec3 FragRay;
vec2 positions[4] = vec2[](
vec2(0.0, 0.0),
vec2(1.0, 0.0),
vec2(0.0, 1.0),
vec2(1.0, 1.0)
);
void main()
{
vec4 viewpos = vec4(positions[gl_VertexIndex] * 2.0 - 1.0, 1.0, 1.0);
viewpos.x /= ProjX;
viewpos.y = -viewpos.y / ProjY;
FragRay = (ViewToWorld * viewpos).xyz - CameraPos;
gl_Position = vec4(positions[gl_VertexIndex] * 2.0 - 1.0, 1.0, 1.0);
}
)glsl";

View file

@ -0,0 +1,165 @@
#include "gpuraytracer.h"
#include "vk_renderdevice.h"
#include "vk_levelmesh.h"
#include "vk_lightmapper.h"
#include "renderdoc_app.h"
#include "doom_levelmesh.h"
#include "levelmeshviewer.h"
#ifndef _WIN32
#include <dlfcn.h>
#endif
static RENDERDOC_API_1_4_2* rdoc_api;
extern bool showviewer;
GPURaytracer::GPURaytracer()
{
LoadRenderDoc();
if (showviewer)
mViewer = std::make_unique<LevelMeshViewer>();
mDevice = std::make_unique<VulkanRenderDevice>(mViewer.get());
PrintVulkanInfo();
}
GPURaytracer::~GPURaytracer()
{
}
void GPURaytracer::Raytrace(DoomLevelMesh* mesh)
{
if (rdoc_api) rdoc_api->StartFrameCapture(nullptr, nullptr);
#ifdef WIN32
LARGE_INTEGER s;
QueryPerformanceCounter(&s);
#endif
try
{
auto levelmesh = mDevice->GetLevelMesh();
auto lightmapper = mDevice->GetLightmapper();
printf(" Map uses %u lightmap textures\n", mesh->LMTextureCount);
mDevice->GetTextureManager()->CreateLightmap(mesh->LMTextureSize, mesh->LMTextureCount);
levelmesh->SetLevelMesh(mesh);
lightmapper->SetLevelMesh(mesh);
// Keep baking until all surfaces have been processed
while (true)
{
levelmesh->BeginFrame();
lightmapper->BeginFrame();
mDevice->GetDescriptorSetManager()->UpdateBindlessDescriptorSet();
TArray<LightmapTile*> tiles;
for (unsigned int i = 0, count = mesh->LightmapTiles.Size(); i < count; i++)
{
LightmapTile* tile = &mesh->LightmapTiles[i];
if (tile->NeedsUpdate)
{
tiles.Push(tile);
}
}
if (tiles.Size() == 0)
break;
printf(" Ray tracing tiles: %u / %u\r", mesh->LightmapTiles.Size() - tiles.Size(), mesh->LightmapTiles.Size());
lightmapper->Raytrace(tiles);
mDevice->GetCommands()->SubmitAndWait();
}
printf(" Ray tracing tiles: %u / %u\n", mesh->LightmapTiles.Size(), mesh->LightmapTiles.Size());
mesh->LMTextureData.Resize(mesh->LMTextureSize * mesh->LMTextureSize * mesh->LMTextureCount * 4);
for (int arrayIndex = 0; arrayIndex < mesh->LMTextureCount; arrayIndex++)
{
mDevice->GetTextureManager()->DownloadLightmap(arrayIndex, mesh->LMTextureData.Data() + arrayIndex * mesh->LMTextureSize * mesh->LMTextureSize * 4);
}
if (mViewer)
mViewer->Exec(mDevice.get(), mesh->SunDirection, mesh->SunColor, 1.0f);
}
catch (...)
{
printf("\n");
throw;
}
#ifdef WIN32
LARGE_INTEGER e, f;
QueryPerformanceCounter(&e);
QueryPerformanceFrequency(&f);
printf(" GPU ray tracing time was %.3f seconds.\n", double(e.QuadPart - s.QuadPart) / double(f.QuadPart));
#endif
printf(" Ray trace complete\n");
if (rdoc_api) rdoc_api->EndFrameCapture(nullptr, nullptr);
}
void GPURaytracer::PrintVulkanInfo()
{
const auto& props = mDevice->GetDevice()->PhysicalDevice.Properties.Properties;
std::string deviceType;
switch (props.deviceType)
{
case VK_PHYSICAL_DEVICE_TYPE_OTHER: deviceType = "other"; break;
case VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU: deviceType = "integrated gpu"; break;
case VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU: deviceType = "discrete gpu"; break;
case VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU: deviceType = "virtual gpu"; break;
case VK_PHYSICAL_DEVICE_TYPE_CPU: deviceType = "cpu"; break;
default: deviceType = std::to_string(props.deviceType); break;
}
std::string apiVersion = std::to_string(VK_VERSION_MAJOR(props.apiVersion)) + "." + std::to_string(VK_VERSION_MINOR(props.apiVersion)) + "." + std::to_string(VK_VERSION_PATCH(props.apiVersion));
std::string driverVersion = std::to_string(VK_VERSION_MAJOR(props.driverVersion)) + "." + std::to_string(VK_VERSION_MINOR(props.driverVersion)) + "." + std::to_string(VK_VERSION_PATCH(props.driverVersion));
printf(" Vulkan device: %s\n", props.deviceName);
printf(" Vulkan device type: %s\n", deviceType.c_str());
printf(" Vulkan version: %s (api) %s (driver)\n", apiVersion.c_str(), driverVersion.c_str());
}
void GPURaytracer::LoadRenderDoc()
{
if (rdoc_api)
return;
#ifdef _WIN32
if (auto mod = GetModuleHandleA("renderdoc.dll"))
{
pRENDERDOC_GetAPI RENDERDOC_GetAPI = (pRENDERDOC_GetAPI)GetProcAddress(mod, "RENDERDOC_GetAPI");
int ret = RENDERDOC_GetAPI(eRENDERDOC_API_Version_1_4_2, (void**)&rdoc_api);
assert(ret == 1);
if (ret != 1)
{
printf(" RENDERDOC_GetAPI returned %d\n", ret);
}
}
#else
if (void* mod = dlopen("librenderdoc.so", RTLD_NOW | RTLD_NOLOAD))
{
pRENDERDOC_GetAPI RENDERDOC_GetAPI = (pRENDERDOC_GetAPI)dlsym(mod, "RENDERDOC_GetAPI");
int ret = RENDERDOC_GetAPI(eRENDERDOC_API_Version_1_4_2, (void**)&rdoc_api);
assert(ret == 1);
if (ret != 1)
{
printf(" RENDERDOC_GetAPI returned %d\n", ret);
}
}
#endif
if (rdoc_api)
{
printf(" RenderDoc enabled\n");
}
}

View file

@ -0,0 +1,22 @@
#pragma once
#include "vk_renderdevice.h"
class DoomLevelMesh;
class LevelMeshViewer;
class GPURaytracer
{
public:
GPURaytracer();
~GPURaytracer();
void Raytrace(DoomLevelMesh* levelMesh);
private:
void PrintVulkanInfo();
void LoadRenderDoc();
std::unique_ptr<LevelMeshViewer> mViewer;
std::unique_ptr<VulkanRenderDevice> mDevice;
};

View file

@ -1,5 +1,5 @@
/*
** ZDRay collision
** Level mesh collision detection
** Copyright (c) 2018 Magnus Norddahl
**
** This software is provided 'as-is', without any express or implied
@ -20,15 +20,16 @@
**
*/
#include "collision.h"
#include "hw_collision.h"
#include <algorithm>
#include <functional>
#include <cfloat>
#include <cstdint>
#ifndef NO_SSE
#include <immintrin.h>
#endif
TriangleMeshShape::TriangleMeshShape(const vec3 *vertices, int num_vertices, const unsigned int *elements, int num_elements)
TriangleMeshShape::TriangleMeshShape(const FFlatVertex *vertices, int num_vertices, const unsigned int *elements, int num_elements)
: vertices(vertices), num_vertices(num_vertices), elements(elements), num_elements(num_elements)
{
int num_triangles = num_elements / 3;
@ -36,7 +37,7 @@ TriangleMeshShape::TriangleMeshShape(const vec3 *vertices, int num_vertices, con
return;
std::vector<int> triangles;
std::vector<vec3> centroids;
std::vector<FVector3> centroids;
triangles.reserve(num_triangles);
centroids.reserve(num_triangles);
for (int i = 0; i < num_triangles; i++)
@ -44,7 +45,7 @@ TriangleMeshShape::TriangleMeshShape(const vec3 *vertices, int num_vertices, con
triangles.push_back(i);
int element_index = i * 3;
vec3 centroid = (vertices[elements[element_index + 0]] + vertices[elements[element_index + 1]] + vertices[elements[element_index + 2]]) * (1.0f / 3.0f);
FVector3 centroid = (vertices[elements[element_index + 0]].fPos() + vertices[elements[element_index + 1]].fPos() + vertices[elements[element_index + 2]].fPos()) * (1.0f / 3.0f);
centroids.push_back(centroid);
}
@ -53,41 +54,54 @@ TriangleMeshShape::TriangleMeshShape(const vec3 *vertices, int num_vertices, con
root = subdivide(&triangles[0], (int)triangles.size(), &centroids[0], &work_buffer[0]);
}
float TriangleMeshShape::sweep(TriangleMeshShape *shape1, SphereShape *shape2, const vec3 &target)
float TriangleMeshShape::sweep(TriangleMeshShape *shape1, SphereShape *shape2, const FVector3 &target)
{
if (shape1->root == -1)
return 1.0f;
return sweep(shape1, shape2, shape1->root, target);
}
bool TriangleMeshShape::find_any_hit(TriangleMeshShape *shape1, TriangleMeshShape *shape2)
{
if (shape1->root == -1)
return false;
return find_any_hit(shape1, shape2, shape1->root, shape2->root);
}
bool TriangleMeshShape::find_any_hit(TriangleMeshShape *shape1, SphereShape *shape2)
{
if (shape1->root == -1)
return false;
return find_any_hit(shape1, shape2, shape1->root);
}
std::vector<int> TriangleMeshShape::find_all_hits(TriangleMeshShape* shape1, SphereShape* shape2)
{
std::vector<int> hits;
find_all_hits(shape1, shape2, shape1->root, hits);
if (shape1->root != -1)
find_all_hits(shape1, shape2, shape1->root, hits);
return hits;
}
bool TriangleMeshShape::find_any_hit(TriangleMeshShape *shape, const vec3 &ray_start, const vec3 &ray_end)
bool TriangleMeshShape::find_any_hit(TriangleMeshShape *shape, const FVector3 &ray_start, const FVector3 &ray_end)
{
if (shape->root == -1)
return false;
return find_any_hit(shape, RayBBox(ray_start, ray_end), shape->root);
}
TraceHit TriangleMeshShape::find_first_hit(TriangleMeshShape *shape, const vec3 &ray_start, const vec3 &ray_end)
TraceHit TriangleMeshShape::find_first_hit(TriangleMeshShape *shape, const FVector3 &ray_start, const FVector3 &ray_end)
{
TraceHit hit;
if (shape->root == -1)
return hit;
// Perform segmented tracing to keep the ray AABB box smaller
vec3 ray_dir = ray_end - ray_start;
float tracedist = length(ray_dir);
FVector3 ray_dir = ray_end - ray_start;
float tracedist = (float)ray_dir.Length();
float segmentlen = std::max(100.0f, tracedist / 20.0f);
for (float t = 0.0f; t < tracedist; t += segmentlen)
{
@ -105,7 +119,7 @@ TraceHit TriangleMeshShape::find_first_hit(TriangleMeshShape *shape, const vec3
return hit;
}
float TriangleMeshShape::sweep(TriangleMeshShape *shape1, SphereShape *shape2, int a, const vec3 &target)
float TriangleMeshShape::sweep(TriangleMeshShape *shape1, SphereShape *shape2, int a, const FVector3 &target)
{
if (sweep_overlap_bv_sphere(shape1, shape2, a, target))
{
@ -265,24 +279,24 @@ float TriangleMeshShape::intersect_triangle_ray(TriangleMeshShape *shape, const
{
const int start_element = shape->nodes[a].element_index;
vec3 p[3] =
FVector3 p[3] =
{
shape->vertices[shape->elements[start_element]],
shape->vertices[shape->elements[start_element + 1]],
shape->vertices[shape->elements[start_element + 2]]
shape->vertices[shape->elements[start_element]].fPos(),
shape->vertices[shape->elements[start_element + 1]].fPos(),
shape->vertices[shape->elements[start_element + 2]].fPos()
};
// Moeller–Trumbore ray-triangle intersection algorithm:
vec3 D = ray.end - ray.start;
FVector3 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];
FVector3 e1 = p[1] - p[0];
FVector3 e2 = p[2] - p[0];
// Begin calculating determinant - also used to calculate u parameter
vec3 P = cross(D, e2);
float det = dot(e1, P);
FVector3 P = D ^ e2; // cross(D, e2);
float det = e1 | P; // dot(e1, P);
// Backface check
//if (det < 0.0f)
@ -295,26 +309,26 @@ float TriangleMeshShape::intersect_triangle_ray(TriangleMeshShape *shape, const
float inv_det = 1.0f / det;
// Calculate distance from p[0] to ray origin
vec3 T = ray.start - p[0];
FVector3 T = ray.start - p[0];
// Calculate u parameter and test bound
float u = dot(T, P) * inv_det;
float u = (T | P) * inv_det; // 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);
FVector3 Q = T ^ e1; // cross(T, e1);
// Calculate V parameter and test bound
float v = dot(D, Q) * inv_det;
float v = (D | Q) * inv_det; // 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;
float t = (e2 | Q) * inv_det; //dot(e2, Q) * inv_det;
if (t <= FLT_EPSILON)
return 1.0f;
@ -325,40 +339,42 @@ float TriangleMeshShape::intersect_triangle_ray(TriangleMeshShape *shape, const
return t;
}
bool TriangleMeshShape::sweep_overlap_bv_sphere(TriangleMeshShape *shape1, SphereShape *shape2, int a, const vec3 &target)
bool TriangleMeshShape::sweep_overlap_bv_sphere(TriangleMeshShape *shape1, SphereShape *shape2, int a, const FVector3 &target)
{
// Convert to ray test by expanding the AABB:
CollisionBBox aabb = shape1->nodes[a].aabb;
aabb.Extents += shape2->radius;
aabb.Extents.X += shape2->radius;
aabb.Extents.Y += shape2->radius;
aabb.Extents.Z += shape2->radius;
return IntersectionTest::ray_aabb(RayBBox(shape2->center, target), aabb) == IntersectionTest::overlap;
}
float TriangleMeshShape::sweep_intersect_triangle_sphere(TriangleMeshShape *shape1, SphereShape *shape2, int a, const vec3 &target)
float TriangleMeshShape::sweep_intersect_triangle_sphere(TriangleMeshShape *shape1, SphereShape *shape2, int a, const FVector3 &target)
{
const int start_element = shape1->nodes[a].element_index;
vec3 p[3] =
FVector3 p[3] =
{
shape1->vertices[shape1->elements[start_element]],
shape1->vertices[shape1->elements[start_element + 1]],
shape1->vertices[shape1->elements[start_element + 2]]
shape1->vertices[shape1->elements[start_element]].fPos(),
shape1->vertices[shape1->elements[start_element + 1]].fPos(),
shape1->vertices[shape1->elements[start_element + 2]].fPos()
};
vec3 c = shape2->center;
vec3 e = target;
FVector3 c = shape2->center;
FVector3 e = target;
float r = shape2->radius;
// Dynamic intersection test between a ray and the minkowski sum of the sphere and polygon:
vec3 n = normalize(cross(p[1] - p[0], p[2] - p[0]));
vec4 plane(n, -dot(n, p[0]));
FVector3 n = ((p[1] - p[0]) ^ (p[2] - p[0])).Unit(); // normalize(cross(p[1] - p[0], p[2] - p[0]));
FVector4 plane(n, -(n | p[0])); // plane(n, -dot(n, p[0]));
// Step 1: Plane intersect test
float sc = dot(plane, vec4(c, 1.0f));
float se = dot(plane, vec4(e, 1.0f));
float sc = (plane | FVector4(c, 1.0f)); // dot(plane, FVector4(c, 1.0f));
float se = (plane | FVector4(e, 1.0f)); // dot(plane, FVector4(e, 1.0f));
bool same_side = sc * se > 0.0f;
if (same_side && std::abs(sc) > r && std::abs(se) > r)
@ -368,29 +384,29 @@ float TriangleMeshShape::sweep_intersect_triangle_sphere(TriangleMeshShape *shap
{
float t = (sc - r) / (sc - se);
vec3 vt = c + (e - c) * t;
FVector3 vt = c + (e - c) * t;
vec3 u0 = p[1] - p[0];
vec3 u1 = p[2] - p[0];
FVector3 u0 = p[1] - p[0];
FVector3 u1 = p[2] - p[0];
vec2 v_2d[3] =
FVector2 v_2d[3] =
{
vec2(0.0f, 0.0f),
vec2(dot(u0, u0), 0.0f),
vec2(0.0f, dot(u1, u1))
FVector2(0.0f, 0.0f),
FVector2((u0 | u0), 0.0f), // FVector2(dot(u0, u0), 0.0f),
FVector2(0.0f, (u1 | u1)) // FVector2(0.0f, dot(u1, u1))
};
vec2 point(dot(u0, vt), dot(u1, vt));
FVector2 point((u0 | vt), (u1 | vt)); // point(dot(u0, vt), dot(u1, vt));
bool inside = false;
vec2 e0 = v_2d[2];
bool y0 = e0.y >= point.y;
FVector2 e0 = v_2d[2];
bool y0 = e0.Y >= point.Y;
for (int i = 0; i < 3; i++)
{
vec2 e1 = v_2d[i];
bool y1 = e1.y >= point.y;
FVector2 e1 = v_2d[i];
bool y1 = e1.Y >= point.Y;
if (y0 != y1 && ((e1.y - point.y) * (e0.x - e1.x) >= (e1.x - point.x) * (e0.y - e1.y)) == y1)
if (y0 != y1 && ((e1.Y - point.Y) * (e0.X - e1.X) >= (e1.X - point.X) * (e0.Y - e1.Y)) == y1)
inside = !inside;
y0 = y1;
@ -403,21 +419,21 @@ float TriangleMeshShape::sweep_intersect_triangle_sphere(TriangleMeshShape *shap
// Step 2: Edge intersect test
vec3 ke[3] =
FVector3 ke[3] =
{
p[1] - p[0],
p[2] - p[1],
p[0] - p[2],
};
vec3 kg[3] =
FVector3 kg[3] =
{
p[0] - c,
p[1] - c,
p[2] - c,
};
vec3 ks = e - c;
FVector3 ks = e - c;
float kgg[3];
float kgs[3];
@ -425,12 +441,12 @@ float TriangleMeshShape::sweep_intersect_triangle_sphere(TriangleMeshShape *shap
for (int i = 0; i < 3; i++)
{
float kee = dot(ke[i], ke[i]);
float keg = dot(ke[i], kg[i]);
float kes = dot(ke[i], ks);
kgg[i] = dot(kg[i], kg[i]);
kgs[i] = dot(kg[i], ks);
kss[i] = dot(ks, ks);
float kee = (ke[i] | ke[i]); // dot(ke[i], ke[i]);
float keg = (ke[i] | kg[i]); // dot(ke[i], kg[i]);
float kes = (ke[i] | ks); // dot(ke[i], ks);
kgg[i] = (kg[i] | kg[i]); // dot(kg[i], kg[i]);
kgs[i] = (kg[i] | ks); // dot(kg[i], ks);
kss[i] = (ks | ks); // dot(ks, ks);
float aa = kee * kss[i] - kes * kes;
float bb = 2 * (keg * kes - kee * kgs[i]);
@ -451,8 +467,8 @@ float TriangleMeshShape::sweep_intersect_triangle_sphere(TriangleMeshShape *shap
if (t >= 0.0f && t <= 1.0f)
{
vec3 ct = c + ks * t;
float d = dot(ct - p[i], ke[i]);
FVector3 ct = c + ks * t;
float d = ((ct - p[i]) | ke[i]); // dot(ct - p[i], ke[i]);
if (d >= 0.0f && d <= kee)
return t;
}
@ -512,49 +528,49 @@ bool TriangleMeshShape::overlap_triangle_sphere(TriangleMeshShape *shape1, Spher
int element_index = shape1->nodes[shape1_node_index].element_index;
vec3 P = shape2->center;
vec3 A = shape1->vertices[shape1->elements[element_index]] - P;
vec3 B = shape1->vertices[shape1->elements[element_index + 1]] - P;
vec3 C = shape1->vertices[shape1->elements[element_index + 2]] - P;
FVector3 P = shape2->center;
FVector3 A = shape1->vertices[shape1->elements[element_index]].fPos() - P;
FVector3 B = shape1->vertices[shape1->elements[element_index + 1]].fPos() - P;
FVector3 C = shape1->vertices[shape1->elements[element_index + 2]].fPos() - P;
float r = shape2->radius;
float rr = r * r;
// Testing if sphere lies outside the triangle plane
vec3 V = cross(B - A, C - A);
float d = dot(A, V);
float e = dot(V, V);
FVector3 V = ((B - A) ^ (C - A)); // cross(B - A, C - A);
float d = A | V; // dot(A, V);
float e = V | V; // dot(V, V);
bool sep1 = d * d > rr * e;
// Testing if sphere lies outside a triangle vertex
float aa = dot(A, A);
float ab = dot(A, B);
float ac = dot(A, C);
float bb = dot(B, B);
float bc = dot(B, C);
float cc = dot(C, C);
float aa = A | A; // dot(A, A);
float ab = A | B; // dot(A, B);
float ac = A | C; // dot(A, C);
float bb = B | B; // dot(B, B);
float bc = B | C; // dot(B, C);
float cc = C | C; // dot(C, C);
bool sep2 = (aa > rr) && (ab > aa) && (ac > aa);
bool sep3 = (bb > rr) && (ab > bb) && (bc > bb);
bool sep4 = (cc > rr) && (ac > cc) && (bc > cc);
// Testing if sphere lies outside a triangle edge
vec3 AB = B - A;
vec3 BC = C - B;
vec3 CA = A - C;
FVector3 AB = B - A;
FVector3 BC = C - B;
FVector3 CA = A - C;
float d1 = ab - aa;
float d2 = bc - bb;
float d3 = ac - cc;
float e1 = dot(AB, AB);
float e2 = dot(BC, BC);
float e3 = dot(CA, CA);
vec3 Q1 = A * e1 - AB * d1;
vec3 Q2 = B * e2 - BC * d2;
vec3 Q3 = C * e3 - CA * d3;
vec3 QC = C * e1 - Q1;
vec3 QA = A * e2 - Q2;
vec3 QB = B * e3 - Q3;
bool sep5 = (dot(Q1, Q1) > rr * e1 * e1) && (dot(Q1, QC) > 0.0f);
bool sep6 = (dot(Q2, Q2) > rr * e2 * e2) && (dot(Q2, QA) > 0.0f);
bool sep7 = (dot(Q3, Q3) > rr * e3 * e3) && (dot(Q3, QB) > 0.0f);
float e1 = (AB | AB); // dot(AB, AB)
float e2 = (BC | BC); // dot(BC, BC)
float e3 = (CA | CA); // dot(CA, CA)
FVector3 Q1 = A * e1 - AB * d1;
FVector3 Q2 = B * e2 - BC * d2;
FVector3 Q3 = C * e3 - CA * d3;
FVector3 QC = C * e1 - Q1;
FVector3 QA = A * e2 - Q2;
FVector3 QB = B * e3 - Q3;
bool sep5 = ((Q1 | Q1) > rr * e1 * e1) && ((Q1 | QC) > 0.0f); // (dot(Q1, Q1) > rr * e1 * e1) && (dot(Q1, QC) > 0.0f);
bool sep6 = ((Q2 | Q2) > rr * e2 * e2) && ((Q2 | QA) > 0.0f); // (dot(Q2, Q2) > rr * e2 * e2) && (dot(Q2, QA) > 0.0f);
bool sep7 = ((Q3 | Q3) > rr * e3 * e3) && ((Q3 | QB) > 0.0f); // (dot(Q3, Q3) > rr * e3 * e3) && (dot(Q3, QB) > 0.0f);
bool separated = sep1 || sep2 || sep3 || sep4 || sep5 || sep6 || sep7;
return (!separated);
@ -567,8 +583,8 @@ bool TriangleMeshShape::is_leaf(int node_index)
float TriangleMeshShape::volume(int node_index)
{
const vec3 &extents = nodes[node_index].aabb.Extents;
return extents.x * extents.y * extents.z;
const FVector3 &extents = nodes[node_index].aabb.Extents;
return extents.X * extents.Y * extents.Z;
}
int TriangleMeshShape::get_min_depth() const
@ -617,30 +633,30 @@ float TriangleMeshShape::get_balanced_depth() const
return std::log2((float)(num_elements / 3));
}
int TriangleMeshShape::subdivide(int *triangles, int num_triangles, const vec3 *centroids, int *work_buffer)
int TriangleMeshShape::subdivide(int *triangles, int num_triangles, const FVector3 *centroids, int *work_buffer)
{
if (num_triangles == 0)
return -1;
// Find bounding box and median of the triangle centroids
vec3 median;
vec3 min, max;
min = vertices[elements[triangles[0] * 3]];
FVector3 median;
FVector3 min, max;
min = vertices[elements[triangles[0] * 3]].fPos();
max = min;
for (int i = 0; i < num_triangles; i++)
{
int element_index = triangles[i] * 3;
for (int j = 0; j < 3; j++)
{
const vec3 &vertex = vertices[elements[element_index + j]];
const FVector3 &vertex = vertices[elements[element_index + j]].fPos();
min.x = std::min(min.x, vertex.x);
min.y = std::min(min.y, vertex.y);
min.z = std::min(min.z, vertex.z);
min.X = std::min(min.X, vertex.X);
min.Y = std::min(min.Y, vertex.Y);
min.Z = std::min(min.Z, vertex.Z);
max.x = std::max(max.x, vertex.x);
max.y = std::max(max.y, vertex.y);
max.z = std::max(max.z, vertex.z);
max.X = std::max(max.X, vertex.X);
max.Y = std::max(max.Y, vertex.Y);
max.Z = std::max(max.Z, vertex.Z);
}
median += centroids[triangles[i]];
@ -656,9 +672,9 @@ int TriangleMeshShape::subdivide(int *triangles, int num_triangles, const vec3 *
// Find the longest axis
float axis_lengths[3] =
{
max.x - min.x,
max.y - min.y,
max.z - min.z
max.X - min.X,
max.Y - min.Y,
max.Z - min.Z
};
int axis_order[3] = { 0, 1, 2 };
@ -666,18 +682,18 @@ int TriangleMeshShape::subdivide(int *triangles, int num_triangles, const vec3 *
// Try split at longest axis, then if that fails the next longest, and then the remaining one
int left_count, right_count;
vec3 axis;
FVector3 axis;
for (int attempt = 0; attempt < 3; attempt++)
{
// Find the split plane for axis
switch (axis_order[attempt])
{
default:
case 0: axis = vec3(1.0f, 0.0f, 0.0f); break;
case 1: axis = vec3(0.0f, 1.0f, 0.0f); break;
case 2: axis = vec3(0.0f, 0.0f, 1.0f); break;
case 0: axis = FVector3(1.0f, 0.0f, 0.0f); break;
case 1: axis = FVector3(0.0f, 1.0f, 0.0f); break;
case 2: axis = FVector3(0.0f, 0.0f, 1.0f); break;
}
vec4 plane(axis, -dot(median, axis));
FVector4 plane(axis, -(median | axis)); // plane(axis, -dot(median, axis));
// Split triangles into two
left_count = 0;
@ -687,7 +703,7 @@ int TriangleMeshShape::subdivide(int *triangles, int num_triangles, const vec3 *
int triangle = triangles[i];
int element_index = triangle * 3;
float side = dot(vec4(centroids[triangles[i]], 1.0f), plane);
float side = (FVector4(centroids[triangles[i]], 1.0f) | plane); // dot(FVector4(centroids[triangles[i]], 1.0f), plane);
if (side >= 0.0f)
{
work_buffer[left_count] = triangle;
@ -733,68 +749,29 @@ int TriangleMeshShape::subdivide(int *triangles, int num_triangles, const vec3 *
/////////////////////////////////////////////////////////////////////////////
IntersectionTest::Result IntersectionTest::plane_aabb(const vec4 &plane, const BBox &aabb)
IntersectionTest::OverlapResult IntersectionTest::sphere_aabb(const FVector3 &center, float radius, const CollisionBBox &aabb)
{
vec3 center = aabb.Center();
vec3 extents = aabb.Extents();
float e = extents.x * std::abs(plane.x) + extents.y * std::abs(plane.y) + extents.z * std::abs(plane.z);
float s = center.x * plane.x + center.y * plane.y + center.z * plane.z + plane.w;
if (s - e > 0)
return inside;
else if (s + e < 0)
return outside;
else
return intersecting;
}
IntersectionTest::Result IntersectionTest::plane_obb(const vec4 &plane, const OrientedBBox &obb)
{
vec3 n = plane.xyz();
float d = plane.w;
float e = obb.Extents.x * std::abs(dot(obb.axis_x, n)) + obb.Extents.y * std::abs(dot(obb.axis_y, n)) + obb.Extents.z * std::abs(dot(obb.axis_z, n));
float s = dot(obb.Center, n) + d;
if (s - e > 0)
return inside;
else if (s + e < 0)
return outside;
else
return intersecting;
}
IntersectionTest::OverlapResult IntersectionTest::sphere(const vec3 &center1, float radius1, const vec3 &center2, float radius2)
{
vec3 h = center1 - center2;
float square_distance = dot(h, h);
float radius_sum = radius1 + radius2;
if (square_distance > radius_sum * radius_sum)
return disjoint;
else
return overlap;
}
IntersectionTest::OverlapResult IntersectionTest::sphere_aabb(const vec3 &center, float radius, const BBox &aabb)
{
vec3 a = aabb.min - center;
vec3 b = center - aabb.max;
a.x = std::max(a.x, 0.0f);
a.y = std::max(a.y, 0.0f);
a.z = std::max(a.z, 0.0f);
b.x = std::max(b.x, 0.0f);
b.y = std::max(b.y, 0.0f);
b.z = std::max(b.z, 0.0f);
vec3 e = a + b;
float d = dot(e, e);
FVector3 a = aabb.min - center;
FVector3 b = center - aabb.max;
a.X = std::max(a.X, 0.0f);
a.Y = std::max(a.Y, 0.0f);
a.Z = std::max(a.Z, 0.0f);
b.X = std::max(b.X, 0.0f);
b.Y = std::max(b.Y, 0.0f);
b.Z = std::max(b.Z, 0.0f);
FVector3 e = a + b;
float d = (e | e); // dot(e, e);
if (d > radius * radius)
return disjoint;
else
return overlap;
}
IntersectionTest::OverlapResult IntersectionTest::aabb(const BBox &a, const BBox &b)
IntersectionTest::OverlapResult IntersectionTest::aabb(const CollisionBBox& a, const CollisionBBox& b)
{
if (a.min.x > b.max.x || b.min.x > a.max.x ||
a.min.y > b.max.y || b.min.y > a.max.y ||
a.min.z > b.max.z || b.min.z > a.max.z)
if (a.min.X > b.max.X || b.min.X > a.max.X ||
a.min.Y > b.max.Y || b.min.Y > a.max.Y ||
a.min.Z > b.max.Z || b.min.Z > a.max.Z)
{
return disjoint;
}
@ -804,51 +781,16 @@ IntersectionTest::OverlapResult IntersectionTest::aabb(const BBox &a, const BBox
}
}
IntersectionTest::Result IntersectionTest::frustum_aabb(const FrustumPlanes &frustum, const BBox &box)
{
bool is_intersecting = false;
for (int i = 0; i < 6; i++)
{
Result result = plane_aabb(frustum.planes[i], box);
if (result == outside)
return outside;
else if (result == intersecting)
is_intersecting = true;
break;
}
if (is_intersecting)
return intersecting;
else
return inside;
}
IntersectionTest::Result IntersectionTest::frustum_obb(const FrustumPlanes &frustum, const OrientedBBox &box)
{
bool is_intersecting = false;
for (int i = 0; i < 6; i++)
{
Result result = plane_obb(frustum.planes[i], box);
if (result == outside)
return outside;
else if (result == intersecting)
is_intersecting = true;
}
if (is_intersecting)
return intersecting;
else
return inside;
}
static const uint32_t clearsignbitmask[] = { 0x7fffffff, 0x7fffffff, 0x7fffffff, 0x7fffffff };
IntersectionTest::OverlapResult IntersectionTest::ray_aabb(const RayBBox &ray, const CollisionBBox &aabb)
{
#ifndef NO_SSE
__m128 v = _mm_loadu_ps(&ray.v.x);
__m128 w = _mm_loadu_ps(&ray.w.x);
__m128 h = _mm_loadu_ps(&aabb.Extents.x);
__m128 c = _mm_sub_ps(_mm_loadu_ps(&ray.c.x), _mm_loadu_ps(&aabb.Center.x));
__m128 v = _mm_loadu_ps(&ray.v.X);
__m128 w = _mm_loadu_ps(&ray.w.X);
__m128 h = _mm_loadu_ps(&aabb.Extents.X);
__m128 c = _mm_sub_ps(_mm_loadu_ps(&ray.c.X), _mm_loadu_ps(&aabb.Center.X));
__m128 clearsignbit = _mm_loadu_ps(reinterpret_cast<const float*>(clearsignbitmask));
@ -857,117 +799,35 @@ IntersectionTest::OverlapResult IntersectionTest::ray_aabb(const RayBBox &ray, c
if (mask & 7)
return disjoint;
__m128 c1 = _mm_shuffle_ps(c, c, _MM_SHUFFLE(3, 0, 0, 1)); // c.y, c.x, c.x
__m128 c2 = _mm_shuffle_ps(c, c, _MM_SHUFFLE(3, 1, 2, 2)); // c.z, c.z, c.y
__m128 w1 = _mm_shuffle_ps(w, w, _MM_SHUFFLE(3, 1, 2, 2)); // w.z, w.z, w.y
__m128 w2 = _mm_shuffle_ps(w, w, _MM_SHUFFLE(3, 0, 0, 1)); // w.y, w.x, w.x
__m128 c1 = _mm_shuffle_ps(c, c, _MM_SHUFFLE(3, 0, 0, 1)); // c.Y, c.X, c.X
__m128 c2 = _mm_shuffle_ps(c, c, _MM_SHUFFLE(3, 1, 2, 2)); // c.Z, c.Z, c.Y
__m128 w1 = _mm_shuffle_ps(w, w, _MM_SHUFFLE(3, 1, 2, 2)); // w.Z, w.Z, w.Y
__m128 w2 = _mm_shuffle_ps(w, w, _MM_SHUFFLE(3, 0, 0, 1)); // w.Y, w.X, w.X
__m128 lhs = _mm_and_ps(_mm_sub_ps(_mm_mul_ps(c1, w1), _mm_mul_ps(c2, w2)), clearsignbit);
__m128 h1 = _mm_shuffle_ps(h, h, _MM_SHUFFLE(3, 0, 0, 1)); // h.y, h.x, h.x
__m128 h2 = _mm_shuffle_ps(h, h, _MM_SHUFFLE(3, 1, 2, 2)); // h.z, h.z, h.y
__m128 v1 = _mm_shuffle_ps(v, v, _MM_SHUFFLE(3, 1, 2, 2)); // v.z, v.z, v.y
__m128 v2 = _mm_shuffle_ps(v, v, _MM_SHUFFLE(3, 0, 0, 1)); // v.y, v.x, v.x
__m128 h1 = _mm_shuffle_ps(h, h, _MM_SHUFFLE(3, 0, 0, 1)); // h.Y, h.X, h.X
__m128 h2 = _mm_shuffle_ps(h, h, _MM_SHUFFLE(3, 1, 2, 2)); // h.Z, h.Z, h.Y
__m128 v1 = _mm_shuffle_ps(v, v, _MM_SHUFFLE(3, 1, 2, 2)); // v.Z, v.Z, v.Y
__m128 v2 = _mm_shuffle_ps(v, v, _MM_SHUFFLE(3, 0, 0, 1)); // v.Y, v.X, v.X
__m128 rhs = _mm_add_ps(_mm_mul_ps(h1, v1), _mm_mul_ps(h2, v2));
mask = _mm_movemask_ps(_mm_cmpgt_ps(lhs, rhs));
return (mask & 7) ? disjoint : overlap;
#else
const vec3 &v = ray.v;
const vec3 &w = ray.w;
const vec3 &h = aabb.Extents;
const FVector3 &v = ray.v;
const FVector3 &w = ray.w;
const FVector3 &h = aabb.Extents;
auto c = ray.c - aabb.Center;
if (std::abs(c.x) > v.x + h.x || std::abs(c.y) > v.y + h.y || std::abs(c.z) > v.z + h.z)
if (std::abs(c.X) > v.X + h.X || std::abs(c.Y) > v.Y + h.Y || std::abs(c.Z) > v.Z + h.Z)
return disjoint;
if (std::abs(c.y * w.z - c.z * w.y) > h.y * v.z + h.z * v.y ||
std::abs(c.x * w.z - c.z * w.x) > h.x * v.z + h.z * v.x ||
std::abs(c.x * w.y - c.y * w.x) > h.x * v.y + h.y * v.x)
if (std::abs(c.Y * w.Z - c.Z * w.Y) > h.Y * v.Z + h.Z * v.Y ||
std::abs(c.X * w.Z - c.Z * w.X) > h.X * v.Z + h.Z * v.X ||
std::abs(c.X * w.Y - c.Y * w.X) > h.X * v.Y + h.Y * v.X)
return disjoint;
return overlap;
#endif
}
/////////////////////////////////////////////////////////////////////////////
FrustumPlanes::FrustumPlanes()
{
}
FrustumPlanes::FrustumPlanes(const mat4 &world_to_projection)
{
planes[0] = near_frustum_plane(world_to_projection);
planes[1] = far_frustum_plane(world_to_projection);
planes[2] = left_frustum_plane(world_to_projection);
planes[3] = right_frustum_plane(world_to_projection);
planes[4] = top_frustum_plane(world_to_projection);
planes[5] = bottom_frustum_plane(world_to_projection);
}
vec4 FrustumPlanes::left_frustum_plane(const mat4 &matrix)
{
vec4 plane(
matrix[3 + 0 * 4] + matrix[0 + 0 * 4],
matrix[3 + 1 * 4] + matrix[0 + 1 * 4],
matrix[3 + 2 * 4] + matrix[0 + 2 * 4],
matrix[3 + 3 * 4] + matrix[0 + 3 * 4]);
plane /= length(plane.xyz());
return plane;
}
vec4 FrustumPlanes::right_frustum_plane(const mat4 &matrix)
{
vec4 plane(
matrix[3 + 0 * 4] - matrix[0 + 0 * 4],
matrix[3 + 1 * 4] - matrix[0 + 1 * 4],
matrix[3 + 2 * 4] - matrix[0 + 2 * 4],
matrix[3 + 3 * 4] - matrix[0 + 3 * 4]);
plane /= length(plane.xyz());
return plane;
}
vec4 FrustumPlanes::top_frustum_plane(const mat4 &matrix)
{
vec4 plane(
matrix[3 + 0 * 4] - matrix[1 + 0 * 4],
matrix[3 + 1 * 4] - matrix[1 + 1 * 4],
matrix[3 + 2 * 4] - matrix[1 + 2 * 4],
matrix[3 + 3 * 4] - matrix[1 + 3 * 4]);
plane /= length(plane.xyz());
return plane;
}
vec4 FrustumPlanes::bottom_frustum_plane(const mat4 &matrix)
{
vec4 plane(
matrix[3 + 0 * 4] + matrix[1 + 0 * 4],
matrix[3 + 1 * 4] + matrix[1 + 1 * 4],
matrix[3 + 2 * 4] + matrix[1 + 2 * 4],
matrix[3 + 3 * 4] + matrix[1 + 3 * 4]);
plane /= length(plane.xyz());
return plane;
}
vec4 FrustumPlanes::near_frustum_plane(const mat4 &matrix)
{
vec4 plane(
matrix[3 + 0 * 4] + matrix[2 + 0 * 4],
matrix[3 + 1 * 4] + matrix[2 + 1 * 4],
matrix[3 + 2 * 4] + matrix[2 + 2 * 4],
matrix[3 + 3 * 4] + matrix[2 + 3 * 4]);
plane /= length(plane.xyz());
return plane;
}
vec4 FrustumPlanes::far_frustum_plane(const mat4 &matrix)
{
vec4 plane(
matrix[3 + 0 * 4] - matrix[2 + 0 * 4],
matrix[3 + 1 * 4] - matrix[2 + 1 * 4],
matrix[3 + 2 * 4] - matrix[2 + 2 * 4],
matrix[3 + 3 * 4] - matrix[2 + 3 * 4]);
plane /= length(plane.xyz());
return plane;
}

View file

@ -1,5 +1,5 @@
/*
** ZDRay collision
** Level mesh collision detection
** Copyright (c) 2018 Magnus Norddahl
**
** This software is provided 'as-is', without any express or implied
@ -22,7 +22,8 @@
#pragma once
#include "math/mathlib.h"
#include "framework/vectors.h"
#include "flatvertices.h"
#include <vector>
#include <cmath>
@ -30,9 +31,9 @@ class SphereShape
{
public:
SphereShape() { }
SphereShape(const vec3 &center, float radius) : center(center), radius(radius) { }
SphereShape(const FVector3 &center, float radius) : center(center), radius(radius) { }
vec3 center;
FVector3 center;
float radius = 0.0f;
};
@ -44,45 +45,49 @@ struct TraceHit
float c = 0.0f;
};
class CollisionBBox : public BBox
class CollisionBBox
{
public:
CollisionBBox() = default;
CollisionBBox(const vec3 &aabb_min, const vec3 &aabb_max) : BBox(aabb_min, aabb_max)
CollisionBBox(const FVector3 &aabb_min, const FVector3 &aabb_max)
{
min = aabb_min;
max = aabb_max;
auto halfmin = aabb_min * 0.5f;
auto halfmax = aabb_max * 0.5f;
Center = halfmax + halfmin;
Extents = halfmax - halfmin;
}
vec3 Center;
vec3 Extents;
FVector3 min;
FVector3 max;
FVector3 Center;
FVector3 Extents;
float ssePadding = 0.0f; // Needed to safely load Extents directly into a sse register
};
class RayBBox
{
public:
RayBBox(const vec3 &ray_start, const vec3 &ray_end) : start(ray_start), end(ray_end)
RayBBox(const FVector3 &ray_start, const FVector3 &ray_end) : start(ray_start), end(ray_end)
{
c = (ray_start + ray_end) * 0.5f;
w = ray_end - c;
v.x = std::abs(w.x);
v.y = std::abs(w.y);
v.z = std::abs(w.z);
v.X = std::abs(w.X);
v.Y = std::abs(w.Y);
v.Z = std::abs(w.Z);
}
vec3 start, end;
vec3 c, w, v;
FVector3 start, end;
FVector3 c, w, v;
float ssePadding = 0.0f; // Needed to safely load v directly into a sse register
};
class TriangleMeshShape
{
public:
TriangleMeshShape(const vec3 *vertices, int num_vertices, const unsigned int *elements, int num_elements);
TriangleMeshShape(const FFlatVertex *vertices, int num_vertices, const unsigned int *elements, int num_elements);
int get_min_depth() const;
int get_max_depth() const;
@ -91,21 +96,21 @@ public:
const CollisionBBox &get_bbox() const { return nodes[root].aabb; }
static float sweep(TriangleMeshShape *shape1, SphereShape *shape2, const vec3 &target);
static float sweep(TriangleMeshShape *shape1, SphereShape *shape2, const FVector3 &target);
static bool find_any_hit(TriangleMeshShape *shape1, TriangleMeshShape *shape2);
static bool find_any_hit(TriangleMeshShape *shape1, SphereShape *shape2);
static bool find_any_hit(TriangleMeshShape *shape, const vec3 &ray_start, const vec3 &ray_end);
static bool find_any_hit(TriangleMeshShape *shape, const FVector3 &ray_start, const FVector3 &ray_end);
static std::vector<int> find_all_hits(TriangleMeshShape* shape1, SphereShape* shape2);
static TraceHit find_first_hit(TriangleMeshShape *shape, const vec3 &ray_start, const vec3 &ray_end);
static TraceHit find_first_hit(TriangleMeshShape *shape, const FVector3 &ray_start, const FVector3 &ray_end);
struct Node
{
Node() = default;
Node(const vec3 &aabb_min, const vec3 &aabb_max, int element_index) : aabb(aabb_min, aabb_max), element_index(element_index) { }
Node(const vec3 &aabb_min, const vec3 &aabb_max, int left, int right) : aabb(aabb_min, aabb_max), left(left), right(right) { }
Node(const FVector3 &aabb_min, const FVector3 &aabb_max, int element_index) : aabb(aabb_min, aabb_max), element_index(element_index) { }
Node(const FVector3 &aabb_min, const FVector3 &aabb_max, int left, int right) : aabb(aabb_min, aabb_max), left(left), right(right) { }
CollisionBBox aabb;
int left = -1;
@ -117,7 +122,7 @@ public:
int get_root() const { return root; }
private:
const vec3 *vertices = nullptr;
const FFlatVertex* vertices = nullptr;
const int num_vertices = 0;
const unsigned int *elements = nullptr;
int num_elements = 0;
@ -125,7 +130,7 @@ private:
std::vector<Node> nodes;
int root = -1;
static float sweep(TriangleMeshShape *shape1, SphereShape *shape2, int a, const vec3 &target);
static float sweep(TriangleMeshShape *shape1, SphereShape *shape2, int a, const FVector3 &target);
static bool find_any_hit(TriangleMeshShape *shape1, TriangleMeshShape *shape2, int a, int b);
static bool find_any_hit(TriangleMeshShape *shape1, SphereShape *shape2, int a);
@ -138,8 +143,8 @@ private:
inline static bool overlap_bv_ray(TriangleMeshShape *shape, const RayBBox &ray, int a);
inline static float intersect_triangle_ray(TriangleMeshShape *shape, const RayBBox &ray, int a, float &barycentricB, float &barycentricC);
inline static bool sweep_overlap_bv_sphere(TriangleMeshShape *shape1, SphereShape *shape2, int a, const vec3 &target);
inline static float sweep_intersect_triangle_sphere(TriangleMeshShape *shape1, SphereShape *shape2, int a, const vec3 &target);
inline static bool sweep_overlap_bv_sphere(TriangleMeshShape *shape1, SphereShape *shape2, int a, const FVector3 &target);
inline static float sweep_intersect_triangle_sphere(TriangleMeshShape *shape1, SphereShape *shape2, int a, const FVector3 &target);
inline static bool overlap_bv(TriangleMeshShape *shape1, TriangleMeshShape *shape2, int a, int b);
inline static bool overlap_bv_triangle(TriangleMeshShape *shape1, TriangleMeshShape *shape2, int a, int b);
@ -150,34 +155,7 @@ private:
inline bool is_leaf(int node_index);
inline float volume(int node_index);
int subdivide(int *triangles, int num_triangles, const vec3 *centroids, int *work_buffer);
};
class OrientedBBox
{
public:
vec3 Center;
vec3 Extents;
vec3 axis_x;
vec3 axis_y;
vec3 axis_z;
};
class FrustumPlanes
{
public:
FrustumPlanes();
explicit FrustumPlanes(const mat4 &world_to_projection);
vec4 planes[6];
private:
static vec4 left_frustum_plane(const mat4 &matrix);
static vec4 right_frustum_plane(const mat4 &matrix);
static vec4 top_frustum_plane(const mat4 &matrix);
static vec4 bottom_frustum_plane(const mat4 &matrix);
static vec4 near_frustum_plane(const mat4 &matrix);
static vec4 far_frustum_plane(const mat4 &matrix);
int subdivide(int *triangles, int num_triangles, const FVector3 *centroids, int *work_buffer);
};
class IntersectionTest
@ -196,12 +174,7 @@ public:
overlap
};
static Result plane_aabb(const vec4 &plane, const BBox &aabb);
static Result plane_obb(const vec4 &plane, const OrientedBBox &obb);
static OverlapResult sphere(const vec3 &center1, float radius1, const vec3 &center2, float radius2);
static OverlapResult sphere_aabb(const vec3 &center, float radius, const BBox &aabb);
static OverlapResult aabb(const BBox &a, const BBox &b);
static Result frustum_aabb(const FrustumPlanes &frustum, const BBox &box);
static Result frustum_obb(const FrustumPlanes &frustum, const OrientedBBox &box);
static OverlapResult sphere_aabb(const FVector3 &center, float radius, const CollisionBBox &aabb);
static OverlapResult aabb(const CollisionBBox &a, const CollisionBBox &b);
static OverlapResult ray_aabb(const RayBBox &ray, const CollisionBBox &box);
};

View file

@ -0,0 +1,285 @@
#include "hw_levelmesh.h"
LevelMesh::LevelMesh()
{
// Default portal
LevelMeshPortal portal;
Portals.Push(portal);
AddEmptyMesh();
UpdateCollision();
Mesh.MaxVertices = std::max(Mesh.Vertices.Size() * 2, (unsigned int)10000);
Mesh.MaxIndexes = std::max(Mesh.Indexes.Size() * 2, (unsigned int)10000);
Mesh.MaxSurfaces = std::max(Mesh.SurfaceIndexes.Size() * 2, (unsigned int)10000);
Mesh.MaxUniforms = std::max(Mesh.Uniforms.Size() * 2, (unsigned int)10000);
Mesh.MaxSurfaceIndexes = std::max(Mesh.SurfaceIndexes.Size() * 2, (unsigned int)10000);
Mesh.MaxNodes = (int)std::max(Collision->get_nodes().size() * 2, (size_t)10000);
Mesh.MaxLights = 100'000;
Mesh.MaxLightIndexes = 4 * 1024 * 1024;
}
void LevelMesh::AddEmptyMesh()
{
// Default empty mesh (we can't make it completely empty since vulkan doesn't like that)
float minval = -100001.0f;
float maxval = -100000.0f;
Mesh.Vertices.Push({ minval, minval, minval });
Mesh.Vertices.Push({ maxval, minval, minval });
Mesh.Vertices.Push({ maxval, maxval, minval });
Mesh.Vertices.Push({ minval, minval, minval });
Mesh.Vertices.Push({ minval, maxval, minval });
Mesh.Vertices.Push({ maxval, maxval, minval });
Mesh.Vertices.Push({ minval, minval, maxval });
Mesh.Vertices.Push({ maxval, minval, maxval });
Mesh.Vertices.Push({ maxval, maxval, maxval });
Mesh.Vertices.Push({ minval, minval, maxval });
Mesh.Vertices.Push({ minval, maxval, maxval });
Mesh.Vertices.Push({ maxval, maxval, maxval });
for (int i = 0; i < 3 * 4; i++)
Mesh.Indexes.Push(i);
}
LevelMeshSurface* LevelMesh::Trace(const FVector3& start, FVector3 direction, float maxDist)
{
maxDist = std::max(maxDist - 10.0f, 0.0f);
FVector3 origin = start;
LevelMeshSurface* hitSurface = nullptr;
while (true)
{
FVector3 end = origin + direction * maxDist;
TraceHit hit = TriangleMeshShape::find_first_hit(Collision.get(), origin, end);
if (hit.triangle < 0)
{
return nullptr;
}
hitSurface = GetSurface(Mesh.SurfaceIndexes[hit.triangle]);
int portal = hitSurface->PortalIndex;
if (!portal)
{
break;
}
auto& transformation = Portals[portal];
auto travelDist = hit.fraction * maxDist + 2.0f;
if (travelDist >= maxDist)
{
break;
}
origin = transformation.TransformPosition(origin + direction * travelDist);
direction = transformation.TransformRotation(direction);
maxDist -= travelDist;
}
return hitSurface; // I hit something
}
LevelMeshTileStats LevelMesh::GatherTilePixelStats()
{
LevelMeshTileStats stats;
int count = GetSurfaceCount();
for (const LightmapTile& tile : LightmapTiles)
{
auto area = tile.AtlasLocation.Area();
stats.pixels.total += area;
if (tile.NeedsUpdate)
{
stats.tiles.dirty++;
stats.pixels.dirty += area;
}
}
stats.tiles.total += LightmapTiles.Size();
return stats;
}
void LevelMesh::UpdateCollision()
{
Collision = std::make_unique<TriangleMeshShape>(Mesh.Vertices.Data(), Mesh.Vertices.Size(), Mesh.Indexes.Data(), Mesh.Indexes.Size());
}
struct LevelMeshPlaneGroup
{
FVector4 plane = FVector4(0, 0, 1, 0);
int sectorGroup = 0;
std::vector<LevelMeshSurface*> surfaces;
};
void LevelMesh::BuildTileSurfaceLists()
{
// Plane group surface is to be rendered with
TArray<LevelMeshPlaneGroup> PlaneGroups;
TArray<int> PlaneGroupIndexes(GetSurfaceCount());
for (int i = 0, count = GetSurfaceCount(); i < count; i++)
{
auto surface = GetSurface(i);
// Is this surface in the same plane as an existing plane group?
int planeGroupIndex = -1;
for (size_t j = 0; j < PlaneGroups.Size(); j++)
{
if (surface->SectorGroup == PlaneGroups[j].sectorGroup)
{
float direction = PlaneGroups[j].plane.XYZ() | surface->Plane.XYZ();
if (direction >= 0.999f && direction <= 1.01f)
{
auto point = (surface->Plane.XYZ() * surface->Plane.W);
auto planeDistance = (PlaneGroups[j].plane.XYZ() | point) - PlaneGroups[j].plane.W;
float dist = std::abs(planeDistance);
if (dist <= 0.1f)
{
planeGroupIndex = (int)j;
break;
}
}
}
}
// Surface is in a new plane. Create a plane group for it
if (planeGroupIndex == -1)
{
planeGroupIndex = PlaneGroups.Size();
LevelMeshPlaneGroup group;
group.plane = surface->Plane;
group.sectorGroup = surface->SectorGroup;
PlaneGroups.Push(group);
}
PlaneGroups[planeGroupIndex].surfaces.push_back(surface);
PlaneGroupIndexes.Push(planeGroupIndex);
}
for (auto& tile : LightmapTiles)
tile.Surfaces.Clear();
for (int i = 0, count = GetSurfaceCount(); i < count; i++)
{
LevelMeshSurface* targetSurface = GetSurface(i);
if (targetSurface->LightmapTileIndex < 0)
continue;
LightmapTile* tile = &LightmapTiles[targetSurface->LightmapTileIndex];
for (LevelMeshSurface* surface : PlaneGroups[PlaneGroupIndexes[i]].surfaces)
{
FVector2 minUV = tile->ToUV(surface->Bounds.min);
FVector2 maxUV = tile->ToUV(surface->Bounds.max);
if (surface != targetSurface && (maxUV.X < 0.0f || maxUV.Y < 0.0f || minUV.X > 1.0f || minUV.Y > 1.0f))
continue; // Bounding box not visible
tile->Surfaces.Push(GetSurfaceIndex(surface));
}
}
}
void LevelMesh::SetupTileTransforms()
{
for (auto& tile : LightmapTiles)
{
tile.SetupTileTransform(LMTextureSize);
}
}
void LevelMesh::PackLightmapAtlas(int lightmapStartIndex)
{
std::vector<LightmapTile*> sortedTiles;
sortedTiles.reserve(LightmapTiles.Size());
for (auto& tile : LightmapTiles)
{
sortedTiles.push_back(&tile);
}
std::sort(sortedTiles.begin(), sortedTiles.end(), [](LightmapTile* a, LightmapTile* b) { return a->AtlasLocation.Height != b->AtlasLocation.Height ? a->AtlasLocation.Height > b->AtlasLocation.Height : a->AtlasLocation.Width > b->AtlasLocation.Width; });
// We do not need to add spacing here as this is already built into the tile size itself.
RectPacker packer(LMTextureSize, LMTextureSize, RectPacker::Spacing(0), RectPacker::Padding(0));
for (LightmapTile* tile : sortedTiles)
{
auto result = packer.insert(tile->AtlasLocation.Width, tile->AtlasLocation.Height);
tile->AtlasLocation.X = result.pos.x;
tile->AtlasLocation.Y = result.pos.y;
tile->AtlasLocation.ArrayIndex = lightmapStartIndex + (int)result.pageIndex;
}
LMTextureCount = (int)packer.getNumPages();
// Calculate final texture coordinates
for (int i = 0, count = GetSurfaceCount(); i < count; i++)
{
auto surface = GetSurface(i);
if (surface->LightmapTileIndex >= 0)
{
const LightmapTile& tile = LightmapTiles[surface->LightmapTileIndex];
for (int i = 0; i < surface->MeshLocation.NumVerts; i++)
{
auto& vertex = Mesh.Vertices[surface->MeshLocation.StartVertIndex + i];
FVector2 uv = tile.ToUV(vertex.fPos(), (float)LMTextureSize);
vertex.lu = uv.X;
vertex.lv = uv.Y;
vertex.lindex = (float)tile.AtlasLocation.ArrayIndex;
}
}
}
#if 0 // Debug atlas tile locations:
float colors[30] =
{
1.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f,
1.0f, 1.0f, 0.0f,
0.0f, 1.0f, 1.0f,
1.0f, 0.0f, 1.0f,
0.5f, 0.0f, 0.0f,
0.0f, 0.5f, 0.0f,
0.5f, 0.5f, 0.0f,
0.0f, 0.5f, 0.5f,
0.5f, 0.0f, 0.5f
};
LMTextureData.Resize(LMTextureSize * LMTextureSize * LMTextureCount * 3);
uint16_t* pixels = LMTextureData.Data();
for (LightmapTile& tile : LightmapTiles)
{
tile.NeedsUpdate = false;
int index = tile.Binding.TypeIndex;
float* color = colors + (index % 10) * 3;
int x = tile.AtlasLocation.X;
int y = tile.AtlasLocation.Y;
int w = tile.AtlasLocation.Width;
int h = tile.AtlasLocation.Height;
for (int yy = y; yy < y + h; yy++)
{
uint16_t* line = pixels + tile.AtlasLocation.ArrayIndex * LMTextureSize * LMTextureSize + yy * LMTextureSize * 3;
for (int xx = x; xx < x + w; xx++)
{
float gray = (yy - y) / (float)h;
line[xx * 3] = floatToHalf(color[0] * gray);
line[xx * 3 + 1] = floatToHalf(color[1] * gray);
line[xx * 3 + 2] = floatToHalf(color[2] * gray);
}
}
}
for (int i = 0, count = GetSurfaceCount(); i < count; i++)
{
auto surface = GetSurface(i);
surface->AlwaysUpdate = false;
}
#endif
}

View file

@ -0,0 +1,110 @@
#pragma once
#include "framework/tarray.h"
#include "framework/vectors.h"
#include "hw_collision.h"
#include "flatvertices.h"
#include "hw_levelmeshlight.h"
#include "hw_levelmeshportal.h"
#include "hw_lightmaptile.h"
#include "hw_levelmeshsurface.h"
#include "hw_materialstate.h"
#include "hw_surfaceuniforms.h"
#include <memory>
#include <dp_rect_pack/dp_rect_pack.h>
typedef dp::rect_pack::RectPacker<int> RectPacker;
struct LevelMeshTileStats;
struct LevelSubmeshDrawRange
{
int PipelineID;
int Start;
int Count;
};
class LevelMesh
{
public:
LevelMesh();
virtual ~LevelMesh() = default;
virtual LevelMeshSurface* GetSurface(int index) { return nullptr; }
virtual unsigned int GetSurfaceIndex(const LevelMeshSurface* surface) const { return 0xffffffff; }
virtual int GetSurfaceCount() { return 0; }
LevelMeshSurface* Trace(const FVector3& start, FVector3 direction, float maxDist);
LevelMeshTileStats GatherTilePixelStats();
// Map defaults
FVector3 SunDirection = FVector3(0.0f, 0.0f, -1.0f);
FVector3 SunColor = FVector3(0.0f, 0.0f, 0.0f);
TArray<LevelMeshPortal> Portals;
struct
{
// Vertex data
TArray<FFlatVertex> Vertices;
TArray<int> UniformIndexes;
// Surface info
TArray<SurfaceUniforms> Uniforms;
TArray<FMaterialState> Materials;
TArray<int32_t> LightIndexes;
// Lights
TArray<LevelMeshLight> Lights;
// Index data
TArray<uint32_t> Indexes;
TArray<int> SurfaceIndexes;
int DynamicIndexStart = 0;
// Above data must not be resized beyond these limits as that's the size of the GPU buffers)
int MaxVertices = 0;
int MaxIndexes = 0;
int MaxSurfaces = 0;
int MaxUniforms = 0;
int MaxSurfaceIndexes = 0;
int MaxNodes = 0;
int MaxLights = 0;
int MaxLightIndexes = 0;
} Mesh;
std::unique_ptr<TriangleMeshShape> Collision;
TArray<LevelSubmeshDrawRange> DrawList;
TArray<LevelSubmeshDrawRange> PortalList;
// Lightmap atlas
int LMTextureCount = 0;
int LMTextureSize = 1024;
TArray<uint16_t> LMTextureData;
uint16_t LightmapSampleDistance = 16;
TArray<LightmapTile> LightmapTiles;
uint32_t AtlasPixelCount() const { return uint32_t(LMTextureCount * LMTextureSize * LMTextureSize); }
void UpdateCollision();
void BuildTileSurfaceLists();
void SetupTileTransforms();
void PackLightmapAtlas(int lightmapStartIndex);
void AddEmptyMesh();
};
struct LevelMeshTileStats
{
struct Stats
{
uint32_t total = 0, dirty = 0;
};
Stats tiles, pixels;
};

View file

@ -0,0 +1,19 @@
#pragma once
#include "framework/vectors.h"
class LevelMeshLight
{
public:
FVector3 Origin;
FVector3 RelativeOrigin;
float Radius;
float Intensity;
float InnerAngleCos;
float OuterAngleCos;
FVector3 SpotDir;
FVector3 Color;
int SectorGroup;
float SoftShadowRadius;
};

View file

@ -0,0 +1,72 @@
#pragma once
#include <cstring>
#include "framework/vectors.h"
#include "framework/matrix.h"
struct LevelMeshPortal
{
LevelMeshPortal() { transformation.loadIdentity(); }
VSMatrix transformation;
int sourceSectorGroup = 0;
int targetSectorGroup = 0;
inline FVector3 TransformPosition(const FVector3& pos) const
{
auto v = transformation * FVector4(pos, 1.0);
return FVector3(v.X, v.Y, v.Z);
}
inline FVector3 TransformRotation(const FVector3& dir) const
{
auto v = transformation * FVector4(dir, 0.0);
return FVector3(v.X, v.Y, v.Z);
}
// Checks only transformation
inline bool IsInverseTransformationPortal(const LevelMeshPortal& portal) const
{
auto diff = portal.TransformPosition(TransformPosition(FVector3(0, 0, 0)));
return abs(diff.X) < 0.001 && abs(diff.Y) < 0.001 && abs(diff.Z) < 0.001;
}
// Checks only transformation
inline bool IsEqualTransformationPortal(const LevelMeshPortal& portal) const
{
auto diff = portal.TransformPosition(FVector3(0, 0, 0)) - TransformPosition(FVector3(0, 0, 0));
return (abs(diff.X) < 0.001 && abs(diff.Y) < 0.001 && abs(diff.Z) < 0.001);
}
// Checks transformation, source and destiantion sector groups
inline bool IsEqualPortal(const LevelMeshPortal& portal) const
{
return sourceSectorGroup == portal.sourceSectorGroup && targetSectorGroup == portal.targetSectorGroup && IsEqualTransformationPortal(portal);
}
// Checks transformation, source and destiantion sector groups
inline bool IsInversePortal(const LevelMeshPortal& portal) const
{
return sourceSectorGroup == portal.targetSectorGroup && targetSectorGroup == portal.sourceSectorGroup && IsInverseTransformationPortal(portal);
}
};
// for use with std::set to recursively go through portals and skip returning portals
struct RecursivePortalComparator
{
bool operator()(const LevelMeshPortal& a, const LevelMeshPortal& b) const
{
return !a.IsInversePortal(b) && std::memcmp(&a.transformation, &b.transformation, sizeof(VSMatrix)) < 0;
}
};
// for use with std::map to reject portals which have the same effect for light rays
struct IdenticalPortalComparator
{
bool operator()(const LevelMeshPortal& a, const LevelMeshPortal& b) const
{
return !a.IsEqualPortal(b) && std::memcmp(&a.transformation, &b.transformation, sizeof(VSMatrix)) < 0;
}
};

View file

@ -0,0 +1,45 @@
#pragma once
#include "framework/tarray.h"
#include "framework/vectors.h"
#include "framework/matrix.h"
#include "framework/bounds.h"
#include "framework/textureid.h"
class LevelSubmesh;
struct LevelMeshSurface;
struct ThingLight;
struct LevelMeshSurface
{
struct
{
unsigned int StartVertIndex = 0;
int NumVerts = 0;
unsigned int StartElementIndex = 0;
unsigned int NumElements = 0;
} MeshLocation;
BBox Bounds;
FVector4 Plane = FVector4(0.0f, 0.0f, 1.0f, 0.0f);
int LightmapTileIndex = -1;
bool AlwaysUpdate = false;
FTextureID Texture = FNullTextureID(); // FGameTexture* Texture = nullptr;
float Alpha = 1.0;
bool IsSky = false;
int PortalIndex = 0;
int SectorGroup = 0;
// Light list location in the lightmapper GPU buffers
struct
{
int Pos = 0;
int Count = 0;
} LightList;
TArray<ThingLight*> Lights;
};

View file

@ -0,0 +1,170 @@
#pragma once
#include "framework/tarray.h"
#include "framework/vectors.h"
#include "framework/bounds.h"
struct LevelMeshSurface;
struct LightmapTileBinding
{
uint32_t Type = 0;
uint32_t TypeIndex = 0;
uint32_t ControlSector = 0xffffffff;
bool operator<(const LightmapTileBinding& other) const
{
if (TypeIndex != other.TypeIndex) return TypeIndex < other.TypeIndex;
if (ControlSector != other.ControlSector) return ControlSector < other.ControlSector;
return Type < other.Type;
}
};
struct LightmapTile
{
// Surface location in lightmap texture
struct
{
int X = 0;
int Y = 0;
int Width = 0;
int Height = 0;
int ArrayIndex = 0;
uint32_t Area() const { return Width * Height; }
} AtlasLocation;
// Calculate world coordinates to UV coordinates
struct
{
FVector3 TranslateWorldToLocal = { 0.0f, 0.0f, 0.0f };
FVector3 ProjLocalToU = { 0.0f, 0.0f, 0.0f };
FVector3 ProjLocalToV = { 0.0f, 0.0f, 0.0f };
} Transform;
LightmapTileBinding Binding;
// Surfaces that are visible within the lightmap tile
TArray<int> Surfaces;
BBox Bounds;
uint16_t SampleDimension = 0;
FVector4 Plane = FVector4(0.0f, 0.0f, 1.0f, 0.0f);
// True if the tile needs to be rendered into the lightmap texture before it can be used
bool NeedsUpdate = true;
FVector2 ToUV(const FVector3& vert) const
{
FVector3 localPos = vert - Transform.TranslateWorldToLocal;
float u = (localPos | Transform.ProjLocalToU) / AtlasLocation.Width;
float v = (localPos | Transform.ProjLocalToV) / AtlasLocation.Height;
return FVector2(u, v);
}
FVector2 ToUV(const FVector3& vert, float textureSize) const
{
// Clamp in case the wall moved outside the tile (happens if a lift moves with a static lightmap on it)
FVector3 localPos = vert - Transform.TranslateWorldToLocal;
float u = std::max(std::min(localPos | Transform.ProjLocalToU, (float)AtlasLocation.Width), 0.0f);
float v = std::max(std::min(localPos | Transform.ProjLocalToV, (float)AtlasLocation.Height), 0.0f);
u = (AtlasLocation.X + u) / textureSize;
v = (AtlasLocation.Y + v) / textureSize;
return FVector2(u, v);
}
enum PlaneAxis
{
AXIS_YZ = 0,
AXIS_XZ,
AXIS_XY
};
static PlaneAxis BestAxis(const FVector4& p)
{
float na = fabs(float(p.X));
float nb = fabs(float(p.Y));
float nc = fabs(float(p.Z));
// figure out what axis the plane lies on
if (na >= nb && na >= nc)
{
return AXIS_YZ;
}
else if (nb >= na && nb >= nc)
{
return AXIS_XZ;
}
return AXIS_XY;
}
void SetupTileTransform(int textureSize)
{
// These calculations align the tile so that there's a one texel border around the actual surface in the tile.
//
// This removes sampling artifacts as a linear sampler reads from a 2x2 area.
// The tile is also aligned to the grid to keep aliasing artifacts consistent.
FVector3 uvMin;
uvMin.X = std::floor(Bounds.min.X / SampleDimension) - 1.0f;
uvMin.Y = std::floor(Bounds.min.Y / SampleDimension) - 1.0f;
uvMin.Z = std::floor(Bounds.min.Z / SampleDimension) - 1.0f;
FVector3 uvMax;
uvMax.X = std::floor(Bounds.max.X / SampleDimension) + 2.0f;
uvMax.Y = std::floor(Bounds.max.Y / SampleDimension) + 2.0f;
uvMax.Z = std::floor(Bounds.max.Z / SampleDimension) + 2.0f;
FVector3 tCoords[2] = { FVector3(0.0f, 0.0f, 0.0f), FVector3(0.0f, 0.0f, 0.0f) };
int width, height;
switch (BestAxis(Plane))
{
default:
case AXIS_YZ:
width = (int)(uvMax.Y - uvMin.Y);
height = (int)(uvMax.Z - uvMin.Z);
tCoords[0].Y = 1.0f / SampleDimension;
tCoords[1].Z = 1.0f / SampleDimension;
break;
case AXIS_XZ:
width = (int)(uvMax.X - uvMin.X);
height = (int)(uvMax.Z - uvMin.Z);
tCoords[0].X = 1.0f / SampleDimension;
tCoords[1].Z = 1.0f / SampleDimension;
break;
case AXIS_XY:
width = (int)(uvMax.X - uvMin.X);
height = (int)(uvMax.Y - uvMin.Y);
tCoords[0].X = 1.0f / SampleDimension;
tCoords[1].Y = 1.0f / SampleDimension;
break;
}
textureSize -= 6; // Lightmapper needs some padding when baking
// Tile can never be bigger than the texture.
if (width > textureSize)
{
tCoords[0] *= textureSize / (float)width;
width = textureSize;
}
if (height > textureSize)
{
tCoords[1] *= textureSize / (float)height;
height = textureSize;
}
Transform.TranslateWorldToLocal.X = uvMin.X * SampleDimension;
Transform.TranslateWorldToLocal.Y = uvMin.Y * SampleDimension;
Transform.TranslateWorldToLocal.Z = uvMin.Z * SampleDimension;
Transform.ProjLocalToU = tCoords[0];
Transform.ProjLocalToV = tCoords[1];
AtlasLocation.Width = width;
AtlasLocation.Height = height;
}
};

View file

@ -0,0 +1,37 @@
#pragma once
enum
{
CLAMP_NONE = 0,
CLAMP_X,
CLAMP_Y,
CLAMP_XY,
CLAMP_XY_NOMIP,
CLAMP_NOFILTER,
CLAMP_NOFILTER_X,
CLAMP_NOFILTER_Y,
CLAMP_NOFILTER_XY,
CLAMP_CAMTEX,
NUMSAMPLERS
};
class FMaterial;
struct FMaterialState
{
FMaterial* mMaterial = nullptr;
int mClampMode;
int mTranslation;
int mOverrideShader;
bool mChanged;
void Reset()
{
mMaterial = nullptr;
mTranslation = 0;
mClampMode = CLAMP_NONE;
mOverrideShader = -1;
mChanged = false;
}
};

View file

@ -0,0 +1,49 @@
#pragma once
#include "framework/vectors.h"
struct SurfaceUniforms
{
FVector4 uObjectColor;
FVector4 uObjectColor2;
FVector4 uDynLightColor;
FVector4 uAddColor;
FVector4 uTextureAddColor;
FVector4 uTextureModulateColor;
FVector4 uTextureBlendColor;
FVector4 uFogColor;
float uDesaturationFactor; // HWDrawInfo::SetColor
float uInterpolationFactor;
float timer;
int useVertexData;
FVector4 uVertexColor; // HWDrawInfo::SetColor
FVector4 uVertexNormal;
FVector4 uGlowTopPlane;
FVector4 uGlowTopColor;
FVector4 uGlowBottomPlane;
FVector4 uGlowBottomColor;
FVector4 uGradientTopPlane;
FVector4 uGradientBottomPlane;
FVector4 uSplitTopPlane;
FVector4 uSplitBottomPlane;
FVector4 uDetailParms;
FVector4 uNpotEmulation;
FVector2 uClipSplit;
FVector2 uSpecularMaterial;
float uLightLevel; // HWDrawInfo::SetColor
float uFogDensity;
float uLightFactor;
float uLightDist;
float uAlphaThreshold;
int uTextureIndex;
float padding2;
float padding3;
};

View file

@ -0,0 +1,326 @@
#ifdef WIN32
#include "levelmeshviewer.h"
#include "vk_renderdevice.h"
#include "framework/matrix.h"
#include <windowsx.h>
#include <stdexcept>
#include <zvulkan/vulkanswapchain.h>
LevelMeshViewer::LevelMeshViewer()
{
CreateViewerWindow();
}
LevelMeshViewer::~LevelMeshViewer()
{
DestroyViewerWindow();
}
void LevelMeshViewer::RenderFrame()
{
RECT clientRect = {};
GetClientRect(WindowHandle, &clientRect);
int width = clientRect.right;
int height = clientRect.bottom;
if (width <= 0 || height <= 0)
{
Sleep(500);
return;
}
RenderDevice->ResizeSwapChain(width, height);
VSMatrix movematrix;
movematrix.loadIdentity();
movematrix.rotate((float)Yaw, 0.0f, 1.0f, 0.0);
movematrix.rotate((float)Pitch, 1.0f, 0.0f, 0.0);
if (MoveLeft)
{
CameraPos += (movematrix * FVector4(-10.0f, 0.0f, 0.0f, 1.0f)).XYZ();
}
if (MoveRight)
{
CameraPos += (movematrix * FVector4(10.0f, 0.0f, 0.0f, 1.0f)).XYZ();
}
if (MoveForward)
{
CameraPos += (movematrix * FVector4(0.0f, 0.0f, 10.0f, 1.0f)).XYZ();
}
if (MoveBackward)
{
CameraPos += (movematrix * FVector4(0.0f, 0.0f, -10.0f, 1.0f)).XYZ();
}
VSMatrix worldToView;
worldToView.loadIdentity();
worldToView.rotate((float)-Pitch, 1.0f, 0.0f, 0.0);
worldToView.rotate((float)-Yaw, 0.0f, 1.0f, 0.0);
worldToView.translate(-CameraPos.X, -CameraPos.Y, -CameraPos.Z);
VSMatrix viewToWorld;
worldToView.inverseMatrix(viewToWorld);
RenderDevice->DrawViewer(CameraPos, viewToWorld, 60.0f, width / (float)height, SunDir, SunColor, SunIntensity);
}
void LevelMeshViewer::OnMouseMove(double dx, double dy)
{
Yaw += dx * 0.1;
while (Yaw < 0.0) Yaw += 360.0;
while (Yaw >= 360.0) Yaw -= 360.0;
Pitch += dy * 0.1;
if (Pitch < -90.0) Pitch = -90.0;
if (Pitch > 90.0) Pitch = 90.0;
}
void LevelMeshViewer::OnKeyDown(WPARAM key)
{
if (key == 'A')
{
MoveLeft = true;
}
else if (key == 'D')
{
MoveRight = true;
}
else if (key == 'W')
{
MoveForward = true;
}
else if (key == 'S')
{
MoveBackward = true;
}
}
void LevelMeshViewer::OnKeyUp(WPARAM key)
{
if (key == 'A')
{
MoveLeft = false;
}
else if (key == 'D')
{
MoveRight = false;
}
else if (key == 'W')
{
MoveForward = false;
}
else if (key == 'S')
{
MoveBackward = false;
}
}
void LevelMeshViewer::Exec(VulkanRenderDevice* renderdevice, const FVector3& sundir, const FVector3& suncolor, float sunintensity)
{
RenderDevice = renderdevice;
SunDir = sundir;
SunColor = suncolor;
SunIntensity = sunintensity;
bool exitFlag = false;
while (!exitFlag)
{
MSG msg = {};
while (PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
{
if (msg.message == WM_QUIT)
{
exitFlag = true;
break;
}
TranslateMessage(&msg);
DispatchMessage(&msg);
}
RenderFrame();
}
ShowWindow(WindowHandle, SW_HIDE);
}
void LevelMeshViewer::CreateViewerWindow()
{
WNDCLASSEX classdesc = {};
classdesc.cbSize = sizeof(WNDCLASSEX);
classdesc.hInstance = GetModuleHandle(nullptr);
classdesc.lpszClassName = L"LevelMeshViewerWindow";
classdesc.lpfnWndProc = &LevelMeshViewer::WindowProc;
classdesc.style = CS_VREDRAW | CS_HREDRAW | CS_DBLCLKS;
classdesc.hbrBackground = CreateSolidBrush(RGB(0, 0, 0));
BOOL result = RegisterClassEx(&classdesc);
if (!result)
throw std::runtime_error("RegisterClassEx failed");
HDC screenDC = GetDC(0);
int dpi = GetDeviceCaps(screenDC, LOGPIXELSX);
ReleaseDC(0, screenDC);
auto applyDpi = [=](int i) { return (i * dpi + (96 / 2)) / 96; };
// Create and show the window
WindowHandle = CreateWindowEx(WS_EX_APPWINDOW, L"LevelMeshViewerWindow", L"ZDRay Viewer", WS_OVERLAPPEDWINDOW | WS_VISIBLE | WS_MAXIMIZE, applyDpi(100), applyDpi(50), applyDpi(1400), applyDpi(800), 0, 0, GetModuleHandle(nullptr), this);
if (!WindowHandle)
throw std::runtime_error("CreateWindowEx failed");
}
void LevelMeshViewer::DestroyViewerWindow()
{
if (WindowHandle)
DestroyWindow(WindowHandle);
WindowHandle = 0;
}
void LevelMeshViewer::OnClose()
{
PostQuitMessage(0);
}
LRESULT LevelMeshViewer::OnWindowMessage(UINT msg, WPARAM wparam, LPARAM lparam)
{
/*if (msg == WM_RBUTTONDOWN)
{
if (!MouseLocked)
{
GetCursorPos(&LockPosition);
::ShowCursor(FALSE);
RECT box = {};
GetClientRect(WindowHandle, &box);
POINT center = {};
center.x = box.right / 2;
center.y = box.bottom / 2;
ClientToScreen(WindowHandle, &center);
SetCursorPos(center.x, center.y);
MouseLocked = true;
}
}
else if (msg == WM_RBUTTONUP)
{
if (MouseLocked)
{
MouseLocked = false;
SetCursorPos(LockPosition.x, LockPosition.y);
::ShowCursor(TRUE);
}
}
else*/ if (msg == WM_MOUSEMOVE)
{
if (MouseLocked && GetFocus() != 0)
{
RECT box = {};
GetClientRect(WindowHandle, &box);
POINT center = {};
center.x = box.right / 2;
center.y = box.bottom / 2;
int mousex = GET_X_LPARAM(lparam) - center.x;
int mousey = GET_Y_LPARAM(lparam) - center.y;
ClientToScreen(WindowHandle, &center);
SetCursorPos(center.x, center.y);
double dpiscale = GetDpiScale();
OnMouseMove(mousex / dpiscale, mousey / dpiscale);
}
else
{
SetCursor((HCURSOR)LoadImage(0, IDC_ARROW, IMAGE_CURSOR, LR_DEFAULTSIZE, LR_DEFAULTSIZE, LR_SHARED));
}
}
else if (msg == WM_SETFOCUS)
{
if (MouseLocked)
{
GetCursorPos(&LockPosition);
::ShowCursor(FALSE);
RECT box = {};
GetClientRect(WindowHandle, &box);
POINT center = {};
center.x = box.right / 2;
center.y = box.bottom / 2;
ClientToScreen(WindowHandle, &center);
SetCursorPos(center.x, center.y);
}
}
else if (msg == WM_KILLFOCUS)
{
if (MouseLocked)
{
SetCursorPos(LockPosition.x, LockPosition.y);
::ShowCursor(TRUE);
}
}
else if (msg == WM_CLOSE)
{
OnClose();
return 0;
}
else if (msg == WM_KEYDOWN)
{
OnKeyDown(wparam);
}
else if (msg == WM_KEYUP)
{
OnKeyUp(wparam);
}
return DefWindowProc(WindowHandle, msg, wparam, lparam);
}
double LevelMeshViewer::GetDpiScale() const
{
return GetDpiForWindow(WindowHandle) / 96.0;
}
int LevelMeshViewer::GetPixelWidth() const
{
RECT box = {};
GetClientRect(WindowHandle, &box);
return box.right;
}
int LevelMeshViewer::GetPixelHeight() const
{
RECT box = {};
GetClientRect(WindowHandle, &box);
return box.bottom;
}
LRESULT LevelMeshViewer::WindowProc(HWND windowhandle, UINT msg, WPARAM wparam, LPARAM lparam)
{
if (msg == WM_CREATE)
{
CREATESTRUCT* createstruct = (CREATESTRUCT*)lparam;
LevelMeshViewer* viewport = (LevelMeshViewer*)createstruct->lpCreateParams;
viewport->WindowHandle = windowhandle;
SetWindowLongPtr(windowhandle, GWLP_USERDATA, (LONG_PTR)viewport);
return viewport->OnWindowMessage(msg, wparam, lparam);
}
else
{
LevelMeshViewer* viewport = (LevelMeshViewer*)GetWindowLongPtr(windowhandle, GWLP_USERDATA);
if (viewport)
{
LRESULT result = viewport->OnWindowMessage(msg, wparam, lparam);
if (msg == WM_DESTROY)
{
SetWindowLongPtr(windowhandle, GWLP_USERDATA, 0);
viewport->WindowHandle = 0;
}
return result;
}
else
{
return DefWindowProc(windowhandle, msg, wparam, lparam);
}
}
}
#endif

View file

@ -0,0 +1,67 @@
#pragma once
#include "framework/vectors.h"
class VulkanRenderDevice;
#ifdef WIN32
#define WIN32_MEAN_AND_LEAN
#define NOMINMAX
#include <Windows.h>
class LevelMeshViewer
{
public:
LevelMeshViewer();
~LevelMeshViewer();
void Exec(VulkanRenderDevice* renderdevice, const FVector3& sundir, const FVector3& suncolor, float sunintensity);
HWND GetWindowHandle() { return WindowHandle; }
private:
void RenderFrame();
void OnMouseMove(double dx, double dy);
void OnKeyDown(WPARAM key);
void OnKeyUp(WPARAM key);
void OnClose();
void CreateViewerWindow();
void DestroyViewerWindow();
double GetDpiScale() const;
int GetPixelWidth() const;
int GetPixelHeight() const;
LRESULT OnWindowMessage(UINT msg, WPARAM wparam, LPARAM lparam);
static LRESULT CALLBACK WindowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam);
VulkanRenderDevice* RenderDevice = nullptr;
HWND WindowHandle = 0;
bool MouseLocked = true;
POINT LockPosition = {};
double Yaw = 0.0;
double Pitch = 0.0;
bool MoveLeft = false;
bool MoveRight = false;
bool MoveForward = false;
bool MoveBackward = false;
FVector3 CameraPos = FVector3(0.0f, 70.0f, -128.0f);
FVector3 SunDir = FVector3(0.0f, 0.0f, 0.0f);
FVector3 SunColor = FVector3(0.0f, 0.0f, 0.0f);
float SunIntensity = 0.0f;
};
#else
class LevelMeshViewer
{
public:
LevelMeshViewer() { }
void Exec(VulkanRenderDevice* renderdevice, const FVector3& sundir, const FVector3& suncolor, float sunintensity) { }
};
#endif

View file

@ -0,0 +1,700 @@
/*
** Vulkan backend
** Copyright (c) 2016-2020 Magnus Norddahl
**
** 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.
**
*/
#include "vk_levelmesh.h"
#include "zvulkan/vulkanbuilders.h"
#include "vk_renderdevice.h"
#include "hw_levelmesh.h"
VkLevelMesh::VkLevelMesh(VulkanRenderDevice* fb) : fb(fb)
{
useRayQuery = fb->IsRayQueryEnabled();
SetLevelMesh(nullptr);
}
void VkLevelMesh::SetLevelMesh(LevelMesh* mesh)
{
if (!mesh)
mesh = &NullMesh;
Mesh = mesh;
CreateVulkanObjects();
}
void VkLevelMesh::Reset()
{
auto deletelist = fb->GetCommands()->DrawDeleteList.get();
deletelist->Add(std::move(VertexBuffer));
deletelist->Add(std::move(UniformIndexBuffer));
deletelist->Add(std::move(IndexBuffer));
deletelist->Add(std::move(NodeBuffer));
deletelist->Add(std::move(SurfaceBuffer));
deletelist->Add(std::move(UniformsBuffer));
deletelist->Add(std::move(SurfaceIndexBuffer));
deletelist->Add(std::move(PortalBuffer));
deletelist->Add(std::move(LightBuffer));
deletelist->Add(std::move(LightIndexBuffer));
deletelist->Add(std::move(StaticBLAS.ScratchBuffer));
deletelist->Add(std::move(StaticBLAS.AccelStructBuffer));
deletelist->Add(std::move(StaticBLAS.AccelStruct));
deletelist->Add(std::move(DynamicBLAS.ScratchBuffer));
deletelist->Add(std::move(DynamicBLAS.AccelStructBuffer));
deletelist->Add(std::move(DynamicBLAS.AccelStruct));
deletelist->Add(std::move(TopLevelAS.TransferBuffer));
deletelist->Add(std::move(TopLevelAS.InstanceBuffer));
deletelist->Add(std::move(TopLevelAS.ScratchBuffer));
deletelist->Add(std::move(TopLevelAS.AccelStructBuffer));
deletelist->Add(std::move(TopLevelAS.AccelStruct));
}
void VkLevelMesh::CreateVulkanObjects()
{
Reset();
CreateBuffers();
UploadMeshes(false);
if (useRayQuery)
{
// Wait for uploads to finish
PipelineBarrier()
.AddMemory(VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_ACCELERATION_STRUCTURE_READ_BIT_KHR | VK_ACCESS_SHADER_READ_BIT)
.Execute(fb->GetCommands()->GetTransferCommands(), VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT);
CreateStaticBLAS();
CreateDynamicBLAS();
CreateTLASInstanceBuffer();
UploadTLASInstanceBuffer();
// Wait for bottom level builds to finish before using it as input to a toplevel accel structure. Also wait for the instance buffer upload to complete.
PipelineBarrier()
.AddMemory(VK_ACCESS_ACCELERATION_STRUCTURE_WRITE_BIT_KHR | VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_ACCELERATION_STRUCTURE_READ_BIT_KHR | VK_ACCESS_SHADER_READ_BIT)
.Execute(fb->GetCommands()->GetTransferCommands(), VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR | VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR);
CreateTopLevelAS(DynamicBLAS.AccelStruct ? 2 : 1);
// Finish building the accel struct before using it from the shaders
PipelineBarrier()
.AddMemory(VK_ACCESS_ACCELERATION_STRUCTURE_WRITE_BIT_KHR, VK_ACCESS_SHADER_READ_BIT)
.Execute(fb->GetCommands()->GetTransferCommands(), VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT);
}
else
{
// Uploads must finish before we can read from the shaders
PipelineBarrier()
.AddMemory(VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT)
.Execute(fb->GetCommands()->GetTransferCommands(), VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT);
}
}
void VkLevelMesh::BeginFrame()
{
UploadMeshes(true);
if (useRayQuery)
{
// Wait for uploads to finish
PipelineBarrier()
.AddMemory(VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_ACCELERATION_STRUCTURE_READ_BIT_KHR | VK_ACCESS_SHADER_READ_BIT)
.Execute(fb->GetCommands()->GetTransferCommands(), VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT);
// Create a new dynamic BLAS
// To do: we should reuse the buffers. However this requires we know when the command buffers are completely done with them first.
auto deletelist = fb->GetCommands()->DrawDeleteList.get();
deletelist->Add(std::move(DynamicBLAS.ScratchBuffer));
deletelist->Add(std::move(DynamicBLAS.AccelStructBuffer));
deletelist->Add(std::move(DynamicBLAS.AccelStruct));
deletelist->Add(std::move(TopLevelAS.TransferBuffer));
deletelist->Add(std::move(TopLevelAS.InstanceBuffer));
if (Mesh->Mesh.DynamicIndexStart < (int)Mesh->Mesh.Indexes.Size())
DynamicBLAS = CreateBLAS(true, Mesh->Mesh.DynamicIndexStart, Mesh->Mesh.Indexes.Size() - Mesh->Mesh.DynamicIndexStart);
CreateTLASInstanceBuffer();
UploadTLASInstanceBuffer();
// Wait for bottom level builds to finish before using it as input to a toplevel accel structure. Also wait for the instance buffer upload to complete.
PipelineBarrier()
.AddMemory(VK_ACCESS_ACCELERATION_STRUCTURE_WRITE_BIT_KHR | VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_ACCELERATION_STRUCTURE_READ_BIT_KHR | VK_ACCESS_SHADER_READ_BIT)
.Execute(fb->GetCommands()->GetTransferCommands(), VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR | VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR);
UpdateTopLevelAS(DynamicBLAS.AccelStruct ? 2 : 1);
// Finish building the accel struct before using it from the shaders
PipelineBarrier()
.AddMemory(VK_ACCESS_ACCELERATION_STRUCTURE_WRITE_BIT_KHR, VK_ACCESS_SHADER_READ_BIT)
.Execute(fb->GetCommands()->GetTransferCommands(), VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT);
}
else
{
// Uploads must finish before we can read from the shaders
PipelineBarrier()
.AddMemory(VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT)
.Execute(fb->GetCommands()->GetTransferCommands(), VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT);
}
}
void VkLevelMesh::UploadMeshes(bool dynamicOnly)
{
if (dynamicOnly)
{
Locations.Index.Push({ Mesh->Mesh.DynamicIndexStart, (int)(Mesh->Mesh.Indexes.Size() - Mesh->Mesh.DynamicIndexStart) });
}
else
{
if (!useRayQuery)
Locations.Node.Push({ 0, (int)Mesh->Collision->get_nodes().size() });
Locations.Vertex.Push({ 0, (int)Mesh->Mesh.Vertices.Size() });
Locations.Index.Push({ 0, (int)Mesh->Mesh.Indexes.Size() });
Locations.SurfaceIndex.Push({ 0, (int)Mesh->Mesh.SurfaceIndexes.Size() });
Locations.Surface.Push({ 0, Mesh->GetSurfaceCount() });
Locations.UniformIndexes.Push({ 0, (int)Mesh->Mesh.UniformIndexes.Size() });
Locations.Uniforms.Push({ 0, (int)Mesh->Mesh.Uniforms.Size() });
Locations.Portals.Push({ 0, (int)Mesh->Portals.Size() });
Locations.Light.Push({ 0, (int)Mesh->Mesh.Lights.Size() });
Locations.LightIndex.Push({ 0, (int)Mesh->Mesh.LightIndexes.Size() });
}
VkLevelMeshUploader uploader(this);
uploader.Upload();
}
void VkLevelMesh::CreateBuffers()
{
VertexBuffer = BufferBuilder()
.Usage(
VK_BUFFER_USAGE_VERTEX_BUFFER_BIT |
VK_BUFFER_USAGE_TRANSFER_DST_BIT |
(useRayQuery ?
VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT |
VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR : 0) |
VK_BUFFER_USAGE_STORAGE_BUFFER_BIT)
.Size(Mesh->Mesh.MaxVertices * sizeof(FFlatVertex))
.DebugName("VertexBuffer")
.Create(fb->GetDevice());
UniformIndexBuffer = BufferBuilder()
.Usage(
VK_BUFFER_USAGE_VERTEX_BUFFER_BIT |
VK_BUFFER_USAGE_TRANSFER_DST_BIT)
.Size(Mesh->Mesh.MaxVertices * sizeof(int))
.DebugName("UniformIndexes")
.Create(fb->GetDevice());
IndexBuffer = BufferBuilder()
.Usage(
VK_BUFFER_USAGE_INDEX_BUFFER_BIT |
VK_BUFFER_USAGE_TRANSFER_DST_BIT |
(useRayQuery ?
VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT |
VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR : 0) |
VK_BUFFER_USAGE_STORAGE_BUFFER_BIT)
.Size((size_t)Mesh->Mesh.MaxIndexes * sizeof(uint32_t))
.DebugName("IndexBuffer")
.Create(fb->GetDevice());
NodeBuffer = BufferBuilder()
.Usage(VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT)
.Size(sizeof(CollisionNodeBufferHeader) + Mesh->Mesh.MaxNodes * sizeof(CollisionNode))
.DebugName("NodeBuffer")
.Create(fb->GetDevice());
SurfaceIndexBuffer = BufferBuilder()
.Usage(VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT)
.Size(Mesh->Mesh.MaxSurfaceIndexes * sizeof(int))
.DebugName("SurfaceBuffer")
.Create(fb->GetDevice());
SurfaceBuffer = BufferBuilder()
.Usage(VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT)
.Size(Mesh->Mesh.MaxSurfaces * sizeof(SurfaceInfo))
.DebugName("SurfaceBuffer")
.Create(fb->GetDevice());
UniformsBuffer = BufferBuilder()
.Usage(VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT)
.Size(Mesh->Mesh.MaxUniforms * sizeof(SurfaceUniforms))
.DebugName("SurfaceUniformsBuffer")
.Create(fb->GetDevice());
PortalBuffer = BufferBuilder()
.Usage(VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT)
.Size(Mesh->Portals.Size() * sizeof(PortalInfo))
.DebugName("PortalBuffer")
.Create(fb->GetDevice());
LightBuffer = BufferBuilder()
.Usage(VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT)
.Size(Mesh->Mesh.MaxLights * sizeof(LightInfo))
.DebugName("LightBuffer")
.Create(fb->GetDevice());
LightIndexBuffer = BufferBuilder()
.Usage(VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT)
.Size(Mesh->Mesh.MaxLightIndexes * sizeof(int32_t))
.DebugName("LightIndexBuffer")
.Create(fb->GetDevice());
}
VkLevelMesh::BLAS VkLevelMesh::CreateBLAS(bool preferFastBuild, int indexOffset, int indexCount)
{
BLAS blas;
VkAccelerationStructureBuildGeometryInfoKHR buildInfo = { VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_BUILD_GEOMETRY_INFO_KHR };
VkAccelerationStructureGeometryKHR accelStructBLDesc = { VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_KHR };
VkAccelerationStructureGeometryKHR* geometries[] = { &accelStructBLDesc };
accelStructBLDesc.geometryType = VK_GEOMETRY_TYPE_TRIANGLES_KHR;
accelStructBLDesc.flags = VK_GEOMETRY_OPAQUE_BIT_KHR;
accelStructBLDesc.geometry.triangles = { VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_TRIANGLES_DATA_KHR };
accelStructBLDesc.geometry.triangles.vertexFormat = VK_FORMAT_R32G32B32A32_SFLOAT;
accelStructBLDesc.geometry.triangles.vertexData.deviceAddress = VertexBuffer->GetDeviceAddress();
accelStructBLDesc.geometry.triangles.vertexStride = sizeof(FFlatVertex);
accelStructBLDesc.geometry.triangles.indexType = VK_INDEX_TYPE_UINT32;
accelStructBLDesc.geometry.triangles.indexData.deviceAddress = IndexBuffer->GetDeviceAddress() + indexOffset * sizeof(uint32_t);
accelStructBLDesc.geometry.triangles.maxVertex = Mesh->Mesh.Vertices.Size() - 1;
buildInfo.type = VK_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL_KHR;
buildInfo.flags = preferFastBuild ? VK_BUILD_ACCELERATION_STRUCTURE_PREFER_FAST_BUILD_BIT_KHR : VK_BUILD_ACCELERATION_STRUCTURE_PREFER_FAST_TRACE_BIT_KHR;
buildInfo.mode = VK_BUILD_ACCELERATION_STRUCTURE_MODE_BUILD_KHR;
buildInfo.geometryCount = 1;
buildInfo.ppGeometries = geometries;
uint32_t maxPrimitiveCount = indexCount / 3;
VkAccelerationStructureBuildSizesInfoKHR sizeInfo = { VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_BUILD_SIZES_INFO_KHR };
vkGetAccelerationStructureBuildSizesKHR(fb->GetDevice()->device, VK_ACCELERATION_STRUCTURE_BUILD_TYPE_DEVICE_KHR, &buildInfo, &maxPrimitiveCount, &sizeInfo);
blas.AccelStructBuffer = BufferBuilder()
.Usage(VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_STORAGE_BIT_KHR | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT)
.Size(sizeInfo.accelerationStructureSize)
.DebugName("BLAS.AccelStructBuffer")
.Create(fb->GetDevice());
blas.AccelStruct = AccelerationStructureBuilder()
.Type(VK_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL_KHR)
.Buffer(blas.AccelStructBuffer.get(), sizeInfo.accelerationStructureSize)
.DebugName("BLAS.AccelStruct")
.Create(fb->GetDevice());
blas.ScratchBuffer = BufferBuilder()
.Usage(VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT)
.Size(sizeInfo.buildScratchSize)
.MinAlignment(fb->GetDevice()->PhysicalDevice.Properties.AccelerationStructure.minAccelerationStructureScratchOffsetAlignment)
.DebugName("BLAS.ScratchBuffer")
.Create(fb->GetDevice());
buildInfo.dstAccelerationStructure = blas.AccelStruct->accelstruct;
buildInfo.scratchData.deviceAddress = blas.ScratchBuffer->GetDeviceAddress();
VkAccelerationStructureBuildRangeInfoKHR rangeInfo = {};
VkAccelerationStructureBuildRangeInfoKHR* rangeInfos[] = { &rangeInfo };
rangeInfo.primitiveCount = maxPrimitiveCount;
fb->GetCommands()->GetTransferCommands()->buildAccelerationStructures(1, &buildInfo, rangeInfos);
return blas;
}
void VkLevelMesh::CreateStaticBLAS()
{
StaticBLAS = CreateBLAS(false, 0, Mesh->Mesh.DynamicIndexStart);
}
void VkLevelMesh::CreateDynamicBLAS()
{
if (Mesh->Mesh.DynamicIndexStart < (int)Mesh->Mesh.Indexes.Size())
DynamicBLAS = CreateBLAS(true, Mesh->Mesh.DynamicIndexStart, Mesh->Mesh.Indexes.Size() - Mesh->Mesh.DynamicIndexStart);
}
void VkLevelMesh::CreateTLASInstanceBuffer()
{
TopLevelAS.TransferBuffer = BufferBuilder()
.Usage(VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VMA_MEMORY_USAGE_CPU_ONLY)
.Size(sizeof(VkAccelerationStructureInstanceKHR) * 2)
.DebugName("TopLevelAS.TransferBuffer")
.Create(fb->GetDevice());
TopLevelAS.InstanceBuffer = BufferBuilder()
.Usage(VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT | VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR | VK_BUFFER_USAGE_TRANSFER_DST_BIT)
.Size(sizeof(VkAccelerationStructureInstanceKHR) * 2)
.DebugName("TopLevelAS.InstanceBuffer")
.Create(fb->GetDevice());
}
void VkLevelMesh::CreateTopLevelAS(int instanceCount)
{
VkAccelerationStructureBuildGeometryInfoKHR buildInfo = { VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_BUILD_GEOMETRY_INFO_KHR };
VkAccelerationStructureGeometryKHR accelStructTLDesc = { VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_KHR };
VkAccelerationStructureGeometryKHR* geometries[] = { &accelStructTLDesc };
buildInfo.type = VK_ACCELERATION_STRUCTURE_TYPE_TOP_LEVEL_KHR;
buildInfo.mode = VK_BUILD_ACCELERATION_STRUCTURE_MODE_BUILD_KHR;
buildInfo.flags = VK_BUILD_ACCELERATION_STRUCTURE_PREFER_FAST_TRACE_BIT_KHR;
buildInfo.geometryCount = 1;
buildInfo.ppGeometries = geometries;
accelStructTLDesc.geometryType = VK_GEOMETRY_TYPE_INSTANCES_KHR;
accelStructTLDesc.geometry.instances = { VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_INSTANCES_DATA_KHR };
accelStructTLDesc.geometry.instances.data.deviceAddress = TopLevelAS.InstanceBuffer->GetDeviceAddress();
uint32_t maxInstanceCount = 2;
VkAccelerationStructureBuildSizesInfoKHR sizeInfo = { VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_BUILD_SIZES_INFO_KHR };
vkGetAccelerationStructureBuildSizesKHR(fb->GetDevice()->device, VK_ACCELERATION_STRUCTURE_BUILD_TYPE_DEVICE_KHR, &buildInfo, &maxInstanceCount, &sizeInfo);
TopLevelAS.AccelStructBuffer = BufferBuilder()
.Usage(VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_STORAGE_BIT_KHR | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT)
.Size(sizeInfo.accelerationStructureSize)
.DebugName("TopLevelAS.AccelStructBuffer")
.Create(fb->GetDevice());
TopLevelAS.AccelStruct = AccelerationStructureBuilder()
.Type(VK_ACCELERATION_STRUCTURE_TYPE_TOP_LEVEL_KHR)
.Buffer(TopLevelAS.AccelStructBuffer.get(), sizeInfo.accelerationStructureSize)
.DebugName("TopLevelAS.AccelStruct")
.Create(fb->GetDevice());
TopLevelAS.ScratchBuffer = BufferBuilder()
.Usage(VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT)
.Size(sizeInfo.buildScratchSize)
.MinAlignment(fb->GetDevice()->PhysicalDevice.Properties.AccelerationStructure.minAccelerationStructureScratchOffsetAlignment)
.DebugName("TopLevelAS.ScratchBuffer")
.Create(fb->GetDevice());
buildInfo.dstAccelerationStructure = TopLevelAS.AccelStruct->accelstruct;
buildInfo.scratchData.deviceAddress = TopLevelAS.ScratchBuffer->GetDeviceAddress();
VkAccelerationStructureBuildRangeInfoKHR rangeInfo = {};
VkAccelerationStructureBuildRangeInfoKHR* rangeInfos[] = { &rangeInfo };
rangeInfo.primitiveCount = instanceCount;
fb->GetCommands()->GetTransferCommands()->buildAccelerationStructures(1, &buildInfo, rangeInfos);
}
void VkLevelMesh::UpdateTopLevelAS(int instanceCount)
{
VkAccelerationStructureBuildGeometryInfoKHR buildInfo = { VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_BUILD_GEOMETRY_INFO_KHR };
VkAccelerationStructureGeometryKHR accelStructTLDesc = { VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_KHR };
VkAccelerationStructureGeometryKHR* geometries[] = { &accelStructTLDesc };
buildInfo.type = VK_ACCELERATION_STRUCTURE_TYPE_TOP_LEVEL_KHR;
buildInfo.mode = VK_BUILD_ACCELERATION_STRUCTURE_MODE_BUILD_KHR;
buildInfo.flags = VK_BUILD_ACCELERATION_STRUCTURE_PREFER_FAST_TRACE_BIT_KHR;
buildInfo.geometryCount = 1;
buildInfo.ppGeometries = geometries;
accelStructTLDesc.geometryType = VK_GEOMETRY_TYPE_INSTANCES_KHR;
accelStructTLDesc.geometry.instances = { VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_INSTANCES_DATA_KHR };
accelStructTLDesc.geometry.instances.data.deviceAddress = TopLevelAS.InstanceBuffer->GetDeviceAddress();
buildInfo.dstAccelerationStructure = TopLevelAS.AccelStruct->accelstruct;
buildInfo.scratchData.deviceAddress = TopLevelAS.ScratchBuffer->GetDeviceAddress();
VkAccelerationStructureBuildRangeInfoKHR rangeInfo = {};
VkAccelerationStructureBuildRangeInfoKHR* rangeInfos[] = { &rangeInfo };
rangeInfo.primitiveCount = instanceCount;
fb->GetCommands()->GetTransferCommands()->buildAccelerationStructures(1, &buildInfo, rangeInfos);
}
void VkLevelMesh::UploadTLASInstanceBuffer()
{
VkAccelerationStructureInstanceKHR instances[2] = {};
instances[0].transform.matrix[0][0] = 1.0f;
instances[0].transform.matrix[1][1] = 1.0f;
instances[0].transform.matrix[2][2] = 1.0f;
instances[0].mask = 0xff;
instances[0].flags = 0;
instances[0].accelerationStructureReference = StaticBLAS.AccelStruct->GetDeviceAddress();
if (DynamicBLAS.AccelStruct)
{
instances[1].transform.matrix[0][0] = 1.0f;
instances[1].transform.matrix[1][1] = 1.0f;
instances[1].transform.matrix[2][2] = 1.0f;
instances[1].mask = 0xff;
instances[1].flags = 0;
instances[1].accelerationStructureReference = DynamicBLAS.AccelStruct->GetDeviceAddress();
}
auto data = (uint8_t*)TopLevelAS.TransferBuffer->Map(0, sizeof(VkAccelerationStructureInstanceKHR) * 2);
memcpy(data, instances, sizeof(VkAccelerationStructureInstanceKHR) * 2);
TopLevelAS.TransferBuffer->Unmap();
fb->GetCommands()->GetTransferCommands()->copyBuffer(TopLevelAS.TransferBuffer.get(), TopLevelAS.InstanceBuffer.get());
}
/////////////////////////////////////////////////////////////////////////////
VkLevelMeshUploader::VkLevelMeshUploader(VkLevelMesh* mesh) : Mesh(mesh)
{
}
void VkLevelMeshUploader::Upload()
{
size_t transferBufferSize = GetTransferSize();
if (transferBufferSize == 0)
{
ClearRanges();
return;
}
BeginTransfer(transferBufferSize);
UploadNodes();
UploadRanges(Mesh->Locations.Vertex, Mesh->Mesh->Mesh.Vertices.Data(), Mesh->VertexBuffer.get());
UploadRanges(Mesh->Locations.UniformIndexes, Mesh->Mesh->Mesh.UniformIndexes.Data(), Mesh->UniformIndexBuffer.get());
UploadRanges(Mesh->Locations.Index, Mesh->Mesh->Mesh.Indexes.Data(), Mesh->IndexBuffer.get());
UploadRanges(Mesh->Locations.SurfaceIndex, Mesh->Mesh->Mesh.SurfaceIndexes.Data(), Mesh->SurfaceIndexBuffer.get());
UploadRanges(Mesh->Locations.LightIndex, Mesh->Mesh->Mesh.LightIndexes.Data(), Mesh->LightIndexBuffer.get());
UploadSurfaces();
UploadUniforms();
UploadPortals();
UploadLights();
EndTransfer(transferBufferSize);
ClearRanges();
}
void VkLevelMeshUploader::ClearRanges()
{
Mesh->Locations.Vertex.Clear();
Mesh->Locations.Index.Clear();
Mesh->Locations.Node.Clear();
Mesh->Locations.SurfaceIndex.Clear();
Mesh->Locations.Surface.Clear();
Mesh->Locations.UniformIndexes.Clear();
Mesh->Locations.Uniforms.Clear();
Mesh->Locations.Portals.Clear();
Mesh->Locations.Light.Clear();
Mesh->Locations.LightIndex.Clear();
}
void VkLevelMeshUploader::BeginTransfer(size_t transferBufferSize)
{
cmdbuffer = Mesh->fb->GetCommands()->GetTransferCommands();
transferBuffer = BufferBuilder()
.Usage(VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VMA_MEMORY_USAGE_CPU_ONLY)
.Size(transferBufferSize)
.DebugName("UploadMeshes")
.Create(Mesh->fb->GetDevice());
data = (uint8_t*)transferBuffer->Map(0, transferBufferSize);
datapos = 0;
}
void VkLevelMeshUploader::EndTransfer(size_t transferBufferSize)
{
assert(datapos == transferBufferSize);
transferBuffer->Unmap();
Mesh->fb->GetCommands()->TransferDeleteList->Add(std::move(transferBuffer));
}
static FVector3 SwapYZ(const FVector3& v)
{
return FVector3(v.X, v.Z, v.Y);
}
void VkLevelMeshUploader::UploadNodes()
{
// Always update the header struct of the collision storage buffer block if something changed
if (Mesh->Locations.Node.Size() > 0)
{
CollisionNodeBufferHeader nodesHeader;
nodesHeader.root = Mesh->Mesh->Collision->get_root();
*((CollisionNodeBufferHeader*)(data + datapos)) = nodesHeader;
cmdbuffer->copyBuffer(transferBuffer.get(), Mesh->NodeBuffer.get(), datapos, 0, sizeof(CollisionNodeBufferHeader));
datapos += sizeof(CollisionNodeBufferHeader) + sizeof(CollisionNode);
}
// Copy collision nodes
for (const MeshBufferRange& range : Mesh->Locations.Node)
{
const auto& srcnodes = Mesh->Mesh->Collision->get_nodes();
CollisionNode* nodes = (CollisionNode*)(data + datapos);
for (int i = 0, count = range.Size; i < count; i++)
{
const auto& node = srcnodes[range.Offset + i];
CollisionNode info;
info.center = SwapYZ(node.aabb.Center);
info.extents = SwapYZ(node.aabb.Extents);
info.left = node.left;
info.right = node.right;
info.element_index = node.element_index;
*(nodes++) = info;
}
size_t copysize = range.Size * sizeof(CollisionNode);
if (copysize > 0)
cmdbuffer->copyBuffer(transferBuffer.get(), Mesh->NodeBuffer.get(), datapos, sizeof(CollisionNodeBufferHeader) + range.Offset * sizeof(CollisionNode), copysize);
datapos += copysize;
}
}
template<typename T>
void VkLevelMeshUploader::UploadRanges(const TArray<MeshBufferRange>& ranges, const T* srcbuffer, VulkanBuffer* destbuffer)
{
for (const MeshBufferRange& range : ranges)
{
size_t copysize = range.Size * sizeof(T);
memcpy(data + datapos, srcbuffer + range.Offset, copysize);
if (copysize > 0)
cmdbuffer->copyBuffer(transferBuffer.get(), destbuffer, datapos, range.Offset * sizeof(T), copysize);
datapos += copysize;
}
}
void VkLevelMeshUploader::UploadSurfaces()
{
for (const MeshBufferRange& range : Mesh->Locations.Surface)
{
SurfaceInfo* surfaces = (SurfaceInfo*)(data + datapos);
for (int j = 0, count = range.Size; j < count; j++)
{
LevelMeshSurface* surface = Mesh->Mesh->GetSurface(range.Offset + j);
SurfaceInfo info;
info.Normal = FVector3(surface->Plane.X, surface->Plane.Z, surface->Plane.Y);
info.PortalIndex = surface->PortalIndex;
info.Sky = surface->IsSky;
info.Alpha = surface->Alpha;
if (surface->Texture.isValid())
{
info.TextureIndex = Mesh->fb->GetBindlessTextureIndex(surface->Texture);
}
else
{
info.TextureIndex = 0;
}
info.LightStart = surface->LightList.Pos;
info.LightEnd = surface->LightList.Pos + surface->LightList.Count;
*(surfaces++) = info;
}
size_t copysize = range.Size * sizeof(SurfaceInfo);
if (copysize > 0)
cmdbuffer->copyBuffer(transferBuffer.get(), Mesh->SurfaceBuffer.get(), datapos, range.Offset * sizeof(SurfaceInfo), copysize);
datapos += copysize;
}
}
void VkLevelMeshUploader::UploadUniforms()
{
for (const MeshBufferRange& range : Mesh->Locations.Uniforms)
{
for (int j = 0, count = range.Size; j < count; j++)
{
auto& surfaceUniforms = Mesh->Mesh->Mesh.Uniforms[range.Offset + j];
auto& material = Mesh->Mesh->Mesh.Materials[range.Offset + j];
/*if (material.mMaterial)
{
auto source = material.mMaterial->Source();
surfaceUniforms.uSpecularMaterial = { source->GetGlossiness(), source->GetSpecularLevel() };
surfaceUniforms.uTextureIndex = Mesh->fb->GetBindlessTextureIndex(material.mMaterial, material.mClampMode, material.mTranslation);
}
else*/
{
surfaceUniforms.uTextureIndex = 0;
}
}
SurfaceUniforms* uniforms = (SurfaceUniforms*)(data + datapos);
size_t copysize = range.Size * sizeof(SurfaceUniforms);
memcpy(uniforms, Mesh->Mesh->Mesh.Uniforms.Data(), copysize);
if (copysize > 0)
cmdbuffer->copyBuffer(transferBuffer.get(), Mesh->UniformsBuffer.get(), datapos, range.Offset * sizeof(SurfaceUniforms), copysize);
datapos += copysize;
}
}
void VkLevelMeshUploader::UploadPortals()
{
for (const MeshBufferRange& range : Mesh->Locations.Portals)
{
PortalInfo* portals = (PortalInfo*)(data + datapos);
for (int i = 0, count = range.Size; i < count; i++)
{
const auto& portal = Mesh->Mesh->Portals[range.Offset + i];
PortalInfo info;
info.transformation = portal.transformation;
*(portals++) = info;
}
size_t copysize = range.Size * sizeof(PortalInfo);
if (copysize > 0)
cmdbuffer->copyBuffer(transferBuffer.get(), Mesh->PortalBuffer.get(), datapos, range.Offset * sizeof(PortalInfo), copysize);
datapos += copysize;
}
}
void VkLevelMeshUploader::UploadLights()
{
for (const MeshBufferRange& range : Mesh->Locations.Light)
{
LightInfo* lights = (LightInfo*)(data + datapos);
for (int i = 0, count = range.Size; i < count; i++)
{
const auto& light = Mesh->Mesh->Mesh.Lights[range.Offset + i];
LightInfo info;
info.Origin = SwapYZ(light.Origin);
info.RelativeOrigin = SwapYZ(light.RelativeOrigin);
info.Radius = light.Radius;
info.Intensity = light.Intensity;
info.InnerAngleCos = light.InnerAngleCos;
info.OuterAngleCos = light.OuterAngleCos;
info.SpotDir = SwapYZ(light.SpotDir);
info.Color = light.Color;
info.SoftShadowRadius = light.SoftShadowRadius;
*(lights++) = info;
}
size_t copysize = range.Size * sizeof(LightInfo);
if (copysize > 0)
cmdbuffer->copyBuffer(transferBuffer.get(), Mesh->LightBuffer.get(), datapos, range.Offset * sizeof(LightInfo), copysize);
datapos += copysize;
}
}
size_t VkLevelMeshUploader::GetTransferSize()
{
// Figure out how much memory we need to transfer it to the GPU
size_t transferBufferSize = 0;
if (Mesh->Locations.Node.Size() > 0) transferBufferSize += sizeof(CollisionNodeBufferHeader) + sizeof(CollisionNode);
for (const MeshBufferRange& range : Mesh->Locations.Node) transferBufferSize += range.Size * sizeof(CollisionNode);
for (const MeshBufferRange& range : Mesh->Locations.Vertex) transferBufferSize += range.Size * sizeof(FFlatVertex);
for (const MeshBufferRange& range : Mesh->Locations.UniformIndexes) transferBufferSize += range.Size * sizeof(int);
for (const MeshBufferRange& range : Mesh->Locations.Index) transferBufferSize += range.Size * sizeof(uint32_t);
for (const MeshBufferRange& range : Mesh->Locations.SurfaceIndex) transferBufferSize += range.Size * sizeof(int);
for (const MeshBufferRange& range : Mesh->Locations.Surface) transferBufferSize += range.Size * sizeof(SurfaceInfo);
for (const MeshBufferRange& range : Mesh->Locations.Uniforms) transferBufferSize += range.Size * sizeof(SurfaceUniforms);
for (const MeshBufferRange& range : Mesh->Locations.Portals) transferBufferSize += range.Size * sizeof(PortalInfo);
for (const MeshBufferRange& range : Mesh->Locations.LightIndex) transferBufferSize += range.Size * sizeof(int32_t);
for (const MeshBufferRange& range : Mesh->Locations.Light) transferBufferSize += range.Size * sizeof(LightInfo);
return transferBufferSize;
}

View file

@ -0,0 +1,192 @@
#pragma once
#include "zvulkan/vulkanobjects.h"
#include "hw_levelmesh.h"
#include "framework/matrix.h"
class VulkanRenderDevice;
struct CollisionNodeBufferHeader
{
int root;
int padding1;
int padding2;
int padding3;
};
struct CollisionNode
{
FVector3 center;
float padding1;
FVector3 extents;
float padding2;
int left;
int right;
int element_index;
int padding3;
};
struct SurfaceInfo
{
FVector3 Normal;
float Sky;
uint32_t PortalIndex;
int32_t TextureIndex;
float Alpha;
float Padding0;
uint32_t LightStart;
uint32_t LightEnd;
uint32_t Padding1;
uint32_t Padding2;
};
struct PortalInfo
{
VSMatrix transformation;
};
struct LightInfo
{
FVector3 Origin;
float Padding0;
FVector3 RelativeOrigin;
float Padding1;
float Radius;
float Intensity;
float InnerAngleCos;
float OuterAngleCos;
FVector3 SpotDir;
float SoftShadowRadius;
FVector3 Color;
float Padding3;
};
static_assert(sizeof(LightInfo) == sizeof(float) * 20);
struct MeshBufferRange
{
int Offset = 0;
int Size = 0;
};
class VkLevelMesh
{
public:
VkLevelMesh(VulkanRenderDevice* fb);
void SetLevelMesh(LevelMesh* mesh);
void BeginFrame();
VulkanAccelerationStructure* GetAccelStruct() { return TopLevelAS.AccelStruct.get(); }
VulkanBuffer* GetVertexBuffer() { return VertexBuffer.get(); }
VulkanBuffer* GetUniformIndexBuffer() { return UniformIndexBuffer.get(); }
VulkanBuffer* GetIndexBuffer() { return IndexBuffer.get(); }
VulkanBuffer* GetNodeBuffer() { return NodeBuffer.get(); }
VulkanBuffer* GetSurfaceIndexBuffer() { return SurfaceIndexBuffer.get(); }
VulkanBuffer* GetSurfaceBuffer() { return SurfaceBuffer.get(); }
VulkanBuffer* GetUniformsBuffer() { return UniformsBuffer.get(); }
VulkanBuffer* GetPortalBuffer() { return PortalBuffer.get(); }
VulkanBuffer* GetLightBuffer() { return LightBuffer.get(); }
VulkanBuffer* GetLightIndexBuffer() { return LightIndexBuffer.get(); }
LevelMesh* GetMesh() { return Mesh; }
private:
struct BLAS
{
std::unique_ptr<VulkanBuffer> ScratchBuffer;
std::unique_ptr<VulkanBuffer> AccelStructBuffer;
std::unique_ptr<VulkanAccelerationStructure> AccelStruct;
};
void Reset();
void CreateVulkanObjects();
void CreateBuffers();
void CreateStaticBLAS();
void CreateDynamicBLAS();
void CreateTLASInstanceBuffer();
void CreateTopLevelAS(int instanceCount);
void UploadMeshes(bool dynamicOnly);
void UploadTLASInstanceBuffer();
void UpdateTopLevelAS(int instanceCount);
BLAS CreateBLAS(bool preferFastBuild, int indexOffset, int indexCount);
VulkanRenderDevice* fb = nullptr;
bool useRayQuery = true;
LevelMesh NullMesh;
LevelMesh* Mesh = nullptr;
struct
{
TArray<MeshBufferRange> Vertex;
TArray<MeshBufferRange> Index;
TArray<MeshBufferRange> Node;
TArray<MeshBufferRange> SurfaceIndex;
TArray<MeshBufferRange> Surface;
TArray<MeshBufferRange> UniformIndexes;
TArray<MeshBufferRange> Uniforms;
TArray<MeshBufferRange> Portals;
TArray<MeshBufferRange> Light;
TArray<MeshBufferRange> LightIndex;
} Locations;
std::unique_ptr<VulkanBuffer> VertexBuffer;
std::unique_ptr<VulkanBuffer> UniformIndexBuffer;
std::unique_ptr<VulkanBuffer> IndexBuffer;
std::unique_ptr<VulkanBuffer> SurfaceIndexBuffer;
std::unique_ptr<VulkanBuffer> SurfaceBuffer;
std::unique_ptr<VulkanBuffer> UniformsBuffer;
std::unique_ptr<VulkanBuffer> PortalBuffer;
std::unique_ptr<VulkanBuffer> LightBuffer;
std::unique_ptr<VulkanBuffer> LightIndexBuffer;
std::unique_ptr<VulkanBuffer> NodeBuffer;
BLAS StaticBLAS;
BLAS DynamicBLAS;
struct
{
std::unique_ptr<VulkanBuffer> TransferBuffer;
std::unique_ptr<VulkanBuffer> ScratchBuffer;
std::unique_ptr<VulkanBuffer> InstanceBuffer;
std::unique_ptr<VulkanBuffer> AccelStructBuffer;
std::unique_ptr<VulkanAccelerationStructure> AccelStruct;
} TopLevelAS;
friend class VkLevelMeshUploader;
};
class VkLevelMeshUploader
{
public:
VkLevelMeshUploader(VkLevelMesh* mesh);
void Upload();
private:
void BeginTransfer(size_t transferBufferSize);
void EndTransfer(size_t transferBufferSize);
size_t GetTransferSize();
void ClearRanges();
void UploadNodes();
void UploadSurfaces();
void UploadUniforms();
void UploadPortals();
void UploadLights();
template<typename T>
void UploadRanges(const TArray<MeshBufferRange>& ranges, const T* srcbuffer, VulkanBuffer* destbuffer);
VkLevelMesh* Mesh = nullptr;
uint8_t* data = nullptr;
size_t datapos = 0;
VulkanCommandBuffer* cmdbuffer = nullptr;
std::unique_ptr<VulkanBuffer> transferBuffer;
};

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,236 @@
#pragma once
#include "hw_levelmesh.h"
#include "zvulkan/vulkanobjects.h"
#include <dp_rect_pack/dp_rect_pack.h>
typedef dp::rect_pack::RectPacker<int> RectPacker;
class VulkanRenderDevice;
class FString;
class ShaderIncludeResult;
struct Uniforms
{
FVector3 SunDir;
float Padding1;
FVector3 SunColor;
float SunIntensity;
};
struct LightmapRaytracePC
{
int32_t SurfaceIndex;
int32_t Padding0;
int32_t Padding1;
int32_t Padding2;
FVector3 WorldToLocal;
float TextureSize;
FVector3 ProjLocalToU;
float Padding3;
FVector3 ProjLocalToV;
float Padding4;
float TileX;
float TileY;
float TileWidth;
float TileHeight;
};
struct LightmapCopyPC
{
int SrcTexSize;
int DestTexSize;
int Padding1;
int Padding2;
};
struct LightmapBakeImage
{
struct
{
std::unique_ptr<VulkanImage> Image;
std::unique_ptr<VulkanImageView> View;
std::unique_ptr<VulkanFramebuffer> Framebuffer;
} raytrace;
struct
{
std::unique_ptr<VulkanImage> Image;
std::unique_ptr<VulkanImageView> View;
std::unique_ptr<VulkanFramebuffer> Framebuffer;
std::unique_ptr<VulkanDescriptorSet> DescriptorSet;
} resolve;
struct
{
std::unique_ptr<VulkanImage> Image;
std::unique_ptr<VulkanImageView> View;
std::unique_ptr<VulkanFramebuffer> Framebuffer;
std::unique_ptr<VulkanDescriptorSet> DescriptorSet[2];
} blur;
struct
{
std::unique_ptr<VulkanDescriptorSet> DescriptorSet;
} copy;
// how much of the image is used for the baking
uint16_t maxX = 0;
uint16_t maxY = 0;
};
struct SelectedTile
{
LightmapTile* Tile = nullptr;
int X = -1;
int Y = -1;
bool Rendered = false;
};
struct CopyTileInfo
{
int SrcPosX;
int SrcPosY;
int DestPosX;
int DestPosY;
int TileWidth;
int TileHeight;
int Padding1;
int Padding2;
};
static_assert(sizeof(CopyTileInfo) == sizeof(int32_t) * 8);
class VkLightmapper
{
public:
VkLightmapper(VulkanRenderDevice* fb);
~VkLightmapper();
void BeginFrame();
void Raytrace(const TArray<LightmapTile*>& surfaces);
void SetLevelMesh(LevelMesh* level);
private:
void ReleaseResources();
void SelectTiles(const TArray<LightmapTile*>& surfaces);
void UploadUniforms();
void Render();
void Resolve();
void Blur();
void CopyResult();
void UpdateAccelStructDescriptors();
void CreateShaders();
void CreateRaytracePipeline();
void CreateResolvePipeline();
void CreateBlurPipeline();
void CreateCopyPipeline();
void CreateUniformBuffer();
void CreateTileBuffer();
void CreateDrawIndexedBuffer();
void CreateBakeImage();
int GetRaytracePipelineIndex();
static FString LoadPrivateShaderLump(const char* lumpname);
static FString LoadPublicShaderLump(const char* lumpname);
static ShaderIncludeResult OnInclude(FString headerName, FString includerName, size_t depth, bool system);
FVector3 SwapYZ(const FVector3& v) { return FVector3(v.X, v.Z, v.Y); }
VulkanRenderDevice* fb = nullptr;
LevelMesh* mesh = nullptr;
bool useRayQuery = true;
TArray<SelectedTile> selectedTiles;
TArray<TArray<SelectedTile*>> copylists;
struct
{
std::unique_ptr<VulkanBuffer> Buffer;
std::unique_ptr<VulkanBuffer> TransferBuffer;
uint8_t* Uniforms = nullptr;
int Index = 0;
int NumStructs = 256;
VkDeviceSize StructStride = sizeof(Uniforms);
} uniforms;
struct
{
const int BufferSize = 100'000;
std::unique_ptr<VulkanBuffer> Buffer;
CopyTileInfo* Tiles = nullptr;
} copytiles;
struct
{
const int BufferSize = 100'000;
std::unique_ptr<VulkanBuffer> CommandsBuffer;
std::unique_ptr<VulkanBuffer> ConstantsBuffer;
VkDrawIndexedIndirectCommand* Commands = nullptr;
LightmapRaytracePC* Constants = nullptr;
int Pos = 0;
} drawindexed;
struct
{
std::unique_ptr<VulkanShader> vertRaytrace;
std::unique_ptr<VulkanShader> vertScreenquad;
std::unique_ptr<VulkanShader> vertCopy;
std::unique_ptr<VulkanShader> fragRaytrace[16];
std::unique_ptr<VulkanShader> fragResolve;
std::unique_ptr<VulkanShader> fragBlur[2];
std::unique_ptr<VulkanShader> fragCopy;
} shaders;
struct
{
std::unique_ptr<VulkanDescriptorSetLayout> descriptorSetLayout0;
std::unique_ptr<VulkanDescriptorSetLayout> descriptorSetLayout1;
std::unique_ptr<VulkanPipelineLayout> pipelineLayout;
std::unique_ptr<VulkanPipeline> pipeline[16];
std::unique_ptr<VulkanRenderPass> renderPass;
std::unique_ptr<VulkanDescriptorPool> descriptorPool0;
std::unique_ptr<VulkanDescriptorPool> descriptorPool1;
std::unique_ptr<VulkanDescriptorSet> descriptorSet0;
std::unique_ptr<VulkanDescriptorSet> descriptorSet1;
} raytrace;
struct
{
std::unique_ptr<VulkanDescriptorSetLayout> descriptorSetLayout;
std::unique_ptr<VulkanPipelineLayout> pipelineLayout;
std::unique_ptr<VulkanPipeline> pipeline;
std::unique_ptr<VulkanRenderPass> renderPass;
std::unique_ptr<VulkanDescriptorPool> descriptorPool;
std::unique_ptr<VulkanSampler> sampler;
} resolve;
struct
{
std::unique_ptr<VulkanDescriptorSetLayout> descriptorSetLayout;
std::unique_ptr<VulkanPipelineLayout> pipelineLayout;
std::unique_ptr<VulkanPipeline> pipeline[2];
std::unique_ptr<VulkanRenderPass> renderPass;
std::unique_ptr<VulkanDescriptorPool> descriptorPool;
std::unique_ptr<VulkanSampler> sampler;
} blur;
struct
{
std::unique_ptr<VulkanDescriptorSetLayout> descriptorSetLayout;
std::unique_ptr<VulkanPipelineLayout> pipelineLayout;
std::unique_ptr<VulkanPipeline> pipeline;
std::unique_ptr<VulkanRenderPass> renderPass;
std::unique_ptr<VulkanDescriptorPool> descriptorPool;
std::unique_ptr<VulkanSampler> sampler;
} copy;
LightmapBakeImage bakeImage;
static const int bakeImageSize = 2048;
};

View file

@ -0,0 +1,658 @@
#include "vk_renderdevice.h"
#include "vk_levelmesh.h"
#include "vk_lightmapper.h"
#include "stacktrace.h"
#include "levelmeshviewer.h"
#include "framework/matrix.h"
#include "glsl/vert_viewer.glsl.h"
#include "glsl/frag_viewer.glsl.h"
#include "glsl/binding_viewer.glsl.h"
#include "glsl/polyfill_rayquery.glsl.h"
#include "glsl/montecarlo.glsl.h"
#include "glsl/trace_ambient_occlusion.glsl.h"
#include "glsl/trace_bounce.glsl.h"
#include "glsl/trace_levelmesh.glsl.h"
#include "glsl/trace_light.glsl.h"
#include "glsl/trace_sunlight.glsl.h"
#include <zvulkan/vulkanbuilders.h>
#include <zvulkan/vulkancompatibledevice.h>
#include <zvulkan/vulkanswapchain.h>
#include <stdexcept>
extern bool VKDebug;
extern bool NoRtx;
void VulkanError(const char* text)
{
throw std::runtime_error(text);
}
void VulkanPrintLog(const char* typestr, const std::string& msg)
{
printf(" [%s] %s\n", typestr, msg.c_str());
printf(" %s\n", CaptureStackTraceText(2).c_str());
}
VulkanRenderDevice::VulkanRenderDevice(LevelMeshViewer* viewer)
{
if (viewer)
{
#ifdef WIN32
auto instance = VulkanInstanceBuilder()
.RequireSurfaceExtensions()
.DebugLayer(VKDebug)
.Create();
auto surface = VulkanSurfaceBuilder()
.Win32Window(viewer->GetWindowHandle())
.Create(instance);
device = VulkanDeviceBuilder()
.Surface(surface)
.OptionalRayQuery()
.RequireExtension(VK_EXT_DESCRIPTOR_INDEXING_EXTENSION_NAME)
.Create(instance);
swapchain = VulkanSwapChainBuilder()
.Create(device.get());
#else
VulkanError("No viewer supported on this platform");
#endif
}
else
{
auto instance = VulkanInstanceBuilder()
.DebugLayer(VKDebug)
.Create();
device = VulkanDeviceBuilder()
.OptionalRayQuery()
.RequireExtension(VK_EXT_DESCRIPTOR_INDEXING_EXTENSION_NAME)
.Create(instance);
}
useRayQuery = !NoRtx && device->PhysicalDevice.Features.RayQuery.rayQuery;
commands = std::make_unique<VkCommandBufferManager>(this);
descriptors = std::make_unique<VkDescriptorSetManager>(this);
samplers = std::make_unique<VkSamplerManager>(this);
textures = std::make_unique<VkTextureManager>(this);
levelmesh = std::make_unique<VkLevelMesh>(this);
lightmapper = std::make_unique<VkLightmapper>(this);
descriptors->AddBindlessTextureIndex(GetTextureManager()->GetNullTextureView(), GetSamplerManager()->Get());
descriptors->UpdateBindlessDescriptorSet();
CreateViewerObjects();
}
VulkanRenderDevice::~VulkanRenderDevice()
{
vkDeviceWaitIdle(device->device);
}
int VulkanRenderDevice::GetBindlessTextureIndex(FTextureID textureID)
{
if (!textureID.isValid())
return 0;
FGameTexture* tex = TexMan.GetGameTexture(textureID);
int& textureIndex = TextureIndexes[tex];
if (textureIndex != 0)
return textureIndex;
VulkanImageView* view;
VulkanSampler* sampler = GetSamplerManager()->Get();
if (tex->GetImageWidth() > 0 && tex->GetImageHeight() > 0)
{
int index = GetTextureManager()->CreateGameTexture(tex->GetImageWidth(), tex->GetImageHeight(), tex->GetImagePixels());
view = GetTextureManager()->GetGameTextureView(index);
}
else
{
view = GetTextureManager()->GetNullTextureView();
}
textureIndex = GetDescriptorSetManager()->AddBindlessTextureIndex(view, sampler);
return textureIndex;
}
void VulkanRenderDevice::DrawViewer(const FVector3& cameraPos, const VSMatrix& viewToWorld, float fovy, float aspect, const FVector3& sundir, const FVector3& suncolor, float sunintensity)
{
int imageIndex = GetCommands()->AcquireImage();
if (imageIndex < 0)
{
return;
}
WriteDescriptors write;
write.AddBuffer(Viewer.DescriptorSet.get(), 0, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, GetLevelMesh()->GetSurfaceIndexBuffer());
write.AddBuffer(Viewer.DescriptorSet.get(), 1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, GetLevelMesh()->GetSurfaceBuffer());
write.AddBuffer(Viewer.DescriptorSet.get(), 2, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, GetLevelMesh()->GetLightBuffer());
write.AddBuffer(Viewer.DescriptorSet.get(), 3, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, GetLevelMesh()->GetLightIndexBuffer());
write.AddBuffer(Viewer.DescriptorSet.get(), 4, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, GetLevelMesh()->GetPortalBuffer());
if (useRayQuery)
{
write.AddAccelerationStructure(Viewer.DescriptorSet.get(), 5, GetLevelMesh()->GetAccelStruct());
}
else
{
write.AddBuffer(Viewer.DescriptorSet.get(), 5, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, GetLevelMesh()->GetNodeBuffer());
}
write.AddBuffer(Viewer.DescriptorSet.get(), 6, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, GetLevelMesh()->GetVertexBuffer());
write.AddBuffer(Viewer.DescriptorSet.get(), 7, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, GetLevelMesh()->GetIndexBuffer());
write.Execute(device.get());
auto commands = GetCommands()->GetDrawCommands();
RenderPassBegin()
.RenderPass(Viewer.RenderPass.get())
.Framebuffer(Framebuffers[imageIndex].get())
.RenderArea(0, 0, CurrentWidth, CurrentHeight)
.AddClearColor(0.0f, 0.0f, 0.0f, 1.0f)
.AddClearDepth(1.0f)
.Execute(commands);
VkViewport viewport = {};
viewport.width = (float)CurrentWidth;
viewport.height = (float)CurrentHeight;
viewport.maxDepth = 1.0f;
commands->setViewport(0, 1, &viewport);
VkRect2D scissor = {};
scissor.extent.width = CurrentWidth;
scissor.extent.height = CurrentHeight;
commands->setScissor(0, 1, &scissor);
float f = 1.0f / std::tan(fovy * (pi::pif() / 360.0f));
ViewerPushConstants pushconstants;
pushconstants.ViewToWorld = viewToWorld;
pushconstants.CameraPos = cameraPos;
pushconstants.ProjX = f / aspect;
pushconstants.ProjY = f;
pushconstants.SunDir = sundir;
pushconstants.SunColor = suncolor;
pushconstants.SunIntensity = sunintensity;
commands->bindDescriptorSet(VK_PIPELINE_BIND_POINT_GRAPHICS, Viewer.PipelineLayout.get(), 0, Viewer.DescriptorSet.get());
commands->bindDescriptorSet(VK_PIPELINE_BIND_POINT_GRAPHICS, Viewer.PipelineLayout.get(), 1, descriptors->GetBindlessSet());
commands->bindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, Viewer.Pipeline.get());
commands->pushConstants(Viewer.PipelineLayout.get(), VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, 0, sizeof(ViewerPushConstants), &pushconstants);
commands->draw(4, 1, 0, 0);
commands->endRenderPass();
GetCommands()->SubmitAndWait(imageIndex);
}
static FString LoadPrivateShaderLump(const char* lumpname)
{
static std::map<FString, FString> sources =
{
{ "shaders/lightmap/binding_viewer.glsl", binding_viewer_glsl },
{ "shaders/lightmap/montecarlo.glsl", montecarlo_glsl },
{ "shaders/lightmap/polyfill_rayquery.glsl", polyfill_rayquery_glsl },
{ "shaders/lightmap/trace_ambient_occlusion.glsl", trace_ambient_occlusion_glsl },
{ "shaders/lightmap/trace_bounce.glsl", trace_bounce_glsl },
{ "shaders/lightmap/trace_levelmesh.glsl", trace_levelmesh_glsl },
{ "shaders/lightmap/trace_light.glsl", trace_light_glsl },
{ "shaders/lightmap/trace_sunlight.glsl", trace_sunlight_glsl },
};
auto it = sources.find(lumpname);
if (it != sources.end())
return it->second;
else
return FString();
}
static FString LoadPublicShaderLump(const char* lumpname)
{
return LoadPrivateShaderLump(lumpname);
}
static ShaderIncludeResult OnInclude(FString headerName, FString includerName, size_t depth, bool system)
{
if (depth > 8)
{
return ShaderIncludeResult("Too much include recursion!");
}
FString includeguardname;
includeguardname << "_HEADERGUARD_" << headerName.GetChars();
includeguardname.ReplaceChars("/\\.", '_');
FString code;
code << "#ifndef " << includeguardname.GetChars() << "\n";
code << "#define " << includeguardname.GetChars() << "\n";
code << "#line 1\n";
if (system)
code << LoadPrivateShaderLump(headerName.GetChars()).GetChars() << "\n";
else
code << LoadPublicShaderLump(headerName.GetChars()).GetChars() << "\n";
code << "#endif\n";
return ShaderIncludeResult(headerName.GetChars(), code.GetChars());
}
void VulkanRenderDevice::CreateViewerObjects()
{
DescriptorSetLayoutBuilder builder;
builder.AddBinding(0, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_FRAGMENT_BIT);
builder.AddBinding(1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_FRAGMENT_BIT);
builder.AddBinding(2, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_FRAGMENT_BIT);
builder.AddBinding(3, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_FRAGMENT_BIT);
builder.AddBinding(4, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_FRAGMENT_BIT);
if (useRayQuery)
{
builder.AddBinding(5, VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR, 1, VK_SHADER_STAGE_FRAGMENT_BIT);
}
else
{
builder.AddBinding(5, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_FRAGMENT_BIT);
}
builder.AddBinding(6, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_FRAGMENT_BIT);
builder.AddBinding(7, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_FRAGMENT_BIT);
builder.DebugName("Viewer.DescriptorSetLayout");
Viewer.DescriptorSetLayout = builder.Create(device.get());
Viewer.DescriptorPool = DescriptorPoolBuilder()
.AddPoolSize(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1)
.AddPoolSize(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 6)
.MaxSets(1)
.DebugName("Viewer.DescriptorPool")
.Create(device.get());
Viewer.DescriptorSet = Viewer.DescriptorPool->allocate(Viewer.DescriptorSetLayout.get());
Viewer.DescriptorSet->SetDebugName("raytrace.descriptorSet1");
std::string versionBlock = R"(
#version 460
#extension GL_GOOGLE_include_directive : enable
#extension GL_EXT_nonuniform_qualifier : enable
)";
if (useRayQuery)
{
versionBlock += "#extension GL_EXT_ray_query : require\r\n";
versionBlock += "#define USE_RAYQUERY\r\n";
}
auto onIncludeLocal = [](std::string headerName, std::string includerName, size_t depth) { return OnInclude(headerName.c_str(), includerName.c_str(), depth, false); };
auto onIncludeSystem = [](std::string headerName, std::string includerName, size_t depth) { return OnInclude(headerName.c_str(), includerName.c_str(), depth, true); };
Viewer.VertexShader = ShaderBuilder()
.Type(ShaderType::Vertex)
.AddSource("versionblock", versionBlock)
.AddSource("vert_viewer.glsl", vert_viewer_glsl)
.OnIncludeLocal(onIncludeLocal)
.OnIncludeSystem(onIncludeSystem)
.DebugName("Viewer.VertexShader")
.Create("vertex", device.get());
Viewer.FragmentShader = ShaderBuilder()
.Type(ShaderType::Fragment)
.AddSource("versionblock", versionBlock)
.AddSource("frag_viewer.glsl", frag_viewer_glsl)
.OnIncludeLocal(onIncludeLocal)
.OnIncludeSystem(onIncludeSystem)
.DebugName("Viewer.FragmentShader")
.Create("vertex", device.get());
Viewer.PipelineLayout = PipelineLayoutBuilder()
.AddSetLayout(Viewer.DescriptorSetLayout.get())
.AddSetLayout(descriptors->GetBindlessLayout())
.AddPushConstantRange(VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, 0, sizeof(ViewerPushConstants))
.DebugName("Viewer.PipelineLayout")
.Create(device.get());
Viewer.RenderPass = RenderPassBuilder()
.AddAttachment(VK_FORMAT_B8G8R8A8_UNORM, VK_SAMPLE_COUNT_1_BIT, VK_ATTACHMENT_LOAD_OP_CLEAR, VK_ATTACHMENT_STORE_OP_STORE, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR)
.AddSubpass()
.AddSubpassColorAttachmentRef(0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL)
.Create(device.get());
Viewer.Pipeline = GraphicsPipelineBuilder()
.RenderPass(Viewer.RenderPass.get())
.Layout(Viewer.PipelineLayout.get())
.Topology(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP)
.AddVertexShader(Viewer.VertexShader.get())
.AddFragmentShader(Viewer.FragmentShader.get())
.AddDynamicState(VK_DYNAMIC_STATE_VIEWPORT)
.AddDynamicState(VK_DYNAMIC_STATE_SCISSOR)
.DebugName("Viewer.Pipeline")
.Create(device.get());
}
void VulkanRenderDevice::ResizeSwapChain(int width, int height)
{
if (width <= 0 || height <= 0 || (width == CurrentWidth && height == CurrentHeight && swapchain->Lost()))
return;
CurrentWidth = width;
CurrentHeight = height;
Framebuffers.clear();
swapchain->Create(width, height, 1, true, false, false);
for (int imageIndex = 0; imageIndex < swapchain->ImageCount(); imageIndex++)
{
Framebuffers.push_back(FramebufferBuilder()
.RenderPass(Viewer.RenderPass.get())
.AddAttachment(swapchain->GetImageView(imageIndex))
.Size(width, height)
.DebugName("framebuffer")
.Create(GetDevice()));
}
}
/////////////////////////////////////////////////////////////////////////////
VkCommandBufferManager::VkCommandBufferManager(VulkanRenderDevice* fb) : fb(fb)
{
mCommandPool = CommandPoolBuilder()
.QueueFamily(fb->GetDevice()->GraphicsFamily)
.DebugName("mCommandPool")
.Create(fb->GetDevice());
mImageAvailableSemaphore = SemaphoreBuilder()
.DebugName("mImageAvailableSemaphore")
.Create(fb->GetDevice());
mRenderFinishedSemaphore = SemaphoreBuilder()
.DebugName("mRenderFinishedSemaphore")
.Create(fb->GetDevice());
mPresentFinishedFence = FenceBuilder()
.DebugName("mPresentFinishedFence")
.Create(fb->GetDevice());
}
int VkCommandBufferManager::AcquireImage()
{
return fb->GetSwapChain()->AcquireImage(mImageAvailableSemaphore.get());
}
void VkCommandBufferManager::SubmitAndWait(int imageIndex)
{
if (mTransferCommands)
{
mTransferCommands->end();
QueueSubmit()
.AddCommandBuffer(mTransferCommands.get())
.Execute(fb->GetDevice(), fb->GetDevice()->GraphicsQueue);
TransferDeleteList->Add(std::move(mTransferCommands));
vkDeviceWaitIdle(fb->GetDevice()->device);
}
if (imageIndex >= 0 && mDrawCommands)
{
mDrawCommands->end();
QueueSubmit()
.AddCommandBuffer(mDrawCommands.get())
.AddWait(VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, mImageAvailableSemaphore.get())
.AddSignal(mRenderFinishedSemaphore.get())
.Execute(fb->GetDevice(), fb->GetDevice()->GraphicsQueue, mPresentFinishedFence.get());
DrawDeleteList->Add(std::move(mDrawCommands));
fb->GetSwapChain()->QueuePresent(imageIndex, mRenderFinishedSemaphore.get());
vkWaitForFences(fb->GetDevice()->device, 1, &mPresentFinishedFence->fence, VK_TRUE, std::numeric_limits<uint64_t>::max());
vkResetFences(fb->GetDevice()->device, 1, &mPresentFinishedFence->fence);
}
TransferDeleteList = std::make_unique<DeleteList>();
DrawDeleteList = std::make_unique<DeleteList>();
}
VulkanCommandBuffer* VkCommandBufferManager::GetTransferCommands()
{
if (!mTransferCommands)
{
mTransferCommands = mCommandPool->createBuffer();
mTransferCommands->begin();
}
return mTransferCommands.get();
}
VulkanCommandBuffer* VkCommandBufferManager::GetDrawCommands()
{
if (!mDrawCommands)
{
mDrawCommands = mCommandPool->createBuffer();
mDrawCommands->begin();
}
return mDrawCommands.get();
}
/////////////////////////////////////////////////////////////////////////////
VkTextureManager::VkTextureManager(VulkanRenderDevice* fb) : fb(fb)
{
CreateNullTexture();
}
void VkTextureManager::CreateNullTexture()
{
NullTexture = ImageBuilder()
.Format(VK_FORMAT_R8G8B8A8_UNORM)
.Size(1, 1)
.Usage(VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT)
.DebugName("VkTextureManager.NullTexture")
.Create(fb->GetDevice());
NullTextureView = ImageViewBuilder()
.Image(NullTexture.get(), VK_FORMAT_R8G8B8A8_UNORM)
.DebugName("VkTextureManager.NullTextureView")
.Create(fb->GetDevice());
auto stagingBuffer = BufferBuilder()
.Size(4)
.Usage(VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VMA_MEMORY_USAGE_CPU_ONLY)
.DebugName("VkTextureManager.NullTextureStaging")
.Create(fb->GetDevice());
// Put white in the texture
uint32_t* data = (uint32_t*)stagingBuffer->Map(0, 4);
*data = 0xffffffff;
stagingBuffer->Unmap();
PipelineBarrier()
.AddImage(NullTexture.get(), VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 0, VK_ACCESS_TRANSFER_WRITE_BIT, VK_IMAGE_ASPECT_COLOR_BIT)
.Execute(fb->GetCommands()->GetTransferCommands(), VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT);
VkBufferImageCopy region = {};
region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
region.imageSubresource.layerCount = 1;
region.imageExtent.depth = 1;
region.imageExtent.width = 1;
region.imageExtent.height = 1;
fb->GetCommands()->GetTransferCommands()->copyBufferToImage(stagingBuffer->buffer, NullTexture->image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &region);
fb->GetCommands()->TransferDeleteList->Add(std::move(stagingBuffer));
PipelineBarrier()
.AddImage(NullTexture.get(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT, VK_IMAGE_ASPECT_COLOR_BIT)
.Execute(fb->GetCommands()->GetTransferCommands(), VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT);
}
int VkTextureManager::CreateGameTexture(int width, int height, const void* pixels)
{
int index = (int)GameTextures.size();
auto texture = ImageBuilder()
.Format(VK_FORMAT_R8G8B8A8_UNORM)
.Size(width, height)
.Usage(VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT)
.DebugName("VkTextureManager.GameTexture")
.Create(fb->GetDevice());
auto textureView = ImageViewBuilder()
.Image(texture.get(), VK_FORMAT_R8G8B8A8_UNORM)
.DebugName("VkTextureManager.GameTextureView")
.Create(fb->GetDevice());
auto stagingBuffer = BufferBuilder()
.Size(width * height * sizeof(uint32_t))
.Usage(VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VMA_MEMORY_USAGE_CPU_ONLY)
.DebugName("VkTextureManager.GameTextureStaging")
.Create(fb->GetDevice());
uint32_t* data = (uint32_t*)stagingBuffer->Map(0, width * height * sizeof(uint32_t));
memcpy(data, pixels, width * height * sizeof(uint32_t));
stagingBuffer->Unmap();
PipelineBarrier()
.AddImage(texture.get(), VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 0, VK_ACCESS_TRANSFER_WRITE_BIT, VK_IMAGE_ASPECT_COLOR_BIT)
.Execute(fb->GetCommands()->GetTransferCommands(), VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT);
VkBufferImageCopy region = {};
region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
region.imageSubresource.layerCount = 1;
region.imageExtent.depth = 1;
region.imageExtent.width = width;
region.imageExtent.height = height;
fb->GetCommands()->GetTransferCommands()->copyBufferToImage(stagingBuffer->buffer, texture->image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &region);
fb->GetCommands()->TransferDeleteList->Add(std::move(stagingBuffer));
PipelineBarrier()
.AddImage(texture.get(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT, VK_IMAGE_ASPECT_COLOR_BIT)
.Execute(fb->GetCommands()->GetTransferCommands(), VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT);
GameTextures.push_back(std::move(texture));
GameTextureViews.push_back(std::move(textureView));
return index;
}
void VkTextureManager::CreateLightmap(int newLMTextureSize, int newLMTextureCount)
{
if (LMTextureSize == newLMTextureSize && LMTextureCount == newLMTextureCount + 1)
return;
LMTextureSize = newLMTextureSize;
LMTextureCount = newLMTextureCount + 1; // the extra texture is for the dynamic lightmap
int w = newLMTextureSize;
int h = newLMTextureSize;
int count = newLMTextureCount;
int pixelsize = 8;
Lightmap.Reset(fb);
Lightmap.Image = ImageBuilder()
.Size(w, h, 1, LMTextureCount)
.Format(VK_FORMAT_R16G16B16A16_SFLOAT)
.Usage(VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT)
.DebugName("VkRenderBuffers.Lightmap")
.Create(fb->GetDevice());
PipelineBarrier()
.AddImage(Lightmap.Image.get(), VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, 0, VK_ACCESS_SHADER_READ_BIT, VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, LMTextureCount)
.Execute(fb->GetCommands()->GetTransferCommands(), VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT);
}
void VkTextureManager::DownloadLightmap(int arrayIndex, uint16_t* buffer)
{
unsigned int totalSize = LMTextureSize * LMTextureSize * 4;
auto stagingBuffer = BufferBuilder()
.Size(totalSize * sizeof(uint16_t))
.Usage(VK_BUFFER_USAGE_TRANSFER_DST_BIT, VMA_MEMORY_USAGE_CPU_ONLY)
.DebugName("DownloadLightmap")
.Create(fb->GetDevice());
auto cmdbuffer = fb->GetCommands()->GetTransferCommands();
PipelineBarrier()
.AddImage(Lightmap.Image.get(), VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_ACCESS_SHADER_READ_BIT, VK_ACCESS_TRANSFER_READ_BIT, VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, arrayIndex, 1)
.Execute(cmdbuffer, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT);
VkBufferImageCopy region = {};
region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
region.imageSubresource.baseArrayLayer = arrayIndex;
region.imageSubresource.layerCount = 1;
region.imageSubresource.mipLevel = 0;
region.imageExtent.width = LMTextureSize;
region.imageExtent.height = LMTextureSize;
region.imageExtent.depth = 1;
cmdbuffer->copyImageToBuffer(Lightmap.Image->image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, stagingBuffer->buffer, 1, &region);
PipelineBarrier()
.AddImage(Lightmap.Image.get(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_TRANSFER_READ_BIT, VK_ACCESS_SHADER_READ_BIT, VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, arrayIndex, 1)
.Execute(cmdbuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT);
fb->GetCommands()->SubmitAndWait();
uint16_t* srcdata = (uint16_t*)stagingBuffer->Map(0, totalSize * sizeof(uint16_t));
memcpy(buffer, srcdata, totalSize * sizeof(uint16_t));
stagingBuffer->Unmap();
}
/////////////////////////////////////////////////////////////////////////////
VkSamplerManager::VkSamplerManager(VulkanRenderDevice* fb) : fb(fb)
{
Sampler = SamplerBuilder()
.MagFilter(VK_FILTER_NEAREST)
.MinFilter(VK_FILTER_NEAREST)
.AddressMode(VK_SAMPLER_ADDRESS_MODE_REPEAT/*VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE*/, VK_SAMPLER_ADDRESS_MODE_REPEAT/*VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE*/, VK_SAMPLER_ADDRESS_MODE_REPEAT)
.MipmapMode(VK_SAMPLER_MIPMAP_MODE_NEAREST)
.MaxLod(0.25f)
.DebugName("VkSamplerManager.Sampler")
.Create(fb->GetDevice());
}
/////////////////////////////////////////////////////////////////////////////
VkDescriptorSetManager::VkDescriptorSetManager(VulkanRenderDevice* fb) : fb(fb)
{
CreateBindlessDescriptorSet();
}
void VkDescriptorSetManager::CreateBindlessDescriptorSet()
{
BindlessDescriptorPool = DescriptorPoolBuilder()
.Flags(VK_DESCRIPTOR_POOL_CREATE_UPDATE_AFTER_BIND_BIT_EXT)
.AddPoolSize(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, MaxBindlessTextures)
.MaxSets(MaxBindlessTextures)
.DebugName("BindlessDescriptorPool")
.Create(fb->GetDevice());
BindlessDescriptorSetLayout = DescriptorSetLayoutBuilder()
.Flags(VK_DESCRIPTOR_SET_LAYOUT_CREATE_UPDATE_AFTER_BIND_POOL_BIT_EXT)
.AddBinding(
0, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
MaxBindlessTextures,
VK_SHADER_STAGE_FRAGMENT_BIT,
VK_DESCRIPTOR_BINDING_PARTIALLY_BOUND_BIT_EXT | VK_DESCRIPTOR_BINDING_VARIABLE_DESCRIPTOR_COUNT_BIT_EXT | VK_DESCRIPTOR_BINDING_UPDATE_AFTER_BIND_BIT_EXT)
.DebugName("BindlessDescriptorSetLayout")
.Create(fb->GetDevice());
BindlessDescriptorSet = BindlessDescriptorPool->allocate(BindlessDescriptorSetLayout.get(), MaxBindlessTextures);
}
void VkDescriptorSetManager::UpdateBindlessDescriptorSet()
{
WriteBindless.Execute(fb->GetDevice());
WriteBindless = WriteDescriptors();
}
int VkDescriptorSetManager::AddBindlessTextureIndex(VulkanImageView* imageview, VulkanSampler* sampler)
{
int index = NextBindlessIndex++;
WriteBindless.AddCombinedImageSampler(BindlessDescriptorSet.get(), 0, index, imageview, sampler, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
return index;
}
/////////////////////////////////////////////////////////////////////////////

View file

@ -0,0 +1,230 @@
#pragma once
#include "framework/zstring.h"
#include "framework/textureid.h"
#include "zvulkan/vulkanobjects.h"
#include "zvulkan/vulkanbuilders.h"
#include "framework/matrix.h"
#include <stdexcept>
#include <unordered_map>
class VkLevelMesh;
class VkLightmapper;
class VkCommandBufferManager;
class VkDescriptorSetManager;
class VkTextureManager;
class VkSamplerManager;
class VulkanSwapChain;
class LevelMeshViewer;
class VSMatrix;
class VulkanRenderDevice
{
public:
VulkanRenderDevice(LevelMeshViewer* viewer);
~VulkanRenderDevice();
VulkanDevice* GetDevice() { return device.get(); }
VulkanSwapChain* GetSwapChain() { return swapchain.get(); }
VkCommandBufferManager* GetCommands() { return commands.get(); }
VkDescriptorSetManager* GetDescriptorSetManager() { return descriptors.get(); }
VkTextureManager* GetTextureManager() { return textures.get(); }
VkSamplerManager* GetSamplerManager() { return samplers.get(); }
VkLevelMesh* GetLevelMesh() { return levelmesh.get(); }
VkLightmapper* GetLightmapper() { return lightmapper.get(); }
int GetBindlessTextureIndex(FTextureID texture);
bool IsRayQueryEnabled() const { return useRayQuery; }
void ResizeSwapChain(int width, int height);
void DrawViewer(const FVector3& cameraPos, const VSMatrix& viewToWorld, float fovy, float aspect, const FVector3& sundir, const FVector3& suncolor, float sunintensity);
bool useRayQuery = false;
private:
void CreateViewerObjects();
std::shared_ptr<VulkanDevice> device;
std::shared_ptr<VulkanSwapChain> swapchain;
std::unique_ptr<VkCommandBufferManager> commands;
std::unique_ptr<VkDescriptorSetManager> descriptors;
std::unique_ptr<VkTextureManager> textures;
std::unique_ptr<VkSamplerManager> samplers;
std::unique_ptr<VkLevelMesh> levelmesh;
std::unique_ptr<VkLightmapper> lightmapper;
struct
{
std::unique_ptr<VulkanDescriptorSetLayout> DescriptorSetLayout;
std::unique_ptr<VulkanDescriptorPool> DescriptorPool;
std::unique_ptr<VulkanDescriptorSet> DescriptorSet;
std::unique_ptr<VulkanShader> VertexShader;
std::unique_ptr<VulkanShader> FragmentShader;
std::unique_ptr<VulkanRenderPass> RenderPass;
std::unique_ptr<VulkanPipelineLayout> PipelineLayout;
std::unique_ptr<VulkanPipeline> Pipeline;
} Viewer;
int CurrentWidth = 0;
int CurrentHeight = 0;
std::vector<std::unique_ptr<VulkanFramebuffer>> Framebuffers;
std::unordered_map<FGameTexture*, int> TextureIndexes;
};
class VkCommandBufferManager
{
public:
VkCommandBufferManager(VulkanRenderDevice* fb);
void SubmitAndWait(int imageIndex = -1);
VulkanCommandBuffer* GetTransferCommands();
VulkanCommandBuffer* GetDrawCommands();
void PushGroup(VulkanCommandBuffer* cmdbuffer, const FString& name) { }
void PopGroup(VulkanCommandBuffer* cmdbuffer) { }
int AcquireImage();
class DeleteList
{
public:
std::vector<std::unique_ptr<VulkanBuffer>> Buffers;
std::vector<std::unique_ptr<VulkanSampler>> Samplers;
std::vector<std::unique_ptr<VulkanImage>> Images;
std::vector<std::unique_ptr<VulkanImageView>> ImageViews;
std::vector<std::unique_ptr<VulkanFramebuffer>> Framebuffers;
std::vector<std::unique_ptr<VulkanAccelerationStructure>> AccelStructs;
std::vector<std::unique_ptr<VulkanDescriptorPool>> DescriptorPools;
std::vector<std::unique_ptr<VulkanDescriptorSet>> Descriptors;
std::vector<std::unique_ptr<VulkanShader>> Shaders;
std::vector<std::unique_ptr<VulkanCommandBuffer>> CommandBuffers;
size_t TotalSize = 0;
void Add(std::unique_ptr<VulkanBuffer> obj) { if (obj) { TotalSize += obj->size; Buffers.push_back(std::move(obj)); } }
void Add(std::unique_ptr<VulkanSampler> obj) { if (obj) { Samplers.push_back(std::move(obj)); } }
void Add(std::unique_ptr<VulkanImage> obj) { if (obj) { Images.push_back(std::move(obj)); } }
void Add(std::unique_ptr<VulkanImageView> obj) { if (obj) { ImageViews.push_back(std::move(obj)); } }
void Add(std::unique_ptr<VulkanFramebuffer> obj) { if (obj) { Framebuffers.push_back(std::move(obj)); } }
void Add(std::unique_ptr<VulkanAccelerationStructure> obj) { if (obj) { AccelStructs.push_back(std::move(obj)); } }
void Add(std::unique_ptr<VulkanDescriptorPool> obj) { if (obj) { DescriptorPools.push_back(std::move(obj)); } }
void Add(std::unique_ptr<VulkanDescriptorSet> obj) { if (obj) { Descriptors.push_back(std::move(obj)); } }
void Add(std::unique_ptr<VulkanCommandBuffer> obj) { if (obj) { CommandBuffers.push_back(std::move(obj)); } }
void Add(std::unique_ptr<VulkanShader> obj) { if (obj) { Shaders.push_back(std::move(obj)); } }
};
std::unique_ptr<DeleteList> TransferDeleteList = std::make_unique<DeleteList>();
std::unique_ptr<DeleteList> DrawDeleteList = std::make_unique<DeleteList>();
private:
VulkanRenderDevice* fb = nullptr;
std::unique_ptr<VulkanCommandPool> mCommandPool;
std::unique_ptr<VulkanCommandBuffer> mTransferCommands;
std::unique_ptr<VulkanCommandBuffer> mDrawCommands;
std::unique_ptr<VulkanSemaphore> mImageAvailableSemaphore;
std::unique_ptr<VulkanSemaphore> mRenderFinishedSemaphore;
std::unique_ptr<VulkanFence> mPresentFinishedFence;
};
struct ViewerPushConstants
{
VSMatrix ViewToWorld;
FVector3 CameraPos;
float ProjX;
FVector3 SunDir;
float ProjY;
FVector3 SunColor;
float SunIntensity;
};
class VkTextureImage
{
public:
void Reset(VulkanRenderDevice* fb)
{
auto deletelist = fb->GetCommands()->DrawDeleteList.get();
for (auto& framebuffer : LMFramebuffers)
deletelist->Add(std::move(framebuffer));
LMFramebuffers.clear();
for (auto& view : LMViews)
deletelist->Add(std::move(view));
LMViews.clear();
deletelist->Add(std::move(Image));
}
std::unique_ptr<VulkanImage> Image;
std::vector<std::unique_ptr<VulkanImageView>> LMViews;
std::vector<std::unique_ptr<VulkanFramebuffer>> LMFramebuffers;
};
class VkTextureManager
{
public:
VkTextureManager(VulkanRenderDevice* fb);
void CreateLightmap(int newLMTextureSize, int newLMTextureCount);
void DownloadLightmap(int arrayIndex, uint16_t* buffer);
int CreateGameTexture(int width, int height, const void* pixels);
VulkanImage* GetGameTexture(int index) { return GameTextures[index].get(); }
VulkanImageView* GetGameTextureView(int index) { return GameTextureViews[index].get(); }
VulkanImage* GetNullTexture() { return NullTexture.get(); }
VulkanImageView* GetNullTextureView() { return NullTextureView.get(); }
VkTextureImage Lightmap;
int LMTextureSize = 0;
int LMTextureCount = 0;
private:
void CreateNullTexture();
VulkanRenderDevice* fb = nullptr;
std::unique_ptr<VulkanImage> NullTexture;
std::unique_ptr<VulkanImageView> NullTextureView;
std::vector<std::unique_ptr<VulkanImage>> GameTextures;
std::vector<std::unique_ptr<VulkanImageView>> GameTextureViews;
};
class VkSamplerManager
{
public:
VkSamplerManager(VulkanRenderDevice* fb);
VulkanSampler* Get() { return Sampler.get(); }
private:
VulkanRenderDevice* fb = nullptr;
std::unique_ptr<VulkanSampler> Sampler;
};
class VkDescriptorSetManager
{
public:
VkDescriptorSetManager(VulkanRenderDevice* fb);
VulkanDescriptorSetLayout* GetBindlessLayout() { return BindlessDescriptorSetLayout.get(); }
VulkanDescriptorSet* GetBindlessSet() { return BindlessDescriptorSet.get(); }
void UpdateBindlessDescriptorSet();
int AddBindlessTextureIndex(VulkanImageView* imageview, VulkanSampler* sampler);
private:
void CreateBindlessDescriptorSet();
VulkanRenderDevice* fb = nullptr;
std::unique_ptr<VulkanDescriptorPool> BindlessDescriptorPool;
std::unique_ptr<VulkanDescriptorSet> BindlessDescriptorSet;
std::unique_ptr<VulkanDescriptorSetLayout> BindlessDescriptorSetLayout;
WriteDescriptors WriteBindless;
int NextBindlessIndex = 0;
static const int MaxBindlessTextures = 16536;
};
inline void I_FatalError(const char* reason) { throw std::runtime_error(reason); }

View file

@ -56,6 +56,8 @@
#include <thread>
#include "framework/zdray.h"
#include "framework/filesystem.h"
#include "framework/file.h"
#include "wad/wad.h"
#include "level/level.h"
#include "commandline/getopt.h"
@ -118,6 +120,7 @@ int LMDims = 1024;
bool VKDebug = false;
bool DumpMesh = false;
bool NoRtx = false;
bool showviewer = false;
int ambientSampleCount = 2048;
@ -160,10 +163,11 @@ static option long_opts[] =
{"dump-mesh", no_argument, 0, 1004},
{"preview", no_argument, 0, 1005},
{"no-rtx", no_argument, 0, 1006},
{"viewer", no_argument, 0, 1007},
{0,0,0,0}
};
static const char short_opts[] = "wVgGvbNrReEm:o:f:p:s:d:PqtzZXx5cj:S:D:::";
static const char short_opts[] = "wVgGvbNrReEm:o:f:p:s:d:PqtzZXx5cj:S:D::::";
// CODE --------------------------------------------------------------------
@ -231,6 +235,11 @@ int main(int argc, char **argv)
fixSame = true;
}
// Search for texture resources with the assumption that the map is in a maps folder and the textures folder is next to it.
// To do: allow specifying the path via commandline
FString inResourceFolder = FilePath::combine(FilePath::remove_last_component(InName), "..").c_str();
fileSystem.AddFolderSource(inResourceFolder);
{
FWadReader inwad(InName);
FWadWriter outwad(OutName, inwad.IsIWAD());
@ -320,6 +329,23 @@ int main(int argc, char **argv)
return 0;
}
static int RoundPowerOfTwo(int x)
{
int mask = 1;
while (mask < 0x40000000)
{
if (x == mask || (x & (mask - 1)) == x)
{
return mask;
}
mask <<= 1;
}
return x;
}
//==========================================================================
//
// ParseArgs
@ -449,7 +475,7 @@ static void ParseArgs(int argc, char **argv)
LMDims = atoi(optarg);
if (LMDims <= 0) LMDims = 1;
if (LMDims > 1024) LMDims = 1024;
LMDims = Math::RoundPowerOfTwo(LMDims);
LMDims = RoundPowerOfTwo(LMDims);
break;
case 'D':
VKDebug = true;
@ -463,6 +489,9 @@ static void ParseArgs(int argc, char **argv)
case 1006:
NoRtx = true;
break;
case 1007:
showviewer = true;
break;
case 1000:
ShowUsage();
exit(0);

View file

@ -1,289 +0,0 @@
//-----------------------------------------------------------------------------
// Note: this is a modified version of dlight. It is not the original software.
//-----------------------------------------------------------------------------
//
// Copyright (c) 2013-2014 Samuel Villarreal
// svkaiser@gmail.com
//
// 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.
//
#include <math.h>
#include "mathlib.h"
#include <assert.h>
#define FULLCIRCLE (M_PI * 2)
Angle::Angle()
{
this->yaw = 0;
this->pitch = 0;
this->roll = 0;
}
Angle::Angle(const float yaw, const float pitch, const float roll)
{
this->yaw = yaw;
this->pitch = pitch;
this->roll = roll;
}
Angle::Angle(const vec3 &vector)
{
this->yaw = vector.x;
this->pitch = vector.y;
this->roll = vector.z;
Clamp180();
}
Angle::Angle(const Angle &an)
{
this->yaw = an.yaw;
this->pitch = an.pitch;
this->roll = an.roll;
}
Angle &Angle::Clamp180()
{
#define CLAMP180(x) \
if(x < -M_PI) for(; x < -M_PI; x = x + FULLCIRCLE); \
if(x > M_PI) for(; x > M_PI; x = x - FULLCIRCLE)
CLAMP180(yaw);
CLAMP180(pitch);
CLAMP180(roll);
#undef CLAMP180
return *this;
}
Angle &Angle::Clamp180Invert()
{
#define CLAMP180(x) \
for(; x < -M_PI; x = x + FULLCIRCLE); \
for(; x > M_PI; x = x - FULLCIRCLE)
CLAMP180(yaw);
CLAMP180(pitch);
CLAMP180(roll);
#undef CLAMP180
yaw = -yaw;
pitch = -pitch;
roll = -roll;
return *this;
}
Angle &Angle::Clamp180InvertSum(const Angle &angle)
{
Angle an = angle;
an.Clamp180Invert();
an.yaw += this->yaw;
an.pitch += this->pitch;
an.roll += this->roll;
an.Clamp180Invert();
this->yaw = an.yaw;
this->pitch = an.pitch;
this->roll = an.roll;
return *this;
}
Angle &Angle::Round()
{
#define ROUND(x) \
x = DEG2RAD((360.0f / 65536.0f) * \
((int)(RAD2DEG(x) * (65536.0f / 360.0f)) & 65535))
yaw = ROUND(yaw);
pitch = ROUND(pitch);
roll = ROUND(roll);
#undef ROUND
return Clamp180();
}
Angle Angle::Diff(Angle &angle)
{
float an;
Angle out;
Clamp180();
angle.Clamp180();
#define DIFF(x) \
if(x <= angle.x) { \
an = angle.x + FULLCIRCLE; \
if(x - angle.x > an - x) { \
out.x = x - an; \
} \
else { \
out.x = x - angle.x; \
} \
} \
else { \
an = angle.x - FULLCIRCLE; \
if(angle.x - x <= x - an) { \
out.x = x - angle.x; \
} \
else { \
out.x = x - an; \
} \
}
DIFF(yaw);
DIFF(pitch);
DIFF(roll);
#undef DIFF
return out;
}
void Angle::ToAxis(vec3 *forward, vec3 *up, vec3 *right)
{
float sy = Math::Sin(yaw);
float cy = Math::Cos(yaw);
float sp = Math::Sin(pitch);
float cp = Math::Cos(pitch);
float sr = Math::Sin(roll);
float cr = Math::Cos(roll);
if (forward)
{
forward->x = sy * cp;
forward->y = sp;
forward->z = cy * cp;
}
if (right)
{
right->x = sr * sp * sy + cr * cy;
right->y = sr * cp;
right->z = sr * sp * cy + cr * -sy;
}
if (up)
{
up->x = cr * sp * sy + -sr * cy;
up->y = cr * cp;
up->z = cr * sp * cy + -sr * -sy;
}
}
vec3 Angle::ToForwardAxis()
{
vec3 vec;
ToAxis(&vec, nullptr, nullptr);
return vec;
}
vec3 Angle::ToUpAxis()
{
vec3 vec;
ToAxis(nullptr, &vec, nullptr);
return vec;
}
vec3 Angle::ToRightAxis()
{
vec3 vec;
ToAxis(nullptr, nullptr, &vec);
return vec;
}
const vec3 &Angle::ToVec3() const
{
return *reinterpret_cast<const vec3*>(&yaw);
}
vec3 &Angle::ToVec3()
{
return *reinterpret_cast<vec3*>(&yaw);
}
Angle Angle::operator+(const Angle &angle)
{
return Angle(yaw + angle.yaw, pitch + angle.pitch, roll + angle.roll);
}
Angle Angle::operator-(const Angle &angle)
{
return Angle(yaw - angle.yaw, pitch - angle.pitch, roll - angle.roll);
}
Angle Angle::operator-()
{
return Angle(-yaw, -pitch, -roll);
}
Angle &Angle::operator+=(const Angle &angle)
{
yaw += angle.yaw;
pitch += angle.pitch;
roll += angle.roll;
return *this;
}
Angle &Angle::operator-=(const Angle &angle)
{
yaw -= angle.yaw;
pitch -= angle.pitch;
roll -= angle.roll;
return *this;
}
Angle &Angle::operator=(const Angle &angle)
{
yaw = angle.yaw;
pitch = angle.pitch;
roll = angle.roll;
return *this;
}
Angle &Angle::operator=(const vec3 &vector)
{
yaw = vector.x;
pitch = vector.y;
roll = vector.z;
return *this;
}
Angle &Angle::operator=(const float *vecs)
{
yaw = vecs[0];
pitch = vecs[1];
roll = vecs[2];
return *this;
}
float Angle::operator[](int index) const
{
assert(index >= 0 && index < 3);
return (&yaw)[index];
}
float &Angle::operator[](int index)
{
assert(index >= 0 && index < 3);
return (&yaw)[index];
}

View file

@ -1,336 +0,0 @@
#include "mat.h"
#include <cmath>
#ifndef NO_SSE
#include <emmintrin.h>
#endif
#include <string.h>
mat4 mat4::null()
{
mat4 m;
memset(m.matrix, 0, sizeof(m.matrix));
return m;
}
mat4 mat4::identity()
{
mat4 m = null();
m.matrix[0] = 1.0f;
m.matrix[5] = 1.0f;
m.matrix[10] = 1.0f;
m.matrix[15] = 1.0f;
return m;
}
mat4 mat4::from_values(float *matrix)
{
mat4 m;
memcpy(m.matrix, matrix, sizeof(m.matrix));
return m;
}
mat4 mat4::transpose(const mat4 &matrix)
{
mat4 m;
for (int y = 0; y < 4; y++)
for (int x = 0; x < 4; x++)
m.matrix[x + y * 4] = matrix.matrix[y + x * 4];
return m;
}
mat4 mat4::translate(float x, float y, float z)
{
mat4 m = identity();
m.matrix[0 + 3 * 4] = x;
m.matrix[1 + 3 * 4] = y;
m.matrix[2 + 3 * 4] = z;
return m;
}
mat4 mat4::scale(float x, float y, float z)
{
mat4 m = null();
m.matrix[0 + 0 * 4] = x;
m.matrix[1 + 1 * 4] = y;
m.matrix[2 + 2 * 4] = z;
m.matrix[3 + 3 * 4] = 1;
return m;
}
mat4 mat4::rotate(float angle, float x, float y, float z)
{
float c = cosf(angle);
float s = sinf(angle);
mat4 m = null();
m.matrix[0 + 0 * 4] = (x*x*(1.0f - c) + c);
m.matrix[0 + 1 * 4] = (x*y*(1.0f - c) - z*s);
m.matrix[0 + 2 * 4] = (x*z*(1.0f - c) + y*s);
m.matrix[1 + 0 * 4] = (y*x*(1.0f - c) + z*s);
m.matrix[1 + 1 * 4] = (y*y*(1.0f - c) + c);
m.matrix[1 + 2 * 4] = (y*z*(1.0f - c) - x*s);
m.matrix[2 + 0 * 4] = (x*z*(1.0f - c) - y*s);
m.matrix[2 + 1 * 4] = (y*z*(1.0f - c) + x*s);
m.matrix[2 + 2 * 4] = (z*z*(1.0f - c) + c);
m.matrix[3 + 3 * 4] = 1.0f;
return m;
}
mat4 mat4::quaternion(float x, float y, float z, float w)
{
mat4 m = mat4::null();
m.matrix[0 * 4 + 0] = 1.0f - 2.0f * y*y - 2.0f * z*z;
m.matrix[1 * 4 + 0] = 2.0f * x*y - 2.0f * w*z;
m.matrix[2 * 4 + 0] = 2.0f * x*z + 2.0f * w*y;
m.matrix[0 * 4 + 1] = 2.0f * x*y + 2.0f * w*z;
m.matrix[1 * 4 + 1] = 1.0f - 2.0f * x*x - 2.0f * z*z;
m.matrix[2 * 4 + 1] = 2.0f * y*z - 2.0f * w*x;
m.matrix[0 * 4 + 2] = 2.0f * x*z - 2.0f * w*y;
m.matrix[1 * 4 + 2] = 2.0f * y*z + 2.0f * w*x;
m.matrix[2 * 4 + 2] = 1.0f - 2.0f * x*x - 2.0f * y*y;
m.matrix[3 * 4 + 3] = 1.0f;
return m;
}
mat4 mat4::swap_yz()
{
mat4 m = null();
m.matrix[0 + 0 * 4] = 1.0f;
m.matrix[1 + 2 * 4] = 1.0f;
m.matrix[2 + 1 * 4] = -1.0f;
m.matrix[3 + 3 * 4] = 1.0f;
return m;
}
mat4 mat4::perspective(float fovy, float aspect, float z_near, float z_far, handedness handedness, clipzrange clipZ)
{
float f = (float)(1.0 / tan(fovy * 3.14159265359 / 360.0));
mat4 m = null();
m.matrix[0 + 0 * 4] = f / aspect;
m.matrix[1 + 1 * 4] = f;
m.matrix[2 + 2 * 4] = (z_far + z_near) / (z_near - z_far);
m.matrix[2 + 3 * 4] = (2.0f * z_far * z_near) / (z_near - z_far);
m.matrix[3 + 2 * 4] = -1.0f;
if (handedness == handedness::left)
{
m = m * mat4::scale(1.0f, 1.0f, -1.0f);
}
if (clipZ == clipzrange::zero_positive_w)
{
mat4 scale_translate = identity();
scale_translate.matrix[2 + 2 * 4] = 0.5f;
scale_translate.matrix[2 + 3 * 4] = 0.5f;
m = scale_translate * m;
}
return m;
}
mat4 mat4::frustum(float left, float right, float bottom, float top, float z_near, float z_far, handedness handedness, clipzrange clipZ)
{
float a = (right + left) / (right - left);
float b = (top + bottom) / (top - bottom);
float c = -(z_far + z_near) / (z_far - z_near);
float d = -(2.0f * z_far) / (z_far - z_near);
mat4 m = null();
m.matrix[0 + 0 * 4] = 2.0f * z_near / (right - left);
m.matrix[1 + 1 * 4] = 2.0f * z_near / (top - bottom);
m.matrix[0 + 2 * 4] = a;
m.matrix[1 + 2 * 4] = b;
m.matrix[2 + 2 * 4] = c;
m.matrix[2 + 3 * 4] = d;
m.matrix[3 + 2 * 4] = -1;
if (handedness == handedness::left)
{
m = m * mat4::scale(1.0f, 1.0f, -1.0f);
}
if (clipZ == clipzrange::zero_positive_w)
{
mat4 scale_translate = identity();
scale_translate.matrix[2 + 2 * 4] = 0.5f;
scale_translate.matrix[2 + 3 * 4] = 0.5f;
m = scale_translate * m;
}
return m;
}
mat4 mat4::look_at(vec3 eye, vec3 center, vec3 up)
{
vec3 f = normalize(center - eye);
vec3 s = cross(f, normalize(up));
vec3 u = cross(s, f);
mat4 m = null();
m.matrix[0 + 0 * 4] = s.x;
m.matrix[0 + 1 * 4] = s.y;
m.matrix[0 + 2 * 4] = s.z;
m.matrix[1 + 0 * 4] = u.x;
m.matrix[1 + 1 * 4] = u.y;
m.matrix[1 + 2 * 4] = u.z;
m.matrix[2 + 0 * 4] = -f.x;
m.matrix[2 + 1 * 4] = -f.y;
m.matrix[2 + 2 * 4] = -f.z;
m.matrix[3 + 3 * 4] = 1.0f;
return m * translate(-eye.x, -eye.y, -eye.z);
}
mat4 mat4::operator*(const mat4 &mult) const
{
mat4 result;
for (int x = 0; x < 4; x++)
{
for (int y = 0; y < 4; y++)
{
result.matrix[x + y * 4] =
matrix[0 * 4 + x] * mult.matrix[y * 4 + 0] +
matrix[1 * 4 + x] * mult.matrix[y * 4 + 1] +
matrix[2 * 4 + x] * mult.matrix[y * 4 + 2] +
matrix[3 * 4 + x] * mult.matrix[y * 4 + 3];
}
}
return result;
}
vec4 mat4::operator*(const vec4 &v) const
{
#ifdef NO_SSE
vec4 result;
result.x = matrix[0 * 4 + 0] * v.x + matrix[1 * 4 + 0] * v.y + matrix[2 * 4 + 0] * v.z + matrix[3 * 4 + 0] * v.w;
result.y = matrix[0 * 4 + 1] * v.x + matrix[1 * 4 + 1] * v.y + matrix[2 * 4 + 1] * v.z + matrix[3 * 4 + 1] * v.w;
result.z = matrix[0 * 4 + 2] * v.x + matrix[1 * 4 + 2] * v.y + matrix[2 * 4 + 2] * v.z + matrix[3 * 4 + 2] * v.w;
result.w = matrix[0 * 4 + 3] * v.x + matrix[1 * 4 + 3] * v.y + matrix[2 * 4 + 3] * v.z + matrix[3 * 4 + 3] * v.w;
return result;
#else
__m128 m0 = _mm_loadu_ps(matrix);
__m128 m1 = _mm_loadu_ps(matrix + 4);
__m128 m2 = _mm_loadu_ps(matrix + 8);
__m128 m3 = _mm_loadu_ps(matrix + 12);
__m128 mv = _mm_loadu_ps(&v.x);
m0 = _mm_mul_ps(m0, _mm_shuffle_ps(mv, mv, _MM_SHUFFLE(0, 0, 0, 0)));
m1 = _mm_mul_ps(m1, _mm_shuffle_ps(mv, mv, _MM_SHUFFLE(1, 1, 1, 1)));
m2 = _mm_mul_ps(m2, _mm_shuffle_ps(mv, mv, _MM_SHUFFLE(2, 2, 2, 2)));
m3 = _mm_mul_ps(m3, _mm_shuffle_ps(mv, mv, _MM_SHUFFLE(3, 3, 3, 3)));
mv = _mm_add_ps(_mm_add_ps(_mm_add_ps(m0, m1), m2), m3);
vec4 result;
_mm_storeu_ps(&result.x, mv);
return result;
#endif
}
/////////////////////////////////////////////////////////////////////////////
mat3::mat3(const mat4 &initmatrix)
{
for (int y = 0; y < 3; y++)
for (int x = 0; x < 3; x++)
matrix[x + y * 3] = initmatrix.matrix[x + y * 4];
}
mat3 mat3::null()
{
mat3 m;
memset(m.matrix, 0, sizeof(m.matrix));
return m;
}
mat3 mat3::identity()
{
mat3 m = null();
m.matrix[0] = 1.0f;
m.matrix[4] = 1.0f;
m.matrix[8] = 1.0f;
return m;
}
mat3 mat3::from_values(float *matrix)
{
mat3 m;
memcpy(m.matrix, matrix, sizeof(m.matrix));
return m;
}
mat3 mat3::transpose(const mat3 &matrix)
{
mat3 m;
for (int y = 0; y < 3; y++)
for (int x = 0; x < 3; x++)
m.matrix[x + y * 3] = matrix.matrix[y + x * 3];
return m;
}
double mat3::determinant(const mat3 &m)
{
double value;
value = m.matrix[0 * 3 + 0] * ((m.matrix[1 * 3 + 1] * m.matrix[2 * 3 + 2]) - (m.matrix[2 * 3 + 1] * m.matrix[1 * 3 + 2]));
value -= m.matrix[0 * 3 + 1] * ((m.matrix[1 * 3 + 0] * m.matrix[2 * 3 + 2]) - (m.matrix[2 * 3 + 0] * m.matrix[1 * 3 + 2]));
value += m.matrix[0 * 3 + 2] * ((m.matrix[1 * 3 + 0] * m.matrix[2 * 3 + 1]) - (m.matrix[2 * 3 + 0] * m.matrix[1 * 3 + 1]));
return value;
}
mat3 mat3::adjoint(const mat3 &m)
{
mat3 result;
result.matrix[0 * 3 + 0] = ((m.matrix[1 * 3 + 1] * m.matrix[2 * 3 + 2]) - (m.matrix[1 * 3 + 2] * m.matrix[2 * 3 + 1]));
result.matrix[1 * 3 + 0] = -((m.matrix[1 * 3 + 0] * m.matrix[2 * 3 + 2]) - (m.matrix[1 * 3 + 2] * m.matrix[2 * 3 + 0]));
result.matrix[2 * 3 + 0] = ((m.matrix[1 * 3 + 0] * m.matrix[2 * 3 + 1]) - (m.matrix[1 * 3 + 1] * m.matrix[2 * 3 + 0]));
result.matrix[0 * 3 + 1] = -((m.matrix[0 * 3 + 1] * m.matrix[2 * 3 + 2]) - (m.matrix[0 * 3 + 2] * m.matrix[2 * 3 + 1]));
result.matrix[1 * 3 + 1] = ((m.matrix[0 * 3 + 0] * m.matrix[2 * 3 + 2]) - (m.matrix[0 * 3 + 2] * m.matrix[2 * 3 + 0]));
result.matrix[2 * 3 + 1] = -((m.matrix[0 * 3 + 0] * m.matrix[2 * 3 + 1]) - (m.matrix[0 * 3 + 1] * m.matrix[2 * 3 + 0]));
result.matrix[0 * 3 + 2] = ((m.matrix[0 * 3 + 1] * m.matrix[1 * 3 + 2]) - (m.matrix[0 * 3 + 2] * m.matrix[1 * 3 + 1]));
result.matrix[1 * 3 + 2] = -((m.matrix[0 * 3 + 0] * m.matrix[1 * 3 + 2]) - (m.matrix[0 * 3 + 2] * m.matrix[1 * 3 + 0]));
result.matrix[2 * 3 + 2] = ((m.matrix[0 * 3 + 0] * m.matrix[1 * 3 + 1]) - (m.matrix[0 * 3 + 1] * m.matrix[1 * 3 + 0]));
return result;
}
mat3 mat3::inverse(const mat3 &matrix)
{
double d = mat3::determinant(matrix);
// Inverse unknown when determinant is close to zero
if (fabs(d) < 1e-15)
{
return null();
}
else
{
mat3 result = mat3::adjoint(matrix);
d = 1.0 / d; // Inverse the determinant
for (int i = 0; i < 9; i++)
{
result.matrix[i] = (float)(result.matrix[i] * d);
}
return result;
}
}
mat3 mat3::operator*(const mat3 &mult) const
{
mat3 result;
for (int x = 0; x < 3; x++)
{
for (int y = 0; y < 3; y++)
{
result.matrix[x + y * 3] =
matrix[0 * 3 + x] * mult.matrix[y * 3 + 0] +
matrix[1 * 3 + x] * mult.matrix[y * 3 + 1] +
matrix[2 * 3 + x] * mult.matrix[y * 3 + 2];
}
}
return result;
}
vec3 mat3::operator*(const vec3 &v) const
{
vec3 result;
result.x = matrix[0 * 3 + 0] * v.x + matrix[1 * 3 + 0] * v.y + matrix[2 * 3 + 0] * v.z;
result.y = matrix[0 * 3 + 1] * v.x + matrix[1 * 3 + 1] * v.y + matrix[2 * 3 + 1] * v.z;
result.z = matrix[0 * 3 + 2] * v.x + matrix[1 * 3 + 2] * v.y + matrix[2 * 3 + 2] * v.z;
return result;
}

View file

@ -1,117 +0,0 @@
#pragma once
#include "vec.h"
enum class handedness
{
left,
right
};
enum class clipzrange
{
negative_positive_w, // OpenGL, -wclip <= zclip <= wclip
zero_positive_w // Direct3D, 0 <= zclip <= wclip
};
struct mat4
{
mat4() = default;
static mat4 null();
static mat4 identity();
static mat4 from_values(float *matrix);
static mat4 transpose(const mat4 &matrix);
static mat4 translate(float x, float y, float z);
static mat4 translate(const vec3 &v) { return translate(v.x, v.y, v.z); }
static mat4 scale(float x, float y, float z);
static mat4 scale(const vec3 &v) { return scale(v.x, v.y, v.z); }
static mat4 rotate(float angle, float x, float y, float z);
static mat4 rotate(float angle, const vec3 &v) { return rotate(angle, v.x, v.y, v.z); }
static mat4 quaternion(float x, float y, float z, float w); // This function assumes that the quarternion is normalized.
static mat4 quaternion(const vec4 &q) { return quaternion(q.x, q.y, q.z, q.w); }
static mat4 swap_yz();
static mat4 perspective(float fovy, float aspect, float z_near, float z_far, handedness handedness, clipzrange clipz);
static mat4 frustum(float left, float right, float bottom, float top, float z_near, float z_far, handedness handedness, clipzrange clipz);
static mat4 look_at(vec3 eye, vec3 center, vec3 up);
vec4 operator*(const vec4 &v) const;
mat4 operator*(const mat4 &m) const;
float operator[](size_t i) const { return matrix[i]; }
float &operator[](size_t i) { return matrix[i]; }
float matrix[4 * 4];
};
struct mat3
{
mat3() = default;
mat3(const mat4 &m);
static mat3 null();
static mat3 identity();
static mat3 from_values(float *matrix);
static mat3 transpose(const mat3 &matrix);
static mat3 inverse(const mat3 &matrix);
static mat3 adjoint(const mat3 &matrix);
static double determinant(const mat3 &m);
float operator[](size_t i) const { return matrix[i]; }
float &operator[](size_t i) { return matrix[i]; }
vec3 operator*(const vec3 &v) const;
mat3 operator*(const mat3 &m) const;
float matrix[3 * 3];
};
struct mat2
{
float operator[](size_t i) const { return matrix[i]; }
float &operator[](size_t i) { return matrix[i]; }
float matrix[2 * 2];
};
struct mat4x3
{
float operator[](size_t i) const { return matrix[i]; }
float &operator[](size_t i) { return matrix[i]; }
float matrix[4 * 3];
};
struct mat4x2
{
float operator[](size_t i) const { return matrix[i]; }
float &operator[](size_t i) { return matrix[i]; }
float matrix[4 * 2];
};
struct mat3x4
{
float operator[](size_t i) const { return matrix[i]; }
float &operator[](size_t i) { return matrix[i]; }
float matrix[3 * 4];
};
struct mat3x2
{
float operator[](size_t i) const { return matrix[i]; }
float &operator[](size_t i) { return matrix[i]; }
float matrix[3 * 2];
};
struct mat2x4
{
float operator[](size_t i) const { return matrix[i]; }
float &operator[](size_t i) { return matrix[i]; }
float matrix[2 * 4];
};
struct mat2x3
{
float operator[](size_t i) const { return matrix[i]; }
float &operator[](size_t i) { return matrix[i]; }
float matrix[2 * 3];
};

View file

@ -1,328 +0,0 @@
//-----------------------------------------------------------------------------
// Note: this is a modified version of dlight. It is not the original software.
//-----------------------------------------------------------------------------
//
// Copyright (c) 2013-2014 Samuel Villarreal
// svkaiser@gmail.com
//
// 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.
//
#pragma once
#include <math.h>
#include <cstdint>
#include <cstddef>
#include "vec.h"
#include "mat.h"
#ifdef M_PI
#undef M_PI
#endif
#define M_PI 3.1415926535897932384626433832795f
#define M_RAD (M_PI / 180.0f)
#define M_DEG (180.0f / M_PI)
#define M_INFINITY 1e30f
#define DEG2RAD(x) ((x) * M_RAD)
#define RAD2DEG(x) ((x) * M_DEG)
#define FLOATSIGNBIT(f) (reinterpret_cast<const unsigned int&>(f) >> 31)
class Angle;
class Math
{
public:
static float Sin(float x);
static float Cos(float x);
static float Tan(float x);
static float ATan2(float x, float y);
static float ACos(float x);
static float Sqrt(float x);
static float Pow(float x, float y);
static float Log(float x);
static float Floor(float x);
static float Ceil(float x);
static float Deg2Rad(float x);
static float Rad2Deg(float x);
static int Abs(int x);
static float Fabs(float x);
static int RoundPowerOfTwo(int x);
static float InvSqrt(float x);
static void Clamp(float &f, const float min, const float max);
static void Clamp(int &i, const int min, const int max);
static void Clamp(uint8_t &b, const uint8_t min, const uint8_t max);
static void Clamp(vec3 &f, const float min, const float max);
static void CubicCurve(const vec3 &start, const vec3 &end, const float time, const vec3 &point, vec3 *vec);
static void QuadraticCurve(const vec3 &start, const vec3 &end, const float time, const vec3 &pt1, const vec3 &pt2, vec3 *vec);
};
class Plane
{
public:
Plane();
Plane(const float a, const float b, const float c, const float d);
Plane(const vec3 &pt1, const vec3 &pt2, const vec3 &pt3);
Plane(const vec3 &normal, const vec3 &point);
Plane(const Plane &plane);
enum PlaneAxis
{
AXIS_YZ = 0,
AXIS_XZ,
AXIS_XY
};
void Set(float a, float b, float c, float d);
const vec3 &Normal() const;
vec3 &Normal();
Plane &SetNormal(const vec3 &normal);
Plane& SetNormal(const vec3& pt1, const vec3& pt2, const vec3& pt3);
Plane& SetNormal(const vec3& v1, const vec3& v2, const vec3& v3, const vec3& v4);
float Distance(const vec3 &point);
Plane &SetDistance(const vec3 &point);
bool IsFacing(const float yaw);
float ToYaw();
float ToPitch();
const vec4 &ToVec4() const;
vec4 &ToVec4();
const PlaneAxis BestAxis() const;
vec3 GetInclination();
static Plane Inverse(const Plane &p) { return Plane(-p.a, -p.b, -p.c, -p.d); }
float zAt(float x, float y) const { return (d - a * x - b * y) / c; }
float a;
float b;
float c;
float d;
};
class Angle
{
public:
Angle();
Angle(const float yaw, const float pitch, const float roll);
Angle(const vec3 &vector);
Angle(const Angle &an);
Angle &Round();
Angle &Clamp180();
Angle &Clamp180Invert();
Angle &Clamp180InvertSum(const Angle &angle);
Angle Diff(Angle &angle);
void ToAxis(vec3 *forward, vec3 *up, vec3 *right);
vec3 ToForwardAxis();
vec3 ToUpAxis();
vec3 ToRightAxis();
const vec3 &ToVec3() const;
vec3 &ToVec3();
Angle operator+(const Angle &angle);
Angle operator-(const Angle &angle);
Angle &operator+=(const Angle &angle);
Angle &operator-=(const Angle &angle);
Angle &operator=(const Angle &angle);
Angle &operator=(const vec3 &vector);
Angle &operator=(const float *vecs);
Angle operator-();
float operator[](int index) const;
float &operator[](int index);
float yaw;
float pitch;
float roll;
};
class BBox
{
public:
BBox();
BBox(const vec3 &vMin, const vec3 &vMax);
void Clear();
vec3 Center() const;
vec3 Extents() const;
float Radius() const;
void AddPoint(const vec3 &vec);
bool PointInside(const vec3 &vec) const;
bool IntersectingBox(const BBox &box) const;
bool IntersectingBox2D(const BBox &box) const;
float DistanceToPlane(Plane &plane);
bool LineIntersect(const vec3 &start, const vec3 &end);
void ToPoints(float *points) const;
void ToVectors(vec3 *vectors) const;
BBox operator+(const float radius) const;
BBox &operator+=(const float radius);
BBox operator+(const vec3 &vec) const;
BBox operator-(const float radius) const;
BBox operator-(const vec3 &vec) const;
BBox &operator-=(const float radius);
BBox operator*(const vec3 &vec) const;
BBox &operator*=(const vec3 &vec);
BBox &operator=(const BBox &bbox);
vec3 operator[](int index) const;
vec3 &operator[](int index);
vec3 min;
vec3 max;
};
/////////////////////////////////////////////////////////////////////////////
inline float Math::Sin(float x)
{
return sinf(x);
}
inline float Math::Cos(float x)
{
return cosf(x);
}
inline float Math::Tan(float x)
{
return tanf(x);
}
inline float Math::ATan2(float x, float y)
{
return atan2f(x, y);
}
inline float Math::ACos(float x)
{
return acosf(x);
}
inline float Math::Sqrt(float x)
{
return x * InvSqrt(x);
}
inline float Math::Pow(float x, float y)
{
return powf(x, y);
}
inline float Math::Log(float x)
{
return logf(x);
}
inline float Math::Floor(float x)
{
return floorf(x);
}
inline float Math::Ceil(float x)
{
return ceilf(x);
}
inline float Math::Deg2Rad(float x)
{
return DEG2RAD(x);
}
inline float Math::Rad2Deg(float x)
{
return RAD2DEG(x);
}
inline int Math::Abs(int x)
{
int y = x >> 31;
return ((x ^ y) - y);
}
inline float Math::Fabs(float x)
{
int tmp = *reinterpret_cast<int*>(&x);
tmp &= 0x7FFFFFFF;
return *reinterpret_cast<float*>(&tmp);
}
inline float Math::InvSqrt(float x)
{
unsigned int i;
float r;
float y;
y = x * 0.5f;
i = *reinterpret_cast<unsigned int*>(&x);
i = 0x5f3759df - (i >> 1);
r = *reinterpret_cast<float*>(&i);
r = r * (1.5f - r * r * y);
return r;
}
inline void Math::Clamp(float &f, const float min, const float max)
{
if (f < min) { f = min; }
if (f > max) { f = max; }
}
inline void Math::Clamp(uint8_t &b, const uint8_t min, const uint8_t max)
{
if (b < min) { b = min; }
if (b > max) { b = max; }
}
inline void Math::Clamp(int &i, const int min, const int max)
{
if (i < min) { i = min; }
if (i > max) { i = max; }
}
inline void Math::Clamp(vec3 &v, const float min, const float max)
{
if (v.x < min) { v.x = min; }
if (v.x > max) { v.x = max; }
if (v.y < min) { v.y = min; }
if (v.y > max) { v.y = max; }
if (v.z < min) { v.z = min; }
if (v.z > max) { v.z = max; }
}
/////////////////////////////////////////////////////////////////////////////
inline vec3 BBox::Center() const
{
return vec3(
(max.x + min.x) * 0.5f,
(max.y + min.y) * 0.5f,
(max.z + min.z) * 0.5f);
}
inline vec3 BBox::Extents() const
{
return vec3(
(max.x - min.x) * 0.5f,
(max.y - min.y) * 0.5f,
(max.z - min.z) * 0.5f);
}

View file

@ -1,203 +0,0 @@
//-----------------------------------------------------------------------------
// Note: this is a modified version of dlight. It is not the original software.
//-----------------------------------------------------------------------------
//
// Copyright (c) 2013-2014 Samuel Villarreal
// svkaiser@gmail.com
//
// 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.
//
#include <math.h>
#include "mathlib.h"
Plane::Plane()
{
this->a = 0;
this->b = 0;
this->c = 0;
this->d = 0;
}
Plane::Plane(const float a, const float b, const float c, const float d)
{
this->a = a;
this->b = b;
this->c = c;
this->d = d;
}
Plane::Plane(const vec3 &pt1, const vec3 &pt2, const vec3 &pt3)
{
SetNormal(pt1, pt2, pt3);
this->d = dot(pt1, Normal());
}
Plane::Plane(const vec3 &normal, const vec3 &point)
{
this->a = normal.x;
this->b = normal.y;
this->c = normal.z;
this->d = dot(point, normal);
}
Plane::Plane(const Plane &plane)
{
this->a = plane.a;
this->b = plane.b;
this->c = plane.c;
this->d = plane.d;
}
void Plane::Set(float a, float b, float c, float d)
{
this->a = a;
this->b = b;
this->c = c;
this->d = d;
}
Plane &Plane::SetNormal(const vec3 &normal)
{
Normal() = normal;
return *this;
}
Plane &Plane::SetNormal(const vec3 &pt1, const vec3 &pt2, const vec3 &pt3)
{
Normal() = normalize(cross(pt2 - pt1, pt3 - pt2));
return *this;
}
Plane& Plane::SetNormal(const vec3& v1, const vec3& v2, const vec3& v3, const vec3& v4)
{
// Handle cases where two vertices share the same position
// TODO fix this hack
// Certain vertex positions are close enough to cause NaN, but aren't equal
auto iv1 = ivec3(v1);
auto iv2 = ivec3(v2);
auto iv3 = ivec3(v3);
auto iv4 = ivec3(v4);
if (iv1 == iv3)
{
return SetNormal(v1, v2, v4);
}
else if (iv2 == iv4)
{
return SetNormal(v1, v2, v3);
}
else if (iv1 == iv2)
{
return SetNormal(v2, v3, v4);
}
else if (iv2 == iv3)
{
return SetNormal(v1, v2, v4);
}
return SetNormal(v1, v2, v3);
}
vec3 const &Plane::Normal() const
{
return *reinterpret_cast<const vec3*>(&a);
}
vec3 &Plane::Normal()
{
return *reinterpret_cast<vec3*>(&a);
}
float Plane::Distance(const vec3 &point)
{
return dot(point, Normal()) - d;
}
Plane &Plane::SetDistance(const vec3 &point)
{
this->d = dot(point, Normal());
return *this;
}
bool Plane::IsFacing(const float yaw)
{
return -Math::Sin(yaw) * a + -Math::Cos(yaw) * b < 0;
}
float Plane::ToYaw()
{
float d = length(Normal());
if (d != 0)
{
float phi;
phi = Math::ACos(b / d);
if (a <= 0)
{
phi = -phi;
}
return phi;
}
return 0;
}
float Plane::ToPitch()
{
return Math::ACos(dot(vec3(0.0f, 0.0f, 1.0f), Normal()));
}
vec4 const &Plane::ToVec4() const
{
return *reinterpret_cast<const vec4*>(&a);
}
vec4 &Plane::ToVec4()
{
return *reinterpret_cast<vec4*>(&a);
}
const Plane::PlaneAxis Plane::BestAxis() const
{
float na = Math::Fabs(a);
float nb = Math::Fabs(b);
float nc = Math::Fabs(c);
// figure out what axis the plane lies on
if (na >= nb && na >= nc)
{
return AXIS_YZ;
}
else if (nb >= na && nb >= nc)
{
return AXIS_XZ;
}
return AXIS_XY;
}
vec3 Plane::GetInclination()
{
vec3 dir = Normal() * dot(vec3(0.0f, 0.0f, 1.0f), Normal());
return normalize(vec3(0.0f, 0.0f, 1.0f) - dir);
}

View file

@ -1,270 +0,0 @@
#pragma once
#include "vec.h"
#include "mat.h"
#include <cfloat>
enum class EulerOrder
{
xyz,
xzy,
yzx,
yxz,
zxy,
zyx
};
template<typename T>
struct quaternionT
{
quaternionT() : x((T)0), y((T)0), z((T)0), w((T)1) { }
quaternionT(T x, T y, T z, T w) : x(x), y(y), z(z), w(w) { }
quaternionT(const vec4T<T> &v) : x(v.x), y(v.y), z(v.z), w(v.w) { }
quaternionT(T angle, const vec3T<T> &axis);
static quaternionT euler(T x, T y, T z, EulerOrder order = EulerOrder::yxz);
static quaternionT euler(const vec3T<T> &xyz, EulerOrder order = EulerOrder::yxz);
static quaternionT rotation_matrix(const mat4 &matrix);
operator vec4T<T>() const { return vec4T<T>(x, y, z, w); }
T x, y, z, w;
};
/// Linear Quaternion Interpolation
template<typename T>
quaternionT<T> lerp(const quaternionT<T> &quaternion_initial, const quaternionT<T> &quaternion_final, T lerp_time)
{
quaternionT<T> q(
quaternion_initial.x * (((T) 1.0) - lerp_time) + quaternion_final.x * lerp_time,
quaternion_initial.y * (((T) 1.0) - lerp_time) + quaternion_final.y * lerp_time,
quaternion_initial.z * (((T) 1.0) - lerp_time) + quaternion_final.z * lerp_time,
quaternion_initial.w * (((T) 1.0) - lerp_time) + quaternion_final.w * lerp_time
);
return normalize(q);
}
/// Spherical Quaternion Interpolation
template<typename T>
quaternionT<T> slerp(const quaternionT<T> &quaternion_initial, const quaternionT<T> &quaternion_final, T slerp_time)
{
T q2[4];
q2[0] = quaternion_final.x;
q2[1] = quaternion_final.y;
q2[2] = quaternion_final.z;
q2[3] = quaternion_final.w;
T cos_theta = quaternion_initial.x * quaternion_final.x
+ quaternion_initial.y * quaternion_final.y
+ quaternion_initial.z * quaternion_final.z
+ quaternion_initial.w * quaternion_final.w;
if (cos_theta < ((T) 0.0))
{
q2[0] = -q2[0];
q2[1] = -q2[1];
q2[2] = -q2[2];
q2[3] = -q2[3];
cos_theta = -cos_theta;
}
T beta = ((T) 1.0) - slerp_time;
if (((T) 1.0) - cos_theta > ((T) 0.001))
{
cos_theta = acos(cos_theta);
T sin_theta = 1.0f / sin(cos_theta);
beta = sin(cos_theta * beta) * sin_theta;
slerp_time = sin(cos_theta * slerp_time) * sin_theta;
}
quaternionT<T> quat;
quat.x = beta * quaternion_initial.x + slerp_time * q2[0];
quat.y = beta * quaternion_initial.y + slerp_time * q2[1];
quat.z = beta * quaternion_initial.z + slerp_time * q2[2];
quat.w = beta * quaternion_initial.w + slerp_time * q2[3];
return quat;
}
template<typename T>
T magnitude(const quaternionT<T> &q)
{
return std::sqrt(q.x * q.x + q.y * q.y + q.z * q.z + q.w * q.w);
}
template<typename T>
quaternionT<T> normalize(const quaternionT<T> &q)
{
T m = magnitude(q);
if (m != T(0))
return quaternionT<T>(q.x / m, q.y / m, q.z / m, q.w / m);
else
return quaternionT<T>(T(0), T(0), T(0), T(0));
}
/// Inverse this quaternion
/// This is the same as the conjugate of a quaternion
template<typename T>
quaternionT<T> inverse(const quaternionT<T> &q)
{
return { -q.x, -q.y, -q.z, q.w };
}
/// Shortest arc quaternion between two vectors
template<typename T>
quaternionT<T> rotation_between(vec3T<T> v0, vec3T<T> v1)
{
v0 = normalize(v0);
v1 = normalize(v1);
T d = dot(v0, v1);
if (d >= 1.0f) // v0 and v1 is the same vector
{
return quaternionT<T>();
}
else if (d < T(1e-6 - 1.0)) // v0 and v1 are parallel but pointing in opposite directions
{
// We can rotate around any axis - find one using the cross product:
vec3T<T> axis = cross(vec3T<T>(T(1), T(0), T(0)), v0);
if (dot(axis, axis) < T(1e-6)) // colinear
axis = cross(vec3T<T>(T(0), T(1), T(0)), v0);
axis = normalize(axis);
return quaternionT<T>((T)3.14159265359, axis);
}
else
{
T s = std::sqrt((1 + d) * 2);
T rcp_s = T(1) / s;
vec3T<T> c = cross(v0, v1);
quaternionT<T> q(vec4T<T>(c * rcp_s, s * T(0.5)));
return normalize(q);
}
}
template<typename T>
quaternionT<T> quaternionT<T>::euler(T x, T y, T z, EulerOrder order)
{
quaternionT<T> q_x(x, vec3T<T>((T)1, (T)0, (T)0));
quaternionT<T> q_y(y, vec3T<T>((T)0, (T)1, (T)0));
quaternionT<T> q_z(z, vec3T<T>((T)0, (T)0, (T)1));
switch (order)
{
default:
case EulerOrder::xyz:
return q_x * q_y * q_z;
case EulerOrder::xzy:
return q_x * q_z * q_y;
case EulerOrder::yzx:
return q_y * q_z * q_x;
case EulerOrder::yxz:
return q_y * q_x * q_z;
case EulerOrder::zxy:
return q_z * q_x * q_y;
case EulerOrder::zyx:
return q_z * q_y * q_x;
}
}
template<typename T>
quaternionT<T> quaternionT<T>::euler(const vec3T<T> &angles, EulerOrder order)
{
return euler(angles.x, angles.y, angles.z, order);
}
template<typename T>
quaternionT<T>::quaternionT(T angle, const vec3T<T> &axis)
{
T len = length(axis);
T half_angle_radians = angle / T(2);
quaternionT<T> q;
q.w = std::cos(half_angle_radians);
q.x = axis.x * std::sin(half_angle_radians) / len;
q.y = axis.y * std::sin(half_angle_radians) / len;
q.z = axis.z * std::sin(half_angle_radians) / len;
*this = normalize(q);
}
template<typename T>
quaternionT<T> quaternionT<T>::rotation_matrix(const mat4 &m)
{
float x, y, z, w;
float size;
// We assume that this is a pure rotation matrix without any scale or translation
float trace = m.matrix[0 * 4 + 0] + m.matrix[1 * 4 + 1] + m.matrix[2 * 4 + 2] + (float)1;
if (trace > (float) 16.0*FLT_EPSILON)
{
size = sqrt(trace) * (float)2;
x = (m.matrix[1 * 4 + 2] - m.matrix[2 * 4 + 1]) / size;
y = (m.matrix[2 * 4 + 0] - m.matrix[0 * 4 + 2]) / size;
z = (m.matrix[0 * 4 + 1] - m.matrix[1 * 4 + 0]) / size;
w = (float) 0.25 * size;
}
else
{
// If the trace of the matrix is equal to zero then identify which major diagonal element has the greatest value.
if (m.matrix[0 * 4 + 0] > m.matrix[1 * 4 + 1] && m.matrix[0 * 4 + 0] > m.matrix[2 * 4 + 2])
{
// Column 0:
size = sqrt((float) 1.0 + m.matrix[0 * 4 + 0] - m.matrix[1 * 4 + 1] - m.matrix[2 * 4 + 2]) * (float)2;
x = (float) 0.25 * size;
y = (m.matrix[0 * 4 + 1] + m.matrix[1 * 4 + 0]) / size;
z = (m.matrix[2 * 4 + 0] + m.matrix[0 * 4 + 2]) / size;
w = (m.matrix[1 * 4 + 2] - m.matrix[2 * 4 + 1]) / size;
}
else if (m.matrix[1 * 4 + 1] > m.matrix[2 * 4 + 2])
{
// Column 1:
size = sqrt((float) 1.0 + m.matrix[1 * 4 + 1] - m.matrix[0 * 4 + 0] - m.matrix[2 * 4 + 2]) * (float)2;
x = (m.matrix[0 * 4 + 1] + m.matrix[1 * 4 + 0]) / size;
y = (float) 0.25 * size;
z = (m.matrix[1 * 4 + 2] + m.matrix[2 * 4 + 1]) / size;
w = (m.matrix[2 * 4 + 0] - m.matrix[0 * 4 + 2]) / size;
}
else
{
// Column 2:
size = sqrt((float) 1.0 + m.matrix[2 * 4 + 2] - m.matrix[0 * 4 + 0] - m.matrix[1 * 4 + 1]) * (float)2;
x = (m.matrix[2 * 4 + 0] + m.matrix[0 * 4 + 2]) / size;
y = (m.matrix[1 * 4 + 2] + m.matrix[2 * 4 + 1]) / size;
z = (float) 0.25 * size;
w = (m.matrix[0 * 4 + 1] - m.matrix[1 * 4 + 0]) / size;
}
}
return { x, y, z, w };
}
template<typename T>
quaternionT<T> operator*(const quaternionT<T> &quaternion_1, const quaternionT<T> &quaternion_2)
{
quaternionT<T> quaternion_dest;
quaternion_dest.x = quaternion_1.w*quaternion_2.x + quaternion_1.x*quaternion_2.w + quaternion_1.y*quaternion_2.z - quaternion_1.z*quaternion_2.y;
quaternion_dest.y = quaternion_1.w*quaternion_2.y + quaternion_1.y*quaternion_2.w + quaternion_1.z*quaternion_2.x - quaternion_1.x*quaternion_2.z;
quaternion_dest.z = quaternion_1.w*quaternion_2.z + quaternion_1.z*quaternion_2.w + quaternion_1.x*quaternion_2.y - quaternion_1.y*quaternion_2.x;
quaternion_dest.w = quaternion_1.w*quaternion_2.w - quaternion_1.x*quaternion_2.x - quaternion_1.y*quaternion_2.y - quaternion_1.z*quaternion_2.z;
return quaternion_dest;
}
template<typename T>
vec3T<T> operator*(const quaternionT<T> &q, const vec3T<T> &v)
{
vec3T<T> q3(q.x, q.y, q.z);
return v + cross(q3, cross(q3, v) + v * q.w) * ((T) 2.0);
}
template<typename T>
quaternionT<T> operator*(const quaternionT<T> &q, const mat4 &m)
{
quaternionT<T> result;
result.x = m.matrix[(4 * 0) + 0] * q.x + m.matrix[(4 * 0) + 1] * q.y + m.matrix[(4 * 0) + 2] * q.z + m.matrix[(4 * 0) + 3] * q.w;
result.y = m.matrix[(4 * 1) + 0] * q.x + m.matrix[(4 * 1) + 1] * q.y + m.matrix[(4 * 1) + 2] * q.z + m.matrix[(4 * 1) + 3] * q.w;
result.z = m.matrix[(4 * 2) + 0] * q.x + m.matrix[(4 * 2) + 1] * q.y + m.matrix[(4 * 2) + 2] * q.z + m.matrix[(4 * 2) + 3] * q.w;
result.w = m.matrix[(4 * 3) + 0] * q.x + m.matrix[(4 * 3) + 1] * q.y + m.matrix[(4 * 3) + 2] * q.z + m.matrix[(4 * 3) + 3] * q.w;
return result;
}
typedef quaternionT<float> quaternion;
typedef quaternionT<double> dquaternion;

View file

@ -1,398 +0,0 @@
#pragma once
#include <cstdint>
#include <algorithm>
#include <cmath>
#undef min
#undef max
class Pointf
{
public:
Pointf() = default;
Pointf(float x, float y) : x(x), y(y) { }
float x = 0.0f;
float y = 0.0f;
};
class Sizef
{
public:
Sizef() = default;
Sizef(float w, float h) : width(w), height(h) { }
float width = 0.0f;
float height = 0.0f;
inline Sizef &operator+=(Sizef b) { width += b.width; height += b.height; return *this; }
inline Sizef &operator-=(Sizef b) { width -= b.width; height -= b.height; return *this; }
};
inline Sizef operator+(Sizef a, Sizef b) { return { a.width + b.width, a.height + b.height }; }
inline Sizef operator-(Sizef a, Sizef b) { return { a.width - b.width, a.height - b.height }; }
class Rectf
{
public:
Rectf() = default;
Rectf(float left, float top, float right, float bottom) : left(left), top(top), right(right), bottom(bottom) { }
Rectf(Pointf pos, Sizef size) : left(pos.x), top(pos.y), right(pos.x + size.width), bottom(pos.y + size.height) { }
static Rectf xywh(float x, float y, float width, float height) { return Rectf(x, y, x + width, y + height); }
Pointf position() const { return { left, top }; }
Sizef size() const { return { right - left, bottom - top }; }
Rectf &boundingRect(const Rectf &rect)
{
Rectf result;
result.left = std::min(left, rect.left);
result.right = std::max(right, rect.right);
result.top = std::min(top, rect.top);
result.bottom = std::max(bottom, rect.bottom);
*this = result;
return *this;
}
bool contains(const Pointf &p) const
{
return (p.x >= left && p.x < right) && (p.y >= top && p.y < bottom);
}
float left = 0.0f;
float top = 0.0f;
float right = 0.0f;
float bottom = 0.0f;
};
template<typename T>
struct vec2T
{
union
{
struct { T x, y; };
struct { T r, g; };
struct { T s, t; };
struct { T v[2]; };
};
vec2T() = default;
vec2T(T v) : x(v), y(v) { }
vec2T(T x, T y) : x(x), y(y) { }
template<typename U>
explicit vec2T(const vec2T<U> &v2) : x(static_cast<T>(v2.x)), y(static_cast<T>(v2.y)) { }
vec2T<T> yx() const { return vec2T<T>(y, x); }
vec2T<T> gr() const { return vec2T<T>(g, r); }
vec2T<T> ts() const { return vec2T<T>(t, s); }
vec2T<T> swizzle(int a, int b) const { return vec2T<T>(v[a], v[b]); }
inline vec2T operator-() const { return vec2T(-x, -y); }
inline vec2T &operator+=(vec2T b) { x += b.x; y += b.y; return *this; }
inline vec2T &operator-=(vec2T b) { x -= b.x; y -= b.y; return *this; }
inline vec2T &operator*=(vec2T b) { x *= b.x; y *= b.y; return *this; }
inline vec2T &operator/=(vec2T b) { x /= b.x; y /= b.y; return *this; }
inline vec2T &operator+=(T b) { x += b; y += b; return *this; }
inline vec2T &operator-=(T b) { x -= b; y -= b; return *this; }
inline vec2T &operator*=(T b) { x *= b; y *= b; return *this; }
inline vec2T &operator/=(T b) { x /= b; y /= b; return *this; }
};
template<typename T>
struct vec3T
{
union
{
struct { T x, y, z; };
struct { T r, g, b; };
struct { T s, t, p; };
struct { T v[3]; };
};
vec3T() = default;
vec3T(T v) : x(v), y(v), z(v) { }
vec3T(const vec2T<T> &v2, T z) : x(v2.x), y(v2.y), z(z) { }
vec3T(T x, T y, T z) : x(x), y(y), z(z) { }
template<typename U>
explicit vec3T(const vec3T<U> &v3) : x(static_cast<T>(v3.x)), y(static_cast<T>(v3.y)), z(static_cast<T>(v3.z)) { }
vec2T<T> xy() const { return vec2T<T>(x, y); }
vec2T<T> rg() const { return vec2T<T>(r, g); }
vec2T<T> st() const { return vec2T<T>(s, t); }
vec2T<T> yx() const { return vec2T<T>(y, x); }
vec2T<T> gr() const { return vec2T<T>(g, r); }
vec2T<T> ts() const { return vec2T<T>(t, s); }
vec3T<T> zyx() const { return vec3T<T>(z, y, x); }
vec3T<T> bgr() const { return vec3T<T>(b, g, r); }
vec3T<T> pts() const { return vec3T<T>(p, t, s); }
vec2T<T> swizzle(int a, int b) const { return vec2T<T>(v[a], v[b]); }
vec3T<T> swizzle(int a, int b, int c) const { return vec3T<T>(v[a], v[b], v[c]); }
T &operator[](int i) { return v[i]; }
const T &operator[](int i) const { return v[i]; }
inline vec3T operator-() const { return vec3T(-x, -y, -z); }
inline vec3T &operator+=(vec3T b) { x += b.x; y += b.y; z += b.z; return *this; }
inline vec3T &operator-=(vec3T b) { x -= b.x; y -= b.y; z -= b.z; return *this; }
inline vec3T &operator*=(vec3T b) { x *= b.x; y *= b.y; z *= b.z; return *this; }
inline vec3T &operator/=(vec3T b) { x /= b.x; y /= b.y; z /= b.z; return *this; }
inline vec3T &operator+=(T b) { x += b; y += b; z += b; return *this; }
inline vec3T &operator-=(T b) { x -= b; y -= b; z -= b; return *this; }
inline vec3T &operator*=(T b) { x *= b; y *= b; z *= b; return *this; }
inline vec3T &operator/=(T b) { x /= b; y /= b; z /= b; return *this; }
};
template<typename T>
struct vec4T
{
union
{
struct { T x, y, z, w; };
struct { T r, g, b, a; };
struct { T s, t, p, q; };
struct { T v[4]; };
};
vec4T() = default;
vec4T(T v) : x(v), y(v), z(v), w(v) { }
vec4T(const vec2T<T> &a, const vec2T<T> &b) : x(a.x), y(a.y), z(b.x), w(b.y) { }
vec4T(const vec3T<T> &v3, T w) : x(v3.x), y(v3.y), z(v3.z), w(w) { }
vec4T(T x, T y, T z, T w) : x(x), y(y), z(z), w(w) { }
template<typename U>
explicit vec4T(const vec4T<U> &v4) : x(static_cast<T>(v4.x)), y(static_cast<T>(v4.y)), z(static_cast<T>(v4.z)), w(static_cast<T>(v4.w)) { }
vec2T<T> xy() const { return vec2T<T>(x, y); }
vec2T<T> rg() const { return vec2T<T>(r, g); }
vec2T<T> st() const { return vec2T<T>(s, t); }
vec2T<T> yx() const { return vec2T<T>(y, x); }
vec2T<T> gr() const { return vec2T<T>(g, r); }
vec2T<T> ts() const { return vec2T<T>(t, s); }
vec3T<T> xyz() const { return vec3T<T>(x, y, z); }
vec3T<T> rgb() const { return vec3T<T>(r, g, b); }
vec3T<T> stp() const { return vec3T<T>(s, t, p); }
vec3T<T> zyx() const { return vec3T<T>(z, y, x); }
vec3T<T> bgr() const { return vec3T<T>(b, g, r); }
vec3T<T> pts() const { return vec3T<T>(p, t, s); }
vec4T<T> wzyx() const { return vec4T<T>(w, z, y, x); }
vec4T<T> abgr() const { return vec4T<T>(a, b, g, r); }
vec4T<T> qpts() const { return vec4T<T>(q, p, t, s); }
vec4T<T> bgra() const { return vec4T<T>(b, g, r, a); }
vec2T<T> swizzle(int a, int b) const { return vec2T<T>(v[a], v[b]); }
vec3T<T> swizzle(int a, int b, int c) const { return vec3T<T>(v[a], v[b], v[c]); }
vec4T<T> swizzle(int a, int b, int c, int d) const { return vec4T<T>(v[a], v[b], v[c], v[d]); }
inline vec4T operator-() const { return vec4T(-x, -y, -z, -w); }
inline vec4T &operator+=(vec4T b) { x += b.x; y += b.y; z += b.z; w += b.w; return *this; }
inline vec4T &operator-=(vec4T b) { x -= b.x; y -= b.y; z -= b.z; w -= b.w; return *this; }
inline vec4T &operator*=(vec4T b) { x *= b.x; y *= b.y; z *= b.z; w *= b.w; return *this; }
inline vec4T &operator/=(vec4T b) { x /= b.x; y /= b.y; z /= b.z; w /= b.w; return *this; }
inline vec4T &operator+=(T b) { x += b; y += b; z += b; w += b; return *this; }
inline vec4T &operator-=(T b) { x -= b; y -= b; z -= b; w -= b; return *this; }
inline vec4T &operator*=(T b) { x *= b; y *= b; z *= b; w *= b; return *this; }
inline vec4T &operator/=(T b) { x /= b; y /= b; z /= b; w /= b; return *this; }
};
template<typename T> vec2T<T> operator+(vec2T<T> a, vec2T<T> b) { return vec2T<T>(a.x + b.x, a.y + b.y); }
template<typename T> vec2T<T> operator-(vec2T<T> a, vec2T<T> b) { return vec2T<T>(a.x - b.x, a.y - b.y); }
template<typename T> vec2T<T> operator*(vec2T<T> a, vec2T<T> b) { return vec2T<T>(a.x * b.x, a.y * b.y); }
template<typename T> vec2T<T> operator/(vec2T<T> a, vec2T<T> b) { return vec2T<T>(a.x / b.x, a.y / b.y); }
template<typename T> vec2T<T> operator+(T a, vec2T<T> b) { return vec2T<T>(a + b.x, a + b.y); }
template<typename T> vec2T<T> operator-(T a, vec2T<T> b) { return vec2T<T>(a - b.x, a - b.y); }
template<typename T> vec2T<T> operator*(T a, vec2T<T> b) { return vec2T<T>(a * b.x, a * b.y); }
template<typename T> vec2T<T> operator/(T a, vec2T<T> b) { return vec2T<T>(a / b.x, a / b.y); }
template<typename T> vec2T<T> operator+(vec2T<T> a, T b) { return vec2T<T>(a.x + b, a.y + b); }
template<typename T> vec2T<T> operator-(vec2T<T> a, T b) { return vec2T<T>(a.x - b, a.y - b); }
template<typename T> vec2T<T> operator*(vec2T<T> a, T b) { return vec2T<T>(a.x * b, a.y * b); }
template<typename T> vec2T<T> operator/(vec2T<T> a, T b) { return vec2T<T>(a.x / b, a.y / b); }
template<typename T> vec3T<T> operator+(vec3T<T> a, vec3T<T> b) { return vec3T<T>(a.x + b.x, a.y + b.y, a.z + b.z); }
template<typename T> vec3T<T> operator-(vec3T<T> a, vec3T<T> b) { return vec3T<T>(a.x - b.x, a.y - b.y, a.z - b.z); }
template<typename T> vec3T<T> operator*(vec3T<T> a, vec3T<T> b) { return vec3T<T>(a.x * b.x, a.y * b.y, a.z * b.z); }
template<typename T> vec3T<T> operator/(vec3T<T> a, vec3T<T> b) { return vec3T<T>(a.x / b.x, a.y / b.y, a.z / b.z); }
template<typename T> vec3T<T> operator+(T a, vec3T<T> b) { return vec3T<T>(a + b.x, a + b.y, a + b.z); }
template<typename T> vec3T<T> operator-(T a, vec3T<T> b) { return vec3T<T>(a - b.x, a - b.y, a - b.z); }
template<typename T> vec3T<T> operator*(T a, vec3T<T> b) { return vec3T<T>(a * b.x, a * b.y, a * b.z); }
template<typename T> vec3T<T> operator/(T a, vec3T<T> b) { return vec3T<T>(a / b.x, a / b.y, a / b.z); }
template<typename T> vec3T<T> operator+(vec3T<T> a, T b) { return vec3T<T>(a.x + b, a.y + b, a.z + b); }
template<typename T> vec3T<T> operator-(vec3T<T> a, T b) { return vec3T<T>(a.x - b, a.y - b, a.z - b); }
template<typename T> vec3T<T> operator*(vec3T<T> a, T b) { return vec3T<T>(a.x * b, a.y * b, a.z * b); }
template<typename T> vec3T<T> operator/(vec3T<T> a, T b) { return vec3T<T>(a.x / b, a.y / b, a.z / b); }
template<typename T> vec4T<T> operator+(vec4T<T> a, vec4T<T> b) { return vec4T<T>(a.x + b.x, a.y + b.y, a.z + b.z, a.w + b.w); }
template<typename T> vec4T<T> operator-(vec4T<T> a, vec4T<T> b) { return vec4T<T>(a.x - b.x, a.y - b.y, a.z - b.z, a.w - b.w); }
template<typename T> vec4T<T> operator*(vec4T<T> a, vec4T<T> b) { return vec4T<T>(a.x * b.x, a.y * b.y, a.z * b.z, a.w * b.w); }
template<typename T> vec4T<T> operator/(vec4T<T> a, vec4T<T> b) { return vec4T<T>(a.x / b.x, a.y / b.y, a.z / b.z, a.w / b.w); }
template<typename T> vec4T<T> operator+(T a, vec4T<T> b) { return vec4T<T>(a + b.x, a + b.y, a + b.z, a + b.w); }
template<typename T> vec4T<T> operator-(T a, vec4T<T> b) { return vec4T<T>(a - b.x, a - b.y, a - b.z, a - b.w); }
template<typename T> vec4T<T> operator*(T a, vec4T<T> b) { return vec4T<T>(a * b.x, a * b.y, a * b.z, a * b.w); }
template<typename T> vec4T<T> operator/(T a, vec4T<T> b) { return vec4T<T>(a / b.x, a / b.y, a / b.z, a / b.w); }
template<typename T> vec4T<T> operator+(vec4T<T> a, T b) { return vec4T<T>(a.x + b, a.y + b, a.z + b, a.w + b); }
template<typename T> vec4T<T> operator-(vec4T<T> a, T b) { return vec4T<T>(a.x - b, a.y - b, a.z - b, a.w - b); }
template<typename T> vec4T<T> operator*(vec4T<T> a, T b) { return vec4T<T>(a.x * b, a.y * b, a.z * b, a.w * b); }
template<typename T> vec4T<T> operator/(vec4T<T> a, T b) { return vec4T<T>(a.x / b, a.y / b, a.z / b, a.w / b); }
template<typename T> bool operator==(const vec2T<T> &a, const vec2T<T> &b) { return a.x == b.x && a.y == b.y; }
template<typename T> bool operator==(const vec3T<T> &a, const vec3T<T> &b) { return a.x == b.x && a.y == b.y && a.z == b.z; }
template<typename T> bool operator==(const vec4T<T> &a, const vec4T<T> &b) { return a.x == b.x && a.y == b.y && a.z == b.z && a.w == b.w; }
template<typename T> bool operator!=(const vec2T<T> &a, const vec2T<T> &b) { return a.x != b.x || a.y != b.y; }
template<typename T> bool operator!=(const vec3T<T> &a, const vec3T<T> &b) { return a.x != b.x || a.y != b.y || a.z != b.z; }
template<typename T> bool operator!=(const vec4T<T> &a, const vec4T<T> &b) { return a.x != b.x || a.y != b.y || a.z != b.z || a.w == b.w; }
typedef vec2T<float> vec2;
typedef vec3T<float> vec3;
typedef vec4T<float> vec4;
typedef vec2T<double> dvec2;
typedef vec3T<double> dvec3;
typedef vec4T<double> dvec4;
typedef vec2T<int32_t> ivec2;
typedef vec3T<int32_t> ivec3;
typedef vec4T<int32_t> ivec4;
typedef vec2T<uint32_t> uvec2;
typedef vec3T<uint32_t> uvec3;
typedef vec4T<uint32_t> uvec4;
typedef vec2T<bool> bvec2;
typedef vec3T<bool> bvec3;
typedef vec4T<bool> bvec4;
typedef vec2T<int16_t> ivec2s;
typedef vec3T<int16_t> ivec3s;
typedef vec4T<int16_t> ivec4s;
typedef vec2T<uint16_t> uvec2s;
typedef vec3T<uint16_t> uvec3s;
typedef vec4T<uint16_t> uvec4s;
typedef vec2T<int8_t> ivec2b;
typedef vec3T<int8_t> ivec3b;
typedef vec4T<int8_t> ivec4b;
typedef vec2T<uint8_t> uvec2b;
typedef vec3T<uint8_t> uvec3b;
typedef vec4T<uint8_t> uvec4b;
inline float dot(vec2 a, vec2 b) { return a.x * b.x + a.y * b.y; }
inline float dot(vec3 a, vec3 b) { return a.x * b.x + a.y * b.y + a.z * b.z; }
inline float dot(vec4 a, vec4 b) { return a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w; }
inline double dot(dvec2 a, dvec2 b) { return a.x * b.x + a.y * b.y; }
inline double dot(dvec3 a, dvec3 b) { return a.x * b.x + a.y * b.y + a.z * b.z; }
inline double dot(dvec4 a, dvec4 b) { return a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w; }
inline float radians(short deg) { return deg * (3.14159265359f / 180.0f); }
inline float degrees(short rad) { return rad * (180.0f / 3.14159265359f); }
inline float radians(int deg) { return deg * (3.14159265359f / 180.0f); }
inline float degrees(int rad) { return rad * (180.0f / 3.14159265359f); }
inline float radians(float deg) { return deg * (3.14159265359f / 180.0f); }
inline float degrees(float rad) { return rad * (180.0f / 3.14159265359f); }
inline double radians(double deg) { return deg * (3.14159265359 / 180.0); }
inline double degrees(double rad) { return rad * (180.0 / 3.14159265359); }
inline float length(vec2 v) { return std::sqrt(dot(v, v)); }
inline float length(vec3 v) { return std::sqrt(dot(v, v)); }
inline float length(vec4 v) { return std::sqrt(dot(v, v)); }
inline double length(dvec2 v) { return std::sqrt(dot(v, v)); }
inline double length(dvec3 v) { return std::sqrt(dot(v, v)); }
inline double length(dvec4 v) { return std::sqrt(dot(v, v)); }
inline vec2 normalize(vec2 v) { return v / length(v); }
inline vec3 normalize(vec3 v) { return v / length(v); }
inline vec4 normalize(vec4 v) { return v / length(v); }
inline dvec2 normalize(dvec2 v) { return v / length(v); }
inline dvec3 normalize(dvec3 v) { return v / length(v); }
inline dvec4 normalize(dvec4 v) { return v / length(v); }
inline vec3 cross(vec3 a, vec3 b) { return { a.y * b.z - b.y * a.z, a.z * b.x - b.z * a.x, a.x * b.y - b.x * a.y }; }
inline dvec3 cross(dvec3 a, dvec3 b) { return { a.y * b.z - b.y * a.z, a.z * b.x - b.z * a.x, a.x * b.y - b.x * a.y }; }
inline vec3 reflect(vec3 I, vec3 N) { return I - 2.0f * dot(N, I) * N; }
inline dvec3 reflect(dvec3 I, dvec3 N) { return I - 2.0 * dot(N, I) * N; }
inline vec3 refract(vec3 I, vec3 N, float eta) { float NdotI = dot(N, I); float k = 1.0f - eta * eta * (1.0f - NdotI * NdotI); return k < 0.0f ? vec3(0.0f) : I * eta - (eta * NdotI + std::sqrt(k)) * N; }
inline dvec3 refract(dvec3 I, dvec3 N, double eta) { double NdotI = dot(N, I); double k = 1.0 - eta * eta * (1.0 - NdotI * NdotI); return k < 0.0 ? dvec3(0.0) : I * eta - (eta * NdotI + std::sqrt(k)) * N; }
template<typename T>
T mix(T a, T b, float t) { return a * (1.0f - t) + b * t; }
template<typename T>
T mix(T a, T b, double t) { return a * (1.0 - t) + b * t; }
template<typename T>
T clamp(T val, T minval, T maxval) { return std::max<T>(std::min<T>(val, maxval), minval); }
template<class T>
T smoothstep(const T edge0, const T edge1, const T x)
{
auto t = clamp<T>((x - edge0) / (edge1 - edge0), T(0.0), T(1.0));
return t * t * (T(3.0) - T(2.0) * t);
}
inline float slerp(float t)
{
float s = t * t;
return s * s * (1.0f / 16.0f) - s * 0.5f + 1.0f;
}
inline double slerp(double t)
{
double s = t * t;
return s * s * (1.0 / 16.0) - s * 0.5 + 1.0;
}
template<typename T>
T spline(T a, T b, T c, T d, float t)
{
float w0 = slerp(t + 1.0f);
float w1 = slerp(t);
float w2 = slerp(t - 1.0f);
float w3 = slerp(t - 2.0f);
float invweight = 1.0f / (w0 + w1 + w2 + w3);
return (a * w0 + b * w1 + c * w2 + d * w3) * invweight;
}
template<typename T>
T spline(T a, T b, T c, T d, double t)
{
double w0 = slerp(t + 1.0);
double w1 = slerp(t);
double w2 = slerp(t - 1.0);
double w3 = slerp(t - 2.0);
double invweight = 1.0 / (w0 + w1 + w2 + w3);
return (a * w0 + b * w1 + c * w2 + d * w3) * invweight;
}
inline dvec2 to_dvec2(vec2 v) { return dvec2(v.x, v.y); }
inline dvec3 to_dvec3(vec3 v) { return dvec3(v.x, v.y, v.z); }
inline dvec4 to_dvec4(vec4 v) { return dvec4(v.x, v.y, v.z, v.w); }
inline vec2 to_vec2(dvec2 v) { return vec2((float)v.x, (float)v.y); }
inline vec3 to_vec3(dvec3 v) { return vec3((float)v.x, (float)v.y, (float)v.z); }
inline vec4 to_vec4(dvec4 v) { return vec4((float)v.x, (float)v.y, (float)v.z, (float)v.w); }

View file

@ -26,15 +26,11 @@
**
**/
#include "model_ue1.h"
#include "model_obj.h"
#include "model_md2.h"
#include "model_md3.h"
#include "modelrenderer.h"
FFileSystem fileSystem;
FTextureManager TexMan;
/////////////////////////////////////////////////////////////////////////////
FModel::FModel()
@ -148,31 +144,13 @@ std::unique_ptr<FModel> LoadModel(const char * path, const char * modelfile)
FileData lumpd = fileSystem.ReadFile(lump);
char * buffer = (char*)lumpd.GetMem();
if ( (size_t)fullname.LastIndexOf("_d.3d") == fullname.Len()-5 )
{
FString anivfile = fullname.GetChars();
anivfile.Substitute("_d.3d","_a.3d");
if ( fileSystem.CheckNumForFullName(anivfile) > 0 )
{
model.reset(new FUE1Model);
}
}
else if ( (size_t)fullname.LastIndexOf("_a.3d") == fullname.Len()-5 )
{
FString datafile = fullname.GetChars();
datafile.Substitute("_a.3d","_d.3d");
if ( fileSystem.CheckNumForFullName(datafile) > 0 )
{
model.reset(new FUE1Model);
}
}
#if 0
else if ( (size_t)fullname.LastIndexOf(".obj") == fullname.Len() - 4 )
if ( (size_t)fullname.LastIndexOf(".obj") == fullname.Len() - 4 )
{
model.reset(new FOBJModel);
}
#endif
else if (!memcmp(buffer, "DMDM", 4))
if (!memcmp(buffer, "DMDM", 4))
{
model.reset(new FDMDModel);
}

View file

@ -3,107 +3,17 @@
#include "framework/tarray.h"
#include "framework/templates.h"
#include "framework/zstring.h"
#include "math/mathlib.h"
#include "framework/vectors.h"
#include "framework/textureid.h"
#include <stdint.h>
#include <string>
#include <memory>
class FModelRenderer;
class FGameTexture;
class IModelVertexBuffer;
class FModel;
struct FSpriteModelFrame;
struct FileData
{
char* GetMem() { return nullptr; }
};
class FFileSystem
{
public:
int CheckNumForFullName(const FString& fullname) { return -1; }
int FileLength(int lump) { return 0; }
FileData ReadFile(int lump) { return {}; }
const char* GetFileFullName(int lump, bool returnshort = true) const { return ""; }
};
extern FFileSystem fileSystem;
class FTextureID
{
public:
bool isValid() const { return false; }
int GetIndex() const { return 0; }
};
class FNullTextureID : public FTextureID
{
public:
};
class FGameTexture
{
public:
};
enum class ETextureType : uint8_t
{
Any,
Wall,
Flat,
Sprite,
WallPatch,
Build, // no longer used but needs to remain for ZScript
SkinSprite,
Decal,
MiscPatch,
FontChar,
Override, // For patches between TX_START/TX_END
Autopage, // Automap background - used to enable the use of FAutomapTexture
SkinGraphic,
Null,
FirstDefined,
Special,
SWCanvas,
};
class FTextureManager
{
public:
FGameTexture* GetGameTexture(FTextureID, bool)
{
return nullptr;
}
enum
{
TEXMAN_TryAny = 1,
TEXMAN_Overridable = 2,
TEXMAN_ReturnFirst = 4,
TEXMAN_AllowSkins = 8,
TEXMAN_ShortNameOnly = 16,
TEXMAN_DontCreate = 32,
TEXMAN_Localize = 64,
TEXMAN_ForceLookup = 128,
TEXMAN_NoAlias = 256,
};
enum
{
HIT_Wall = 1,
HIT_Flat = 2,
HIT_Sky = 4,
HIT_Sprite = 8,
HIT_Columnmode = HIT_Wall | HIT_Sky | HIT_Sprite
};
FTextureID CheckForTexture(const char* name, ETextureType usetype, uint32_t flags = TEXMAN_TryAny) { return {}; }
};
extern FTextureManager TexMan;
struct FModelVertex
{
float x, y, z; // world position

View file

@ -23,7 +23,6 @@
#pragma once
#include "model.h"
#include "math/mathlib.h"
#if 0

Some files were not shown because too many files have changed in this diff Show more