179 Commits
main ... main

Author SHA1 Message Date
cc95912a7e Next patch 2026-02-24 23:46:31 -08:00
85a44b930d Node patch 2026-02-24 23:04:47 -08:00
cd3090817a Next patch 2026-02-24 21:30:00 -08:00
d45c632dcd Patch_2 2026-02-24 17:35:26 -08:00
1c3c30e6ce Patch_2 2026-02-24 11:44:01 -08:00
c9839c9be6 Merge pull request 'Patch_2' (#2) from e2002e_0 into main
Reviewed-on: Onek8/LNXSDK#2
2026-02-24 18:54:11 +00:00
6a17251520 merge upstream 2026-02-24 07:40:25 +00:00
0adcafd697 Patch_1 2026-02-21 22:17:44 -08:00
423807c62f merge upstream 2026-02-21 22:47:25 +00:00
5d1132b24c merge upstream 2026-02-21 07:52:44 +00:00
232ae3e7bc Upload files to "Krom" 2026-02-21 07:50:44 +00:00
a861665c98 Delete Krom/Krom.exe 2026-02-21 07:49:27 +00:00
4852a40848 Delete Krom/Krom 2026-02-21 07:49:09 +00:00
d5f3f05ab6 merge upstream 2025-11-14 17:48:37 +00:00
f5fa754e17 merge upstream 2025-10-03 22:16:29 +00:00
69a2bb1e7e merge upstream 2025-09-30 06:02:59 +00:00
5b86f32b51 merge upstream 2025-09-29 22:41:09 +00:00
590e6219d5 merge upstream 2025-08-14 23:01:45 +00:00
662981fa03 Update leenkx/blender/lnx/material/make_mesh.py 2025-08-14 22:46:53 +00:00
a3866fb604 merge upstream 2025-08-14 21:32:32 +00:00
fbf63e4f17 Update leenkx/Shaders/std/brdf.glsl 2025-07-22 23:33:21 +00:00
a318e08072 Update leenkx/Shaders/sss_pass/sss_pass.frag.glsl 2025-07-22 23:20:35 +00:00
7ae458a9dd merge upstream 2025-07-22 23:06:30 +00:00
9984615f8c merge upstream 2025-07-17 17:23:34 +00:00
0430e06acd merge upstream 2025-07-16 05:57:15 +00:00
9622f35b73 Update leenkx/Shaders/std/brdf.glsl 2025-07-10 09:10:43 +00:00
da666e6d23 Update leenkx/blender/lnx/material/cycles_nodes/nodes_shader.py 2025-07-10 09:03:26 +00:00
6ff7804ec1 Update leenkx/Shaders/ssrefr_pass/ssrefr_pass.json 2025-07-10 09:01:58 +00:00
0265ef5b64 Update leenkx/Shaders/ssrefr_pass/ssrefr_pass.frag.glsl 2025-07-10 09:01:28 +00:00
53c5000975 Update leenkx/Shaders/ssrefr_pass/ssrefr_pass.frag.glsl 2025-07-10 09:00:12 +00:00
7647231696 Update leenkx/Shaders/ssrefr_pass/ssrefr_pass.frag.glsl 2025-07-10 08:59:53 +00:00
1e583a795d Update leenkx/blender/lnx/props_renderpath.py 2025-07-10 05:10:54 +00:00
da25d8c313 Update leenkx/Shaders/voxel_resolve_diffuse/voxel_resolve_diffuse.comp.glsl 2025-07-10 01:16:02 +00:00
70695b3b31 Update leenkx/Shaders/deferred_light/deferred_light.frag.glsl 2025-07-10 01:14:03 +00:00
858537d54c revert 2998a5585e
revert Update leenkx/Shaders/ssao_pass/ssao_pass.frag.glsl
2025-07-10 00:58:53 +00:00
2998a5585e Update leenkx/Shaders/ssao_pass/ssao_pass.frag.glsl 2025-07-10 00:57:07 +00:00
c35c59e6a9 Update leenkx/Shaders/voxel_temporal/voxel_temporal.comp.glsl 2025-07-10 00:10:46 +00:00
15ac833f2c Update leenkx/Shaders/std/constants.glsl 2025-07-10 00:09:21 +00:00
8077f00ada Update leenkx/blender/lnx/props_renderpath.py 2025-07-09 23:53:56 +00:00
58140ad583 Update leenkx/Shaders/std/shadows.glsl 2025-07-09 23:18:52 +00:00
9e2b601445 revert 0439dde4a8
revert Update leenkx/Shaders/deferred_light/deferred_light.frag.glsl
2025-07-09 23:16:54 +00:00
0439dde4a8 Update leenkx/Shaders/deferred_light/deferred_light.frag.glsl 2025-07-09 23:15:54 +00:00
2e7ccb5151 merge upstream 2025-07-06 17:41:02 +00:00
b89ebfd9c6 Update leenkx/blender/lnx/material/make_refract.py 2025-07-03 04:34:04 +00:00
a142b248ef merge upstream 2025-07-03 04:30:45 +00:00
1c648b0433 merge upstream 2025-07-02 05:19:56 +00:00
3103a976a6 merge upstream 2025-06-30 21:02:24 +00:00
82fa7bcfe3 merge upstream 2025-06-30 20:40:44 +00:00
709e0ed9cb merge upstream 2025-06-24 18:45:57 +00:00
7bcf985023 merge upstream 2025-06-24 18:24:23 +00:00
d23232205b merge upstream 2025-06-22 21:38:20 +00:00
88c7c5b99e merge upstream 2025-06-13 15:18:22 +00:00
2434ad07f2 merge upstream 2025-06-10 20:28:46 +00:00
00493bed9c merge upstream 2025-06-07 21:35:02 +00:00
7b17633c28 Update leenkx/blender/lnx/material/make_cluster.py 2025-06-07 19:47:12 +00:00
1ef805eb0b Update leenkx/blender/lnx/material/make_mesh.py 2025-06-06 20:01:29 +00:00
cb7b041fea merge upstream 2025-06-06 19:44:32 +00:00
a0e8e1a1a6 merge upstream 2025-06-05 17:49:54 +00:00
846c3b2c11 Update leenkx/blender/lnx/material/make_mesh.py 2025-06-03 16:18:05 +00:00
b5af208766 Update leenkx/Shaders/std/shadows.glsl 2025-06-03 03:09:32 +00:00
63565052e3 Disable BayerMatrix momentaraily 2025-06-02 20:37:45 +00:00
38eb66a0b5 Update leenkx/blender/lnx/material/make_cluster.py 2025-06-02 20:35:28 +00:00
908efdd554 Update leenkx/blender/lnx/material/make_mesh.py 2025-06-02 20:33:49 +00:00
e014484d27 merge upstream 2025-06-02 20:16:04 +00:00
872433cafb Update leenkx/Shaders/voxel_temporal/voxel_temporal.comp.glsl 2025-06-01 03:42:22 +00:00
74bbb6ca87 Update leenkx/Shaders/voxel_sdf_jumpflood/voxel_sdf_jumpflood.comp.glsl 2025-06-01 03:41:14 +00:00
cbbd6fe495 Update leenkx/Shaders/voxel_offsetprev/voxel_offsetprev.comp.glsl 2025-06-01 03:40:37 +00:00
45a48acf8a Update leenkx/Sources/leenkx/renderpath/Inc.hx 2025-06-01 03:39:35 +00:00
c769b3ca26 merge upstream 2025-05-30 21:46:34 +00:00
c4378be891 Update leenkx/Shaders/voxel_temporal/voxel_temporal.comp.glsl 2025-05-30 19:19:33 +00:00
6ad615a961 Update leenkx/Shaders/std/constants.glsl 2025-05-30 19:18:06 +00:00
1e4510ba56 Update leenkx/Shaders/std/conetrace.glsl 2025-05-30 19:16:59 +00:00
a2714bf101 merge upstream 2025-05-30 19:10:59 +00:00
5639234eb9 Update leenkx/Shaders/std/light.glsl 2025-05-29 21:22:25 +00:00
1591ccdae5 Update leenkx/Shaders/deferred_light/deferred_light.json 2025-05-29 16:56:38 +00:00
9cb5232187 Update leenkx/Shaders/deferred_light/deferred_light.frag.glsl 2025-05-29 16:53:10 +00:00
6b25d8c8ad revert 13ca31f480
revert Update leenkx/Sources/leenkx/renderpath/Inc.hx
2025-05-28 01:36:04 +00:00
13ca31f480 Update leenkx/Sources/leenkx/renderpath/Inc.hx 2025-05-28 01:28:14 +00:00
d102e59040 Update leenkx/blender/lnx/material/make_voxel.py 2025-05-27 18:53:36 +00:00
df0e24c307 Update leenkx/Shaders/voxel_temporal/voxel_temporal.comp.glsl 2025-05-27 18:42:41 +00:00
2df86850f8 Update leenkx/Shaders/std/constants.glsl 2025-05-27 17:51:56 +00:00
02ff259860 Update leenkx/blender/lnx/material/make_finalize.py 2025-05-27 17:23:03 +00:00
2e6de515ef Update leenkx/Shaders/voxel_temporal/voxel_temporal.comp.glsl 2025-05-26 23:29:54 +00:00
41b840212c Update leenkx/blender/lnx/props_ui.py 2025-05-26 23:25:50 +00:00
429e6d6768 Update leenkx/Shaders/std/constants.glsl 2025-05-22 19:30:19 +00:00
2d8bfbf181 Update leenkx/Sources/leenkx/renderpath/RenderPathForward.hx 2025-05-22 03:07:01 +00:00
1ad7e0eaf4 Update leenkx/Sources/leenkx/renderpath/RenderPathDeferred.hx 2025-05-22 03:03:50 +00:00
ae72401657 Update leenkx/Sources/leenkx/renderpath/Inc.hx 2025-05-22 02:57:45 +00:00
58b9000305 Update leenkx/Shaders/voxel_temporal/voxel_temporal.comp.glsl 2025-05-22 02:27:46 +00:00
0cc86c41b8 Update leenkx/Shaders/voxel_resolve_specular/voxel_resolve_specular.comp.glsl 2025-05-22 02:25:46 +00:00
25f8c5f64c Update leenkx/Shaders/voxel_resolve_ao/voxel_resolve_ao.comp.glsl 2025-05-22 02:24:58 +00:00
109544cea9 Update leenkx/Shaders/std/light.glsl 2025-05-22 02:20:28 +00:00
4c92c4bcc9 Update leenkx/Shaders/std/conetrace.glsl 2025-05-22 02:16:35 +00:00
3433afb1c3 Update leenkx/Shaders/ssrefr_pass/ssrefr_pass.frag.glsl 2025-05-22 02:14:06 +00:00
e22b522059 Update leenkx/Shaders/deferred_light/deferred_light.frag.glsl 2025-05-22 02:10:45 +00:00
200af34740 Update leenkx/Shaders/blur_edge_pass/blur_edge_pass.frag.glsl 2025-05-22 02:07:44 +00:00
6d7b0078b4 Update leenkx/blender/lnx/props_renderpath.py 2025-05-21 23:26:42 +00:00
fa501cb09b Update leenkx/blender/lnx/write_data.py 2025-05-21 23:18:53 +00:00
62a4bbb714 Update leenkx/blender/lnx/props_ui.py 2025-05-21 23:14:37 +00:00
eab3e3b30c Update leenkx/blender/lnx/props_renderpath.py 2025-05-21 23:07:11 +00:00
3be7528a6c Update leenkx/blender/lnx/material/make_voxel.py 2025-05-21 22:58:06 +00:00
c9dd46c5e3 Update leenkx/blender/lnx/material/make_shader.py 2025-05-21 22:56:05 +00:00
99806b8069 revert e98bfb125d
revert Update leenkx/blender/lnx/material/make_shader.py
2025-05-21 22:51:16 +00:00
e98bfb125d Update leenkx/blender/lnx/material/make_shader.py 2025-05-21 22:47:03 +00:00
c4c0e2beaa Delete leenkx/blender/lnx/material/make_refraction_buffer.py 2025-05-21 22:43:18 +00:00
36cbc934ba Update leenkx/blender/lnx/material/make_finalize.py 2025-05-21 22:42:11 +00:00
210d5ea532 Update leenkx/blender/lnx/material/make_finalize.py 2025-05-21 22:39:52 +00:00
dab9a38424 Update leenkx/blender/lnx/material/make_mesh.py 2025-05-21 22:37:15 +00:00
b7bbe40348 Update leenkx/blender/lnx/material/make_cluster.py 2025-05-21 22:33:01 +00:00
8b084156ff Update leenkx/blender/lnx/make_world.py 2025-05-21 22:29:40 +00:00
538c364f33 Update leenkx/blender/lnx/make_renderpath.py 2025-05-21 22:26:58 +00:00
09eee93ac9 Update leenkx/Sources/iron/object/Uniforms.hx 2025-05-21 16:25:58 +00:00
8b5a77c001 Update leenkx/Sources/leenkx/renderpath/RenderPathForward.hx 2025-05-21 16:23:20 +00:00
436b7fac02 Update leenkx/Sources/leenkx/renderpath/RenderPathDeferred.hx 2025-05-21 16:21:05 +00:00
9ef9f5a637 Update leenkx/Sources/leenkx/renderpath/Inc.hx 2025-05-21 16:15:50 +00:00
b8ca4be56a Update leenkx/Shaders/voxel_temporal/voxel_temporal.comp.glsl 2025-05-21 16:08:14 +00:00
29e4993f06 Update leenkx/Shaders/voxel_sdf_jumpflood/voxel_sdf_jumpflood.comp.glsl 2025-05-21 16:05:53 +00:00
4134352688 Update leenkx/Shaders/voxel_resolve_specular/voxel_resolve_specular.comp.glsl 2025-05-21 02:10:21 +00:00
25cf758a33 Update leenkx/Shaders/voxel_resolve_diffuse/voxel_resolve_diffuse.comp.glsl 2025-05-21 02:08:50 +00:00
23af038a16 Update leenkx/Shaders/voxel_resolve_ao/voxel_resolve_ao.comp.glsl 2025-05-21 02:07:21 +00:00
7f7878aaa6 Update leenkx/Shaders/voxel_offsetprev/voxel_offsetprev.comp.glsl 2025-05-21 02:05:33 +00:00
d7e076fb56 Add leenkx/Shaders/voxel_light/voxel_light.comp.glsl 2025-05-21 02:02:05 +00:00
dab915b60d Delete leenkx/Shaders/voxel_light.comp.glsl 2025-05-21 02:01:43 +00:00
74389ba76a Add leenkx/Shaders/voxel_light.comp.glsl 2025-05-21 02:01:35 +00:00
bd5afc797d Update leenkx/Shaders/std/shadows.glsl 2025-05-21 01:05:37 +00:00
27b4ec42a8 Update leenkx/Shaders/std/light.glsl 2025-05-21 01:04:30 +00:00
ab7edaa9e3 Update leenkx/Shaders/std/constants.glsl 2025-05-21 01:02:56 +00:00
27540ac7e9 Update leenkx/Shaders/std/conetrace.glsl 2025-05-21 01:01:47 +00:00
a5b512f20b Update leenkx/Shaders/ssrefr_pass/ssrefr_pass.frag.glsl 2025-05-21 00:55:14 +00:00
f8d0e67f33 Update leenkx/Shaders/ssr_pass/ssr_pass.frag.glsl 2025-05-21 00:51:33 +00:00
915118617d Update leenkx/Shaders/ssgi_pass/ssgi_pass.json 2025-05-21 00:50:45 +00:00
ea69511e67 Update leenkx/Shaders/ssgi_pass/ssgi_pass.frag.glsl 2025-05-21 00:47:22 +00:00
3926a7f83e Update leenkx/Shaders/deferred_light/deferred_light.json 2025-05-21 00:42:00 +00:00
0eafd14ae2 Update leenkx/Shaders/deferred_light/deferred_light.frag.glsl 2025-05-21 00:40:56 +00:00
08614512d7 Update leenkx/Shaders/blur_edge_pass/blur_edge_pass.frag.glsl 2025-05-21 00:19:31 +00:00
08261a9335 Update leenkx/Shaders/blur_edge_pass/blur_edge_pass.frag.glsl 2025-05-21 00:16:55 +00:00
ce3c1cea6a Delete leenkx/Shaders/voxel_resolve_shadows/voxel_resolve_shadows.comp.glsl 2025-05-21 00:00:40 +00:00
392d12a816 Delete leenkx/Shaders/voxel_resolve_refraction/voxel_resolve_refraction.comp.glsl 2025-05-21 00:00:32 +00:00
6b423038d4 Delete leenkx/Shaders/voxel_light/voxel_light.comp.glsl 2025-05-21 00:00:02 +00:00
76628fc010 Add leenkx/Shaders/std/aabb.glsl 2025-05-20 23:57:25 +00:00
99f687b10c Update leenkx/Shaders/std/constants.glsl 2025-05-20 23:55:32 +00:00
316441b954 merge upstream 2025-05-20 20:00:31 +00:00
9bf83bc49f Update leenkx/blender/lnx/material/make_mesh.py 2025-04-18 22:52:53 +00:00
d88e1f0f42 Update leenkx/blender/lnx/material/make_cluster.py 2025-04-18 22:52:03 +00:00
96f4e29778 Update leenkx/blender/lnx/material/make_mesh.py 2025-04-12 08:49:04 +00:00
1d705d2ca2 Update leenkx/blender/lnx/material/make_cluster.py 2025-04-12 08:48:33 +00:00
0979cd976f Update leenkx/blender/lnx/write_data.py 2025-04-11 22:20:24 +00:00
db6d786ee4 merge upstream 2025-04-11 22:06:01 +00:00
106e36e30d merge upstream 2025-04-10 17:16:12 +00:00
2bb296028f merge upstream 2025-04-09 17:30:27 +00:00
25d7ba3e72 merge upstream 2025-04-08 06:33:53 +00:00
bf7b4416ec Update leenkx/blender/lnx/make_renderpath.py 2025-04-07 17:29:09 +00:00
a2d03cfe6e Update leenkx/Shaders/std/light.glsl 2025-04-07 17:26:00 +00:00
95f0ecfc54 Update leenkx/Sources/leenkx/system/Starter.hx 2025-04-07 17:07:52 +00:00
07f59224fc Update leenkx/blender/lnx/write_data.py 2025-04-07 16:50:46 +00:00
02259985be Update leenkx/blender/lnx/props.py 2025-04-07 16:46:33 +00:00
6b8585c81a Update leenkx/blender/lnx/props_renderpath.py 2025-04-07 16:43:01 +00:00
5d78eabf94 Update leenkx/blender/lnx/material/cycles_nodes/nodes_texture.py 2025-04-07 16:39:49 +00:00
41c1459c4e Update leenkx/blender/lnx/material/make_voxel.py 2025-04-07 16:34:12 +00:00
304a497565 Update leenkx/blender/lnx/make_renderpath.py 2025-04-07 16:27:25 +00:00
9fa399371a Update leenkx/blender/lnx/material/make_mesh.py 2025-04-07 16:25:06 +00:00
4625fdb6b2 Update leenkx/blender/lnx/material/make_cluster.py 2025-04-07 16:13:16 +00:00
79553927aa Update leenkx/Shaders/water_pass/water_pass.frag.glsl 2025-04-07 16:06:53 +00:00
86661c1012 Update leenkx/Shaders/voxel_light/voxel_light.comp.glsl 2025-04-07 16:04:17 +00:00
03967c7a2b Update leenkx/Sources/leenkx/system/Starter.hx 2025-04-07 15:50:41 +00:00
61fd48a12f Update leenkx/Shaders/ssr_pass/ssr_pass.frag.glsl 2025-04-07 15:47:22 +00:00
519039b8b6 Update leenkx/Shaders/std/light.glsl 2025-04-07 15:45:04 +00:00
5244b1b3e8 Update leenkx/Sources/leenkx/renderpath/RenderPathForward.hx 2025-04-07 15:41:41 +00:00
7ae3bbe496 Update leenkx/Sources/leenkx/renderpath/RenderPathDeferred.hx 2025-04-07 15:39:38 +00:00
001be2f8da Update leenkx/Sources/leenkx/renderpath/Inc.hx 2025-04-07 15:35:22 +00:00
6a25b3c5d7 Update leenkx/Shaders/deferred_light/deferred_light.frag.glsl 2025-04-07 15:28:46 +00:00
8d4ac7251a Update leenkx/Shaders/voxel_temporal/voxel_temporal.comp.glsl 2025-04-07 15:22:49 +00:00
ae63b252c6 Update leenkx/Shaders/voxel_sdf_jumpflood/voxel_sdf_jumpflood.comp.glsl 2025-04-07 15:19:03 +00:00
ee73823206 Update leenkx/Shaders/voxel_resolve_specular/voxel_resolve_specular.comp.glsl 2025-04-07 15:17:43 +00:00
af2850e20c Update leenkx/Shaders/voxel_resolve_diffuse/voxel_resolve_diffuse.comp.glsl 2025-04-07 14:49:37 +00:00
bc4a31d415 Update leenkx/Shaders/voxel_resolve_ao/voxel_resolve_ao.comp.glsl 2025-04-07 14:47:58 +00:00
5303ad3ac6 Update leenkx/Shaders/ssrefr_pass/ssrefr_pass.frag.glsl 2025-04-07 14:44:14 +00:00
5153cff790 Update leenkx/Shaders/std/shadows.glsl 2025-04-07 14:41:53 +00:00
abe17870ce Update leenkx/Shaders/std/conetrace.glsl 2025-04-07 14:38:30 +00:00
137 changed files with 12947 additions and 3603 deletions

4
.gitignore vendored
View File

@ -1,3 +1,5 @@
__pycache__/
*.pyc
*.DS_Store
*.DS_Store
**/workspace.xml
**/vcs.xml

View File

@ -227,7 +227,7 @@ class SystemImpl {
}
static inline var maxGamepads: Int = 4;
static var frame: Framebuffer;
public static var frame: Framebuffer;
static var keyboard: Keyboard = null;
static var mouse: kha.input.Mouse;
static var surface: Surface;
@ -388,7 +388,8 @@ class SystemImpl {
{
alpha: false,
antialias: options.framebuffer.samplesPerPixel > 1,
stencil: true
stencil: true,
xrCompatible: true
}); // preserveDrawingBuffer: true } ); Warning: preserveDrawingBuffer can cause huge performance issues on mobile browsers
SystemImpl.gl.pixelStorei(GL.UNPACK_PREMULTIPLY_ALPHA_WEBGL, 1);
@ -417,7 +418,8 @@ class SystemImpl {
{
alpha: false,
antialias: options.framebuffer.samplesPerPixel > 1,
stencil: true
stencil: true,
xrCompatible: true
}); // preserveDrawingBuffer: true } ); WARNING: preserveDrawingBuffer causes huge performance issues (on mobile browser)!
SystemImpl.gl.pixelStorei(GL.UNPACK_PREMULTIPLY_ALPHA_WEBGL, 1);
SystemImpl.gl.getExtension("OES_texture_float");
@ -547,6 +549,12 @@ class SystemImpl {
];
function animate(timestamp) {
if (untyped Browser.window._khaSkipWindowRender == true) {
if (requestAnimationFrame != null)
requestAnimationFrame(animate);
return;
}
if (requestAnimationFrame == null)
Browser.window.setTimeout(animate, 1000.0 / 60.0);
else

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -157,4 +157,5 @@ extern class Krom {
static function getConstantLocationCompute(shader: Dynamic, name: String): Dynamic;
static function getTextureUnitCompute(shader: Dynamic, name: String): Dynamic;
static function compute(x: Int, y: Int, z: Int): Void;
static function viewportSetCamera(posX: Float, posY: Float, posZ: Float, rotX: Float, rotY: Float, rotZ: Float, rotW: Float): Void;
}

0
Krom/Krom Executable file → Normal file
View File

View File

@ -14,7 +14,7 @@ out vec4 fragColor;
vec2 barrelDistortion(vec2 coord, float amt) {
vec2 cc = coord - 0.5;
float dist = dot(cc, cc);
return coord + cc * dist * amt;
return coord - cc * dist * amt;
}
float sat(float value)
{
@ -56,8 +56,6 @@ void main() {
if (CAType == 1) {
float reci_num_iter_f = 1.0 / float(num_iter);
vec2 resolution = vec2(1,1);
vec2 uv = (texCoord.xy/resolution.xy);
vec4 sumcol = vec4(0.0);
vec4 sumw = vec4(0.0);
for (int i=0; i < num_iter; ++i)
@ -65,19 +63,21 @@ void main() {
float t = float(i) * reci_num_iter_f;
vec4 w = spectrum_offset(t);
sumw += w;
sumcol += w * texture(tex, barrelDistortion(uv, 0.6 * max_distort * t));
vec2 distortedUV = barrelDistortion(texCoord, 0.6 * max_distort * t);
sumcol += w * texture(tex, distortedUV);
}
if (on == 1) fragColor = sumcol / sumw; else fragColor = texture(tex, texCoord);
}
// Simple
// inward sampling to avoid edge artifacts
else {
vec3 col = vec3(0.0);
col.x = texture(tex, texCoord + ((vec2(0.0, 1.0) * max_distort) / vec2(1000.0))).x;
col.y = texture(tex, texCoord + ((vec2(-0.85, -0.5) * max_distort) / vec2(1000.0))).y;
col.z = texture(tex, texCoord + ((vec2(0.85, -0.5) * max_distort) / vec2(1000.0))).z;
if (on == 1) fragColor = vec4(col.x, col.y, col.z, fragColor.w);
vec2 toCenter = (vec2(0.5) - texCoord) * max_distort / 500.0;
col.x = texture(tex, texCoord + toCenter * 0.0).x;
col.y = texture(tex, texCoord + toCenter * 0.5).y;
col.z = texture(tex, texCoord + toCenter * 1.0).z;
if (on == 1) fragColor = vec4(col.x, col.y, col.z, 1.0);
else fragColor = texture(tex, texCoord);
}
}

View File

@ -357,6 +357,12 @@ void main() {
#else
fragColor = textureLod(tex, texCo, 0.0);
#endif
// TODO: re-investigate white artifacts
fragColor.rgb = clamp(fragColor.rgb, vec3(0.0), vec3(65504.0));
if (any(isnan(fragColor.rgb)) || any(isinf(fragColor.rgb))) {
fragColor.rgb = vec3(0.0);
}
#endif

View File

@ -29,10 +29,11 @@ uniform sampler2D gbuffer1;
#ifdef _VoxelGI
uniform sampler2D voxels_diffuse;
uniform sampler2D voxels_specular;
#endif
#else
#ifdef _VoxelAOvar
uniform sampler2D voxels_ao;
#endif
#endif
#ifdef _VoxelShadow
uniform sampler3D voxels;
uniform sampler3D voxelsSDF;
@ -58,6 +59,10 @@ uniform vec3 backgroundCol;
uniform sampler2D ssaotex;
#endif
#ifdef _SSGI
uniform sampler2D ssgitex;
#endif
#ifdef _SSS
uniform vec2 lightPlane;
#endif
@ -97,8 +102,23 @@ uniform mat4 invVP;
#endif
uniform vec2 cameraProj;
#ifdef _VRStereo
uniform vec3 eye; // center camera position
uniform vec3 eyeLook; // center camera look
uniform vec3 eyeLeft;
uniform vec3 eyeRight;
uniform vec3 eyeLookLeft;
uniform vec3 eyeLookRight;
uniform mat4 invVPLeft;
uniform mat4 invVPRight;
#ifdef _SinglePoint
uniform vec3 pointPosLeft;
uniform vec3 pointPosRight;
#endif
#else
uniform vec3 eye;
uniform vec3 eyeLook;
#endif
#ifdef _Clusters
uniform vec4 lightsArray[maxLights * 3];
@ -113,11 +133,15 @@ uniform vec2 cameraPlane;
#ifdef _SinglePoint
#ifdef _Spot
//!uniform sampler2DShadow shadowMapSpot[1];
#ifdef _ShadowMapTransparent
//!uniform sampler2D shadowMapSpotTransparent[1];
#endif
//!uniform mat4 LWVPSpot[1];
#else
//!uniform samplerCubeShadow shadowMapPoint[1];
#ifdef _ShadowMapTransparent
//!uniform samplerCube shadowMapPointTransparent[1];
#endif
//!uniform vec2 lightProj;
#endif
#endif
@ -125,30 +149,40 @@ uniform vec2 cameraPlane;
#ifdef _ShadowMapAtlas
#ifdef _SingleAtlas
uniform sampler2DShadow shadowMapAtlas;
#ifdef _ShadowMapTransparent
uniform sampler2D shadowMapAtlasTransparent;
#endif
#endif
#endif
#ifdef _ShadowMapAtlas
#ifndef _SingleAtlas
//!uniform sampler2DShadow shadowMapAtlasPoint;
#ifdef _ShadowMapTransparent
//!uniform sampler2D shadowMapAtlasPointTransparent;
#endif
//!uniform vec4 pointLightDataArray[4];
#endif
//!uniform vec4 pointLightDataArray[maxLightsCluster * 6];
#else
//!uniform samplerCubeShadow shadowMapPoint[4];
#ifdef _ShadowMapTransparent
//!uniform samplerCube shadowMapPointTransparent[4];
#endif
#endif
//!uniform vec2 lightProj;
#ifdef _Spot
#ifdef _ShadowMapAtlas
#ifndef _SingleAtlas
//!uniform sampler2DShadow shadowMapAtlasSpot;
#ifdef _ShadowMapTransparent
//!uniform sampler2D shadowMapAtlasSpotTransparent;
#endif
#endif
#else
//!uniform sampler2DShadow shadowMapSpot[4];
#ifdef _ShadowMapTransparent
//!uniform sampler2D shadowMapSpotTransparent[4];
#endif
#endif
//!uniform mat4 LWVPSpotArray[maxLightsCluster];
#endif
#endif
@ -161,12 +195,16 @@ uniform vec3 sunCol;
#ifdef _ShadowMapAtlas
#ifndef _SingleAtlas
uniform sampler2DShadow shadowMapAtlasSun;
#ifdef _ShadowMapTransparent
uniform sampler2D shadowMapAtlasSunTransparent;
#endif
#endif
#else
uniform sampler2DShadow shadowMap;
#ifdef _ShadowMapTransparent
uniform sampler2D shadowMapTransparent;
#endif
#endif
uniform float shadowsBias;
#ifdef _CSM
//!uniform vec4 casData[shadowmapCascades * 4 + 4];
@ -177,7 +215,9 @@ uniform vec3 sunCol;
#endif
#ifdef _SinglePoint // Fast path for single light
#ifndef _VRStereo
uniform vec3 pointPos;
#endif
uniform vec3 pointCol;
#ifdef _ShadowMap
uniform float pointBias;
@ -202,6 +242,8 @@ out vec4 fragColor;
void main() {
vec4 g0 = textureLod(gbuffer0, texCoord, 0.0); // Normal.xy, roughness, metallic/matid
vec4 g1 = textureLod(gbuffer1, texCoord, 0.0); // Basecolor.rgb, spec/occ
float depth = textureLod(gbufferD, texCoord, 0.0).r * 2.0 - 1.0;
vec3 n;
n.z = 1.0 - abs(g0.x) - abs(g0.y);
@ -213,31 +255,50 @@ void main() {
uint matid;
unpackFloatInt16(g0.a, metallic, matid);
vec4 g1 = textureLod(gbuffer1, texCoord, 0.0); // Basecolor.rgb, spec/occ
vec2 occspec = unpackFloat2(g1.a);
vec3 albedo = surfaceAlbedo(g1.rgb, metallic); // g1.rgb - basecolor
vec3 f0 = surfaceF0(g1.rgb, metallic);
float depth = textureLod(gbufferD, texCoord, 0.0).r * 2.0 - 1.0;
// re-investigate clamp basecolor to prevent extreme values causing glitches
vec3 basecolor = min(g1.rgb, vec3(2.0));
vec3 albedo = surfaceAlbedo(basecolor, metallic);
vec3 f0 = surfaceF0(basecolor, metallic);
#ifdef _VRStereo
bool isLeftEye = texCoord.x < 0.5;
vec3 eyePos = isLeftEye ? eyeLeft : eyeRight;
mat4 invVP_eye = isLeftEye ? invVPLeft : invVPRight;
vec2 eyeTexCoord = vec2(
isLeftEye ? texCoord.x * 2.0 : (texCoord.x - 0.5) * 2.0,
texCoord.y
);
vec3 p = getPos2(invVP_eye, depth, eyeTexCoord);
vec3 v = normalize(eyePos - p);
#else
vec3 p = getPos(eye, eyeLook, normalize(viewRay), depth, cameraProj);
vec3 v = normalize(eye - p);
#endif
float dotNV = max(dot(n, v), 0.0);
#ifdef _gbuffer2
vec4 g2 = textureLod(gbuffer2, texCoord, 0.0);
#endif
#ifdef _MicroShadowing
occspec.x = mix(1.0, occspec.x, dotNV); // AO Fresnel
#endif
#ifdef _Brdf
vec2 envBRDF = texelFetch(senvmapBrdf, ivec2(vec2(dotNV, 1.0 - roughness) * 256.0), 0).xy;
vec3 F = f0 * envBRDF.x + envBRDF.y;
#else
vec3 F = f0;
#endif
#ifndef _VoxelAOvar
#ifndef _VoxelGI
// Envmap
#ifdef _Irr
vec3 envl = shIrradiance(n, shirr);
#ifdef _gbuffer2
@ -259,6 +320,7 @@ void main() {
vec3 reflectionWorld = reflect(-v, n);
float lod = getMipFromRoughness(roughness, envmapNumMipmaps);
vec3 prefilteredColor = textureLod(senvmapRadiance, envMapEquirect(reflectionWorld), lod).rgb;
prefilteredColor = min(prefilteredColor, vec3(20.0));
#endif
#ifdef _EnvLDR
@ -271,33 +333,33 @@ void main() {
envl.rgb *= albedo;
#ifdef _Brdf
envl.rgb *= 1.0 - (f0 * envBRDF.x + envBRDF.y); //LV: We should take refracted light into account
envl.rgb *= 1.0 - F; //LV: We should take refracted light into account
#endif
#ifdef _Rad // Indirect specular
envl.rgb += prefilteredColor * (f0 * envBRDF.x + envBRDF.y); //LV: Removed "1.5 * occspec.y". Specular should be weighted only by FV LUT
envl.rgb += prefilteredColor * F; //LV: Removed "1.5 * occspec.y". Specular should be weighted only by FV LUT
#else
#ifdef _EnvCol
envl.rgb += backgroundCol * (f0 * envBRDF.x + envBRDF.y); //LV: Eh, what's the point of weighting it only by F0?
envl.rgb += backgroundCol * F; //LV: Eh, what's the point of weighting it only by F0?
#endif
#endif
envl.rgb *= envmapStrength * occspec.x;
#ifdef _VoxelGI
vec4 indirect_diffuse = textureLod(voxels_diffuse, texCoord, 0.0);
fragColor.rgb = (indirect_diffuse.rgb * albedo + envl.rgb * (1.0 - indirect_diffuse.a)) * voxelgiDiff;
if(roughness < 1.0 && occspec.y > 0.0)
fragColor.rgb += textureLod(voxels_specular, texCoord, 0.0).rgb * occspec.y * voxelgiRefl;
#endif
#ifdef _VoxelAOvar
envl.rgb *= textureLod(voxels_ao, texCoord, 0.0).r;
#endif
#ifndef _VoxelGI
fragColor.rgb = envl;
#endif
#endif
#ifdef _VoxelGI
fragColor.rgb = textureLod(voxels_diffuse, texCoord, 0.0).rgb * voxelgiDiff;
if(roughness < 1.0 && occspec.y > 0.0)
fragColor.rgb += textureLod(voxels_specular, texCoord, 0.0).rgb * occspec.y * voxelgiRefl;
#else
#ifdef _VoxelAOvar
fragColor.rgb = textureLod(voxels_ao, texCoord, 0.0).rgb * voxelgiOcc;
#endif
#endif
// Show voxels
// vec3 origin = vec3(texCoord * 2.0 - 1.0, 0.99);
// vec3 direction = vec3(0.0, 0.0, -1.0);
@ -312,11 +374,12 @@ void main() {
// fragColor.rgb = texture(ssaotex, texCoord).rrr;
#ifdef _SSAO
// #ifdef _RTGI
// fragColor.rgb *= textureLod(ssaotex, texCoord, 0.0).rgb;
// #else
fragColor.rgb *= textureLod(ssaotex, texCoord, 0.0).r;
// #endif
#endif
#ifdef _SSGI
vec3 ssgiColor = textureLod(ssgitex, texCoord, 0.0).rgb;
fragColor.rgb += ssgiColor * albedo;
#endif
#ifdef _EmissionShadeless
@ -350,39 +413,69 @@ void main() {
#ifdef _CSM
svisibility = shadowTestCascade(
#ifdef _ShadowMapAtlas
#ifdef _ShadowMapTransparent
#ifndef _SingleAtlas
shadowMapAtlasSun, shadowMapAtlasSunTransparent
#else
shadowMapAtlas, shadowMapAtlasTransparent
#endif
#else
#ifndef _SingleAtlas
shadowMapAtlasSun
#else
shadowMapAtlas
#endif
#endif
#else
shadowMap, shadowMapTransparent
#ifdef _ShadowMapTransparent
shadowMap, shadowMapTransparent
#else
shadowMap
#endif
#endif
, eye, p + n * shadowsBias * 10, shadowsBias, false
);
, eye, p + n * shadowsBias * 2, shadowsBias
#ifdef _ShadowMapTransparent
, false
#endif
);
#else
vec4 lPos = LWVP * vec4(p + n * shadowsBias * 100, 1.0);
vec4 lPos = LWVP * vec4(p + n * shadowsBias * 2, 1.0);
if (lPos.w > 0.0) {
svisibility = shadowTest(
#ifdef _ShadowMapAtlas
#ifdef _ShadowMapTransparent
#ifndef _SingleAtlas
shadowMapAtlasSun, shadowMapAtlasSunTransparent
#else
shadowMapAtlas, shadowMapAtlasTransparent
#endif
#else
#ifndef _SingleAtlas
shadowMapAtlasSun
#else
shadowMapAtlas
#endif
#endif
#else
#ifdef _ShadowMapTransparent
shadowMap, shadowMapTransparent
#else
shadowMap
#endif
, lPos.xyz / lPos.w, shadowsBias, false
);
#endif
, lPos.xyz / lPos.w, shadowsBias
#ifdef _ShadowMapTransparent
, false
#endif
);
}
#endif
#endif
#ifdef _VoxelShadow
svisibility *= (1.0 - traceShadow(p, n, voxels, voxelsSDF, sunDir, clipmaps, gl_FragCoord.xy).r) * voxelgiShad;
svisibility *= (1.0 - traceShadow(p, n, voxels, voxelsSDF, sunDir, clipmaps, gl_FragCoord.xy, -g2.rg).r) * voxelgiShad;
#endif
#ifdef _SSRS
// vec2 coords = getProjectedCoord(hitCoord);
// vec2 deltaCoords = abs(vec2(0.5, 0.5) - coords.xy);
@ -436,16 +529,25 @@ void main() {
#ifdef _SinglePoint
#ifdef _VRStereo
vec3 lightPos = pointPosLeft;
#else
vec3 lightPos = pointPos;
#endif
fragColor.rgb += sampleLight(
p, n, v, dotNV, pointPos, pointCol, albedo, roughness, occspec.y, f0
p, n, v, dotNV, lightPos, pointCol, albedo, roughness, occspec.y, f0
#ifdef _ShadowMap
, 0, pointBias, true, false
, 0, pointBias, true
#ifdef _ShadowMapTransparent
, false
#endif
#endif
#ifdef _Spot
, true, spotData.x, spotData.y, spotDir, spotData.zw, spotRight
#endif
#ifdef _VoxelShadow
, voxels, voxelsSDF, clipmaps
, voxels, voxelsSDF, clipmaps, -g2.rg
#endif
#ifdef _MicroShadowing
, occspec.x
@ -457,7 +559,9 @@ void main() {
#ifdef _Spot
#ifdef _SSS
if (matid == 2) fragColor.rgb += fragColor.rgb * SSSSTransmittance(LWVPSpot[0], p, n, normalize(pointPos - p), lightPlane.y, shadowMapSpot[0]);//TODO implement transparent shadowmaps into the SSSSTransmittance()
#ifdef _ShadowMap
if (matid == 2) fragColor.rgb += fragColor.rgb * SSSSTransmittance(LWVPSpot[0], p, n, normalize(lightPos - p), lightPlane.y, shadowMapSpot[0]);//TODO implement transparent shadowmaps into the SSSSTransmittance()
#endif
#endif
#endif
@ -492,7 +596,10 @@ void main() {
f0
#ifdef _ShadowMap
// light index, shadow bias, cast_shadows
, li, lightsArray[li * 3 + 2].x, lightsArray[li * 3 + 2].z != 0.0, false
, li, lightsArray[li * 3 + 2].x, lightsArray[li * 3 + 2].z != 0.0
#ifdef _ShadowMapTransparent
, false
#endif
#endif
#ifdef _Spot
, lightsArray[li * 3 + 2].y != 0.0
@ -503,7 +610,7 @@ void main() {
, lightsArraySpot[li * 2 + 1].xyz // right
#endif
#ifdef _VoxelShadow
, voxels, voxelsSDF, clipmaps
, voxels, voxelsSDF, clipmaps, -g2.rg
#endif
#ifdef _MicroShadowing
, occspec.x
@ -514,14 +621,11 @@ void main() {
);
}
#endif // _Clusters
/*
#ifdef _VoxelRefract
if(opac < 1.0) {
vec3 refraction = traceRefraction(p, n, voxels, v, ior, roughness, eye) * voxelgiRefr;
fragColor.rgb = mix(refraction, fragColor.rgb, opac);
}
#endif
*/
fragColor.rgb = clamp(fragColor.rgb, vec3(0.0), vec3(65504.0));
if (any(isnan(fragColor.rgb)) || any(isinf(fragColor.rgb))) {
fragColor.rgb = vec3(0.0);
}
fragColor.a = 1.0; // Mark as opaque
}

View File

@ -20,6 +20,36 @@
"name": "eyeLook",
"link": "_cameraLook"
},
{
"name": "eyeLeft",
"link": "_eyeLeft",
"ifdef": ["_VRStereo"]
},
{
"name": "eyeRight",
"link": "_eyeRight",
"ifdef": ["_VRStereo"]
},
{
"name": "eyeLookLeft",
"link": "_eyeLookLeft",
"ifdef": ["_VRStereo"]
},
{
"name": "eyeLookRight",
"link": "_eyeLookRight",
"ifdef": ["_VRStereo"]
},
{
"name": "invVPLeft",
"link": "_inverseViewProjectionMatrixLeft",
"ifdef": ["_VRStereo"]
},
{
"name": "invVPRight",
"link": "_inverseViewProjectionMatrixRight",
"ifdef": ["_VRStereo"]
},
{
"name": "clipmaps",
"link": "_clipmaps",
@ -176,8 +206,19 @@
{
"name": "pointPos",
"link": "_pointPosition",
"ifndef": ["_VRStereo"],
"ifdef": ["_SinglePoint"]
},
{
"name": "pointPosLeft",
"link": "_pointPositionLeft",
"ifdef": ["_VRStereo", "_SinglePoint"]
},
{
"name": "pointPosRight",
"link": "_pointPositionRight",
"ifdef": ["_VRStereo", "_SinglePoint"]
},
{
"name": "pointCol",
"link": "_pointColor",

View File

@ -97,6 +97,31 @@
"link": "_cascadeData",
"ifdef": ["_Sun", "_ShadowMap", "_CSM"]
},
{
"name": "eyeLookRight",
"link": "_eyeLookRight",
"ifdef": ["_VRStereo"]
},
{
"name": "invVPLeft",
"link": "_inverseViewProjectionMatrixLeft",
"ifdef": ["_VRStereo"]
},
{
"name": "invVPRight",
"link": "_inverseViewProjectionMatrixRight",
"ifdef": ["_VRStereo"]
},
{
"name": "invVP",
"link": "_viewProjectionMatrix",
"ifdef": ["_SSRS"]
},
{
"name": "smSizeUniform",
"link": "_shadowMapSize",
"ifdef": ["_SMSizeUniform"]
},
{
"name": "lightPlane",
"link": "_lightPlane",
@ -108,8 +133,6 @@
"ifdef": ["_SSRS"]
},
{
"name": "smSizeUniform",
"link": "_shadowMapSize",
"ifdef": ["_SMSizeUniform"]
},
{
@ -120,8 +143,19 @@
{
"name": "pointPos",
"link": "_pointPosition",
"ifndef": ["_VRStereo"],
"ifdef": ["_SinglePoint"]
},
{
"name": "pointPosLeft",
"link": "_pointPositionLeft",
"ifdef": ["_VRStereo", "_SinglePoint"]
},
{
"name": "pointPosRight",
"link": "_pointPositionRight",
"ifdef": ["_VRStereo", "_SinglePoint"]
},
{
"name": "pointCol",
"link": "_pointColor",

View File

@ -0,0 +1,9 @@
https://gpuopen.com/manuals/fidelityfx_sdk/license/
Copyright © 2024 Advanced Micro Devices, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files(the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and /or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions :
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@ -0,0 +1,157 @@
#version 450
// AMD FidelityFX Super Resolution 1.0.2 - EASU (Edge Adaptive Spatial Upsampling)
#include "compiled.inc"
uniform sampler2D tex;
uniform vec2 screenSize;
in vec2 texCoord;
out vec4 fragColor;
// Helper functions from AMD ffx_a.h
float APrxLoRcpF1(float a) {
return uintBitsToFloat(uint(0x7ef07ebb) - floatBitsToUint(a));
}
float AMax3F1(float x, float y, float z) {
return max(x, max(y, z));
}
float AMin3F1(float x, float y, float z) {
return min(x, min(y, z));
}
// Attempt to use textureGather for efficiency when available
#if __VERSION__ >= 400
void FsrEasuTap(
inout vec3 aC,
inout float aW,
vec2 off,
vec2 dir,
vec2 len,
float lob,
float clp,
vec3 c
) {
vec2 v = off * dir;
float d2 = v.x + v.y;
d2 = clamp(d2 * APrxLoRcpF1(max(abs(v.x), abs(v.y))), 0.0, 1.0);
d2 = d2 * d2;
d2 = d2 * len.x + len.y;
float wB = 2.0 / 5.0 * d2 - 1.0;
float wA = lob * d2 - 1.0;
wB *= wB;
wA *= wA;
float w = 25.0 / 16.0 * wA * wB;
w = min(w, clp);
w = max(w, 0.0);
aC += c * w;
aW += w;
}
vec3 FsrEasuF(vec2 ip) {
vec2 inputSize = textureSize(tex, 0);
vec2 inputRcp = 1.0 / inputSize;
// Position in input pixels
vec2 pp = ip * inputSize - 0.5;
vec2 fp = floor(pp);
pp -= fp;
// 12-tap kernel
// b c
// e f g h
// i j k l
// n o
ivec2 sp = ivec2(fp);
vec3 b = texelFetch(tex, sp + ivec2(0, -1), 0).rgb;
vec3 c = texelFetch(tex, sp + ivec2(1, -1), 0).rgb;
vec3 e = texelFetch(tex, sp + ivec2(-1, 0), 0).rgb;
vec3 f = texelFetch(tex, sp + ivec2(0, 0), 0).rgb;
vec3 g = texelFetch(tex, sp + ivec2(1, 0), 0).rgb;
vec3 h = texelFetch(tex, sp + ivec2(2, 0), 0).rgb;
vec3 i = texelFetch(tex, sp + ivec2(-1, 1), 0).rgb;
vec3 j = texelFetch(tex, sp + ivec2(0, 1), 0).rgb;
vec3 k = texelFetch(tex, sp + ivec2(1, 1), 0).rgb;
vec3 l = texelFetch(tex, sp + ivec2(2, 1), 0).rgb;
vec3 n = texelFetch(tex, sp + ivec2(0, 2), 0).rgb;
vec3 o = texelFetch(tex, sp + ivec2(1, 2), 0).rgb;
// Luma for edge detection (using green channel approximation)
float bL = b.g + 0.5 * (b.r + b.b);
float cL = c.g + 0.5 * (c.r + c.b);
float eL = e.g + 0.5 * (e.r + e.b);
float fL = f.g + 0.5 * (f.r + f.b);
float gL = g.g + 0.5 * (g.r + g.b);
float hL = h.g + 0.5 * (h.r + h.b);
float iL = i.g + 0.5 * (i.r + i.b);
float jL = j.g + 0.5 * (j.r + j.b);
float kL = k.g + 0.5 * (k.r + k.b);
float lL = l.g + 0.5 * (l.r + l.b);
float nL = n.g + 0.5 * (n.r + n.b);
float oL = o.g + 0.5 * (o.r + o.b);
// Gradient detection
float dirX = (cL - bL) + (gL - fL) + (kL - jL) + (oL - nL);
float dirY = (eL - iL) + (fL - jL) + (gL - kL) + (hL - lL);
// Normalize direction
float dirR = APrxLoRcpF1(max(abs(dirX), abs(dirY)));
dirX *= dirR;
dirY *= dirR;
// Calculate stretch based on edge direction
float len = length(vec2(dirX, dirY));
len = len * 0.5;
len *= len;
float stretch = (dirX * dirX + dirY * dirY) * APrxLoRcpF1(max(abs(dirX), abs(dirY)));
vec2 len2 = vec2(1.0 + (stretch - 1.0) * len, 1.0 - 0.5 * len);
float lob = 0.5 + (0.25 - 0.04 - 0.5) * len;
float clp = APrxLoRcpF1(lob);
// Accumulate samples
vec3 aC = vec3(0.0);
float aW = 0.0;
vec2 dir = vec2(dirX, dirY);
FsrEasuTap(aC, aW, vec2(0.0, -1.0) - pp, dir, len2, lob, clp, b);
FsrEasuTap(aC, aW, vec2(1.0, -1.0) - pp, dir, len2, lob, clp, c);
FsrEasuTap(aC, aW, vec2(-1.0, 0.0) - pp, dir, len2, lob, clp, e);
FsrEasuTap(aC, aW, vec2(0.0, 0.0) - pp, dir, len2, lob, clp, f);
FsrEasuTap(aC, aW, vec2(1.0, 0.0) - pp, dir, len2, lob, clp, g);
FsrEasuTap(aC, aW, vec2(2.0, 0.0) - pp, dir, len2, lob, clp, h);
FsrEasuTap(aC, aW, vec2(-1.0, 1.0) - pp, dir, len2, lob, clp, i);
FsrEasuTap(aC, aW, vec2(0.0, 1.0) - pp, dir, len2, lob, clp, j);
FsrEasuTap(aC, aW, vec2(1.0, 1.0) - pp, dir, len2, lob, clp, k);
FsrEasuTap(aC, aW, vec2(2.0, 1.0) - pp, dir, len2, lob, clp, l);
FsrEasuTap(aC, aW, vec2(0.0, 2.0) - pp, dir, len2, lob, clp, n);
FsrEasuTap(aC, aW, vec2(1.0, 2.0) - pp, dir, len2, lob, clp, o);
// Normalize
vec3 pix = aC / aW;
// Clamp to neighborhood min/max to prevent ringing
vec3 mn = min(min(min(f, g), j), k);
vec3 mx = max(max(max(f, g), j), k);
pix = clamp(pix, mn, mx);
return pix;
}
#else
// Fallback for older GLSL - simple bilinear
vec3 FsrEasuF(vec2 ip) {
return texture(tex, ip).rgb;
}
#endif
void main() {
vec3 col = FsrEasuF(texCoord);
fragColor = vec4(col, 1.0);
}

View File

@ -0,0 +1,19 @@
{
"contexts": [
{
"name": "fsr1_easu_pass",
"depth_write": false,
"compare_mode": "always",
"cull_mode": "none",
"links": [
{
"name": "screenSize",
"link": "_screenSize"
}
],
"texture_params": [],
"vertex_shader": "../include/pass.vert.glsl",
"fragment_shader": "fsr1_easu_pass.frag.glsl"
}
]
}

View File

@ -0,0 +1,9 @@
https://gpuopen.com/manuals/fidelityfx_sdk/license/
Copyright © 2024 Advanced Micro Devices, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files(the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and /or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions :
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@ -0,0 +1,116 @@
#version 450
// AMD FidelityFX Super Resolution 1.0.2 - RCAS (Robust Contrast Adaptive Sharpening)
#include "compiled.inc"
uniform sampler2D tex;
// Sharpness in "stops": 0.0 = maximum sharpness, higher = less sharp
// Converted to linear via exp2(-sharpness)
#ifdef _FSR1_Ultra_Quality
const float SHARPNESS_STOPS = 0.0;
#elif defined(_FSR1_Balanced)
const float SHARPNESS_STOPS = 1.0;
#elif defined(_FSR1_Performance)
const float SHARPNESS_STOPS = 2.0;
#elif defined(_FSR1_Custom)
uniform vec4 PPComp15;
#define SHARPNESS_STOPS (PPComp15.x * 2.0)
#else
const float SHARPNESS_STOPS = 0.5; // Quality (default)
#endif
// FSR RCAS limit - prevents unnatural sharpening artifacts
#define FSR_RCAS_LIMIT (0.25 - (1.0 / 16.0))
in vec2 texCoord;
out vec4 fragColor;
// AMD helper functions from ffx_a.h
float AMin3F1(float x, float y, float z) { return min(x, min(y, z)); }
float AMax3F1(float x, float y, float z) { return max(x, max(y, z)); }
// High precision reciprocal (required for limiters per AMD docs)
// Added epsilon to prevent division by zero in dark areas
float ARcpF1(float a) {
return 1.0 / max(a, 1e-8);
}
// Medium precision reciprocal approximation (from AMD ffx_a.h)
// Only used for noise detection and final resolve
float APrxMedRcpF1(float a) {
return uintBitsToFloat(uint(0x7ef19fff) - floatBitsToUint(a));
}
void main() {
// Get texture size and texel offset
vec2 texSize = vec2(textureSize(tex, 0));
vec2 texelSize = 1.0 / texSize;
// Algorithm uses minimal 3x3 pixel neighborhood
// b
// d e f
// h
// Clamp inputs to [0,1] - FSR expects sRGB normalized input
vec3 b = clamp(texture(tex, texCoord + vec2(0.0, -texelSize.y)).rgb, 0.0, 1.0);
vec3 d = clamp(texture(tex, texCoord + vec2(-texelSize.x, 0.0)).rgb, 0.0, 1.0);
vec4 ee = texture(tex, texCoord);
vec3 e = clamp(ee.rgb, 0.0, 1.0);
vec3 f = clamp(texture(tex, texCoord + vec2(texelSize.x, 0.0)).rgb, 0.0, 1.0);
vec3 h = clamp(texture(tex, texCoord + vec2(0.0, texelSize.y)).rgb, 0.0, 1.0);
// Luma times 2 (AMD's luma calculation: B*0.5 + R*0.5 + G)
float bL = b.b * 0.5 + (b.r * 0.5 + b.g);
float dL = d.b * 0.5 + (d.r * 0.5 + d.g);
float eL = e.b * 0.5 + (e.r * 0.5 + e.g);
float fL = f.b * 0.5 + (f.r * 0.5 + f.g);
float hL = h.b * 0.5 + (h.r * 0.5 + h.g);
// Noise detection (official AMD algorithm with safety for flat areas)
float nz = 0.25 * bL + 0.25 * dL + 0.25 * fL + 0.25 * hL - eL;
float range = AMax3F1(AMax3F1(bL, dL, eL), fL, hL) - AMin3F1(AMin3F1(bL, dL, eL), fL, hL);
// Use safe division instead of APrxMedRcpF1 for range to avoid NaN in flat areas
nz = clamp(abs(nz) / max(range, 1e-5), 0.0, 1.0);
nz = -0.5 * nz + 1.0;
// Min and max of ring (per channel)
float mn4R = min(AMin3F1(b.r, d.r, f.r), h.r);
float mn4G = min(AMin3F1(b.g, d.g, f.g), h.g);
float mn4B = min(AMin3F1(b.b, d.b, f.b), h.b);
float mx4R = max(AMax3F1(b.r, d.r, f.r), h.r);
float mx4G = max(AMax3F1(b.g, d.g, f.g), h.g);
float mx4B = max(AMax3F1(b.b, d.b, f.b), h.b);
// Immediate constants for peak range
vec2 peakC = vec2(1.0, -4.0);
// Limiters - these need HIGH PRECISION reciprocals (per AMD docs)
float hitMinR = min(mn4R, e.r) * ARcpF1(4.0 * mx4R);
float hitMinG = min(mn4G, e.g) * ARcpF1(4.0 * mx4G);
float hitMinB = min(mn4B, e.b) * ARcpF1(4.0 * mx4B);
float hitMaxR = (peakC.x - max(mx4R, e.r)) * ARcpF1(4.0 * mn4R + peakC.y);
float hitMaxG = (peakC.x - max(mx4G, e.g)) * ARcpF1(4.0 * mn4G + peakC.y);
float hitMaxB = (peakC.x - max(mx4B, e.b)) * ARcpF1(4.0 * mn4B + peakC.y);
float lobeR = max(-hitMinR, hitMaxR);
float lobeG = max(-hitMinG, hitMaxG);
float lobeB = max(-hitMinB, hitMaxB);
// Apply sharpness (convert from stops to linear)
float sharpness = exp2(-SHARPNESS_STOPS);
float lobe = max(-FSR_RCAS_LIMIT, min(AMax3F1(lobeR, lobeG, lobeB), 0.0)) * sharpness;
// Apply noise removal
lobe *= nz;
// Resolve using safe reciprocal to avoid any edge case issues
float denom = 4.0 * lobe + 1.0;
float rcpL = 1.0 / max(denom, 0.25); // denom should be in [0.25, 1.0] range
vec3 pix;
pix.r = (lobe * b.r + lobe * d.r + lobe * h.r + lobe * f.r + e.r) * rcpL;
pix.g = (lobe * b.g + lobe * d.g + lobe * h.g + lobe * f.g + e.g) * rcpL;
pix.b = (lobe * b.b + lobe * d.b + lobe * h.b + lobe * f.b + e.b) * rcpL;
// Ensure output is clamped to valid range
fragColor = vec4(clamp(pix, 0.0, 1.0), ee.a);
}

View File

@ -0,0 +1,24 @@
{
"contexts": [
{
"name": "fsr1_rcas_pass",
"depth_write": false,
"compare_mode": "always",
"cull_mode": "none",
"links": [
{
"name": "screenSize",
"link": "_screenSize"
},
{
"name": "PPComp15",
"link": "_PPComp15",
"ifdef": ["_FSR1_Custom"]
}
],
"texture_params": [],
"vertex_shader": "../include/pass.vert.glsl",
"fragment_shader": "fsr1_rcas_pass.frag.glsl"
}
]
}

View File

@ -8,6 +8,7 @@ uniform sampler2D gbufferD;
uniform sampler2D gbuffer0;
uniform sampler2D gbuffer1;
uniform mat4 invVP;
uniform mat4 invW;
uniform vec3 probep;
uniform vec3 eye;
@ -25,19 +26,27 @@ void main() {
float roughness = g0.b;
if (roughness > 0.95) {
fragColor.rgb = vec3(0.0);
fragColor = vec4(0.0);
return;
}
float spec = fract(textureLod(gbuffer1, texCoord, 0.0).a);
if (spec == 0.0) {
fragColor.rgb = vec3(0.0);
fragColor = vec4(0.0);
return;
}
float depth = textureLod(gbufferD, texCoord, 0.0).r * 2.0 - 1.0;
vec3 wp = getPos2(invVP, depth, texCoord);
vec3 localPos = (invW * vec4(wp, 1.0)).xyz;
// return if surface is inside probe volume bounds
if (abs(localPos.x) > 1.0 || abs(localPos.y) > 1.0 || abs(localPos.z) > 1.0) {
fragColor = vec4(0.0);
return;
}
vec2 enc = g0.rg;
vec3 n;
n.z = 1.0 - abs(enc.x) - abs(enc.y);
@ -50,5 +59,5 @@ void main() {
r.y = -r.y;
#endif
float intensity = clamp((1.0 - roughness) * dot(wp - probep, n), 0.0, 1.0);
fragColor.rgb = texture(probeTex, r).rgb * intensity;
fragColor = vec4(texture(probeTex, r).rgb * intensity, 1.0);
}

View File

@ -20,6 +20,10 @@
"name": "invVP",
"link": "_inverseViewProjectionMatrix"
},
{
"name": "invW",
"link": "_inverseWorldMatrix"
},
{
"name": "probep",
"link": "_probePosition"

View File

@ -25,13 +25,13 @@ void main() {
float roughness = g0.b;
if (roughness > 0.95) {
fragColor.rgb = vec3(0.0);
fragColor = vec4(0.0);
return;
}
float spec = fract(textureLod(gbuffer1, texCoord, 0.0).a);
if (spec == 0.0) {
fragColor.rgb = vec3(0.0);
fragColor = vec4(0.0);
return;
}
@ -50,5 +50,5 @@ void main() {
n = normalize(n);
float intensity = clamp((1.0 - roughness) * dot(n, proben), 0.0, 1.0);
fragColor.rgb = texture(probeTex, tc).rgb * intensity;
fragColor = vec4(texture(probeTex, tc).rgb * intensity, 1.0);
}

View File

@ -5,42 +5,56 @@
uniform sampler2D tex;
uniform sampler2D gbuffer0;
uniform sampler2D gbufferD;
uniform vec2 dirInv; // texStep
in vec2 texCoord;
out float fragColor;
out vec3 fragColor;
const float blurWeights[5] = float[] (0.227027, 0.1945946, 0.1216216, 0.054054, 0.016216);
// const float blurWeights[10] = float[] (0.132572, 0.125472, 0.106373, 0.08078, 0.05495, 0.033482, 0.018275, 0.008934, 0.003912, 0.001535);
const float discardThreshold = 0.95;
float doBlur(const float blurWeight, const int pos, const vec3 nor, const vec2 texCoord) {
const float posadd = pos + 0.5;
vec3 nor2 = getNor(textureLod(gbuffer0, texCoord + pos * dirInv, 0.0).rg);
float influenceFactor = step(discardThreshold, dot(nor2, nor));
float col = textureLod(tex, texCoord + posadd * dirInv, 0.0).r;
fragColor += col * blurWeight * influenceFactor;
float weight = blurWeight * influenceFactor;
nor2 = getNor(textureLod(gbuffer0, texCoord - pos * dirInv, 0.0).rg);
influenceFactor = step(discardThreshold, dot(nor2, nor));
col = textureLod(tex, texCoord - posadd * dirInv, 0.0).r;
fragColor += col * blurWeight * influenceFactor;
weight += blurWeight * influenceFactor;
return weight;
}
const int KERNEL_SIZE = 13;
const float blurWeights[13] = float[](0.1, 0.09, 0.08, 0.07, 0.06, 0.05, 0.04, 0.03, 0.025, 0.02, 0.015, 0.01, 0.005);
void main() {
vec3 nor = getNor(textureLod(gbuffer0, texCoord, 0.0).rg);
vec3 centerNor = getNor(textureLod(gbuffer0, texCoord, 0.0).rg);
float centerDepth = textureLod(gbufferD, texCoord, 0.0).r;
fragColor = textureLod(tex, texCoord, 0.0).r * blurWeights[0];
float weight = blurWeights[0];
for (int i = 1; i < 5; i++) {
weight += doBlur(blurWeights[i], i, nor, texCoord);
// skip sky pixels
if (centerDepth == 1.0) {
fragColor = vec3(0.0);
return;
}
fragColor = textureLod(tex, texCoord, 0.0).rgb * blurWeights[0];
float totalWeight = blurWeights[0];
for (int i = 1; i < KERNEL_SIZE; i++) {
vec2 offset = float(i) * dirInv;
vec2 uvPos = texCoord + offset;
vec3 norPos = getNor(textureLod(gbuffer0, uvPos, 0.0).rg);
float depthPos = textureLod(gbufferD, uvPos, 0.0).r;
float normalWeight = max(0.0, dot(norPos, centerNor));
normalWeight = pow(normalWeight, 8.0); // Softer normal falloff for better blending
float depthWeight = 1.0 - smoothstep(0.0, 0.02, abs(depthPos - centerDepth));
float w = blurWeights[i] * normalWeight * depthWeight;
fragColor += textureLod(tex, uvPos, 0.0).rgb * w;
totalWeight += w;
vec2 uvNeg = texCoord - offset;
vec3 norNeg = getNor(textureLod(gbuffer0, uvNeg, 0.0).rg);
float depthNeg = textureLod(gbufferD, uvNeg, 0.0).r;
normalWeight = max(0.0, dot(norNeg, centerNor));
normalWeight = pow(normalWeight, 8.0);
depthWeight = 1.0 - smoothstep(0.0, 0.02, abs(depthNeg - centerDepth));
w = blurWeights[i] * normalWeight * depthWeight;
fragColor += textureLod(tex, uvNeg, 0.0).rgb * w;
totalWeight += w;
}
fragColor = fragColor / weight;
fragColor /= totalWeight;
}

View File

@ -4,37 +4,34 @@
#include "std/math.glsl"
#include "std/gbuffer.glsl"
uniform sampler2D gbuffer0;
uniform sampler2D gbuffer1;
uniform sampler2D gbufferD;
uniform sampler2D gbuffer0; // Normal
// #ifdef _RTGI
// uniform sampler2D gbuffer1; // Basecol
// #endif
uniform mat4 P;
uniform mat3 V3;
uniform vec2 cameraProj;
const float angleMix = 0.5f;
#ifdef _SSGICone9
const float strength = 2.0 * (1.0 / ssgiStrength);
#else
const float strength = 2.0 * (1.0 / ssgiStrength) * 1.8;
#ifdef _EmissionShaded
uniform sampler2D gbufferEmission;
#endif
uniform mat4 P;
uniform mat4 invP;
uniform mat3 V3;
#ifdef _Sun
uniform vec3 sunDir;
uniform vec3 sunCol;
#endif
#ifdef _CPostprocess
uniform vec3 PPComp12;
#endif
in vec3 viewRay;
in vec2 texCoord;
out float fragColor;
out vec4 fragColor;
vec3 hitCoord;
vec2 coord;
float depth;
// #ifdef _RTGI
// vec3 col = vec3(0.0);
// #endif
vec3 vpos;
const float GOLDEN_ANGLE = 2.39996323;
const int RAY_STEPS = 12;
vec2 getProjectedCoord(vec3 hitCoord) {
vec4 projectedCoord = P * vec4(hitCoord, 1.0);
vec2 getProjectedCoord(const vec3 viewPos) {
vec4 projectedCoord = P * vec4(viewPos, 1.0);
projectedCoord.xy /= projectedCoord.w;
projectedCoord.xy = projectedCoord.xy * 0.5 + 0.5;
#ifdef _InvY
@ -43,65 +40,149 @@ vec2 getProjectedCoord(vec3 hitCoord) {
return projectedCoord.xy;
}
float getDeltaDepth(vec3 hitCoord) {
coord = getProjectedCoord(hitCoord);
depth = textureLod(gbufferD, coord, 0.0).r * 2.0 - 1.0;
vec3 p = getPosView(viewRay, depth, cameraProj);
return p.z - hitCoord.z;
}
void rayCast(vec3 dir) {
hitCoord = vpos;
dir *= ssgiRayStep * 2;
float dist = 0.15;
for (int i = 0; i < ssgiMaxSteps; i++) {
hitCoord += dir;
float delta = getDeltaDepth(hitCoord);
if (delta > 0.0 && delta < 0.2) {
dist = distance(vpos, hitCoord);
break;
}
vec3 cosineSampleHemisphere(vec3 n, vec2 rand) {
float phi = PI * 2.0 * rand.x;
float cosTheta = sqrt(1.0 - rand.y);
float sinTheta = sqrt(rand.y);
vec3 h = vec3(cos(phi) * sinTheta, sin(phi) * sinTheta, cosTheta);
vec3 tangent, bitangent;
vec3 absN = abs(n);
if (absN.x <= absN.y && absN.x <= absN.z) {
tangent = normalize(cross(n, vec3(1.0, 0.0, 0.0)));
} else if (absN.y <= absN.z) {
tangent = normalize(cross(n, vec3(0.0, 1.0, 0.0)));
} else {
tangent = normalize(cross(n, vec3(0.0, 0.0, 1.0)));
}
fragColor += dist;
// #ifdef _RTGI
// col += textureLod(gbuffer1, coord, 0.0).rgb * ((ssgiRayStep * ssgiMaxSteps) - dist);
// #endif
bitangent = cross(n, tangent);
return normalize(tangent * h.x + bitangent * h.y + n * h.z);
}
vec3 tangent(const vec3 n) {
vec3 t1 = cross(n, vec3(0, 0, 1));
vec3 t2 = cross(n, vec3(0, 1, 0));
if (length(t1) > length(t2)) return normalize(t1);
else return normalize(t2);
vec3 traceRay(vec3 origin, vec3 dir, float maxDist, float minDist) {
float stepSize = maxDist / float(RAY_STEPS);
vec3 pos = origin + dir * minDist;
float prevDepthDiff = 0.0;
float hadValidPrev = 0.0;
for (int i = 1; i <= RAY_STEPS; i++) {
pos += dir * stepSize;
vec2 uv = getProjectedCoord(pos);
vec2 sampleUV = clamp(uv, vec2(0.001), vec2(0.999));
float sampleDepth = textureLod(gbufferD, sampleUV, 0.0).r * 2.0 - 1.0;
if (sampleDepth == 1.0) {
hadValidPrev = 0.0;
continue;
}
vec3 sampleViewPos = getPosView2(invP, sampleDepth, sampleUV);
float depthDiff = pos.z - sampleViewPos.z;
float rayDist = length(pos - origin);
float thickness = 0.15 + rayDist * 0.25;
float crossed = hadValidPrev * step(0.0, prevDepthDiff) * step(depthDiff, 0.0);
float withinThickness = step(abs(depthDiff), thickness);
if (crossed > 0.5 || withinThickness > 0.5) {
float distWeight = 1.0 - (rayDist / maxDist);
distWeight = max(0.0, distWeight * distWeight);
return vec3(sampleUV, distWeight);
}
prevDepthDiff = depthDiff;
hadValidPrev = 1.0;
}
return vec3(-1.0);
}
void main() {
fragColor = 0;
vec4 g0 = textureLod(gbuffer0, texCoord, 0.0);
float d = textureLod(gbufferD, texCoord, 0.0).r * 2.0 - 1.0;
float depth = textureLod(gbufferD, texCoord, 0.0).r * 2.0 - 1.0;
if (depth == 1.0) {
fragColor = vec4(0.0, 0.0, 0.0, 1.0);
return;
}
vec4 g0 = textureLod(gbuffer0, texCoord, 0.0);
vec2 enc = g0.rg;
vec3 n;
n.z = 1.0 - abs(enc.x) - abs(enc.y);
n.xy = n.z >= 0.0 ? enc.xy : octahedronWrap(enc.xy);
n = normalize(V3 * n);
n = normalize(n);
vpos = getPosView(viewRay, d, cameraProj);
vec3 viewNormal = V3 * n;
vec3 viewPos = getPosView2(invP, depth, texCoord);
rayCast(n);
vec3 o1 = normalize(tangent(n));
vec3 o2 = (cross(o1, n));
vec3 c1 = 0.5f * (o1 + o2);
vec3 c2 = 0.5f * (o1 - o2);
rayCast(mix(n, o1, angleMix));
rayCast(mix(n, o2, angleMix));
rayCast(mix(n, -c1, angleMix));
rayCast(mix(n, -c2, angleMix));
#ifdef _SSGICone9
rayCast(mix(n, -o1, angleMix));
rayCast(mix(n, -o2, angleMix));
rayCast(mix(n, c1, angleMix));
rayCast(mix(n, c2, angleMix));
#ifdef _CPostprocess
float radius = PPComp12.y;
float strength = PPComp12.x;
#else
float radius = ssgiRadius;
float strength = ssgiStrength;
#endif
float noise = fract(52.9829189 * fract(0.06711056 * texCoord.x * 1000.0 + 0.00583715 * texCoord.y * 1000.0));
vec3 gi = vec3(0.0);
int validSamples = 0;
// min distance to avoid self shadowing artiffacts
float minDist = radius * 0.05;
for (int i = 0; i < ssgiSamples; i++) {
float fi = float(i) + noise;
vec2 rand = vec2(
fract(fi * 0.7548776662 + noise),
fract(fi * 0.5698402909 + noise * 1.5)
);
vec3 rayDir = cosineSampleHemisphere(viewNormal, rand);
vec3 hitResult = traceRay(viewPos, rayDir, radius, minDist);
if (hitResult.x < 0.0) continue;
vec2 hitUV = hitResult.xy;
float distWeight = hitResult.z;
vec3 hitAlbedo = textureLod(gbuffer1, hitUV, 1.0).rgb;
#ifdef _Sun
vec4 hitG0 = textureLod(gbuffer0, hitUV, 0.0);
vec2 hitEnc = hitG0.rg;
vec3 hitN;
hitN.z = 1.0 - abs(hitEnc.x) - abs(hitEnc.y);
hitN.xy = hitN.z >= 0.0 ? hitEnc.xy : octahedronWrap(hitEnc.xy);
hitN = normalize(hitN);
float hitNdotL = max(0.0, dot(hitN, sunDir));
vec3 hitRadiance = hitAlbedo * sunCol * hitNdotL;
#else
vec3 hitRadiance = hitAlbedo * 0.5;
#endif
#ifdef _EmissionShaded
hitRadiance += textureLod(gbufferEmission, hitUV, 0.0).rgb;
#endif
gi += hitRadiance * distWeight;
validSamples++;
}
if (validSamples > 0) {
gi /= float(validSamples);
}
gi *= strength;
#ifdef _EmissionShaded
gi += textureLod(gbufferEmission, texCoord, 0.0).rgb * 0.3;
#endif
fragColor = vec4(min(gi, vec3(2.0)), 1.0);
}

View File

@ -10,21 +10,32 @@
"name": "P",
"link": "_projectionMatrix"
},
{
"name": "V3",
"link": "_viewMatrix3"
},
{
"name": "invP",
"link": "_inverseProjectionMatrix"
},
{
"name": "cameraProj",
"link": "_cameraPlaneProj"
"name": "V3",
"link": "_viewMatrix3"
},
{
"name": "sunDir",
"link": "_sunDirection",
"ifdef": ["_Sun"]
},
{
"name": "sunCol",
"link": "_sunColor",
"ifdef": ["_Sun"]
},
{
"name": "PPComp12",
"link": "_PPComp12",
"ifdef": ["_CPostprocess"]
}
],
"texture_params": [],
"vertex_shader": "../include/pass_viewray2.vert.glsl",
"vertex_shader": "../include/pass.vert.glsl",
"fragment_shader": "ssgi_pass.frag.glsl"
}
]

View File

@ -64,20 +64,26 @@ vec4 rayCast(vec3 dir) {
ddepth = getDeltaDepth(hitCoord);
if (ddepth > 0.0) return binarySearch(dir);
}
return vec4(texCoord, 0.0, 1.0);
return vec4(texCoord, 0.0, 0.0);
}
void main() {
vec4 g0 = textureLod(gbuffer0, texCoord, 0.0);
float roughness = g0.z;
vec4 gr = textureLod(gbuffer_refraction, texCoord, 0.0);
float ior = gr.x;
float opac = gr.y;
float d = textureLod(gbufferD, texCoord, 0.0).r * 2.0 - 1.0;
if (d == 0.0 || d == 1.0 || opac == 1.0 || ior == 1.0) {
fragColor.rgb = textureLod(tex1, texCoord, 0.0).rgb;
float transmittance = gr.y;
float surfaceDepth = gr.z;
float d = surfaceDepth * 2.0 - 1.0;
vec4 sceneSample = textureLod(tex, texCoord, 0.0);
if (surfaceDepth == 0.0 || transmittance == 0.0 || ior == 1.0) {
vec3 background = textureLod(tex1, texCoord, 0.0).rgb;
fragColor.rgb = sceneSample.rgb + background * (1.0 - sceneSample.a);
fragColor.a = 1.0;
return;
}
vec4 g0 = textureLod(gbuffer0, texCoord, 0.0);
float roughness = g0.z;
vec2 enc = g0.rg;
vec3 n;
n.z = 1.0 - abs(enc.x) - abs(enc.y);
@ -86,21 +92,32 @@ void main() {
vec3 viewNormal = V3 * n;
vec3 viewPos = getPosView(viewRay, d, cameraProj);
vec3 refracted = refract(normalize(viewPos), viewNormal, 1.0 / ior);
vec3 incident = normalize(viewPos);
vec3 refracted = refract(incident, viewNormal, 1.0 / ior);
if (length(refracted) < 0.001) {
vec3 background = textureLod(tex1, texCoord, 0.0).rgb;
fragColor.rgb = sceneSample.rgb + background * (1.0 - sceneSample.a);
fragColor.a = 1.0;
return;
}
hitCoord = viewPos;
vec3 dir = refracted * (1.0 - rand(texCoord) * ss_refractionJitter * roughness) * 2.0;
vec4 coords = rayCast(dir);
vec2 deltaCoords = abs(vec2(0.5, 0.5) - coords.xy);
float screenEdgeFactor = clamp(1.0 - (deltaCoords.x + deltaCoords.y), 0.0, 1.0);
vec2 screenEdge = smoothstep(0.0, 0.1, coords.xy) * smoothstep(0.0, 0.1, 1.0 - coords.xy);
float screenEdgeFactor = screenEdge.x * screenEdge.y;
float refractivity = 1.0 - roughness;
float intensity = pow(refractivity, ss_refractionFalloffExp) * screenEdgeFactor * \
clamp(-refracted.z, 0.0, 1.0) * clamp((length(viewPos - hitCoord)), 0.0, 1.0) * coords.w;
float intensity = pow(refractivity, ss_refractionFalloffExp) * screenEdgeFactor * coords.w;
intensity = clamp(intensity, 0.0, 1.0);
vec3 refractionCol = textureLod(tex1, coords.xy, 0.0).rgb;
refractionCol *= intensity;
vec3 color = textureLod(tex, texCoord.xy, 0.0).rgb;
fragColor.rgb = mix(refractionCol, color, opac);
vec3 refractedBackground = textureLod(tex1, coords.xy, 0.0).rgb;
vec3 straightBackground = textureLod(tex1, texCoord, 0.0).rgb;
vec3 behindColor = mix(straightBackground, refractedBackground, intensity);
fragColor.rgb = sceneSample.rgb + behindColor * (1.0 - sceneSample.a);
fragColor.a = 1.0;
}

View File

@ -5,6 +5,9 @@
"depth_write": false,
"compare_mode": "always",
"cull_mode": "none",
"blend_source": "blend_one",
"blend_destination": "blend_zero",
"blend_operation": "add",
"links": [
{
"name": "P",

View File

@ -36,6 +36,7 @@
#version 450
#include "compiled.inc"
#include "std/gbuffer.glsl"
uniform sampler2D gbufferD;
uniform sampler2D gbuffer0;
@ -47,69 +48,92 @@ uniform vec2 cameraProj;
in vec2 texCoord;
out vec4 fragColor;
const float SSSS_FOVY = 108.0;
const vec3 SKIN_SSS_RADIUS = vec3(4.8, 2.4, 1.5);
const float SSS_DISTANCE_SCALE = 0.001;
// Temp hash func -
float hash13(vec3 p3) {
p3 = fract(p3 * vec3(0.1031, 0.1030, 0.0973));
p3 += dot(p3, p3.yzx + 33.33);
return fract((p3.x + p3.y) * p3.z);
}
// Separable SSS Reflectance
// const float sssWidth = 0.005;
vec4 SSSSBlur() {
// Quality = 0
const int SSSS_N_SAMPLES = 11;
vec4 kernel[SSSS_N_SAMPLES];
kernel[0] = vec4(0.560479, 0.669086, 0.784728, 0);
kernel[1] = vec4(0.00471691, 0.000184771, 5.07566e-005, -2);
kernel[2] = vec4(0.0192831, 0.00282018, 0.00084214, -1.28);
kernel[3] = vec4(0.03639, 0.0130999, 0.00643685, -0.72);
kernel[4] = vec4(0.0821904, 0.0358608, 0.0209261, -0.32);
kernel[5] = vec4(0.0771802, 0.113491, 0.0793803, -0.08);
kernel[6] = vec4(0.0771802, 0.113491, 0.0793803, 0.08);
kernel[7] = vec4(0.0821904, 0.0358608, 0.0209261, 0.32);
kernel[8] = vec4(0.03639, 0.0130999, 0.00643685, 0.72);
kernel[9] = vec4(0.0192831, 0.00282018, 0.00084214, 1.28);
kernel[10] = vec4(0.00471691, 0.000184771, 5.07565e-005, 2);
const int SSSS_N_SAMPLES = 15;
vec4 kernel[SSSS_N_SAMPLES];
kernel[0] = vec4(0.233, 0.455, 0.649, 0.0); // Center sample
kernel[1] = vec4(0.100, 0.336, 0.344, 0.37); // +0.37mm
kernel[2] = vec4(0.118, 0.198, 0.0, 0.97); // +0.97mm
kernel[3] = vec4(0.113, 0.007, 0.007, 1.93); // +1.93mm
kernel[4] = vec4(0.358, 0.004, 0.0, 3.87); // +3.87mm
kernel[5] = vec4(0.078, 0.0, 0.0, 6.53); // +6.53mm (red only)
kernel[6] = vec4(0.0, 0.0, 0.0, 0.0); // Unused
kernel[7] = vec4(0.0, 0.0, 0.0, 0.0); // Unused
kernel[8] = vec4(0.100, 0.336, 0.344, -0.37); // -0.37mm
kernel[9] = vec4(0.118, 0.198, 0.0, -0.97); // -0.97mm
kernel[10] = vec4(0.113, 0.007, 0.007, -1.93); // -1.93mm
kernel[11] = vec4(0.358, 0.004, 0.0, -3.87); // -3.87mm
kernel[12] = vec4(0.078, 0.0, 0.0, -6.53); // -6.53mm (red only)
kernel[13] = vec4(0.0, 0.0, 0.0, 0.0); // Unused
kernel[14] = vec4(0.0, 0.0, 0.0, 0.0); // Unused
vec4 colorM = textureLod(tex, texCoord, 0.0);
// Fetch linear depth of current pixel
float depth = textureLod(gbufferD, texCoord, 0.0).r;
float depthM = cameraProj.y / (depth - cameraProj.x);
// Calculate the sssWidth scale (1.0 for a unit plane sitting on the projection window)
float distanceToProjectionWindow = 1.0 / tan(0.5 * radians(SSSS_FOVY));
float scale = distanceToProjectionWindow / depthM;
float distanceScale = 1.0 / max(depthM, 0.1);
vec2 finalStep = sssWidth * distanceScale * dir * SSS_DISTANCE_SCALE;
// Calculate the final step to fetch the surrounding pixels
vec2 finalStep = sssWidth * scale * dir;
finalStep *= 1.0;//SSSS_STREGTH_SOURCE; // Modulate it using the alpha channel.
finalStep *= 1.0 / 3.0; // Divide by 3 as the kernels range from -3 to 3.
finalStep *= 0.05; //
// Accumulate the center sample:
vec4 colorBlurred = colorM;
colorBlurred.rgb *= kernel[0].rgb;
// Accumulate the other samples
vec3 jitterSeed = vec3(texCoord.xy * 1000.0, fract(cameraProj.x * 0.0001));
float jitterOffset = (hash13(jitterSeed) * 2.0 - 1.0) * 0.15;
finalStep *= (1.0 + jitterOffset);
vec3 colorBlurred = vec3(0.0);
vec3 weightSum = vec3(0.0);
colorBlurred += colorM.rgb * kernel[0].rgb;
weightSum += kernel[0].rgb;
for (int i = 1; i < SSSS_N_SAMPLES; i++) {
// Fetch color and depth for current sample
vec2 offset = texCoord + kernel[i].a * finalStep;
float sampleJitter = hash13(vec3(texCoord.xy * 720.0, float(i) * 37.45)) * 0.1 - 0.05;
vec2 offset = texCoord + (kernel[i].a + sampleJitter) * finalStep;
vec4 color = textureLod(tex, offset, 0.0);
//#if SSSS_FOLLOW_SURFACE == 1
// If the difference in depth is huge, we lerp color back to "colorM":
//float depth = textureLod(tex, offset, 0.0).r;
//float s = clamp(300.0f * distanceToProjectionWindow * sssWidth * abs(depthM - depth),0.0,1.0);
//color.rgb = mix(color.rgb, colorM.rgb, s);
//#endif
// Accumulate
colorBlurred.rgb += kernel[i].rgb * color.rgb;
const float DEPTH_THRESHOLD = 0.05;
float sampleDepth = textureLod(gbufferD, offset, 0.0).r;
float sampleDepthM = cameraProj.y / (sampleDepth - cameraProj.x);
float depthDiff = abs(depthM - sampleDepthM);
float depthWeight = exp(-depthDiff * 10.0);
if (depthDiff > DEPTH_THRESHOLD) {
color.rgb = mix(colorM.rgb, color.rgb, depthWeight);
}
colorBlurred += color.rgb * kernel[i].rgb;
weightSum += kernel[i].rgb;
}
return colorBlurred;
vec3 normalizedColor = colorBlurred / max(weightSum, vec3(0.00001));
float dither = hash13(vec3(texCoord * 1333.0, 0.0)) * 0.003 - 0.0015;
normalizedColor = max(normalizedColor + vec3(dither), vec3(0.0));
return vec4(normalizedColor, colorM.a);
}
void main() {
if (textureLod(gbuffer0, texCoord, 0.0).a == 8192.0) {
fragColor = clamp(SSSSBlur(), 0.0, 1.0);
}
else {
vec4 g0 = textureLod(gbuffer0, texCoord, 0.0);
float metallic;
uint matid;
unpackFloatInt16(g0.a, metallic, matid);
if (matid == 2u) {
vec4 originalColor = textureLod(tex, texCoord, 0.0);
vec4 blurredColor = SSSSBlur();
vec4 sssContribution = blurredColor - originalColor;
vec4 combined = originalColor + max(vec4(0.0), sssContribution) * 0.8;
fragColor = max(vec4(0.0), min(combined, vec4(10.0)));
} else {
fragColor = textureLod(tex, texCoord, 0.0);
}
}

View File

@ -0,0 +1,18 @@
#ifndef _AABB_GLSL
#define _AABB_GLSL
bool IntersectAABB(vec3[2] a, vec3[2] b) {
const float EPSILON = 0.001; // Small tolerance to prevent false negatives
if (abs(a[0].x - b[0].x) > (a[1].x + b[1].x + EPSILON)) return false;
if (abs(a[0].y - b[0].y) > (a[1].y + b[1].y + EPSILON)) return false;
if (abs(a[0].z - b[0].z) > (a[1].z + b[1].z + EPSILON)) return false;
return true;
}
void AABBfromMinMax(inout vec3[2] aabb, vec3 _min, vec3 _max)
{
aabb[0] = (_min + _max) * 0.5f;
aabb[1] = abs(_max - aabb[0]);
}
#endif

View File

@ -36,7 +36,8 @@ float d_ggx(const float nh, const float a) {
vec3 specularBRDF(const vec3 f0, const float roughness, const float nl, const float nh, const float nv, const float vh) {
float a = roughness * roughness;
return d_ggx(nh, a) * g2_approx(nl, nv, a) * f_schlick(f0, vh) / max(4.0 * nv, 1e-5); //NdotL cancels out later
vec3 result = d_ggx(nh, a) * g2_approx(nl, nv, a) * f_schlick(f0, vh) / max(4.0 * nv, 1e-5); //NdotL cancels out later
return min(result, vec3(200.0));
}
// John Hable - Optimizing GGX Shaders

View File

@ -22,7 +22,7 @@ THE SOFTWARE.
#ifndef _CONETRACE_GLSL_
#define _CONETRACE_GLSL_
#include "std/voxels_constants.glsl"
#include "std/constants.glsl"
// References
// https://github.com/Friduric/voxel-cone-tracing
@ -92,7 +92,7 @@ vec4 traceCone(const sampler3D voxels, const sampler3D voxelsSDF, const vec3 ori
float dist = voxelSize0;
float step_dist = dist;
vec3 samplePos;
vec3 start_pos = origin + n * voxelSize0 * voxelgiOffset;
vec3 start_pos = origin + n * voxelSize0;
int clipmap_index0 = 0;
vec3 aniso_direction = -dir;
@ -125,7 +125,7 @@ vec4 traceCone(const sampler3D voxels, const sampler3D voxelsSDF, const vec3 ori
if(clipmap_blend > 0.0 && clipmap_index < voxelgiClipmapCount - 1) {
vec4 mipSampleNext = sampleVoxel(voxels, p0, clipmaps, clipmap_index + 1.0, step_dist, precomputed_direction, face_offset, direction_weight);
mipSample = mix(mipSample, mipSampleNext, smoothstep(0.0, 1.0, clipmap_blend));
mipSample = mix(mipSample, mipSampleNext, clipmap_blend);
}
sampleCol += (1.0 - sampleCol.a) * mipSample;
@ -148,8 +148,9 @@ vec4 traceCone(const sampler3D voxels, const sampler3D voxelsSDF, const vec3 ori
vec4 traceDiffuse(const vec3 origin, const vec3 normal, const sampler3D voxels, const float clipmaps[voxelgiClipmapCount * 10]) {
float sum = 0.0;
vec4 amount = vec4(0.0);
mat3 TBN = makeTangentBasis(normal);
for (int i = 0; i < DIFFUSE_CONE_COUNT; ++i) {
vec3 coneDir = DIFFUSE_CONE_DIRECTIONS[i];
vec3 coneDir = TBN * DIFFUSE_CONE_DIRECTIONS[i];
const float cosTheta = dot(normal, coneDir);
if (cosTheta <= 0)
continue;
@ -166,7 +167,7 @@ vec4 traceDiffuse(const vec3 origin, const vec3 normal, const sampler3D voxels,
}
vec4 traceSpecular(const vec3 origin, const vec3 normal, const sampler3D voxels, const sampler3D voxelsSDF, const vec3 viewDir, const float roughness, const float clipmaps[voxelgiClipmapCount * 10], const vec2 pixel, const vec2 velocity) {
vec3 specularDir = reflect(-viewDir, normal);
vec3 specularDir = reflect(normalize(-viewDir), normal);
vec3 P = origin + specularDir * ((BayerMatrix8[int(pixel.x + velocity.x) % 8][int(pixel.y + velocity.y) % 8] - 0.5)) * voxelgiStep;
vec4 amount = traceCone(voxels, voxelsSDF, P, normal, specularDir, 0, true, roughness, voxelgiStep, clipmaps);
@ -176,9 +177,9 @@ vec4 traceSpecular(const vec3 origin, const vec3 normal, const sampler3D voxels,
return amount * voxelgiOcc;
}
vec4 traceRefraction(const vec3 origin, const vec3 normal, sampler3D voxels, sampler3D voxelsSDF, const vec3 viewDir, const float ior, const float roughness, const float clipmaps[voxelgiClipmapCount * 10], const vec2 pixel, const vec2 velocity) {
const float transmittance = 1.0;
vec3 refractionDir = refract(-viewDir, normal, 1.0 / ior);
vec4 traceRefraction(const vec3 origin, const vec3 normal, sampler3D voxels, sampler3D voxelsSDF, const vec3 viewDir, const float ior, const float roughness, const float clipmaps[voxelgiClipmapCount * 10], const vec2 pixel, const vec2 velocity, const float opacity) {
const float transmittance = 1.0 - opacity;
vec3 refractionDir = refract(normalize(-viewDir), normal, 1.0 / ior);
vec3 P = origin + refractionDir * (BayerMatrix8[int(pixel.x + velocity.x) % 8][int(pixel.y + velocity.y) % 8] - 0.5) * voxelgiStep;
vec4 amount = transmittance * traceCone(voxels, voxelsSDF, P, normal, refractionDir, 0, true, roughness, voxelgiStep, clipmaps);
@ -196,7 +197,7 @@ float traceConeAO(const sampler3D voxels, const vec3 origin, const vec3 n, const
float dist = voxelSize0;
float step_dist = dist;
vec3 samplePos;
vec3 start_pos = origin + n * voxelSize0 * voxelgiOffset;
vec3 start_pos = origin + n * voxelSize0;
int clipmap_index0 = 0;
vec3 aniso_direction = -dir;
@ -259,7 +260,6 @@ float traceAO(const vec3 origin, const vec3 normal, const sampler3D voxels, cons
}
#endif
#ifdef _VoxelShadow
float traceConeShadow(const sampler3D voxels, const sampler3D voxelsSDF, const vec3 origin, const vec3 n, const vec3 dir, const float aperture, const float step_size, const float clipmaps[voxelgiClipmapCount * 10]) {
float sampleCol = 0.0;
@ -267,7 +267,7 @@ float traceConeShadow(const sampler3D voxels, const sampler3D voxelsSDF, const v
float dist = voxelSize0;
float step_dist = dist;
vec3 samplePos;
vec3 start_pos = origin + n * voxelSize0 * voxelgiOffset;
vec3 start_pos = origin + n * voxelSize0;
int clipmap_index0 = 0;
vec3 aniso_direction = -dir;
@ -287,7 +287,7 @@ float traceConeShadow(const sampler3D voxels, const sampler3D voxelsSDF, const v
float clipmap_blend = fract(lod);
vec3 p0 = start_pos + dir * dist;
samplePos = (p0 - vec3(clipmaps[int(clipmap_index * 10 + 4)], clipmaps[int(clipmap_index * 10 + 5)], clipmaps[int(clipmap_index * 10 + 6)])) / (float(clipmaps[int(clipmap_index * 10)]) * voxelgiResolution.x);
samplePos = (p0 - vec3(clipmaps[int(clipmap_index * 10 + 4)], clipmaps[int(clipmap_index * 10 + 5)], clipmaps[int(clipmap_index * 10 + 6)])) / (float(clipmaps[int(clipmap_index * 10)]) * voxelgiResolution);
samplePos = samplePos * 0.5 + 0.5;
if ((any(notEqual(samplePos, clamp(samplePos, 0.0, 1.0))))) {
@ -328,9 +328,9 @@ float traceConeShadow(const sampler3D voxels, const sampler3D voxelsSDF, const v
}
float traceShadow(const vec3 origin, const vec3 normal, const sampler3D voxels, const sampler3D voxelsSDF, const vec3 dir, const float clipmaps[voxelgiClipmapCount * 10], const vec2 pixel) {
vec3 P = origin + dir * (BayerMatrix8[int(pixel.x) % 8][int(pixel.y) % 8] - 0.5) * voxelgiStep;
float amount = traceConeShadow(voxels, voxelsSDF, P, normal, dir, DIFFUSE_CONE_APERTURE, voxelgiStep, clipmaps);
float traceShadow(const vec3 origin, const vec3 normal, const sampler3D voxels, const sampler3D voxelsSDF, const vec3 dir, const float clipmaps[voxelgiClipmapCount * 10], const vec2 pixel, const vec2 velocity) {
vec3 P = origin + dir * (BayerMatrix8[int(pixel.x + velocity.x) % 8][int(pixel.y + velocity.y) % 8] - 0.5) * voxelgiStep;
float amount = traceConeShadow(voxels, voxelsSDF, P, normal, dir, SHADOW_CONE_APERTURE, voxelgiStep, clipmaps);
amount = clamp(amount, 0.0, 1.0);
return amount * voxelgiOcc;
}

View File

@ -0,0 +1,88 @@
/*
Copyright (c) 2024 Turánszki János
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
const int DIFFUSE_CONE_COUNT = 16;
const float SHADOW_CONE_APERTURE = radians(15.0);
const float DIFFUSE_CONE_APERTURE = 0.872665;
mat3 makeTangentBasis(const vec3 normal) {
// Create a tangent basis from normal vector
vec3 tangent;
vec3 bitangent;
// Compute tangent (Frisvad's method)
if (abs(normal.z) < 0.999) {
tangent = normalize(cross(vec3(0, 1, 0), normal));
} else {
tangent = normalize(cross(normal, vec3(1, 0, 0)));
}
bitangent = cross(normal, tangent);
return mat3(tangent, bitangent, normal);
}
// 16 optimized cone directions for hemisphere sampling (Z-up, normalized)
const vec3 DIFFUSE_CONE_DIRECTIONS[16] = vec3[](
vec3(0.707107, 0.000000, 0.707107), // Front
vec3(-0.707107, 0.000000, 0.707107), // Back
vec3(0.000000, 0.707107, 0.707107), // Right
vec3(0.000000, -0.707107, 0.707107), // Left
vec3(0.500000, 0.500000, 0.707107), // Front-right
vec3(-0.500000, 0.500000, 0.707107), // Back-right
vec3(0.500000, -0.500000, 0.707107), // Front-left
vec3(-0.500000, -0.500000, 0.707107),// Back-left
vec3(0.353553, 0.000000, 0.935414), // Narrow front
vec3(-0.353553, 0.000000, 0.935414), // Narrow back
vec3(0.000000, 0.353553, 0.935414), // Narrow right
vec3(0.000000, -0.353553, 0.935414), // Narrow left
vec3(0.270598, 0.270598, 0.923880), // Narrow front-right
vec3(-0.270598, 0.270598, 0.923880), // Narrow back-right
vec3(0.270598, -0.270598, 0.923880), // Narrow front-left
vec3(-0.270598, -0.270598, 0.923880) // Narrow back-left
);
// TO DO - Disabled momentarily instead of changing formulas
const float off_BayerMatrix8[8][8] =
{
{ 1.0 / 65.0, 49.0 / 65.0, 13.0 / 65.0, 61.0 / 65.0, 4.0 / 65.0, 52.0 / 65.0, 16.0 / 65.0, 64.0 / 65.0 },
{ 33.0 / 65.0, 17.0 / 65.0, 45.0 / 65.0, 29.0 / 65.0, 36.0 / 65.0, 20.0 / 65.0, 48.0 / 65.0, 32.0 / 65.0 },
{ 9.0 / 65.0, 57.0 / 65.0, 5.0 / 65.0, 53.0 / 65.0, 12.0 / 65.0, 60.0 / 65.0, 8.0 / 65.0, 56.0 / 65.0 },
{ 41.0 / 65.0, 25.0 / 65.0, 37.0 / 65.0, 21.0 / 65.0, 44.0 / 65.0, 28.0 / 65.0, 40.0 / 65.0, 24.0 / 65.0 },
{ 3.0 / 65.0, 51.0 / 65.0, 15.0 / 65.0, 63.0 / 65.0, 2.0 / 65.0, 50.0 / 65.0, 14.0 / 65.0, 62.0 / 65.0 },
{ 35.0 / 65.0, 19.0 / 65.0, 47.0 / 65.0, 31.0 / 65.0, 34.0 / 65.0, 18.0 / 65.0, 46.0 / 65.0, 30.0 / 65.0 },
{ 11.0 / 65.0, 59.0 / 65.0, 7.0 / 65.0, 55.0 / 65.0, 10.0 / 65.0, 58.0 / 65.0, 6.0 / 65.0, 54.0 / 65.0 },
{ 43.0 / 65.0, 27.0 / 65.0, 39.0 / 65.0, 23.0 / 65.0, 42.0 / 65.0, 26.0 / 65.0, 38.0 / 65.0, 22.0 / 65.0 }
};
const float BayerMatrix8[8][8] =
{
{ 1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0 },
{ 1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0 },
{ 1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0 },
{ 1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0 },
{ 1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0 },
{ 1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0 },
{ 1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0 },
{ 1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0 }
};

View File

@ -8,10 +8,10 @@
// const float compoDOFLength = 160.0; // Focal length in mm 18-200
// const float compoDOFFstop = 128.0; // F-stop value
const int samples = 6; // Samples on the first ring
const int samples = 8; // Samples on the first ring
const int rings = 6; // Ring count
const vec2 focus = vec2(0.5, 0.5);
const float coc = 0.11; // Circle of confusion size in mm (35mm film = 0.03mm)
const float coc = 0.03; // Circle of confusion size in mm (35mm film = 0.03mm)
const float maxblur = 1.0;
const float threshold = 0.5; // Highlight threshold
const float gain = 2.0; // Highlight gain
@ -55,21 +55,26 @@ vec3 dof(
float f = DOFLength; // Focal length in mm
float d = fDepth * 1000.0; // Focal plane in mm
float o = depth * 1000.0; // Depth in mm
float a = (o * f) / (o - f);
float b = (d * f) / (d - f);
float c = (d - f) / (d * DOFFStop * coc);
float a = (o > f) ? (o * f) / (o - f) : 0.0;
float b = (d > f) ? (d * f) / (d - f) : 0.0;
float sensorSize = max(DOFFStop, 10.0);
float c = (d - f) / (d * sensorSize * coc);
float blur = abs(a - b) * c;
blur = clamp(blur, 0.0, 1.0);
vec2 noise = rand2(texCoord) * namount * blur;
float w = (texStep.x) * blur * maxblur + noise.x;
float h = (texStep.y) * blur * maxblur + noise.y;
vec3 col = vec3(0.0);
if (blur < 0.05) {
col = textureLod(tex, texCoord, 0.0).rgb;
}
else {
col = textureLod(tex, texCoord, 0.0).rgb;
vec3 sharpCol = textureLod(tex, texCoord, 0.0).rgb;
vec3 col = sharpCol;
float blurThreshold = 0.02;
float blurRange = 0.06;
if (blur > blurThreshold) {
float blurAmount = smoothstep(blurThreshold, blurThreshold + blurRange, blur);
vec3 blurredCol = sharpCol;
float s = 1.0;
int ringsamples;
@ -81,11 +86,12 @@ vec3 dof(
float ph = (sin(float(j) * step) * float(i));
float p = 1.0;
// if (pentagon) p = penta(vec2(pw, ph));
col += color(texCoord + vec2(pw * w, ph * h), blur, tex, texStep) * mix(1.0, (float(i)) / (float(rings)), bias) * p;
blurredCol += color(texCoord + vec2(pw * w, ph * h), blur, tex, texStep) * mix(1.0, (float(i)) / (float(rings)), bias) * p;
s += 1.0 * mix(1.0, (float(i)) / (float(rings)), bias) * p;
}
}
col /= s;
blurredCol /= s;
col = mix(sharpCol, blurredCol, blurAmount);
}
return col;
}

View File

@ -1,11 +1,11 @@
#ifndef _GBUFFER_GLSL_
#define _GBUFFER_GLSL_
vec2 octahedronWrap(const vec2 v) {
vec2 octahedronWrap(vec2 v) {
return (1.0 - abs(v.yx)) * (vec2(v.x >= 0.0 ? 1.0 : -1.0, v.y >= 0.0 ? 1.0 : -1.0));
}
vec3 getNor(const vec2 enc) {
vec3 getNor(vec2 enc) {
vec3 n;
n.z = 1.0 - abs(enc.x) - abs(enc.y);
n.xy = n.z >= 0.0 ? enc.xy : octahedronWrap(enc.xy);
@ -13,13 +13,13 @@ vec3 getNor(const vec2 enc) {
return n;
}
vec3 getPosView(const vec3 viewRay, const float depth, const vec2 cameraProj) {
vec3 getPosView(vec3 viewRay, float depth, vec2 cameraProj) {
float linearDepth = cameraProj.y / (cameraProj.x - depth);
//float linearDepth = cameraProj.y / ((depth * 0.5 + 0.5) - cameraProj.x);
return viewRay * linearDepth;
}
vec3 getPos(const vec3 eye, const vec3 eyeLook, const vec3 viewRay, const float depth, const vec2 cameraProj) {
vec3 getPos(vec3 eye, vec3 eyeLook, vec3 viewRay, float depth, vec2 cameraProj) {
// eyeLook, viewRay should be normalized
float linearDepth = cameraProj.y / ((depth * 0.5 + 0.5) - cameraProj.x);
float viewZDist = dot(eyeLook, viewRay);
@ -27,7 +27,7 @@ vec3 getPos(const vec3 eye, const vec3 eyeLook, const vec3 viewRay, const float
return wposition;
}
vec3 getPosNoEye(const vec3 eyeLook, const vec3 viewRay, const float depth, const vec2 cameraProj) {
vec3 getPosNoEye(vec3 eyeLook, vec3 viewRay, float depth, vec2 cameraProj) {
// eyeLook, viewRay should be normalized
float linearDepth = cameraProj.y / ((depth * 0.5 + 0.5) - cameraProj.x);
float viewZDist = dot(eyeLook, viewRay);
@ -36,10 +36,10 @@ vec3 getPosNoEye(const vec3 eyeLook, const vec3 viewRay, const float depth, cons
}
#if defined(HLSL) || defined(METAL)
vec3 getPos2(const mat4 invVP, const float depth, vec2 coord) {
vec3 getPos2(mat4 invVP, float depth, vec2 coord) {
coord.y = 1.0 - coord.y;
#else
vec3 getPos2(const mat4 invVP, const float depth, const vec2 coord) {
vec3 getPos2(mat4 invVP, float depth, vec2 coord) {
#endif
vec4 pos = vec4(coord * 2.0 - 1.0, depth, 1.0);
pos = invVP * pos;
@ -48,10 +48,10 @@ vec3 getPos2(const mat4 invVP, const float depth, const vec2 coord) {
}
#if defined(HLSL) || defined(METAL)
vec3 getPosView2(const mat4 invP, const float depth, vec2 coord) {
vec3 getPosView2(mat4 invP, float depth, vec2 coord) {
coord.y = 1.0 - coord.y;
#else
vec3 getPosView2(const mat4 invP, const float depth, const vec2 coord) {
vec3 getPosView2(mat4 invP, float depth, vec2 coord) {
#endif
vec4 pos = vec4(coord * 2.0 - 1.0, depth, 1.0);
pos = invP * pos;
@ -60,10 +60,10 @@ vec3 getPosView2(const mat4 invP, const float depth, const vec2 coord) {
}
#if defined(HLSL) || defined(METAL)
vec3 getPos2NoEye(const vec3 eye, const mat4 invVP, const float depth, vec2 coord) {
vec3 getPos2NoEye(vec3 eye, mat4 invVP, float depth, vec2 coord) {
coord.y = 1.0 - coord.y;
#else
vec3 getPos2NoEye(const vec3 eye, const mat4 invVP, const float depth, const vec2 coord) {
vec3 getPos2NoEye(vec3 eye, mat4 invVP, float depth, vec2 coord) {
#endif
vec4 pos = vec4(coord * 2.0 - 1.0, depth, 1.0);
pos = invVP * pos;
@ -71,24 +71,24 @@ vec3 getPos2NoEye(const vec3 eye, const mat4 invVP, const float depth, const vec
return pos.xyz - eye;
}
float packFloat(const float f1, const float f2) {
float packFloat(float f1, float f2) {
return floor(f1 * 100.0) + min(f2, 1.0 - 1.0 / 100.0);
}
vec2 unpackFloat(const float f) {
vec2 unpackFloat(float f) {
return vec2(floor(f) / 100.0, fract(f));
}
float packFloat2(const float f1, const float f2) {
float packFloat2(float f1, float f2) {
// Higher f1 = less precise f2
return floor(f1 * 255.0) + min(f2, 1.0 - 1.0 / 100.0);
}
vec2 unpackFloat2(const float f) {
vec2 unpackFloat2(float f) {
return vec2(floor(f) / 255.0, fract(f));
}
vec4 encodeRGBM(const vec3 rgb) {
vec4 encodeRGBM(vec3 rgb) {
const float maxRange = 6.0;
float maxRGB = max(rgb.x, max(rgb.g, rgb.b));
float m = maxRGB / maxRange;
@ -96,7 +96,7 @@ vec4 encodeRGBM(const vec3 rgb) {
return vec4(rgb / (m * maxRange), m);
}
vec3 decodeRGBM(const vec4 rgbm) {
vec3 decodeRGBM(vec4 rgbm) {
const float maxRange = 6.0;
return rgbm.rgb * rgbm.a * maxRange;
}
@ -150,7 +150,7 @@ vec3 decNor(uint val) {
/**
Packs a float in [0, 1] and an integer in [0..15] into a single 16 bit float value.
**/
float packFloatInt16(const float f, const uint i) {
float packFloatInt16(float f, uint i) {
const uint numBitFloat = 12;
const float maxValFloat = float((1 << numBitFloat) - 1);
@ -160,7 +160,7 @@ float packFloatInt16(const float f, const uint i) {
return float(bitsInt | bitsFloat);
}
void unpackFloatInt16(const float val, out float f, out uint i) {
void unpackFloatInt16(float val, out float f, out uint i) {
const uint numBitFloat = 12;
const float maxValFloat = float((1 << numBitFloat) - 1);

View File

@ -1,239 +1,680 @@
#ifndef _LIGHT_GLSL_
#define _LIGHT_GLSL_
#include "compiled.inc"
#include "std/brdf.glsl"
#include "std/math.glsl"
#ifdef _ShadowMap
#include "std/shadows.glsl"
#endif
#ifdef _VoxelShadow
#include "std/conetrace.glsl"
//!uniform sampler2D voxels_shadows;
#endif
#ifdef _LTC
#include "std/ltc.glsl"
#endif
#ifdef _LightIES
#include "std/ies.glsl"
#endif
#ifdef _SSRS
#include "std/ssrs.glsl"
#endif
#ifdef _Spot
#include "std/light_common.glsl"
#endif
#ifdef _ShadowMap
#ifdef _SinglePoint
#ifdef _Spot
#ifndef _LTC
uniform sampler2DShadow shadowMapSpot[1];
uniform sampler2D shadowMapSpotTransparent[1];
uniform mat4 LWVPSpot[1];
#endif
#else
uniform samplerCubeShadow shadowMapPoint[1];
uniform samplerCube shadowMapPointTransparent[1];
uniform vec2 lightProj;
#endif
#endif
#ifdef _Clusters
#ifdef _SingleAtlas
//!uniform sampler2DShadow shadowMapAtlas;
//!uniform sampler2D shadowMapAtlasTransparent;
#endif
uniform vec2 lightProj;
#ifdef _ShadowMapAtlas
#ifndef _SingleAtlas
uniform sampler2DShadow shadowMapAtlasPoint;
uniform sampler2D shadowMapAtlasPointTransparent;
#endif
#else
uniform samplerCubeShadow shadowMapPoint[4];
uniform samplerCube shadowMapPointTransparent[4];
#endif
#ifdef _Spot
#ifdef _ShadowMapAtlas
#ifndef _SingleAtlas
uniform sampler2DShadow shadowMapAtlasSpot;
uniform sampler2D shadowMapAtlasSpotTransparent;
#endif
#else
uniform sampler2DShadow shadowMapSpot[4];
uniform sampler2D shadowMapSpotTransparent[4];
#endif
uniform mat4 LWVPSpotArray[maxLightsCluster];
#endif
#endif
#endif
#ifdef _LTC
uniform vec3 lightArea0;
uniform vec3 lightArea1;
uniform vec3 lightArea2;
uniform vec3 lightArea3;
uniform sampler2D sltcMat;
uniform sampler2D sltcMag;
#ifdef _ShadowMap
#ifndef _Spot
#ifdef _SinglePoint
uniform sampler2DShadow shadowMapSpot[1];
uniform sampler2D shadowMapSpotTransparent[1];
uniform mat4 LWVPSpot[1];
#endif
#ifdef _Clusters
uniform sampler2DShadow shadowMapSpot[maxLightsCluster];
uniform sampler2D shadowMapSpotTransparent[maxLightsCluster];
uniform mat4 LWVPSpotArray[maxLightsCluster];
#endif
#endif
#endif
#endif
vec3 sampleLight(const vec3 p, const vec3 n, const vec3 v, const float dotNV, const vec3 lp, const vec3 lightCol,
const vec3 albedo, const float rough, const float spec, const vec3 f0
#ifdef _ShadowMap
, int index, float bias, bool receiveShadow, bool transparent
#endif
#ifdef _Spot
, const bool isSpot, const float spotSize, float spotBlend, vec3 spotDir, vec2 scale, vec3 right
#endif
#ifdef _VoxelShadow
, sampler3D voxels, sampler3D voxelsSDF, float clipmaps[10 * voxelgiClipmapCount]
#endif
#ifdef _MicroShadowing
, float occ
#endif
#ifdef _SSRS
, sampler2D gbufferD, mat4 invVP, vec3 eye
#endif
) {
vec3 ld = lp - p;
vec3 l = normalize(ld);
vec3 h = normalize(v + l);
float dotNH = max(0.0, dot(n, h));
float dotVH = max(0.0, dot(v, h));
float dotNL = max(0.0, dot(n, l));
#ifdef _LTC
float theta = acos(dotNV);
vec2 tuv = vec2(rough, theta / (0.5 * PI));
tuv = tuv * LUT_SCALE + LUT_BIAS;
vec4 t = textureLod(sltcMat, tuv, 0.0);
mat3 invM = mat3(
vec3(1.0, 0.0, t.y),
vec3(0.0, t.z, 0.0),
vec3(t.w, 0.0, t.x));
float ltcspec = ltcEvaluate(n, v, dotNV, p, invM, lightArea0, lightArea1, lightArea2, lightArea3);
ltcspec *= textureLod(sltcMag, tuv, 0.0).a;
float ltcdiff = ltcEvaluate(n, v, dotNV, p, mat3(1.0), lightArea0, lightArea1, lightArea2, lightArea3);
vec3 direct = albedo * ltcdiff + ltcspec * spec * 0.05;
#else
vec3 direct = lambertDiffuseBRDF(albedo, dotNL) +
specularBRDF(f0, rough, dotNL, dotNH, dotNV, dotVH) * spec;
#endif
direct *= attenuate(distance(p, lp));
direct *= lightCol;
#ifdef _MicroShadowing
direct *= clamp(dotNL + 2.0 * occ * occ - 1.0, 0.0, 1.0);
#endif
#ifdef _SSRS
direct *= traceShadowSS(l, p, gbufferD, invVP, eye);
#endif
#ifdef _VoxelShadow
direct *= (1.0 - traceShadow(p, n, voxels, voxelsSDF, l, clipmaps, gl_FragCoord.xy).r) * voxelgiShad;
#endif
#ifdef _LTC
#ifdef _ShadowMap
if (receiveShadow) {
#ifdef _SinglePoint
vec4 lPos = LWVPSpotArray[0] * vec4(p + n * bias * 10, 1.0);
direct *= shadowTest(shadowMapSpot[0], shadowMapSpotTransparent[0], lPos.xyz / lPos.w, bias, transparent);
#endif
#ifdef _Clusters
vec4 lPos = LWVPSpotArray[index] * vec4(p + n * bias * 10, 1.0);
if (index == 0) direct *= shadowTest(shadowMapSpot[0], shadowMapSpotTransparent[0], lPos.xyz / lPos.w, bias, transparent);
else if (index == 1) direct *= shadowTest(shadowMapSpot[1], shadowMapSpotTransparent[1], lPos.xyz / lPos.w, bias, transparent);
else if (index == 2) direct *= shadowTest(shadowMapSpot[2], shadowMapSpotTransparent[2], lPos.xyz / lPos.w, bias, transparent);
else if (index == 3) direct *= shadowTest(shadowMapSpot[3], shadowMapSpotTransparent[3], lPos.xyz / lPos.w, bias, transparent);
#endif
}
#endif
return direct;
#endif
#ifdef _Spot
if (isSpot) {
direct *= spotlightMask(l, spotDir, right, scale, spotSize, spotBlend);
#ifdef _ShadowMap
if (receiveShadow) {
#ifdef _SinglePoint
vec4 lPos = LWVPSpot[0] * vec4(p + n * bias * 10, 1.0);
direct *= shadowTest(shadowMapSpot[0], shadowMapSpotTransparent[0], lPos.xyz / lPos.w, bias, transparent);
#endif
#ifdef _Clusters
vec4 lPos = LWVPSpotArray[index] * vec4(p + n * bias * 10, 1.0);
#ifdef _ShadowMapAtlas
direct *= shadowTest(
#ifndef _SingleAtlas
shadowMapAtlasSpot, shadowMapAtlasSpotTransparent
#else
shadowMapAtlas, shadowMapAtlasTransparent
#endif
, lPos.xyz / lPos.w, bias, transparent
);
#else
if (index == 0) direct *= shadowTest(shadowMapSpot[0], shadowMapSpotTransparent[0], lPos.xyz / lPos.w, bias, transparent);
else if (index == 1) direct *= shadowTest(shadowMapSpot[1], shadowMapSpotTransparent[1], lPos.xyz / lPos.w, bias, transparent);
else if (index == 2) direct *= shadowTest(shadowMapSpot[2], shadowMapSpotTransparent[2], lPos.xyz / lPos.w, bias, transparent);
else if (index == 3) direct *= shadowTest(shadowMapSpot[3], shadowMapSpotTransparent[3], lPos.xyz / lPos.w, bias, transparent);
#endif
#endif
}
#endif
return direct;
}
#endif
#ifdef _LightIES
direct *= iesAttenuation(-l);
#endif
#ifdef _ShadowMap
if (receiveShadow) {
#ifdef _SinglePoint
#ifndef _Spot
direct *= PCFCube(shadowMapPoint[0], shadowMapPointTransparent[0], ld, -l, bias, lightProj, n, transparent);
#endif
#endif
#ifdef _Clusters
#ifdef _ShadowMapAtlas
direct *= PCFFakeCube(
#ifndef _SingleAtlas
shadowMapAtlasPoint, shadowMapAtlasPointTransparent
#else
shadowMapAtlas, shadowMapAtlasTransparent
#endif
, ld, -l, bias, lightProj, n, index, transparent
);
#else
if (index == 0) direct *= PCFCube(shadowMapPoint[0], shadowMapPointTransparent[0], ld, -l, bias, lightProj, n, transparent);
else if (index == 1) direct *= PCFCube(shadowMapPoint[1], shadowMapPointTransparent[1], ld, -l, bias, lightProj, n, transparent);
else if (index == 2) direct *= PCFCube(shadowMapPoint[2], shadowMapPointTransparent[2], ld, -l, bias, lightProj, n, transparent);
else if (index == 3) direct *= PCFCube(shadowMapPoint[3], shadowMapPointTransparent[3], ld, -l, bias, lightProj, n, transparent);
#endif
#endif
}
#endif
return direct;
}
#endif
#ifndef _LIGHT_GLSL_
#define _LIGHT_GLSL_
#include "compiled.inc"
#include "std/brdf.glsl"
#include "std/math.glsl"
#ifdef _ShadowMap
#include "std/shadows.glsl"
#endif
#ifdef _VoxelShadow
#include "std/conetrace.glsl"
#endif
#ifdef _LTC
#include "std/ltc.glsl"
#endif
#ifdef _LightIES
#include "std/ies.glsl"
#endif
#ifdef _SSRS
#include "std/ssrs.glsl"
#endif
#ifdef _Spot
#include "std/light_common.glsl"
#endif
#ifdef _VoxelShadow
#include "std/conetrace.glsl"
#endif
#ifdef _ShadowMap
#ifdef _SinglePoint
#ifdef _Spot
#ifndef _LTC
uniform sampler2DShadow shadowMapSpot[1];
#ifdef _ShadowMapTransparent
uniform sampler2D shadowMapSpotTransparent[1];
#endif
uniform mat4 LWVPSpotArray[1];
#endif
#else
uniform samplerCubeShadow shadowMapPoint[1];
#ifdef _ShadowMapTransparent
uniform samplerCube shadowMapPointTransparent[1];
#endif
uniform vec2 lightProj;
#endif
#endif
#ifdef _Clusters
#ifdef _SingleAtlas
//!uniform sampler2DShadow shadowMapAtlas;
#ifdef _ShadowMapTransparent
//!uniform sampler2D shadowMapAtlasTransparent;
#endif
#endif
uniform vec2 lightProj;
#ifdef _ShadowMapAtlas
#ifndef _SingleAtlas
uniform sampler2DShadow shadowMapAtlasPoint;
#ifdef _ShadowMapTransparent
uniform sampler2D shadowMapAtlasPointTransparent;
#endif
#endif
#else
uniform samplerCubeShadow shadowMapPoint[4];
#ifdef _ShadowMapTransparent
uniform samplerCube shadowMapPointTransparent[4];
#endif
#endif
#ifdef _Spot
#ifdef _ShadowMapAtlas
#ifndef _SingleAtlas
uniform sampler2DShadow shadowMapAtlasSpot;
#ifdef _ShadowMapTransparent
uniform sampler2D shadowMapAtlasSpotTransparent;
#endif
#endif
#else
uniform sampler2DShadow shadowMapSpot[4];
#ifdef _ShadowMapTransparent
uniform sampler2D shadowMapSpotTransparent[4];
#endif
#endif
uniform mat4 LWVPSpotArray[maxLightsCluster];
#endif
#endif
#endif
#ifdef _LTC
uniform vec3 lightArea0;
uniform vec3 lightArea1;
uniform vec3 lightArea2;
uniform vec3 lightArea3;
uniform sampler2D sltcMat;
uniform sampler2D sltcMag;
#ifdef _ShadowMap
#ifndef _Spot
#ifdef _SinglePoint
uniform sampler2DShadow shadowMapSpot[1];
#ifdef _ShadowMapTransparent
uniform sampler2D shadowMapSpotTransparent[1];
#endif
uniform mat4 LWVPSpotArray[1];
#endif
#ifdef _Clusters
uniform sampler2DShadow shadowMapSpot[maxLightsCluster];
#ifdef _ShadowMapTransparent
uniform sampler2D shadowMapSpotTransparent[maxLightsCluster];
#endif
uniform mat4 LWVPSpotArray[maxLightsCluster];
#endif
#endif
#endif
#endif
vec3 sampleLight(const vec3 p, const vec3 n, const vec3 v, const float dotNV, const vec3 lp, const vec3 lightCol,
const vec3 albedo, const float rough, const float spec, const vec3 f0
#ifdef _ShadowMap
, int index, float bias, bool receiveShadow
#ifdef _ShadowMapTransparent
, bool transparent
#endif
#endif
#ifdef _Spot
, const bool isSpot, const float spotSize, float spotBlend, vec3 spotDir, vec2 scale, vec3 right
#endif
#ifdef _VoxelShadow
, sampler3D voxels, sampler3D voxelsSDF, float clipmaps[10 * voxelgiClipmapCount], vec2 velocity
#endif
#ifdef _MicroShadowing
, float occ
#endif
#ifdef _SSRS
, sampler2D gbufferD, mat4 invVP, vec3 eye
#endif
) {
vec3 ld = lp - p;
vec3 l = normalize(ld);
vec3 h = normalize(v + l);
float dotNH = max(0.0, dot(n, h));
float dotVH = max(0.0, dot(v, h));
float dotNL = max(0.0, dot(n, l));
#ifdef _LTC
float theta = acos(dotNV);
vec2 tuv = vec2(rough, theta / (0.5 * PI));
tuv = tuv * LUT_SCALE + LUT_BIAS;
vec4 t = textureLod(sltcMat, tuv, 0.0);
mat3 invM = mat3(
vec3(1.0, 0.0, t.y),
vec3(0.0, t.z, 0.0),
vec3(t.w, 0.0, t.x));
float ltcspec = ltcEvaluate(n, v, dotNV, p, invM, lightArea0, lightArea1, lightArea2, lightArea3);
ltcspec *= textureLod(sltcMag, tuv, 0.0).a;
float ltcdiff = ltcEvaluate(n, v, dotNV, p, mat3(1.0), lightArea0, lightArea1, lightArea2, lightArea3);
vec3 direct = albedo * ltcdiff + ltcspec * spec * 0.05;
#else
vec3 direct = lambertDiffuseBRDF(albedo, dotNL) +
specularBRDF(f0, rough, dotNL, dotNH, dotNV, dotVH) * spec;
#endif
direct *= attenuate(distance(p, lp));
direct *= min(lightCol, vec3(100.0));
#ifdef _MicroShadowing
direct *= clamp(dotNL + 2.0 * occ * occ - 1.0, 0.0, 1.0);
#endif
#ifdef _SSRS
direct *= traceShadowSS(l, p, gbufferD, invVP, eye);
#endif
#ifdef _VoxelShadow
vec3 lightDir = l;
#ifdef _Spot
if (isSpot)
lightDir = spotDir;
#endif
direct *= (1.0 - traceShadow(p, n, voxels, voxelsSDF, lightDir, clipmaps, gl_FragCoord.xy, velocity).r) * voxelgiShad;
#endif
#ifdef _LTC
#ifdef _ShadowMap
if (receiveShadow) {
#ifdef _SinglePoint
vec4 lPos = LWVPSpot[0] * vec4(p + n * bias * 2, 1.0);
direct *= shadowTest(shadowMapSpot[0],
#ifdef _ShadowMapTransparent
shadowMapSpotTransparent[0],
#endif
lPos.xyz / lPos.w, bias
#ifdef _ShadowMapTransparent
, transparent
#endif
);
#endif
#ifdef _Clusters
vec4 lPos = LWVPSpot[index] * vec4(p + n * bias * 2, 1.0);
if (index == 0) direct *= shadowTest(shadowMapSpot[0],
#ifdef _ShadowMapTransparent
shadowMapSpotTransparent[0],
#endif
lPos.xyz / lPos.w, bias
#ifdef _ShadowMapTransparent
, transparent
#endif
);
else if (index == 1) direct *= shadowTest(shadowMapSpot[1],
#ifdef _ShadowMapTransparent
shadowMapSpotTransparent[1],
#endif
lPos.xyz / lPos.w, bias
#ifdef _ShadowMapTransparent
, transparent
#endif
);
else if (index == 2) direct *= shadowTest(shadowMapSpot[2],
#ifdef _ShadowMapTransparent
shadowMapSpotTransparent[2],
#endif
lPos.xyz / lPos.w, bias
#ifdef _ShadowMapTransparent
, transparent
#endif
);
else if (index == 3) direct *= shadowTest(shadowMapSpot[3],
#ifdef _ShadowMapTransparent
shadowMapSpotTransparent[3],
#endif
lPos.xyz / lPos.w, bias
#ifdef _ShadowMapTransparent
, transparent
#endif
);
#endif
}
#endif
return direct;
#endif
#ifdef _Spot
if (isSpot) {
direct *= spotlightMask(l, spotDir, right, scale, spotSize, spotBlend);
#ifdef _ShadowMap
if (receiveShadow) {
#ifdef _SinglePoint
vec4 lPos = LWVPSpotArray[0] * vec4(p + n * bias * 2, 1.0);
direct *= shadowTest(shadowMapSpot[0],
#ifdef _ShadowMapTransparent
shadowMapSpotTransparent[0],
#endif
lPos.xyz / lPos.w, bias
#ifdef _ShadowMapTransparent
, transparent
#endif
);
#endif
#ifdef _Clusters
vec4 lPos = LWVPSpotArray[index] * vec4(p + n * bias * 2, 1.0);
#ifdef _ShadowMapAtlas
direct *= shadowTest(
#ifdef _ShadowMapTransparent
#ifndef _SingleAtlas
shadowMapAtlasSpot, shadowMapAtlasSpotTransparent
#else
shadowMapAtlas, shadowMapAtlasTransparent
#endif
#else
#ifndef _SingleAtlas
shadowMapAtlasSpot
#else
shadowMapAtlas
#endif
#endif
, lPos.xyz / lPos.w, bias
#ifdef _ShadowMapTransparent
, transparent
#endif
);
#else
if (index == 0) direct *= shadowTest(shadowMapSpot[0],
#ifdef _ShadowMapTransparent
shadowMapSpotTransparent[0],
#endif
lPos.xyz / lPos.w, bias
#ifdef _ShadowMapTransparent
, transparent
#endif
);
else if (index == 1) direct *= shadowTest(shadowMapSpot[1],
#ifdef _ShadowMapTransparent
shadowMapSpotTransparent[1],
#endif
lPos.xyz / lPos.w, bias
#ifdef _ShadowMapTransparent
, transparent
#endif
);
else if (index == 2) direct *= shadowTest(shadowMapSpot[2],
#ifdef _ShadowMapTransparent
shadowMapSpotTransparent[2],
#endif
lPos.xyz / lPos.w, bias
#ifdef _ShadowMapTransparent
, transparent
#endif
);
else if (index == 3) direct *= shadowTest(shadowMapSpot[3],
#ifdef _ShadowMapTransparent
shadowMapSpotTransparent[3],
#endif
lPos.xyz / lPos.w, bias
#ifdef _ShadowMapTransparent
, transparent
#endif
);
#endif
#endif
}
#endif
return direct;
}
#endif
#ifdef _LightIES
direct *= iesAttenuation(-l);
#endif
#ifdef _ShadowMap
if (receiveShadow) {
#ifdef _SinglePoint
#ifndef _Spot
direct *= PCFCube(shadowMapPoint[0],
#ifdef _ShadowMapTransparent
shadowMapPointTransparent[0],
#endif
ld, -l, bias, lightProj, n
#ifdef _ShadowMapTransparent
, transparent
#endif
);
#endif
#endif
#ifdef _Clusters
#ifdef _ShadowMapAtlas
direct *= PCFFakeCube(
#ifdef _ShadowMapTransparent
#ifndef _SingleAtlas
shadowMapAtlasPoint, shadowMapAtlasPointTransparent
#else
shadowMapAtlas, shadowMapAtlasTransparent
#endif
#else
#ifndef _SingleAtlas
shadowMapAtlasPoint
#else
shadowMapAtlas
#endif
#endif
, ld, -l, bias, lightProj, n, index
#ifdef _ShadowMapTransparent
, transparent
#endif
);
#else
if (index == 0) direct *= PCFCube(shadowMapPoint[0],
#ifdef _ShadowMapTransparent
shadowMapPointTransparent[0],
#endif
ld, -l, bias, lightProj, n
#ifdef _ShadowMapTransparent
, transparent
#endif
);
else if (index == 1) direct *= PCFCube(shadowMapPoint[1],
#ifdef _ShadowMapTransparent
shadowMapPointTransparent[1],
#endif
ld, -l, bias, lightProj, n
#ifdef _ShadowMapTransparent
, transparent
#endif
);
else if (index == 2) direct *= PCFCube(shadowMapPoint[2],
#ifdef _ShadowMapTransparent
shadowMapPointTransparent[2],
#endif
ld, -l, bias, lightProj, n
#ifdef _ShadowMapTransparent
, transparent
#endif
);
else if (index == 3) direct *= PCFCube(shadowMapPoint[3],
#ifdef _ShadowMapTransparent
shadowMapPointTransparent[3],
#endif
ld, -l, bias, lightProj, n
#ifdef _ShadowMapTransparent
, transparent
#endif
);
#endif
#endif
}
#endif
return direct;
}
#ifdef _VoxelGI
vec3 sampleLightVoxels(const vec3 p, const vec3 n, const vec3 v, const float dotNV, const vec3 lp, const vec3 lightCol,
const vec3 albedo, const float rough, const float spec, const vec3 f0
#ifdef _ShadowMap
, int index, float bias, bool receiveShadow
#ifdef _ShadowMapTransparent
, bool transparent
#endif
#endif
#ifdef _Spot
, const bool isSpot, const float spotSize, float spotBlend, vec3 spotDir, vec2 scale, vec3 right
#endif
) {
vec3 ld = lp - p;
vec3 l = normalize(ld);
vec3 h = normalize(v + l);
float dotNH = max(0.0, dot(n, h));
float dotVH = max(0.0, dot(v, h));
float dotNL = max(0.0, dot(n, l));
#ifdef _LTC
float theta = acos(dotNV);
vec2 tuv = vec2(rough, theta / (0.5 * PI));
tuv = tuv * LUT_SCALE + LUT_BIAS;
vec4 t = textureLod(sltcMat, tuv, 0.0);
mat3 invM = mat3(
vec3(1.0, 0.0, t.y),
vec3(0.0, t.z, 0.0),
vec3(t.w, 0.0, t.x));
float ltcspec = ltcEvaluate(n, v, dotNV, p, invM, lightArea0, lightArea1, lightArea2, lightArea3);
ltcspec *= textureLod(sltcMag, tuv, 0.0).a;
float ltcdiff = ltcEvaluate(n, v, dotNV, p, mat3(1.0), lightArea0, lightArea1, lightArea2, lightArea3);
vec3 direct = albedo * ltcdiff + ltcspec * spec * 0.05;
#else
vec3 direct = lambertDiffuseBRDF(albedo, dotNL) +
specularBRDF(f0, rough, dotNL, dotNH, dotNV, dotVH) * spec;
#endif
direct *= attenuate(distance(p, lp));
// CRITICAL: Clamp light color to prevent extreme HDR values causing white sphere artifacts
direct *= min(lightCol, vec3(100.0));
#ifdef _LTC
#ifdef _ShadowMap
if (receiveShadow) {
#ifdef _SinglePoint
vec4 lPos = LWVPSpot[0] * vec4(p + n * bias * 2, 1.0);
direct *= shadowTest(shadowMapSpot[0],
#ifdef _ShadowMapTransparent
shadowMapSpotTransparent[0],
#endif
lPos.xyz / lPos.w, bias
#ifdef _ShadowMapTransparent
, transparent
#endif
);
#endif
#ifdef _Clusters
vec4 lPos = LWVPSpot[index] * vec4(p + n * bias * 2, 1.0);
if (index == 0) direct *= shadowTest(shadowMapSpot[0],
#ifdef _ShadowMapTransparent
shadowMapSpotTransparent[0],
#endif
lPos.xyz / lPos.w, bias
#ifdef _ShadowMapTransparent
, transparent
#endif
);
else if (index == 1) direct *= shadowTest(shadowMapSpot[1],
#ifdef _ShadowMapTransparent
shadowMapSpotTransparent[1],
#endif
lPos.xyz / lPos.w, bias
#ifdef _ShadowMapTransparent
, transparent
#endif
);
else if (index == 2) direct *= shadowTest(shadowMapSpot[2],
#ifdef _ShadowMapTransparent
shadowMapSpotTransparent[2],
#endif
lPos.xyz / lPos.w, bias
#ifdef _ShadowMapTransparent
, transparent
#endif
);
else if (index == 3) direct *= shadowTest(shadowMapSpot[3],
#ifdef _ShadowMapTransparent
shadowMapSpotTransparent[3],
#endif
lPos.xyz / lPos.w, bias
#ifdef _ShadowMapTransparent
, transparent
#endif
);
#endif
}
#endif
return direct;
#endif
#ifdef _Spot
if (isSpot) {
direct *= spotlightMask(l, spotDir, right, scale, spotSize, spotBlend);
#ifdef _ShadowMap
if (receiveShadow) {
#ifdef _SinglePoint
vec4 lPos = LWVPSpotArray[0] * vec4(p + n * bias * 2, 1.0);
direct *= shadowTest(shadowMapSpot[0],
#ifdef _ShadowMapTransparent
shadowMapSpotTransparent[0],
#endif
lPos.xyz / lPos.w, bias
#ifdef _ShadowMapTransparent
, transparent
#endif
);
#endif
#ifdef _Clusters
vec4 lPos = LWVPSpotArray[index] * vec4(p + n * bias * 2, 1.0);
#ifdef _ShadowMapAtlas
direct *= shadowTest(
#ifdef _ShadowMapTransparent
#ifndef _SingleAtlas
shadowMapAtlasSpot, shadowMapAtlasSpotTransparent
#else
shadowMapAtlas, shadowMapAtlasTransparent
#endif
#else
#ifndef _SingleAtlas
shadowMapAtlasSpot
#else
shadowMapAtlas
#endif
#endif
, lPos.xyz / lPos.w, bias
#ifdef _ShadowMapTransparent
, transparent
#endif
);
#else
if (index == 0) direct *= shadowTest(shadowMapSpot[0],
#ifdef _ShadowMapTransparent
shadowMapSpotTransparent[0],
#endif
lPos.xyz / lPos.w, bias
#ifdef _ShadowMapTransparent
, transparent
#endif
);
else if (index == 1) direct *= shadowTest(shadowMapSpot[1],
#ifdef _ShadowMapTransparent
shadowMapSpotTransparent[1],
#endif
lPos.xyz / lPos.w, bias
#ifdef _ShadowMapTransparent
, transparent
#endif
);
else if (index == 2) direct *= shadowTest(shadowMapSpot[2],
#ifdef _ShadowMapTransparent
shadowMapSpotTransparent[2],
#endif
lPos.xyz / lPos.w, bias
#ifdef _ShadowMapTransparent
, transparent
#endif
);
else if (index == 3) direct *= shadowTest(shadowMapSpot[3],
#ifdef _ShadowMapTransparent
shadowMapSpotTransparent[3],
#endif
lPos.xyz / lPos.w, bias
#ifdef _ShadowMapTransparent
, transparent
#endif
);
#endif
#endif
}
#endif
return direct;
}
#endif
#ifdef _LightIES
direct *= iesAttenuation(-l);
#endif
#ifdef _ShadowMap
if (receiveShadow) {
#ifdef _SinglePoint
#ifndef _Spot
direct *= PCFCube(shadowMapPoint[0],
#ifdef _ShadowMapTransparent
shadowMapPointTransparent[0],
#endif
ld, -l, bias, lightProj, n
#ifdef _ShadowMapTransparent
, transparent
#endif
);
#endif
#endif
#ifdef _Clusters
#ifdef _ShadowMapAtlas
direct *= PCFFakeCube(
#ifdef _ShadowMapTransparent
#ifndef _SingleAtlas
shadowMapAtlasPoint, shadowMapAtlasPointTransparent
#else
shadowMapAtlas, shadowMapAtlasTransparent
#endif
#else
#ifndef _SingleAtlas
shadowMapAtlasPoint
#else
shadowMapAtlas
#endif
#endif
, ld, -l, bias, lightProj, n, index
#ifdef _ShadowMapTransparent
, transparent
#endif
);
#else
if (index == 0) direct *= PCFCube(shadowMapPoint[0],
#ifdef _ShadowMapTransparent
shadowMapPointTransparent[0],
#endif
ld, -l, bias, lightProj, n
#ifdef _ShadowMapTransparent
, transparent
#endif
);
else if (index == 1) direct *= PCFCube(shadowMapPoint[1],
#ifdef _ShadowMapTransparent
shadowMapPointTransparent[1],
#endif
ld, -l, bias, lightProj, n
#ifdef _ShadowMapTransparent
, transparent
#endif
);
else if (index == 2) direct *= PCFCube(shadowMapPoint[2],
#ifdef _ShadowMapTransparent
shadowMapPointTransparent[2],
#endif
ld, -l, bias, lightProj, n
#ifdef _ShadowMapTransparent
, transparent
#endif
);
else if (index == 3) direct *= PCFCube(shadowMapPoint[3],
#ifdef _ShadowMapTransparent
shadowMapPointTransparent[3],
#endif
ld, -l, bias, lightProj, n
#ifdef _ShadowMapTransparent
, transparent
#endif
);
#endif
#endif
}
#endif
return direct;
}
#endif
#endif

View File

@ -5,6 +5,7 @@ uniform vec2 morphDataDim;
uniform vec4 morphWeights[8];
void getMorphedVertex(vec2 uvCoord, inout vec3 A){
vec3 totalDelta = vec3(0.0);
for(int i = 0; i<8; i++ )
{
vec4 tempCoordY = vec4( uvCoord.y - (i * 4) * morphDataDim.y,
@ -13,21 +14,28 @@ void getMorphedVertex(vec2 uvCoord, inout vec3 A){
uvCoord.y - (i * 4 + 3) * morphDataDim.y);
vec3 morph = texture(morphDataPos, vec2(uvCoord.x, tempCoordY.x)).rgb * morphScaleOffset.x + morphScaleOffset.y;
A += morphWeights[i].x * morph;
totalDelta += morphWeights[i].x * morph;
morph = texture(morphDataPos, vec2(uvCoord.x, tempCoordY.y)).rgb * morphScaleOffset.x + morphScaleOffset.y;
A += morphWeights[i].y * morph;
totalDelta += morphWeights[i].y * morph;
morph = texture(morphDataPos, vec2(uvCoord.x, tempCoordY.z)).rgb * morphScaleOffset.x + morphScaleOffset.y;
A += morphWeights[i].z * morph;
totalDelta += morphWeights[i].z * morph;
morph = texture(morphDataPos, vec2(uvCoord.x, tempCoordY.w)).rgb * morphScaleOffset.x + morphScaleOffset.y;
A += morphWeights[i].w * morph;
totalDelta += morphWeights[i].w * morph;
}
//float deltaLength = length(totalDelta);
//if (deltaLength > 5.0) {
// clamp corrupted data
//totalDelta = normalize(totalDelta) * 5.0;
//}
A += totalDelta;
}
void getMorphedNormal(vec2 uvCoord, vec3 oldNor, inout vec3 morphNor){
vec3 normalDelta = vec3(0.0);
for(int i = 0; i<8; i++ )
{
vec4 tempCoordY = vec4( uvCoord.y - (i * 4) * morphDataDim.y,
@ -35,19 +43,11 @@ void getMorphedNormal(vec2 uvCoord, vec3 oldNor, inout vec3 morphNor){
uvCoord.y - (i * 4 + 2) * morphDataDim.y,
uvCoord.y - (i * 4 + 3) * morphDataDim.y);
vec3 norm = oldNor + morphWeights[i].x * (texture(morphDataNor, vec2(uvCoord.x, tempCoordY.x)).rgb * 2.0 - 1.0);
morphNor += norm;
norm = oldNor + morphWeights[i].y * (texture(morphDataNor, vec2(uvCoord.x, tempCoordY.y)).rgb * 2.0 - 1.0);
morphNor += norm;
norm = oldNor + morphWeights[i].z * (texture(morphDataNor, vec2(uvCoord.x, tempCoordY.z)).rgb * 2.0 - 1.0);
morphNor += norm;
norm = oldNor + morphWeights[i].w * (texture(morphDataNor, vec2(uvCoord.x, tempCoordY.w)).rgb * 2.0 - 1.0);
morphNor += norm;
normalDelta += morphWeights[i].x * (texture(morphDataNor, vec2(uvCoord.x, tempCoordY.x)).rgb * 2.0 - 1.0);
normalDelta += morphWeights[i].y * (texture(morphDataNor, vec2(uvCoord.x, tempCoordY.y)).rgb * 2.0 - 1.0);
normalDelta += morphWeights[i].z * (texture(morphDataNor, vec2(uvCoord.x, tempCoordY.z)).rgb * 2.0 - 1.0);
normalDelta += morphWeights[i].w * (texture(morphDataNor, vec2(uvCoord.x, tempCoordY.w)).rgb * 2.0 - 1.0);
}
morphNor = normalize(morphNor);
morphNor = normalize(oldNor + normalDelta);
}

View File

@ -0,0 +1 @@
<EFBFBD><EFBFBD>shader_datas<EFBFBD><EFBFBD><EFBFBD>name<EFBFBD>copy_pass<EFBFBD>contexts<EFBFBD><EFBFBD><EFBFBD>name<EFBFBD>copy_pass<EFBFBD>constants<EFBFBD><EFBFBD>texture_units<EFBFBD><EFBFBD><EFBFBD>name<EFBFBD>tex<EFBFBD>vertex_elements<EFBFBD><EFBFBD><EFBFBD>data<EFBFBD>float2<EFBFBD>name<EFBFBD>pos<EFBFBD>vertex_shader<EFBFBD>pass.vert<72>fragment_shader<65>pass_copy.frag<61>depth_write¬compare_mode<64>always<79>cull_mode<64>none<6E><65>name<6D>compositor_pass<73>contexts<74><73><EFBFBD>name<6D>compositor_pass<73>constants<74><73>texture_units<74><73><EFBFBD>name<6D>tex<65>vertex_elements<74><73><EFBFBD>data<74>float2<74>name<6D>pos<6F>vertex_shader<65>compositor_pass.vert<72>fragment_shader<65>compositor_pass.frag<61>depth_write¬compare_mode<64>always<79>cull_mode<64>none<6E><65>name<6D>deferred_light<68>contexts<74><73><EFBFBD>name<6D>deferred_light<68>constants<74><73><EFBFBD>type<70>mat4<74>name<6D>invVP<56>link<6E>_inverseViewProjectionMatrix<69><78>type<70>vec3<63>name<6D>eye<79>link<6E>_cameraPosition<6F><6E>type<70>float<61>name<6D>envmapStrength<74>link<6E>_envmapStrength<74><68>type<70>floats<74>name<6D>shirr<72>link<6E>_envmapIrradiance<63><65>type<70>int<6E>name<6D>envmapNumMipmaps<70>link<6E>_envmapNumMipmaps<70><73>type<70>vec2<63>name<6D>cameraProj<6F>link<6E>_cameraPlaneProj<6F><6A>type<70>vec3<63>name<6D>eyeLook<6F>link<6E>_cameraLook<6F><6B>type<70>vec2<63>name<6D>lightProj<6F>link<6E>_lightPlaneProj<6F><6A>type<70>vec3<63>name<6D>pointPos<6F>link<6E>_pointPosition<6F><6E>type<70>vec3<63>name<6D>pointCol<6F>link<6E>_pointColor<6F><72>type<70>float<61>name<6D>pointBias<61>link<6E>_pointShadowsBias<61>texture_units<74><73><EFBFBD>name<6D>gbufferD<72><44>name<6D>gbuffer0<72><30>name<6D>gbuffer1<72><31>name<6D>senvmapBrdf<64>link<6E>$brdf.png<6E><67>name<6D>senvmapRadiance<63>link<6E>_envmapRadiance<63><65>name<6D>shadowMapPoint[0]<5D>vertex_elements<74><73><EFBFBD>data<74>float2<74>name<6D>pos<6F>vertex_shader<65>pass_viewray.vert<72>fragment_shader<65>deferred_light.frag<61>color_attachments<74><73>RGBA64<36>depth_write¬compare_mode<64>always<79>cull_mode<64>none<6E><65>name<6D>water_pass<73>contexts<74><73><EFBFBD>name<6D>water_pass<73>constants<74><73><EFBFBD>type<70>mat4<74>name<6D>invVP<56><50>type<70>vec3<63>name<6D>eye<79>link<6E>_cameraPosition<6F><6E>type<70>float<61>name<6D>time<6D>link<6E>_time<6D><65>type<70>float<61>name<6D>holoOverallStrength<74><68>type<70>vec2<63>name<6D>cameraProj<6F>link<6E>_cameraPlaneProj<6F><6A>type<70>vec3<63>name<6D>eyeLook<6F>link<6E>_cameraLook<6F><6B>type<70>vec3<63>name<6D>ld<6C>link<6E>_lightDirection<6F>texture_units<74><73><EFBFBD>name<6D>tex<65><78>name<6D>gbufferD<72><44>name<6D>gbuffer0<72>vertex_elements<74><73><EFBFBD>data<74>float2<74>name<6D>pos<6F>vertex_shader<65>pass_viewray.vert<72>fragment_shader<65>water_pass.frag<61>depth_write¬compare_mode<64>always<79>cull_mode<64>none<6E>blend_source<63>source_alpha<68>blend_destination<6F>inverse_source_alpha<68>blend_operation<6F>add<64>alpha_blend_source<63>blend_one<6E>alpha_blend_destination<6F>blend_one<6E>alpha_blend_operation<6F>add<64><64>contexts<74><73><EFBFBD>name<6D>World_World<6C>depth_write¬compare_mode<64>less<73>cull_mode<64>clockwise<73>vertex_elements<74><73><EFBFBD>name<6D>pos<6F>data<74>float3<74><33>name<6D>nor<6F>data<74>float3<74>color_attachments<74><73>_HDR<44>texture_units<74><73>constants<74><73><EFBFBD>name<6D>SMVP<56>type<70>mat4<74>link<6E>_skydomeMatrix<69>vertex_shader<65>World_World.vert<72>fragment_shader<65>World_World.frag<61>name<6D>World_World

View File

@ -23,6 +23,59 @@ uniform vec2 smSizeUniform;
#endif
#ifdef _ShadowMapAtlas
// PCF that clamps samples to tile boundaries to prevent bleeding
vec3 PCFTileAware(sampler2DShadow shadowMap,
#ifdef _ShadowMapTransparent
sampler2D shadowMapTransparent,
#endif
const vec2 uv, const float compare, const vec2 smSize,
const vec2 tileMin, const vec2 tileMax
#ifdef _ShadowMapTransparent
, const bool transparent
#endif
) {
vec3 result = vec3(0.0);
vec2 offset;
offset = vec2(-1.0, -1.0) / smSize;
result.x = texture(shadowMap, vec3(clamp(uv + offset, tileMin, tileMax), compare));
offset = vec2(-1.0, 0.0) / smSize;
result.x += texture(shadowMap, vec3(clamp(uv + offset, tileMin, tileMax), compare));
offset = vec2(-1.0, 1.0) / smSize;
result.x += texture(shadowMap, vec3(clamp(uv + offset, tileMin, tileMax), compare));
offset = vec2(0.0, -1.0) / smSize;
result.x += texture(shadowMap, vec3(clamp(uv + offset, tileMin, tileMax), compare));
result.x += texture(shadowMap, vec3(uv, compare));
offset = vec2(0.0, 1.0) / smSize;
result.x += texture(shadowMap, vec3(clamp(uv + offset, tileMin, tileMax), compare));
offset = vec2(1.0, -1.0) / smSize;
result.x += texture(shadowMap, vec3(clamp(uv + offset, tileMin, tileMax), compare));
offset = vec2(1.0, 0.0) / smSize;
result.x += texture(shadowMap, vec3(clamp(uv + offset, tileMin, tileMax), compare));
offset = vec2(1.0, 1.0) / smSize;
result.x += texture(shadowMap, vec3(clamp(uv + offset, tileMin, tileMax), compare));
result = result.xxx / 9.0;
#ifdef _ShadowMapTransparent
if (transparent == false) {
vec4 shadowmap_transparent = texture(shadowMapTransparent, uv);
if (shadowmap_transparent.a < compare)
result *= shadowmap_transparent.rgb;
}
#endif
return result;
}
// https://www.khronos.org/registry/OpenGL/specs/gl/glspec20.pdf // p:168
// https://www.gamedev.net/forums/topic/687535-implementing-a-cube-map-lookup-function/5337472/
vec2 sampleCube(vec3 dir, out int faceIndex) {
@ -58,7 +111,15 @@ vec2 sampleCube(vec3 dir, out int faceIndex) {
}
#endif
vec3 PCF(sampler2DShadow shadowMap, sampler2D shadowMapTransparent, const vec2 uv, const float compare, const vec2 smSize, const bool transparent) {
vec3 PCF(sampler2DShadow shadowMap,
#ifdef _ShadowMapTransparent
sampler2D shadowMapTransparent,
#endif
const vec2 uv, const float compare, const vec2 smSize
#ifdef _ShadowMapTransparent
, const bool transparent
#endif
) {
vec3 result = vec3(0.0);
result.x = texture(shadowMap, vec3(uv + (vec2(-1.0, -1.0) / smSize), compare));
result.x += texture(shadowMap, vec3(uv + (vec2(-1.0, 0.0) / smSize), compare));
@ -71,11 +132,13 @@ vec3 PCF(sampler2DShadow shadowMap, sampler2D shadowMapTransparent, const vec2 u
result.x += texture(shadowMap, vec3(uv + (vec2(1.0, 1.0) / smSize), compare));
result = result.xxx / 9.0;
#ifdef _ShadowMapTransparent
if (transparent == false) {
vec4 shadowmap_transparent = texture(shadowMapTransparent, uv);
if (shadowmap_transparent.a < compare)
result *= shadowmap_transparent.rgb;
}
#endif
return result;
}
@ -87,41 +150,15 @@ float lpToDepth(vec3 lp, const vec2 lightProj) {
return zcomp * 0.5 + 0.5;
}
#ifndef _ShadowMapAtlas
vec3 PCFCube(samplerCubeShadow shadowMapCube, samplerCube shadowMapCubeTransparent, vec3 lp, vec3 ml, float bias, vec2 lightProj, vec3 n, const bool transparent) {
const float s = shadowmapCubePcfSize;
float compare = lpToDepth(lp, lightProj) - bias * 1.5;
ml = ml + n * bias * 20;
#ifdef _InvY
ml.y = -ml.y;
#endif
float shadowFactor = 0.0;
shadowFactor = texture(shadowMapCube, vec4(ml, compare));
shadowFactor += texture(shadowMapCube, vec4(ml + vec3(s, s, s), compare));
shadowFactor += texture(shadowMapCube, vec4(ml + vec3(-s, s, s), compare));
shadowFactor += texture(shadowMapCube, vec4(ml + vec3(s, -s, s), compare));
shadowFactor += texture(shadowMapCube, vec4(ml + vec3(s, s, -s), compare));
shadowFactor += texture(shadowMapCube, vec4(ml + vec3(-s, -s, s), compare));
shadowFactor += texture(shadowMapCube, vec4(ml + vec3(s, -s, -s), compare));
shadowFactor += texture(shadowMapCube, vec4(ml + vec3(-s, s, -s), compare));
shadowFactor += texture(shadowMapCube, vec4(ml + vec3(-s, -s, -s), compare));
shadowFactor /= 9.0;
vec3 result = vec3(shadowFactor);
if (transparent == false) {
vec4 shadowmap_transparent = texture(shadowMapCubeTransparent, ml);
if (shadowmap_transparent.a < compare)
result *= shadowmap_transparent.rgb;
}
return result;
}
#endif
#ifdef _ShadowMapAtlas
vec3 PCFCube(samplerCubeShadow shadowMapCube, samplerCube shadowMapCubeTransparent, const vec3 lp, vec3 ml, const float bias, const vec2 lightProj, const vec3 n, const bool transparent) {
vec3 PCFCube(samplerCubeShadow shadowMapCube,
#ifdef _ShadowMapTransparent
samplerCube shadowMapCubeTransparent,
#endif
const vec3 lp, vec3 ml, const float bias, const vec2 lightProj, const vec3 n
#ifdef _ShadowMapTransparent
, const bool transparent
#endif
) {
const float s = shadowmapCubePcfSize; // TODO: incorrect...
float compare = lpToDepth(lp, lightProj) - bias * 1.5;
ml = ml + n * bias * 20;
@ -140,16 +177,18 @@ vec3 PCFCube(samplerCubeShadow shadowMapCube, samplerCube shadowMapCubeTranspare
result.x += texture(shadowMapCube, vec4(ml + vec3(-s, -s, -s), compare));
result = result.xxx / 9.0;
#ifdef _ShadowMapTransparent
if (transparent == false) {
vec4 shadowmap_transparent = texture(shadowMapCubeTransparent, ml);
if (shadowmap_transparent.a < compare)
result *= shadowmap_transparent.rgb;
}
#endif
return result;
}
#ifdef _ShadowMapAtlas
// transform "out-of-bounds" coordinates to the correct face/coordinate system
// https://www.khronos.org/opengl/wiki/File:CubeMapAxes.png
vec2 transformOffsetedUV(const int faceIndex, out int newFaceIndex, vec2 uv) {
@ -243,21 +282,31 @@ vec2 transformOffsetedUV(const int faceIndex, out int newFaceIndex, vec2 uv) {
return uv;
}
vec3 PCFFakeCube(sampler2DShadow shadowMap, sampler2D shadowMapTransparent, const vec3 lp, vec3 ml, const float bias, const vec2 lightProj, const vec3 n, const int index, const bool transparent) {
vec3 PCFFakeCube(sampler2DShadow shadowMap,
#ifdef _ShadowMapTransparent
sampler2D shadowMapTransparent,
#endif
const vec3 lp, vec3 ml, const float bias, const vec2 lightProj, const vec3 n, const int index
#ifdef _ShadowMapTransparent
, const bool transparent
#endif
) {
const vec2 smSize = smSizeUniform; // TODO: incorrect...
const float compare = lpToDepth(lp, lightProj) - bias * 1.5;
ml = ml + n * bias * 20;
int faceIndex = 0;
const int lightIndex = index * 6;
const vec2 uv = sampleCube(ml, faceIndex);
vec4 pointLightTile = pointLightDataArray[lightIndex + faceIndex]; // x: tile X offset, y: tile Y offset, z: tile size relative to atlas
vec2 uvtiled = pointLightTile.z * uv + pointLightTile.xy;
#ifdef _FlipY
uvtiled.y = 1.0 - uvtiled.y; // invert Y coordinates for direct3d coordinate system
#endif
if (any(lessThan(uvtiled, vec2(0.0))) || any(greaterThan(uvtiled, vec2(1.0)))) {
return vec3(1.0);
}
vec3 result = vec3(0.0);
result.x += texture(shadowMap, vec3(uvtiled, compare));
// soft shadowing
@ -270,14 +319,6 @@ vec3 PCFFakeCube(sampler2DShadow shadowMap, sampler2D shadowMapTransparent, cons
#endif
result.x += texture(shadowMap, vec3(uvtiled, compare));
uvtiled = transformOffsetedUV(faceIndex, newFaceIndex, vec2(uv + (vec2(-1.0, 1.0) / smSize)));
pointLightTile = pointLightDataArray[lightIndex + newFaceIndex];
uvtiled = pointLightTile.z * uvtiled + pointLightTile.xy;
#ifdef _FlipY
uvtiled.y = 1.0 - uvtiled.y; // invert Y coordinates for direct3d coordinate system
#endif
result.x += texture(shadowMap, vec3(uvtiled, compare));
uvtiled = transformOffsetedUV(faceIndex, newFaceIndex, vec2(uv + (vec2(0.0, -1.0) / smSize)));
pointLightTile = pointLightDataArray[lightIndex + newFaceIndex];
uvtiled = pointLightTile.z * uvtiled + pointLightTile.xy;
@ -334,30 +375,72 @@ vec3 PCFFakeCube(sampler2DShadow shadowMap, sampler2D shadowMapTransparent, cons
uvtiled.y = 1.0 - uvtiled.y; // invert Y coordinates for direct3d coordinate system
#endif
#ifdef _ShadowMapTransparent
if (transparent == false) {
vec4 shadowmap_transparent = texture(shadowMapTransparent, uvtiled);
if (shadowmap_transparent.a < compare)
result *= shadowmap_transparent.rgb;
}
#endif
return result;
}
#endif
vec3 shadowTest(sampler2DShadow shadowMap, sampler2D shadowMapTransparent, const vec3 lPos, const float shadowsBias, const bool transparent) {
#ifdef _ShadowMapAtlas
uniform vec4 tileBounds;
#endif
vec3 shadowTest(sampler2DShadow shadowMap,
#ifdef _ShadowMapTransparent
sampler2D shadowMapTransparent,
#endif
const vec3 lPos, const float shadowsBias
#ifdef _ShadowMapTransparent
, const bool transparent
#endif
) {
if (lPos.x < 0.0 || lPos.y < 0.0 || lPos.x > 1.0 || lPos.y > 1.0) return vec3(1.0);
#ifdef _ShadowMapAtlas
// use tile PCF
#ifdef _SMSizeUniform
vec2 smSizeAtlas = smSizeUniform;
#else
const vec2 smSizeAtlas = shadowmapSize;
#endif
return PCFTileAware(shadowMap,
#ifdef _ShadowMapTransparent
shadowMapTransparent,
#endif
lPos.xy, lPos.z - shadowsBias, smSizeAtlas,
tileBounds.xy, tileBounds.zw
#ifdef _ShadowMapTransparent
, transparent
#endif
);
#else
// use PCF for non-atlas shadows
#ifdef _SMSizeUniform
vec2 smSize = smSizeUniform;
#else
const vec2 smSize = shadowmapSize;
#endif
if (lPos.x < 0.0 || lPos.y < 0.0 || lPos.x > 1.0 || lPos.y > 1.0) return vec3(1.0);
return PCF(shadowMap, shadowMapTransparent, lPos.xy, lPos.z - shadowsBias, smSize, transparent);
return PCF(shadowMap,
#ifdef _ShadowMapTransparent
shadowMapTransparent,
#endif
lPos.xy, lPos.z - shadowsBias, smSize
#ifdef _ShadowMapTransparent
, transparent
#endif
);
#endif
}
#ifdef _CSM
mat4 getCascadeMat(const float d, out int casi, out int casIndex) {
const int c = shadowmapCascades;
// Get cascade index
// TODO: use bounding box slice selection instead of sphere
const vec4 ci = vec4(float(c > 0), float(c > 1), float(c > 2), float(c > 3));
@ -373,21 +456,26 @@ mat4 getCascadeMat(const float d, out int casi, out int casIndex) {
float(d > casData[c * 4].z),
float(d > casData[c * 4].w));
casi = int(min(dot(ci, comp), c));
// Get cascade mat
casIndex = casi * 4;
return mat4(
casData[casIndex ],
casData[casIndex + 1],
casData[casIndex + 2],
casData[casIndex + 3]);
// if (casIndex == 0) return mat4(casData[0], casData[1], casData[2], casData[3]);
// ..
}
vec3 shadowTestCascade(sampler2DShadow shadowMap, sampler2D shadowMapTransparent, const vec3 eye, const vec3 p, const float shadowsBias, const bool transparent) {
vec3 shadowTestCascade(sampler2DShadow shadowMap,
#ifdef _ShadowMapTransparent
sampler2D shadowMapTransparent,
#endif
const vec3 eye, const vec3 p, const float shadowsBias
#ifdef _ShadowMapTransparent
, const bool transparent
#endif
) {
#ifdef _SMSizeUniform
vec2 smSize = smSizeUniform;
#else
@ -395,16 +483,22 @@ vec3 shadowTestCascade(sampler2DShadow shadowMap, sampler2D shadowMapTransparent
#endif
const int c = shadowmapCascades;
float d = distance(eye, p);
int casi;
int casIndex;
mat4 LWVP = getCascadeMat(d, casi, casIndex);
vec4 lPos = LWVP * vec4(p, 1.0);
lPos.xyz /= lPos.w;
vec3 visibility = vec3(1.0);
if (lPos.w > 0.0) visibility = PCF(shadowMap, shadowMapTransparent, lPos.xy, lPos.z - shadowsBias, smSize, transparent);
if (lPos.w > 0.0) visibility = PCF(shadowMap,
#ifdef _ShadowMapTransparent
shadowMapTransparent,
#endif
lPos.xy, lPos.z - shadowsBias, smSize
#ifdef _ShadowMapTransparent
, transparent
#endif
);
// Blend cascade
// https://github.com/TheRealMJP/Shadows
@ -423,13 +517,21 @@ vec3 shadowTestCascade(sampler2DShadow shadowMap, sampler2D shadowMapTransparent
vec4 lPos2 = LWVP2 * vec4(p, 1.0);
lPos2.xyz /= lPos2.w;
vec3 visibility2 = vec3(1.0);
if (lPos2.w > 0.0) visibility2 = PCF(shadowMap, shadowMapTransparent, lPos2.xy, lPos2.z - shadowsBias, smSize, transparent);
// use lPos2 coordinates for second cascade, not lPos
if (lPos2.w > 0.0) visibility2 = PCF(shadowMap,
#ifdef _ShadowMapTransparent
shadowMapTransparent,
#endif
lPos2.xy, lPos2.z - shadowsBias, smSize
#ifdef _ShadowMapTransparent
, transparent
#endif
);
float lerpAmt = smoothstep(0.0, blendThres, splitDist);
return mix(visibility2, visibility, lerpAmt);
}
return visibility;
// Visualize cascades
// if (ci == 0) albedo.rgb = vec3(1.0, 0.0, 0.0);
// if (ci == 4) albedo.rgb = vec3(0.0, 1.0, 0.0);
@ -437,4 +539,4 @@ vec3 shadowTestCascade(sampler2DShadow shadowMap, sampler2D shadowMapTransparent
// if (ci == 12) albedo.rgb = vec3(1.0, 1.0, 0.0);
}
#endif
#endif
#endif

View File

@ -13,32 +13,80 @@ out vec4 fragColor;
const float SMAA_REPROJECTION_WEIGHT_SCALE = 30.0;
// TODO: Move to a utility
bool isInvalidValue(float v) {
return (v != v) || (v > 65000.0) || (v < -65000.0);
}
bool hasInvalidValues(vec3 v) {
return isInvalidValue(v.x) || isInvalidValue(v.y) || isInvalidValue(v.z);
}
void main() {
vec4 current = textureLod(tex, texCoord, 0.0);
current.rgb = clamp(current.rgb, vec3(0.0), vec3(10.0));
current.a = clamp(current.a, 0.0, 1.0);
if (hasInvalidValues(current.rgb)) {
current = vec4(0.0, 0.0, 0.0, 1.0);
}
#ifdef _Veloc
// Velocity is assumed to be calculated for motion blur, so we need to inverse it for reprojection
vec2 velocity = -textureLod(sveloc, texCoord, 0.0).rg;
velocity = clamp(velocity, vec2(-1.0), vec2(1.0));
if (isInvalidValue(velocity.x) || isInvalidValue(velocity.y)) {
velocity = vec2(0.0);
}
#ifdef _InvY
velocity.y = -velocity.y;
#endif
// Reproject current coordinates and fetch previous pixel
vec4 previous = textureLod(tex2, texCoord + velocity, 0.0);
vec2 prevCoord = texCoord + velocity;
prevCoord = clamp(prevCoord, vec2(0.0), vec2(1.0));
vec4 previous = textureLod(tex2, prevCoord, 0.0);
previous.rgb = clamp(previous.rgb, vec3(0.0), vec3(10.0));
previous.a = clamp(previous.a, 0.0, 1.0);
if (hasInvalidValues(previous.rgb)) {
previous = current; // Fallback to current frame if previous is corrupted
}
// Attenuate the previous pixel if the velocity is different
#ifdef _SMAA
float delta = abs(current.a * current.a - previous.a * previous.a) / 5.0;
float currentAlpha = clamp(current.a, 0.0, 1.0);
float previousAlpha = clamp(previous.a, 0.0, 1.0);
float delta = abs(currentAlpha * currentAlpha - previousAlpha * previousAlpha) / 5.0;
delta = clamp(delta, 0.0, 1.0); // Ensure delta is in valid range
#else
const float delta = 0.0;
#endif
float weight = 0.5 * clamp(1.0 - sqrt(delta) * SMAA_REPROJECTION_WEIGHT_SCALE, 0.0, 1.0);
float weight = 0.5 * clamp(1.0 - sqrt(max(delta, 0.0)) * SMAA_REPROJECTION_WEIGHT_SCALE, 0.0, 1.0);
// Blend the pixels according to the calculated weight:
fragColor = vec4(mix(current.rgb, previous.rgb, weight), 1.0);
vec3 blended = mix(current.rgb, previous.rgb, weight);
blended = clamp(blended, vec3(0.0), vec3(10.0));
if (hasInvalidValues(blended)) {
blended = current.rgb;
}
fragColor = vec4(blended, 1.0);
#else
vec4 previous = textureLod(tex2, texCoord, 0.0);
fragColor = vec4(mix(current.rgb, previous.rgb, 0.5), 1.0);
previous.rgb = clamp(previous.rgb, vec3(0.0), vec3(10.0));
if (hasInvalidValues(previous.rgb)) {
previous.rgb = current.rgb;
}
vec3 blended = mix(current.rgb, previous.rgb, 0.5);
blended = clamp(blended, vec3(0.0), vec3(10.0));
if (hasInvalidValues(blended)) {
blended = current.rgb;
}
fragColor = vec4(blended, 1.0);
#endif
}

View File

@ -33,6 +33,7 @@ uniform layout(r32ui) uimage3D voxelsLight;
#ifdef _ShadowMap
uniform sampler2DShadow shadowMap;
uniform sampler2D shadowMapTransparent;
uniform sampler2DShadow shadowMapSpot;
#ifdef _ShadowMapAtlas
uniform sampler2DShadow shadowMapPoint;
@ -86,30 +87,28 @@ float lpToDepth(vec3 lp, const vec2 lightProj) {
void main() {
int res = voxelgiResolution.x;
ivec3 dst = ivec3(gl_GlobalInvocationID.xyz);
dst.y += clipmapLevel * res;
vec3 P = (gl_GlobalInvocationID.xyz + 0.5) / voxelgiResolution;
P = P * 2.0 - 1.0;
P *= clipmaps[int(clipmapLevel * 10)];
P *= voxelgiResolution;
P += vec3(clipmaps[int(clipmapLevel * 10 + 4)], clipmaps[int(clipmapLevel * 10 + 5)], clipmaps[int(clipmapLevel * 10 + 6)]);
vec3 wposition = (gl_GlobalInvocationID.xyz + 0.5) / voxelgiResolution.x;
wposition = wposition * 2.0 - 1.0;
wposition *= float(clipmaps[int(clipmapLevel * 10)]);
wposition *= voxelgiResolution.x;
wposition += vec3(clipmaps[clipmapLevel * 10 + 4], clipmaps[clipmapLevel * 10 + 5], clipmaps[clipmapLevel * 10 + 6]);
vec3 visibility;
vec3 lp = lightPos - P;
float visibility;
vec3 lp = lightPos -wposition;
vec3 l;
if (lightType == 0) { l = lightDir; visibility = vec3(1.0); }
else { l = normalize(lp); visibility = vec3(attenuate(distance(P, lightPos))); }
if (lightType == 0) { l = lightDir; visibility = 1.0; }
else { l = normalize(lp); visibility = attenuate(distance(wposition, lightPos)); }
#ifdef _ShadowMap
if (lightShadow == 1) {
vec4 lightPosition = LVP * vec4(P, 1.0);
vec4 lightPosition = LVP * vec4(wposition, 1.0);
vec3 lPos = lightPosition.xyz / lightPosition.w;
visibility = texture(shadowMap, vec3(lPos.xy, lPos.z - shadowsBias)).rrr;
visibility = texture(shadowMap, vec3(lPos.xy, lPos.z - shadowsBias)).r;
}
else if (lightShadow == 2) {
vec4 lightPosition = LVP * vec4(P, 1.0);
vec4 lightPosition = LVP * vec4(wposition, 1.0);
vec3 lPos = lightPosition.xyz / lightPosition.w;
visibility *= texture(shadowMapSpot, vec3(lPos.xy, lPos.z - shadowsBias)).r;
}
@ -130,9 +129,7 @@ void main() {
}
#endif
vec3 light = visibility * lightColor;
imageAtomicAdd(voxelsLight, dst + ivec3(0, 0, 0), uint(light.r * 255));
imageAtomicAdd(voxelsLight, dst + ivec3(0, 0, voxelgiResolution.x), uint(light.g * 255));
imageAtomicAdd(voxelsLight, dst + ivec3(0, 0, voxelgiResolution.x * 2), uint(light.b * 255));
imageAtomicAdd(voxelsLight, dst, uint(visibility * lightColor.r * 255));
imageAtomicAdd(voxelsLight, dst + ivec3(0, 0, voxelgiResolution.x), uint(visibility * lightColor.g * 255));
imageAtomicAdd(voxelsLight, dst + ivec3(0, 0, voxelgiResolution.x * 2), uint(visibility * lightColor.b * 255));
}

View File

@ -27,14 +27,14 @@ layout (local_size_x = 8, local_size_y = 8, local_size_z = 8) in;
#include "std/math.glsl"
#include "std/gbuffer.glsl"
#include "std/imageatomic.glsl"
#include "std/voxels_constants.glsl"
#include "std/constants.glsl"
#ifdef _VoxelGI
uniform layout(rgba8) image3D voxelsB;
uniform layout(rgba8) image3D voxelsOut;
#else
uniform layout(r16) image3D voxelsB;
uniform layout(r16) image3D voxelsOut;
uniform layout(r8) image3D voxelsB;
uniform layout(r8) image3D voxelsOut;
#endif
uniform int clipmapLevel;

View File

@ -29,19 +29,38 @@ layout (local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
#include "std/gbuffer.glsl"
#include "std/imageatomic.glsl"
#include "std/conetrace.glsl"
#include "std/brdf.glsl"
#include "std/shirr.glsl"
uniform sampler3D voxels;
uniform sampler2D gbufferD;
uniform sampler2D gbuffer0;
uniform layout(r8) image2D voxels_ao;
uniform layout(rgba8) image2D voxels_ao;
uniform float clipmaps[voxelgiClipmapCount * 10];
uniform mat4 InvVP;
uniform vec2 cameraProj;
uniform vec3 eye;
uniform vec3 eyeLook;
uniform vec2 postprocess_resolution;
uniform sampler2D gbuffer1;
#ifdef _gbuffer2
uniform sampler2D gbuffer2;
#endif
uniform float envmapStrength;
#ifdef _Irr
uniform float shirr[7 * 4];
#endif
#ifdef _Brdf
uniform sampler2D senvmapBrdf;
#endif
#ifdef _Rad
uniform sampler2D senvmapRadiance;
uniform int envmapNumMipmaps;
#endif
#ifdef _EnvCol
uniform vec3 backgroundCol;
#endif
void main() {
const vec2 pixel = gl_GlobalInvocationID.xy;
vec2 uv = (pixel + 0.5) / postprocess_resolution;
@ -54,12 +73,11 @@ void main() {
float x = uv.x * 2 - 1;
float y = uv.y * 2 - 1;
vec4 v = vec4(x, y, 1.0, 1.0);
v = vec4(InvVP * v);
v.xyz /= v.w;
vec3 viewRay = v.xyz - eye;
vec4 clipPos = vec4(x, y, depth, 1.0);
vec4 worldPos = InvVP * clipPos;
vec3 P = worldPos.xyz / worldPos.w;
vec3 P = getPos(eye, eyeLook, normalize(viewRay), depth, cameraProj);
vec3 v = normalize(eye - P);
vec4 g0 = textureLod(gbuffer0, uv, 0.0);
vec3 n;
@ -67,7 +85,89 @@ void main() {
n.xy = n.z >= 0.0 ? g0.xy : octahedronWrap(g0.xy);
n = normalize(n);
float occ = 1.0 - traceAO(P, n, voxels, clipmaps);
float roughness = g0.b;
float metallic;
uint matid;
unpackFloatInt16(g0.a, metallic, matid);
imageStore(voxels_ao, ivec2(pixel), vec4(occ));
vec4 g1 = textureLod(gbuffer1, uv, 0.0); // Basecolor.rgb, spec/occ
vec2 occspec = unpackFloat2(g1.a);
vec3 albedo = surfaceAlbedo(g1.rgb, metallic); // g1.rgb - basecolor
vec3 f0 = surfaceF0(g1.rgb, metallic);
float dotNV = max(dot(n, v), 0.0);
#ifdef _gbuffer2
vec4 g2 = textureLod(gbuffer2, uv, 0.0);
#endif
#ifdef _MicroShadowing
occspec.x = mix(1.0, occspec.x, dotNV); // AO Fresnel
#endif
#ifdef _Brdf
vec2 envBRDF = texelFetch(senvmapBrdf, ivec2(vec2(dotNV, 1.0 - roughness) * 256.0), 0).xy;
vec3 F = f0 * envBRDF.x + envBRDF.y;
#endif
// Envmap
#ifdef _Irr
vec4 shPacked[7];
for (int i = 0; i < 7; i++) {
int base = i * 4;
shPacked[i] = vec4(
shirr[base],
shirr[base + 1],
shirr[base + 2],
shirr[base + 3]
);
}
vec3 envl = shIrradiance(n, shPacked);
#ifdef _gbuffer2
if (g2.b < 0.5) {
envl = envl;
} else {
envl = vec3(0.0);
}
#endif
#ifdef _EnvTex
envl /= PI;
#endif
#else
vec3 envl = vec3(0.0);
#endif
#ifdef _Rad
vec3 reflectionWorld = reflect(-v, n);
float lod = getMipFromRoughness(roughness, envmapNumMipmaps);
vec3 prefilteredColor = textureLod(senvmapRadiance, envMapEquirect(reflectionWorld), lod).rgb;
#endif
#ifdef _EnvLDR
envl.rgb = pow(envl.rgb, vec3(2.2));
#ifdef _Rad
prefilteredColor = pow(prefilteredColor, vec3(2.2));
#endif
#endif
envl.rgb *= albedo;
#ifdef _Brdf
envl.rgb *= 1.0 - F; //LV: We should take refracted light into account
#endif
#ifdef _Rad // Indirect specular
envl.rgb += prefilteredColor * F; //LV: Removed "1.5 * occspec.y". Specular should be weighted only by FV LUT
#else
#ifdef _EnvCol
envl.rgb += backgroundCol * F; //LV: Eh, what's the point of weighting it only by F0?
#endif
#endif
envl.rgb *= envmapStrength * occspec.x;
vec3 occ = envl * (1.0 - traceAO(P, n, voxels, clipmaps));
imageStore(voxels_ao, ivec2(pixel), vec4(occ, 1.0));
}

View File

@ -29,6 +29,8 @@ layout (local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
#include "std/gbuffer.glsl"
#include "std/imageatomic.glsl"
#include "std/conetrace.glsl"
#include "std/brdf.glsl"
#include "std/shirr.glsl"
uniform sampler3D voxels;
uniform sampler2D gbufferD;
@ -37,29 +39,44 @@ uniform layout(rgba8) image2D voxels_diffuse;
uniform float clipmaps[voxelgiClipmapCount * 10];
uniform mat4 InvVP;
uniform vec2 cameraProj;
uniform vec3 eye;
uniform vec3 eyeLook;
uniform vec2 postprocess_resolution;
uniform sampler2D gbuffer1;
#ifdef _gbuffer2
uniform sampler2D gbuffer2;
#endif
uniform float envmapStrength;
#ifdef _Irr
uniform float shirr[7 * 4];
#endif
#ifdef _Brdf
uniform sampler2D senvmapBrdf;
#endif
#ifdef _Rad
uniform sampler2D senvmapRadiance;
uniform int envmapNumMipmaps;
#endif
#ifdef _EnvCol
uniform vec3 backgroundCol;
#endif
void main() {
const vec2 pixel = gl_GlobalInvocationID.xy;
vec2 uv = (pixel + 0.5) / postprocess_resolution;
#ifdef _InvY
uv.y = 1.0 - uv.y
uv.y = 1.0 - uv.y;
#endif
float depth = textureLod(gbufferD, uv, 0.0).r * 2.0 - 1.0;
if (depth == 0) return;
if (depth == 0.0) return;
float x = uv.x * 2 - 1;
float y = uv.y * 2 - 1;
vec4 v = vec4(x, y, 1.0, 1.0);
v = vec4(InvVP * v);
v.xyz /= v.w;
vec3 viewRay = v.xyz - eye;
vec3 P = getPos(eye, eyeLook, normalize(viewRay), depth, cameraProj);
vec4 clipPos = vec4(x, y, depth, 1.0);
vec4 worldPos = InvVP * clipPos;
vec3 P = worldPos.xyz / worldPos.w;
vec3 v = normalize(eye - P);
vec4 g0 = textureLod(gbuffer0, uv, 0.0);
vec3 n;
@ -67,7 +84,94 @@ void main() {
n.xy = n.z >= 0.0 ? g0.xy : octahedronWrap(g0.xy);
n = normalize(n);
vec4 color = traceDiffuse(P, n, voxels, clipmaps);
float roughness = g0.b;
float metallic;
uint matid;
unpackFloatInt16(g0.a, metallic, matid);
imageStore(voxels_diffuse, ivec2(pixel), color);
vec4 g1 = textureLod(gbuffer1, uv, 0.0); // Basecolor.rgb, spec/occ
vec2 occspec = unpackFloat2(g1.a);
vec3 albedo = surfaceAlbedo(g1.rgb, metallic); // g1.rgb - basecolor
vec3 f0 = surfaceF0(g1.rgb, metallic);
float dotNV = max(dot(n, v), 0.0);
#ifdef _gbuffer2
vec4 g2 = textureLod(gbuffer2, uv, 0.0);
#endif
#ifdef _MicroShadowing
occspec.x = mix(1.0, occspec.x, dotNV); // AO Fresnel
#endif
#ifdef _Brdf
vec2 envBRDF = texelFetch(senvmapBrdf, ivec2(vec2(dotNV, 1.0 - roughness) * 256.0), 0).xy;
vec3 F = f0 * envBRDF.x + envBRDF.y;
#else
vec3 F = f0;
#endif
// Envmap
#ifdef _Irr
vec4 shPacked[7];
for (int i = 0; i < 7; i++) {
int base = i * 4;
shPacked[i] = vec4(
shirr[base],
shirr[base + 1],
shirr[base + 2],
shirr[base + 3]
);
}
vec3 envl = shIrradiance(n, shPacked);
#ifdef _gbuffer2
if (g2.b < 0.5) {
envl = envl;
} else {
envl = vec3(0.0);
}
#endif
#ifdef _EnvTex
envl /= PI;
#endif
#else
vec3 envl = vec3(0.0);
#endif
#ifdef _Rad
vec3 reflectionWorld = reflect(-v, n);
float lod = getMipFromRoughness(roughness, envmapNumMipmaps);
vec3 prefilteredColor = textureLod(senvmapRadiance, envMapEquirect(reflectionWorld), lod).rgb;
#endif
#ifdef _EnvLDR
envl.rgb = pow(envl.rgb, vec3(2.2));
#ifdef _Rad
prefilteredColor = pow(prefilteredColor, vec3(2.2));
#endif
#endif
envl.rgb *= albedo;
#ifdef _Brdf
envl.rgb *= 1.0 - F; //LV: We should take refracted light into account
#endif
#ifdef _Rad // Indirect specular
envl.rgb += prefilteredColor * F; //LV: Removed "1.5 * occspec.y". Specular should be weighted only by FV LUT
#else
#ifdef _EnvCol
envl.rgb += backgroundCol * F; //LV: Eh, what's the point of weighting it only by F0?
#endif
#endif
envl.rgb *= envmapStrength * occspec.x;
vec4 trace = traceDiffuse(P, n, voxels, clipmaps);
vec3 color = trace.rgb * albedo * (1.0 - F);
color += envl * (1.0 - trace.a);
imageStore(voxels_diffuse, ivec2(pixel), vec4(color, 1.0));
}

View File

@ -29,6 +29,7 @@ layout (local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
#include "std/gbuffer.glsl"
#include "std/imageatomic.glsl"
#include "std/conetrace.glsl"
#include "std/brdf.glsl"
uniform sampler2D gbufferD;
uniform sampler2D gbuffer0;
@ -38,9 +39,7 @@ uniform layout(rgba8) image2D voxels_specular;
uniform float clipmaps[voxelgiClipmapCount * 10];
uniform mat4 InvVP;
uniform vec2 cameraProj;
uniform vec3 eye;
uniform vec3 eyeLook;
uniform vec2 postprocess_resolution;
uniform sampler2D sveloc;
@ -56,12 +55,10 @@ void main() {
float x = uv.x * 2 - 1;
float y = uv.y * 2 - 1;
vec4 v = vec4(x, y, 1.0, 1.0);
v = vec4(InvVP * v);
v.xyz /= v.w;
vec4 clipPos = vec4(x, y, depth, 1.0);
vec4 worldPos = InvVP * clipPos;
vec3 P = worldPos.xyz / worldPos.w;
vec3 viewRay = v.xyz - eye;
vec3 P = getPos(eye, eyeLook, normalize(viewRay), depth, cameraProj);
vec4 g0 = textureLod(gbuffer0, uv, 0.0);
vec3 n;
@ -71,7 +68,7 @@ void main() {
vec2 velocity = -textureLod(sveloc, uv, 0.0).rg;
vec3 color = traceSpecular(P, n, voxels, voxelsSDF, normalize(eye - P), g0.z, clipmaps, pixel, velocity).rgb;
vec3 color = traceSpecular(P, n, voxels, voxelsSDF, normalize(eye - P), g0.z * g0.z, clipmaps, pixel, velocity).rgb;
imageStore(voxels_specular, ivec2(pixel), vec4(color, 1.0));
}

View File

@ -23,8 +23,8 @@ THE SOFTWARE.
#include "compiled.inc"
uniform layout(r16) image3D input_sdf;
uniform layout(r16) image3D output_sdf;
uniform layout(r8) image3D input_sdf;
uniform layout(r8) image3D output_sdf;
uniform float jump_size;
uniform int clipmapLevel;

View File

@ -46,15 +46,15 @@ uniform layout(r32ui) uimage3D voxels;
uniform layout(r32ui) uimage3D voxelsLight;
uniform layout(rgba8) image3D voxelsB;
uniform layout(rgba8) image3D voxelsOut;
uniform layout(r16) image3D SDF;
uniform layout(r8) image3D SDF;
#else
#ifdef _VoxelAOvar
#ifdef _VoxelShadow
uniform layout(r16) image3D SDF;
uniform layout(r8) image3D SDF;
#endif
uniform layout(r32ui) uimage3D voxels;
uniform layout(r16) image3D voxelsB;
uniform layout(r16) image3D voxelsOut;
uniform layout(r8) image3D voxelsB;
uniform layout(r8) image3D voxelsOut;
#endif
#endif
@ -74,14 +74,9 @@ void main() {
#endif
#endif
ivec3 src = ivec3(gl_GlobalInvocationID.xyz);
#ifdef _VoxelGI
vec3 light = vec3(0.0);
light.r = float(imageLoad(voxelsLight, src)) / 255;
light.g = float(imageLoad(voxelsLight, src + ivec3(0, 0, voxelgiResolution.x))) / 255;
light.b = float(imageLoad(voxelsLight, src + ivec3(0, 0, voxelgiResolution.x * 2))) / 255;
light /= 3;
#endif
int nor_count = 0;
vec3 avgNormal = vec3(0.0);
mat3 TBN = mat3(0.0);
for (int i = 0; i < 6 + DIFFUSE_CONE_COUNT; i++)
{
@ -91,7 +86,7 @@ void main() {
float aniso_colors[6];
#endif
src = ivec3(gl_GlobalInvocationID.xyz);
ivec3 src = ivec3(gl_GlobalInvocationID.xyz);
src.x += i * res;
ivec3 dst = src;
dst.y += clipmapLevel * res;
@ -104,44 +99,67 @@ void main() {
if (i < 6) {
#ifdef _VoxelGI
vec4 basecol = vec4(0.0);
basecol.r = float(imageLoad(voxels, src)) / 255;
basecol.g = float(imageLoad(voxels, src + ivec3(0, 0, voxelgiResolution.x))) / 255;
basecol.b = float(imageLoad(voxels, src + ivec3(0, 0, voxelgiResolution.x * 2))) / 255;
basecol.a = float(imageLoad(voxels, src + ivec3(0, 0, voxelgiResolution.x * 3))) / 255;
basecol /= 4;
vec3 emission = vec3(0.0);
emission.r = float(imageLoad(voxels, src + ivec3(0, 0, voxelgiResolution.x * 4))) / 255;
emission.g = float(imageLoad(voxels, src + ivec3(0, 0, voxelgiResolution.x * 5))) / 255;
emission.b = float(imageLoad(voxels, src + ivec3(0, 0, voxelgiResolution.x * 6))) / 255;
emission /= 3;
vec3 N = vec3(0.0);
N.r = float(imageLoad(voxels, src + ivec3(0, 0, voxelgiResolution.x * 7))) / 255;
N.g = float(imageLoad(voxels, src + ivec3(0, 0, voxelgiResolution.x * 8))) / 255;
N /= 2;
vec3 wnormal = decode_oct(N.rg * 2 - 1);
vec3 envl = vec3(0.0);
envl.r = float(imageLoad(voxels, src + ivec3(0, 0, voxelgiResolution.x * 9))) / 255;
envl.g = float(imageLoad(voxels, src + ivec3(0, 0, voxelgiResolution.x * 10))) / 255;
envl.b = float(imageLoad(voxels, src + ivec3(0, 0, voxelgiResolution.x * 11))) / 255;
envl /= 3;
envl *= 100;
int count = int(imageLoad(voxels, src + ivec3(0, 0, voxelgiResolution.x * 15)));
if (count > 0) {
vec4 basecol = vec4(0.0);
basecol.r = float(imageLoad(voxels, src)) / 255;
basecol.g = float(imageLoad(voxels, src + ivec3(0, 0, voxelgiResolution.x))) / 255;
basecol.b = float(imageLoad(voxels, src + ivec3(0, 0, voxelgiResolution.x * 2))) / 255;
basecol.a = float(imageLoad(voxels, src + ivec3(0, 0, voxelgiResolution.x * 3))) / 255;
basecol /= count;
vec3 emission = vec3(0.0);
emission.r = float(imageLoad(voxels, src + ivec3(0, 0, voxelgiResolution.x * 4))) / 255;
emission.g = float(imageLoad(voxels, src + ivec3(0, 0, voxelgiResolution.x * 5))) / 255;
emission.b = float(imageLoad(voxels, src + ivec3(0, 0, voxelgiResolution.x * 6))) / 255;
emission /= count;
vec3 N = vec3(0.0);
N.r = float(imageLoad(voxels, src + ivec3(0, 0, voxelgiResolution.x * 7))) / 255;
N.g = float(imageLoad(voxels, src + ivec3(0, 0, voxelgiResolution.x * 8))) / 255;
N /= count;
N = decode_oct(N.rg * 2.0 - 1.0);
//clipmap to world
vec3 wposition = (gl_GlobalInvocationID.xyz + 0.5) / voxelgiResolution.x;
wposition = wposition * 2.0 - 1.0;
wposition *= float(clipmaps[int(clipmapLevel * 10)]);
wposition *= voxelgiResolution.x;
wposition += vec3(clipmaps[clipmapLevel * 10 + 4], clipmaps[clipmapLevel * 10 + 5], clipmaps[clipmapLevel * 10 + 6]);
if (abs(N.x) > 0)
avgNormal.x += N.x;
if (abs(N.y) > 0)
avgNormal.y += N.y;
if (abs(N.z) > 0)
avgNormal.z += N.z;
if (i == 5)
{
avgNormal = normalize(avgNormal);
TBN = makeTangentBasis(avgNormal);
}
radiance = basecol;
vec4 trace = traceDiffuse(wposition, wnormal, voxelsSampler, clipmaps);
vec3 indirect = trace.rgb + envl.rgb * (1.0 - trace.a);
radiance.rgb *= light + indirect;
radiance.rgb += emission.rgb;
vec3 envl = vec3(0.0);
envl.r = float(imageLoad(voxels, src + ivec3(0, 0, voxelgiResolution.x * 9))) / 255;
envl.g = float(imageLoad(voxels, src + ivec3(0, 0, voxelgiResolution.x * 10))) / 255;
envl.b = float(imageLoad(voxels, src + ivec3(0, 0, voxelgiResolution.x * 11))) / 255;
envl /= count;
vec3 light = vec3(0.0);
light.r = float(imageLoad(voxels, src + ivec3(0, 0, voxelgiResolution.x * 12))) / 255;
light.g = float(imageLoad(voxels, src + ivec3(0, 0, voxelgiResolution.x * 13))) / 255;
light.b = float(imageLoad(voxels, src + ivec3(0, 0, voxelgiResolution.x * 14))) / 255;
light /= count;
//clipmap to world
vec3 wposition = (gl_GlobalInvocationID.xyz + 0.5) / voxelgiResolution.x;
wposition = wposition * 2.0 - 1.0;
wposition *= float(clipmaps[int(clipmapLevel * 10)]);
wposition *= voxelgiResolution.x;
wposition += vec3(clipmaps[clipmapLevel * 10 + 4], clipmaps[clipmapLevel * 10 + 5], clipmaps[clipmapLevel * 10 + 6]);
radiance = basecol;
vec4 trace = traceDiffuse(wposition, N, voxelsSampler, clipmaps);
vec3 indirect = trace.rgb + envl.rgb * (1.0 - trace.a);
radiance.rgb *= light + indirect;
radiance.rgb += emission.rgb;
}
#else
opac = float(imageLoad(voxels, src)) / 255;
int count = int(imageLoad(voxels, src + ivec3(0, 0, voxelgiResolution.x)));
if (count > 0) {
opac = float(imageLoad(voxels, src)) / 255;
opac /= count;
}
#endif
#ifdef _VoxelGI
@ -195,7 +213,7 @@ void main() {
}
else {
// precompute cone sampling:
vec3 coneDirection = DIFFUSE_CONE_DIRECTIONS[i - 6];
vec3 coneDirection = TBN * DIFFUSE_CONE_DIRECTIONS[i - 6];
vec3 aniso_direction = -coneDirection;
uvec3 face_offsets = uvec3(
aniso_direction.x > 0 ? 0 : 1,
@ -236,4 +254,4 @@ void main() {
imageStore(SDF, dst_sdf, vec4(sdf));
#endif
#endif
}
}

View File

@ -53,6 +53,21 @@ class App {
static function update() {
if (Scene.active == null || !Scene.active.ready) return;
// VR is handling it so we prevent double updates
// TODO: avoid js.Syntax
#if (kha_webgl && lnx_vr)
var vrActive = false;
js.Syntax.code("
if (typeof kha !== 'undefined' && kha.vr && kha.vr.VrInterface) {
const vr = kha.vr.VrInterface.instance;
if (vr && vr.IsPresenting && vr.IsPresenting()) {
{0} = true;
}
}
", vrActive);
if (vrActive) return;
#end
iron.system.Time.update();
if (lastw == -1) {
@ -138,6 +153,21 @@ class App {
traitInits.splice(0, traitInits.length);
}
// skip for XR callback to handle rendering
// TODO: avoid js Syntax
#if (kha_webgl && lnx_vr)
var vrActive = false;
js.Syntax.code("
if (typeof kha !== 'undefined' && kha.vr && kha.vr.VrInterface) {
const vr = kha.vr.VrInterface.instance;
if (vr && vr.IsPresenting && vr.IsPresenting()) {
{0} = true;
}
}
", vrActive);
if (!vrActive) {
#end
Scene.active.renderFrame(frame.g4);
for (f in traitRenders) {
@ -146,6 +176,10 @@ class App {
render2D(frame);
#if (kha_webgl && lnx_vr)
}
#end
#if lnx_debug
renderPathTime = kha.Scheduler.realTime() - startTime;
#end

View File

@ -18,10 +18,44 @@ import iron.object.LightObject;
import iron.object.MeshObject;
import iron.object.Uniforms;
import iron.object.Clipmap;
#if lnx_vr
import iron.math.Vec4;
import iron.math.Mat4;
import iron.math.Quat;
#end
class RenderPath {
public static var active: RenderPath;
#if lnx_vr
static var vrSimulateMode: Bool = false;
static var vrCameraOffsetSet:Bool = false;
static var vrCameraOffset:Vec4 = new Vec4();
static var wasVRPresenting:Bool = false;
public static var vrCalibrationPosition:Vec4 = null;
public static var vrCalibrationRotation:iron.math.Quat = null;
public static var vrCalibrationSaved:Bool = false;
public static var vrCenterCameraWorld:Mat4 = null;
static var vrOriginalSuperSample:Float = -1.0;
public static inline function isVRPresenting(): Bool {
#if (kha_webgl && lnx_vr)
return kha.vr.VrInterface.instance != null && kha.vr.VrInterface.instance.IsPresenting();
#else
return false;
#end
}
public static inline function isVRSimulateMode(): Bool {
return vrSimulateMode;
}
// TODO: done remove safely
public static inline function debugLog(msg: String, once: Bool = true): Void {
return;
}
#end
public var frameScissor = false;
public var frameScissorX = 0;
@ -43,9 +77,15 @@ class RenderPath {
public var isProbe = false;
public var currentG: Graphics = null;
public var frameG: Graphics;
#if lnx_vr
var beginCalled = false;
var scissorSet = false;
var viewportScaled = false;
var renderToXRFramebuffer = false;
#end
public var drawOrder = DrawOrder.Distance;
public var paused = false;
public var ready(get, null): Bool;
public var ready(get, never): Bool;
function get_ready(): Bool { return loading == 0; }
public var commands: Void->Void = null;
public var setupDepthTexture: Void->Void = null;
@ -123,9 +163,93 @@ class RenderPath {
public function renderFrame(g: Graphics) {
if (!ready || paused || iron.App.w() == 0 || iron.App.h() == 0) return;
if (lastW > 0 && (lastW != iron.App.w() || lastH != iron.App.h())) resize();
lastW = iron.App.w();
lastH = iron.App.h();
var appW = iron.App.w();
var appH = iron.App.h();
// use native XR framebuffer dimensions
#if (kha_webgl && lnx_vr)
if (kha.vr.VrInterface.instance != null) {
var vr = kha.vr.VrInterface.instance;
var isPresenting = vr != null && vr.IsPresenting();
// save/restore camera position between modes
if (!wasVRPresenting && isPresenting) {
if (Scene.active != null && Scene.active.camera != null) {
if (vrCalibrationPosition == null) vrCalibrationPosition = new Vec4();
if (vrCalibrationRotation == null) vrCalibrationRotation = new Quat();
vrCalibrationPosition.setFrom(Scene.active.camera.transform.loc);
vrCalibrationRotation.setFrom(Scene.active.camera.transform.rot);
vrCalibrationSaved = true;
}
// save original super sampling for later
vrOriginalSuperSample = leenkx.renderpath.Inc.superSample;
// compositeToXR function handles blitting to VR framebuffer
var xrVr: kha.js.vr.VrInterface = cast vr;
if (xrVr.xrGLLayer != null) {
var vrWidth = untyped xrVr.xrGLLayer.framebufferWidth;
var vrHeight = untyped xrVr.xrGLLayer.framebufferHeight;
}
}
else if (wasVRPresenting && !isPresenting) {
// reset VR frame time before anything else
#if (kha_webgl && lnx_vr)
iron.system.Time.vrFrameTime = -1.0;
#end
if (vrCalibrationSaved && Scene.active != null && Scene.active.camera != null) {
Scene.active.camera.transform.loc.setFrom(vrCalibrationPosition);
Scene.active.camera.transform.rot.setFrom(vrCalibrationRotation);
Scene.active.camera.buildMatrix();
Scene.active.camera.buildProjection();
}
// restore original super sampling from simulate mode
if (vrOriginalSuperSample >= 0.0) {
leenkx.renderpath.Inc.superSample = vrOriginalSuperSample;
for (rt in renderTargets) {
if (rt.raw.width == 0 && rt.raw.scale != null) {
rt.raw.scale = vrOriginalSuperSample;
}
}
resize();
vrOriginalSuperSample = -1.0;
}
// reset offset for next session
vrCameraOffsetSet = false;
vrCameraOffset = null;
}
wasVRPresenting = isPresenting;
if (isPresenting) {
// TODO: re-investigate using super sampling to avoid pixelation in simulate mode while giving max quality in headset
if (vrOriginalSuperSample >= 0.0 && leenkx.renderpath.Inc.superSample != 4.0) {
leenkx.renderpath.Inc.superSample = 4.0;
for (rt in renderTargets) {
if (rt.raw.width == 0 && rt.raw.scale != null) {
rt.raw.scale = 4.0;
}
}
resize();
}
var xrVr: kha.js.vr.VrInterface = cast vr;
if (xrVr.xrGLLayer != null) {
appW = xrVr.xrGLLayer.framebufferWidth;
appH = xrVr.xrGLLayer.framebufferHeight;
}
}
}
#end
if (lastW > 0 && (lastW != appW || lastH != appH)) resize();
lastW = appW;
lastH = appH;
frameTime = Time.time() - lastFrameTime;
lastFrameTime = Time.time();
@ -191,7 +315,9 @@ class RenderPath {
}
light = Scene.active.lights[0];
commands();
if (commands != null) {
commands();
}
if (!isProbe) frame++;
}
@ -207,13 +333,13 @@ class RenderPath {
begin(frameG, Scene.active.camera.currentFace);
}
else { // Screen, planar probe
currentW = iron.App.w();
currentH = iron.App.h();
currentW = kha.System.windowWidth();
currentH = kha.System.windowHeight();
if (frameScissor) setFrameScissor();
begin(frameG);
if (!isProbe) {
setCurrentViewport(iron.App.w(), iron.App.h());
setCurrentScissor(iron.App.w(), iron.App.h());
setCurrentViewport(kha.System.windowWidth(), kha.System.windowHeight());
setCurrentScissor(kha.System.windowWidth(), kha.System.windowHeight());
}
}
}
@ -258,16 +384,42 @@ class RenderPath {
if (currentG != null) end();
currentG = g;
additionalTargets = additionalRenderTargets;
face >= 0 ? g.beginFace(face) : g.begin(additionalRenderTargets);
// we still bind but skip begin() when explicitly rendering to XR framebuffer (renderToXRFramebuffer flag)
#if lnx_vr
if (!renderToXRFramebuffer) {
face >= 0 ? g.beginFace(face) : g.begin(additionalRenderTargets);
beginCalled = true;
} else {
// XR framebuffer is already bound by VrInterface so we dont rebind
beginCalled = false;
}
#else
face >= 0 ? g.beginFace(face) : g.begin(additionalRenderTargets);
#end
}
inline function end() {
if (currentG == null) return;
if (scissorSet) {
currentG.disableScissor();
scissorSet = false;
}
#if lnx_vr
if (beginCalled) {
currentG.end();
beginCalled = false;
}
// persist for rendering both eyes
if (!isVRPresenting()) {
currentG = null;
additionalTargets = null;
}
#else
currentG.end();
currentG = null;
#end
bindParams = null;
}
@ -341,8 +493,8 @@ class RenderPath {
if (a.data.sortingIndex != b.data.sortingIndex) {
return a.data.sortingIndex > b.data.sortingIndex ? 1 : -1;
}
return a.data.name >= b.data.name ? 1 : -1; });
return a.data.name >= b.data.name ? 1 : -1;
});
}
public function drawMeshes(context: String) {
@ -521,44 +673,208 @@ class RenderPath {
return Reflect.field(kha.Shaders, handle + "_comp");
}
#if lnx_vr
// blits to each eyes viewport in the XR framebuffer.
public function compositeToXR(sourceTarget: String) {
#if (kha_webgl && lnx_vr)
var vr: kha.js.vr.VrInterface = cast kha.vr.VrInterface.instance;
if (vr == null || vr._glContext == null || vr.xrGLLayer == null) {
return;
}
var gl: js.html.webgl.WebGL2RenderingContext = cast vr._glContext;
var source = renderTargets.get(sourceTarget);
if (source == null) {
return;
}
var sourceFB: js.html.webgl.Framebuffer = untyped source.image.g4.renderTargetFrameBuffer;
if (sourceFB == null) {
return;
}
// trace('Framebuffer OK');
renderToXRFramebuffer = true;
gl.bindFramebuffer(js.html.webgl.WebGL2RenderingContext.DRAW_FRAMEBUFFER, vr.xrGLLayer.framebuffer);
gl.bindFramebuffer(js.html.webgl.WebGL2RenderingContext.READ_FRAMEBUFFER, sourceFB);
var readStatus = gl.checkFramebufferStatus(js.html.webgl.WebGL2RenderingContext.READ_FRAMEBUFFER);
var drawStatus = gl.checkFramebufferStatus(js.html.webgl.WebGL2RenderingContext.DRAW_FRAMEBUFFER);
if (readStatus != js.html.webgl.WebGL2RenderingContext.FRAMEBUFFER_COMPLETE ||
drawStatus != js.html.webgl.WebGL2RenderingContext.FRAMEBUFFER_COMPLETE) {
return;
}
var halfWidth = Std.int(source.image.width / 2);
var fullHeight = source.image.height;
if (vr._leftViewport != null) {
var vp = vr._leftViewport;
gl.blitFramebuffer(
0, 0, halfWidth, fullHeight,
vp.x, vp.y, vp.x + vp.width, vp.y + vp.height,
js.html.webgl.WebGL2RenderingContext.COLOR_BUFFER_BIT,
js.html.webgl.WebGL2RenderingContext.LINEAR
);
}
if (vr._rightViewport != null) {
var vp = vr._rightViewport;
gl.blitFramebuffer(
halfWidth, 0, source.image.width, fullHeight,
vp.x, vp.y, vp.x + vp.width, vp.y + vp.height,
js.html.webgl.WebGL2RenderingContext.COLOR_BUFFER_BIT,
js.html.webgl.WebGL2RenderingContext.LINEAR
);
}
gl.bindFramebuffer(js.html.webgl.WebGL2RenderingContext.FRAMEBUFFER, null);
renderToXRFramebuffer = false;
#end
}
#end
#if lnx_vr
public function drawStereo(drawMeshes: Void->Void) {
var vr = kha.vr.VrInterface.instance;
vrSimulateMode = false;
if (currentG == null && frameG != null) {
currentG = frameG;
}
var appw = iron.App.w();
var apph = iron.App.h();
var halfw = Std.int(appw / 2);
var g = currentG;
// get render target dimensions not App.w/h gbuffer is scaled in simulate mode with supersampling
if (vr != null && vr.IsPresenting()) {
// Left eye
Scene.active.camera.V.setFrom(Scene.active.camera.leftV);
var gbuffer0 = renderTargets.get("gbuffer0");
var actualWidth = (gbuffer0 != null && gbuffer0.image != null) ? gbuffer0.image.width : appw;
var actualHeight = (gbuffer0 != null && gbuffer0.image != null) ? gbuffer0.image.height : apph;
var actualHalfWidth = Std.int(actualWidth / 2);
var vrFBWidth = actualWidth;
var vrFBHeight = actualHeight;
var vrHalfWidth = actualHalfWidth;
var isVRPresenting = false;
vrSimulateMode = false;
var vr:Dynamic = null;
var vrExists = false;
#if (kha_webgl && lnx_vr)
if (kha.vr.VrInterface.instance != null) {
vr = kha.vr.VrInterface.instance;
vrExists = true;
}
#end
if (vrExists && vr != null && vr.IsPresenting()) {
vrSimulateMode = false;
isVRPresenting = true;
// get framebuffer dimensions from XR layer
#if (kha_webgl && lnx_vr)
var xrVr: kha.js.vr.VrInterface = cast vr;
if (xrVr.xrGLLayer != null) {
vrFBWidth = untyped xrVr.xrGLLayer.framebufferWidth;
vrFBHeight = untyped xrVr.xrGLLayer.framebufferHeight;
vrHalfWidth = Std.int(vrFBWidth / 2);
}
#end
if (Scene.active == null || Scene.active.camera == null) {
return;
}
#if (kha_webgl && lnx_vr)
if (vrCenterCameraWorld == null) vrCenterCameraWorld = Mat4.identity();
vrCenterCameraWorld.setFrom(Scene.active.camera.transform.world);
#end
// LEFT EYE
// HMD center for room scale position tracking
#if (kha_webgl && lnx_vr)
var xrVr: kha.js.vr.VrInterface = cast vr;
if (xrVr.currentViewerPose != null) {
var viewerTransform = untyped xrVr.currentViewerPose.transform;
if (viewerTransform != null && viewerTransform.position != null) {
// VR present calibration is used to position objects in world space not the camera
var pos = viewerTransform.position;
// camera follows headset directly in local floor space
Scene.active.camera.transform.loc.set(pos.x, pos.y, pos.z);
if (viewerTransform.orientation != null) {
Scene.active.camera.transform.rot.set(
viewerTransform.orientation.x,
viewerTransform.orientation.y,
viewerTransform.orientation.z,
viewerTransform.orientation.w
);
}
Scene.active.camera.transform.buildMatrix();
}
}
iron.system.VRController.updatePoses();
#end
Scene.active.camera.V.self = vr.GetViewMatrix(0);
Scene.active.camera.P.self = vr.GetProjectionMatrix(0);
g.viewport(0, 0, halfw, apph);
Scene.active.camera.VP.setFrom(Scene.active.camera.P);
Scene.active.camera.VP.multmat(Scene.active.camera.V);
Scene.active.camera.buildMatrix(); // update frustum for culling
var renderWidth = actualWidth;
var renderHeight = actualHeight;
var renderHalfWidth = actualHalfWidth;
// left half of render target
g.viewport(0, 0, renderHalfWidth, renderHeight);
g.scissor(0, 0, renderHalfWidth, renderHeight);
drawMeshes();
// Right eye
begin(g, additionalTargets);
Scene.active.camera.V.setFrom(Scene.active.camera.rightV);
// RIGHT EYE
Scene.active.camera.V.self = vr.GetViewMatrix(1);
Scene.active.camera.P.self = vr.GetProjectionMatrix(1);
g.viewport(halfw, 0, halfw, apph);
Scene.active.camera.VP.setFrom(Scene.active.camera.P);
Scene.active.camera.VP.multmat(Scene.active.camera.V);
Scene.active.camera.buildMatrix();
// right half of render target
g.viewport(renderHalfWidth, 0, renderHalfWidth, renderHeight);
g.scissor(renderHalfWidth, 0, renderHalfWidth, renderHeight);
drawMeshes();
// restore for post-processing
g.disableScissor();
g.viewport(0, 0, renderWidth, renderHeight);
}
else { // Simulate
Scene.active.camera.buildProjection(halfw / apph);
vrSimulateMode = true;
var ipd_offset = 0.032 * 35.0;
// Left eye
g.viewport(0, 0, halfw, apph);
#if (kha_webgl && lnx_vr)
if (vrCenterCameraWorld == null) vrCenterCameraWorld = Mat4.identity();
vrCenterCameraWorld.setFrom(Scene.active.camera.transform.world);
#end
Scene.active.camera.buildProjection(actualHalfWidth / actualHeight);
Scene.active.camera.transform.move(Scene.active.camera.right(), -ipd_offset);
Scene.active.camera.buildMatrix();
g.viewport(0, 0, actualHalfWidth, actualHeight);
g.scissor(0, 0, actualHalfWidth, actualHeight);
drawMeshes();
// Right eye
begin(g, additionalTargets);
Scene.active.camera.transform.move(Scene.active.camera.right(), 0.032);
Scene.active.camera.transform.move(Scene.active.camera.right(), ipd_offset * 2.0);
Scene.active.camera.buildMatrix();
g.viewport(halfw, 0, halfw, apph);
g.viewport(actualHalfWidth, 0, actualHalfWidth, actualHeight);
g.scissor(actualHalfWidth, 0, actualHalfWidth, actualHeight);
drawMeshes();
Scene.active.camera.transform.move(Scene.active.camera.right(), -0.032);
Scene.active.camera.transform.move(Scene.active.camera.right(), -ipd_offset);
Scene.active.camera.buildMatrix();
g.disableScissor();
g.viewport(0, 0, actualWidth, actualHeight);
}
}
#end

View File

@ -954,7 +954,12 @@ class Scene {
static function createTraitClassInstance(traitName: String, args: Array<Dynamic>): Dynamic {
var cname = Type.resolveClass(traitName);
if (cname == null) return null;
return Type.createInstance(cname, args);
try {
return Type.createInstance(cname, args);
} catch() {
trace("Error creating trait: " + traitName);
return null;
}
}
function loadEmbeddedData(datas: Array<String>, done: Void->Void) {

View File

@ -37,7 +37,9 @@ class Armature {
}
public function getAction(name: String): TAction {
for (a in actions) if (a.name == name) return a;
for (a in actions) {
if (a.name == name) return a;
}
return null;
}

View File

@ -141,6 +141,7 @@ class Animation {
sampler.cacheSet = false;
sampler.trackEnd = false;
if (anim == null || anim.tracks == null || anim.tracks.length == 0) return;
var track = anim.tracks[0];
if (frameIndex == -1) {
@ -442,7 +443,12 @@ class ActionSampler {
*/
public inline function setObjectAction(actionData: TObj) {
this.actionData = [actionData];
this.totalFrames = actionData.anim.tracks[0].frames.length;
if (actionData != null && actionData.anim != null && actionData.anim.tracks != null && actionData.anim.tracks.length > 0) {
this.totalFrames = actionData.anim.tracks[0].frames.length;
}
else {
this.totalFrames = 0;
}
actionDataInit = true;
}

View File

@ -108,9 +108,11 @@ class BoneAnimation extends Animation {
object.transform.rot.set(0, 0, 0, 1);
object.transform.buildMatrix();
var refs = mo.parent.raw.bone_actions;
if (refs != null && refs.length > 0) {
Data.getSceneRaw(refs[0], function(action: TSceneFormat) { play(action.name); });
if (mo.parent != null && mo.parent.raw != null && mo.parent.raw.bone_actions != null) {
var refs = mo.parent.raw.bone_actions;
if (refs.length > 0) {
Data.getSceneRaw(refs[0], function(action: TSceneFormat) { play(action.name); });
}
}
}
if (armatureObject.raw.relative_bone_constraints) relativeBoneConstraints = true;
@ -183,8 +185,10 @@ class BoneAnimation extends Animation {
}
function setAction(action: String) {
if (armature == null) return;
armature.initMats();
var a = armature.getAction(action);
if (a == null) return;
skeletonBones = a.bones;
skeletonMats = a.mats;
if(! rootMotionCacheInit) skeletonMats.push(Mat4.identity());
@ -193,8 +197,11 @@ class BoneAnimation extends Animation {
}
function getAction(action: String): Array<TObj> {
if (armature == null) return null;
armature.initMats();
return armature.getAction(action).bones;
var a = armature.getAction(action);
if (a == null) return null;
return a.bones;
}
function multParent(i: Int, fasts: Array<Mat4>, bones: Array<TObj>, mats: Array<Mat4>) {
@ -225,9 +232,9 @@ class BoneAnimation extends Animation {
}
override public function play(action = "", onComplete: Void->Void = null, blendTime = 0.2, speed = 1.0, loop = true) {
super.play(action, onComplete, blendTime, speed, loop);
if (action != "") {
setAction(action);
super.play(action, onComplete, blendTime, speed, loop);
var tempAnimParam = new ActionSampler(action);
registerAction("tempAction", tempAnimParam);
updateAnimation = function(mats){
@ -239,6 +246,10 @@ class BoneAnimation extends Animation {
override public function update(delta: FastFloat) {
this.delta = delta;
if (!isSkinned && skeletonBones == null) setAction(armature.actions[0].name);
// TODO: double check skip culling for skinned meshes if they need animation updates for bounds
// if (object != null && !object.visible) return;
if (object != null && (!object.visible || object.culled)) return;
if (skeletonBones == null || skeletonBones.length == 0) return;
@ -248,7 +259,6 @@ class BoneAnimation extends Animation {
super.update(delta);
if(updateAnimation != null) {
updateAnimation(skeletonMats);
}
@ -401,6 +411,7 @@ class BoneAnimation extends Animation {
}
var bones = sampler.getBoneAction();
if (bones == null) return;
for(b in bones){
if (b.anim != null) {
updateTrack(b.anim, sampler);
@ -410,13 +421,14 @@ class BoneAnimation extends Animation {
}
public function sampleAction(sampler: ActionSampler, actionMats: Array<Mat4>) {
if(! sampler.actionDataInit) {
var bones = getAction(sampler.action);
sampler.setBoneAction(bones);
}
var bones = sampler.getBoneAction();
if (bones == null) return;
actionMats[skeletonBones.length].setIdentity();
var rootMotionEnabled = sampler.rootMotionPos || sampler.rootMotionRot;
for (i in 0...bones.length) {
@ -427,7 +439,6 @@ class BoneAnimation extends Animation {
updateAnimSampled(bones[i].anim, actionMats[i], sampler);
}
}
}
function updateAnimSampled(anim: TAnimation, mm: Mat4, sampler: ActionSampler) {
@ -588,6 +599,9 @@ class BoneAnimation extends Animation {
public override function getTotalFrames(sampler: ActionSampler): Int {
var bones = getAction(sampler.action);
if (bones == null){
return 0;
}
var track = bones[0].anim.tracks[0];
return Std.int(track.frames[track.frames.length - 1] - track.frames[0]);
}
@ -1048,9 +1062,9 @@ class BoneAnimation extends Animation {
var rootLen = root.bone_length * rootMat.getScale().x;
// Get distance form root to goal
var goalLen = Math.abs(Vec4.distance(rootMat.getLoc(), goal));
var goalLen: FastFloat = Math.abs(Vec4.distance(rootMat.getLoc(), goal));
var totalLength = effectorLen + rootLen;
var totalLength: FastFloat = effectorLen + rootLen;
// Get tip location of effector bone
var effectorTipPos = new Vec4().setFrom(effectorMat.look()).normalize();
@ -1070,7 +1084,7 @@ class BoneAnimation extends Animation {
// Get unit vector of effector bone
var vectorEffector = new Vec4().setFrom(effectorMat.look()).normalize();
// Get dot product of vectors
var dot = new Vec4().setFrom(vectorRootEffector).dot(vectorRoot);
// Calmp between -1 and 1

View File

@ -42,9 +42,10 @@ class CameraObject extends Object {
this.data = data;
#if lnx_vr
iron.system.VR.initButton();
#end
// dont just auto initialize VR button - headset trait controls VR
// #if lnx_vr
// iron.system.VR.initButton();
// #end
buildProjection();
@ -85,7 +86,14 @@ class CameraObject extends Object {
projectionJitter();
#end
// matrices are set by VR system so avoid rebuilding transforms unless its in preview/not presenting
#if (kha_webgl && lnx_vr)
if (@:privateAccess !RenderPath.isVRPresenting()) {
buildMatrix();
}
#else
buildMatrix();
#end
RenderPath.active.renderFrame(g);

View File

@ -59,6 +59,9 @@ class LightObject extends Object {
public static var clustersData: kha.Image = null;
static var lpos = new Vec4();
public static var LWVPMatrixArray: Float32Array = null;
#if lnx_vr
static var originalLightPositions: Float32Array = null;
#end
#end // lnx_clusters
public var V: Mat4 = Mat4.identity();
@ -519,7 +522,7 @@ class LightObject extends Object {
updateLightsArray(); // TODO: only update on light change
}
static function updateLightsArray() {
public static function updateLightsArray() {
if (lightsArray == null) { // vec4x3 - 1: pos, a, color, b, 2: dir, c
lightsArray = new Float32Array(maxLights * 4 * 3);
#if lnx_spot
@ -578,6 +581,49 @@ class LightObject extends Object {
}
}
// VR deferred stereo we save original light positions before adjusting for per-eye rendering
#if lnx_vr
public static function saveOriginalLightPositions() {
if (lightsArray == null) return;
if (originalLightPositions == null) {
originalLightPositions = new Float32Array(lightsArray.length);
}
for (i in 0...lightsArray.length) {
originalLightPositions[i] = lightsArray[i];
}
}
// negative for left eye, positive for right eye
public static function adjustLightPositionsForVREye(offsetX: Float, rightVec: Vec4) {
if (lightsArray == null) return;
var lights = Scene.active.lights;
var n = lights.length > maxLights ? maxLights : lights.length;
var i = 0;
for (l in lights) {
if (discardLightCulled(l)) continue;
if (i >= n) break;
lightsArray[i * 12 ] = originalLightPositions[i * 12 ] + rightVec.x * offsetX;
lightsArray[i * 12 + 1] = originalLightPositions[i * 12 + 1] + rightVec.y * offsetX;
lightsArray[i * 12 + 2] = originalLightPositions[i * 12 + 2] + rightVec.z * offsetX;
i++;
}
}
public static function restoreOriginalLightPositions() {
if (lightsArray == null || originalLightPositions == null) return;
for (i in 0...lightsArray.length) {
lightsArray[i] = originalLightPositions[i];
}
}
#end
public static function updateLWVPMatrixArray(object: Object, type: String) {
if (LWVPMatrixArray == null) {
LWVPMatrixArray = new Float32Array(maxLightsCluster * 16);
@ -629,8 +675,8 @@ class LightObject extends Object {
LWVPMatrixArray[i * 16 + 13] = m._31;
LWVPMatrixArray[i * 16 + 14] = m._32;
LWVPMatrixArray[i * 16 + 15] = m._33;
i++; // only increment in light type
}
i++;
}
return LWVPMatrixArray;
}

View File

@ -20,6 +20,13 @@ class MorphTarget {
public var morphDataPos: Image;
public var morphDataNor: Image;
public var morphMap: Map<String, Int> = null;
public var isDirty: Bool = true;
var previousWeights: Float32Array;
var changeThreshold: FastFloat = 0.001; // skip smaller
var pendingUpdates: Map<Int, Float> = null;
var batchUpdateEnabled: Bool = true;
var lastFlushFrame: Int = 0;
public function new(data: TMorphTarget) {
initWeights(data.morph_target_defaults);
@ -42,6 +49,14 @@ class MorphTarget {
morphMap.set(name, i);
i++;
}
previousWeights = new Float32Array(morphWeights.length);
for (i in 0...morphWeights.length) {
previousWeights.set(i, morphWeights.get(i));
}
// batch system
pendingUpdates = new Map<Int, Float>();
}
inline function initWeights(defaults: Float32Array) {
@ -54,9 +69,96 @@ class MorphTarget {
public function setMorphValue(name: String, value: Float) {
var i = morphMap.get(name);
if (i != null) {
morphWeights.set(i, value);
if (batchUpdateEnabled) {
pendingUpdates.set(i, value);
} else {
setMorphValueDirect(i, value);
}
}
}
// faster indexed access
public inline function setMorphValueDirect(index: Int, value: Float) {
var current = morphWeights.get(index);
// allow explicit zero values to reset
if (value == 0.0 && current != 0.0) {
morphWeights.set(index, value);
isDirty = true;
return;
}
var delta = value - current;
if (delta < -changeThreshold || delta > changeThreshold) {
morphWeights.set(index, value);
isDirty = true;
}
}
// flush pending batch
public function flushBatchedUpdates() {
if (pendingUpdates.keys().hasNext()) {
var anyChanged = false;
var hasZeros = false;
for (index in pendingUpdates.keys()) {
var value = pendingUpdates.get(index);
if (value == null) continue;
if (value == 0.0) hasZeros = true;
var current = morphWeights.get(index);
var delta = value - current;
if (value == 0.0 && current != 0.0) {
try{
morphWeights.set(index, cast value);
}catch(e){
trace("ERROR: " + e);
}
anyChanged = true;
}
else if (delta < -changeThreshold || delta > changeThreshold) {
morphWeights.set(index, cast value);
anyChanged = true;
}
}
pendingUpdates.clear();
if (anyChanged || hasZeros) {
isDirty = true;
}
}
}
public inline function markClean() {
isDirty = false;
for (i in 0...morphWeights.length) {
previousWeights.set(i, morphWeights.get(i));
}
}
public inline function markDirty() {
isDirty = true;
}
// toggle batch mode
public inline function setBatchMode(enabled: Bool) {
if (!enabled && batchUpdateEnabled) {
flushBatchedUpdates();
}
batchUpdateEnabled = enabled;
}
public function resetAllWeights() {
for (i in 0...morphWeights.length) {
morphWeights.set(i, 0.0);
}
pendingUpdates.clear();
isDirty = true;
}
}
#end

View File

@ -210,8 +210,12 @@ class Object {
}
#if lnx_skin
public function getBoneAnimation(armatureUid): BoneAnimation {
for (a in Scene.active.animations) if (a.armature != null && a.armature.uid == armatureUid) return cast a;
public function getBoneAnimation(armatureUid: Int): BoneAnimation {
for (a in Scene.active.animations) {
if (a.armature != null && a.armature.uid == armatureUid) {
return cast a;
}
}
return null;
}
#else

View File

@ -97,7 +97,9 @@ class ObjectAnimation extends Animation {
}
public override function getTotalFrames(sampler: ActionSampler): Int {
var track = getAction(sampler.action).anim.tracks[0];
var action = getAction(sampler.action);
if (action == null || action.anim == null || action.anim.tracks == null || action.anim.tracks.length == 0) return 0;
var track = action.anim.tracks[0];
return Std.int(track.frames[track.frames.length - 1] - track.frames[0]);
}

View File

@ -7,16 +7,20 @@ import kha.graphics4.TextureFilter;
import kha.graphics4.MipMapFilter;
import kha.arrays.Float32Array;
import iron.math.Vec4;
import iron.math.Mat4;
import iron.math.Quat;
import iron.math.Mat3;
import iron.math.Mat4;
import iron.data.WorldData;
import iron.data.MaterialData;
import iron.data.ShaderData;
import iron.data.SceneFormat;
import iron.data.WorldData;
import iron.data.SceneFormat.TShaderConstant;
import iron.data.SceneFormat.TBindConstant;
import iron.object.Transform;
import iron.object.LightObject;
import iron.Scene;
import iron.RenderPath;
import iron.system.Input;
import iron.system.Time;
import iron.RenderPath;
using StringTools;
// Structure for setting shader uniforms
@ -38,6 +42,7 @@ class Uniforms {
public static var helpMat = Mat4.identity();
public static var helpMat2 = Mat4.identity();
public static var helpMat3 = Mat3.identity();
public static var helpMat4 = Mat4.identity();
public static var helpVec = new Vec4();
public static var helpVec2 = new Vec4();
public static var helpQuat = new Quat(); // Keep at identity
@ -47,6 +52,10 @@ class Uniforms {
public static var externalVec4Links: Array<Object->MaterialData->String->Vec4> = null;
public static var externalVec3Links: Array<Object->MaterialData->String->Vec4> = null;
public static var externalVec2Links: Array<Object->MaterialData->String->Vec4> = null;
public static var eyeLeftCallCount = 0;
public static var lastFrameChecked = -1;
public static var externalFloatLinks: Array<Object->MaterialData->String->Null<kha.FastFloat>> = null;
public static var externalFloatsLinks: Array<Object->MaterialData->String->Float32Array> = null;
public static var externalIntLinks: Array<Object->MaterialData->String->Null<Int>> = null;
@ -59,6 +68,10 @@ class Uniforms {
public static var defaultFilter = TextureFilter.LinearFilter;
#end
#if lnx_morph_target
public static var forceUploadMorphWeights: Bool = false;
#end
public static function setContextConstants(g: Graphics, context: ShaderContext, bindParams: Array<String>) {
if (context.raw.constants != null) {
for (i in 0...context.raw.constants.length) {
@ -181,11 +194,15 @@ class Uniforms {
// Multiple voxel volumes, always set params
g.setImageTexture(context.textureUnits[j], rt.image); // image2D/3D
if (rt.raw.name.startsWith("voxels_")) {
g.setTextureParameters(context.textureUnits[j], TextureAddressing.Clamp, TextureAddressing.Clamp, TextureFilter.LinearFilter, TextureFilter.LinearFilter, MipMapFilter.NoMipFilter);
g.setTextureParameters(context.textureUnits[j], TextureAddressing.Clamp, TextureAddressing.Clamp, TextureFilter.LinearFilter, TextureFilter.LinearFilter, MipMapFilter.LinearMipFilter);
}
else if (rt.raw.name.startsWith("voxelsSDF"))
{
g.setTexture3DParameters(context.textureUnits[j], TextureAddressing.Clamp, TextureAddressing.Clamp, TextureAddressing.Clamp, TextureFilter.PointFilter, TextureFilter.PointFilter, MipMapFilter.NoMipFilter);
}
else if (rt.raw.name.startsWith("voxels"))
{
g.setTexture3DParameters(context.textureUnits[j], TextureAddressing.Clamp, TextureAddressing.Clamp, TextureAddressing.Clamp, TextureFilter.LinearFilter, TextureFilter.LinearFilter, MipMapFilter.NoMipFilter);
g.setTexture3DParameters(context.textureUnits[j], TextureAddressing.Clamp, TextureAddressing.Clamp, TextureAddressing.Clamp, TextureFilter.LinearFilter, TextureFilter.LinearFilter, MipMapFilter.PointMipFilter);
}
else
{
@ -286,6 +303,89 @@ class Uniforms {
helpMat.getInverse(helpMat);
m = helpMat;
}
#if lnx_vr
case "_inverseViewProjectionMatrixLeft": {
var vr = kha.vr.VrInterface.instance;
if (vr != null && vr.IsPresenting()) {
var leftView = vr.GetViewMatrix(0);
var leftProj = vr.GetProjectionMatrix(0);
helpMat._00 = leftView._00; helpMat._01 = leftView._01; helpMat._02 = leftView._02; helpMat._03 = leftView._03;
helpMat._10 = leftView._10; helpMat._11 = leftView._11; helpMat._12 = leftView._12; helpMat._13 = leftView._13;
helpMat._20 = leftView._20; helpMat._21 = leftView._21; helpMat._22 = leftView._22; helpMat._23 = leftView._23;
helpMat._30 = leftView._30; helpMat._31 = leftView._31; helpMat._32 = leftView._32; helpMat._33 = leftView._33;
helpMat2._00 = leftProj._00; helpMat2._01 = leftProj._01; helpMat2._02 = leftProj._02; helpMat2._03 = leftProj._03;
helpMat2._10 = leftProj._10; helpMat2._11 = leftProj._11; helpMat2._12 = leftProj._12; helpMat2._13 = leftProj._13;
helpMat2._20 = leftProj._20; helpMat2._21 = leftProj._21; helpMat2._22 = leftProj._22; helpMat2._23 = leftProj._23;
helpMat2._30 = leftProj._30; helpMat2._31 = leftProj._31; helpMat2._32 = leftProj._32; helpMat2._33 = leftProj._33;
helpMat.multmat(helpMat2);
helpMat.getInverse(helpMat);
} else if (iron.RenderPath.isVRSimulateMode()) {
var ipd_offset = 0.032 * 35.0; // Match eye offset
var rightVec = camera.rightWorld();
var eyeLeftX = camera.transform.worldx() - rightVec.x * ipd_offset;
var eyeLeftY = camera.transform.worldy() - rightVec.y * ipd_offset;
var eyeLeftZ = camera.transform.worldz() - rightVec.z * ipd_offset;
helpMat.setFrom(camera.transform.world);
helpMat._30 = eyeLeftX;
helpMat._31 = eyeLeftY;
helpMat._32 = eyeLeftZ;
helpMat.getInverse(helpMat); // Now it's a view matrix
helpMat.multmat(camera.P);
helpMat.getInverse(helpMat);
} else {
helpMat.setFrom(camera.V);
helpMat.multmat(camera.P);
helpMat.getInverse(helpMat);
}
m = helpMat;
}
case "_inverseViewProjectionMatrixRight": {
var vr = kha.vr.VrInterface.instance;
if (vr != null && vr.IsPresenting()) {
var rightView = vr.GetViewMatrix(1);
var rightProj = vr.GetProjectionMatrix(1);
// kha.math.FastMatrix4 to iron.math.Mat4
helpMat2._00 = rightView._00; helpMat2._01 = rightView._01; helpMat2._02 = rightView._02; helpMat2._03 = rightView._03;
helpMat2._10 = rightView._10; helpMat2._11 = rightView._11; helpMat2._12 = rightView._12; helpMat2._13 = rightView._13;
helpMat2._20 = rightView._20; helpMat2._21 = rightView._21; helpMat2._22 = rightView._22; helpMat2._23 = rightView._23;
helpMat2._30 = rightView._30; helpMat2._31 = rightView._31; helpMat2._32 = rightView._32; helpMat2._33 = rightView._33;
helpMat4._00 = rightProj._00; helpMat4._01 = rightProj._01; helpMat4._02 = rightProj._02; helpMat4._03 = rightProj._03;
helpMat4._10 = rightProj._10; helpMat4._11 = rightProj._11; helpMat4._12 = rightProj._12; helpMat4._13 = rightProj._13;
helpMat4._20 = rightProj._20; helpMat4._21 = rightProj._21; helpMat4._22 = rightProj._22; helpMat4._23 = rightProj._23;
helpMat4._30 = rightProj._30; helpMat4._31 = rightProj._31; helpMat4._32 = rightProj._32; helpMat4._33 = rightProj._33;
helpMat2.multmat(helpMat4);
helpMat2.getInverse(helpMat2);
m = helpMat2;
} else if (iron.RenderPath.isVRSimulateMode()) {
var ipd_offset = 0.032 * 35.0;
var rightVec = camera.rightWorld();
// calculate right eye position in world space
var eyeRightX = camera.transform.worldx() + rightVec.x * ipd_offset;
var eyeRightY = camera.transform.worldy() + rightVec.y * ipd_offset;
var eyeRightZ = camera.transform.worldz() + rightVec.z * ipd_offset;
helpMat2.setFrom(camera.transform.world);
helpMat2._30 = eyeRightX;
helpMat2._31 = eyeRightY;
helpMat2._32 = eyeRightZ;
helpMat2.getInverse(helpMat2);
helpMat2.multmat(camera.P);
helpMat2.getInverse(helpMat2);
m = helpMat2;
} else {
// fallback to center camera
helpMat2.setFrom(camera.V);
helpMat2.multmat(camera.P);
helpMat2.getInverse(helpMat2);
m = helpMat2;
}
}
#end
case "_viewProjectionMatrix": {
#if lnx_centerworld
m = vmat(camera.V);
@ -398,6 +498,28 @@ class Uniforms {
v = helpVec;
}
}
#if lnx_vr
case "_pointPositionLeft": {
var point = RenderPath.active.point;
if (point != null) {
var lightWorldX = point.transform.worldx();
var lightWorldY = point.transform.worldy();
var lightWorldZ = point.transform.worldz();
helpVec.set(lightWorldX, lightWorldY, lightWorldZ);
v = helpVec;
}
}
case "_pointPositionRight": {
var point = RenderPath.active.point;
if (point != null) {
var lightWorldX = point.transform.worldx();
var lightWorldY = point.transform.worldy();
var lightWorldZ = point.transform.worldz();
helpVec.set(lightWorldX, lightWorldY, lightWorldZ);
v = helpVec;
}
}
#end
#if lnx_spot
case "_spotDirection": {
var point = RenderPath.active.point;
@ -484,6 +606,84 @@ class Uniforms {
helpVec = camera.rightWorld().normalize();
v = helpVec;
}
#if lnx_vr
case "_eyeLeft": {
var currentFrame = iron.RenderPath.active.frame;
if (currentFrame != lastFrameChecked) {
eyeLeftCallCount = 0;
lastFrameChecked = currentFrame;
}
eyeLeftCallCount++;
var vr = kha.vr.VrInterface.instance;
if (vr != null && vr.IsPresenting()) {
var leftViewMatrix = vr.GetViewMatrix(0);
var invLeft = leftViewMatrix.inverse();
helpVec.set(invLeft._30, invLeft._31, invLeft._32);
// trace("eyeLeft: " + helpVec.x + ", " + helpVec.y + ", " + helpVec.z);
} else if (iron.RenderPath.isVRSimulateMode()) {
var ipd_offset = 0.032 * 35.0;
var rightVec = camera.rightWorld();
var centerX = camera.transform.worldx();
var centerY = camera.transform.worldy();
var centerZ = camera.transform.worldz();
helpVec.set(
centerX - rightVec.x * ipd_offset,
centerY - rightVec.y * ipd_offset,
centerZ - rightVec.z * ipd_offset
);
} else {
helpVec.set(camera.transform.worldx(), camera.transform.worldy(), camera.transform.worldz());
}
v = helpVec;
}
case "_eyeRight": {
var vr = kha.vr.VrInterface.instance;
if (vr != null && vr.IsPresenting()) {
var rightViewMatrix = vr.GetViewMatrix(1);
var invRight = rightViewMatrix.inverse();
helpVec.set(invRight._30, invRight._31, invRight._32);
} else if (iron.RenderPath.isVRSimulateMode()) {
var ipd_offset = 0.032 * 35.0;
var rightVec = camera.rightWorld();
var centerX = camera.transform.worldx();
var centerY = camera.transform.worldy();
var centerZ = camera.transform.worldz();
helpVec.set(
centerX + rightVec.x * ipd_offset,
centerY + rightVec.y * ipd_offset,
centerZ + rightVec.z * ipd_offset
);
} else {
helpVec.set(camera.transform.worldx(), camera.transform.worldy(), camera.transform.worldz());
}
v = helpVec;
}
case "_eyeLookLeft": {
var vr = kha.vr.VrInterface.instance;
if (vr != null && vr.IsPresenting()) {
var leftViewMatrix = vr.GetViewMatrix(0);
var invLeft = leftViewMatrix.inverse();
helpVec.set(-invLeft._20, -invLeft._21, -invLeft._22);
helpVec.normalize();
} else {
helpVec = camera.lookWorld().normalize();
}
v = helpVec;
}
case "_eyeLookRight": {
var vr = kha.vr.VrInterface.instance;
if (vr != null && vr.IsPresenting()) {
var rightViewMatrix = vr.GetViewMatrix(1);
var invRight = rightViewMatrix.inverse();
helpVec.set(-invRight._20, -invRight._21, -invRight._22);
helpVec.normalize();
} else {
helpVec = camera.lookWorld().normalize();
}
v = helpVec;
}
#end
case "_backgroundCol": {
if (camera.data.raw.clear_color != null) helpVec.set(camera.data.raw.clear_color[0], camera.data.raw.clear_color[1], camera.data.raw.clear_color[2]);
v = helpVec;
@ -1161,7 +1361,19 @@ class Uniforms {
#end // lnx_clusters
#if lnx_morph_target
case "_morphWeights": {
fa = cast(object, MeshObject).morphTarget.morphWeights;
var morphTarget = cast(object, MeshObject).morphTarget;
morphTarget.flushBatchedUpdates();
if (forceUploadMorphWeights) {
fa = morphTarget.morphWeights;
}
else {
if (morphTarget.isDirty) {
fa = morphTarget.morphWeights;
}
else {
return;
}
}
}
#end
}
@ -1175,6 +1387,12 @@ class Uniforms {
if (fa == null) return;
g.setFloats(location, fa);
#if lnx_morph_target
if (c.link == "_morphWeights") {
cast(object, MeshObject).morphTarget.markClean();
}
#end
}
else if (c.type == "int") {
var i: Null<Int> = null;
@ -1203,6 +1421,7 @@ class Uniforms {
if (materialContext.raw.bind_constants != null) {
for (i in 0...materialContext.raw.bind_constants.length) {
var matc = materialContext.raw.bind_constants[i];
if (matc == null) continue;
var pos = -1;
for (i in 0...context.raw.constants.length) {
if (context.raw.constants[i].name == matc.name) {

View File

@ -3,6 +3,14 @@ package iron.system;
class Time {
public static var scale = 1.0;
// TODO: VR Frame Time Override - used to sync physics with VR headset refresh rate
#if lnx_vr
public static var vrFrameTime: Float = -1.0; // VR frame time in seconds (-1 = not in VR)
static var lastVRFrameTime: Float = 0.0;
static var vrFrameCount: Int = 0;
static var normalModeLogged: Bool = false;
#end
static var frequency: Null<Int> = null;
static function initFrequency() {
frequency = kha.Display.primary != null ? kha.Display.primary.frequency : 60;
@ -47,6 +55,24 @@ class Time {
}
public static function update() {
#if lnx_vr
// TODO: use VR frame time when in VR present mode to sync physics with headset refresh
if (vrFrameTime >= 0.0) {
if (lastVRFrameTime > 0.0) {
_delta = vrFrameTime - lastVRFrameTime;
} else {
_delta = 1.0 / 90.0; // Default to 90Hz for first VR frame
}
lastVRFrameTime = vrFrameTime;
return;
} else {
if (!normalModeLogged) {
normalModeLogged = true;
}
}
#end
_delta = realTime() - lastTime;
lastTime = realTime();
}

View File

@ -0,0 +1,138 @@
package iron.system;
#if lnx_vr
import iron.math.Vec4;
import iron.math.Quat;
class VRController {
public static var leftHandPosition: Vec4 = new Vec4();
public static var leftHandRotation: Quat = new Quat();
public static var rightHandPosition: Vec4 = new Vec4();
public static var rightHandRotation: Quat = new Quat();
public static var leftHandActive: Bool = false;
public static var rightHandActive: Bool = false;
public static var leftThumbstickX: Float = 0.0;
public static var leftThumbstickY: Float = 0.0;
public static var rightThumbstickX: Float = 0.0;
public static var rightThumbstickY: Float = 0.0;
public static var leftTrigger: Float = 0.0;
public static var rightTrigger: Float = 0.0;
public static var leftGrip: Float = 0.0;
public static var rightGrip: Float = 0.0;
public static var leftButtonX: Bool = false;
public static var leftButtonY: Bool = false;
public static var rightButtonA: Bool = false;
public static var rightButtonB: Bool = false;
public static var debugLog:Bool = false;
public static function enableDebug() {
debugLog = true;
}
public static function disableDebug() {
debugLog = false;
}
public static function updatePoses() {
var vr: kha.js.vr.VrInterface = cast kha.vr.VrInterface.instance;
if (vr == null || !vr.IsPresenting()) {
if (debugLog) trace("[VRController] Not presenting or VR null");
leftHandActive = false;
rightHandActive = false;
return;
}
untyped window._vrControllerFrame = (untyped window._vrControllerFrame || 0) + 1;
leftHandActive = false;
rightHandActive = false;
leftButtonX = false;
leftButtonY = false;
rightButtonA = false;
rightButtonB = false;
var refSpace = untyped vr.xrRefSpace;
if (vr.currentInputSources == null || vr.currentFrame == null || refSpace == null) {
return;
}
var inputSources = vr.currentInputSources;
var frame = vr.currentFrame;
var sourceCount:Int = untyped inputSources.length;
for (i in 0...sourceCount) {
var inputSource = untyped inputSources[i];
if (inputSource == null) continue;
var handedness = untyped inputSource.handedness; // "left", "right", or "none"
var gripSpace = untyped inputSource.gripSpace;
var targetRaySpace = untyped inputSource.targetRaySpace;
// use targetRaySpace first laser/pointer and fall back to gripSpace
var space = (targetRaySpace != null) ? targetRaySpace : gripSpace;
if (space == null) {
continue;
}
var pose = untyped frame.getPose(space, refSpace);
if (pose == null || pose.transform == null) {
continue;
}
var transform = pose.transform;
var pos = transform.position;
var orient = transform.orientation;
if (handedness == "left") {
leftHandPosition.set(pos.x, pos.y, pos.z);
leftHandRotation.set(orient.x, orient.y, orient.z, orient.w);
leftHandActive = true;
var gamepad = untyped inputSource.gamepad;
if (gamepad != null) {
// [0]=thumbstickX [1]=thumbstickY [2]=touchpadX [3]=touchpadY
if (gamepad.axes != null && gamepad.axes.length >= 2) {
leftThumbstickX = gamepad.axes[0];
leftThumbstickY = gamepad.axes[1];
}
// [0]=trigger [1]=grip [4]=X [5]=Y
if (gamepad.buttons != null) {
if (gamepad.buttons.length > 0) leftTrigger = gamepad.buttons[0].value;
if (gamepad.buttons.length > 1) leftGrip = gamepad.buttons[1].value;
if (gamepad.buttons.length > 4) leftButtonX = gamepad.buttons[4].pressed;
if (gamepad.buttons.length > 5) leftButtonY = gamepad.buttons[5].pressed;
}
}
}
else if (handedness == "right") {
rightHandPosition.set(pos.x, pos.y, pos.z);
rightHandRotation.set(orient.x, orient.y, orient.z, orient.w);
rightHandActive = true;
var gamepad = untyped inputSource.gamepad;
if (gamepad != null) {
if (gamepad.axes != null && gamepad.axes.length >= 2) {
rightThumbstickX = gamepad.axes[0];
rightThumbstickY = gamepad.axes[1];
}
if (gamepad.buttons != null) {
if (gamepad.buttons.length > 0) rightTrigger = gamepad.buttons[0].value;
if (gamepad.buttons.length > 1) rightGrip = gamepad.buttons[1].value;
if (gamepad.buttons.length > 4) rightButtonA = gamepad.buttons[4].pressed;
if (gamepad.buttons.length > 5) rightButtonB = gamepad.buttons[5].pressed;
}
}
}
}
}
}
#end

View File

@ -44,6 +44,7 @@ typedef TConfig = {
@:optional var rp_supersample: Null<Float>;
@:optional var rp_shadowmap_cube: Null<Int>; // size
@:optional var rp_shadowmap_cascade: Null<Int>; // size for single cascade
@:optional var rp_ssao: Null<Bool>;
@:optional var rp_ssgi: Null<Bool>;
@:optional var rp_ssr: Null<Bool>;
@:optional var rp_ssrefr: Null<Bool>;

View File

@ -2,13 +2,16 @@ package leenkx.logicnode;
import iron.object.Object;
#if lnx_bullet
import leenkx.trait.physics.PhysicsConstraint;
import leenkx.trait.physics.bullet.PhysicsConstraint.ConstraintType;
#elseif lnx_oimo
// TODO
#if lnx_physics
import leenkx.trait.physics.PhysicsConstraint;
#if lnx_bullet
import leenkx.trait.physics.bullet.PhysicsConstraint.ConstraintType;
#elseif lnx_jolt
import leenkx.trait.physics.jolt.PhysicsConstraint.ConstraintType;
#else
import leenkx.trait.physics.oimo.PhysicsConstraint.ConstraintType;
#end
#end
class AddPhysicsConstraintNode extends LogicNode {
public var property0: String;//Type
@ -27,7 +30,7 @@ class AddPhysicsConstraintNode extends LogicNode {
if (pivotObject == null || rb1 == null || rb2 == null) return;
#if lnx_bullet
#if lnx_physics
var disableCollisions: Bool = inputs[4].get();
var breakable: Bool = inputs[5].get();
@ -110,8 +113,6 @@ class AddPhysicsConstraintNode extends LogicNode {
}
pivotObject.addTrait(con);
}
#elseif lnx_oimo
// TODO
#end
runOutput(0);
}

View File

@ -37,86 +37,90 @@ class CreateLeenkxNode extends LogicNode {
function onEvent() {
#if js
var window:haxe.DynamicAccess<Dynamic> = untyped js.Browser.window;
var lxCxNew = window.get('lxNew');
var lnxjs:Dynamic = js.Lib.global;
var lnxCxNew = lnxjs.lnxNew;
if (lnxCxNew == null) {
trace("ERROR: lnxNew not available");
return;
}
var lxCn:Dynamic = lxCxNew(net_Url);
lxCn.on("connections", function(c) {
leenkx.network.Leenkx.data.set(net_Url, c + 1);
leenkx.network.Leenkx.connections[net_Url].onconnections();
});
lxCn.on("message", function(address,message) {
leenkx.network.Leenkx.data.set(net_Url, message);
leenkx.network.Leenkx.id.set(net_Url, address);
leenkx.network.Leenkx.connections[net_Url].onmessage();
});
lxCn.on("seen", function(address) {
leenkx.network.Leenkx.id.set(net_Url, address);
leenkx.network.Leenkx.connections[net_Url].onseen();
});
lxCn.on("left", function(address) {
leenkx.network.Leenkx.id.set(net_Url, address);
leenkx.network.Leenkx.connections[net_Url].onleft();
});
lxCn.on("server", function(address) {
leenkx.network.Leenkx.id.set(net_Url, address);
leenkx.network.Leenkx.connections[net_Url].onserver();
});
lxCn.on("ping", function(address) {
leenkx.network.Leenkx.id.set(net_Url, address);
leenkx.network.Leenkx.connections[net_Url].onping();
});
lxCn.on("timeout", function(address) {
leenkx.network.Leenkx.id.set(net_Url, address);
leenkx.network.Leenkx.connections[net_Url].ontimeout();
});
lxCn.on("rpc", function(address, call, args, nonce) {
leenkx.network.Leenkx.data.set(net_Url, call);
leenkx.network.Leenkx.id.set(net_Url, address);
call(args);
leenkx.network.Leenkx.connections[net_Url].onrpc();
});
lxCn.on("rpc-response", function(address, nonce, response) {
leenkx.network.Leenkx.id.set(net_Url, address);
leenkx.network.Leenkx.connections[net_Url].onrpcresponse();
});
lxCn.on("wireleft", function(wirecount, wire) {
leenkx.network.Leenkx.data.set(net_Url, wirecount);
leenkx.network.Leenkx.id.set(net_Url, wire.peerId);
leenkx.network.Leenkx.connections[net_Url].onwireleft();
});
lxCn.on("wireseen", function(wirecount, wire) {
leenkx.network.Leenkx.data.set(net_Url, wirecount);
leenkx.network.Leenkx.id.set(net_Url, wire.peerId);
leenkx.network.Leenkx.connections[net_Url].onwireseen();
});
lxCn.on("torrent", function(identifier, torrent) {
leenkx.network.Leenkx.data.set(net_Url, torrent);
leenkx.network.Leenkx.id.set(net_Url, identifier);
leenkx.network.Leenkx.connections[net_Url].ontorrent();
});
lxCn.on("tracker", function(identifier) {
leenkx.network.Leenkx.id.set(net_Url, identifier);
leenkx.network.Leenkx.connections[net_Url].ontracker();
});
lxCn.on("announce", function(identifier) {
leenkx.network.Leenkx.id.set(net_Url, identifier);
leenkx.network.Leenkx.connections[net_Url].onannounce();
});
window.set("lx_" + net_Url, lxCn);
Leenkx.connections[net_Url].client = lxCn;
var script = '
window.addEventListener("beforeunload", function (e) {
leenkx.network.Leenkx.connections.h["' + net_Url + '"].client.destroy();
delete e["returnValue"];
});
';
js.Syntax.code('(1, eval)({0})', script.toString());
runOutput(0);
#end
var lnxCn:Dynamic = lnxCxNew(net_Url);
lnxCn.on("connections", function(c) {
leenkx.network.Leenkx.data.set(net_Url, c + 1);
leenkx.network.Leenkx.connections[net_Url].onconnections();
});
lnxCn.on("message", function(address, message) {
leenkx.network.Leenkx.data.set(net_Url, message);
leenkx.network.Leenkx.id.set(net_Url, address);
leenkx.network.Leenkx.connections[net_Url].onmessage();
});
lnxCn.on("seen", function(address) {
leenkx.network.Leenkx.id.set(net_Url, address);
leenkx.network.Leenkx.connections[net_Url].onseen();
});
lnxCn.on("left", function(address) {
leenkx.network.Leenkx.id.set(net_Url, address);
leenkx.network.Leenkx.connections[net_Url].onleft();
});
lnxCn.on("server", function(address) {
leenkx.network.Leenkx.id.set(net_Url, address);
leenkx.network.Leenkx.connections[net_Url].onserver();
});
lnxCn.on("ping", function(address) {
leenkx.network.Leenkx.id.set(net_Url, address);
leenkx.network.Leenkx.connections[net_Url].onping();
});
lnxCn.on("timeout", function(address) {
leenkx.network.Leenkx.id.set(net_Url, address);
leenkx.network.Leenkx.connections[net_Url].ontimeout();
});
lnxCn.on("rpc", function(address, call, args, nonce) {
leenkx.network.Leenkx.data.set(net_Url, call);
leenkx.network.Leenkx.id.set(net_Url, address);
call(args);
leenkx.network.Leenkx.connections[net_Url].onrpc();
});
lnxCn.on("rpc-response", function(address, nonce, response) {
leenkx.network.Leenkx.id.set(net_Url, address);
leenkx.network.Leenkx.connections[net_Url].onrpcresponse();
});
lnxCn.on("wireleft", function(wirecount, wire) {
leenkx.network.Leenkx.data.set(net_Url, wirecount);
leenkx.network.Leenkx.id.set(net_Url, wire.peerId);
leenkx.network.Leenkx.connections[net_Url].onwireleft();
});
lnxCn.on("wireseen", function(wirecount, wire) {
leenkx.network.Leenkx.data.set(net_Url, wirecount);
leenkx.network.Leenkx.id.set(net_Url, wire.peerId);
leenkx.network.Leenkx.connections[net_Url].onwireseen();
});
lnxCn.on("torrent", function(identifier, torrent) {
leenkx.network.Leenkx.data.set(net_Url, torrent);
leenkx.network.Leenkx.id.set(net_Url, identifier);
leenkx.network.Leenkx.connections[net_Url].ontorrent();
});
lnxCn.on("tracker", function(identifier) {
leenkx.network.Leenkx.id.set(net_Url, identifier);
leenkx.network.Leenkx.connections[net_Url].ontracker();
});
lnxCn.on("announce", function(identifier) {
leenkx.network.Leenkx.id.set(net_Url, identifier);
leenkx.network.Leenkx.connections[net_Url].onannounce();
});
Reflect.setField(lnxjs, "lnx_" + net_Url, lnxCn);
Leenkx.connections[net_Url].client = lnxCn;
var script = 'globalThis.addEventListener("beforeunload", function (e) {
leenkx.network.Leenkx.connections.h["' + net_Url + '"].client.destroy();
delete e["returnValue"];
});';
js.Syntax.code('(1, eval)({0})', script);
runOutput(0);
#end
}

View File

@ -32,10 +32,15 @@ class LeenkxCloseConnectionNode extends LogicNode {
} else {
var script = '
try{
leenkx.network.Leenkx.connections.h["1008"].client.torrent._peers[p].conn._pc.close();
leenkx.network.Leenkx.connections.h["1008"].client.torrent._peers[p].conn.destroy();
leenkx.network.Leenkx.id.set("1008",p);
leenkx.network.Leenkx.connections.h["1008"].onclose();
var lnxConn = leenkx.network.Leenkx.connections.h["' + connection._url + '"];
if (lnxConn && lnxConn.client && lnxConn.client.torrent && lnxConn.client.torrent._peers) {
for (var p in lnxConn.client.torrent._peers) {
lnxConn.client.torrent._peers[p].conn._pc.close();
lnxConn.client.torrent._peers[p].conn.destroy();
leenkx.network.Leenkx.id.set("' + connection._url + '", p);
lnxConn.onclose();
}
}
}catch(error){
console.log("Error: " + error);
}

View File

@ -40,7 +40,7 @@ class LeenkxEventNode extends LogicNode {
default: throw "Failed to set client event type.";
}
} else if (property0 == "host") {
#if sys
#if (sys || kha_krom)
var net_Domain = inputs[0].get();
var net_Port = inputs[1].get();
net_Url = "ws://" + net_Domain + ":" + Std.string(net_Port);
@ -53,7 +53,7 @@ class LeenkxEventNode extends LogicNode {
}
#end
} else if (property0 == "securehost"){
#if sys
#if (sys || kha_krom)
var net_Domain = inputs[0].get();
var net_Port = inputs[1].get();
net_Url = "wss://" + net_Domain + ":" + Std.string(net_Port);

View File

@ -27,9 +27,9 @@ class LeenkxSendMessageNode extends LogicNode {
try {
if(inputs[5].get() == true){
var script = 'for (p in lx_' + connection._url +'.torrent._peers){
var script = 'for (p in lnx_' + connection._url +'.torrent._peers){
try{
lx_' + connection._url +'.torrent._peers[p].conn.send(`'+ api + message + '`);
lnx_' + connection._url +'.torrent._peers[p].conn.send(`'+ api + message + '`);
}catch(error){
console.log("Error: " + error);
}
@ -37,7 +37,7 @@ class LeenkxSendMessageNode extends LogicNode {
js.Syntax.code('(1, eval)({0})', script.toString());
runOutput(0);
} else {
var script = 'lx_' + connection._url +'.send(`' + inputs[4].get() + '`, `'+ api + message + '` );';
var script = 'lnx_' + connection._url +'.send(`' + inputs[4].get() + '`, `'+ api + message + '` );';
js.Syntax.code('(1, eval)({0})', script.toString());
runOutput(0);
}
@ -58,9 +58,9 @@ class LeenkxSendMessageNode extends LogicNode {
try {
connection.buffer = buffer;
if(inputs[5].get() == true){
var script = 'for (p in lx_' + connection._url +'.torrent._peers){
var script = 'for (p in lnx_' + connection._url +'.torrent._peers){
try{
lx_' + connection._url +'.torrent._peers[p].conn.send(leenkx.network.Leenkx.connections.h["'+ connection._url +'"].buffer );
lnx_' + connection._url +'.torrent._peers[p].conn.send(leenkx.network.Leenkx.connections.h["'+ connection._url +'"].buffer );
}catch(error){
console.log("Error: " + error);
}
@ -68,7 +68,7 @@ class LeenkxSendMessageNode extends LogicNode {
js.Syntax.code('(1, eval)({0})', script.toString());
runOutput(0);
} else {
var script = 'lx_' + connection._url +'.send("' + inputs[4].get() + '",leenkx.network.Leenkx.connections.h["'+ connection._url +'"].buffer );';
var script = 'lnx_' + connection._url +'.send("' + inputs[4].get() + '",leenkx.network.Leenkx.connections.h["'+ connection._url +'"].buffer );';
js.Syntax.code('(1, eval)({0})', script.toString());
runOutput(0);
}
@ -77,15 +77,15 @@ class LeenkxSendMessageNode extends LogicNode {
}
}
} else {
var window:haxe.DynamicAccess<Dynamic> = untyped js.Browser.window;
var lxCn = window.get('lx_' + connection._url);
if(inputs[5].get() == true){
lxCn.send(api+message);
}else{
lxCn.send(inputs[4].get(), api+message);
}
runOutput(0);
return;
if(inputs[5].get() == true){
var script = 'lnx_' + connection._url + '.send(`' + api + message + '`);';
js.Syntax.code('(1, eval)({0})', script);
}else{
var script = 'lnx_' + connection._url + '.send(`' + inputs[4].get() + '`, `' + api + message + '`);';
js.Syntax.code('(1, eval)({0})', script);
}
runOutput(0);
return;
}
case "vector":
if(property0 == "client"){
@ -103,9 +103,9 @@ class LeenkxSendMessageNode extends LogicNode {
try {
connection.buffer = buffer;
if(inputs[5].get() == true){
var script = 'for (p in lx_' + connection._url +'.torrent._peers){
var script = 'for (p in lnx_' + connection._url +'.torrent._peers){
try{
lx_' + connection._url +'.torrent._peers[p].conn.send("' + api + message + '");
lnx_' + connection._url +'.torrent._peers[p].conn.send("' + api + message + '");
}catch(error){
console.log("Error: " + error);
}
@ -113,7 +113,7 @@ class LeenkxSendMessageNode extends LogicNode {
js.Syntax.code('(1, eval)({0})', script.toString());
runOutput(0);
} else {
var script = 'lx_' + connection._url +'.send("' + inputs[4].get() + '",leenkx.network.Leenkx.connections.h["'+ connection._url +'"].buffer );';
var script = 'lnx_' + connection._url +'.send("' + inputs[4].get() + '",leenkx.network.Leenkx.connections.h["'+ connection._url +'"].buffer );';
js.Syntax.code('(1, eval)({0})', script.toString());
runOutput(0);
}
@ -121,12 +121,12 @@ class LeenkxSendMessageNode extends LogicNode {
trace("Error: " + error);
}
} else {
var window:haxe.DynamicAccess<Dynamic> = untyped js.Browser.window;
var lxCn = window.get('lx_' + connection._url);
if(inputs[5].get() == true){
lxCn.send(api+message);
var script = 'lnx_' + connection._url + '.send(`' + api + message + '`);';
js.Syntax.code('(1, eval)({0})', script);
}else{
lxCn.send(inputs[4].get(), api+message);
var script = 'lnx_' + connection._url + '.send(`' + inputs[4].get() + '`, `' + api + message + '`);';
js.Syntax.code('(1, eval)({0})', script);
}
runOutput(0);
return;
@ -143,9 +143,9 @@ class LeenkxSendMessageNode extends LogicNode {
try {
connection.buffer = buffer;
if(inputs[5].get() == true){
var script = 'for (p in lx_' + connection._url +'.torrent._peers){
var script = 'for (p in lnx_' + connection._url +'.torrent._peers){
try{
lx_' + connection._url +'.torrent._peers[p].conn.send(leenkx.network.Leenkx.connections.h["'+ connection._url +'"].buffer );
lnx_' + connection._url +'.torrent._peers[p].conn.send(leenkx.network.Leenkx.connections.h["'+ connection._url +'"].buffer );
}catch(error){
console.log("Error: " + error);
}
@ -153,7 +153,7 @@ class LeenkxSendMessageNode extends LogicNode {
js.Syntax.code('(1, eval)({0})', script.toString());
runOutput(0);
} else {
var script = 'lx_' + connection._url +'.send("' + inputs[4].get() + '",leenkx.network.Leenkx.connections.h["'+ connection._url +'"].buffer );';
var script = 'lnx_' + connection._url +'.send("' + inputs[4].get() + '",leenkx.network.Leenkx.connections.h["'+ connection._url +'"].buffer );';
js.Syntax.code('(1, eval)({0})', script.toString());
runOutput(0);
}
@ -161,12 +161,12 @@ class LeenkxSendMessageNode extends LogicNode {
trace("Error: " + error);
}
} else {
var window:haxe.DynamicAccess<Dynamic> = untyped js.Browser.window;
var lxCn = window.get('lx_' + connection._url);
if(inputs[5].get() == true){
lxCn.send(api+message);
var script = 'lnx_' + connection._url + '.send(`' + api + message + '`);';
js.Syntax.code('(1, eval)({0})', script);
}else{
lxCn.send(inputs[4].get(), api+message);
var script = 'lnx_' + connection._url + '.send(`' + inputs[4].get() + '`, `' + api + message + '`);';
js.Syntax.code('(1, eval)({0})', script);
}
runOutput(0);
return;
@ -183,9 +183,9 @@ class LeenkxSendMessageNode extends LogicNode {
try {
connection.buffer = buffer;
if(inputs[5].get() == true){
var script = 'for (p in lx_' + connection._url +'.torrent._peers){
var script = 'for (p in lnx_' + connection._url +'.torrent._peers){
try{
lx_' + connection._url +'.torrent._peers[p].conn.send(leenkx.network.Leenkx.connections.h["'+ connection._url +'"].buffer );
lnx_' + connection._url +'.torrent._peers[p].conn.send(leenkx.network.Leenkx.connections.h["'+ connection._url +'"].buffer );
}catch(error){
console.log("Error: " + error);
}
@ -193,7 +193,7 @@ class LeenkxSendMessageNode extends LogicNode {
js.Syntax.code('(1, eval)({0})', script.toString());
runOutput(0);
} else {
var script = 'lx_' + connection._url +'.send("' + inputs[4].get() + '",leenkx.network.Leenkx.connections.h["'+ connection._url +'"].buffer );';
var script = 'lnx_' + connection._url +'.send("' + inputs[4].get() + '",leenkx.network.Leenkx.connections.h["'+ connection._url +'"].buffer );';
js.Syntax.code('(1, eval)({0})', script.toString());
runOutput(0);
}
@ -201,12 +201,12 @@ class LeenkxSendMessageNode extends LogicNode {
trace("Error: " + error);
}
} else {
var window:haxe.DynamicAccess<Dynamic> = untyped js.Browser.window;
var lxCn = window.get('lx_' + connection._url);
if(inputs[5].get() == true){
lxCn.send(api+message);
var script = 'lnx_' + connection._url + '.send(`' + api + message + '`);';
js.Syntax.code('(1, eval)({0})', script);
}else{
lxCn.send(inputs[4].get(), api+message);
var script = 'lnx_' + connection._url + '.send(`' + inputs[4].get() + '`, `' + api + message + '`);';
js.Syntax.code('(1, eval)({0})', script);
}
runOutput(0);
return;
@ -225,9 +225,9 @@ class LeenkxSendMessageNode extends LogicNode {
try {
connection.buffer = buffer;
if(inputs[5].get() == true){
var script = 'for (p in lx_' + connection._url +'.torrent._peers){
var script = 'for (p in lnx_' + connection._url +'.torrent._peers){
try{
lx_' + connection._url +'.torrent._peers[p].conn.send(leenkx.network.Leenkx.connections.h["'+ connection._url +'"].buffer );
lnx_' + connection._url +'.torrent._peers[p].conn.send(leenkx.network.Leenkx.connections.h["'+ connection._url +'"].buffer );
}catch(error){
console.log("Error: " + error);
}
@ -235,7 +235,7 @@ class LeenkxSendMessageNode extends LogicNode {
js.Syntax.code('(1, eval)({0})', script.toString());
runOutput(0);
} else {
var script = 'lx_' + connection._url +'.send("' + inputs[4].get() + '",leenkx.network.Leenkx.connections.h["'+ connection._url +'"].buffer );';
var script = 'lnx_' + connection._url +'.send("' + inputs[4].get() + '",leenkx.network.Leenkx.connections.h["'+ connection._url +'"].buffer );';
js.Syntax.code('(1, eval)({0})', script.toString());
runOutput(0);
}
@ -243,12 +243,12 @@ class LeenkxSendMessageNode extends LogicNode {
trace("Error: " + error);
}
} else {
var window:haxe.DynamicAccess<Dynamic> = untyped js.Browser.window;
var lxCn = window.get('lx_' + connection._url);
if(inputs[5].get() == true){
lxCn.send(api+message);
var script = 'lnx_' + connection._url + '.send(`' + api + message + '`);';
js.Syntax.code('(1, eval)({0})', script);
}else{
lxCn.send(inputs[4].get(), api+message);
var script = 'lnx_' + connection._url + '.send(`' + inputs[4].get() + '`, `' + api + message + '`);';
js.Syntax.code('(1, eval)({0})', script);
}
runOutput(0);
return;
@ -283,10 +283,10 @@ class LeenkxSendMessageNode extends LogicNode {
connection.buffer = buffer;
if(inputs[5].get() == true){
var script = 'for (p in lx_' + connection._url +'.torrent._peers){
var script = 'for (p in lnx_' + connection._url +'.torrent._peers){
try{
//console.log("Mine: " + lx_8001.torrent.discovery.peerId + " || Incomming: " + p);
lx_' + connection._url +'.torrent._peers[p].conn.send(leenkx.network.Leenkx.connections.h["'+ connection._url +'"].buffer );
lnx_' + connection._url +'.torrent._peers[p].conn.send(leenkx.network.Leenkx.connections.h["'+ connection._url +'"].buffer );
}catch(error){
console.log("Error: " + error);
}
@ -294,8 +294,8 @@ class LeenkxSendMessageNode extends LogicNode {
js.Syntax.code('(1, eval)({0})', script.toString());
runOutput(0);
} else {
//var script = 'lx_' + connection._url +'.send("' + inputs[4].get() + '",leenkx.network.Leenkx.connections.h["'+ connection._url +'"].buffer );';
var script = 'lx_' + connection._url +'.send(leenkx.network.Leenkx.connections.h["'+ connection._url +'"].buffer );';
//var script = 'lnx_' + connection._url +'.send("' + inputs[4].get() + '",leenkx.network.Leenkx.connections.h["'+ connection._url +'"].buffer );';
var script = 'lnx_' + connection._url +'.send(leenkx.network.Leenkx.connections.h["'+ connection._url +'"].buffer );';
js.Syntax.code('(1, eval)({0})', script.toString());
runOutput(0);
}
@ -319,9 +319,9 @@ class LeenkxSendMessageNode extends LogicNode {
try {
connection.buffer = buffer;
if(inputs[5].get() == true){
var script = 'for (p in lx_' + connection._url +'.torrent._peers){
var script = 'for (p in lnx_' + connection._url +'.torrent._peers){
try{
lx_' + connection._url +'.torrent._peers[p].conn.send(leenkx.network.Leenkx.connections.h["'+ connection._url +'"].buffer );
lnx_' + connection._url +'.torrent._peers[p].conn.send(leenkx.network.Leenkx.connections.h["'+ connection._url +'"].buffer );
}catch(error){
console.log("Error: " + error);
}
@ -329,7 +329,7 @@ class LeenkxSendMessageNode extends LogicNode {
js.Syntax.code('(1, eval)({0})', script.toString());
runOutput(0);
} else {
var script = 'lx_' + connection._url +'.send("' + inputs[4].get() + '",leenkx.network.Leenkx.connections.h["'+ connection._url +'"].buffer );';
var script = 'lnx_' + connection._url +'.send("' + inputs[4].get() + '",leenkx.network.Leenkx.connections.h["'+ connection._url +'"].buffer );';
js.Syntax.code('(1, eval)({0})', script.toString());
runOutput(0);
}
@ -337,12 +337,12 @@ class LeenkxSendMessageNode extends LogicNode {
trace("Error: " + error);
}
} else {
var window:haxe.DynamicAccess<Dynamic> = untyped js.Browser.window;
var lxCn = window.get('lx_' + connection._url);
if(inputs[5].get() == true){
lxCn.send(api+message);
var script = 'lnx_' + connection._url + '.send(`' + api + message + '`);';
js.Syntax.code('(1, eval)({0})', script);
}else{
lxCn.send(inputs[4].get(), api+message);
var script = 'lnx_' + connection._url + '.send(`' + inputs[4].get() + '`, `' + api + message + '`);';
js.Syntax.code('(1, eval)({0})', script);
}
runOutput(0);
return;
@ -360,9 +360,9 @@ class LeenkxSendMessageNode extends LogicNode {
try {
if(inputs[5].get() == true){
var script = 'for (p in lx_' + connection._url +'.torrent._peers){
var script = 'for (p in lnx_' + connection._url +'.torrent._peers){
try{
lx_' + connection._url +'.torrent._peers[p].conn.send("' + api + message + '");
lnx_' + connection._url +'.torrent._peers[p].conn.send("' + api + message + '");
}catch(error){
console.log("Error: " + error);
}
@ -370,7 +370,7 @@ class LeenkxSendMessageNode extends LogicNode {
js.Syntax.code('(1, eval)({0})', script.toString());
runOutput(0);
} else {
var script = 'for (p in lx_' + connection._url +'.torrent._peers){
var script = 'for (p in lnx_' + connection._url +'.torrent._peers){
try{
lx_' + connection._url +'.torrent._peers[' + inputs[4].get() + '].conn.send("' + api + message + '");

View File

@ -16,7 +16,7 @@ class NetworkCloseConnectionNode extends LogicNode {
if(property1 == "client") {
var connection = cast(inputs[1].get(), leenkx.network.WebSocket);
if (connection == null) return;
#if sys
#if (sys || kha_krom)
try{
var net_Url = connection._protocol + "://" + connection._host + ":" + connection._port;
connection.close();
@ -44,7 +44,7 @@ class NetworkCloseConnectionNode extends LogicNode {
}
#end
} else if(property1 == "securehost"){
#if sys
#if (sys || kha_krom)
var connection = cast(inputs[1].get(), leenkx.network.WebSocketSecureServer<leenkx.network.Connect.HostHandler>);
if (connection == null) return;
var net_Url = "wss://" + @:privateAccess connection._host + ":" + @:privateAccess connection._port;
@ -56,7 +56,7 @@ class NetworkCloseConnectionNode extends LogicNode {
}
#end
} else {
#if sys
#if (sys || kha_krom)
var connection = cast(inputs[1].get(), leenkx.network.WebSocketServer<leenkx.network.Connect.HostHandler>);
if (connection == null) return;
var net_Url = "ws://" + @:privateAccess connection._host + ":" + @:privateAccess connection._port;

View File

@ -28,7 +28,7 @@ class NetworkEventNode extends LogicNode {
default: throw "Failed to set client event type.";
}
} else if (property0 == "host") {
#if sys
#if (sys || kha_krom)
var net_Domain = inputs[0].get();
var net_Port = inputs[1].get();
net_Url = "ws://" + net_Domain + ":" + Std.string(net_Port);
@ -41,7 +41,7 @@ class NetworkEventNode extends LogicNode {
}
#end
} else if (property0 == "securehost"){
#if sys
#if (sys || kha_krom)
var net_Domain = inputs[0].get();
var net_Port = inputs[1].get();
net_Url = "wss://" + net_Domain + ":" + Std.string(net_Port);

View File

@ -11,7 +11,7 @@ class NetworkHostCloseClientNode extends LogicNode {
}
override function run(from:Int) {
#if sys
#if (sys || kha_krom)
if(property0 == false){
var connection = cast(inputs[1].get(), leenkx.network.WebSocketServer<HostHandler>);
if (connection == null) return;

View File

@ -12,7 +12,7 @@ class NetworkHostGetIpNode extends LogicNode {
}
override function run(from:Int) {
#if sys
#if (sys || kha_krom)
if(property0 == false){
var connection = cast(inputs[1].get(), leenkx.network.WebSocketServer<HostHandler>);
if (connection == null) return;

View File

@ -12,7 +12,7 @@ class NetworkHostNode extends LogicNode {
}
override function run(from:Int) {
#if sys
#if (sys || kha_krom)
if(property0 == false) {
final net_Object: Object = tree.object;
var net_Domain: String = inputs[1].get();
@ -49,7 +49,7 @@ class NetworkHostNode extends LogicNode {
#end
}
#if sys
#if (sys || kha_krom)
override function get(from: Int): Dynamic {
if(property0 == false) {
return switch (from) {

View File

@ -85,10 +85,6 @@ class NetworkHttpRequestNode extends LogicNode {
} catch( e : Dynamic ) {
trace("Could not complete request: " + e);
}
callbackType = 0;
runOutput(0);
}
override function get(from: Int): Dynamic {

View File

@ -19,7 +19,7 @@ class NetworkOpenConnectionNode extends LogicNode {
var connection = cast(inputs[1].get(), leenkx.network.WebSocket);
if (connection == null) return;
var object = tree.object;
#if sys
#if (sys || kha_krom)
net_Url = connection._protocol + "://" + connection._host + ":" + connection._port;
Client.connections[net_Url] = null;
var client = new leenkx.network.Connect.Client(net_Url, object);
@ -94,7 +94,7 @@ class NetworkOpenConnectionNode extends LogicNode {
#end
runOutput(0);
} else if (property0 == "securehost"){
#if sys
#if (sys || kha_krom)
var connection = cast(inputs[1].get(), leenkx.network.WebSocketSecureServer<leenkx.network.Connect.HostHandler>);
if (connection == null) return;
net_Url = "wss://" + @:privateAccess connection._host + ":" + @:privateAccess connection._port;
@ -108,7 +108,7 @@ class NetworkOpenConnectionNode extends LogicNode {
runOutput(0);
#end
} else {
#if sys
#if (sys || kha_krom)
var connection = cast(inputs[1].get(), leenkx.network.WebSocketServer<leenkx.network.Connect.HostHandler>);
if (connection == null) return;
net_Url = "ws://" + @:privateAccess connection._host + ":" + @:privateAccess connection._port;
@ -126,7 +126,7 @@ class NetworkOpenConnectionNode extends LogicNode {
override function get(from: Int): Dynamic {
return switch (property0) {
#if sys
#if (sys || kha_krom)
case "host": Host.connections[net_Url];
case "securehost": SecureHost.connections[net_Url];
#end

View File

@ -38,7 +38,7 @@ class NetworkSendMessageNode extends LogicNode {
trace("Error: " + error);
}
}
#if sys
#if (sys || kha_krom)
else if(inputs[5].get() == true){
if(property0 == "securehost"){
var connection = cast(inputs[1].get(), leenkx.network.WebSocketSecureServer<SecureHostHandler>);
@ -127,7 +127,7 @@ class NetworkSendMessageNode extends LogicNode {
trace("Error: " + error);
}
}
#if sys
#if (sys || kha_krom)
else if(inputs[5].get() == true){
if(property0 == "securehost"){
var connection = cast(inputs[1].get(), leenkx.network.WebSocketSecureServer<SecureHostHandler>);
@ -233,7 +233,7 @@ class NetworkSendMessageNode extends LogicNode {
trace("Error: " + error);
}
}
#if sys
#if (sys || kha_krom)
else if(inputs[5].get() == true){
if(property0 == "securehost"){
var connection = cast(inputs[1].get(), leenkx.network.WebSocketSecureServer<SecureHostHandler>);
@ -327,7 +327,7 @@ class NetworkSendMessageNode extends LogicNode {
trace("Error: " + error);
}
}
#if sys
#if (sys || kha_krom)
else if(inputs[5].get() == true){
if(property0 == "securehost"){
var connection = cast(inputs[1].get(), leenkx.network.WebSocketSecureServer<SecureHostHandler>);
@ -423,7 +423,7 @@ class NetworkSendMessageNode extends LogicNode {
trace("Error: " + error);
}
}
#if sys
#if (sys || kha_krom)
else if(inputs[5].get() == true){
if(property0 == "securehost"){
var connection = cast(inputs[1].get(), leenkx.network.WebSocketSecureServer<SecureHostHandler>);
@ -541,7 +541,7 @@ class NetworkSendMessageNode extends LogicNode {
trace("Error: " + error);
}
}
#if sys
#if (sys || kha_krom)
else if(inputs[5].get() == true){
if(property0 == "securehost"){
var connection = cast(inputs[1].get(), leenkx.network.WebSocketSecureServer<SecureHostHandler>);
@ -702,7 +702,7 @@ class NetworkSendMessageNode extends LogicNode {
trace("Error: " + error);
}
}
#if sys
#if (sys || kha_krom)
else if(inputs[5].get() == true){
if(property0 == "securehost"){
var connection = cast(inputs[1].get(), leenkx.network.WebSocketSecureServer<SecureHostHandler>);

View File

@ -1,11 +1,13 @@
package leenkx.network;
#if sys
#if (sys || kha_krom)
import leenkx.network.WebSocketServer;
import leenkx.network.WebSocketSecureServer;
import leenkx.network.SocketImpl;
#end
#if sys
import sys.ssl.Key;
import sys.ssl.Certificate;
import leenkx.network.SocketImpl;
#end
import leenkx.network.WebSocket;
import leenkx.network.Types;
@ -115,7 +117,7 @@ class Host extends Connect {
public static var onErrorEvent: String = "Host.onError";
public static var onCloseEvent: String = "Host.onClose";
public static var object: Object = null;
#if sys
#if (sys || kha_krom)
public static var connections:Map<String, WebSocketServer<HostHandler>> = [];
#else
public static var connections = null;
@ -131,14 +133,15 @@ class Host extends Connect {
object = net_object;
net_Url = "ws://" + net_Domain + ":" + net_Port;
#if sys
if (connections[net_Url] != null) return;
connections[net_Url] = new WebSocketServer<HostHandler>(net_Domain, net_Port, net_Max);
#if (sys || kha_krom)
if (connections[net_Url] == null) {
connections[net_Url] = new WebSocketServer<HostHandler>(net_Domain, net_Port, net_Max);
}
#end
}
}
#if sys
#if (sys || kha_krom)
class HostHandler extends WebSocketHandler {
public function new(s: SocketImpl) {
@ -217,7 +220,7 @@ class SecureHost extends Connect {
public static var onCloseEvent: String = "SecureHost.onClose";
public static var object: Object = null;
public static var net_Url: String;
#if sys
#if (sys || kha_krom)
public static var connections:Map<String, WebSocketSecureServer<SecureHostHandler>> = [];
#else
public static var connections = null;
@ -235,13 +238,18 @@ class SecureHost extends Connect {
#if sys
var cert = Certificate.loadFile(net_Cert);
var key = Key.loadFile(net_Key);
if (connections[net_Url] != null) return;
connections[net_Url] = new WebSocketSecureServer<SecureHostHandler>(net_Domain, net_Port, cert, key, cert, net_Max);
if (connections[net_Url] == null) {
connections[net_Url] = new WebSocketSecureServer<SecureHostHandler>(net_Domain, net_Port, cert, key, cert, net_Max);
}
#elseif kha_krom
if (connections[net_Url] == null) {
connections[net_Url] = new WebSocketSecureServer<SecureHostHandler>(net_Domain, net_Port, net_Cert, net_Key, net_Cert, net_Max);
}
#end
}
}
#if sys
#if (sys || kha_krom)
class SecureHostHandler extends WebSocketHandler {
public function new(s: SocketImpl) {
@ -313,3 +321,4 @@ class SecureHostHandler extends WebSocketHandler {
}
}
#end

View File

@ -2,7 +2,6 @@ package leenkx.network;
import leenkx.network.Types;
import haxe.io.Bytes;
import js.Browser;
import iron.object.Object;
import leenkx.system.Event;
import leenkx.network.Buffer;
@ -30,11 +29,13 @@ class Leenkx {
public static var onAnnounceEvent: String = "Leenkx.onAnnounce";
public static var onTorrentDoneEvent: String = "Leenkx.onTorrentDone";
public static var connections:Map<String, leenkx.network.LeenkxSocket> = [];
#if js
public static var peers:js.lib.Map<String, String> = new js.lib.Map<String,String>();
public static var data:js.lib.Map<String, Dynamic> = new js.lib.Map<String,Dynamic>();
public static var id:js.lib.Map<String, String> = new js.lib.Map<String,String>();
public static var torrent:js.lib.Map<String, Dynamic> = new js.lib.Map<String,Dynamic>();
public static var file:js.lib.Map<String, Dynamic> = new js.lib.Map<String,Dynamic>();
public static var file:js.lib.Map<String, Dynamic> = new js.lib.Map<String,Dynamic>();
#end
public static var lxNew:Void->Void;
public function new(net_Url: String, net_object: Object) {
@ -53,10 +54,6 @@ class Leenkx {
}
if (object != null) {
//var script = "const scope = '/';const sw = navigator.serviceWorker.register(`LeenkxFS.js`, { scope });";
//js.Syntax.code('(1, eval)({0})', script);
final loadEvent = Event.get(Leenkx.onLoadEvent);
final openEvent = Event.get(Leenkx.onOpenEvent);
final messageEvent = Event.get(Leenkx.onMessageEvent);
@ -77,197 +74,204 @@ class Leenkx {
final announceEvent = Event.get(Leenkx.onAnnounceEvent);
final torrentDoneEvent = Event.get(Leenkx.onTorrentDoneEvent);
Leenkx.connections[net_Url].onopen = function() {
if (openEvent != null) {
for (e in openEvent) {
if (e.mask == object.uid) {
e.onEvent();
}
}
}
};
Leenkx.connections[net_Url].onmessage = function() {
if (messageEvent != null) {
for (e in messageEvent) {
if (e.mask == object.uid) {
e.onEvent();
}
}
}
};
Leenkx.connections[net_Url].onerror = function() {
if (errorEvent != null) {
for (e in errorEvent) {
if (e.mask == object.uid) {
e.onEvent();
}
}
}
};
Leenkx.connections[net_Url].onclose = function() {
if (closeEvent != null) {
for (e in closeEvent) {
if (e.mask == object.uid) {
e.onEvent();
}
}
}
};
Leenkx.connections[net_Url].onseen = function() {
if (seenEvent != null) {
for (e in seenEvent) {
if (e.mask == object.uid) {
e.onEvent();
}
}
}
};
Leenkx.connections[net_Url].onserver = function() {
if (serverEvent != null) {
for (e in serverEvent) {
if (e.mask == object.uid) {
e.onEvent();
}
}
}
};
Leenkx.connections[net_Url].onconnections = function() {
if (connectionsEvent != null) {
for (e in connectionsEvent) {
if (e.mask == object.uid) {
e.onEvent();
}
}
}
};
Leenkx.connections[net_Url].onping = function() {
if (pingEvent != null) {
for (e in pingEvent) {
if (e.mask == object.uid) {
e.onEvent();
}
}
}
};
Leenkx.connections[net_Url].onleft = function() {
if (leftEvent != null) {
for (e in leftEvent) {
if (e.mask == object.uid) {
e.onEvent();
}
}
}
};
Leenkx.connections[net_Url].ontimeout = function() {
if (timeoutEvent != null) {
for (e in timeoutEvent) {
if (e.mask == object.uid) {
e.onEvent();
}
}
}
};
Leenkx.connections[net_Url].onrpc = function() {
if (rpcEvent != null) {
for (e in rpcEvent) {
if (e.mask == object.uid) {
e.onEvent();
}
}
}
};
Leenkx.connections[net_Url].onrpcresponse = function() {
if (rpcresponseEvent != null) {
for (e in rpcresponseEvent) {
if (e.mask == object.uid) {
e.onEvent();
}
}
}
};
Leenkx.connections[net_Url].onwireleft = function() {
if (wireleftEvent != null) {
for (e in wireleftEvent) {
if (e.mask == object.uid) {
e.onEvent();
}
}
}
};
Leenkx.connections[net_Url].onwireseen = function() {
if (wireseenEvent != null) {
for (e in wireseenEvent) {
if (e.mask == object.uid) {
e.onEvent();
}
}
}
};
Leenkx.connections[net_Url].ontorrent = function() {
if (torrentEvent != null) {
for (e in torrentEvent) {
if (e.mask == object.uid) {
e.onEvent();
}
}
}
};
Leenkx.connections[net_Url].ontracker = function() {
if (trackerEvent != null) {
for (e in trackerEvent) {
if (e.mask == object.uid) {
e.onEvent();
}
}
}
};
Leenkx.connections[net_Url].onannounce = function() {
if (announceEvent != null) {
for (e in announceEvent) {
if (e.mask == object.uid) {
e.onEvent();
}
}
}
};
Leenkx.connections[net_Url].onload = function() {
if (loadEvent != null) {
for (e in loadEvent) {
if (e.mask == object.uid) {
e.onEvent();
}
}
}
};
Leenkx.connections[net_Url].ontorrentdone = function() {
if (torrentDoneEvent != null) {
for (e in torrentDoneEvent) {
if (e.mask == object.uid) {
e.onEvent();
}
}
}
};
var lnkx = Browser.document.createScriptElement();
lnkx.type = "text/javascript";
lnkx.src = "Leenkx.js";
lnkx.onload = function() {
js.Syntax.code('(1, eval)({0})', 'lxNew =function(url){ var cx = new Leenkx(url); return cx;}');
Leenkx.connections[net_Url].onopen = function() {
if (openEvent != null) {
for (e in openEvent) {
if (e.mask == object.uid) {
e.onEvent();
}
}
}
#if js
var lnxjs:Dynamic = js.Lib.global;
var connectionUrl = net_Url;
if (lnxjs.Leenkx != null) {
if (lnxjs.lnxNew == null) {
js.Syntax.code('globalThis.lnxNew = function(url) { return new Leenkx(url); }');
}
Leenkx.connections[net_Url].onmessage = function() {
if (messageEvent != null) {
for (e in messageEvent) {
if (e.mask == object.uid) {
e.onEvent();
}
Leenkx.connections[connectionUrl].onload();
} else {
kha.Assets.loadBlobFromPath("Leenkx.js", function(b: kha.Blob) {
if (b != null) {
js.Syntax.code("(1,eval)({0})", b.toString());
if (lnxjs.Leenkx != null && lnxjs.lnxNew == null) {
js.Syntax.code('globalThis.lnxNew = function(url) { return new Leenkx(url); }');
}
} else {
trace("Warning: Leenkx.js blob is null - file may not be in assets");
}
}
Leenkx.connections[net_Url].onerror = function() {
if (errorEvent != null) {
for (e in errorEvent) {
if (e.mask == object.uid) {
e.onEvent();
}
}
}
}
Leenkx.connections[net_Url].onclose = function() {
if (closeEvent != null) {
for (e in closeEvent) {
if (e.mask == object.uid) {
e.onEvent();
}
}
}
}
Leenkx.connections[net_Url].onseen = function() {
if (seenEvent != null) {
for (e in seenEvent) {
if (e.mask == object.uid) {
e.onEvent();
}
}
}
}
Leenkx.connections[net_Url].onserver = function() {
if (serverEvent != null) {
for (e in serverEvent) {
if (e.mask == object.uid) {
e.onEvent();
}
}
}
}
Leenkx.connections[net_Url].onconnections = function() {
if (connectionsEvent != null) {
for (e in connectionsEvent) {
if (e.mask == object.uid) {
e.onEvent();
}
}
}
}
Leenkx.connections[net_Url].onping = function() {
if (pingEvent != null) {
for (e in pingEvent) {
if (e.mask == object.uid) {
e.onEvent();
}
}
}
}
Leenkx.connections[net_Url].onleft = function() {
if (leftEvent != null) {
for (e in leftEvent) {
if (e.mask == object.uid) {
e.onEvent();
}
}
}
}
Leenkx.connections[net_Url].ontimeout = function() {
if (timeoutEvent != null) {
for (e in timeoutEvent) {
if (e.mask == object.uid) {
e.onEvent();
}
}
}
}
Leenkx.connections[net_Url].onrpc = function() {
if (rpcEvent != null) {
for (e in rpcEvent) {
if (e.mask == object.uid) {
e.onEvent();
}
}
}
}
Leenkx.connections[net_Url].onrpcresponse = function() {
if (rpcresponseEvent != null) {
for (e in rpcresponseEvent) {
if (e.mask == object.uid) {
e.onEvent();
}
}
}
}
Leenkx.connections[net_Url].onwireleft = function() {
if (wireleftEvent != null) {
for (e in wireleftEvent) {
if (e.mask == object.uid) {
e.onEvent();
}
}
}
}
Leenkx.connections[net_Url].onwireseen = function() {
if (wireseenEvent != null) {
for (e in wireseenEvent) {
if (e.mask == object.uid) {
e.onEvent();
}
}
}
}
Leenkx.connections[net_Url].ontorrent = function() {
if (torrentEvent != null) {
for (e in torrentEvent) {
if (e.mask == object.uid) {
e.onEvent();
}
}
}
}
Leenkx.connections[net_Url].ontracker = function() {
if (trackerEvent != null) {
for (e in trackerEvent) {
if (e.mask == object.uid) {
e.onEvent();
}
}
}
}
Leenkx.connections[net_Url].onannounce = function() {
if (announceEvent != null) {
for (e in announceEvent) {
if (e.mask == object.uid) {
e.onEvent();
}
}
}
}
Leenkx.connections[net_Url].onload = function() {
if (loadEvent != null) {
for (e in loadEvent) {
if (e.mask == object.uid) {
e.onEvent();
}
}
}
}
Leenkx.connections[net_Url].ontorrentdone = function() {
if (torrentDoneEvent != null) {
for (e in torrentDoneEvent) {
if (e.mask == object.uid) {
e.onEvent();
}
}
}
}
Leenkx.connections[net_Url].onload();
//var script = 'leenkx.network.Leenkx.connections.h["' + net_Url + '"].onload();';
//js.Syntax.code('(1, eval)({0})', script);
}
lnkx.onerror = function(error, i) {
trace("ERROR - " + error + " | " + i);
}
Browser.document.head.appendChild(lnkx);
Leenkx.connections[connectionUrl].onload();
}, function(err: kha.AssetError) {
trace("ERROR loading Leenkx.js: " + err.url + " - " + err.error);
Leenkx.connections[connectionUrl].onload();
});
}
#end
}
}
}

View File

@ -7,8 +7,8 @@ class Log {
public static var mask:Int = 0;
#if sys
public static var logFn:Dynamic->Void = Sys.println;
#if (sys || kha_krom)
public static var logFn:Dynamic->Void = function(data:Dynamic) { trace(data); };
#elseif js
public static var logFn:Dynamic->Void = js.html.Console.log;
#end

View File

@ -1,3 +1,7 @@
package leenkx.network;
#if kha_krom
typedef SecureSocketImpl = leenkx.network.krom.KromSecureSocket;
#else
typedef SecureSocketImpl = sys.ssl.Socket;
#end

View File

@ -12,6 +12,10 @@ typedef SocketImpl = leenkx.network.cs.NonBlockingSocket;
typedef SocketImpl = leenkx.network.nodejs.NodeSocket;
#elseif kha_krom
typedef SocketImpl = leenkx.network.krom.KromSocket;
#else
typedef SocketImpl = sys.net.Socket;

View File

@ -2,7 +2,128 @@ package leenkx.network;
import leenkx.network.Types;
#if js
#if kha_krom
import haxe.io.Bytes;
#if (haxe_ver < 4)
typedef JsBuffer = js.html.ArrayBuffer;
#else
typedef JsBuffer = js.lib.ArrayBuffer;
#end
@:native("WebSocket")
extern class NativeWebSocket {
var binaryType:String;
var onopen:Dynamic;
var onclose:Dynamic;
var onerror:Dynamic;
var onmessage:Dynamic;
var readyState:Int;
function new(url:String):Void;
function close(?code:Int, ?reason:String):Void;
function send(data:Dynamic):Void;
}
class WebSocket {
public var _protocol:String;
public var _host:String;
public var _port:Int = 0;
public var _path:String;
private var _url:String;
private var _ws:NativeWebSocket = null;
public var binaryType:Dynamic;
public var onopen:Void->Void;
public var onclose:Void->Void;
public var onerror:Dynamic->Void;
public var onmessage:MessageType->Void;
public function new(url:String, immediateOpen:Bool = true) {
_url = url;
parseUrl(url);
if (immediateOpen) {
open();
}
}
private function parseUrl(url:String):Void {
var urlArr = url.split(":");
if (urlArr.length < 2) return;
_protocol = urlArr[0];
var hostPart = urlArr[1];
if (hostPart.substr(0, 2) == "//") hostPart = hostPart.substr(2);
_host = hostPart;
if (urlArr.length >= 3) {
var portPathPart = urlArr[2];
var slashIndex = portPathPart.indexOf("/");
if (slashIndex >= 0) {
_port = Std.parseInt(portPathPart.substr(0, slashIndex));
_path = portPathPart.substr(slashIndex);
} else {
_port = Std.parseInt(portPathPart);
_path = "/";
}
} else {
_port = (_protocol == "wss") ? 443 : 80;
_path = "/";
}
if (_port == null || _port == 0) _port = (_protocol == "wss") ? 443 : 80;
}
public function open():Void {
if (_ws != null) {
throw "Socket already connected";
}
_ws = new NativeWebSocket(_url);
_ws.binaryType = "arraybuffer";
_ws.onopen = function() {
if (onopen != null) onopen();
};
_ws.onclose = function() {
if (onclose != null) onclose();
};
_ws.onerror = function(err:Dynamic) {
if (onerror != null) onerror(err);
};
_ws.onmessage = function(event:Dynamic) {
if (onmessage != null) {
if (Std.isOfType(event.data, JsBuffer)) {
var buffer = new Buffer();
buffer.writeBytes(Bytes.ofData(event.data));
onmessage(MessageType.BytesMessage(buffer));
} else {
onmessage(MessageType.StrMessage(event.data));
}
}
};
}
public function close():Void {
if (_ws != null) {
_ws.close();
_ws = null;
}
}
public function send(msg:Dynamic):Void {
if (_ws == null) return;
if (Std.isOfType(msg, Bytes)) {
var bytes:Bytes = cast msg;
_ws.send(bytes.getData());
} else if (Std.isOfType(msg, Buffer)) {
var buffer:Buffer = cast msg;
_ws.send(buffer.readAllAvailableBytes().getData());
} else {
_ws.send(msg);
}
}
}
#elseif js
import haxe.Constraints.Function;
import haxe.io.Bytes;

View File

@ -7,13 +7,22 @@ class WebSocketHandler extends Handler {
public function new(socket:SocketImpl) {
super(socket);
#if kha_krom
_creationTime = kha.Scheduler.time();
#else
_creationTime = Sys.time();
#end
_socket.setBlocking(false);
Log.debug('New socket handler', id);
}
public override function handle() {
if (this.state == State.Handshake && Sys.time() - _creationTime > (MAX_WAIT_TIME / 1000)) {
#if kha_krom
var currentTime = kha.Scheduler.time();
#else
var currentTime = Sys.time();
#end
if (this.state == State.Handshake && currentTime - _creationTime > (MAX_WAIT_TIME / 1000)) {
Log.info('No handshake detected in ${MAX_WAIT_TIME}ms, closing connection', id);
this.close();
return;

View File

@ -2,8 +2,10 @@ package leenkx.network;
import haxe.Constraints;
#if !kha_krom
import sys.ssl.Key;
import sys.ssl.Certificate;
#end
@:generic
class WebSocketSecureServer
@ -14,11 +16,21 @@ class WebSocketSecureServer
#end
extends WebSocketServer<T> {
#if kha_krom
private var _cert:Dynamic;
private var _key:Dynamic;
private var _caChain:Dynamic;
#else
private var _cert:Certificate;
private var _key:Key;
private var _caChain:Certificate;
#end
#if kha_krom
public function new(host:String, port:Int, cert:Dynamic, key:Dynamic, caChain:Dynamic, maxConnections:Int = 1) {
#else
public function new(host:String, port:Int, cert:Certificate, key:Key, caChain:Certificate, maxConnections:Int = 1) {
#end
super(host, port, maxConnections);
_cert=cert;

View File

@ -3,6 +3,9 @@ package leenkx.network;
import haxe.Constraints;
import haxe.MainLoop;
import haxe.io.Error;
#if kha_krom
import leenkx.network.krom.KromSocket.KromHost;
#end
@:generic
class WebSocketServer
@ -51,11 +54,20 @@ class WebSocketServer
_stopServer = false;
_serverSocket = createSocket();
_serverSocket.setBlocking(false);
#if kha_krom
_serverSocket.bind(new KromHost(_host), _port);
#else
_serverSocket.bind(new sys.net.Host(_host), _port);
#end
_serverSocket.listen(_maxConnections);
Log.info('Starting server - ${_host}:${_port} (maxConnections: ${_maxConnections})');
#if cs
#if kha_krom
kha.Scheduler.addTimeTask(function() {
tick();
}, 0, sleepAmount);
#elseif cs
while (true) {
var continueLoop = tick();
if (continueLoop == false) {

View File

@ -0,0 +1,62 @@
package leenkx.network.krom;
import haxe.io.Bytes;
import haxe.io.BytesOutput;
import haxe.io.Error;
import leenkx.network.krom.KromSocket.KromHost;
@:native("krom_socket_enable_ssl") extern function krom_socket_enable_ssl(id:Int):Bool;
class KromSecureSocket extends KromSocket {
private var _sslEnabled:Bool = false;
private var _hostname:String = null;
private var _certPath:String = null;
private var _keyPath:String = null;
private var _caPath:String = null;
public var verifyCert:Bool = true;
public function new() {
super();
}
public function setHostname(hostname:String):Void {
_hostname = hostname;
}
public function setCA(ca:Dynamic):Void {
if (ca != null) {
_caPath = Std.string(ca);
}
}
public function setCertificate(cert:Dynamic, key:Dynamic):Void {
if (cert != null) {
_certPath = Std.string(cert);
}
if (key != null) {
_keyPath = Std.string(key);
}
}
public function enableSSL():Bool {
if (getSocketId() >= 0 && !_sslEnabled) {
var result = krom_socket_enable_ssl(getSocketId());
if (result) {
_sslEnabled = true;
} else {
trace("SecureSocket: Failed to enable SSL for socket " + getSocketId());
}
return result;
}
return _sslEnabled;
}
override public function connect(host:KromHost, port:Int):Void {
super.connect(host, port);
enableSSL();
}
public function isSSLEnabled():Bool {
return _sslEnabled;
}
}

View File

@ -0,0 +1,249 @@
package leenkx.network.krom;
import haxe.io.Bytes;
import haxe.io.BytesInput;
import haxe.io.BytesOutput;
import haxe.io.Error;
#if (haxe_ver < 4)
typedef JsBuffer = js.html.ArrayBuffer;
#else
typedef JsBuffer = js.lib.ArrayBuffer;
#end
@:native("krom_socket_create") extern function krom_socket_create():Int;
@:native("krom_socket_close") extern function krom_socket_close(id:Int):Void;
@:native("krom_socket_bind") extern function krom_socket_bind(id:Int, addr:String, port:Int):Bool;
@:native("krom_socket_listen") extern function krom_socket_listen(id:Int, backlog:Int):Bool;
@:native("krom_socket_accept") extern function krom_socket_accept(id:Int):Int;
@:native("krom_socket_connect") extern function krom_socket_connect(id:Int, host:String, port:Int):Bool;
@:native("krom_socket_send") extern function krom_socket_send(id:Int, data:JsBuffer):Int;
@:native("krom_socket_recv") extern function krom_socket_recv(id:Int, maxLen:Int):Dynamic;
@:native("krom_socket_set_blocking") extern function krom_socket_set_blocking(id:Int, blocking:Bool):Void;
@:native("krom_socket_is_connected") extern function krom_socket_is_connected(id:Int):Bool;
class KromSocket {
private var _socketId:Int = -1;
private var _host:KromHost = null;
private var _port:Int = 0;
private var _blocking:Bool = true;
public var input:KromSocketInput = null;
public var output:KromSocketOutput = null;
private static var _connections:Array<KromSocket> = [];
private var _newConnections:Array<KromSocket> = [];
public function new() {
_socketId = krom_socket_create();
if (_socketId >= 0) {
input = new KromSocketInput(this);
output = new KromSocketOutput(this);
}
}
private function setSocketId(id:Int):Void {
_socketId = id;
if (_socketId >= 0) {
input = new KromSocketInput(this);
output = new KromSocketOutput(this);
}
}
public function getSocketId():Int {
return _socketId;
}
public function bind(host:KromHost, port:Int):Void {
_host = host;
_port = port;
if (_socketId >= 0) {
if (!krom_socket_bind(_socketId, host.host, port)) {
trace("Failed to bind to " + host.host + ":" + port);
}
}
}
public function listen(connections:Int):Void {
if (_host == null) {
throw "You must bind the Socket to an address!";
}
if (_socketId >= 0) {
if (!krom_socket_listen(_socketId, connections)) {
trace("Failed to listen");
}
}
}
public function accept():KromSocket {
if (_socketId < 0) {
throw "Blocking";
}
var clientId = krom_socket_accept(_socketId);
if (clientId < 0) {
throw "Blocking";
}
var clientSocket = new KromSocket();
clientSocket.setSocketId(clientId);
_connections.push(clientSocket);
return clientSocket;
}
public function connect(host:KromHost, port:Int):Void {
if (_socketId >= 0) {
if (!krom_socket_connect(_socketId, host.host, port)) {
throw "Connection failed to " + host.host + ":" + port;
}
}
}
public function setBlocking(blocking:Bool):Void {
_blocking = blocking;
if (_socketId >= 0) {
krom_socket_set_blocking(_socketId, blocking);
}
}
public function setTimeout(timeout:Int):Void {
// TODO: Re-investigate timeout handling
}
public function peer():{host:KromHost, port:Int} {
return {host: _host != null ? _host : new KromHost("0.0.0.0"), port: _port};
}
public function host():{host:KromHost, port:Int} {
return {host: _host != null ? _host : new KromHost("0.0.0.0"), port: _port};
}
public function close():Void {
if (_socketId >= 0) {
krom_socket_close(_socketId);
_socketId = -1;
}
_connections.remove(this);
}
public function sendRaw(data:Bytes):Int {
var arrayBuffer:JsBuffer = data.getData();
var exactBuffer:JsBuffer = untyped arrayBuffer.slice(0, data.length);
return krom_socket_send(_socketId, exactBuffer);
}
// throws "ConnectionClosed" if peer disconnected
public function recvRaw(maxLength:Int):Bytes {
if (_socketId < 0) return null;
var result:Dynamic = krom_socket_recv(_socketId, maxLength);
// V8 returns ArrayBuffer data, null would-block, or -2 connection closed
if (result == -2) {
_socketId = -1;
throw "ConnectionClosed";
}
if (result == null) return null;
return Bytes.ofData(result);
}
public function isConnected():Bool {
if (_socketId < 0) return false;
return krom_socket_is_connected(_socketId);
}
public static function select(read:Array<KromSocket>, write:Array<KromSocket>, others:Array<KromSocket>, ?timeout:Float):{read:Array<KromSocket>, write:Array<KromSocket>, others:Array<KromSocket>} {
var readable:Array<KromSocket> = [];
if (read != null) {
for (sock in read) {
if (sock._socketId >= 0) {
readable.push(sock);
}
}
}
if (readable.length == 0) {
return null;
}
return {
read: readable,
write: write,
others: others
};
}
}
class KromSocketInput {
private var _socket:KromSocket;
public var hasData:Bool = false;
private var _buffer:Bytes = null;
private var _bufferPos:Int = 0;
public function new(socket:KromSocket) {
_socket = socket;
}
public function readBytes(buf:Bytes, pos:Int, len:Int):Int {
var data = _socket.recvRaw(len);
if (data == null || data.length == 0) {
hasData = false;
throw Error.Blocked;
}
hasData = true;
var toRead = data.length < len ? data.length : len;
buf.blit(pos, data, 0, toRead);
return toRead;
}
public function read(nbytes:Int):Bytes {
var buf = Bytes.alloc(nbytes);
var read = readBytes(buf, 0, nbytes);
if (read < nbytes) {
return buf.sub(0, read);
}
return buf;
}
}
class KromSocketOutput {
private var _socket:KromSocket;
private var _buffer:BytesOutput;
public function new(socket:KromSocket) {
_socket = socket;
_buffer = new BytesOutput();
}
public function write(data:Bytes):Void {
_buffer.write(data);
}
public function writeBytes(buf:Bytes, pos:Int, len:Int):Int {
_buffer.writeBytes(buf, pos, len);
return len;
}
public function flush():Void {
var data = _buffer.getBytes();
if (data.length > 0) {
_socket.sendRaw(data);
_buffer = new BytesOutput();
}
}
}
class KromHost {
public var host:String;
public function new(hostname:String) {
host = hostname;
}
public static function resolve(hostname:String):String {
return hostname;
}
public function toString():String {
return host;
}
}

View File

@ -13,11 +13,15 @@ import iron.math.Vec2;
import haxe.ds.Vector;
import iron.object.Object;
import iron.object.Animation;
import iron.object.Animation.ActionSampler;
#if lnx_skin
import iron.object.BoneAnimation;
#end
import iron.object.ObjectAnimation;
class AnimationExtension {
#if lnx_skin
public static function solveIKBlend(boneAnimation: BoneAnimation, actionMats: Array<Mat4>, effector: TObj, goal: Vec4, precision = 0.01, maxIterations = 100, chainLenght = 100, pole: Vec4 = null, rollAngle = 0.0, influence = 0.0, layerMask: Null<Int> = null, threshold: FastFloat = 0.1) {
var matsBlend = boneAnimation.initMatsEmpty();
@ -31,7 +35,9 @@ class AnimationExtension {
boneAnimation.solveIK(actionMats, effector, goal, precision, maxIterations, chainLenght, pole, rollAngle);
boneAnimation.blendAction(matsBlend, actionMats, actionMats, influence, layerMask, threshold);
}
#end
#if lnx_skin
public static function solveTwoBoneIKBlend(boneAnimation: BoneAnimation, actionMats: Array<Mat4>, effector: TObj, goal: Vec4, pole: Vec4 = null, rollAngle = 0.0, influence = 0.0, layerMask: Null<Int> = null, threshold: FastFloat = 0.1) {
var matsBlend = boneAnimation.initMatsEmpty();
@ -45,6 +51,7 @@ class AnimationExtension {
boneAnimation.solveTwoBoneIK(actionMats, effector, goal, pole, rollAngle);
boneAnimation.blendAction(matsBlend, actionMats, actionMats, influence, layerMask, threshold);
}
#end
static inline function sortWeights(vecs: Array<Vec2>, sampleVec: Vec2): Map<Int, Vec2> {
var weightIndex: Array<WeightIndex> = [];
@ -113,7 +120,9 @@ class AnimationExtension {
class OneShotOperator {
#if lnx_skin
var boneAnimation: BoneAnimation;
#end
var objectAnimation: ObjectAnimation;
var isArmature: Bool;
var oneShotAction: ActionSampler;
@ -136,12 +145,15 @@ class OneShotOperator {
var animation = animation;
this.oneShotAction = oneShotAction;
#if lnx_skin
if(Std.isOfType(animation, BoneAnimation)) {
boneAnimation = cast animation;
tempMatsBone = boneAnimation.initMatsEmpty();
this.isArmature = true;
}
else {
else
#end
{
objectAnimation = cast animation;
tempMatsObject = objectAnimation.initTransformMap();
this.isArmature = false;
@ -150,10 +162,13 @@ class OneShotOperator {
}
function initOneShot() {
#if lnx_skin
if(isArmature) {
totalFrames = boneAnimation.getTotalFrames(oneShotAction) - 1;
}
else {
else
#end
{
totalFrames = objectAnimation.getTotalFrames(oneShotAction) - 1;
}
blendFactor = 0.0;
@ -257,7 +272,9 @@ class OneShotOperator {
class SwitchActionOperator {
#if lnx_skin
var boneAnimation: BoneAnimation;
#end
var objectAnimation: ObjectAnimation;
var isArmature: Bool;
var boneLayer: Null<Int>;
@ -268,12 +285,14 @@ class SwitchActionOperator {
var tween: TAnim = null;
public function new(animation: Animation) {
#if lnx_skin
if(Std.isOfType(animation, BoneAnimation)) {
boneAnimation = cast animation;
this.isArmature = true;
}
else {
else
#end
{
objectAnimation = cast animation;
this.isArmature = false;
}
@ -338,6 +357,7 @@ class SwitchActionOperator {
}
}
#if lnx_skin
class SimpleBiPedalIK {
var object: Object;
@ -477,6 +497,7 @@ class SimpleBiPedalIK {
}
}
#end
@:enum abstract SelectAction(Int) from Int to Int {
var action1 = 0;

View File

@ -0,0 +1,56 @@
package leenkx.renderpath;
import iron.RenderPath;
/**
* AMD FidelityFX Super Resolution (FSR V1) - Spatial Upscaling
* MIT License -
* Quality sharpness values:
* - Ultra_Quality: 0.0
* - Quality: 0.25
* - Balanced: 0.5
* - Performance: 0.75
*/
class FSR1 {
static var path: RenderPath;
public static inline var ULTRA_QUALITY: Float = 0.0;
public static inline var QUALITY: Float = 0.25;
public static inline var BALANCED: Float = 0.5;
public static inline var PERFORMANCE: Float = 0.75;
public static function init(_path: RenderPath) {
path = _path;
#if rp_fsr1
path.loadShader("shader_datas/fsr1_easu_pass/fsr1_easu_pass");
path.loadShader("shader_datas/fsr1_rcas_pass/fsr1_rcas_pass");
#end
}
public static function getSharpnessFromPreset(preset: String): Float {
return switch(preset) {
case "Ultra_Quality": ULTRA_QUALITY;
case "Quality": QUALITY;
case "Balanced": BALANCED;
case "Performance": PERFORMANCE;
default: QUALITY;
};
}
public static function applyRCAS(inputTarget: String, outputTarget: String = null) {
#if rp_fsr1
if (path == null) return;
if (outputTarget != null) {
path.setTarget(outputTarget);
} else {
path.setTarget("");
}
path.bindTarget(inputTarget, "tex");
path.drawShader("shader_datas/fsr1_rcas_pass/fsr1_rcas_pass");
#end
}
}

View File

@ -14,6 +14,13 @@ class Inc {
static var spotIndex = 0;
static var lastFrame = -1;
#if lnx_shadowmap_atlas
static var tilesToRemove: Array<ShadowMapTile> = [];
#if lnx_shadowmap_atlas_lod
static var tilesToChangeSize: Array<ShadowMapTile> = [];
#end
#end
#if ((rp_voxels != 'Off') && lnx_config)
static var voxelsCreated = false;
#end
@ -34,10 +41,10 @@ class Inc {
#if (rp_voxels == "Voxel GI")
static var voxel_td1:kha.compute.TextureUnit;
static var voxel_te1:kha.compute.TextureUnit;
static var voxel_tf1:kha.compute.TextureUnit;
static var voxel_cc1:kha.compute.ConstantLocation;
#else
#if lnx_voxelgi_shadows
static var voxel_tf1:kha.compute.TextureUnit;
static var voxel_te1:kha.compute.TextureUnit;
#end
#end
#if (lnx_voxelgi_shadows || rp_voxels == "Voxel GI")
@ -53,12 +60,28 @@ class Inc {
static var voxel_tb3:kha.compute.TextureUnit;
static var voxel_tc3:kha.compute.TextureUnit;
static var voxel_td3:kha.compute.TextureUnit;
static var voxel_te3:kha.compute.TextureUnit;
static var voxel_tf3:kha.compute.TextureUnit;
#if lnx_brdf
static var voxel_tg3:kha.compute.TextureUnit;
#end
#if lnx_radiance
static var voxel_th3:kha.compute.TextureUnit;
#end
static var voxel_ca3:kha.compute.ConstantLocation;
static var voxel_cb3:kha.compute.ConstantLocation;
static var voxel_cc3:kha.compute.ConstantLocation;
static var voxel_cd3:kha.compute.ConstantLocation;
static var voxel_ce3:kha.compute.ConstantLocation;
#if lnx_irradiance
static var voxel_cf3:kha.compute.ConstantLocation;
#end
#if lnx_radiance
static var voxel_cg3:kha.compute.ConstantLocation;
#end
#if lnx_envcol
static var voxel_ch3:kha.compute.ConstantLocation;
#end
#if (rp_voxels == "Voxel GI")
static var voxel_sh4:kha.compute.Shader = null;
static var voxel_ta4:kha.compute.TextureUnit;
@ -71,33 +94,6 @@ class Inc {
static var voxel_cb4:kha.compute.ConstantLocation;
static var voxel_cc4:kha.compute.ConstantLocation;
static var voxel_cd4:kha.compute.ConstantLocation;
static var voxel_ce4:kha.compute.ConstantLocation;
static var voxel_cf4:kha.compute.ConstantLocation;
#end
#if (rp_voxels == "Voxel GI")
static var voxel_sh5:kha.compute.Shader = null;
static var voxel_ta5:kha.compute.TextureUnit;
static var voxel_ca5:kha.compute.ConstantLocation;
static var voxel_cb5:kha.compute.ConstantLocation;
static var voxel_cc5:kha.compute.ConstantLocation;
static var voxel_cd5:kha.compute.ConstantLocation;
static var voxel_ce5:kha.compute.ConstantLocation;
static var voxel_cf5:kha.compute.ConstantLocation;
static var voxel_cg5:kha.compute.ConstantLocation;
#if rp_shadowmap
static var voxel_tb5:kha.compute.TextureUnit;
static var voxel_tc5:kha.compute.TextureUnit;
static var voxel_td5:kha.compute.TextureUnit;
static var voxel_ch5:kha.compute.ConstantLocation;
static var voxel_ci5:kha.compute.ConstantLocation;
static var voxel_cj5:kha.compute.ConstantLocation;
static var voxel_ck5:kha.compute.ConstantLocation;
static var voxel_cl5:kha.compute.ConstantLocation;
static var voxel_cm5:kha.compute.ConstantLocation;
#if lnx_shadowmap_atlas
static var m2 = iron.math.Mat4.identity();
#end
#end
#end
#end //rp_voxels
@ -160,12 +156,41 @@ class Inc {
}
public static function bindShadowMapAtlas() {
var hasAtlas = false;
for (atlas in ShadowMapAtlas.shadowMapAtlases) {
path.bindTarget(atlas.target, atlas.target);
hasAtlas = true;
}
if (!hasAtlas) {
#if lnx_shadowmap_atlas_single_map
path.bindTarget("empty_shadowmap", "shadowMapAtlas");
#else
path.bindTarget("empty_shadowmap", "shadowMapAtlasSun");
path.bindTarget("empty_shadowmap", "shadowMapAtlasPoint");
path.bindTarget("empty_shadowmap", "shadowMapAtlasSpot");
#end
}
#if rp_shadowmap_transparent
var hasAtlasT = false;
for (atlas in ShadowMapAtlas.shadowMapAtlasesTransparent) {
path.bindTarget(atlas.target, atlas.target);
hasAtlasT = true;
}
if (!hasAtlasT) {
#if lnx_shadowmap_atlas_single_map
path.bindTarget("empty_shadowmap_transparent", "shadowMapAtlasTransparent");
#else
path.bindTarget("empty_shadowmap_transparent", "shadowMapAtlasSunTransparent");
path.bindTarget("empty_shadowmap_transparent", "shadowMapAtlasPointTransparent");
path.bindTarget("empty_shadowmap_transparent", "shadowMapAtlasSpotTransparent");
#end
}
#end
}
static function getShadowMapAtlas(atlas:ShadowMapAtlas, transparent: Bool):String {
@ -206,29 +231,35 @@ class Inc {
for (atlas in ShadowMapAtlas.shadowMapAtlases) {
atlas.rejectedLights = [];
}
#if rp_shadowmap_transparent
for (atlas in ShadowMapAtlas.shadowMapAtlasesTransparent) {
atlas.rejectedLights = [];
}
#end
#end
for (light in iron.Scene.active.lights) {
if (!light.lightInAtlas && !light.culledLight && light.visible && light.shadowMapScale > 0.0
&& light.data.raw.strength > 0.0 && light.data.raw.cast_shadow) {
ShadowMapAtlas.addLight(light, false);
}
#if rp_shadowmap_transparent
if (!light.lightInAtlasTransparent && !light.culledLight && light.visible && light.shadowMapScale > 0.0
&& light.data.raw.strength > 0.0 && light.data.raw.cast_shadow) {
ShadowMapAtlas.addLight(light, true);
}
#end
}
// update point light data before rendering
updatePointLightAtlasData(true);
updatePointLightAtlasData(false);
#if rp_shadowmap_transparent
updatePointLightAtlasData(true);
#end
for (atlas in ShadowMapAtlas.shadowMapAtlases) {
var tilesToRemove = [];
tilesToRemove.resize(0);
#if lnx_shadowmap_atlas_lod
var tilesToChangeSize = [];
tilesToChangeSize.resize(0);
#end
var shadowmap = getShadowMapAtlas(atlas, false);
@ -299,12 +330,14 @@ class Inc {
path.currentFace = -1;
}
path.endStream();
path.currentG = null;
}
#if rp_shadowmap_transparent
for (atlas in ShadowMapAtlas.shadowMapAtlasesTransparent) {
var tilesToRemove = [];
tilesToRemove.resize(0);
#if lnx_shadowmap_atlas_lod
var tilesToChangeSize = [];
tilesToChangeSize.resize(0);
#end
var shadowmap = getShadowMapAtlas(atlas, true);
@ -376,6 +409,8 @@ class Inc {
}
path.endStream();
path.currentG = null;
#if lnx_shadowmap_atlas_lod
for (tile in tilesToChangeSize) {
tilesToRemove.push(tile);
@ -384,9 +419,6 @@ class Inc {
if (newTile != null)
atlas.activeTiles.push(newTile);
}
// update point light data after changing size of tiles to avoid render issues
updatePointLightAtlasData(false);
// update point light data after changing size of tiles to avoid render issues
updatePointLightAtlasData(true);
#end
@ -395,10 +427,8 @@ class Inc {
tile.freeTile();
}
}
#if lnx_debug
endShadowsLogicProfile();
#end
#end // rp_shadowmap
#end
}
#else
public static function bindShadowMap() {
@ -501,6 +531,7 @@ class Inc {
else if (l.data.raw.type == "spot" || l.data.raw.type == "area") spotIndex++;
}
#if rp_shadowmap_transparent
pointIndex = 0;
spotIndex = 0;
for (l in iron.Scene.active.lights) {
@ -522,6 +553,7 @@ class Inc {
if (l.data.raw.type == "point") pointIndex++;
else if (l.data.raw.type == "spot" || l.data.raw.type == "area") spotIndex++;
}
#end
#end // rp_shadowmap
}
#end
@ -556,8 +588,16 @@ class Inc {
}
if (superSample != config.rp_supersample) {
superSample = config.rp_supersample;
var inVRPresent = false;
#if (kha_webgl && lnx_vr)
inVRPresent = kha.vr.VrInterface.instance != null &&
kha.vr.VrInterface.instance.IsPresenting();
#end
for (rt in path.renderTargets) {
if (rt.raw.width == 0 && rt.raw.scale != null) {
// VR present mode renderpath automatically overrides Inc.superSample to 4.0
rt.raw.scale = getSuperSampling();
}
}
@ -589,7 +629,7 @@ class Inc {
t.width = 0;
t.height = 0;
t.displayp = getDisplayp();
t.format = "R32";
t.format = "R16";
t.scale = getSuperSampling();
t.depth_buffer = "main";
path.createRenderTarget(t);
@ -615,10 +655,14 @@ class Inc {
#end
#if (rp_voxels != "Off")
{
path.bindTarget("voxelsOut", "voxels");
path.bindTarget("voxelsSDF", "voxelsSDF");
}
path.bindTarget("voxelsOut", "voxels");
#if (rp_voxels == "Voxel GI" || lnx_voxelgi_shadows)
path.bindTarget("voxelsSDF", "voxelsSDF");
#end
#end
#if rp_ssrs
path.bindTarget("_main", "gbufferD");
#end
path.drawMeshes("translucent");
@ -679,12 +723,11 @@ class Inc {
t.width = 0;
t.height = 0;
t.displayp = getDisplayp();
//t.scale = Inc.getSuperSampling();
t.format = t.name == "voxels_ao" ? "R8" : "RGBA32";
t.format = "RGBA32";
}
else {
if (t.name == "voxelsSDF" || t.name == "voxelsSDFtmp") {
t.format = "R16";
t.format = "R8";
t.width = res;
t.height = res * Main.voxelgiClipmapCount;
t.depth = res;
@ -693,16 +736,16 @@ class Inc {
#if (rp_voxels == "Voxel AO")
{
if (t.name == "voxelsOut" || t.name == "voxelsOutB") {
t.format = "R16";
t.format = "R8";
t.width = res * (6 + 16);
t.height = res * Main.voxelgiClipmapCount;
t.depth = res;
}
else {
t.format = "R32";
t.format = "R32UI";
t.width = res * 6;
t.height = res;
t.depth = res;
t.depth = res * 2;
}
}
#else
@ -713,17 +756,11 @@ class Inc {
t.height = res * Main.voxelgiClipmapCount;
t.depth = res;
}
else if (t.name == "voxelsLight") {
t.format = "R32";
t.width = res;
t.height = res;
t.depth = res * 3;
}
else {
t.format = "R32";
t.format = "R32UI";
t.width = res * 6;
t.height = res;
t.depth = res * 12;
t.depth = res * 16;
}
}
#end
@ -835,14 +872,15 @@ class Inc {
voxel_ca1 = voxel_sh1.getConstantLocation("clipmaps");
voxel_cb1 = voxel_sh1.getConstantLocation("clipmapLevel");
voxel_cc1 = voxel_sh1.getConstantLocation("envmapStrength");
#if (rp_voxels == "Voxel GI")
voxel_td1 = voxel_sh1.getTextureUnit("voxelsSampler");
voxel_te1 = voxel_sh1.getTextureUnit("voxelsLight");
voxel_tf1 = voxel_sh1.getTextureUnit("SDF");
voxel_te1 = voxel_sh1.getTextureUnit("SDF");
voxel_cc1 = voxel_sh1.getConstantLocation("envmapStrength");
#else
#if lnx_voxelgi_shadows
voxel_tf1 = voxel_sh1.getTextureUnit("SDF");
voxel_te1 = voxel_sh1.getTextureUnit("SDF");
#end
#end
}
@ -873,12 +911,28 @@ class Inc {
#else
voxel_td3 = voxel_sh3.getTextureUnit("voxels_diffuse");
#end
voxel_ca3 = voxel_sh3.getConstantLocation("clipmaps");
voxel_te3 = voxel_sh3.getTextureUnit("gbuffer1");
voxel_tf3 = voxel_sh3.getTextureUnit("gbuffer2");
#if lnx_brdf
voxel_tg3 = voxel_sh3.getTextureUnit("senvmapBrdf");
#end
#if lnx_radiance
voxel_th3 = voxel_sh3.getTextureUnit("senvmapRadiance");
#end
voxel_ca3 = voxel_sh3.getConstantLocation("clipmaps");
voxel_cb3 = voxel_sh3.getConstantLocation("InvVP");
voxel_cc3 = voxel_sh3.getConstantLocation("cameraProj");
voxel_cd3 = voxel_sh3.getConstantLocation("eye");
voxel_ce3 = voxel_sh3.getConstantLocation("eyeLook");
voxel_cf3 = voxel_sh3.getConstantLocation("postprocess_resolution");
voxel_cc3 = voxel_sh3.getConstantLocation("eye");
voxel_cd3 = voxel_sh3.getConstantLocation("postprocess_resolution");
voxel_ce3 = voxel_sh3.getConstantLocation("envmapStrength");
#if lnx_irradiance
voxel_cf3 = voxel_sh3.getConstantLocation("shirr");
#end
#if lnx_radiance
voxel_cg3 = voxel_sh3.getConstantLocation("envmapNumMipmaps");
#end
#if lnx_envcol
voxel_ch3 = voxel_sh3.getConstantLocation("backgroundCol");
#end
}
#if (rp_voxels == "Voxel GI")
if (voxel_sh4 == null)
@ -892,40 +946,8 @@ class Inc {
voxel_tf4 = voxel_sh4.getTextureUnit("sveloc");
voxel_ca4 = voxel_sh4.getConstantLocation("clipmaps");
voxel_cb4 = voxel_sh4.getConstantLocation("InvVP");
voxel_cc4 = voxel_sh4.getConstantLocation("cameraProj");
voxel_cd4 = voxel_sh4.getConstantLocation("eye");
voxel_ce4 = voxel_sh4.getConstantLocation("eyeLook");
voxel_cf4 = voxel_sh4.getConstantLocation("postprocess_resolution");
}
#end
#if (rp_voxels == "Voxel GI")
if (voxel_sh5 == null)
{
voxel_sh5 = path.getComputeShader("voxel_light");
voxel_ta5 = voxel_sh5.getTextureUnit("voxelsLight");
voxel_ca5 = voxel_sh5.getConstantLocation("clipmaps");
voxel_cb5 = voxel_sh5.getConstantLocation("clipmapLevel");
voxel_cc5 = voxel_sh5.getConstantLocation("lightPos");
voxel_cd5 = voxel_sh5.getConstantLocation("lightColor");
voxel_ce5 = voxel_sh5.getConstantLocation("lightType");
voxel_cf5 = voxel_sh5.getConstantLocation("lightDir");
voxel_cg5 = voxel_sh5.getConstantLocation("spotData");
#if rp_shadowmap
voxel_tb5 = voxel_sh5.getTextureUnit("shadowMap");
voxel_tc5 = voxel_sh5.getTextureUnit("shadowMapSpot");
voxel_td5 = voxel_sh5.getTextureUnit("shadowMapPoint");
voxel_ch5 = voxel_sh5.getConstantLocation("lightShadow");
voxel_ci5 = voxel_sh5.getConstantLocation("lightProj");
voxel_cj5 = voxel_sh5.getConstantLocation("LVP");
voxel_ck5 = voxel_sh5.getConstantLocation("shadowsBias");
#if lnx_shadowmap_atlas
voxel_cl5 = voxel_sh5.getConstantLocation("index");
voxel_cm5 = voxel_sh5.getConstantLocation("pointLightDataArray");
#end
#end
voxel_cc4 = voxel_sh4.getConstantLocation("eye");
voxel_cd4 = voxel_sh4.getConstantLocation("postprocess_resolution");
}
#end
}
@ -976,11 +998,11 @@ class Inc {
kha.compute.Compute.setTexture(voxel_tc1, rts.get("voxelsOut").image, kha.compute.Access.Write);
#if (rp_voxels == "Voxel GI")
kha.compute.Compute.setSampledTexture(voxel_td1, rts.get("voxelsOutB").image);
kha.compute.Compute.setTexture(voxel_te1, rts.get("voxelsLight").image, kha.compute.Access.Read);
kha.compute.Compute.setTexture(voxel_tf1, rts.get("voxelsSDF").image, kha.compute.Access.Write);
kha.compute.Compute.setTexture(voxel_te1, rts.get("voxelsSDF").image, kha.compute.Access.Write);
kha.compute.Compute.setFloat(voxel_cc1, iron.Scene.active.world == null ? 0.0 : iron.Scene.active.world.probe.raw.strength);
#else
#if lnx_voxelgi_shadows
kha.compute.Compute.setTexture(voxel_tf1, rts.get("voxelsSDF").image, kha.compute.Access.Write);
kha.compute.Compute.setTexture(voxel_te1, rts.get("voxelsSDF").image, kha.compute.Access.Write);
#end
#end
@ -1002,6 +1024,8 @@ class Inc {
kha.compute.Compute.setInt(voxel_cb1, iron.RenderPath.clipmapLevel);
kha.compute.Compute.setFloat(voxel_cc1, iron.Scene.active.world == null ? 0.0 : iron.Scene.active.world.probe.raw.strength);
kha.compute.Compute.compute(Std.int(res / 8), Std.int(res / 8), Std.int(res / 8));
}
@ -1054,6 +1078,7 @@ class Inc {
}
}
#end
#if (rp_voxels == "Voxel AO")
public static function resolveAO() {
var rts = path.renderTargets;
@ -1066,13 +1091,20 @@ class Inc {
kha.compute.Compute.setSampledTexture(voxel_ta3, rts.get("voxelsOut").image);
kha.compute.Compute.setSampledTexture(voxel_tb3, rts.get("half").image);
#if lnx_deferred
kha.compute.Compute.setSampledTexture(voxel_tc3, rts.get("gbuffer0").image);
#else
kha.compute.Compute.setSampledTexture(voxel_tc3, rts.get("lbuffer1").image);
#end
kha.compute.Compute.setTexture(voxel_td3, rts.get("voxels_ao").image, kha.compute.Access.Write);
kha.compute.Compute.setSampledTexture(voxel_te3, rts.get("gbuffer1").image);
#if rp_gbuffer2
kha.compute.Compute.setSampledTexture(voxel_tf3, rts.get("gbuffer2").image);
#end
#if lnx_brdf
kha.compute.Compute.setSampledTexture(voxel_tg3, iron.Scene.active.embedded.get("brdf.png"));
#end
#if lnx_radiance
kha.compute.Compute.setSampledTexture(voxel_th3, iron.Scene.active.world.probe.radiance);
#end
var fa:Float32Array = new Float32Array(Main.voxelgiClipmapCount * 10);
for (i in 0...Main.voxelgiClipmapCount) {
fa[i * 10] = clipmaps[i].voxelSize;
@ -1099,18 +1131,7 @@ class Inc {
kha.compute.Compute.setMatrix(voxel_cb3, m.self);
var near = camera.data.raw.near_plane;
var far = camera.data.raw.far_plane;
var v = new iron.math.Vec2();
v.x = far / (far - near);
v.y = (-far * near) / (far - near);
kha.compute.Compute.setFloat2(voxel_cc3, v.x, v.y);
kha.compute.Compute.setFloat3(voxel_cd3, camera.transform.worldx(), camera.transform.worldy(), camera.transform.worldz());
var eyeLook = camera.lookWorld().normalize();
kha.compute.Compute.setFloat3(voxel_ce3, eyeLook.x, eyeLook.y, eyeLook.z);
kha.compute.Compute.setFloat3(voxel_cc3, camera.transform.worldx(), camera.transform.worldy(), camera.transform.worldz());
var width = iron.App.w();
var height = iron.App.h();
@ -1125,7 +1146,32 @@ class Inc {
width = Std.int(dp * Inc.getSuperSampling());
}
}
kha.compute.Compute.setFloat2(voxel_cf3, width, height);
kha.compute.Compute.setFloat2(voxel_cd3, width, height);
kha.compute.Compute.setFloat(voxel_ce3, iron.Scene.active.world == null ? 0.0 : iron.Scene.active.world.probe.raw.strength);
#if lnx_irradiance
var irradiance = iron.Scene.active.world == null ?
iron.data.WorldData.getEmptyIrradiance() :
iron.Scene.active.world.probe.irradiance;
kha.compute.Compute.setFloats(voxel_cf3, irradiance);
#end
#if lnx_radiance
kha.compute.Compute.setFloat(voxel_cg3, iron.Scene.active.world != null ? iron.Scene.active.world.probe.raw.radiance_mipmaps + 1 - 2 : 1);
#end
#if lnx_envcol
var x: kha.FastFloat = 0.0;
var y: kha.FastFloat = 0.0;
var z: kha.FastFloat = 0.0;
if (camera.data.raw.clear_color != null) {
x = camera.data.raw.clear_color[0];
y = camera.data.raw.clear_color[1];
z = camera.data.raw.clear_color[2];
}
kha.compute.Compute.setFloat3(voxel_ch3, x, y, z);
#end
kha.compute.Compute.compute(Std.int((width + 7) / 8), Std.int((height + 7) / 8), 1);
}
@ -1141,12 +1187,18 @@ class Inc {
kha.compute.Compute.setSampledTexture(voxel_ta3, rts.get("voxelsOut").image);
kha.compute.Compute.setSampledTexture(voxel_tb3, rts.get("half").image);
#if lnx_deferred
kha.compute.Compute.setSampledTexture(voxel_tc3, rts.get("gbuffer0").image);
#else
kha.compute.Compute.setSampledTexture(voxel_tc3, rts.get("lbuffer1").image);
#end
kha.compute.Compute.setTexture(voxel_td3, rts.get("voxels_diffuse").image, kha.compute.Access.Write);
kha.compute.Compute.setSampledTexture(voxel_te3, rts.get("gbuffer1").image);
#if rp_gbuffer2
kha.compute.Compute.setSampledTexture(voxel_tf3, rts.get("gbuffer2").image);
#end
#if lnx_brdf
kha.compute.Compute.setSampledTexture(voxel_tg3, iron.Scene.active.embedded.get("brdf.png"));
#end
#if lnx_radiance
kha.compute.Compute.setSampledTexture(voxel_th3, iron.Scene.active.world.probe.radiance);
#end
var fa:Float32Array = new Float32Array(Main.voxelgiClipmapCount * 10);
for (i in 0...Main.voxelgiClipmapCount) {
@ -1174,18 +1226,7 @@ class Inc {
kha.compute.Compute.setMatrix(voxel_cb3, m.self);
var near = camera.data.raw.near_plane;
var far = camera.data.raw.far_plane;
var v = new iron.math.Vec2();
v.x = far / (far - near);
v.y = (-far * near) / (far - near);
kha.compute.Compute.setFloat2(voxel_cc3, v.x, v.y);
kha.compute.Compute.setFloat3(voxel_cd3, camera.transform.worldx(), camera.transform.worldy(), camera.transform.worldz());
var eyeLook = camera.lookWorld().normalize();
kha.compute.Compute.setFloat3(voxel_ce3, eyeLook.x, eyeLook.y, eyeLook.z);
kha.compute.Compute.setFloat3(voxel_cc3, camera.transform.worldx(), camera.transform.worldy(), camera.transform.worldz());
var width = iron.App.w();
var height = iron.App.h();
@ -1200,7 +1241,32 @@ class Inc {
width = Std.int(dp * Inc.getSuperSampling());
}
}
kha.compute.Compute.setFloat2(voxel_cf3, width, height);
kha.compute.Compute.setFloat2(voxel_cd3, width, height);
kha.compute.Compute.setFloat(voxel_ce3, iron.Scene.active.world == null ? 0.0 : iron.Scene.active.world.probe.raw.strength);
#if lnx_irradiance
var irradiance = iron.Scene.active.world == null ?
iron.data.WorldData.getEmptyIrradiance() :
iron.Scene.active.world.probe.irradiance;
kha.compute.Compute.setFloats(voxel_cf3, irradiance);
#end
#if lnx_radiance
kha.compute.Compute.setFloat(voxel_cg3, iron.Scene.active.world != null ? iron.Scene.active.world.probe.raw.radiance_mipmaps + 1 - 2 : 1);
#end
#if lnx_envcol
var x: kha.FastFloat = 0.0;
var y: kha.FastFloat = 0.0;
var z: kha.FastFloat = 0.0;
if (camera.data.raw.clear_color != null) {
x = camera.data.raw.clear_color[0];
y = camera.data.raw.clear_color[1];
z = camera.data.raw.clear_color[2];
}
kha.compute.Compute.setFloat3(voxel_ch3, x, y, z);
#end
kha.compute.Compute.compute(Std.int((width + 7) / 8), Std.int((height + 7) / 8), 1);
}
@ -1216,15 +1282,12 @@ class Inc {
kha.compute.Compute.setSampledTexture(voxel_ta4, rts.get("voxelsOut").image);
kha.compute.Compute.setSampledTexture(voxel_tb4, rts.get("half").image);
#if lnx_deferred
kha.compute.Compute.setSampledTexture(voxel_tc4, rts.get("gbuffer0").image);
#else
kha.compute.Compute.setSampledTexture(voxel_tc4, rts.get("lbuffer1").image);
#end
kha.compute.Compute.setSampledTexture(voxel_td4, rts.get("voxelsSDF").image);
kha.compute.Compute.setTexture(voxel_te4, rts.get("voxels_specular").image, kha.compute.Access.Write);
//kha.compute.Compute.setSampledTexture(voxel_tf4, rts.get("gbuffer2").image);
#if rp_gbuffer2
kha.compute.Compute.setSampledTexture(voxel_tf4, rts.get("gbuffer2").image);
#end
var fa:Float32Array = new Float32Array(Main.voxelgiClipmapCount * 10);
for (i in 0...Main.voxelgiClipmapCount) {
@ -1252,18 +1315,7 @@ class Inc {
kha.compute.Compute.setMatrix(voxel_cb4, m.self);
var near = camera.data.raw.near_plane;
var far = camera.data.raw.far_plane;
var v = new iron.math.Vec2();
v.x = far / (far - near);
v.y = (-far * near) / (far - near);
kha.compute.Compute.setFloat2(voxel_cc4, v.x, v.y);
kha.compute.Compute.setFloat3(voxel_cd4, camera.transform.worldx(), camera.transform.worldy(), camera.transform.worldz());
var eyeLook = camera.lookWorld().normalize();
kha.compute.Compute.setFloat3(voxel_ce4, eyeLook.x, eyeLook.y, eyeLook.z);
kha.compute.Compute.setFloat3(voxel_cc4, camera.transform.worldx(), camera.transform.worldy(), camera.transform.worldz());
var width = iron.App.w();
var height = iron.App.h();
@ -1278,146 +1330,10 @@ class Inc {
width = Std.int(dp * Inc.getSuperSampling());
}
}
kha.compute.Compute.setFloat2(voxel_cf4, width, height);
kha.compute.Compute.setFloat2(voxel_cd4, width, height);
kha.compute.Compute.compute(Std.int((width + 7) / 8), Std.int((height + 7) / 8), 1);
}
public static function computeVoxelsLight() {
var rts = path.renderTargets;
var res = iron.RenderPath.getVoxelRes();
var camera = iron.Scene.active.camera;
var clipmaps = iron.RenderPath.clipmaps;
var clipmap = clipmaps[iron.RenderPath.clipmapLevel];
var lights = iron.Scene.active.lights;
pointIndex = spotIndex = 0;
for (i in 0...lights.length) {
var l = lights[i];
if (!l.visible) continue;
path.light = l;
kha.compute.Compute.setShader(voxel_sh5);
kha.compute.Compute.setTexture(voxel_ta5, rts.get("voxelsLight").image, kha.compute.Access.Write);
var fa:Float32Array = new Float32Array(Main.voxelgiClipmapCount * 10);
for (i in 0...Main.voxelgiClipmapCount) {
fa[i * 10] = clipmaps[i].voxelSize;
fa[i * 10 + 1] = clipmaps[i].extents.x;
fa[i * 10 + 2] = clipmaps[i].extents.y;
fa[i * 10 + 3] = clipmaps[i].extents.z;
fa[i * 10 + 4] = clipmaps[i].center.x;
fa[i * 10 + 5] = clipmaps[i].center.y;
fa[i * 10 + 6] = clipmaps[i].center.z;
fa[i * 10 + 7] = clipmaps[i].offset_prev.x;
fa[i * 10 + 8] = clipmaps[i].offset_prev.y;
fa[i * 10 + 9] = clipmaps[i].offset_prev.z;
}
kha.compute.Compute.setFloats(voxel_ca5, fa);
kha.compute.Compute.setInt(voxel_cb5, iron.RenderPath.clipmapLevel);
#if rp_shadowmap
if (l.data.raw.type == "sun") {
#if lnx_shadowmap_atlas
#if lnx_shadowmap_atlas_single_map
kha.compute.Compute.setSampledTexture(voxel_tb5, rts.get("shadowMapAtlas").image);
#else
kha.compute.Compute.setSampledTexture(voxel_tb5, rts.get("shadowMapAtlasSun").image);
#end
#else
kha.compute.Compute.setSampledTexture(voxel_tb5, rts.get("shadowMap").image);
#end
kha.compute.Compute.setInt(voxel_ch5, 1); // lightShadow
}
else if (l.data.raw.type == "spot" || l.data.raw.type == "area") {
#if lnx_shadowmap_atlas
#if lnx_shadowmap_atlas_single_map
kha.compute.Compute.setSampledTexture(voxel_tc5, rts.get("shadowMapAtlas").image);
#else
kha.compute.Compute.setSampledTexture(voxel_tc5, rts.get("shadowMapAtlasSpot").image);
#end
#else
kha.compute.Compute.setSampledTexture(voxel_tc5, rts.get("shadowMapSpot[" + spotIndex + "]").image);
spotIndex++;
#end
kha.compute.Compute.setInt(voxel_ch5, 2);
}
else {
#if lnx_shadowmap_atlas
#if lnx_shadowmap_atlas_single_map
kha.compute.Compute.setSampledTexture(voxel_td5, rts.get("shadowMapAtlas").image);
#else
kha.compute.Compute.setSampledTexture(voxel_td5, rts.get("shadowMapAtlasPoint").image);
kha.compute.Compute.setInt(voxel_cl5, i);
kha.compute.Compute.setFloats(voxel_cm5, iron.object.LightObject.pointLightsData);
#end
#else
kha.compute.Compute.setSampledCubeMap(voxel_td5, rts.get("shadowMapPoint[" + pointIndex + "]").cubeMap);
pointIndex++;
#end
kha.compute.Compute.setInt(voxel_ch5, 3);
}
// lightProj
var near = l.data.raw.near_plane;
var far = l.data.raw.far_plane;
var a:kha.FastFloat = far + near;
var b:kha.FastFloat = far - near;
var f2:kha.FastFloat = 2.0;
var c:kha.FastFloat = f2 * far * near;
var vx:kha.FastFloat = a / b;
var vy:kha.FastFloat = c / b;
kha.compute.Compute.setFloat2(voxel_ci5, vx, vy);
// LVP
m.setFrom(l.VP);
m.multmat(iron.object.Uniforms.biasMat);
#if lnx_shadowmap_atlas
if (l.data.raw.type == "sun")
{
// tile matrix
m.setIdentity();
// scale [0-1] coords to [0-tilescale]
m2._00 = l.tileScale[0];
m2._11 = l.tileScale[0];
// offset coordinate start from [0, 0] to [tile-start-x, tile-start-y]
m2._30 = l.tileOffsetX[0];
m2._31 = l.tileOffsetY[0];
m.multmat(m2);
#if (!kha_opengl)
m2.setIdentity();
m2._11 = -1.0;
m2._31 = 1.0;
m.multmat(m2);
#end
}
#end
kha.compute.Compute.setMatrix(voxel_cj5, m.self);
// shadowsBias
kha.compute.Compute.setFloat(voxel_ck5, l.data.raw.shadows_bias);
#end // rp_shadowmap
// lightPos
kha.compute.Compute.setFloat3(voxel_cc5, l.transform.worldx(), l.transform.worldy(), l.transform.worldz());
// lightCol
var f = l.data.raw.strength;
kha.compute.Compute.setFloat3(voxel_cd5, l.data.raw.color[0] * f, l.data.raw.color[1] * f, l.data.raw.color[2] * f);
// lightType
kha.compute.Compute.setInt(voxel_ce5, iron.data.LightData.typeToInt(l.data.raw.type));
// lightDir
var v = l.look();
kha.compute.Compute.setFloat3(voxel_cf5, v.x, v.y, v.z);
// spotData
if (l.data.raw.type == "spot") {
var vx = l.data.raw.spot_size;
var vy = vx - l.data.raw.spot_blend;
kha.compute.Compute.setFloat2(voxel_cg5, vx, vy);
}
kha.compute.Compute.compute(Std.int(res / 8), Std.int(res / 8), Std.int(res / 8));
}
}
#end // GI
#end // Voxels
}
@ -1498,6 +1414,10 @@ class ShadowMapAtlas {
return;
}
mainTile.forEachTileLinked(function(tile) {
tile.isTransparent = transparent;
});
atlas.activeTiles.push(mainTile);
// notify the tile on light remove
light.tileNotifyOnRemove = mainTile.notifyOnLightRemove;
@ -1686,6 +1606,7 @@ class ShadowMapTile {
public var size:Int;
public var tiles:Array<ShadowMapTile> = [];
public var linkedTile:ShadowMapTile = null;
public var isTransparent:Bool = false; // track for tile
#if lnx_shadowmap_atlas_lod
public var parentTile: ShadowMapTile = null;
@ -1917,7 +1838,11 @@ class ShadowMapTile {
public function freeTile(): Void {
// prevent duplicates
if (light != null && unlockLight) {
light.lightInAtlas = false;
if (isTransparent) {
light.lightInAtlasTransparent = false;
} else {
light.lightInAtlas = false;
}
unlockLight = false;
}

View File

@ -15,6 +15,11 @@ class RenderPathDeferred {
static var bloomUpsampler: Upsampler;
#end
#if rp_ssgi
static var ssgitex = "ssgi_a";
static var ssgitexb = "ssgi_b";
#end
public static inline function setTargetMeshes() {
//Always keep the order of render targets the same as defined in compiled.inc
path.setTarget("gbuffer0", [
@ -57,12 +62,11 @@ class RenderPathDeferred {
Inc.initGI("voxels");
Inc.initGI("voxelsOut");
Inc.initGI("voxelsOutB");
#if (lnx_voxelgi_shadows || rp_voxels == "Voxel GI")
#if (rp_voxels == "Voxel GI" || lnx_voxelgi_shadows)
Inc.initGI("voxelsSDF");
Inc.initGI("voxelsSDFtmp");
#end
#if (rp_voxels == "Voxel GI")
Inc.initGI("voxelsLight");
Inc.initGI("voxels_diffuse");
Inc.initGI("voxels_specular");
#else
@ -183,13 +187,7 @@ class RenderPathDeferred {
path.loadShader("shader_datas/copy_pass/copy_pass");
#end
#if ((rp_ssgi == "RTGI") || (rp_ssgi == "RTAO"))
{
path.loadShader("shader_datas/ssgi_pass/ssgi_pass");
path.loadShader("shader_datas/blur_edge_pass/blur_edge_pass_x");
path.loadShader("shader_datas/blur_edge_pass/blur_edge_pass_y");
}
#elseif (rp_ssgi == "SSAO")
#if rp_ssao
{
path.loadShader("shader_datas/ssao_pass/ssao_pass");
path.loadShader("shader_datas/blur_edge_pass/blur_edge_pass_x");
@ -197,7 +195,15 @@ class RenderPathDeferred {
}
#end
#if ((rp_ssgi != "Off") || rp_volumetriclight)
#if rp_ssgi
{
path.loadShader("shader_datas/ssgi_pass/ssgi_pass");
path.loadShader("shader_datas/ssgi_blur_pass/ssgi_blur_pass_x");
path.loadShader("shader_datas/ssgi_blur_pass/ssgi_blur_pass_y");
}
#end
#if rp_ssao
{
var t = new RenderTargetRaw();
t.name = "singlea";
@ -206,7 +212,7 @@ class RenderPathDeferred {
t.displayp = Inc.getDisplayp();
t.format = "R8";
t.scale = Inc.getSuperSampling();
#if rp_ssgi_half
#if rp_ssao_half
t.scale *= 0.5;
#end
path.createRenderTarget(t);
@ -218,10 +224,60 @@ class RenderPathDeferred {
t.displayp = Inc.getDisplayp();
t.format = "R8";
t.scale = Inc.getSuperSampling();
#if rp_ssao_half
t.scale *= 0.5;
#end
path.createRenderTarget(t);
}
#end
#if rp_ssgi
{
var t = new RenderTargetRaw();
t.name = "ssgi_a";
t.width = 0;
t.height = 0;
t.displayp = Inc.getDisplayp();
t.format = "RGBA32";
t.scale = Inc.getSuperSampling();
#if rp_ssgi_half
t.scale *= 0.5;
#end
path.createRenderTarget(t);
var t = new RenderTargetRaw();
t.name = "ssgi_b";
t.width = 0;
t.height = 0;
t.displayp = Inc.getDisplayp();
t.format = "RGBA32";
t.scale = Inc.getSuperSampling();
#if rp_ssgi_half
t.scale *= 0.5;
#end
path.createRenderTarget(t);
}
#end
#if rp_volumetriclight
{
var t = new RenderTargetRaw();
t.name = "volumetrica";
t.width = 0;
t.height = 0;
t.displayp = Inc.getDisplayp();
t.format = "R8";
t.scale = Inc.getSuperSampling();
path.createRenderTarget(t);
var t = new RenderTargetRaw();
t.name = "volumetricb";
t.width = 0;
t.height = 0;
t.displayp = Inc.getDisplayp();
t.format = "R8";
t.scale = Inc.getSuperSampling();
path.createRenderTarget(t);
}
#end
@ -307,6 +363,14 @@ class RenderPathDeferred {
}
#end
#if rp_fsr1
{
path.loadShader("shader_datas/fsr1_easu_pass/fsr1_easu_pass");
path.loadShader("shader_datas/fsr1_rcas_pass/fsr1_rcas_pass");
path.loadShader("shader_datas/copy_pass/copy_pass");
}
#end
#if rp_autoexposure
{
var t = new RenderTargetRaw();
@ -327,7 +391,7 @@ class RenderPathDeferred {
}
#end
#if (rp_ssr_half || rp_ssgi_half || rp_voxels != "Off") //we need half depth for resolve voxels shaders
#if (rp_ssr_half || rp_ssao_half || rp_ssgi_half || rp_voxels != "Off") //we need half depth for resolve voxels shaders
{
path.loadShader("shader_datas/downsample_depth/downsample_depth");
var t = new RenderTargetRaw();
@ -349,6 +413,7 @@ class RenderPathDeferred {
t.displayp = Inc.getDisplayp();
t.format = Inc.getHdrFormat();
t.scale = Inc.getSuperSampling();
t.depth_buffer = "main";
path.createRenderTarget(t);
}
#end
@ -368,7 +433,7 @@ class RenderPathDeferred {
t.scale = Inc.getSuperSampling();
path.createRenderTarget(t);
// holds background depth
// holds background color
var t = new RenderTargetRaw();
t.name = "refr";
t.width = 0;
@ -437,6 +502,31 @@ class RenderPathDeferred {
}
#end
// TODO: Re-investigate creating fallback empty shadowmaps for WebGL when no lights exist
#if lnx_shadowmap_atlas
{
var t = new RenderTargetRaw();
t.name = "empty_shadowmap";
t.width = 1;
t.height = 1;
t.format = "DEPTH16";
path.createRenderTarget(t);
path.setTarget("empty_shadowmap");
path.clearTarget(null, 1.0);
#if rp_shadowmap_transparent
var t2 = new RenderTargetRaw();
t2.name = "empty_shadowmap_transparent";
t2.width = 1;
t2.height = 1;
t2.format = "RGBA64";
path.createRenderTarget(t2);
path.setTarget("empty_shadowmap_transparent");
path.clearTarget(0xffffffff, null);
#end
}
#end
#if rp_chromatic_aberration
{
path.loadShader("shader_datas/chromatic_aberration_pass/chromatic_aberration_pass");
@ -461,8 +551,8 @@ class RenderPathDeferred {
#if (rp_ssrefr || lnx_voxelgi_refract)
{
path.setTarget("gbuffer_refraction"); // Only clear gbuffer0
path.clearTarget(0xffffff00);
path.setTarget("gbuffer_refraction");
path.clearTarget(0xffff00ff);
}
#end
@ -511,38 +601,24 @@ class RenderPathDeferred {
}
#end
#if (rp_ssr_half || rp_ssgi_half || (rp_voxels != "Off"))
#if (rp_ssr_half || rp_ssao_half || rp_ssgi_half || (rp_voxels != "Off"))
path.setTarget("half");
path.bindTarget("_main", "texdepth");
path.drawShader("shader_datas/downsample_depth/downsample_depth");
#end
#if ((rp_ssgi == "RTGI") || (rp_ssgi == "RTAO"))
{
if (leenkx.data.Config.raw.rp_ssgi != false) {
path.setTarget("singlea");
#if rp_ssgi_half
path.bindTarget("half", "gbufferD");
#else
path.bindTarget("_main", "gbufferD");
#end
path.bindTarget("gbuffer0", "gbuffer0");
path.drawShader("shader_datas/ssgi_pass/ssgi_pass");
#if (rp_shadowmap)
// atlasing is exclusive for now
#if lnx_shadowmap_atlas
Inc.drawShadowMapAtlas();
#else
Inc.drawShadowMap();
#end
#end
path.setTarget("singleb");
path.bindTarget("singlea", "tex");
path.bindTarget("gbuffer0", "gbuffer0");
path.drawShader("shader_datas/blur_edge_pass/blur_edge_pass_x");
path.setTarget("singlea");
path.bindTarget("singleb", "tex");
path.bindTarget("gbuffer0", "gbuffer0");
path.drawShader("shader_datas/blur_edge_pass/blur_edge_pass_y");
}
}
#elseif (rp_ssgi == "SSAO")
#if rp_ssao
{
if (leenkx.data.Config.raw.rp_ssgi != false) {
if (leenkx.data.Config.raw.rp_ssao != false) {
path.setTarget("singlea");
path.bindTarget("_main", "gbufferD");
path.bindTarget("gbuffer0", "gbuffer0");
@ -561,13 +637,34 @@ class RenderPathDeferred {
}
#end
#if (rp_shadowmap)
// atlasing is exclusive for now
#if lnx_shadowmap_atlas
Inc.drawShadowMapAtlas();
#else
Inc.drawShadowMap();
#end
#if rp_ssgi
{
if (leenkx.data.Config.raw.rp_ssgi != false) {
path.setTarget("ssgi_a");
path.bindTarget("_main", "gbufferD");
path.bindTarget("gbuffer0", "gbuffer0");
path.bindTarget("gbuffer1", "gbuffer1");
#if rp_gbuffer_emission
{
path.bindTarget("gbuffer_emission", "gbufferEmission");
}
#end
path.drawShader("shader_datas/ssgi_pass/ssgi_pass");
path.setTarget("ssgi_b");
path.bindTarget("ssgi_a", "tex");
path.bindTarget("gbuffer0", "gbuffer0");
path.bindTarget("_main", "gbufferD");
path.drawShader("shader_datas/ssgi_blur_pass/ssgi_blur_pass_x");
path.setTarget("ssgi_a");
path.bindTarget("ssgi_b", "tex");
path.bindTarget("gbuffer0", "gbuffer0");
path.bindTarget("_main", "gbufferD");
path.drawShader("shader_datas/ssgi_blur_pass/ssgi_blur_pass_y");
}
}
#end
// Voxels
@ -580,9 +677,6 @@ class RenderPathDeferred {
if (iron.RenderPath.pre_clear == true)
{
#if (rp_voxels == "Voxel GI")
path.clearImage("voxelsLight", 0x00000000);
#end
path.clearImage("voxels", 0x00000000);
path.clearImage("voxelsOut", 0x00000000);
path.clearImage("voxelsOutB", 0x00000000);
@ -594,26 +688,30 @@ class RenderPathDeferred {
}
else
{
#if (rp_voxels == "Voxel GI")
path.clearImage("voxelsLight", 0x00000000);
#end
path.clearImage("voxels", 0x00000000);
Inc.computeVoxelsOffsetPrev();
}
path.setTarget("");
path.bindTarget("voxels", "voxels");
#if rp_shadowmap
{
#if lnx_shadowmap_atlas
Inc.bindShadowMapAtlas();
#else
Inc.bindShadowMap();
#end
}
#end
var res = iron.RenderPath.getVoxelRes();
path.setViewport(res, res);
path.bindTarget("voxels", "voxels");
path.drawMeshes("voxel");
#if (rp_voxels == "Voxel GI")
Inc.computeVoxelsLight();
#end
Inc.computeVoxelsTemporal();
#if (lnx_voxelgi_shadows || rp_voxels == "Voxel GI")
#if (rp_voxels == "Voxel GI")
Inc.computeVoxelsSDF();
#end
@ -628,7 +726,6 @@ class RenderPathDeferred {
}
}
#end
// ---
// Deferred light
// ---
@ -652,9 +749,9 @@ class RenderPathDeferred {
}
#end
#if (rp_ssgi != "Off")
#if rp_ssao
{
if (leenkx.data.Config.raw.rp_ssgi != false) {
if (leenkx.data.Config.raw.rp_ssao != false) {
path.bindTarget("singlea", "ssaotex");
}
else {
@ -663,6 +760,14 @@ class RenderPathDeferred {
}
#end
#if rp_ssgi
{
if (leenkx.data.Config.raw.rp_ssgi != false) {
path.bindTarget("ssgi_a", "ssgitex");
}
}
#end
var voxelao_pass = false;
#if (rp_voxels != "Off")
if (leenkx.data.Config.raw.rp_gi != false)
@ -766,96 +871,6 @@ class RenderPathDeferred {
}
#end
#if rp_volumetriclight
{
path.setTarget("singlea");
path.bindTarget("_main", "gbufferD");
#if lnx_shadowmap_atlas
Inc.bindShadowMapAtlas();
#else
Inc.bindShadowMap();
#end
path.drawShader("shader_datas/volumetric_light/volumetric_light");
path.setTarget("singleb");
path.bindTarget("singlea", "tex");
path.drawShader("shader_datas/blur_bilat_pass/blur_bilat_pass_x");
path.setTarget("tex");
path.bindTarget("singleb", "tex");
path.drawShader("shader_datas/blur_bilat_blend_pass/blur_bilat_blend_pass_y");
}
#end
#if rp_bloom
{
inline Inc.drawBloom("tex", bloomDownsampler, bloomUpsampler);
}
#end
#if rp_sss
{
#if (!kha_opengl)
path.setDepthFrom("tex", "gbuffer1"); // Unbind depth so we can read it
#end
path.setTarget("buf");
path.bindTarget("tex", "tex");
path.bindTarget("_main", "gbufferD");
path.bindTarget("gbuffer0", "gbuffer0");
path.drawShader("shader_datas/sss_pass/sss_pass_x");
path.setTarget("tex");
path.bindTarget("buf", "tex");
path.bindTarget("_main", "gbufferD");
path.bindTarget("gbuffer0", "gbuffer0");
path.drawShader("shader_datas/sss_pass/sss_pass_y");
#if (!kha_opengl)
path.setDepthFrom("tex", "gbuffer0");
#end
}
#end
#if rp_ssrefr
{
if (leenkx.data.Config.raw.rp_ssrefr != false)
{
//save depth
path.setTarget("gbufferD1");
path.bindTarget("_main", "tex");
path.drawShader("shader_datas/copy_pass/copy_pass");
//save background color
path.setTarget("refr");
path.bindTarget("tex", "tex");
path.drawShader("shader_datas/copy_pass/copy_pass");
path.setTarget("gbuffer0", ["tex", "gbuffer_refraction"]);
#if (rp_voxels != "Off")
{
path.bindTarget("voxelsOut", "voxels");
path.bindTarget("voxelsSDF", "voxelsSDF");
path.bindTarget("gbuffer2", "sveloc");
}
#end
path.drawMeshes("refraction");
path.setTarget("tex");
path.bindTarget("tex", "tex");
path.bindTarget("refr", "tex1");
path.bindTarget("_main", "gbufferD");
path.bindTarget("gbufferD1", "gbufferD1");
path.bindTarget("gbuffer0", "gbuffer0");
path.bindTarget("gbuffer_refraction", "gbuffer_refraction");
path.drawShader("shader_datas/ssrefr_pass/ssrefr_pass");
}
}
#end
#if rp_ssr
{
if (leenkx.data.Config.raw.rp_ssr != false) {
@ -900,6 +915,118 @@ class RenderPathDeferred {
}
#end
#if rp_sss
{
#if (!kha_opengl)
path.setDepthFrom("tex", "gbuffer1"); // Unbind depth so we can read it
#end
path.setTarget("buf");
path.bindTarget("tex", "tex");
path.bindTarget("_main", "gbufferD");
path.bindTarget("gbuffer0", "gbuffer0");
path.drawShader("shader_datas/sss_pass/sss_pass_x");
path.setTarget("tex");
path.bindTarget("buf", "tex");
path.bindTarget("_main", "gbufferD");
path.bindTarget("gbuffer0", "gbuffer0");
path.drawShader("shader_datas/sss_pass/sss_pass_y");
#if (!kha_opengl)
path.setDepthFrom("tex", "gbuffer0");
#end
}
#end
#if rp_ssrefr
{
if (leenkx.data.Config.raw.rp_ssrefr != false)
{
//save depth
path.setTarget("gbufferD1");
path.bindTarget("_main", "tex");
path.drawShader("shader_datas/copy_pass/copy_pass");
//save background color
path.setTarget("refr");
path.bindTarget("tex", "tex");
path.drawShader("shader_datas/copy_pass/copy_pass");
path.setTarget("gbuffer0", ["tex", "gbuffer_refraction"]);
#if rp_shadowmap
{
#if lnx_shadowmap_atlas
Inc.bindShadowMapAtlas();
#else
Inc.bindShadowMap();
#end
}
#end
#if (rp_voxels != "Off")
path.bindTarget("voxelsOut", "voxels");
#if (rp_voxels == "Voxel GI" || lnx_voxelgi_shadows)
path.bindTarget("voxelsSDF", "voxelsSDF");
#end
#end
#if rp_ssrs
path.bindTarget("_main", "gbufferD");
#end
path.drawMeshes("refraction");
path.setTarget("buf");
path.bindTarget("tex", "tex"); // scene with refractive objects
path.bindTarget("refr", "tex1"); // background without refractive objects
path.bindTarget("_main", "gbufferD");
path.bindTarget("gbufferD1", "gbufferD1");
path.bindTarget("gbuffer0", "gbuffer0");
path.bindTarget("gbuffer_refraction", "gbuffer_refraction");
path.drawShader("shader_datas/ssrefr_pass/ssrefr_pass");
path.setTarget("tex");
path.bindTarget("buf", "tex");
path.drawShader("shader_datas/copy_pass/copy_pass");
}
}
#end
#if rp_chromatic_aberration
{
path.setTarget("buf");
path.bindTarget("tex", "tex");
path.drawShader("shader_datas/chromatic_aberration_pass/chromatic_aberration_pass");
path.setTarget("tex");
path.bindTarget("buf", "tex");
path.drawShader("shader_datas/copy_pass/copy_pass");
}
#end
#if rp_volumetriclight
{
path.setTarget("volumetrica");
path.bindTarget("_main", "gbufferD");
#if lnx_shadowmap_atlas
Inc.bindShadowMapAtlas();
#else
Inc.bindShadowMap();
#end
path.drawShader("shader_datas/volumetric_light/volumetric_light");
path.setTarget("volumetricb");
path.bindTarget("volumetrica", "tex");
path.drawShader("shader_datas/blur_bilat_pass/blur_bilat_pass_x");
path.setTarget("tex");
path.bindTarget("volumetricb", "tex");
path.drawShader("shader_datas/blur_bilat_blend_pass/blur_bilat_blend_pass_y");
}
#end
#if ((rp_motionblur == "Camera") || (rp_motionblur == "Object"))
{
if (leenkx.data.Config.raw.rp_motionblur != false) {
@ -934,22 +1061,6 @@ class RenderPathDeferred {
}
#end
#if rp_chromatic_aberration
{
path.setTarget("buf");
path.bindTarget("tex", "tex");
path.drawShader("shader_datas/chromatic_aberration_pass/chromatic_aberration_pass");
path.setTarget("tex");
path.bindTarget("buf", "tex");
path.drawShader("shader_datas/copy_pass/copy_pass");
}
#end
// We are just about to enter compositing, add more custom passes here
// #if rp_custom_pass
// {
// }
// #end
// Begin compositor
#if rp_autoexposure
@ -964,6 +1075,12 @@ class RenderPathDeferred {
}
#end
#if rp_bloom
{
inline Inc.drawBloom("tex", bloomDownsampler, bloomUpsampler);
}
#end
#if (rp_supersampling == 4)
var framebuffer = "buf";
#else
@ -983,6 +1100,7 @@ class RenderPathDeferred {
}
#end
path.setTarget(target);
path.clearTarget(0x00000000);
path.bindTarget("tex", "tex");
#if rp_compositordepth
@ -1066,12 +1184,58 @@ class RenderPathDeferred {
}
#end
#if rp_fsr1
{
#if ((rp_antialiasing == "SMAA") || (rp_antialiasing == "TAA"))
#if (rp_supersampling == 4)
var fsrSource = "buf";
var fsrDest = "buf";
#else
path.setTarget("bufb");
path.bindTarget(framebuffer != "" ? framebuffer : "buf", "tex");
path.drawShader("shader_datas/copy_pass/copy_pass");
var fsrSource = "bufb";
var fsrDest = "";
#end
#else
#if (rp_supersampling == 4)
var fsrSource = "buf";
var fsrDest = "buf";
#else
path.setTarget("bufa");
path.bindTarget(target != "" ? target : "buf", "tex");
path.drawShader("shader_datas/copy_pass/copy_pass");
var fsrSource = "bufa";
var fsrDest = "";
#end
#end
path.setTarget(fsrDest);
path.bindTarget(fsrSource, "tex");
path.drawShader("shader_datas/fsr1_rcas_pass/fsr1_rcas_pass");
}
#end
#if (rp_supersampling == 4)
{
var finalTarget = "";
path.setTarget(finalTarget);
path.bindTarget(framebuffer, "tex");
path.drawShader("shader_datas/supersample_resolve/supersample_resolve");
path.drawShader("shader_datas/copy_pass/copy_pass");
}
#end
// VR: Composite final output to XR framebuffer
#if (kha_webgl && lnx_vr)
if (iron.RenderPath.isVRPresenting()) {
#if ((rp_antialiasing == "SMAA") || (rp_antialiasing == "TAA"))
var vrCompositeSource = framebuffer;
#else
var vrCompositeSource = "tex";
#end
if (vrCompositeSource == "") vrCompositeSource = "tex";
path.compositeToXR(vrCompositeSource);
}
#end
}

View File

@ -29,7 +29,12 @@ class RenderPathForward {
}
#else
{
path.setTarget("");
var isVR = iron.RenderPath.isVRPresenting() || iron.RenderPath.isVRSimulateMode();
if (isVR) {
path.setTarget("lbuffer0");
} else {
path.setTarget("");
}
}
#end
}
@ -142,16 +147,17 @@ class RenderPathForward {
t.width = 0;
t.height = 0;
t.displayp = Inc.getDisplayp();
t.format = "R32";
t.format = "DEPTH24";
t.scale = Inc.getSuperSampling();
path.createRenderTarget(t);
//holds colors before refractive meshes are drawn
var t = new RenderTargetRaw();
t.name = "refr";
t.width = 0;
t.height = 0;
t.displayp = Inc.getDisplayp();
t.format = "RGBA64";
t.format = Inc.getHdrFormat();
t.scale = Inc.getSuperSampling();
path.createRenderTarget(t);
}
@ -200,17 +206,10 @@ class RenderPathForward {
Inc.initGI("voxels");
Inc.initGI("voxelsOut");
Inc.initGI("voxelsOutB");
#if (lnx_voxelgi_shadows || (rp_voxels == "Voxel GI"))
#if (rp_voxels == "Voxel GI" || lnx_voxelgi_shadows)
Inc.initGI("voxelsSDF");
Inc.initGI("voxelsSDFtmp");
#end
#if (rp_voxels == "Voxel GI")
Inc.initGI("voxelsLight");
Inc.initGI("voxels_diffuse");
Inc.initGI("voxels_specular");
#else
Inc.initGI("voxels_ao");
#end
iron.RenderPath.clipmaps = new Array<Clipmap>();
for (i in 0...Main.voxelgiClipmapCount) {
var clipmap = new iron.object.Clipmap();
@ -283,6 +282,50 @@ class RenderPathForward {
}
#end
#if rp_ssao
{
var t = new RenderTargetRaw();
t.name = "singlea";
t.width = 0;
t.height = 0;
t.displayp = Inc.getDisplayp();
t.format = "R8";
t.scale = Inc.getSuperSampling();
path.createRenderTarget(t);
var t = new RenderTargetRaw();
t.name = "singleb";
t.width = 0;
t.height = 0;
t.displayp = Inc.getDisplayp();
t.format = "R8";
t.scale = Inc.getSuperSampling();
path.createRenderTarget(t);
}
#end
#if rp_ssgi
{
var t = new RenderTargetRaw();
t.name = "ssgi_a";
t.width = 0;
t.height = 0;
t.displayp = Inc.getDisplayp();
t.format = "RGBA32";
t.scale = Inc.getSuperSampling();
path.createRenderTarget(t);
var t = new RenderTargetRaw();
t.name = "ssgi_b";
t.width = 0;
t.height = 0;
t.displayp = Inc.getDisplayp();
t.format = "RGBA32";
t.scale = Inc.getSuperSampling();
path.createRenderTarget(t);
}
#end
#if rp_water
{
path.loadShader("shader_datas/water_pass/water_pass");
@ -303,7 +346,15 @@ class RenderPathForward {
}
#end
#if (rp_ssr_half || rp_ssgi_half || (rp_voxels != "Off"))
#if rp_fsr1
{
path.loadShader("shader_datas/fsr1_easu_pass/fsr1_easu_pass");
path.loadShader("shader_datas/fsr1_rcas_pass/fsr1_rcas_pass");
path.loadShader("shader_datas/copy_pass/copy_pass");
}
#end
#if (rp_ssr_half || rp_ssao_half || rp_ssgi_half || (rp_voxels != "Off"))
{
path.loadShader("shader_datas/downsample_depth/downsample_depth");
var t = new RenderTargetRaw();
@ -374,9 +425,6 @@ class RenderPathForward {
if (iron.RenderPath.pre_clear == true)
{
#if (rp_voxels == "Voxel GI")
path.clearImage("voxelsLight", 0x00000000);
#end
path.clearImage("voxels", 0x00000000);
path.clearImage("voxelsOut", 0x00000000);
path.clearImage("voxelsOutB", 0x00000000);
@ -388,9 +436,6 @@ class RenderPathForward {
}
else
{
#if (rp_voxels == "Voxel GI")
path.clearImage("voxelsLight", 0x00000000);
#end
path.clearImage("voxels", 0x00000000);
Inc.computeVoxelsOffsetPrev();
}
@ -400,27 +445,12 @@ class RenderPathForward {
path.setViewport(res, res);
path.bindTarget("voxels", "voxels");
path.drawMeshes("voxel");
#if (rp_voxels == "Voxel GI")
Inc.computeVoxelsLight();
#end
Inc.computeVoxelsTemporal();
#if (lnx_voxelgi_shadows || (rp_voxels == "Voxel GI"))
Inc.computeVoxelsSDF();
#end
if (iron.RenderPath.res_pre_clear == true)
{
iron.RenderPath.res_pre_clear = false;
#if (rp_voxels == "Voxel GI")
path.clearImage("voxels_diffuse", 0x00000000);
path.clearImage("voxels_specular", 0x00000000);
#else
path.clearImage("voxels_ao", 0x00000000);
#end
}
}
#end
@ -438,21 +468,21 @@ class RenderPathForward {
#if (rp_ssrefr || lnx_voxelgi_refract)
{
path.setTarget("gbuffer_refraction"); // Only clear gbuffer0
path.clearTarget(0xff000000);
path.setTarget("gbuffer_refraction");
path.clearTarget(0xffff00ff);
}
#end
#if rp_depthprepass
{
path.drawMeshes("depth");
}
#end
#if rp_ssrefr
{
path.setTarget("gbuffer_refraction");
path.clearTarget(0xffffff00);
#if rp_stereo
var isVR = iron.RenderPath.isVRPresenting() || iron.RenderPath.isVRSimulateMode();
if (!isVR) {
#end
path.drawMeshes("depth");
#if rp_stereo
}
#end
}
#end
@ -472,35 +502,35 @@ class RenderPathForward {
#if (rp_voxels != "Off")
if (leenkx.data.Config.raw.rp_gi != false)
{
#if (rp_voxels == "Voxel AO")
Inc.resolveAO();
path.bindTarget("voxels_ao", "voxels_ao");
#else
Inc.resolveDiffuse();
Inc.resolveSpecular();
path.bindTarget("voxels_diffuse", "voxels_diffuse");
path.bindTarget("voxels_specular", "voxels_specular");
#end
#if lnx_voxelgi_shadows
#if (rp_voxels != "Off")
path.bindTarget("voxelsOut", "voxels");
#if (rp_voxels == "Voxel GI" || lnx_voxelgi_shadows)
path.bindTarget("voxelsSDF", "voxelsSDF");
#end
#end
}
#end
#if rp_stereo
{
path.drawStereo(drawMeshes);
if (iron.RenderPath.isVRPresenting()) {
#if (kha_webgl && lnx_vr)
// split-screen lbuffer0 to XR framebuffer
path.compositeToXR("lbuffer0");
#end
return;
}
}
#else
{
RenderPathCreator.drawMeshes();
drawMeshes();
}
#end
#if (rp_render_to_texture || rp_voxels != "Off")
{
#if (rp_ssr_half || rp_ssgi_half || rp_voxels != "Off")
#if (rp_ssr_half || rp_ssao_half || rp_ssgi_half || rp_voxels != "Off")
path.setTarget("half");
path.bindTarget("_main", "texdepth");
path.drawShader("shader_datas/downsample_depth/downsample_depth");
@ -522,14 +552,30 @@ class RenderPathForward {
path.setTarget("lbuffer0", ["lbuffer1", "gbuffer_refraction"]);
#if rp_shadowmap
{
#if lnx_shadowmap_atlas
Inc.bindShadowMapAtlas();
#else
Inc.bindShadowMap();
#end
}
#end
#if (rp_voxels != "Off")
path.bindTarget("voxelsOut", "voxels");
#if (rp_voxels == "Voxel GI" || lnx_voxelgi_shadows)
path.bindTarget("voxelsSDF", "voxelsSDF");
#end
#end
#if rp_ssrs
path.bindTarget("_main", "gbufferD");
#end
path.drawMeshes("refraction");
path.setTarget("lbuffer0");
path.setTarget("bufa");
path.bindTarget("lbuffer0", "tex");
path.bindTarget("refr", "tex1");
path.bindTarget("_main", "gbufferD");
@ -538,6 +584,9 @@ class RenderPathForward {
path.bindTarget("gbuffer_refraction", "gbuffer_refraction");
path.drawShader("shader_datas/ssrefr_pass/ssrefr_pass");
path.setTarget("lbuffer0");
path.bindTarget("bufa", "tex");
path.drawShader("shader_datas/copy_pass/copy_pass");
}
}
#end
@ -577,12 +626,71 @@ class RenderPathForward {
}
#end
#if rp_ssrefr
{
if (leenkx.data.Config.raw.rp_ssrefr != false)
{
path.setTarget("gbufferD1");
path.bindTarget("_main", "tex");
path.drawShader("shader_datas/copy_pass/copy_pass");
path.setTarget("refr");
path.bindTarget("lbuffer0", "tex");
path.drawShader("shader_datas/copy_pass/copy_pass");
path.setTarget("lbuffer0", ["lbuffer1", "gbuffer_refraction"]);
#if rp_shadowmap
{
#if lnx_shadowmap_atlas
Inc.bindShadowMapAtlas();
#else
Inc.bindShadowMap();
#end
}
#end
#if (rp_voxels != "Off")
path.bindTarget("voxelsOut", "voxels");
path.bindTarget("voxelsSDF", "voxelsSDF");
#end
path.drawMeshes("refraction");
path.setTarget("bufa");
path.bindTarget("lbuffer0", "tex");
path.bindTarget("refr", "tex1");
path.bindTarget("_main", "gbufferD");
path.bindTarget("gbufferD1", "gbufferD1");
path.bindTarget("lbuffer1", "gbuffer0");
path.bindTarget("gbuffer_refraction", "gbuffer_refraction");
path.drawShader("shader_datas/ssrefr_pass/ssrefr_pass");
path.setTarget("lbuffer0");
path.bindTarget("bufa", "tex");
path.drawShader("shader_datas/copy_pass/copy_pass");
}
}
#end
#if rp_bloom
{
inline Inc.drawBloom("lbuffer0", bloomDownsampler, bloomUpsampler);
}
#end
#if rp_chromatic_aberration
{
path.setTarget("bufa");
path.bindTarget("lbuffer0", "tex");
path.drawShader("shader_datas/chromatic_aberration_pass/chromatic_aberration_pass");
path.setTarget("lbuffer0");
path.bindTarget("bufa", "tex");
path.drawShader("shader_datas/copy_pass/copy_pass");
}
#end
#if rp_volumetriclight
{
path.setTarget("singlea");
@ -623,18 +731,6 @@ class RenderPathForward {
}
#end
#if rp_chromatic_aberration
{
path.setTarget("bufa");
path.bindTarget("lbuffer0", "tex");
path.drawShader("shader_datas/chromatic_aberration_pass/chromatic_aberration_pass");
path.setTarget("lbuffer0");
path.bindTarget("bufa", "tex");
path.drawShader("shader_datas/copy_pass/copy_pass");
}
#end
#if (rp_supersampling == 4)
var framebuffer = "buf";
#else
@ -654,6 +750,7 @@ class RenderPathForward {
}
#end
path.setTarget(target);
path.clearTarget(0x00000000);
#if rp_compositordepth
{
@ -701,6 +798,40 @@ class RenderPathForward {
}
#end
#if rp_fsr1
{
// FSR1 RCAS sharpening pass applied after AA, expects sRGB [0-1] input
#if ((rp_antialiasing == "SMAA") || (rp_antialiasing == "TAA"))
#if (rp_supersampling == 4)
var fsrSource = "buf";
var fsrDest = "buf";
#else
// SMAA outputs to framebuffer which needs an intermediate buffer
path.setTarget("bufb");
path.bindTarget(framebuffer != "" ? framebuffer : "buf", "tex");
path.drawShader("shader_datas/copy_pass/copy_pass");
var fsrSource = "bufb";
var fsrDest = "";
#end
#else
#if (rp_supersampling == 4)
var fsrSource = "buf";
var fsrDest = "buf";
#else
path.setTarget("bufa");
path.bindTarget(target != "" ? target : "buf", "tex");
path.drawShader("shader_datas/copy_pass/copy_pass");
var fsrSource = "bufa";
var fsrDest = "";
#end
#end
path.setTarget(fsrDest);
path.bindTarget(fsrSource, "tex");
path.drawShader("shader_datas/fsr1_rcas_pass/fsr1_rcas_pass");
}
#end
#if (rp_supersampling == 4)
{
var finalTarget = "";

View File

@ -41,7 +41,11 @@ class Starter {
try {
#end
kha.System.start({title: Main.projectName, width: c.window_w, height: c.window_h, window: {mode: windowMode, windowFeatures: windowFeatures}, framebuffer: {samplesPerPixel: c.window_msaa, verticalSync: c.window_vsync}}, function(window: kha.Window) {
kha.System.start({title: Main.projectName, width: c.window_w, height: c.window_h, window: {
#if lnx_render_viewport
visible: false,
#end
mode: windowMode, windowFeatures: windowFeatures}, framebuffer: {samplesPerPixel: c.window_msaa, verticalSync: c.window_vsync}}, function(window: kha.Window) {
iron.App.init(function() {
#if lnx_loadscreen
@ -92,6 +96,25 @@ class Starter {
}
#end
#if (js && lnx_jolt)
function loadLibJolt(name: String) {
kha.Assets.loadBlobFromPath(name, function(b: kha.Blob) {
js.Syntax.code("(1,eval)({0})", b.toString());
#if kha_krom
js.Syntax.code("Jolt({print:function(s){iron.log(s);},instantiateWasm:function(imports,successCallback) {
var wasmbin = Krom.loadBlob('jolt.wasm.wasm');
var module = new WebAssembly.Module(wasmbin);
var inst = new WebAssembly.Instance(module,imports);
successCallback(inst);
return inst.exports;
}}).then(function(m){ Jolt=m; tasks--; start();})");
#else
js.Syntax.code("Jolt({print:function(s){iron.log(s);},locateFile:function(f){return 'jolt.wasm.wasm';}}).then(function(m){ Jolt=m; tasks--; start();})");
#end
});
}
#end
#if (js && lnx_navigation)
function loadLib(name: String) {
kha.Assets.loadBlobFromPath(name, function(b: kha.Blob) {
@ -122,6 +145,11 @@ class Starter {
#end
#end
#if (js && lnx_jolt)
tasks++;
loadLibJolt("jolt.wasm.js");
#end
#if (js && lnx_navigation)
tasks++;
#if kha_krom

View File

@ -7,13 +7,11 @@ class KinematicCharacterController extends iron.Trait { public function new() {
#else
#if lnx_bullet
typedef KinematicCharacterController = leenkx.trait.physics.bullet.KinematicCharacterController;
#elseif lnx_jolt
typedef KinematicCharacterController = leenkx.trait.physics.jolt.KinematicCharacterController;
#else
typedef KinematicCharacterController = leenkx.trait.physics.oimo.KinematicCharacterController;
#end
#end

View File

@ -21,6 +21,8 @@ class PhysicsCache {
#if lnx_bullet
var rb = object.getTrait(leenkx.trait.physics.bullet.RigidBody);
#elseif lnx_jolt
var rb = object.getTrait(leenkx.trait.physics.jolt.RigidBody);
#else
var rb = object.getTrait(leenkx.trait.physics.oimo.RigidBody);
#end
@ -42,6 +44,9 @@ class PhysicsCache {
#if lnx_bullet
if (leenkx.trait.physics.bullet.PhysicsWorld.active == null) return null;
return leenkx.trait.physics.bullet.PhysicsWorld.active.getContacts(rb);
#elseif lnx_jolt
if (leenkx.trait.physics.jolt.PhysicsWorld.active == null) return null;
return leenkx.trait.physics.jolt.PhysicsWorld.active.getContacts(rb);
#else
if (leenkx.trait.physics.oimo.PhysicsWorld.active == null) return null;
return leenkx.trait.physics.oimo.PhysicsWorld.active.getContacts(rb);
@ -61,6 +66,9 @@ class PhysicsCache {
#if lnx_bullet
if (leenkx.trait.physics.bullet.PhysicsWorld.active == null) return null;
var contacts = leenkx.trait.physics.bullet.PhysicsWorld.active.getContacts(rb);
#elseif lnx_jolt
if (leenkx.trait.physics.jolt.PhysicsWorld.active == null) return null;
var contacts = leenkx.trait.physics.jolt.PhysicsWorld.active.getContacts(rb);
#else
if (leenkx.trait.physics.oimo.PhysicsWorld.active == null) return null;
var contacts = leenkx.trait.physics.oimo.PhysicsWorld.active.getContacts(rb);

View File

@ -10,6 +10,9 @@ class PhysicsConstraint extends iron.Trait { public function new() { super(); }
#if lnx_bullet
typedef PhysicsConstraint = leenkx.trait.physics.bullet.PhysicsConstraint;
typedef ConstraintAxis = leenkx.trait.physics.bullet.PhysicsConstraint.ConstraintAxis;
#elseif lnx_jolt
typedef PhysicsConstraint = leenkx.trait.physics.jolt.PhysicsConstraint;
typedef ConstraintAxis = leenkx.trait.physics.jolt.PhysicsConstraint.ConstraintType;
#else
typedef PhysicsConstraint = leenkx.trait.physics.oimo.PhysicsConstraint;
typedef ConstraintAxis = leenkx.trait.physics.oimo.PhysicsConstraint.ConstraintAxis;

View File

@ -7,13 +7,11 @@ class PhysicsHook extends iron.Trait { public function new() { super(); } }
#else
#if lnx_bullet
typedef PhysicsHook = leenkx.trait.physics.bullet.PhysicsHook;
#elseif lnx_jolt
typedef PhysicsHook = leenkx.trait.physics.jolt.PhysicsHook;
#else
typedef PhysicsHook = leenkx.trait.physics.oimo.PhysicsHook;
#end
#end

View File

@ -10,6 +10,9 @@ class PhysicsWorld extends iron.Trait { public function new() { super(); } }
#if lnx_bullet
typedef PhysicsWorld = leenkx.trait.physics.bullet.PhysicsWorld;
typedef Hit = leenkx.trait.physics.bullet.PhysicsWorld.Hit;
#elseif lnx_jolt
typedef PhysicsWorld = leenkx.trait.physics.jolt.PhysicsWorld;
typedef Hit = leenkx.trait.physics.jolt.PhysicsWorld.Hit;
#else
typedef PhysicsWorld = leenkx.trait.physics.oimo.PhysicsWorld;
typedef Hit = leenkx.trait.physics.oimo.PhysicsWorld.Hit;

View File

@ -8,15 +8,14 @@ class RigidBody extends iron.Trait { public function new() { super(); } }
#else
#if lnx_bullet
typedef RigidBody = leenkx.trait.physics.bullet.RigidBody;
typedef Shape = leenkx.trait.physics.bullet.RigidBody.Shape;
#elseif lnx_jolt
typedef RigidBody = leenkx.trait.physics.jolt.RigidBody;
typedef Shape = leenkx.trait.physics.jolt.RigidBody.Shape;
#else
typedef RigidBody = leenkx.trait.physics.oimo.RigidBody;
typedef Shape = leenkx.trait.physics.oimo.RigidBody.Shape;
#end
#end

View File

@ -1,5 +1,7 @@
package leenkx.trait.physics;
import iron.Trait;
#if (!lnx_physics_soft)
class SoftBody extends Trait { public function new() { super(); } }
@ -7,13 +9,11 @@ class SoftBody extends Trait { public function new() { super(); } }
#else
#if lnx_bullet
typedef SoftBody = leenkx.trait.physics.bullet.SoftBody;
#elseif lnx_jolt
typedef SoftBody = leenkx.trait.physics.jolt.SoftBody;
#else
typedef SoftBody = leenkx.trait.physics.oimo.SoftBody;
#end
#end

View File

@ -0,0 +1,376 @@
package leenkx.trait.physics.jolt;
#if lnx_jolt
import kha.FastFloat;
import kha.System;
import iron.math.Vec4;
#if lnx_ui
import leenkx.ui.Canvas;
#end
using StringTools;
enum abstract DebugDrawMode(Int) from Int to Int {
var NoDebug = 0;
var DrawWireframe = 1;
var DrawAabb = 2;
var DrawContactPoints = 4;
var DrawConstraints = 8;
var DrawConstraintLimits = 16;
var DrawRayCast = 32;
var DrawAll = 63;
@:op(A | B) static function or(lhs:DebugDrawMode, rhs:DebugDrawMode):DebugDrawMode;
@:op(A & B) static function and(lhs:DebugDrawMode, rhs:DebugDrawMode):DebugDrawMode;
}
class DebugDrawHelper {
static inline var contactPointSizePx = 4;
static inline var contactPointNormalColor = 0xffffffff;
final rayCastColor:Vec4 = new Vec4(0.0, 1.0, 0.0);
final rayCastHitColor:Vec4 = new Vec4(1.0, 0.0, 0.0);
final rayCastHitPointColor:Vec4 = new Vec4(1.0, 1.0, 0.0);
final wireframeColor:Vec4 = new Vec4(0.0, 1.0, 0.0);
final aabbColor:Vec4 = new Vec4(1.0, 1.0, 0.0);
final constraintColor:Vec4 = new Vec4(0.0, 0.5, 1.0);
final physicsWorld:PhysicsWorld;
final lines:Array<LineData> = [];
final texts:Array<TextData> = [];
var font:kha.Font = null;
var rayCasts:Array<TRayCastData> = [];
var debugDrawMode:DebugDrawMode = NoDebug;
public function new(physicsWorld:PhysicsWorld, debugDrawMode:DebugDrawMode) {
this.physicsWorld = physicsWorld;
this.debugDrawMode = debugDrawMode;
#if lnx_ui
iron.data.Data.getFont(Canvas.defaultFontName, function(defaultFont:kha.Font) {
font = defaultFont;
});
#end
iron.App.notifyOnRender2D(onRender);
if (debugDrawMode & DrawRayCast != 0) {
iron.App.notifyOnFixedUpdate(function() {
rayCasts.resize(0);
});
}
}
public function drawLine(fromX:Float, fromY:Float, fromZ:Float, toX:Float, toY:Float, toZ:Float, r:Float, g:Float, b:Float) {
final fromScreenSpace = worldToScreenFast(new Vec4(fromX, fromY, fromZ, 1.0));
final toScreenSpace = worldToScreenFast(new Vec4(toX, toY, toZ, 1.0));
if (fromScreenSpace.w == 1 || toScreenSpace.w == 1) {
lines.push({
fromX: fromScreenSpace.x,
fromY: fromScreenSpace.y,
toX: toScreenSpace.x,
toY: toScreenSpace.y,
color: kha.Color.fromFloats(r, g, b, 1.0)
});
}
}
public function drawLineVec(from:Vec4, to:Vec4, color:Vec4) {
drawLine(from.x, from.y, from.z, to.x, to.y, to.z, color.x, color.y, color.z);
}
public function drawContactPoint(pointX:Float, pointY:Float, pointZ:Float, normalX:Float, normalY:Float, normalZ:Float, distance:Float, r:Float, g:Float, b:Float) {
final contactPointScreenSpace = worldToScreenFast(new Vec4(pointX, pointY, pointZ, 1.0));
final toScreenSpace = worldToScreenFast(new Vec4(pointX + normalX * distance, pointY + normalY * distance, pointZ + normalZ * distance, 1.0));
if (contactPointScreenSpace.w == 1) {
final color = kha.Color.fromFloats(r, g, b, 1.0);
lines.push({
fromX: contactPointScreenSpace.x - contactPointSizePx,
fromY: contactPointScreenSpace.y - contactPointSizePx,
toX: contactPointScreenSpace.x + contactPointSizePx,
toY: contactPointScreenSpace.y + contactPointSizePx,
color: color
});
lines.push({
fromX: contactPointScreenSpace.x - contactPointSizePx,
fromY: contactPointScreenSpace.y + contactPointSizePx,
toX: contactPointScreenSpace.x + contactPointSizePx,
toY: contactPointScreenSpace.y - contactPointSizePx,
color: color
});
if (toScreenSpace.w == 1) {
lines.push({
fromX: contactPointScreenSpace.x,
fromY: contactPointScreenSpace.y,
toX: toScreenSpace.x,
toY: toScreenSpace.y,
color: contactPointNormalColor
});
}
}
}
public function rayCast(rayCastData:TRayCastData) {
rayCasts.push(rayCastData);
}
function drawRayCast(f:Vec4, t:Vec4, hit:Bool) {
final from = worldToScreenFast(f.clone());
final to = worldToScreenFast(t.clone());
var c:kha.Color;
if (from.w == 1 && to.w == 1) {
if (hit)
c = kha.Color.fromFloats(rayCastHitColor.x, rayCastHitColor.y, rayCastHitColor.z);
else
c = kha.Color.fromFloats(rayCastColor.x, rayCastColor.y, rayCastColor.z);
lines.push({
fromX: from.x,
fromY: from.y,
toX: to.x,
toY: to.y,
color: c
});
}
}
function drawHitPoint(hp:Vec4) {
final hitPoint = worldToScreenFast(hp.clone());
final c = kha.Color.fromFloats(rayCastHitPointColor.x, rayCastHitPointColor.y, rayCastHitPointColor.z);
if (hitPoint.w == 1) {
lines.push({
fromX: hitPoint.x - contactPointSizePx,
fromY: hitPoint.y - contactPointSizePx,
toX: hitPoint.x + contactPointSizePx,
toY: hitPoint.y + contactPointSizePx,
color: c
});
lines.push({
fromX: hitPoint.x - contactPointSizePx,
fromY: hitPoint.y + contactPointSizePx,
toX: hitPoint.x + contactPointSizePx,
toY: hitPoint.y - contactPointSizePx,
color: c
});
if (font != null) {
texts.push({
x: hitPoint.x,
y: hitPoint.y,
color: c,
text: 'RAYCAST HIT'
});
}
}
}
public function drawBox(center:Vec4, halfExtents:Vec4, color:Vec4) {
var c = center;
var h = halfExtents;
// Bottom face
drawLine(c.x - h.x, c.y - h.y, c.z - h.z, c.x + h.x, c.y - h.y, c.z - h.z, color.x, color.y, color.z);
drawLine(c.x + h.x, c.y - h.y, c.z - h.z, c.x + h.x, c.y + h.y, c.z - h.z, color.x, color.y, color.z);
drawLine(c.x + h.x, c.y + h.y, c.z - h.z, c.x - h.x, c.y + h.y, c.z - h.z, color.x, color.y, color.z);
drawLine(c.x - h.x, c.y + h.y, c.z - h.z, c.x - h.x, c.y - h.y, c.z - h.z, color.x, color.y, color.z);
// Top face
drawLine(c.x - h.x, c.y - h.y, c.z + h.z, c.x + h.x, c.y - h.y, c.z + h.z, color.x, color.y, color.z);
drawLine(c.x + h.x, c.y - h.y, c.z + h.z, c.x + h.x, c.y + h.y, c.z + h.z, color.x, color.y, color.z);
drawLine(c.x + h.x, c.y + h.y, c.z + h.z, c.x - h.x, c.y + h.y, c.z + h.z, color.x, color.y, color.z);
drawLine(c.x - h.x, c.y + h.y, c.z + h.z, c.x - h.x, c.y - h.y, c.z + h.z, color.x, color.y, color.z);
// Vertical edges
drawLine(c.x - h.x, c.y - h.y, c.z - h.z, c.x - h.x, c.y - h.y, c.z + h.z, color.x, color.y, color.z);
drawLine(c.x + h.x, c.y - h.y, c.z - h.z, c.x + h.x, c.y - h.y, c.z + h.z, color.x, color.y, color.z);
drawLine(c.x + h.x, c.y + h.y, c.z - h.z, c.x + h.x, c.y + h.y, c.z + h.z, color.x, color.y, color.z);
drawLine(c.x - h.x, c.y + h.y, c.z - h.z, c.x - h.x, c.y + h.y, c.z + h.z, color.x, color.y, color.z);
}
public function drawSphere(center:Vec4, radius:Float, color:Vec4) {
final segments = 16;
final step = Math.PI * 2 / segments;
// XY circle
for (i in 0...segments) {
var angle1 = i * step;
var angle2 = (i + 1) * step;
drawLine(
center.x + Math.cos(angle1) * radius, center.y + Math.sin(angle1) * radius, center.z,
center.x + Math.cos(angle2) * radius, center.y + Math.sin(angle2) * radius, center.z,
color.x, color.y, color.z
);
}
// XZ circle
for (i in 0...segments) {
var angle1 = i * step;
var angle2 = (i + 1) * step;
drawLine(
center.x + Math.cos(angle1) * radius, center.y, center.z + Math.sin(angle1) * radius,
center.x + Math.cos(angle2) * radius, center.y, center.z + Math.sin(angle2) * radius,
color.x, color.y, color.z
);
}
// YZ circle
for (i in 0...segments) {
var angle1 = i * step;
var angle2 = (i + 1) * step;
drawLine(
center.x, center.y + Math.cos(angle1) * radius, center.z + Math.sin(angle1) * radius,
center.x, center.y + Math.cos(angle2) * radius, center.z + Math.sin(angle2) * radius,
color.x, color.y, color.z
);
}
}
public function setDebugMode(debugDrawMode:DebugDrawMode) {
this.debugDrawMode = debugDrawMode;
}
public function getDebugMode():DebugDrawMode {
return debugDrawMode;
}
function drawBodyWireframe(body:RigidBody) {
if (body == null || body.object == null)
return;
var transform = body.object.transform;
var pos = transform.world.getLoc();
var dim = transform.dim;
var halfExtents = new Vec4(dim.x * 0.5, dim.y * 0.5, dim.z * 0.5);
drawBox(pos, halfExtents, wireframeColor);
}
function drawBodyAabb(body:RigidBody) {
if (body == null || body.object == null)
return;
var transform = body.object.transform;
var pos = transform.world.getLoc();
var dim = transform.dim;
var halfExtents = new Vec4(dim.x * 0.5, dim.y * 0.5, dim.z * 0.5);
drawBox(pos, halfExtents, aabbColor);
}
function drawConstraintDebug(constraint:PhysicsConstraint) {
if (constraint == null || constraint.body1 == null || constraint.body2 == null)
return;
var pos1 = constraint.body1.object.transform.world.getLoc();
var pos2 = constraint.body2.object.transform.world.getLoc();
drawLineVec(pos1, pos2, constraintColor);
}
function onRender(g:kha.graphics2.Graphics) {
if (getDebugMode() == NoDebug) {
return;
}
// Draw physics debug info
if (debugDrawMode & DrawWireframe != 0 || debugDrawMode & DrawAabb != 0) {
for (body in physicsWorld.rbMap) {
if (debugDrawMode & DrawWireframe != 0) {
drawBodyWireframe(body);
}
if (debugDrawMode & DrawAabb != 0) {
drawBodyAabb(body);
}
}
}
if (debugDrawMode & DrawConstraints != 0) {
for (constraint in physicsWorld.constraints) {
drawConstraintDebug(constraint);
}
}
g.opacity = 1.0;
for (line in lines) {
g.color = line.color;
g.drawLine(line.fromX, line.fromY, line.toX, line.toY, 1.0);
}
lines.resize(0);
if (font != null) {
g.font = font;
g.fontSize = 12;
for (text in texts) {
g.color = text.color;
g.drawString(text.text, text.x, text.y);
}
texts.resize(0);
}
if (debugDrawMode & DrawRayCast != 0) {
for (rayCastData in rayCasts) {
if (rayCastData.hasHit) {
drawRayCast(rayCastData.from, rayCastData.hitPoint, true);
drawHitPoint(rayCastData.hitPoint);
} else {
drawRayCast(rayCastData.from, rayCastData.to, false);
}
}
}
}
inline function worldToScreenFast(loc:Vec4):Vec4 {
final cam = iron.Scene.active.camera;
loc.w = 1.0;
loc.applyproj(cam.VP);
if (loc.z < -1 || loc.z > 1) {
loc.w = 0.0;
} else {
loc.x = (loc.x + 1) * 0.5 * System.windowWidth();
loc.y = (1 - loc.y) * 0.5 * System.windowHeight();
loc.w = 1.0;
}
return loc;
}
}
@:structInit
class LineData {
public var fromX:FastFloat;
public var fromY:FastFloat;
public var toX:FastFloat;
public var toY:FastFloat;
public var color:kha.Color;
}
@:structInit
class TextData {
public var x:FastFloat;
public var y:FastFloat;
public var color:kha.Color;
public var text:String;
}
@:structInit
typedef TRayCastData = {
var from:Vec4;
var to:Vec4;
var hasHit:Bool;
@:optional var hitPoint:Vec4;
@:optional var hitNormal:Vec4;
}
#end

View File

@ -0,0 +1,380 @@
package leenkx.trait.physics.jolt;
#if lnx_jolt
import iron.Trait;
import iron.math.Vec4;
import iron.math.Quat;
import iron.object.Transform;
import iron.object.MeshObject;
class KinematicCharacterController extends Trait {
var shape:ControllerShape;
public var physics:PhysicsWorld;
public var transform:Transform = null;
public var mass:Float;
public var friction:Float;
public var restitution:Float;
public var collisionMargin:Float;
public var animated:Bool;
public var group = 1;
var bodyScaleX:Float;
var bodyScaleY:Float;
var bodyScaleZ:Float;
var currentScaleX:Float;
var currentScaleY:Float;
var currentScaleZ:Float;
var jumpSpeed:Float;
public var body:jolt.Jt.Body;
public var bodyId:jolt.Jt.BodyID;
public var ready = false;
static var nextId = 0;
public var id = 0;
public var onReady:Void->Void = null;
static var nullvec = true;
static var vec1:jolt.Jt.Vec3;
static var quat1:jolt.Jt.Quat;
static var quat = new Quat();
var walkDirection:Vec4 = new Vec4();
var gravityEnabled = true;
var gravityFactor = 1.0;
public function new(mass = 1.0, shape = ControllerShape.Capsule, jumpSpeed = 8.0, friction = 0.5, restitution = 0.0,
collisionMargin = 0.0, animated = false, group = 1) {
super();
this.mass = mass;
this.jumpSpeed = jumpSpeed;
this.shape = shape;
this.friction = friction;
this.restitution = restitution;
this.collisionMargin = collisionMargin;
this.animated = animated;
this.group = group;
notifyOnAdd(init);
notifyOnLateUpdate(lateUpdate);
notifyOnRemove(removeFromWorld);
}
inline function withMargin(f:Float):Float {
return f + f * collisionMargin;
}
public function notifyOnReady(f:Void->Void) {
onReady = f;
if (ready)
onReady();
}
public function init() {
if (ready)
return;
transform = object.transform;
physics = PhysicsWorld.active;
if (physics == null) {
new PhysicsWorld();
physics = PhysicsWorld.active;
}
#if js
// Check if Jolt is initialized - defer if not
if (!physics.physicsReady) {
haxe.Timer.delay(init, 16);
return;
}
#end
ready = true;
if (nullvec) {
nullvec = false;
vec1 = new jolt.Jt.Vec3(0, 0, 0);
quat1 = new jolt.Jt.Quat(0, 0, 0, 1);
}
var joltShape:jolt.Jt.Shape = createShape();
var pos = transform.world.getLoc();
var rot = new iron.math.Quat();
rot.fromMat(transform.world);
// Jolt uses RVec3 for world positions
var jPos = new jolt.Jt.RVec3(pos.x, pos.y, pos.z);
var jRot = new jolt.Jt.Quat(rot.x, rot.y, rot.z, rot.w);
var settings = new jolt.Jt.BodyCreationSettings(joltShape, jPos, jRot, 1, 1);
// Use kinematic body for character controller
settings.mFriction = friction;
settings.mRestitution = restitution;
body = physics.bodyInterface.CreateBody(settings);
bodyId = body.GetID();
#if hl
settings.delete();
jPos.delete();
jRot.delete();
#end
physics.bodyInterface.AddBody(bodyId, 1);
bodyScaleX = currentScaleX = transform.scale.x;
bodyScaleY = currentScaleY = transform.scale.y;
bodyScaleZ = currentScaleZ = transform.scale.z;
id = nextId;
nextId++;
if (onReady != null)
onReady();
}
function createShape():jolt.Jt.Shape {
var t = transform;
if (shape == ControllerShape.Box) {
var halfExtent = new jolt.Jt.Vec3(withMargin(t.dim.x / 2), withMargin(t.dim.y / 2), withMargin(t.dim.z / 2));
return new jolt.Jt.BoxShape(halfExtent);
} else if (shape == ControllerShape.Sphere) {
var width = Math.max(t.dim.x, Math.max(t.dim.y, t.dim.z));
return new jolt.Jt.SphereShape(withMargin(width / 2));
} else if (shape == ControllerShape.Cylinder) {
var radius = Math.max(t.dim.x, t.dim.y) / 2;
var halfHeight = t.dim.z / 2;
return new jolt.Jt.CylinderShape(withMargin(halfHeight), withMargin(radius));
} else if (shape == ControllerShape.Capsule) {
var r = t.dim.x / 2;
var halfHeight = (t.dim.z - r * 2) / 2;
if (halfHeight < 0.01) halfHeight = 0.01;
return new jolt.Jt.CapsuleShape(withMargin(halfHeight), withMargin(r));
} else if (shape == ControllerShape.Cone) {
var radius = Math.max(t.dim.x, t.dim.y) / 2;
var halfHeight = t.dim.z / 2;
return new jolt.Jt.CylinderShape(withMargin(halfHeight), withMargin(radius));
}
// Default capsule
var r = t.dim.x / 2;
var halfHeight = (t.dim.z - r * 2) / 2;
if (halfHeight < 0.01) halfHeight = 0.01;
return new jolt.Jt.CapsuleShape(withMargin(halfHeight), withMargin(r));
}
function lateUpdate() {
if (!ready)
return;
if (object.animation != null || animated) {
syncTransform();
} else {
var p = physics.bodyInterface.GetPosition(bodyId);
var q = physics.bodyInterface.GetRotation(bodyId);
#if js
transform.loc.set(cast p.GetX(), cast p.GetY(), cast p.GetZ());
#else
transform.loc.set(p.GetX(), p.GetY(), p.GetZ());
#end
transform.rot.set(q.GetX(), q.GetY(), q.GetZ(), q.GetW());
#if hl
p.delete();
q.delete();
#end
if (object.parent != null) {
var ptransform = object.parent.transform;
transform.loc.x -= ptransform.worldx();
transform.loc.y -= ptransform.worldy();
transform.loc.z -= ptransform.worldz();
}
transform.buildMatrix();
}
}
public function canJump():Bool {
// Simple ground check - could be improved with raycast
return onGround();
}
public function onGround():Bool {
// Perform downward raycast to check ground
var pos = transform.world.getLoc();
var from = new Vec4(pos.x, pos.y, pos.z);
var to = new Vec4(pos.x, pos.y, pos.z - 0.2);
var hit = physics.rayCast(from, to);
return hit != null;
}
public function setJumpSpeed(jumpSpeed:Float) {
this.jumpSpeed = jumpSpeed;
}
public function setFallSpeed(fallSpeed:Float) {
// Jolt handles this through gravity
}
public function setMaxSlope(slopeRadians:Float) {
// Would need CharacterVirtual for proper slope handling
}
public function getMaxSlope():Float {
return Math.PI / 4; // 45 degrees default
}
public function setMaxJumpHeight(maxJumpHeight:Float) {
// Calculate jump speed from height: v = sqrt(2 * g * h)
var g = physics.getGravity().length();
jumpSpeed = Math.sqrt(2 * g * maxJumpHeight);
}
public function setWalkDirection(dir:Vec4) {
walkDirection.setFrom(dir);
var vel = new jolt.Jt.Vec3(dir.x, dir.y, dir.z);
physics.bodyInterface.SetLinearVelocity(bodyId, vel);
#if hl vel.delete(); #end
}
public function setUpInterpolate(value:Bool) {
// Not directly applicable in Jolt kinematic body
}
public function jump() {
var currentVel = physics.bodyInterface.GetLinearVelocity(bodyId);
var vel = new jolt.Jt.Vec3(currentVel.GetX(), currentVel.GetY(), jumpSpeed);
physics.bodyInterface.SetLinearVelocity(bodyId, vel);
#if hl currentVel.delete(); vel.delete(); #end
}
public function removeFromWorld() {
if (physics != null && ready) {
physics.bodyInterface.RemoveBody(bodyId);
physics.bodyInterface.DestroyBody(bodyId);
}
}
public function activate() {
physics.bodyInterface.ActivateBody(bodyId);
}
public function disableGravity() {
gravityEnabled = false;
physics.bodyInterface.SetGravityFactor(bodyId, 0.0);
}
public function enableGravity() {
gravityEnabled = true;
physics.bodyInterface.SetGravityFactor(bodyId, gravityFactor);
}
public function setGravity(f:Float) {
gravityFactor = f / 9.81; // Normalize
if (gravityEnabled) {
physics.bodyInterface.SetGravityFactor(bodyId, gravityFactor);
}
}
public function setActivationState(newState:Int) {
if (newState == ControllerActivationState.NoDeactivation) {
// Keep active - Jolt handles this differently
activate();
}
}
public function setFriction(f:Float) {
physics.bodyInterface.SetFriction(bodyId, f);
this.friction = f;
}
public function syncTransform() {
var t = transform;
t.buildMatrix();
var pos = t.world.getLoc();
var rot = new iron.math.Quat();
rot.fromMat(t.world);
// Jolt uses RVec3 for world positions
var p = new jolt.Jt.RVec3(pos.x, pos.y, pos.z);
var q = new jolt.Jt.Quat(rot.x, rot.y, rot.z, rot.w);
physics.bodyInterface.SetPosition(bodyId, p, 0);
physics.bodyInterface.SetRotation(bodyId, q, 0);
#if hl p.delete(); q.delete(); #end
activate();
}
public function getLinearVelocity():Vec4 {
var vel = physics.bodyInterface.GetLinearVelocity(bodyId);
var result = new Vec4(vel.GetX(), vel.GetY(), vel.GetZ());
#if hl vel.delete(); #end
return result;
}
public function setLinearVelocity(velocity:Vec4) {
var vel = new jolt.Jt.Vec3(velocity.x, velocity.y, velocity.z);
physics.bodyInterface.SetLinearVelocity(bodyId, vel);
#if hl vel.delete(); #end
}
public function getPosition():Vec4 {
var pos = physics.bodyInterface.GetPosition(bodyId);
var result = new Vec4(pos.GetX(), pos.GetY(), pos.GetZ());
#if hl pos.delete(); #end
return result;
}
public function setPosition(position:Vec4) {
var p = new jolt.Jt.RVec3(position.x, position.y, position.z);
physics.bodyInterface.SetPosition(bodyId, p, 0);
#if hl p.delete(); #end
}
public function warp(position:Vec4) {
setPosition(position);
var zeroVel = new jolt.Jt.Vec3(0, 0, 0);
physics.bodyInterface.SetLinearVelocity(bodyId, zeroVel);
#if hl zeroVel.delete(); #end
}
public function move(direction:Vec4, speed:Float) {
var moveVel = new Vec4(direction.x * speed, direction.y * speed, direction.z * speed);
// Preserve vertical velocity for jumping/falling
var currentVel = physics.bodyInterface.GetLinearVelocity(bodyId);
var vel = new jolt.Jt.Vec3(moveVel.x, moveVel.y, currentVel.GetZ());
physics.bodyInterface.SetLinearVelocity(bodyId, vel);
#if hl currentVel.delete(); vel.delete(); #end
}
public function getGroundState():Int {
if (onGround()) {
return 0; // OnGround
}
return 3; // InAir
}
public function isSupported():Bool {
return onGround();
}
}
@:enum abstract ControllerShape(Int) from Int to Int {
var Box = 0;
var Sphere = 1;
var ConvexHull = 2;
var Cone = 3;
var Cylinder = 4;
var Capsule = 5;
}
@:enum abstract ControllerActivationState(Int) from Int to Int {
var Active = 1;
var NoDeactivation = 4;
var NoSimulation = 5;
}
#end

View File

@ -0,0 +1,405 @@
package leenkx.trait.physics.jolt;
#if lnx_jolt
import iron.Trait;
import iron.math.Vec4;
import iron.math.Quat;
import iron.math.Mat4;
import iron.object.Object;
@:enum abstract ConstraintType(Int) from Int to Int {
var Fixed = 0;
var Point = 1;
var Hinge = 2;
var Slider = 3;
var Piston = 4;
var Generic = 5;
var GenericSpring = 6;
var Distance = 7;
}
class PhysicsConstraint extends Trait {
public var id:Int;
public var physics:PhysicsWorld;
public var body1:RigidBody;
public var body2:RigidBody;
public var type:ConstraintType;
public var con:jolt.Jt.Constraint;
public var conReady:Bool = false;
public var disableCollisions:Bool;
var body1Obj:Object;
var body2Obj:Object;
var limits:Array<Float>;
var breakingThreshold:Float;
static var nextId = 0;
public function new(body1:Object, body2:Object, type:ConstraintType, disableCollisions:Bool = false, breakingThreshold:Float = 0.0,
limits:Array<Float> = null) {
super();
this.type = type;
this.disableCollisions = disableCollisions;
this.breakingThreshold = breakingThreshold;
this.limits = limits;
this.id = nextId++;
this.body1Obj = body1;
this.body2Obj = body2;
notifyOnInit(function() {
this.body1 = body1.getTrait(RigidBody);
this.body2 = body2.getTrait(RigidBody);
tryInit();
});
notifyOnRemove(removeFromWorld);
}
function tryInit() {
if (this.body1 != null && this.body1.ready && this.body2 != null && this.body2.ready) {
init();
} else if (this.body1 != null || this.body2 != null) {
// Bodies exist but not ready yet, retry next frame
iron.App.notifyOnUpdate(retryInit);
}
}
function retryInit() {
iron.App.removeUpdate(retryInit);
tryInit();
}
function init() {
physics = PhysicsWorld.active;
// Compute constraint frames in each body's local space (exactly matches Bullet approach)
var t = object.transform; // pivot object
var t1 = body1Obj.transform; // body1 object
var t2 = body2Obj.transform; // body2 object
// Frame In A: pivot transform in body1's local space
var frameT = t.world.clone();
var frameInA = t1.world.clone();
frameInA.getInverse(frameInA);
frameT.multmat(frameInA);
frameInA = frameT.clone();
// Frame In B: pivot transform in body2's local space
frameT = t.world.clone();
var frameInB = t2.world.clone();
frameInB.getInverse(frameInB);
frameT.multmat(frameInB);
frameInB = frameT.clone();
// Decompose frames to get local positions and orientations
var locA = new Vec4();
var rotA = new Quat();
var sclA = new Vec4();
frameInA.decompose(locA, rotA, sclA);
var locB = new Vec4();
var rotB = new Quat();
var sclB = new Vec4();
frameInB.decompose(locB, rotB, sclB);
// Extract local axes from each frame (normalized to remove scale)
var rightA = frameInA.right().normalize();
var upA = frameInA.up().normalize();
var rightB = frameInB.right().normalize();
var upB = frameInB.up().normalize();
// Create Jolt vectors for body1 local frame
var jPt1 = new jolt.Jt.RVec3(locA.x, locA.y, locA.z);
var jAxX1 = new jolt.Jt.Vec3(rightA.x, rightA.y, rightA.z);
var jAxY1 = new jolt.Jt.Vec3(upA.x, upA.y, upA.z);
// Create Jolt vectors for body2 local frame
var jPt2 = new jolt.Jt.RVec3(locB.x, locB.y, locB.z);
var jAxX2 = new jolt.Jt.Vec3(rightB.x, rightB.y, rightB.z);
var jAxY2 = new jolt.Jt.Vec3(upB.x, upB.y, upB.z);
switch (type) {
case Fixed:
var settings = new jolt.Jt.FixedConstraintSettings();
settings.mSpace = 0; // LocalToBodyCOM
settings.mAutoDetectPoint = false;
settings.mPoint1 = jPt1;
settings.mPoint2 = jPt2;
settings.mAxisX1 = jAxX1;
settings.mAxisY1 = jAxY1;
settings.mAxisX2 = jAxX2;
settings.mAxisY2 = jAxY2;
con = settings.Create(body1.body, body2.body);
#if hl settings.delete(); #end
case Point:
var settings = new jolt.Jt.PointConstraintSettings();
settings.mSpace = 0;
settings.mPoint1 = jPt1;
settings.mPoint2 = jPt2;
con = settings.Create(body1.body, body2.body);
#if hl settings.delete(); #end
case Hinge:
var settings = new jolt.Jt.HingeConstraintSettings();
settings.mSpace = 0;
settings.mPoint1 = jPt1;
settings.mPoint2 = jPt2;
settings.mHingeAxis1 = jAxY1;
settings.mHingeAxis2 = jAxY2;
settings.mNormalAxis1 = jAxX1;
settings.mNormalAxis2 = jAxX2;
if (limits != null && limits.length >= 3 && limits[0] != 0) {
settings.mLimitsMin = limits[1];
settings.mLimitsMax = limits[2];
}
con = settings.Create(body1.body, body2.body);
#if hl settings.delete(); #end
case Slider:
var settings = new jolt.Jt.SliderConstraintSettings();
settings.mSpace = 0;
settings.mAutoDetectPoint = false;
settings.mPoint1 = jPt1;
settings.mPoint2 = jPt2;
settings.mSliderAxis1 = jAxX1;
settings.mSliderAxis2 = jAxX2;
settings.mNormalAxis1 = jAxY1;
settings.mNormalAxis2 = jAxY2;
if (limits != null && limits.length >= 3 && limits[0] != 0) {
settings.mLimitsMin = limits[1];
settings.mLimitsMax = limits[2];
}
con = settings.Create(body1.body, body2.body);
#if hl settings.delete(); #end
case Distance:
var settings = new jolt.Jt.DistanceConstraintSettings();
settings.mSpace = 0;
settings.mPoint1 = jPt1;
settings.mPoint2 = jPt2;
if (limits != null && limits.length >= 2) {
settings.mMinDistance = limits[0];
settings.mMaxDistance = limits[1];
}
con = settings.Create(body1.body, body2.body);
#if hl settings.delete(); #end
case Piston:
var settings = new jolt.Jt.SliderConstraintSettings();
settings.mSpace = 0;
settings.mAutoDetectPoint = false;
settings.mPoint1 = jPt1;
settings.mPoint2 = jPt2;
settings.mSliderAxis1 = jAxY1;
settings.mSliderAxis2 = jAxY2;
settings.mNormalAxis1 = jAxX1;
settings.mNormalAxis2 = jAxX2;
if (limits != null && limits.length >= 3 && limits[0] != 0) {
settings.mLimitsMin = limits[1];
settings.mLimitsMax = limits[2];
}
con = settings.Create(body1.body, body2.body);
#if hl settings.delete(); #end
case Generic:
var settings = new jolt.Jt.SixDOFConstraintSettings();
settings.mSpace = 0;
settings.mPosition1 = jPt1;
settings.mPosition2 = jPt2;
settings.mAxisX1 = jAxX1;
settings.mAxisY1 = jAxY1;
settings.mAxisX2 = jAxX2;
settings.mAxisY2 = jAxY2;
if (limits != null) {
applySixDOFLimits(settings);
} else {
for (i in 0...6) settings.MakeFreeAxis(i);
}
con = settings.Create(body1.body, body2.body);
#if hl settings.delete(); #end
case GenericSpring:
var settings = new jolt.Jt.SixDOFConstraintSettings();
settings.mSpace = 0;
settings.mPosition1 = jPt1;
settings.mPosition2 = jPt2;
settings.mAxisX1 = jAxX1;
settings.mAxisY1 = jAxY1;
settings.mAxisX2 = jAxX2;
settings.mAxisY2 = jAxY2;
if (limits != null) {
applySixDOFLimits(settings);
} else {
for (i in 0...6) settings.MakeFreeAxis(i);
}
con = settings.Create(body1.body, body2.body);
#if hl settings.delete(); #end
default:
var settings = new jolt.Jt.FixedConstraintSettings();
settings.mSpace = 0;
settings.mAutoDetectPoint = false;
settings.mPoint1 = jPt1;
settings.mPoint2 = jPt2;
settings.mAxisX1 = jAxX1;
settings.mAxisY1 = jAxY1;
settings.mAxisX2 = jAxX2;
settings.mAxisY2 = jAxY2;
con = settings.Create(body1.body, body2.body);
#if hl settings.delete(); #end
}
// Clean up temporary Jolt objects
#if hl
jPt1.delete();
jPt2.delete();
jAxX1.delete();
jAxY1.delete();
jAxX2.delete();
jAxY2.delete();
#end
conReady = true;
physics.addPhysicsConstraint(this);
}
function applySixDOFLimits(settings:jolt.Jt.SixDOFConstraintSettings) {
// Linear X (limits[0..2]): limits[0]=enabled, limits[1]=lower, limits[2]=upper
if (limits.length > 2 && limits[0] != 0) {
if (limits[1] > limits[2])
settings.MakeFreeAxis(0);
else
settings.SetLimitedAxis(0, limits[1], limits[2]);
} else {
settings.MakeFreeAxis(0);
}
// Linear Y (limits[3..5])
if (limits.length > 5 && limits[3] != 0) {
if (limits[4] > limits[5])
settings.MakeFreeAxis(1);
else
settings.SetLimitedAxis(1, limits[4], limits[5]);
} else {
settings.MakeFreeAxis(1);
}
// Linear Z (limits[6..8])
if (limits.length > 8 && limits[6] != 0) {
if (limits[7] > limits[8])
settings.MakeFreeAxis(2);
else
settings.SetLimitedAxis(2, limits[7], limits[8]);
} else {
settings.MakeFreeAxis(2);
}
// Angular X (limits[9..11])
if (limits.length > 11 && limits[9] != 0) {
if (limits[10] > limits[11])
settings.MakeFreeAxis(3);
else
settings.SetLimitedAxis(3, limits[10], limits[11]);
} else {
settings.MakeFreeAxis(3);
}
// Angular Y (limits[12..14])
if (limits.length > 14 && limits[12] != 0) {
if (limits[13] > limits[14])
settings.MakeFreeAxis(4);
else
settings.SetLimitedAxis(4, limits[13], limits[14]);
} else {
settings.MakeFreeAxis(4);
}
// Angular Z (limits[15..17])
if (limits.length > 17 && limits[15] != 0) {
if (limits[16] > limits[17])
settings.MakeFreeAxis(5);
else
settings.SetLimitedAxis(5, limits[16], limits[17]);
} else {
settings.MakeFreeAxis(5);
}
}
function removeFromWorld() {
if (physics != null) {
physics.removePhysicsConstraint(this);
}
}
public function delete() {
conReady = false;
}
public function setEnabled(enabled:Bool) {
if (conReady) {
con.SetEnabled(enabled);
}
}
public function isEnabled():Bool {
return conReady ? con.GetEnabled() : false;
}
// Bullet-compatible limit setting methods
public function setHingeConstraintLimits(angLimit:Bool, lowerAngLimit:Float, upperAngLimit:Float) {
if (limits == null) limits = [for (i in 0...36) 0.0];
limits[0] = angLimit ? 1 : 0;
limits[1] = lowerAngLimit * (Math.PI / 180);
limits[2] = upperAngLimit * (Math.PI / 180);
}
public function setSliderConstraintLimits(linLimit:Bool, lowerLinLimit:Float, upperLinLimit:Float) {
if (limits == null) limits = [for (i in 0...36) 0.0];
limits[0] = linLimit ? 1 : 0;
limits[1] = lowerLinLimit;
limits[2] = upperLinLimit;
}
public function setPistonConstraintLimits(linLimit:Bool, lowerLinLimit:Float, upperLinLimit:Float, angLimit:Bool, lowerAngLimit:Float, upperAngLimit:Float) {
if (limits == null) limits = [for (i in 0...36) 0.0];
limits[0] = linLimit ? 1 : 0;
limits[1] = lowerLinLimit;
limits[2] = upperLinLimit;
limits[3] = angLimit ? 1 : 0;
limits[4] = lowerAngLimit * (Math.PI / 180);
limits[5] = upperAngLimit * (Math.PI / 180);
}
public function setGenericConstraintLimits(setLimit:Bool = false, lowerLimit:Float = 1.0, upperLimit:Float = -1.0, axis:ConstraintAxis = X, isAngular:Bool = false) {
if (limits == null) limits = [for (i in 0...36) 0.0];
var i = switch (axis) {
case X: 0;
case Y: 3;
case Z: 6;
};
var j = isAngular ? 9 : 0;
var radian = isAngular ? (Math.PI / 180) : 1;
limits[i + j] = setLimit ? 1 : 0;
limits[i + j + 1] = lowerLimit * radian;
limits[i + j + 2] = upperLimit * radian;
}
public function setSpringParams(setSpring:Bool = false, stiffness:Float = 10.0, damping:Float = 0.5, axis:ConstraintAxis = X, isAngular:Bool = false) {
if (limits == null) limits = [for (i in 0...36) 0.0];
var i = switch (axis) {
case X: 18;
case Y: 21;
case Z: 24;
};
var j = isAngular ? 9 : 0;
limits[i + j] = setSpring ? 1 : 0;
limits[i + j + 1] = stiffness;
limits[i + j + 2] = damping;
}
}
@:enum abstract ConstraintAxis(Int) from Int to Int {
var X = 0;
var Y = 1;
var Z = 2;
}
#end

View File

@ -0,0 +1,122 @@
package leenkx.trait.physics.jolt;
#if lnx_jolt
import iron.math.Vec4;
import iron.math.Mat4;
import iron.math.Quat;
import iron.Trait;
import iron.object.Object;
import iron.object.MeshObject;
import iron.object.Transform;
import iron.data.MeshData;
import iron.data.SceneFormat;
class PhysicsHook extends Trait {
var target:Object;
var targetName:String;
var targetTransform:Transform;
var verts:Array<Float>;
var hookBodyId:jolt.Jt.BodyID = null;
var constraintId:Int = -1;
static var nullvec = true;
static var vec1:jolt.Jt.Vec3;
static var quat1:jolt.Jt.Quat;
static var quat = new Quat();
public function new(targetName:String, verts:Array<Float>) {
super();
this.targetName = targetName;
this.verts = verts;
iron.Scene.active.notifyOnInit(function() {
notifyOnInit(init);
notifyOnUpdate(update);
});
}
function init() {
if (nullvec) {
nullvec = false;
vec1 = new jolt.Jt.Vec3(0, 0, 0);
quat1 = new jolt.Jt.Quat(0, 0, 0, 1);
}
target = targetName != "" ? iron.Scene.active.getChild(targetName) : null;
targetTransform = target != null ? target.transform : iron.Scene.global.transform;
var physics = PhysicsWorld.active;
if (physics == null)
return;
#if lnx_physics_soft
var sb:SoftBody = object.getTrait(SoftBody);
if (sb != null && sb.ready) {
// For soft body hooks, pin vertices near the target
var numVerts = Std.int(verts.length / 3);
for (j in 0...numVerts) {
var x = verts[j * 3] + sb.vertOffsetX + sb.object.transform.loc.x;
var y = verts[j * 3 + 1] + sb.vertOffsetY + sb.object.transform.loc.y;
var z = verts[j * 3 + 2] + sb.vertOffsetZ + sb.object.transform.loc.z;
// Find and pin matching vertices
for (i in 0...@:privateAccess sb.particles.length) {
var p = @:privateAccess sb.particles[i];
if (Math.abs(p.position.x - x) < 0.01 && Math.abs(p.position.y - y) < 0.01 && Math.abs(p.position.z - z) < 0.01) {
sb.pinVertex(i);
}
}
}
return;
}
#end
// Rigid body hook using fixed constraint
var rb1:RigidBody = object.getTrait(RigidBody);
if (rb1 != null && rb1.ready) {
var settings = new jolt.Jt.FixedConstraintSettings();
settings.mAutoDetectPoint = true;
var constraint = settings.Create(rb1.body, rb1.body);
physics.physicsSystem.AddConstraint(constraint);
return;
}
// Rigid body or soft body not initialized yet
notifyOnInit(init);
}
function update() {
#if lnx_physics_soft
// Soft body hook - update pinned vertex positions to follow target
var sb:SoftBody = object.getTrait(SoftBody);
if (sb != null && sb.ready) {
var numVerts = Std.int(verts.length / 3);
for (j in 0...numVerts) {
var x = verts[j * 3] + sb.vertOffsetX + sb.object.transform.loc.x;
var y = verts[j * 3 + 1] + sb.vertOffsetY + sb.object.transform.loc.y;
var z = verts[j * 3 + 2] + sb.vertOffsetZ + sb.object.transform.loc.z;
// Update pinned vertex positions to target
for (i in 0...@:privateAccess sb.particles.length) {
var p = @:privateAccess sb.particles[i];
if (p.pinned) {
// Move pinned vertex with target
var dx = targetTransform.worldx() - targetTransform.loc.x;
var dy = targetTransform.worldy() - targetTransform.loc.y;
var dz = targetTransform.worldz() - targetTransform.loc.z;
p.position.x = x + dx;
p.position.y = y + dy;
p.position.z = z + dz;
}
}
}
}
#end
}
}
#end

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