Compare commits

112 Commits

Author SHA1 Message Date
3bee97a560 Update leenkx/blender/lnx/material/cycles.py 2025-11-14 17:41:39 +00:00
4f4f28d62f Merge pull request 'main' (#111) from Onek8/LNXSDK:main into main
Reviewed-on: LeenkxTeam/LNXSDK#111
2025-11-06 16:32:50 +00:00
7076fb6b7e Update leenkx/blender/lnx/material/cycles.py 2025-11-06 16:29:46 +00:00
b72a22b5e9 Update leenkx/blender/lnx/props_ui.py 2025-11-06 16:17:18 +00:00
b265ab863c Update leenkx/blender/lnx/props.py 2025-11-06 16:13:39 +00:00
48f5575e4e Update leenkx/Sources/leenkx/logicnode/OnContactNode.hx 2025-10-03 07:56:12 +00:00
f2c4be6336 Update leenkx/Sources/leenkx/logicnode/HasContactNode.hx 2025-10-03 07:55:46 +00:00
2ddc938db8 Update leenkx/Sources/leenkx/logicnode/AnyContactNode.hx 2025-10-03 07:55:14 +00:00
5eb735ada2 Update leenkx/Sources/leenkx/trait/physics/bullet/PhysicsWorld.hx 2025-10-03 07:52:01 +00:00
9894cc20f2 Update leenkx/Sources/leenkx/trait/physics/PhysicsWorld.hx 2025-10-03 07:51:32 +00:00
dbe6d0829a Add leenkx/Sources/leenkx/trait/physics/PhysicsCache.hx 2025-10-03 07:51:07 +00:00
6f383e2ab2 Update leenkx/Sources/leenkx/trait/physics/PhysicsWorld.hx 2025-10-03 05:38:54 +00:00
5c2d29d7ce Update leenkx/Sources/leenkx/trait/physics/bullet/PhysicsWorld.hx 2025-10-03 05:37:50 +00:00
28579e14d7 Update leenkx/Sources/leenkx/logicnode/AnyContactNode.hx 2025-10-03 05:37:06 +00:00
2ec6f43cc5 Update leenkx/Sources/leenkx/logicnode/OnContactNode.hx 2025-10-03 05:36:14 +00:00
027021815a Update leenkx/Sources/leenkx/logicnode/HasContactNode.hx 2025-10-03 05:35:48 +00:00
b9b387803f Add leenkx/blender/lnx/logicnode/physics/LN_any_contact.py 2025-10-03 05:06:23 +00:00
e05d9d0237 Update leenkx/Sources/leenkx/logicnode/HasContactNode.hx 2025-10-03 05:04:48 +00:00
c908e6cad2 Update leenkx/Sources/leenkx/logicnode/OnContactNode.hx 2025-10-03 05:04:18 +00:00
506a0a0245 Add leenkx/Sources/leenkx/logicnode/AnyContactNode.hx 2025-10-03 05:03:29 +00:00
5cf33724e4 Update leenkx/Sources/leenkx/trait/physics/bullet/PhysicsWorld.hx 2025-10-03 05:02:28 +00:00
ac5aa3d19c Merge pull request 'Hashlink fix' (#110) from Onek8/LNXSDK:main into main
Reviewed-on: LeenkxTeam/LNXSDK#110
2025-10-03 03:21:56 +00:00
0c534ee632 Update leenkx/Sources/iron/object/ParticleSystem.hx 2025-10-01 01:42:51 +00:00
e3e7855d26 Merge pull request 'main' (#109) from Onek8/LNXSDK:main into main
Reviewed-on: LeenkxTeam/LNXSDK#109
2025-09-30 05:59:23 +00:00
f7917974f8 Update leenkx/Sources/iron/object/ObjectAnimation.hx 2025-09-30 05:52:44 +00:00
fa2d8f05d5 Update leenkx/Sources/iron/object/ObjectAnimation.hx 2025-09-30 05:35:50 +00:00
73fcb55acc Update leenkx/blender/lnx/props_traits.py 2025-09-29 05:28:13 +00:00
c24baa3364 Update leenkx/blender/lnx/props_traits_props.py 2025-09-29 05:27:43 +00:00
4517c4863f Merge pull request 'Downward support to 2.8 LTS!!' (#108) from Onek8/LNXSDK:main into main
Reviewed-on: LeenkxTeam/LNXSDK#108
2025-09-28 20:02:58 +00:00
1299306e09 Update leenkx.py 2025-09-28 20:01:00 +00:00
f97d8fd846 Blender 2.8 - 4.5 Support 2025-09-28 12:44:04 -07:00
8f8d4b1376 Update leenkx/blender/lnx/props_traits.py 2025-09-28 00:09:57 +00:00
a926fa8dbb Update leenkx/blender/lnx/nodes_logic.py 2025-09-27 03:03:08 +00:00
6c3efa6c83 Update leenkx/blender/lnx/props_ui.py 2025-09-24 01:54:38 +00:00
21afad6d09 Update leenkx/blender/lnx/exporter.py 2025-09-24 01:53:43 +00:00
04c6983a09 Update leenkx/Sources/iron/data/SceneFormat.hx 2025-09-24 01:52:47 +00:00
45966ef0bb Update leenkx/blender/lnx/props.py 2025-09-24 01:51:11 +00:00
a72edc6203 Update leenkx/Sources/iron/object/ParticleSystem.hx 2025-09-24 01:50:03 +00:00
6af1ef2df1 Update leenkx/blender/lnx/props_ui.py 2025-09-24 01:33:47 +00:00
46e3047877 Update leenkx/blender/lnx/props.py 2025-09-23 19:57:53 +00:00
de74af215a Merge pull request 'main' (#107) from Onek8/LNXSDK:main into main
Reviewed-on: LeenkxTeam/LNXSDK#107
2025-09-23 17:54:11 +00:00
b6e96553c2 Update leenkx/blender/lnx/lightmapper/utility/build.py 2025-09-19 22:52:01 +00:00
58e009f709 Terrain Generation fix 2025-09-19 21:17:58 +00:00
e88f101ca6 t3du - Particle info random 2025-09-19 19:40:49 +00:00
d28d59b9e6 t3du - Particle info random 2025-09-19 19:38:12 +00:00
a4398c7279 t3du - Particle info random 2025-09-19 19:31:45 +00:00
abedfd799e t3du - Fix World Errors 2025-09-19 19:28:54 +00:00
4520422f6b t3du - Fix World Errors 2025-09-19 19:27:02 +00:00
88418c06c3 t3du - Fix World Errors 2025-09-19 19:25:30 +00:00
aedc2783ab t3du - Labels for finding nodes 2025-09-19 19:22:45 +00:00
1505414c4c t3du - Labels for finding nodes 2025-09-19 19:19:25 +00:00
fa818602c4 t3du - Labels for finding nodes 2025-09-19 19:18:05 +00:00
79dc458671 t3du - Labels for finding nodes 2025-09-19 19:15:41 +00:00
8e635fb1e9 t3du - Labels for finding nodes 2025-09-19 19:11:47 +00:00
4c2e6ab26a t3du - Probabilistic Index Node 2025-09-19 19:09:18 +00:00
2371e3777e t3du - Probabilistic Index Node 2025-09-19 19:08:03 +00:00
b458b77e5c moisesjpelaez - Include external blend files on build 2025-09-19 19:04:43 +00:00
9b76f8cca9 moisesjpelaez - Include external blend files on build 2025-09-19 19:03:22 +00:00
5f2acb209e moisesjpelaez - Include external blend files on build 2025-09-19 19:00:50 +00:00
6fc446e7a9 moisesjpelaez - General Fixes 2025-09-19 18:54:44 +00:00
71e57026e1 moisesjpelaez - General Fixes 2025-09-19 18:53:25 +00:00
5288a98440 moisesjpelaez - General Fixes 2025-09-19 18:49:09 +00:00
35e346be39 moisesjpelaez - General Fixes 2025-09-19 18:39:54 +00:00
843ef0b058 moisesjpelaez - General Fixes 2025-09-19 18:39:14 +00:00
177890bf39 moisesjpelaez - General Fixes 2025-09-19 18:37:01 +00:00
9ac37e6dc7 moisesjpelaez - General Fixes 2025-09-19 18:34:42 +00:00
e697437778 moisesjpelaez - General Fixes 2025-09-19 18:33:44 +00:00
c94fc0fd97 moisesjpelaez - General Fixes 2025-09-19 18:29:52 +00:00
cd0a6f6788 Update leenkx/Sources/iron/data/SceneFormat.hx 2025-09-19 18:28:19 +00:00
4400e0e9c8 moisesjpelaez - General Fixes 2025-09-19 18:27:22 +00:00
20cf07cfc3 moisesjpelaez - General Fixes 2025-09-19 18:25:54 +00:00
1939f19c05 moisesjpelaez - General Fixes 2025-09-19 18:24:19 +00:00
0d2b152ccb moisesjpelaez - General Fixes 2025-09-19 18:15:23 +00:00
7f58e0fc85 moisesjpelaez - General Fixes 2025-09-19 18:13:00 +00:00
0e4a6575c7 moisesjpelaez - General Material Updates 2025-09-19 18:09:04 +00:00
024676f43a moisesjpelaez - General Material Updates 2025-09-19 17:43:54 +00:00
8fe758862c moisesjpelaez - General Material Updates 2025-09-19 17:35:59 +00:00
1f3d1b47ae moisesjpelaez - General Material Updates 2025-09-19 17:34:27 +00:00
f659a3c2be moisesjpelaez - General Material Updates 2025-09-19 17:32:38 +00:00
6eeb9017d4 moisesjpelaez - General Material Updates 2025-09-19 17:30:42 +00:00
afe89c3834 Update leenkx/Sources/iron/data/ShaderData.hx 2025-09-19 17:27:14 +00:00
8b695f72bb moisesjpelaez - General Material Updates 2025-09-19 17:25:03 +00:00
3d99fa60c0 moisesjpelaez - General Material Updates 2025-09-19 17:23:42 +00:00
43be7729ba moisesjpelaez - Tween var 2025-09-19 17:17:41 +00:00
de0b1075c2 moisesjpelaez - Time Fix 2025-09-19 17:13:16 +00:00
c7aba23fa4 t3du - Fix DOF condition 2025-09-19 17:08:21 +00:00
881f3267cc t3du - Fix DOF condition 2025-09-19 17:06:10 +00:00
19b79d61c7 ObiNoWanKenobi - FirstPersonController Changes 2025-09-19 17:03:20 +00:00
fcbab54a0c moisesjpelaez - General Fixes 2025-09-19 16:57:49 +00:00
8fd05d5514 Update leenkx/Sources/leenkx/renderpath/RenderPathForward.hx 2025-08-28 19:21:48 +00:00
ad4013ed75 Update leenkx/Sources/leenkx/renderpath/RenderPathForward.hx 2025-08-28 19:11:31 +00:00
8ac567b57b Merge pull request 'Update leenkx/Shaders/std/conetrace.glsl' (#104) from Onek8/LNXSDK:main into main
Reviewed-on: LeenkxTeam/LNXSDK#104
2025-08-14 23:01:04 +00:00
43b7ae7060 Update leenkx/Shaders/std/conetrace.glsl 2025-08-14 22:58:57 +00:00
29e9e71a6a Merge pull request 'main' (#103) from Onek8/LNXSDK:main into main
Reviewed-on: LeenkxTeam/LNXSDK#103
2025-08-14 21:29:54 +00:00
bfb85b0a3b Update leenkx/Sources/iron/Scene.hx 2025-08-14 20:29:28 +00:00
ef99b800e0 Update leenkx/Sources/iron/App.hx 2025-08-14 20:27:20 +00:00
7cca955fc5 Update leenkx/Sources/iron/App.hx 2025-08-14 20:26:33 +00:00
7e7bbd5eae merge upstream 2025-08-14 20:24:23 +00:00
c31b2a18ad Update leenkx/blender/lnx/logicnode/draw/LN_draw_string.py 2025-08-14 19:03:28 +00:00
fb47bf2564 Update leenkx/blender/lnx/logicnode/draw/LN_draw_Text_Area_string.py 2025-08-14 19:01:59 +00:00
7ae6750620 Update leenkx/blender/lnx/logicnode/camera/LN_set_camera_start_end.py 2025-08-14 19:00:58 +00:00
5b87010f76 Update leenkx/Sources/leenkx/trait/internal/DebugConsole.hx 2025-08-14 18:58:52 +00:00
97e952fc15 Update leenkx/Sources/leenkx/logicnode/DrawStringNode.hx 2025-08-14 18:57:13 +00:00
b440539d65 Merge pull request 'main' (#102) from Onek8/LNXSDK:main into main
Reviewed-on: LeenkxTeam/LNXSDK#102
2025-07-23 17:34:02 +00:00
60a9db6459 Update api/api.hxml 2025-07-22 21:54:56 +00:00
3b5a93c92a Update leenkx/Sources/leenkx/trait/PhysicsBreak.hx 2025-07-22 21:51:25 +00:00
4af990796e t3du - Add TSceneFormat as Trait property type 2025-07-21 23:27:34 +00:00
9fb4916c3c t3du - Add TSceneFormat as Trait property type 2025-07-21 23:24:30 +00:00
f61d5833bb Update leenkx/blender/lnx/exporter.py 2025-07-21 23:15:59 +00:00
40b52be713 t3du - Add TSceneFormat as Trait property type 2025-07-21 23:12:43 +00:00
07d8422f22 Merge pull request 'Update leenkx/Sources/iron/system/Time.hx' (#101) from Onek8/LNXSDK:main into main
Reviewed-on: LeenkxTeam/LNXSDK#101
2025-07-19 20:23:46 +00:00
7179d42b27 Update leenkx/Sources/iron/system/Time.hx 2025-07-19 20:07:08 +00:00
111 changed files with 4407 additions and 4614 deletions

View File

@ -2,10 +2,12 @@
-cp ../Kha/Backends/Krom
-cp ../leenkx/Sources
-cp ../iron/Sources
-cp ../lib/aura/Sources
-cp ../lib/haxebullet/Sources
-cp ../lib/haxerecast/Sources
-cp ../lib/zui/Sources
--macro include('iron', true, null, ['../iron/Sources'])
--macro include('aura', true, null, ['../lib/aura/Sources'])
--macro include('haxebullet', true, null, ['../lib/haxebullet/Sources'])
--macro include('haxerecast', true, null, ['../lib/haxerecast/Sources'])
--macro include('leenkx', true, ['leenkx.network'], ['../leenkx/Sources','../iron/Sources'])

View File

@ -24,7 +24,7 @@ import textwrap
import threading
import traceback
import typing
from typing import Callable, Optional
from typing import Callable, Optional, List
import webbrowser
import bpy
@ -33,6 +33,12 @@ from bpy.props import *
from bpy.types import Operator, AddonPreferences
if bpy.app.version < (2, 90, 0):
ListType = List
else:
ListType = list
class SDKSource(IntEnum):
PREFS = 0
LOCAL = 1
@ -73,9 +79,10 @@ def detect_sdk_path():
area = win.screen.areas[0]
area_type = area.type
area.type = "INFO"
with bpy.context.temp_override(window=win, screen=win.screen, area=area):
bpy.ops.info.select_all(action='SELECT')
bpy.ops.info.report_copy()
if bpy.app.version >= (2, 92, 0):
with bpy.context.temp_override(window=win, screen=win.screen, area=area):
bpy.ops.info.select_all(action='SELECT')
bpy.ops.info.report_copy()
area.type = area_type
clipboard = bpy.context.window_manager.clipboard
@ -85,6 +92,7 @@ def detect_sdk_path():
if match:
addon_prefs.sdk_path = os.path.dirname(match[-1])
def get_link_web_server(self):
return self.get('link_web_server', 'http://localhost/')
@ -558,7 +566,7 @@ def remove_readonly(func, path, excinfo):
func(path)
def run_proc(cmd: list[str], done: Optional[Callable[[bool], None]] = None):
def run_proc(cmd: ListType[str], done: Optional[Callable[[bool], None]] = None):
def fn(p, done):
p.wait()
if done is not None:
@ -840,7 +848,13 @@ def update_leenkx_py(sdk_path: str, force_relink=False):
else:
raise err
else:
lnx_module_file.unlink(missing_ok=True)
if bpy.app.version < (2, 92, 0):
try:
lnx_module_file.unlink()
except FileNotFoundError:
pass
else:
lnx_module_file.unlink(missing_ok=True)
shutil.copy(Path(sdk_path) / 'leenkx.py', lnx_module_file)

View File

@ -29,11 +29,10 @@ uniform sampler2D gbuffer1;
#ifdef _VoxelGI
uniform sampler2D voxels_diffuse;
uniform sampler2D voxels_specular;
#else
#endif
#ifdef _VoxelAOvar
uniform sampler2D voxels_ao;
#endif
#endif
#ifdef _VoxelShadow
uniform sampler3D voxels;
uniform sampler3D voxelsSDF;
@ -57,10 +56,6 @@ uniform vec3 backgroundCol;
#ifdef _SSAO
uniform sampler2D ssaotex;
#else
#ifdef _SSGI
uniform sampler2D ssaotex;
#endif
#endif
#ifdef _SSS
@ -118,15 +113,11 @@ 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
@ -134,40 +125,30 @@ 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
#endif
//!uniform vec4 pointLightDataArray[maxLightsCluster * 6];
//!uniform vec4 pointLightDataArray[4];
#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
@ -180,16 +161,12 @@ 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];
@ -250,22 +227,17 @@ void main() {
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
@ -299,33 +271,33 @@ void main() {
envl.rgb *= albedo;
#ifdef _Brdf
envl.rgb *= 1.0 - F; //LV: We should take refracted light into account
envl.rgb *= 1.0 - (f0 * envBRDF.x + envBRDF.y); //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
envl.rgb += prefilteredColor * (f0 * envBRDF.x + envBRDF.y); //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?
envl.rgb += backgroundCol * (f0 * envBRDF.x + envBRDF.y); //LV: Eh, what's the point of weighting it only by F0?
#endif
#endif
envl.rgb *= envmapStrength * occspec.x;
fragColor.rgb = envl;
#endif
#endif
#ifdef _VoxelGI
fragColor.rgb = textureLod(voxels_diffuse, texCoord, 0.0).rgb * voxelgiDiff;
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;
#else
#ifdef _VoxelAOvar
fragColor.rgb = textureLod(voxels_ao, texCoord, 0.0).rgb * voxelgiOcc;
#endif
#endif
#ifdef _VoxelAOvar
envl.rgb *= textureLod(voxels_ao, texCoord, 0.0).r;
#endif
#ifndef _VoxelGI
fragColor.rgb = envl;
#endif
// Show voxels
// vec3 origin = vec3(texCoord * 2.0 - 1.0, 0.99);
// vec3 direction = vec3(0.0, 0.0, -1.0);
@ -345,10 +317,6 @@ void main() {
// #else
fragColor.rgb *= textureLod(ssaotex, texCoord, 0.0).r;
// #endif
#else
#ifdef _SSGI
fragColor.rgb += textureLod(ssaotex, texCoord, 0.0).rgb;
#endif
#endif
#ifdef _EmissionShadeless
@ -381,70 +349,40 @@ void main() {
#ifdef _ShadowMap
#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
#ifdef _ShadowMapTransparent
shadowMap, shadowMapTransparent
#else
shadowMap
#endif
#endif
, eye, p + n * shadowsBias * 10, shadowsBias
#ifdef _ShadowMapTransparent
, false
#endif
);
#ifdef _ShadowMapAtlas
#ifndef _SingleAtlas
shadowMapAtlasSun, shadowMapAtlasSunTransparent
#else
shadowMapAtlas, shadowMapAtlasTransparent
#endif
#else
shadowMap, shadowMapTransparent
#endif
, eye, p + n * shadowsBias * 10, shadowsBias, false
);
#else
vec4 lPos = LWVP * vec4(p + n * shadowsBias * 100, 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
#endif
, lPos.xyz / lPos.w, shadowsBias
#ifdef _ShadowMapTransparent
, false
#endif
);
#ifdef _ShadowMapAtlas
#ifndef _SingleAtlas
shadowMapAtlasSun, shadowMapAtlasSunTransparent
#else
shadowMapAtlas, shadowMapAtlasTransparent
#endif
#else
shadowMap, shadowMapTransparent
#endif
, lPos.xyz / lPos.w, shadowsBias, false
);
}
#endif
#endif
#ifdef _VoxelShadow
svisibility *= (1.0 - traceShadow(p, n, voxels, voxelsSDF, sunDir, clipmaps, gl_FragCoord.xy, -g2.rg).r) * voxelgiShad;
svisibility *= (1.0 - traceShadow(p, n, voxels, voxelsSDF, sunDir, clipmaps, gl_FragCoord.xy).r) * voxelgiShad;
#endif
#ifdef _SSRS
// vec2 coords = getProjectedCoord(hitCoord);
// vec2 deltaCoords = abs(vec2(0.5, 0.5) - coords.xy);
@ -501,16 +439,13 @@ void main() {
fragColor.rgb += sampleLight(
p, n, v, dotNV, pointPos, pointCol, albedo, roughness, occspec.y, f0
#ifdef _ShadowMap
, 0, pointBias, true
#ifdef _ShadowMapTransparent
, false
#endif
, 0, pointBias, true, false
#endif
#ifdef _Spot
, true, spotData.x, spotData.y, spotDir, spotData.zw, spotRight
#endif
#ifdef _VoxelShadow
, voxels, voxelsSDF, clipmaps, -g2.rg
, voxels, voxelsSDF, clipmaps
#endif
#ifdef _MicroShadowing
, occspec.x
@ -557,10 +492,7 @@ void main() {
f0
#ifdef _ShadowMap
// light index, shadow bias, cast_shadows
, li, lightsArray[li * 3 + 2].x, lightsArray[li * 3 + 2].z != 0.0
#ifdef _ShadowMapTransparent
, false
#endif
, li, lightsArray[li * 3 + 2].x, lightsArray[li * 3 + 2].z != 0.0, false
#endif
#ifdef _Spot
, lightsArray[li * 3 + 2].y != 0.0
@ -571,7 +503,7 @@ void main() {
, lightsArraySpot[li * 2 + 1].xyz // right
#endif
#ifdef _VoxelShadow
, voxels, voxelsSDF, clipmaps, -g2.rg
, voxels, voxelsSDF, clipmaps
#endif
#ifdef _MicroShadowing
, occspec.x
@ -582,5 +514,14 @@ 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.a = 1.0; // Mark as opaque
}

View File

@ -1,506 +1,107 @@
#version 450
#include "compiled.inc"
#include "std/gbuffer.glsl"
#include "std/brdf.glsl"
#include "std/math.glsl"
#ifdef _Clusters
#include "std/clusters.glsl"
#endif
#ifdef _ShadowMap
#include "std/shadows.glsl"
#endif
#ifdef _LTC
#include "std/ltc.glsl"
#endif
#ifdef _LightIES
#include "std/ies.glsl"
#endif
#ifdef _Spot
#include "std/light_common.glsl"
#endif
#include "std/constants.glsl"
#include "std/gbuffer.glsl"
uniform sampler2D gbuffer0;
uniform sampler2D gbuffer1;
uniform sampler2D gbufferD;
#ifdef _EmissionShaded
uniform sampler2D gbufferEmission;
#endif
uniform sampler2D sveloc;
uniform sampler2D gbuffer0; // Normal
// #ifdef _RTGI
// uniform sampler2D gbuffer1; // Basecol
// #endif
uniform mat4 P;
uniform mat3 V3;
uniform vec2 cameraProj;
uniform vec3 eye;
uniform vec3 eyeLook;
uniform vec2 screenSize;
uniform mat4 invVP;
in vec2 texCoord;
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;
#endif
in vec3 viewRay;
out vec3 fragColor;
in vec2 texCoord;
out float fragColor;
float metallic;
uint matid;
vec3 hitCoord;
vec2 coord;
float depth;
// #ifdef _RTGI
// vec3 col = vec3(0.0);
// #endif
vec3 vpos;
#ifdef _SMSizeUniform
//!uniform vec2 smSizeUniform;
#endif
vec2 getProjectedCoord(vec3 hitCoord) {
vec4 projectedCoord = P * vec4(hitCoord, 1.0);
projectedCoord.xy /= projectedCoord.w;
projectedCoord.xy = projectedCoord.xy * 0.5 + 0.5;
#ifdef _InvY
projectedCoord.y = 1.0 - projectedCoord.y;
#endif
return projectedCoord.xy;
}
#ifdef _Clusters
uniform vec4 lightsArray[maxLights * 3];
#ifdef _Spot
uniform vec4 lightsArraySpot[maxLights * 2];
#endif
uniform sampler2D clustersData;
uniform vec2 cameraPlane;
#endif
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;
}
#ifdef _SinglePoint // Fast path for single light
uniform vec3 pointPos;
uniform vec3 pointCol;
#ifdef _ShadowMap
uniform float pointBias;
#endif
#ifdef _Spot
uniform vec3 spotDir;
uniform vec3 spotRight;
uniform vec4 spotData;
#endif
#endif
#ifdef _CPostprocess
uniform vec3 PPComp12;
#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;
//!uniform vec4 pointLightDataArray[maxLightsCluster * 6];
#else
uniform samplerCubeShadow shadowMapPoint[4];
uniform samplerCube shadowMapPointTransparent[4];
#endif
#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 mat4 LWVPSpotArray[maxLightsCluster];
#endif
#endif
#endif
#endif
#ifdef _Sun
uniform vec3 sunDir;
uniform vec3 sunCol;
#ifdef _ShadowMap
#ifdef _ShadowMapAtlas
#ifndef _SingleAtlas
uniform sampler2DShadow shadowMapAtlasSun;
uniform sampler2D shadowMapAtlasSunTransparent;
#endif
#else
uniform sampler2DShadow shadowMap;
uniform sampler2D shadowMapTransparent;
#endif
uniform float shadowsBias;
#ifdef _CSM
//!uniform vec4 casData[shadowmapCascades * 4 + 4];
#else
uniform mat4 LWVP;
#endif
#endif // _ShadowMap
#endif
vec3 sampleLight(const vec3 p, const vec3 n, const vec3 lp, const vec3 lightCol
#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
) {
vec3 ld = lp - p;
vec3 l = normalize(ld);
vec3 visibility = lightCol;
visibility *= attenuate(distance(p, lp));
#ifdef _LTC
#ifdef _ShadowMap
if (receiveShadow) {
#ifdef _SinglePoint
vec4 lPos = LWVPSpotArray[0] * vec4(p + n * bias * 10, 1.0);
visibility *= 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) visibility *= shadowTest(shadowMapSpot[0],
shadowMapSpotTransparent[0],
lPos.xyz / lPos.w, bias, transparent);
else if (index == 1) visibility *= shadowTest(shadowMapSpot[1],
shadowMapSpotTransparent[1],
, lPos.xyz / lPos.w, bias, transparent);
else if (index == 2) visibility *= shadowTest(shadowMapSpot[2],
shadowMapSpotTransparent[2],
lPos.xyz / lPos.w, bias, transparent);
else if (index == 3) visibility *= shadowTest(shadowMapSpot[3],
shadowMapSpotTransparent[3],
lPos.xyz / lPos.w, bias, transparent);
#endif
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;
}
#endif
return visibility;
#endif
#ifdef _Spot
if (isSpot) {
visibility *= spotlightMask(l, spotDir, right, scale, spotSize, spotBlend);
#ifdef _ShadowMap
if (receiveShadow) {
#ifdef _SinglePoint
vec4 lPos = LWVPSpot[0] * vec4(p + n * bias * 10, 1.0);
visibility *= 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
visibility *= shadowTest(
#ifndef _SingleAtlas
shadowMapAtlasSpot, shadowMapAtlasSpotTransparent
#else
shadowMapAtlas, shadowMapAtlasTransparent
#endif
, lPos.xyz / lPos.w, bias, transparent
);
#else
if (index == 0) visibility *= shadowTest(shadowMapSpot[0],
shadowMapSpotTransparent[0],
lPos.xyz / lPos.w, bias, transparent);
else if (index == 1) visibility *= shadowTest(shadowMapSpot[1],
shadowMapSpotTransparent[1],
lPos.xyz / lPos.w, bias, transparent);
else if (index == 2) visibility *= shadowTest(shadowMapSpot[2],
shadowMapSpotTransparent[2],
lPos.xyz / lPos.w, bias, transparent);
else if (index == 3) visibility *= shadowTest(shadowMapSpot[3],
shadowMapSpotTransparent[3],
lPos.xyz / lPos.w, bias, transparent);
#endif
#endif
}
#endif
return visibility;
}
#endif
#ifdef _LightIES
visibility *= iesAttenuation(-l);
#endif
#ifdef _ShadowMap
if (receiveShadow) {
#ifdef _SinglePoint
#ifndef _Spot
visibility *= PCFCube(shadowMapPoint[0],
shadowMapPointTransparent[0],
ld, -l, bias, lightProj, n, transparent);
#endif
#endif
#ifdef _Clusters
#ifdef _ShadowMapAtlas
visibility *= PCFFakeCube(
#ifndef _SingleAtlas
shadowMapAtlasPoint, shadowMapAtlasPointTransparent
#else
shadowMapAtlas, shadowMapAtlasTransparent
#endif
, ld, -l, bias, lightProj, n, index, transparent
);
#else
if (index == 0) visibility *= PCFCube(shadowMapPoint[0],
shadowMapPointTransparent[0],
ld, -l, bias, lightProj, n, transparent);
else if (index == 1) visibility *= PCFCube(shadowMapPoint[1],
shadowMapPointTransparent[1],
ld, -l, bias, lightProj, n, transparent);
else if (index == 2) visibility *= PCFCube(shadowMapPoint[2],
shadowMapPointTransparent[2],
ld, -l, bias, lightProj, n, transparent);
else if (index == 3) visibility *= PCFCube(shadowMapPoint[3],
shadowMapPointTransparent[3],
ld, -l, bias, lightProj, n, transparent);
#endif
#endif
}
#endif
return visibility;
fragColor += dist;
// #ifdef _RTGI
// col += textureLod(gbuffer1, coord, 0.0).rgb * ((ssgiRayStep * ssgiMaxSteps) - dist);
// #endif
}
vec3 getVisibility(vec3 p, vec3 n, float depth, vec2 uv) {
vec3 visibility = vec3(0.0);
#ifdef _Sun
#ifdef _ShadowMap
#ifdef _CSM
visibility = shadowTestCascade(
#ifdef _ShadowMapAtlas
#ifndef _SingleAtlas
shadowMapAtlasSun, shadowMapAtlasSunTransparent
#else
shadowMapAtlas, shadowMapAtlasTransparent
#endif
#else
shadowMap, shadowMapTransparent
#endif
, eye, p + n * shadowsBias * 10, shadowsBias, false
);
#else
vec4 lPos = LWVP * vec4(p + n * shadowsBias * 100, 1.0);
if (lPos.w > 0.0) {
visibility = shadowTest(
#ifdef _ShadowMapAtlas
#ifndef _SingleAtlas
shadowMapAtlasSun, shadowMapAtlasSunTransparent
#else
shadowMapAtlas, shadowMapAtlasTransparent
#endif
#else
shadowMap, shadowMapTransparent
#endif
, lPos.xyz / lPos.w, shadowsBias, false
);
}
#endif
#endif
#endif
#ifdef _SinglePoint
visibility += sampleLight(
p, n, pointPos, pointCol
#ifdef _ShadowMap
, 0, pointBias, true, false
#endif
#ifdef _Spot
, true, spotData.x, spotData.y, spotDir, spotData.zw, spotRight
#endif
);
#endif
#ifdef _Clusters
float viewz = linearize(depth, cameraProj);
int clusterI = getClusterI(uv, viewz, cameraPlane);
int numLights = int(texelFetch(clustersData, ivec2(clusterI, 0), 0).r * 255);
#ifdef HLSL
viewz += textureLod(clustersData, vec2(0.0), 0.0).r * 1e-9; // TODO: krafix bug, needs to generate sampler
#endif
#ifdef _Spot
int numSpots = int(texelFetch(clustersData, ivec2(clusterI, 1 + maxLightsCluster), 0).r * 255);
int numPoints = numLights - numSpots;
#endif
for (int i = 0; i < min(numLights, maxLightsCluster); i++) {
int li = int(texelFetch(clustersData, ivec2(clusterI, i + 1), 0).r * 255);
visibility += sampleLight(
p,
n,
lightsArray[li * 3].xyz, // lp
lightsArray[li * 3 + 1].xyz // lightCol
#ifdef _ShadowMap
// light index, shadow bias, cast_shadows
, li, lightsArray[li * 3 + 2].x, lightsArray[li * 3 + 2].z != 0.0, false
#endif
#ifdef _Spot
, lightsArray[li * 3 + 2].y != 0.0
, lightsArray[li * 3 + 2].y // spot size (cutoff)
, lightsArraySpot[li * 2].w // spot blend (exponent)
, lightsArraySpot[li * 2].xyz // spotDir
, vec2(lightsArray[li * 3].w, lightsArray[li * 3 + 1].w) // scale
, lightsArraySpot[li * 2 + 1].xyz // right
#endif
);
}
#endif // _Clusters
return visibility;
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 getWorldPos(vec2 uv, float depth) {
vec4 pos = invVP * vec4(uv * 2.0 - 1.0, depth * 2.0 - 1.0, 1.0);
return pos.xyz / pos.w;
}
vec3 getNormal(vec2 uv) {
vec4 g0 = textureLod(gbuffer0, uv, 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);
return normalize(n);
}
vec3 calculateIndirectLight(vec2 uv, vec3 pos, vec3 normal, float depth) {
// Simplified visibility - replace with your full visibility function if needed
vec3 sampleColor = textureLod(gbuffer1, uv, 0.0).rgb * getVisibility(pos, normal, depth, uv);
#ifdef _EmissionShadeless
if (matid == 1) { // pure emissive material, color stored in basecol
sampleColor += textureLod(gbuffer1, uv, 0.0).rgb;
}
#endif
#ifdef _EmissionShaded
#ifdef _EmissionShadeless
else {
#endif
vec3 sampleEmission = textureLod(gbufferEmission, uv, 0.0).rgb;
sampleColor += sampleEmission; // Emission should be added directly
#ifdef _EmissionShadeless
}
#endif
#endif
return sampleColor;
}
// Improved sampling parameters
const float GOLDEN_ANGLE = 2.39996323;
const float MAX_DEPTH_DIFFERENCE = 0.9; // More conservative depth threshold
const float SAMPLE_BIAS = 0.01; // Small offset to avoid self-occlusion
void main() {
float depth = textureLod(gbufferD, texCoord, 0.0).r;
if (depth >= 1.0) {
fragColor = vec3(0.0);
return;
}
vec4 g0 = textureLod(gbuffer0, texCoord, 0.0); // Normal.xy, roughness, metallic/matid
unpackFloatInt16(g0.a, metallic, matid);
vec2 velocity = -textureLod(sveloc, texCoord, 0.0).rg;
fragColor = 0;
vec4 g0 = textureLod(gbuffer0, texCoord, 0.0);
float d = textureLod(gbufferD, texCoord, 0.0).r * 2.0 - 1.0;
vec2 enc = g0.rg;
vec3 n;
n.z = 1.0 - abs(g0.x) - abs(g0.y);
n.xy = n.z >= 0.0 ? g0.xy : octahedronWrap(g0.xy);
n = normalize(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);
vec3 pos = getWorldPos(texCoord, depth);
vec3 normal = getNormal(texCoord);
vec3 centerColor = textureLod(gbuffer1, texCoord, 0.0).rgb;
vpos = getPosView(viewRay, d, cameraProj);
float radius = ssaoRadius;
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));
vec3 gi = vec3(0.0);
float totalWeight = 0.0;
float angle = fract(sin(dot(texCoord, vec2(12.9898, 78.233))) * 100.0);
for (int i = 0; i < ssgiSamples; i++) {
// Use quasi-random sequence for better coverage
float r = sqrt((float(i) + 0.5) / float(ssgiSamples)) * radius;
float a = (float(i) * GOLDEN_ANGLE) + angle;
vec2 offset = vec2(cos(a), sin(a)) * r * radius;
vec2 sampleUV = clamp(texCoord + offset * (BayerMatrix8[int(gl_FragCoord.x + velocity.x) % 8][int(gl_FragCoord.y + velocity.y) % 8] - 0.5) / screenSize, vec2(0.001), vec2(0.999));
float sampleDepth = textureLod(gbufferD, sampleUV, 0.0).r;
if (sampleDepth >= 1.0) continue;
vec3 samplePos = getWorldPos(sampleUV, sampleDepth);
vec3 sampleNormal = getNormal(sampleUV);
// Apply small bias to sample position to avoid self-occlusion
samplePos += sampleNormal * SAMPLE_BIAS;
vec3 dir = pos - samplePos;
float dist = length(dir);
if (abs(pos.z - samplePos.z) > MAX_DEPTH_DIFFERENCE) continue;;
vec3 sampleColor = calculateIndirectLight(sampleUV, samplePos, sampleNormal, sampleDepth);
float weight = 1.0 / (1.0 + dist * dist * 2.0) * max(dot(sampleNormal, n), 0.0);
gi += sampleColor * weight;
totalWeight += weight;
}
// Normalize and apply intensity
if (totalWeight > 0.0) {
gi /= totalWeight;
#ifdef _CPostprocess
gi *= PPComp12.x;
#else
gi *= ssaoStrength;
#endif
}
#ifdef _EmissionShadeless
if (matid == 1) { // pure emissive material, color stored in basecol
gi += textureLod(gbuffer1, texCoord, 0.0).rgb;
}
#ifdef _SSGICone9
rayCast(mix(n, -o1, angleMix));
rayCast(mix(n, -o2, angleMix));
rayCast(mix(n, c1, angleMix));
rayCast(mix(n, c2, angleMix));
#endif
#ifdef _EmissionShaded
#ifdef _EmissionShadeless
else {
#endif
gi += textureLod(gbufferEmission, texCoord, 0.0).rgb;
#ifdef _EmissionShadeless
}
#endif
#endif
fragColor = gi / (gi + vec3(1.0)); // Reinhard tone mapping
}

View File

@ -6,10 +6,6 @@
"compare_mode": "always",
"cull_mode": "none",
"links": [
{
"name": "invVP",
"link": "_inverseViewProjectionMatrix"
},
{
"name": "P",
"link": "_projectionMatrix"
@ -19,180 +15,16 @@
"link": "_viewMatrix3"
},
{
"name": "eye",
"link": "_cameraPosition"
},
{
"name": "eyeLook",
"link": "_cameraLook"
"name": "invP",
"link": "_inverseProjectionMatrix"
},
{
"name": "cameraProj",
"link": "_cameraPlaneProj"
},
{
"name": "screenSize",
"link": "_screenSize"
},
{
"name": "PPComp12",
"link": "_PPComp12",
"ifdef": ["_CPostprocess"]
},
{
"name": "lightsArraySpot",
"link": "_lightsArraySpot",
"ifdef": ["_Clusters", "_Spot"]
},
{
"name": "lightsArray",
"link": "_lightsArray",
"ifdef": ["_Clusters"]
},
{
"name": "clustersData",
"link": "_clustersData",
"ifdef": ["_Clusters"]
},
{
"name": "cameraPlane",
"link": "_cameraPlane",
"ifdef": ["_Clusters"]
},
{
"name": "sunDir",
"link": "_sunDirection",
"ifdef": ["_Sun"]
},
{
"name": "sunCol",
"link": "_sunColor",
"ifdef": ["_Sun"]
},
{
"name": "shadowsBias",
"link": "_sunShadowsBias",
"ifdef": ["_Sun", "_ShadowMap"]
},
{
"name": "LWVP",
"link": "_biasLightWorldViewProjectionMatrixSun",
"ifndef": ["_CSM"],
"ifdef": ["_Sun", "_ShadowMap"]
},
{
"name": "casData",
"link": "_cascadeData",
"ifdef": ["_Sun", "_ShadowMap", "_CSM"]
},
{
"name": "lightArea0",
"link": "_lightArea0",
"ifdef": ["_LTC"]
},
{
"name": "lightArea1",
"link": "_lightArea1",
"ifdef": ["_LTC"]
},
{
"name": "lightArea2",
"link": "_lightArea2",
"ifdef": ["_LTC"]
},
{
"name": "lightArea3",
"link": "_lightArea3",
"ifdef": ["_LTC"]
},
{
"name": "sltcMat",
"link": "_ltcMat",
"ifdef": ["_LTC"]
},
{
"name": "sltcMag",
"link": "_ltcMag",
"ifdef": ["_LTC"]
},
{
"name": "smSizeUniform",
"link": "_shadowMapSize",
"ifdef": ["_SMSizeUniform"]
},
{
"name": "lightProj",
"link": "_lightPlaneProj",
"ifdef": ["_ShadowMap"]
},
{
"name": "pointPos",
"link": "_pointPosition",
"ifdef": ["_SinglePoint"]
},
{
"name": "pointCol",
"link": "_pointColor",
"ifdef": ["_SinglePoint"]
},
{
"name": "pointBias",
"link": "_pointShadowsBias",
"ifdef": ["_SinglePoint", "_ShadowMap"]
},
{
"name": "spotDir",
"link": "_spotDirection",
"ifdef": ["_SinglePoint", "_Spot"]
},
{
"name": "spotData",
"link": "_spotData",
"ifdef": ["_SinglePoint", "_Spot"]
},
{
"name": "spotRight",
"link": "_spotRight",
"ifdef": ["_SinglePoint", "_Spot"]
},
{
"name": "LWVPSpotArray",
"link": "_biasLightWorldViewProjectionMatrixSpotArray",
"ifdef": ["_Clusters", "_ShadowMap", "_Spot"]
},
{
"name": "pointLightDataArray",
"link": "_pointLightsAtlasArray",
"ifdef": ["_Clusters", "_ShadowMap", "_ShadowMapAtlas"]
},
{
"name": "LWVPSpot[0]",
"link": "_biasLightWorldViewProjectionMatrixSpot0",
"ifndef": ["_ShadowMapAtlas"],
"ifdef": ["_LTC", "_ShadowMap"]
},
{
"name": "LWVPSpot[1]",
"link": "_biasLightWorldViewProjectionMatrixSpot1",
"ifndef": ["_ShadowMapAtlas"],
"ifdef": ["_LTC", "_ShadowMap"]
},
{
"name": "LWVPSpot[2]",
"link": "_biasLightWorldViewProjectionMatrixSpot2",
"ifndef": ["_ShadowMapAtlas"],
"ifdef": ["_LTC", "_ShadowMap"]
},
{
"name": "LWVPSpot[3]",
"link": "_biasLightWorldViewProjectionMatrixSpot3",
"ifndef": ["_ShadowMapAtlas"],
"ifdef": ["_LTC", "_ShadowMap"]
}
],
"texture_params": [],
"vertex_shader": "../include/pass_viewray.vert.glsl",
"vertex_shader": "../include/pass_viewray2.vert.glsl",
"fragment_shader": "ssgi_pass.frag.glsl"
}
]

View File

@ -72,11 +72,10 @@ void main() {
float roughness = g0.z;
vec4 gr = textureLod(gbuffer_refraction, texCoord, 0.0);
float ior = gr.x;
float opac = 1.0 - gr.y;
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;
fragColor.a = opac;
return;
}
vec2 enc = g0.rg;
@ -99,12 +98,9 @@ void main() {
clamp(-refracted.z, 0.0, 1.0) * clamp((length(viewPos - hitCoord)), 0.0, 1.0) * coords.w;
intensity = clamp(intensity, 0.0, 1.0);
vec4 refractionCol = textureLod(tex1, coords.xy, 0.0).rgba;
refractionCol.a = opac;
//refractionCol *= intensity;
vec4 color = textureLod(tex, texCoord.xy, 0.0).rgba;
color.a = opac;
vec3 refractionCol = textureLod(tex1, coords.xy, 0.0).rgb;
refractionCol *= intensity;
vec3 color = textureLod(tex, texCoord.xy, 0.0).rgb;
fragColor.rgba = mix(refractionCol, color, opac);
fragColor.a = opac;
fragColor.rgb = mix(refractionCol, color, opac);
}

View File

@ -5,12 +5,6 @@
"depth_write": false,
"compare_mode": "always",
"cull_mode": "none",
"blend_source": "source_alpha",
"blend_destination": "inverse_source_alpha",
"blend_operation": "add",
"alpha_blend_source": "blend_one",
"alpha_blend_destination": "blend_one",
"alpha_blend_operation": "add",
"links": [
{
"name": "P",

View File

@ -1,18 +0,0 @@
#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

@ -86,7 +86,7 @@ vec3 surfaceAlbedo(const vec3 baseColor, const float metalness) {
}
vec3 surfaceF0(const vec3 baseColor, const float metalness) {
return mix(vec3(0.04), baseColor * (2.0 + metalness * 1.2), metalness);
return mix(vec3(0.04), baseColor, metalness);
}
float getMipFromRoughness(const float roughness, const float numMipmaps) {

View File

@ -22,7 +22,7 @@ THE SOFTWARE.
#ifndef _CONETRACE_GLSL_
#define _CONETRACE_GLSL_
#include "std/constants.glsl"
#include "std/voxels_constants.glsl"
// References
// https://github.com/Friduric/voxel-cone-tracing
@ -92,14 +92,14 @@ 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;
vec3 start_pos = origin + n * voxelSize0 * voxelgiOffset;
int clipmap_index0 = 0;
vec3 aniso_direction = -dir;
vec3 face_offset = vec3(
aniso_direction.x > 0.0 ? 0 : 1,
aniso_direction.y > 0.0 ? 2 : 3,
aniso_direction.z > 0.0 ? 4 : 5
aniso_direction.x > 0.0 ? 0.0 : 1.0,
aniso_direction.y > 0.0 ? 2.0 : 3.0,
aniso_direction.z > 0.0 ? 4.0 : 5.0
) / (6 + DIFFUSE_CONE_COUNT);
vec3 direction_weight = abs(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, clipmap_blend);
mipSample = mix(mipSample, mipSampleNext, smoothstep(0.0, 1.0, clipmap_blend));
}
sampleCol += (1.0 - sampleCol.a) * mipSample;
@ -148,9 +148,8 @@ 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 = TBN * DIFFUSE_CONE_DIRECTIONS[i];
vec3 coneDir = DIFFUSE_CONE_DIRECTIONS[i];
const float cosTheta = dot(normal, coneDir);
if (cosTheta <= 0)
continue;
@ -167,7 +166,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(normalize(-viewDir), normal);
vec3 specularDir = reflect(-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);
@ -177,9 +176,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 opacity) {
const float transmittance = 1.0 - opacity;
vec3 refractionDir = refract(normalize(-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 transmittance = 1.0;
vec3 refractionDir = refract(-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);
@ -197,14 +196,14 @@ 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;
vec3 start_pos = origin + n * voxelSize0 * voxelgiOffset;
int clipmap_index0 = 0;
vec3 aniso_direction = -dir;
vec3 face_offset = vec3(
aniso_direction.x > 0.0 ? 0 : 1,
aniso_direction.y > 0.0 ? 2 : 3,
aniso_direction.z > 0.0 ? 4 : 5
aniso_direction.x > 0.0 ? 0.0 : 1.0,
aniso_direction.y > 0.0 ? 2.0 : 3.0,
aniso_direction.z > 0.0 ? 4.0 : 5.0
) / (6 + DIFFUSE_CONE_COUNT);
vec3 direction_weight = abs(dir);
@ -260,6 +259,7 @@ 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,14 +267,14 @@ 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;
vec3 start_pos = origin + n * voxelSize0 * voxelgiOffset;
int clipmap_index0 = 0;
vec3 aniso_direction = -dir;
vec3 face_offset = vec3(
aniso_direction.x > 0.0 ? 0 : 1,
aniso_direction.y > 0.0 ? 2 : 3,
aniso_direction.z > 0.0 ? 4 : 5
aniso_direction.x > 0.0 ? 0.0 : 1.0,
aniso_direction.y > 0.0 ? 2.0 : 3.0,
aniso_direction.z > 0.0 ? 4.0 : 5.0
) / (6 + DIFFUSE_CONE_COUNT);
vec3 direction_weight = abs(dir);
float coneCoefficient = 2.0 * tan(aperture * 0.5);
@ -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);
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 = 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, 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);
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);
amount = clamp(amount, 0.0, 1.0);
return amount * voxelgiOcc;
}

View File

@ -1,679 +1,239 @@
#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 *= 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
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 * 10, 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 * 10, 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 * 10, 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 * 10, 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));
direct *= lightCol;
#ifdef _LTC
#ifdef _ShadowMap
if (receiveShadow) {
#ifdef _SinglePoint
vec4 lPos = LWVPSpot[0] * vec4(p + n * bias * 10, 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 * 10, 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 * 10, 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 * 10, 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
#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

View File

@ -58,15 +58,7 @@ vec2 sampleCube(vec3 dir, out int faceIndex) {
}
#endif
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 PCF(sampler2DShadow shadowMap, sampler2D shadowMapTransparent, const vec2 uv, const float compare, const vec2 smSize, const bool transparent) {
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));
@ -79,13 +71,11 @@ vec3 PCF(sampler2DShadow shadowMap,
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;
}
@ -97,15 +87,41 @@ float lpToDepth(vec3 lp, const vec2 lightProj) {
return zcomp * 0.5 + 0.5;
}
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
) {
#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) {
const float s = shadowmapCubePcfSize; // TODO: incorrect...
float compare = lpToDepth(lp, lightProj) - bias * 1.5;
ml = ml + n * bias * 20;
@ -124,18 +140,16 @@ vec3 PCFCube(samplerCubeShadow shadowMapCube,
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) {
@ -229,31 +243,21 @@ vec2 transformOffsetedUV(const int faceIndex, out int newFaceIndex, vec2 uv) {
return uv;
}
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
) {
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) {
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); // Handle edge cases by returning full light
}
vec3 result = vec3(0.0);
result.x += texture(shadowMap, vec3(uvtiled, compare));
// soft shadowing
@ -266,6 +270,14 @@ vec3 PCFFakeCube(sampler2DShadow shadowMap,
#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;
@ -322,47 +334,30 @@ vec3 PCFFakeCube(sampler2DShadow shadowMap,
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,
#ifdef _ShadowMapTransparent
sampler2D shadowMapTransparent,
#endif
const vec3 lPos, const float shadowsBias
#ifdef _ShadowMapTransparent
, const bool transparent
#endif
) {
vec3 shadowTest(sampler2DShadow shadowMap, sampler2D shadowMapTransparent, const vec3 lPos, const float shadowsBias, const bool transparent) {
#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,
#ifdef _ShadowMapTransparent
shadowMapTransparent,
#endif
lPos.xy, lPos.z - shadowsBias, smSize
#ifdef _ShadowMapTransparent
, transparent
#endif
);
return PCF(shadowMap, shadowMapTransparent, lPos.xy, lPos.z - shadowsBias, smSize, transparent);
}
#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));
@ -378,26 +373,21 @@ 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,
#ifdef _ShadowMapTransparent
sampler2D shadowMapTransparent,
#endif
const vec3 eye, const vec3 p, const float shadowsBias
#ifdef _ShadowMapTransparent
, const bool transparent
#endif
) {
vec3 shadowTestCascade(sampler2DShadow shadowMap, sampler2D shadowMapTransparent, const vec3 eye, const vec3 p, const float shadowsBias, const bool transparent) {
#ifdef _SMSizeUniform
vec2 smSize = smSizeUniform;
#else
@ -405,22 +395,16 @@ vec3 shadowTestCascade(sampler2DShadow shadowMap,
#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,
#ifdef _ShadowMapTransparent
shadowMapTransparent,
#endif
lPos.xy, lPos.z - shadowsBias, smSize
#ifdef _ShadowMapTransparent
, transparent
#endif
);
if (lPos.w > 0.0) visibility = PCF(shadowMap, shadowMapTransparent, lPos.xy, lPos.z - shadowsBias, smSize, transparent);
// Blend cascade
// https://github.com/TheRealMJP/Shadows
@ -439,20 +423,13 @@ vec3 shadowTestCascade(sampler2DShadow shadowMap,
vec4 lPos2 = LWVP2 * vec4(p, 1.0);
lPos2.xyz /= lPos2.w;
vec3 visibility2 = vec3(1.0);
if (lPos2.w > 0.0) visibility2 = PCF(shadowMap,
#ifdef _ShadowMapTransparent
shadowMapTransparent,
#endif
lPos.xy, lPos.z - shadowsBias, smSize
#ifdef _ShadowMapTransparent
, transparent
#endif
);
if (lPos2.w > 0.0) visibility2 = PCF(shadowMap, shadowMapTransparent, lPos2.xy, lPos2.z - shadowsBias, smSize, transparent);
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);
@ -460,4 +437,4 @@ vec3 shadowTestCascade(sampler2DShadow shadowMap,
// if (ci == 12) albedo.rgb = vec3(1.0, 1.0, 0.0);
}
#endif
#endif
#endif

View File

@ -21,49 +21,29 @@ THE SOFTWARE.
*/
const int DIFFUSE_CONE_COUNT = 16;
const float DIFFUSE_CONE_APERTURE = radians(45.0);
const float SHADOW_CONE_APERTURE = radians(15.0);
const vec3 DIFFUSE_CONE_DIRECTIONS[16] = {
vec3(0.0000, 0.0000, 1.0000), // Central direction
vec3(0.3827, 0.0000, 0.9239), // Ring 1
vec3(-0.3827, 0.0000, 0.9239),
vec3(0.0000, 0.3827, 0.9239),
vec3(0.0000, -0.3827, 0.9239),
vec3(0.2706, 0.2706, 0.9239), // Ring 2
vec3(-0.2706, 0.2706, 0.9239),
vec3(0.2706, -0.2706, 0.9239),
vec3(-0.2706, -0.2706, 0.9239),
vec3(0.1802, 0.3604, 0.9239), // Ring 3
vec3(-0.1802, 0.3604, 0.9239),
vec3(0.1802, -0.3604, 0.9239),
vec3(-0.1802, -0.3604, 0.9239),
vec3(0.3604, 0.1802, 0.9239),
vec3(-0.3604, 0.1802, 0.9239),
vec3(0.3604, -0.1802, 0.9239)
};
const float DIFFUSE_CONE_APERTURE = 0.872665; // 50 degrees in radians
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] =
const float 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 },
@ -74,15 +54,3 @@ const float off_BayerMatrix8[8][8] =
{ 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

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

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/constants.glsl"
#include "std/voxels_constants.glsl"
#ifdef _VoxelGI
uniform layout(rgba8) image3D voxelsB;
uniform layout(rgba8) image3D voxelsOut;
#else
uniform layout(r8) image3D voxelsB;
uniform layout(r8) image3D voxelsOut;
uniform layout(r16) image3D voxelsB;
uniform layout(r16) image3D voxelsOut;
#endif
uniform int clipmapLevel;

View File

@ -29,38 +29,19 @@ 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(rgba8) image2D voxels_ao;
uniform layout(r8) 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;
@ -73,11 +54,12 @@ void main() {
float x = uv.x * 2 - 1;
float y = uv.y * 2 - 1;
vec4 clipPos = vec4(x, y, depth, 1.0);
vec4 worldPos = InvVP * clipPos;
vec3 P = worldPos.xyz / worldPos.w;
vec4 v = vec4(x, y, 1.0, 1.0);
v = vec4(InvVP * v);
v.xyz /= v.w;
vec3 viewRay = v.xyz - eye;
vec3 v = normalize(eye - P);
vec3 P = getPos(eye, eyeLook, normalize(viewRay), depth, cameraProj);
vec4 g0 = textureLod(gbuffer0, uv, 0.0);
vec3 n;
@ -85,89 +67,7 @@ void main() {
n.xy = n.z >= 0.0 ? g0.xy : octahedronWrap(g0.xy);
n = normalize(n);
float roughness = g0.b;
float metallic;
uint matid;
unpackFloatInt16(g0.a, metallic, matid);
float occ = 1.0 - traceAO(P, n, voxels, clipmaps);
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));
imageStore(voxels_ao, ivec2(pixel), vec4(occ));
}

View File

@ -29,8 +29,6 @@ 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;
@ -39,44 +37,29 @@ 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.0) return;
if (depth == 0) return;
float x = uv.x * 2 - 1;
float y = uv.y * 2 - 1;
vec4 clipPos = vec4(x, y, depth, 1.0);
vec4 worldPos = InvVP * clipPos;
vec3 P = worldPos.xyz / worldPos.w;
vec3 v = normalize(eye - P);
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 g0 = textureLod(gbuffer0, uv, 0.0);
vec3 n;
@ -84,94 +67,7 @@ void main() {
n.xy = n.z >= 0.0 ? g0.xy : octahedronWrap(g0.xy);
n = normalize(n);
float roughness = g0.b;
float metallic;
uint matid;
unpackFloatInt16(g0.a, metallic, matid);
vec4 color = traceDiffuse(P, n, voxels, clipmaps);
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));
imageStore(voxels_diffuse, ivec2(pixel), color);
}

View File

@ -0,0 +1,79 @@
/*
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.
*/
#version 450
layout (local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
#include "compiled.inc"
#include "std/math.glsl"
#include "std/gbuffer.glsl"
#include "std/imageatomic.glsl"
#include "std/conetrace.glsl"
uniform sampler2D gbufferD;
uniform sampler2D gbuffer0;
uniform sampler3D voxels;
uniform sampler3D voxelsSDF;
uniform sampler2D gbuffer_refraction;
uniform layout(rgba8) image2D voxels_refraction;
uniform float clipmaps[voxelgiClipmapCount * 10];
uniform mat4 InvVP;
uniform vec2 cameraProj;
uniform vec3 eye;
uniform vec3 eyeLook;
uniform vec2 postprocess_resolution;
void main() {
const vec2 pixel = gl_GlobalInvocationID.xy;
vec2 uv = (pixel + 0.5) / postprocess_resolution;
#ifdef _InvY
uv.y = 1.0 - uv.y
#endif
float depth = textureLod(gbufferD, uv, 0.0).r * 2.0 - 1.0;
if (depth == 0) return;
vec2 ior_opac = textureLod(gbuffer_refraction, uv, 0.0).xy;
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 g0 = textureLod(gbuffer0, uv, 0.0);
vec3 n;
n.z = 1.0 - abs(g0.x) - abs(g0.y);
n.xy = n.z >= 0.0 ? g0.xy : octahedronWrap(g0.xy);
n = normalize(n);
vec3 color = vec3(0.0);
if(ior_opac.y < 1.0)
color = traceRefraction(P, n, voxels, voxelsSDF, normalize(eye - P), ior_opac.x, g0.b, clipmaps, pixel).rgb;
imageStore(voxels_refraction, ivec2(pixel), vec4(color, 1.0));
}

View File

@ -0,0 +1,75 @@
/*
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.
*/
#version 450
layout (local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
#include "compiled.inc"
#include "std/math.glsl"
#include "std/gbuffer.glsl"
#include "std/imageatomic.glsl"
#include "std/conetrace.glsl"
uniform sampler3D voxels;
uniform sampler3D voxelsSDF;
uniform sampler2D gbufferD;
uniform sampler2D gbuffer0;
uniform layout(r16) image2D voxels_shadows;
uniform float clipmaps[voxelgiClipmapCount * 10];
uniform mat4 InvVP;
uniform vec2 cameraProj;
uniform vec3 eye;
uniform vec3 eyeLook;
uniform vec2 postprocess_resolution;
uniform vec3 lPos;
void main() {
const vec2 pixel = gl_GlobalInvocationID.xy;
vec2 uv = (pixel + 0.5) / postprocess_resolution;
#ifdef _InvY
uv.y = 1.0 - uv.y;
#endif
float depth = textureLod(gbufferD, uv, 0.0).r * 2.0 - 1.0;
if (depth == 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 g0 = textureLod(gbuffer0, uv, 0.0);
vec3 n;
n.z = 1.0 - abs(g0.x) - abs(g0.y);
n.xy = n.z >= 0.0 ? g0.xy : octahedronWrap(g0.xy);
n = normalize(n);
float occ = 1.0 - traceShadow(P, n, voxels, voxelsSDF, normalize(lPos - P), clipmaps, pixel);
imageStore(voxels_shadows, ivec2(pixel), vec4(occ));
}

View File

@ -29,7 +29,6 @@ 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;
@ -39,7 +38,9 @@ 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;
@ -55,10 +56,12 @@ void main() {
float x = uv.x * 2 - 1;
float y = uv.y * 2 - 1;
vec4 clipPos = vec4(x, y, depth, 1.0);
vec4 worldPos = InvVP * clipPos;
vec3 P = worldPos.xyz / worldPos.w;
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 g0 = textureLod(gbuffer0, uv, 0.0);
vec3 n;
@ -68,7 +71,7 @@ void main() {
vec2 velocity = -textureLod(sveloc, uv, 0.0).rg;
vec3 color = traceSpecular(P, n, voxels, voxelsSDF, normalize(eye - P), g0.z * g0.z, clipmaps, pixel, velocity).rgb;
vec3 color = traceSpecular(P, n, voxels, voxelsSDF, normalize(eye - P), 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(r8) image3D input_sdf;
uniform layout(r8) image3D output_sdf;
uniform layout(r16) image3D input_sdf;
uniform layout(r16) 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(r8) image3D SDF;
uniform layout(r16) image3D SDF;
#else
#ifdef _VoxelAOvar
#ifdef _VoxelShadow
uniform layout(r8) image3D SDF;
uniform layout(r16) image3D SDF;
#endif
uniform layout(r32ui) uimage3D voxels;
uniform layout(r8) image3D voxelsB;
uniform layout(r8) image3D voxelsOut;
uniform layout(r16) image3D voxelsB;
uniform layout(r16) image3D voxelsOut;
#endif
#endif
@ -74,9 +74,14 @@ void main() {
#endif
#endif
int nor_count = 0;
vec3 avgNormal = vec3(0.0);
mat3 TBN = mat3(0.0);
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
for (int i = 0; i < 6 + DIFFUSE_CONE_COUNT; i++)
{
@ -86,7 +91,7 @@ void main() {
float aniso_colors[6];
#endif
ivec3 src = ivec3(gl_GlobalInvocationID.xyz);
src = ivec3(gl_GlobalInvocationID.xyz);
src.x += i * res;
ivec3 dst = src;
dst.y += clipmapLevel * res;
@ -99,67 +104,44 @@ void main() {
if (i < 6) {
#ifdef _VoxelGI
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);
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;
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);
}
//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]);
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;
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;
//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
int count = int(imageLoad(voxels, src + ivec3(0, 0, voxelgiResolution.x)));
if (count > 0) {
opac = float(imageLoad(voxels, src)) / 255;
opac /= count;
}
opac = float(imageLoad(voxels, src)) / 255;
#endif
#ifdef _VoxelGI
@ -213,7 +195,7 @@ void main() {
}
else {
// precompute cone sampling:
vec3 coneDirection = TBN * DIFFUSE_CONE_DIRECTIONS[i - 6];
vec3 coneDirection = DIFFUSE_CONE_DIRECTIONS[i - 6];
vec3 aniso_direction = -coneDirection;
uvec3 face_offsets = uvec3(
aniso_direction.x > 0 ? 0 : 1,
@ -254,4 +236,4 @@ void main() {
imageStore(SDF, dst_sdf, vec4(sdf));
#endif
#endif
}
}

View File

@ -75,17 +75,16 @@ vec4 binarySearch(vec3 dir) {
}
vec4 rayCast(vec3 dir) {
float ddepth;
dir *= ss_refractionRayStep;
for (int i = 0; i < maxSteps; i++) {
hitCoord += dir;
ddepth = getDeltaDepth(hitCoord);
if (ddepth > 0.0)
return binarySearch(dir);
}
// No hit — fallback to projecting the ray to UV space
vec2 fallbackUV = getProjectedCoord(hitCoord);
return vec4(fallbackUV, 0.0, 0.5); // We set .w lower to indicate fallback
#ifdef _CPostprocess
dir *= PPComp9.x;
#else
dir *= ssrRayStep;
#endif
for (int i = 0; i < maxSteps; i++) {
hitCoord += dir;
if (getDeltaDepth(hitCoord) > 0.0) return binarySearch(dir);
}
return vec4(0.0);
}
#endif //SSR

View File

@ -54,6 +54,22 @@ class App {
if (Scene.active == null || !Scene.active.ready) return;
iron.system.Time.update();
if (lastw == -1) {
lastw = App.w();
lasth = App.h();
}
if (lastw != App.w() || lasth != App.h()) {
if (onResize != null) onResize();
else {
if (Scene.active != null && Scene.active.camera != null) {
Scene.active.camera.buildProjection();
}
}
}
lastw = App.w();
lasth = App.h();
if (pauseUpdates) return;
#if lnx_debug
@ -98,22 +114,6 @@ class App {
for (cb in endFrameCallbacks) cb();
updateTime = kha.Scheduler.realTime() - startTime;
#end
// Rebuild projection on window resize
if (lastw == -1) {
lastw = App.w();
lasth = App.h();
}
if (lastw != App.w() || lasth != App.h()) {
if (onResize != null) onResize();
else {
if (Scene.active != null && Scene.active.camera != null) {
Scene.active.camera.buildProjection();
}
}
}
lastw = App.w();
lasth = App.h();
}
static function render(frames: Array<kha.Framebuffer>) {

View File

@ -331,15 +331,18 @@ class RenderPath {
});
}
public static function sortMeshesShader(meshes: Array<MeshObject>) {
public static function sortMeshesIndex(meshes: Array<MeshObject>) {
meshes.sort(function(a, b): Int {
#if rp_depth_texture
var depthDiff = boolToInt(a.depthRead) - boolToInt(b.depthRead);
if (depthDiff != 0) return depthDiff;
#end
return a.materials[0].name >= b.materials[0].name ? 1 : -1;
});
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; });
}
public function drawMeshes(context: String) {
@ -399,7 +402,7 @@ class RenderPath {
#if lnx_batch
sortMeshesDistance(Scene.active.meshBatch.nonBatched);
#else
drawOrder == DrawOrder.Shader ? sortMeshesShader(meshes) : sortMeshesDistance(meshes);
drawOrder == DrawOrder.Index ? sortMeshesIndex(meshes) : sortMeshesDistance(meshes);
#end
meshesSorted = true;
}
@ -914,6 +917,6 @@ class CachedShaderContext {
@:enum abstract DrawOrder(Int) from Int {
var Distance = 0; // Early-z
var Shader = 1; // Less state changes
var Index = 1; // Less state changes
// var Mix = 2; // Distance buckets sorted by shader
}

View File

@ -887,8 +887,12 @@ class Scene {
var ptype: String = t.props[i * 3 + 1];
var pval: Dynamic = t.props[i * 3 + 2];
if (StringTools.endsWith(ptype, "Object") && pval != "") {
if (StringTools.endsWith(ptype, "Object") && pval != "" && pval != null) {
Reflect.setProperty(traitInst, pname, Scene.active.getChild(pval));
} else if (ptype == "TSceneFormat" && pval != "") {
Data.getSceneRaw(pval, function (r: TSceneFormat) {
Reflect.setProperty(traitInst, pname, r);
});
}
else {
switch (ptype) {

View File

@ -9,6 +9,7 @@ import iron.data.SceneFormat;
class MeshData {
public var name: String;
public var sortingIndex: Int;
public var raw: TMeshData;
public var format: TSceneFormat;
public var geom: Geometry;
@ -23,7 +24,8 @@ class MeshData {
public function new(raw: TMeshData, done: MeshData->Void) {
this.raw = raw;
this.name = raw.name;
this.sortingIndex = raw.sorting_index;
if (raw.scale_pos != null) scalePos = raw.scale_pos;
if (raw.scale_tex != null) scaleTex = raw.scale_tex;

View File

@ -49,6 +49,7 @@ typedef TMeshData = {
@:structInit class TMeshData {
#end
public var name: String;
public var sorting_index: Int;
public var vertex_arrays: Array<TVertexArray>;
public var index_arrays: Array<TIndexArray>;
@:optional public var dynamic_usage: Null<Bool>;
@ -222,6 +223,7 @@ typedef TShaderData = {
@:structInit class TShaderData {
#end
public var name: String;
public var next_pass: String;
public var contexts: Array<TShaderContext>;
}
@ -393,6 +395,7 @@ typedef TParticleData = {
public var name: String;
public var type: Int; // 0 - Emitter, Hair
public var auto_start: Bool;
public var dynamic_emitter: Bool;
public var is_unique: Bool;
public var loop: Bool;
public var count: Int;

View File

@ -22,6 +22,7 @@ using StringTools;
class ShaderData {
public var name: String;
public var nextPass: String;
public var raw: TShaderData;
public var contexts: Array<ShaderContext> = [];
@ -33,6 +34,7 @@ class ShaderData {
public function new(raw: TShaderData, done: ShaderData->Void, overrideContext: TShaderOverride = null) {
this.raw = raw;
this.name = raw.name;
this.nextPass = raw.next_pass;
for (c in raw.contexts) contexts.push(null);
var contextsLoaded = 0;

View File

@ -302,6 +302,10 @@ class MeshObject extends Object {
// Render mesh
var ldata = lod.data;
// Next pass rendering first (inverse order)
renderNextPass(g, context, bindParams, lod);
for (i in 0...ldata.geom.indexBuffers.length) {
var mi = ldata.geom.materialIndices[i];
@ -405,4 +409,85 @@ class MeshObject extends Object {
}
}
}
function renderNextPass(g: Graphics, context: String, bindParams: Array<String>, lod: MeshObject) {
var ldata = lod.data;
for (i in 0...ldata.geom.indexBuffers.length) {
var mi = ldata.geom.materialIndices[i];
if (mi >= materials.length) continue;
var currentMaterial: MaterialData = materials[mi];
if (currentMaterial == null || currentMaterial.shader == null) continue;
var nextPassName: String = currentMaterial.shader.nextPass;
if (nextPassName == null || nextPassName == "") continue;
var nextMaterial: MaterialData = null;
for (mat in materials) {
// First try exact match
if (mat.name == nextPassName) {
nextMaterial = mat;
break;
}
// If no exact match, try to match base name for linked materials
if (mat.name.indexOf("_") > 0 && mat.name.substr(mat.name.length - 6) == ".blend") {
var baseName = mat.name.substring(0, mat.name.indexOf("_"));
if (baseName == nextPassName) {
nextMaterial = mat;
break;
}
}
}
if (nextMaterial == null) continue;
var nextMaterialContext: MaterialContext = null;
var nextShaderContext: ShaderContext = null;
for (j in 0...nextMaterial.raw.contexts.length) {
if (nextMaterial.raw.contexts[j].name.substr(0, context.length) == context) {
nextMaterialContext = nextMaterial.contexts[j];
nextShaderContext = nextMaterial.shader.getContext(context);
break;
}
}
if (nextShaderContext == null) continue;
if (skipContext(context, nextMaterial)) continue;
var elems = nextShaderContext.raw.vertex_elements;
// Uniforms
if (nextShaderContext.pipeState != lastPipeline) {
g.setPipeline(nextShaderContext.pipeState);
lastPipeline = nextShaderContext.pipeState;
}
Uniforms.setContextConstants(g, nextShaderContext, bindParams);
Uniforms.setObjectConstants(g, nextShaderContext, this);
Uniforms.setMaterialConstants(g, nextShaderContext, nextMaterialContext);
// VB / IB
#if lnx_deinterleaved
g.setVertexBuffers(ldata.geom.get(elems));
#else
if (ldata.geom.instancedVB != null) {
g.setVertexBuffers([ldata.geom.get(elems), ldata.geom.instancedVB]);
}
else {
g.setVertexBuffer(ldata.geom.get(elems));
}
#end
g.setIndexBuffer(ldata.geom.indexBuffers[i]);
// Draw next pass for this specific geometry section
if (ldata.geom.instanced) {
g.drawIndexedVerticesInstanced(ldata.geom.instanceCount, ldata.geom.start, ldata.geom.count);
}
else {
g.drawIndexedVertices(ldata.geom.start, ldata.geom.count);
}
}
}
}

View File

@ -24,6 +24,9 @@ class ObjectAnimation extends Animation {
public var transformMap: Map<String, FastFloat>;
var defaultSampler: ActionSampler = null;
static inline var DEFAULT_SAMPLER_ID = "__object_default_action__";
public static var trackNames: Array<String> = [ "xloc", "yloc", "zloc",
"xrot", "yrot", "zrot",
"qwrot", "qxrot", "qyrot", "qzrot",
@ -39,7 +42,6 @@ class ObjectAnimation extends Animation {
isSkinned = false;
super();
}
function getAction(action: String): TObj {
for (a in oactions) if (a != null && a.objects[0].name == action) return a.objects[0];
return null;
@ -47,10 +49,29 @@ class ObjectAnimation extends Animation {
override public function play(action = "", onComplete: Void->Void = null, blendTime = 0.0, speed = 1.0, loop = true) {
super.play(action, onComplete, blendTime, speed, loop);
if (this.action == "" && oactions[0] != null) this.action = oactions[0].objects[0].name;
if (this.action == "" && oactions != null && oactions[0] != null){
this.action = oactions[0].objects[0].name;
}
oaction = getAction(this.action);
if (oaction != null) {
isSampled = oaction.sampled != null && oaction.sampled;
if (defaultSampler != null) {
deRegisterAction(DEFAULT_SAMPLER_ID);
}
var callbacks = onComplete != null ? [onComplete] : null;
defaultSampler = new ActionSampler(this.action, speed, loop, false, callbacks);
registerAction(DEFAULT_SAMPLER_ID, defaultSampler);
if (paused) defaultSampler.paused = true;
updateAnimation = function(map: Map<String, FastFloat>) {
sampleAction(defaultSampler, map);
};
}
else {
if (defaultSampler != null) {
deRegisterAction(DEFAULT_SAMPLER_ID);
defaultSampler = null;
}
updateAnimation = null;
}
}
@ -61,12 +82,13 @@ class ObjectAnimation extends Animation {
Animation.beginProfile();
#end
if(transformMap == null) transformMap = new Map();
if (transformMap == null) transformMap = new Map();
transformMap = initTransformMap();
super.update(delta);
if (defaultSampler != null) defaultSampler.paused = paused;
if (paused) return;
if(updateAnimation == null) return;
if (updateAnimation == null) return;
if (!isSkinned) updateObjectAnimation();
#if lnx_debug

View File

@ -8,6 +8,8 @@ import kha.arrays.Float32Array;
import iron.data.Data;
import iron.data.ParticleData;
import iron.data.SceneFormat;
import iron.data.Geometry;
import iron.data.MeshData;
import iron.system.Time;
import iron.math.Mat4;
import iron.math.Quat;
@ -17,6 +19,7 @@ import iron.math.Vec4;
class ParticleSystem {
public var data: ParticleData;
public var speed = 1.0;
public var dynamicEmitter: Bool = true;
var currentSpeed = 0.0;
var particles: Array<Particle>;
var ready: Bool;
@ -52,6 +55,12 @@ class ParticleSystem {
var random = 0.0;
var tmpV4 = new Vec4();
var instancedData: Float32Array = null;
var lastSpawnedCount: Int = 0;
var hasUniqueGeom: Bool = false;
public function new(sceneName: String, pref: TParticleReference) {
seed = pref.seed;
currentSpeed = speed;
@ -62,6 +71,12 @@ class ParticleSystem {
Data.getParticle(sceneName, pref.particle, function(b: ParticleData) {
data = b;
r = data.raw;
var dyn: Null<Bool> = r.dynamic_emitter;
var dynValue: Bool = true;
if (dyn != null) {
dynValue = dyn;
}
dynamicEmitter = dynValue;
if (Scene.active.raw.gravity != null) {
gx = Scene.active.raw.gravity[0] * r.weight_gravity;
gy = Scene.active.raw.gravity[1] * r.weight_gravity;
@ -98,6 +113,8 @@ class ParticleSystem {
lap = 0;
lapTime = 0;
speed = currentSpeed;
lastSpawnedCount = 0;
instancedData = null;
}
public function pause() {
@ -130,8 +147,13 @@ class ParticleSystem {
// Copy owner world transform but discard scale
owner.transform.world.decompose(ownerLoc, ownerRot, ownerScl);
object.transform.loc = ownerLoc;
object.transform.rot = ownerRot;
if (dynamicEmitter) {
object.transform.loc.x = 0; object.transform.loc.y = 0; object.transform.loc.z = 0;
object.transform.rot = new Quat();
} else {
object.transform.loc = ownerLoc;
object.transform.rot = ownerRot;
}
// Set particle size per particle system
object.transform.scale = new Vec4(r.particle_size, r.particle_size, r.particle_size, 1);
@ -158,13 +180,18 @@ class ParticleSystem {
if (lap > prevLap && !r.loop) {
end();
}
if (lap > prevLap && r.loop) {
lastSpawnedCount = 0;
}
updateGpu(object, owner);
}
public function getData(): Mat4 {
var hair = r.type == 1;
m._00 = animtime;
// Store loop flag in the sign: positive -> loop, negative -> no loop
m._00 = r.loop ? animtime : -animtime;
m._01 = hair ? 1 / particles.length : spawnRate;
m._02 = hair ? 1 : lifetime;
m._03 = particles.length;
@ -187,17 +214,26 @@ class ParticleSystem {
return r.size_random;
}
public function getRandom(): FastFloat {
public inline function getRandom(): FastFloat {
return random;
}
public function getSize(): FastFloat {
public inline function getSize(): FastFloat {
return r.particle_size;
}
function updateGpu(object: MeshObject, owner: MeshObject) {
if (!object.data.geom.instanced) setupGeomGpu(object, owner);
// GPU particles transform is attached to owner object
if (dynamicEmitter) {
if (!hasUniqueGeom) ensureUniqueGeom(object);
var needSetup = instancedData == null || object.data.geom.instancedVB == null;
if (needSetup) setupGeomGpuDynamic(object, owner);
updateSpawnedInstances(object, owner);
}
else {
if (!hasUniqueGeom) ensureUniqueGeom(object);
if (!object.data.geom.instanced) setupGeomGpu(object, owner);
}
// GPU particles transform is attached to owner object in static mode
}
function setupGeomGpu(object: MeshObject, owner: MeshObject) {
@ -258,18 +294,134 @@ class ParticleSystem {
object.data.geom.setupInstanced(instancedData, 1, Usage.StaticUsage);
}
function fhash(n: Int): Float {
var s = n + 1.0;
s *= 9301.0 % s;
s = (s * 9301.0 + 49297.0) % 233280.0;
return s / 233280.0;
// allocate instanced VB once for this object
function setupGeomGpuDynamic(object: MeshObject, owner: MeshObject) {
if (instancedData == null) instancedData = new Float32Array(particles.length * 3);
lastSpawnedCount = 0;
// Create instanced VB once if missing (seed with our instancedData)
if (object.data.geom.instancedVB == null) {
object.data.geom.setupInstanced(instancedData, 1, Usage.DynamicUsage);
}
}
function ensureUniqueGeom(object: MeshObject) {
if (hasUniqueGeom) return;
var newData: MeshData = null;
new MeshData(object.data.raw, function(dat: MeshData) {
dat.scalePos = object.data.scalePos;
dat.scaleTex = object.data.scaleTex;
dat.format = object.data.format;
newData = dat;
});
if (newData != null) object.setData(newData);
hasUniqueGeom = true;
}
function updateSpawnedInstances(object: MeshObject, owner: MeshObject) {
if (instancedData == null) return;
var targetCount = count;
if (targetCount > particles.length) targetCount = particles.length;
if (targetCount <= lastSpawnedCount) return;
var normFactor = 1 / 32767;
var scalePosOwner = owner.data.scalePos;
var scalePosParticle = object.data.scalePos;
var particleSize = r.particle_size;
var base = 1.0 / (particleSize * scalePosParticle);
switch (r.emit_from) {
case 0: // Vert
var pa = owner.data.geom.positions;
var osx = owner.transform.scale.x;
var osy = owner.transform.scale.y;
var osz = owner.transform.scale.z;
var pCount = Std.int(pa.values.length / pa.size);
for (idx in lastSpawnedCount...targetCount) {
var j = Std.int(fhash(idx) * pCount);
var lx = pa.values[j * pa.size ] * normFactor * scalePosOwner * osx;
var ly = pa.values[j * pa.size + 1] * normFactor * scalePosOwner * osy;
var lz = pa.values[j * pa.size + 2] * normFactor * scalePosOwner * osz;
tmpV4.x = lx; tmpV4.y = ly; tmpV4.z = lz; tmpV4.w = 1;
tmpV4.applyQuat(ownerRot);
var o = idx * 3;
instancedData.set(o , (tmpV4.x + ownerLoc.x) * base);
instancedData.set(o + 1, (tmpV4.y + ownerLoc.y) * base);
instancedData.set(o + 2, (tmpV4.z + ownerLoc.z) * base);
}
case 1: // Face
var positions = owner.data.geom.positions.values;
var osx1 = owner.transform.scale.x;
var osy1 = owner.transform.scale.y;
var osz1 = owner.transform.scale.z;
for (idx in lastSpawnedCount...targetCount) {
var ia = owner.data.geom.indices[Std.random(owner.data.geom.indices.length)];
var faceIndex = Std.random(Std.int(ia.length / 3));
var i0 = ia[faceIndex * 3 + 0];
var i1 = ia[faceIndex * 3 + 1];
var i2 = ia[faceIndex * 3 + 2];
var v0x = positions[i0 * 4 ], v0y = positions[i0 * 4 + 1], v0z = positions[i0 * 4 + 2];
var v1x = positions[i1 * 4 ], v1y = positions[i1 * 4 + 1], v1z = positions[i1 * 4 + 2];
var v2x = positions[i2 * 4 ], v2y = positions[i2 * 4 + 1], v2z = positions[i2 * 4 + 2];
var rx = Math.random(); var ry = Math.random(); if (rx + ry > 1) { rx = 1 - rx; ry = 1 - ry; }
var pxs = v0x + rx * (v1x - v0x) + ry * (v2x - v0x);
var pys = v0y + rx * (v1y - v0y) + ry * (v2y - v0y);
var pzs = v0z + rx * (v1z - v0z) + ry * (v2z - v0z);
var px = pxs * normFactor * scalePosOwner * osx1;
var py = pys * normFactor * scalePosOwner * osy1;
var pz = pzs * normFactor * scalePosOwner * osz1;
tmpV4.x = px; tmpV4.y = py; tmpV4.z = pz; tmpV4.w = 1;
tmpV4.applyQuat(ownerRot);
var o1 = idx * 3;
instancedData.set(o1 , (tmpV4.x + ownerLoc.x) * base);
instancedData.set(o1 + 1, (tmpV4.y + ownerLoc.y) * base);
instancedData.set(o1 + 2, (tmpV4.z + ownerLoc.z) * base);
}
case 2: // Volume
var dim = object.transform.dim;
for (idx in lastSpawnedCount...targetCount) {
tmpV4.x = (Math.random() * 2.0 - 1.0) * (dim.x * 0.5);
tmpV4.y = (Math.random() * 2.0 - 1.0) * (dim.y * 0.5);
tmpV4.z = (Math.random() * 2.0 - 1.0) * (dim.z * 0.5);
tmpV4.w = 1;
tmpV4.applyQuat(ownerRot);
var o2 = idx * 3;
instancedData.set(o2 , (tmpV4.x + ownerLoc.x) * base);
instancedData.set(o2 + 1, (tmpV4.y + ownerLoc.y) * base);
instancedData.set(o2 + 2, (tmpV4.z + ownerLoc.z) * base);
}
}
// Upload full active range [0..targetCount) to this object's instanced VB
var geom = object.data.geom;
if (geom.instancedVB == null) {
geom.setupInstanced(instancedData, 1, Usage.DynamicUsage);
}
var vb = geom.instancedVB.lock();
var totalFloats = targetCount * 3; // xyz per instance
var i = 0;
while (i < totalFloats) {
vb.setFloat32(i * 4, instancedData[i]);
i++;
}
geom.instancedVB.unlock();
geom.instanceCount = targetCount;
lastSpawnedCount = targetCount;
}
inline function fhash(n: Int): Float {
var s = n + 1.0;
s *= 9301.0 % s;
s = (s * 9301.0 + 49297.0) % 233280.0;
return s / 233280.0;
}
public function remove() {}
/**
Generates a random point in the triangle with vertex positions abc.
Please note that the given position vectors are changed in-place by this
function and can be considered garbage afterwards, so make sure to clone
them first if needed.

View File

@ -181,15 +181,11 @@ 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.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);
g.setTextureParameters(context.textureUnits[j], TextureAddressing.Clamp, TextureAddressing.Clamp, TextureFilter.LinearFilter, TextureFilter.LinearFilter, 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.PointMipFilter);
g.setTexture3DParameters(context.textureUnits[j], TextureAddressing.Clamp, TextureAddressing.Clamp, TextureAddressing.Clamp, TextureFilter.LinearFilter, TextureFilter.LinearFilter, MipMapFilter.NoMipFilter);
}
else
{

View File

@ -14,7 +14,7 @@ class Time {
return 1 / frequency;
}
static var _fixedStep: Null<Float>;
static var _fixedStep: Null<Float> = 1/60;
public static var fixedStep(get, never): Float;
static function get_fixedStep(): Float {
return _fixedStep;
@ -39,11 +39,11 @@ class Time {
}
public static inline function time(): Float {
return kha.Scheduler.time();
return kha.Scheduler.time() * scale;
}
public static inline function realTime(): Float {
return kha.Scheduler.realTime();
return kha.Scheduler.realTime() * scale;
}
public static function update() {

View File

@ -94,34 +94,34 @@ class Tween {
// Way too much Reflect trickery..
var ps = Reflect.fields(a.props);
for (i in 0...ps.length) {
var p = ps[i];
for (j in 0...ps.length) {
var p = ps[j];
var k = a._time / a.duration;
if (k > 1) k = 1;
if (a._comps[i] == 1) {
var fromVal: Float = a._x[i];
if (a._comps[j] == 1) {
var fromVal: Float = a._x[j];
var toVal: Float = Reflect.getProperty(a.props, p);
var val: Float = fromVal + (toVal - fromVal) * eases[a.ease](k);
Reflect.setProperty(a.target, p, val);
}
else { // _comps[i] == 4
else { // _comps[j] == 4
var obj = Reflect.getProperty(a.props, p);
var toX: Float = Reflect.getProperty(obj, "x");
var toY: Float = Reflect.getProperty(obj, "y");
var toZ: Float = Reflect.getProperty(obj, "z");
var toW: Float = Reflect.getProperty(obj, "w");
if (a._normalize[i]) {
var qdot = (a._x[i] * toX) + (a._y[i] * toY) + (a._z[i] * toZ) + (a._w[i] * toW);
if (a._normalize[j]) {
var qdot = (a._x[j] * toX) + (a._y[j] * toY) + (a._z[j] * toZ) + (a._w[j] * toW);
if (qdot < 0.0) {
toX = -toX; toY = -toY; toZ = -toZ; toW = -toW;
}
}
var x: Float = a._x[i] + (toX - a._x[i]) * eases[a.ease](k);
var y: Float = a._y[i] + (toY - a._y[i]) * eases[a.ease](k);
var z: Float = a._z[i] + (toZ - a._z[i]) * eases[a.ease](k);
var w: Float = a._w[i] + (toW - a._w[i]) * eases[a.ease](k);
if (a._normalize[i]) {
var x: Float = a._x[j] + (toX - a._x[j]) * eases[a.ease](k);
var y: Float = a._y[j] + (toY - a._y[j]) * eases[a.ease](k);
var z: Float = a._z[j] + (toZ - a._z[j]) * eases[a.ease](k);
var w: Float = a._w[j] + (toW - a._w[j]) * eases[a.ease](k);
if (a._normalize[j]) {
var l = Math.sqrt(x * x + y * y + z * z + w * w);
if (l > 0.0) {
l = 1.0 / l;

View File

@ -0,0 +1,48 @@
package leenkx.logicnode;
import iron.object.Object;
#if lnx_physics
import leenkx.trait.physics.PhysicsCache;
import leenkx.trait.physics.RigidBody;
#end
class AnyContactNode extends LogicNode {
public var property0: String;
var lastContact = false;
public function new(tree: LogicTree) {
super(tree);
tree.notifyOnUpdate(update);
}
function update() {
var object1: Object = inputs[0].get();
if (object1 == null) object1 = tree.object;
if (object1 == null) return;
var contact = false;
#if lnx_physics
var rb1 = PhysicsCache.getCachedRigidBody(object1);
if (rb1 != null) {
var rbs = PhysicsCache.getCachedContacts(rb1);
contact = (rbs != null && rbs.length > 0);
}
#end
var shouldTrigger = false;
switch (property0) {
case "begin":
shouldTrigger = contact && !lastContact;
case "overlap":
shouldTrigger = contact;
case "end":
shouldTrigger = !contact && lastContact;
}
lastContact = contact;
if (shouldTrigger) runOutput(0);
}
}

View File

@ -62,7 +62,7 @@ class DrawStringNode extends LogicNode {
override function get(from: Int): Dynamic {
return from == 1 ? RenderToTexture.g.font.height(RenderToTexture.g.fontSize) : RenderToTexture.g.font.width(RenderToTexture.g.fontSize, string);
return from == 1 ? RenderToTexture.g.font.width(RenderToTexture.g.fontSize, string) : RenderToTexture.g.font.height(RenderToTexture.g.fontSize);
}
}

View File

@ -1,7 +1,10 @@
package leenkx.logicnode;
import iron.object.Object;
#if lnx_physics
import leenkx.trait.physics.PhysicsCache;
import leenkx.trait.physics.RigidBody;
#end
class HasContactNode extends LogicNode {
@ -15,12 +18,15 @@ class HasContactNode extends LogicNode {
if (object1 == null || object2 == null) return false;
#if lnx_physics
var physics = leenkx.trait.physics.PhysicsWorld.active;
var rb2 = object2.getTrait(RigidBody);
var rbs = physics.getContacts(object1.getTrait(RigidBody));
if (rbs != null) for (rb in rbs) if (rb == rb2) return true;
#end
#if lnx_physics
var rb1 = PhysicsCache.getCachedRigidBody(object1);
var rb2 = PhysicsCache.getCachedRigidBody(object2);
if (rb1 != null && rb2 != null) {
var rbs = PhysicsCache.getCachedContacts(rb1);
return PhysicsCache.hasContactWith(rbs, rb2);
}
#end
return false;
}
}

View File

@ -1,7 +1,11 @@
package leenkx.logicnode;
import iron.object.Object;
#if lnx_physics
import leenkx.trait.physics.PhysicsCache;
import leenkx.trait.physics.RigidBody;
#end
class OnContactNode extends LogicNode {
@ -23,22 +27,15 @@ class OnContactNode extends LogicNode {
var contact = false;
#if lnx_physics
var physics = leenkx.trait.physics.PhysicsWorld.active;
var rb1 = object1.getTrait(RigidBody);
if (rb1 != null) {
var rbs = physics.getContacts(rb1);
if (rbs != null) {
var rb2 = object2.getTrait(RigidBody);
for (rb in rbs) {
if (rb == rb2) {
contact = true;
break;
}
}
#if lnx_physics
var rb1 = PhysicsCache.getCachedRigidBody(object1);
var rb2 = PhysicsCache.getCachedRigidBody(object2);
if (rb1 != null && rb2 != null) {
var rbs = PhysicsCache.getCachedContacts(rb1);
contact = PhysicsCache.hasContactWith(rbs, rb2);
}
}
#end
#end
var b = false;
switch (property0) {

View File

@ -0,0 +1,41 @@
package leenkx.logicnode;
class ProbabilisticIndexNode extends LogicNode {
public function new(tree: LogicTree) {
super(tree);
}
override function get(from: Int): Dynamic {
var probs: Array<Float> = [];
var probs_acum: Array<Float> = [];
var sum: Float = 0;
for (p in 0...inputs.length){
probs.push(inputs[p].get());
sum += probs[p];
}
if (sum > 1){
for (p in 0...probs.length)
probs[p] /= sum;
}
sum = 0;
for (p in 0...probs.length){
sum += probs[p];
probs_acum.push(sum);
}
var rand: Float = Math.random();
for (p in 0...probs.length){
if (p == 0 && rand <= probs_acum[p]) return p;
else if (0 < p && p < probs.length-1 && probs_acum[p-1] < rand && rand <= probs_acum[p]) return p;
else if (p == probs.length-1 && probs_acum[p-1] < rand) return p;
}
return null;
}
}

View File

@ -1,5 +1,7 @@
package leenkx.logicnode;
import iron.data.SceneFormat;
class SetWorldNode extends LogicNode {
public function new(tree: LogicTree) {
@ -10,25 +12,6 @@ class SetWorldNode extends LogicNode {
var world: String = inputs[1].get();
if (world != null){
//check if world shader data exists
var file: String = 'World_'+world+'_data';
#if lnx_json
file += ".json";
#elseif lnx_compress
file += ".lz4";
#else
file += '.lnx';
#end
var exists: Bool = false;
iron.data.Data.getBlob(file, function(b: kha.Blob) {
if (b != null) exists = true;
});
assert(Error, exists == true, "World must be either associated to a scene or have fake user");
iron.Scene.active.raw.world_ref = world;
var npath = leenkx.renderpath.RenderPathCreator.get();
npath.loadShader("shader_datas/World_" + world + "/World_" + world);

View File

@ -34,10 +34,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_cc1:kha.compute.ConstantLocation;
static var voxel_tf1:kha.compute.TextureUnit;
#else
#if lnx_voxelgi_shadows
static var voxel_te1:kha.compute.TextureUnit;
static var voxel_tf1:kha.compute.TextureUnit;
#end
#end
#if (lnx_voxelgi_shadows || rp_voxels == "Voxel GI")
@ -53,28 +53,12 @@ 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;
@ -87,6 +71,33 @@ 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
@ -152,11 +163,9 @@ class Inc {
for (atlas in ShadowMapAtlas.shadowMapAtlases) {
path.bindTarget(atlas.target, atlas.target);
}
#if rp_shadowmap_transparent
for (atlas in ShadowMapAtlas.shadowMapAtlasesTransparent) {
path.bindTarget(atlas.target, atlas.target);
}
#end
}
static function getShadowMapAtlas(atlas:ShadowMapAtlas, transparent: Bool):String {
@ -197,30 +206,24 @@ 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(false);
#if rp_shadowmap_transparent
updatePointLightAtlasData(true);
#end
updatePointLightAtlasData(false);
for (atlas in ShadowMapAtlas.shadowMapAtlases) {
var tilesToRemove = [];
@ -298,7 +301,6 @@ class Inc {
path.endStream();
}
#if rp_shadowmap_transparent
for (atlas in ShadowMapAtlas.shadowMapAtlasesTransparent) {
var tilesToRemove = [];
#if lnx_shadowmap_atlas_lod
@ -393,8 +395,10 @@ class Inc {
tile.freeTile();
}
}
#if lnx_debug
endShadowsLogicProfile();
#end
#end
#end // rp_shadowmap
}
#else
public static function bindShadowMap() {
@ -497,7 +501,6 @@ 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) {
@ -519,7 +522,6 @@ 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
@ -587,7 +589,7 @@ class Inc {
t.width = 0;
t.height = 0;
t.displayp = getDisplayp();
t.format = "R16";
t.format = "R32";
t.scale = getSuperSampling();
t.depth_buffer = "main";
path.createRenderTarget(t);
@ -613,14 +615,10 @@ class Inc {
#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");
{
path.bindTarget("voxelsOut", "voxels");
path.bindTarget("voxelsSDF", "voxelsSDF");
}
#end
path.drawMeshes("translucent");
@ -681,11 +679,12 @@ class Inc {
t.width = 0;
t.height = 0;
t.displayp = getDisplayp();
t.format = "RGBA32";
//t.scale = Inc.getSuperSampling();
t.format = t.name == "voxels_ao" ? "R8" : "RGBA32";
}
else {
if (t.name == "voxelsSDF" || t.name == "voxelsSDFtmp") {
t.format = "R8";
t.format = "R16";
t.width = res;
t.height = res * Main.voxelgiClipmapCount;
t.depth = res;
@ -694,16 +693,16 @@ class Inc {
#if (rp_voxels == "Voxel AO")
{
if (t.name == "voxelsOut" || t.name == "voxelsOutB") {
t.format = "R8";
t.format = "R16";
t.width = res * (6 + 16);
t.height = res * Main.voxelgiClipmapCount;
t.depth = res;
}
else {
t.format = "R32UI";
t.format = "R32";
t.width = res * 6;
t.height = res;
t.depth = res * 2;
t.depth = res;
}
}
#else
@ -714,11 +713,17 @@ 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 = "R32UI";
t.format = "R32";
t.width = res * 6;
t.height = res;
t.depth = res * 16;
t.depth = res * 12;
}
}
#end
@ -830,15 +835,14 @@ 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("SDF");
voxel_cc1 = voxel_sh1.getConstantLocation("envmapStrength");
voxel_te1 = voxel_sh1.getTextureUnit("voxelsLight");
voxel_tf1 = voxel_sh1.getTextureUnit("SDF");
#else
#if lnx_voxelgi_shadows
voxel_te1 = voxel_sh1.getTextureUnit("SDF");
voxel_tf1 = voxel_sh1.getTextureUnit("SDF");
#end
#end
}
@ -869,28 +873,12 @@ class Inc {
#else
voxel_td3 = voxel_sh3.getTextureUnit("voxels_diffuse");
#end
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_ca3 = voxel_sh3.getConstantLocation("clipmaps");
voxel_cb3 = voxel_sh3.getConstantLocation("InvVP");
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
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");
}
#if (rp_voxels == "Voxel GI")
if (voxel_sh4 == null)
@ -904,8 +892,40 @@ 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("eye");
voxel_cd4 = voxel_sh4.getConstantLocation("postprocess_resolution");
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
}
#end
}
@ -956,11 +976,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("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);
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);
#else
#if lnx_voxelgi_shadows
kha.compute.Compute.setTexture(voxel_te1, rts.get("voxelsSDF").image, kha.compute.Access.Write);
kha.compute.Compute.setTexture(voxel_tf1, rts.get("voxelsSDF").image, kha.compute.Access.Write);
#end
#end
@ -982,8 +1002,6 @@ 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));
}
@ -1036,7 +1054,6 @@ class Inc {
}
}
#end
#if (rp_voxels == "Voxel AO")
public static function resolveAO() {
var rts = path.renderTargets;
@ -1049,20 +1066,13 @@ 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;
@ -1089,7 +1099,18 @@ class Inc {
kha.compute.Compute.setMatrix(voxel_cb3, m.self);
kha.compute.Compute.setFloat3(voxel_cc3, camera.transform.worldx(), camera.transform.worldy(), camera.transform.worldz());
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);
var width = iron.App.w();
var height = iron.App.h();
@ -1104,32 +1125,7 @@ class Inc {
width = Std.int(dp * Inc.getSuperSampling());
}
}
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.setFloat2(voxel_cf3, width, height);
kha.compute.Compute.compute(Std.int((width + 7) / 8), Std.int((height + 7) / 8), 1);
}
@ -1145,18 +1141,12 @@ 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) {
@ -1184,7 +1174,18 @@ class Inc {
kha.compute.Compute.setMatrix(voxel_cb3, m.self);
kha.compute.Compute.setFloat3(voxel_cc3, camera.transform.worldx(), camera.transform.worldy(), camera.transform.worldz());
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);
var width = iron.App.w();
var height = iron.App.h();
@ -1199,32 +1200,7 @@ class Inc {
width = Std.int(dp * Inc.getSuperSampling());
}
}
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.setFloat2(voxel_cf3, width, height);
kha.compute.Compute.compute(Std.int((width + 7) / 8), Std.int((height + 7) / 8), 1);
}
@ -1240,12 +1216,15 @@ 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);
#if rp_gbuffer2
kha.compute.Compute.setSampledTexture(voxel_tf4, rts.get("gbuffer2").image);
#end
//kha.compute.Compute.setSampledTexture(voxel_tf4, rts.get("gbuffer2").image);
var fa:Float32Array = new Float32Array(Main.voxelgiClipmapCount * 10);
for (i in 0...Main.voxelgiClipmapCount) {
@ -1273,7 +1252,18 @@ class Inc {
kha.compute.Compute.setMatrix(voxel_cb4, m.self);
kha.compute.Compute.setFloat3(voxel_cc4, camera.transform.worldx(), camera.transform.worldy(), camera.transform.worldz());
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);
var width = iron.App.w();
var height = iron.App.h();
@ -1288,10 +1278,146 @@ class Inc {
width = Std.int(dp * Inc.getSuperSampling());
}
}
kha.compute.Compute.setFloat2(voxel_cd4, width, height);
kha.compute.Compute.setFloat2(voxel_cf4, 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
}

View File

@ -15,11 +15,6 @@ class RenderPathDeferred {
static var bloomUpsampler: Upsampler;
#end
#if (rp_ssgi == "SSGI")
static var ssgitex = "singleb";
static var ssgitexb = "singleb";
#end
public static inline function setTargetMeshes() {
//Always keep the order of render targets the same as defined in compiled.inc
path.setTarget("gbuffer0", [
@ -62,11 +57,12 @@ class RenderPathDeferred {
Inc.initGI("voxels");
Inc.initGI("voxelsOut");
Inc.initGI("voxelsOutB");
#if (rp_voxels == "Voxel GI" || lnx_voxelgi_shadows)
#if (lnx_voxelgi_shadows || rp_voxels == "Voxel GI")
Inc.initGI("voxelsSDF");
Inc.initGI("voxelsSDFtmp");
#end
#if (rp_voxels == "Voxel GI")
Inc.initGI("voxelsLight");
Inc.initGI("voxels_diffuse");
Inc.initGI("voxels_specular");
#else
@ -199,26 +195,16 @@ class RenderPathDeferred {
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 == "SSGI")
{
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");
}
#end
#if (rp_ssgi != "Off")
#if ((rp_ssgi != "Off") || rp_volumetriclight)
{
var t = new RenderTargetRaw();
t.name = "singlea";
t.width = 0;
t.height = 0;
t.displayp = Inc.getDisplayp();
#if (rp_ssgi == "SSGI")
t.format = "RGBA32";
#else
t.format = "R8";
#end
t.scale = Inc.getSuperSampling();
#if rp_ssgi_half
t.scale *= 0.5;
@ -230,66 +216,6 @@ class RenderPathDeferred {
t.width = 0;
t.height = 0;
t.displayp = Inc.getDisplayp();
#if (rp_ssgi == "SSGI")
t.format = "RGBA32";
#else
t.format = "R8";
#end
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();
#if rp_ssgi_half // Do we keep this ?
t.scale *= 0.5;
#end
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();
#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();
#if rp_ssgi_half // Do we keep this ?
t.scale *= 0.5;
#end
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();
#if rp_ssgi_half
@ -442,7 +368,7 @@ class RenderPathDeferred {
t.scale = Inc.getSuperSampling();
path.createRenderTarget(t);
// holds background color
// holds background depth
var t = new RenderTargetRaw();
t.name = "refr";
t.width = 0;
@ -535,7 +461,7 @@ class RenderPathDeferred {
#if (rp_ssrefr || lnx_voxelgi_refract)
{
path.setTarget("gbuffer_refraction");
path.setTarget("gbuffer_refraction"); // Only clear gbuffer0
path.clearTarget(0xffffff00);
}
#end
@ -591,16 +517,30 @@ class RenderPathDeferred {
path.drawShader("shader_datas/downsample_depth/downsample_depth");
#end
#if (rp_shadowmap)
// atlasing is exclusive for now
#if lnx_shadowmap_atlas
Inc.drawShadowMapAtlas();
#else
Inc.drawShadowMap();
#end
#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_ssgi == "SSAO")
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 (leenkx.data.Config.raw.rp_ssgi != false) {
path.setTarget("singlea");
@ -619,43 +559,15 @@ class RenderPathDeferred {
path.drawShader("shader_datas/blur_edge_pass/blur_edge_pass_y");
}
}
#elseif (rp_ssgi == "SSGI")
{
if (leenkx.data.Config.raw.rp_ssgi != false) {
path.setTarget("singlea");
path.bindTarget("_main", "gbufferD");
path.bindTarget("gbuffer0", "gbuffer0");
path.bindTarget("gbuffer1", "gbuffer1");
#if rp_gbuffer_emission
{
path.bindTarget("gbuffer_emission", "gbufferEmission");
}
#end
#if rp_gbuffer2
path.bindTarget("gbuffer2", "sveloc");
#end
#if rp_shadowmap
{
#if lnx_shadowmap_atlas
Inc.bindShadowMapAtlas();
#else
Inc.bindShadowMap();
#end
}
#end
#end
path.drawShader("shader_datas/ssgi_pass/ssgi_pass");
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");
}
}
#if (rp_shadowmap)
// atlasing is exclusive for now
#if lnx_shadowmap_atlas
Inc.drawShadowMapAtlas();
#else
Inc.drawShadowMap();
#end
#end
// Voxels
@ -668,6 +580,9 @@ 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);
@ -679,30 +594,26 @@ 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 (rp_voxels == "Voxel GI")
#if (lnx_voxelgi_shadows || rp_voxels == "Voxel GI")
Inc.computeVoxelsSDF();
#end
@ -717,6 +628,7 @@ class RenderPathDeferred {
}
}
#end
// ---
// Deferred light
// ---
@ -848,9 +760,15 @@ class RenderPathDeferred {
}
#end
#if (rp_translucency && !rp_ssrefr)
{
Inc.drawTranslucency("tex");
}
#end
#if rp_volumetriclight
{
path.setTarget("volumetrica");
path.setTarget("singlea");
path.bindTarget("_main", "gbufferD");
#if lnx_shadowmap_atlas
Inc.bindShadowMapAtlas();
@ -859,16 +777,85 @@ class RenderPathDeferred {
#end
path.drawShader("shader_datas/volumetric_light/volumetric_light");
path.setTarget("volumetricb");
path.bindTarget("volumetrica", "tex");
path.setTarget("singleb");
path.bindTarget("singlea", "tex");
path.drawShader("shader_datas/blur_bilat_pass/blur_bilat_pass_x");
path.setTarget("tex");
path.bindTarget("volumetricb", "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) {
@ -913,88 +900,6 @@ 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_translucency && !rp_ssrefr)
{
Inc.drawTranslucency("tex");
}
#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("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_motionblur == "Camera") || (rp_motionblur == "Object"))
{
if (leenkx.data.Config.raw.rp_motionblur != false) {
@ -1059,12 +964,6 @@ class RenderPathDeferred {
}
#end
#if rp_bloom
{
inline Inc.drawBloom("tex", bloomDownsampler, bloomUpsampler);
}
#end
#if (rp_supersampling == 4)
var framebuffer = "buf";
#else

View File

@ -142,17 +142,16 @@ class RenderPathForward {
t.width = 0;
t.height = 0;
t.displayp = Inc.getDisplayp();
t.format = "DEPTH24";
t.format = "R32";
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 = Inc.getHdrFormat();
t.format = "RGBA64";
t.scale = Inc.getSuperSampling();
path.createRenderTarget(t);
}
@ -201,10 +200,17 @@ class RenderPathForward {
Inc.initGI("voxels");
Inc.initGI("voxelsOut");
Inc.initGI("voxelsOutB");
#if (rp_voxels == "Voxel GI" || lnx_voxelgi_shadows)
#if (lnx_voxelgi_shadows || (rp_voxels == "Voxel GI"))
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();
@ -251,25 +257,18 @@ class RenderPathForward {
#end
#end
#if (rp_volumetriclight || rp_ssgi != "Off")
#if rp_volumetriclight
{
#if (rp_volumetriclight)
path.loadShader("shader_datas/volumetric_light/volumetric_light");
path.loadShader("shader_datas/blur_bilat_pass/blur_bilat_pass_x");
path.loadShader("shader_datas/blur_bilat_blend_pass/blur_bilat_blend_pass_y");
#end
var t = new RenderTargetRaw();
t.name = "singlea";
t.width = 0;
t.height = 0;
t.displayp = Inc.getDisplayp();
#if (rp_ssgi == "SSGI")
t.format = "RGBA32";
#else
t.format = "R8";
#end
t.scale = Inc.getSuperSampling();
path.createRenderTarget(t);
@ -278,11 +277,7 @@ class RenderPathForward {
t.width = 0;
t.height = 0;
t.displayp = Inc.getDisplayp();
#if (rp_ssgi == "SSGI")
t.format = "RGBA32";
#else
t.format = "R8";
#end
t.scale = Inc.getSuperSampling();
path.createRenderTarget(t);
}
@ -379,6 +374,9 @@ 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);
@ -390,6 +388,9 @@ class RenderPathForward {
}
else
{
#if (rp_voxels == "Voxel GI")
path.clearImage("voxelsLight", 0x00000000);
#end
path.clearImage("voxels", 0x00000000);
Inc.computeVoxelsOffsetPrev();
}
@ -399,12 +400,27 @@ 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
@ -423,7 +439,7 @@ class RenderPathForward {
#if (rp_ssrefr || lnx_voxelgi_refract)
{
path.setTarget("gbuffer_refraction"); // Only clear gbuffer0
path.clearTarget(0xffffff00);
path.clearTarget(0xff000000);
}
#end
@ -433,6 +449,13 @@ class RenderPathForward {
}
#end
#if rp_ssrefr
{
path.setTarget("gbuffer_refraction");
path.clearTarget(0xffffff00);
}
#end
RenderPathCreator.setTargetMeshes();
#if rp_shadowmap
@ -449,11 +472,18 @@ class RenderPathForward {
#if (rp_voxels != "Off")
if (leenkx.data.Config.raw.rp_gi != false)
{
#if (rp_voxels != "Off")
path.bindTarget("voxelsOut", "voxels");
#if (rp_voxels == "Voxel GI" || lnx_voxelgi_shadows)
path.bindTarget("voxelsSDF", "voxelsSDF");
#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
path.bindTarget("voxelsOut", "voxels");
path.bindTarget("voxelsSDF", "voxelsSDF");
#end
}
#end
@ -492,31 +522,14 @@ 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.bindTarget("lbuffer0", "tex");
path.bindTarget("refr", "tex1");
path.bindTarget("_main", "gbufferD");
@ -564,50 +577,6 @@ 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("lbuffer0");
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");
}
}
#end
#if rp_bloom
{
inline Inc.drawBloom("lbuffer0", bloomDownsampler, bloomUpsampler);
@ -672,18 +641,20 @@ class RenderPathForward {
var framebuffer = "";
#end
#if ((rp_antialiasing == "Off") || (rp_antialiasing == "FXAA"))
RenderPathCreator.finalTarget = path.currentTarget;
var target = "";
#if ((rp_antialiasing == "Off") || (rp_antialiasing == "FXAA") || (!rp_render_to_texture))
{
RenderPathCreator.finalTarget = path.currentTarget;
path.setTarget(framebuffer);
target = framebuffer;
}
#else
{
path.setTarget("buf");
RenderPathCreator.finalTarget = path.currentTarget;
target = "buf";
}
#end
path.setTarget(target);
#if rp_compositordepth
{
path.bindTarget("_main", "gbufferD");
@ -702,6 +673,15 @@ class RenderPathForward {
}
#end
#if rp_overlays
{
path.setTarget(target);
path.clearTarget(null, 1.0);
path.drawMeshes("overlay");
}
#end
#if ((rp_antialiasing == "SMAA") || (rp_antialiasing == "TAA"))
{
path.setTarget("bufa");
@ -732,12 +712,6 @@ class RenderPathForward {
}
#end
#if rp_overlays
{
path.clearTarget(null, 1.0);
path.drawMeshes("overlay");
}
#end
}
public static function setupDepthTexture() {

View File

@ -3,33 +3,35 @@ package leenkx.system;
import haxe.Constraints.Function;
class Signal {
var callbacks:Array<Function> = [];
var callbacks: Array<Function> = [];
public function new() {
}
public function connect(callback:Function) {
public function connect(callback: Function) {
if (!callbacks.contains(callback)) callbacks.push(callback);
}
public function disconnect(callback:Function) {
public function disconnect(callback: Function) {
if (callbacks.contains(callback)) callbacks.remove(callback);
}
public function emit(...args:Any) {
for (callback in callbacks) Reflect.callMethod(this, callback, args);
public function emit(...args: Any) {
for (callback in callbacks.copy()) {
if (callbacks.contains(callback)) Reflect.callMethod(null, callback, args);
}
}
public function getConnections():Array<Function> {
public function getConnections(): Array<Function> {
return callbacks;
}
public function isConnected(callBack:Function):Bool {
public function isConnected(callBack: Function):Bool {
return callbacks.contains(callBack);
}
public function isNull():Bool {
public function isNull(): Bool {
return callbacks.length == 0;
}
}

View File

@ -41,11 +41,7 @@ class Starter {
try {
#end
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) {
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) {
iron.App.init(function() {
#if lnx_loadscreen
@ -61,7 +57,7 @@ class Starter {
iron.Scene.getRenderPath = getRenderPath;
#end
#if lnx_draworder_shader
iron.RenderPath.active.drawOrder = iron.RenderPath.DrawOrder.Shader;
iron.RenderPath.active.drawOrder = iron.RenderPath.DrawOrder.Index;
#end // else Distance
});
});

View File

@ -1,87 +1,243 @@
package leenkx.trait;
import iron.Trait;
import iron.math.Vec4;
import iron.system.Input;
import iron.object.Object;
import iron.object.CameraObject;
import leenkx.trait.physics.PhysicsWorld;
import leenkx.trait.internal.CameraController;
import leenkx.trait.physics.RigidBody;
import kha.FastFloat;
class FirstPersonController extends CameraController {
class FirstPersonController extends Trait {
#if (!lnx_physics)
public function new() { super(); }
#else
#if (!lnx_physics)
public function new() { super(); }
#else
var head: Object;
static inline var rotationSpeed = 2.0;
@prop public var rotationSpeed:Float = 0.15;
@prop public var maxPitch:Float = 2.2;
@prop public var minPitch:Float = 0.5;
@prop public var enableJump:Bool = true;
@prop public var jumpForce:Float = 22.0;
@prop public var moveSpeed:Float = 500.0;
public function new() {
super();
@prop public var forwardKey:String = "w";
@prop public var backwardKey:String = "s";
@prop public var leftKey:String = "a";
@prop public var rightKey:String = "d";
@prop public var jumpKey:String = "space";
iron.Scene.active.notifyOnInit(init);
}
@prop public var allowAirJump:Bool = false;
function init() {
head = object.getChildOfType(CameraObject);
@prop public var canRun:Bool = true;
@prop public var runKey:String = "shift";
@prop public var runSpeed:Float = 1000.0;
PhysicsWorld.active.notifyOnPreUpdate(preUpdate);
notifyOnUpdate(update);
notifyOnRemove(removed);
}
// Sistema de estamina
@prop public var stamina:Bool = false;
@prop public var staminaBase:Float = 75.0;
@prop public var staRecoverPerSec:Float = 5.0;
@prop public var staDecreasePerSec:Float = 5.0;
@prop public var staRecoverTime:Float = 2.0;
@prop public var staDecreasePerJump:Float = 5.0;
@prop public var enableFatigue:Bool = false;
@prop public var fatigueSpeed:Float = 0.5; // the reduction of movement when fatigue is activated...
@prop public var fatigueThreshold:Float = 30.0; // Tiempo corriendo sin parar para la activacion // Time running non-stop for activation...
@prop public var fatRecoveryThreshold:Float = 7.5; // Tiempo sin correr/saltar para salir de fatiga // Time without running/jumping to get rid of fatigue...
var xVec = Vec4.xAxis();
var zVec = Vec4.zAxis();
function preUpdate() {
if (Input.occupied || !body.ready) return;
// Var Privadas
var head:CameraObject;
var pitch:Float = 0.0;
var body:RigidBody;
var mouse = Input.getMouse();
var kb = Input.getKeyboard();
var moveForward:Bool = false;
var moveBackward:Bool = false;
var moveLeft:Bool = false;
var moveRight:Bool = false;
var isRunning:Bool = false;
if (mouse.started() && !mouse.locked) mouse.lock();
else if (kb.started("escape") && mouse.locked) mouse.unlock();
var canJump:Bool = true;
var staminaValue:Float = 0.0;
var timeSinceStop:Float = 0.0;
if (mouse.locked || mouse.down()) {
head.transform.rotate(xVec, -mouse.movementY / 250 * rotationSpeed);
transform.rotate(zVec, -mouse.movementX / 250 * rotationSpeed);
body.syncTransform();
var fatigueTimer:Float = 0.0;
var fatigueCooldown:Float = 0.0;
var isFatigueActive:Bool = false;
public function new() {
super();
iron.Scene.active.notifyOnInit(init);
}
function init() {
body = object.getTrait(RigidBody);
head = object.getChildOfType(CameraObject);
PhysicsWorld.active.notifyOnPreUpdate(preUpdate);
notifyOnUpdate(update);
notifyOnRemove(removed);
staminaValue = staminaBase;
}
function removed() {
PhysicsWorld.active.removePreUpdate(preUpdate);
}
var zVec = Vec4.zAxis();
function preUpdate() {
if (Input.occupied || body == null) return;
var mouse = Input.getMouse();
var kb = Input.getKeyboard();
if (mouse.started() && !mouse.locked)
mouse.lock();
else if (kb.started("escape") && mouse.locked)
mouse.unlock();
if (mouse.locked || mouse.down()) {
var deltaTime:Float = iron.system.Time.delta;
object.transform.rotate(zVec, -mouse.movementX * rotationSpeed * deltaTime);
var deltaPitch:Float = -(mouse.movementY * rotationSpeed * deltaTime);
pitch += deltaPitch;
pitch = Math.max(minPitch, Math.min(maxPitch, pitch));
head.transform.setRotation(pitch, 0.0, 0.0);
body.syncTransform();
}
}
var dir:Vec4 = new Vec4();
function isFatigued():Bool {
return enableFatigue && isFatigueActive;
}
function update() {
if (body == null) return;
var deltaTime:Float = iron.system.Time.delta;
var kb = Input.getKeyboard();
moveForward = kb.down(forwardKey);
moveBackward = kb.down(backwardKey);
moveLeft = kb.down(leftKey);
moveRight = kb.down(rightKey);
var isMoving = moveForward || moveBackward || moveLeft || moveRight;
var isGrounded:Bool = false;
#if lnx_physics
var vel = body.getLinearVelocity();
if (Math.abs(vel.z) < 0.1) {
isGrounded = true;
}
#end
// Dejo establecido el salto para tener en cuenta la (enableFatigue) si es que es false/true....
if (isGrounded && !isFatigued()) {
canJump = true;
}
}
// Saltar con estamina
if (enableJump && kb.started(jumpKey) && canJump) {
var jumpPower = jumpForce;
// Disminuir el salto al 50% si la (stamina) esta por debajo o en el 20%.
if (stamina) {
if (staminaValue <= 0) {
jumpPower = 0;
} else if (staminaValue <= staminaBase * 0.2) {
jumpPower *= 0.5;
}
function removed() {
PhysicsWorld.active.removePreUpdate(preUpdate);
}
staminaValue -= staDecreasePerJump;
if (staminaValue < 0.0) staminaValue = 0.0;
timeSinceStop = 0.0;
}
var dir = new Vec4();
function update() {
if (!body.ready) return;
if (jumpPower > 0) {
body.applyImpulse(new Vec4(0, 0, jumpPower));
if (!allowAirJump) canJump = false;
}
}
if (jump) {
body.applyImpulse(new Vec4(0, 0, 16));
jump = false;
// Control de estamina y correr
if (canRun && kb.down(runKey) && isMoving) {
if (stamina) {
if (staminaValue > 0.0) {
isRunning = true;
staminaValue -= staDecreasePerSec * deltaTime;
if (staminaValue < 0.0) staminaValue = 0.0;
} else {
isRunning = false;
}
} else {
isRunning = true;
}
} else {
isRunning = false;
}
// (temporizadores aparte)
if (isRunning) {
timeSinceStop = 0.0;
fatigueTimer += deltaTime;
fatigueCooldown = 0.0;
} else {
timeSinceStop += deltaTime;
fatigueCooldown += deltaTime;
}
// Evitar correr y saltar al estar fatigado...
if (isFatigued()) {
isRunning = false;
canJump = false;
}
// Move
dir.set(0, 0, 0);
if (moveForward) dir.add(transform.look());
if (moveBackward) dir.add(transform.look().mult(-1));
if (moveLeft) dir.add(transform.right().mult(-1));
if (moveRight) dir.add(transform.right());
// Activar fatiga despues de correr continuamente durante cierto umbral
if (enableFatigue && fatigueTimer >= fatigueThreshold) {
isFatigueActive = true;
}
// Push down
var btvec = body.getLinearVelocity();
body.setLinearVelocity(0.0, 0.0, btvec.z - 1.0);
// Eliminar la fatiga despues de recuperarse
if (enableFatigue && isFatigueActive && fatigueCooldown >= fatRecoveryThreshold) {
isFatigueActive = false;
fatigueTimer = 0.0;
}
if (moveForward || moveBackward || moveLeft || moveRight) {
var dirN = dir.normalize();
dirN.mult(6);
body.activate();
body.setLinearVelocity(dirN.x, dirN.y, btvec.z - 1.0);
}
// Recuperar estamina si no esta corriendo
if (stamina && !isRunning && staminaValue < staminaBase && !isFatigued()) {
if (timeSinceStop >= staRecoverTime) {
staminaValue += staRecoverPerSec * deltaTime;
if (staminaValue > staminaBase) staminaValue = staminaBase;
}
}
// Keep vertical
body.setAngularFactor(0, 0, 0);
camera.buildMatrix();
}
#end
// Movimiento ejes (local)
dir.set(0, 0, 0);
if (moveForward) dir.add(object.transform.look());
if (moveBackward) dir.add(object.transform.look().mult(-1));
if (moveLeft) dir.add(object.transform.right().mult(-1));
if (moveRight) dir.add(object.transform.right());
var btvec = body.getLinearVelocity();
body.setLinearVelocity(0.0, 0.0, btvec.z - 1.0);
if (isMoving) {
var dirN = dir.normalize();
var baseSpeed = moveSpeed;
if (isRunning && moveForward) {
baseSpeed = runSpeed;
}
var currentSpeed = isFatigued() ? baseSpeed * fatigueSpeed : baseSpeed;
dirN.mult(currentSpeed * deltaTime);
body.activate();
body.setLinearVelocity(dirN.x, dirN.y, btvec.z - 1.0);
}
body.setAngularFactor(0, 0, 0);
head.buildMatrix();
}
#end
}
// Stamina and fatigue system.....

View File

@ -73,7 +73,17 @@ class PhysicsBreak extends Trait {
collisionMargin: 0.04,
linearDeactivationThreshold: 0.0,
angularDeactivationThrshold: 0.0,
deactivationTime: 0.0
deactivationTime: 0.0,
linearVelocityMin: 0.0,
linearVelocityMax: 0.0,
angularVelocityMin: 0.0,
angularVelocityMax: 0.0,
lockTranslationX: false,
lockTranslationY: false,
lockTranslationZ: false,
lockRotationX: false,
lockRotationY: false,
lockRotationZ: false
};
o.addTrait(new RigidBody(Shape.ConvexHull, ud.mass, ud.friction, 0, 1, params));
if (cast(o, MeshObject).data.geom.positions.values.length < 600) {

View File

@ -281,7 +281,7 @@ class DebugConsole extends Trait {
function drawObjectNameInList(object: iron.object.Object, selected: Bool) {
var _y = ui._y;
if (object.parent.name == 'Root')
if (object.parent.name == 'Root' && object.raw == null)
ui.text(object.uid+'_'+object.name+' ('+iron.Scene.active.raw.world_ref+')');
else
ui.text(object.uid+'_'+object.name);

View File

@ -0,0 +1,98 @@
package leenkx.trait.physics;
import iron.object.Object;
class PhysicsCache {
#if lnx_physics
static var rbCache: Map<Int, Dynamic> = new Map();
static var contactsCache: Map<Int, Array<Dynamic>> = new Map();
static var physicsFrame: Int = 0;
static var lastQueryFrame: Int = -1;
#end
public static function getCachedRigidBody(object: Object): Dynamic {
#if (!lnx_physics)
return null;
#else
if (object == null) return null;
var cached = rbCache.get(object.uid);
if (cached != null) return cached;
#if lnx_bullet
var rb = object.getTrait(leenkx.trait.physics.bullet.RigidBody);
#else
var rb = object.getTrait(leenkx.trait.physics.oimo.RigidBody);
#end
if (rb != null) rbCache.set(object.uid, rb);
return rb;
#end
}
public static function getCachedContacts(rb: Dynamic): Array<Dynamic> {
#if (!lnx_physics)
return null;
#else
if (rb == null) return null;
var rbObjectId = (rb.object != null) ? rb.object.uid : -1;
if (rbObjectId == -1) {
#if lnx_bullet
if (leenkx.trait.physics.bullet.PhysicsWorld.active == null) return null;
return leenkx.trait.physics.bullet.PhysicsWorld.active.getContacts(rb);
#else
if (leenkx.trait.physics.oimo.PhysicsWorld.active == null) return null;
return leenkx.trait.physics.oimo.PhysicsWorld.active.getContacts(rb);
#end
}
if (lastQueryFrame == physicsFrame) {
var cached = contactsCache.get(rbObjectId);
if (cached != null) return cached;
}
lastQueryFrame = physicsFrame;
var cached = contactsCache.get(rbObjectId);
if (cached != null) return cached;
#if lnx_bullet
if (leenkx.trait.physics.bullet.PhysicsWorld.active == null) return null;
var contacts = leenkx.trait.physics.bullet.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);
#end
if (contacts != null) {
contactsCache.set(rbObjectId, contacts);
}
return contacts;
#end
}
public static inline function hasContactWith(contacts: Array<Dynamic>, target: Dynamic): Bool {
#if (!lnx_physics)
return false;
#else
return contacts != null && target != null && contacts.indexOf(target) >= 0;
#end
}
public static function clearCache() {
#if lnx_physics
rbCache.clear();
contactsCache.clear();
#end
}
public static function clearContactsCache() {
#if lnx_physics
physicsFrame++;
contactsCache.clear();
#end
}
}

View File

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

View File

@ -7,6 +7,7 @@ import iron.system.Time;
import iron.math.Vec4;
import iron.math.Quat;
import iron.math.RayCaster;
import leenkx.trait.physics.PhysicsCache;
class Hit {
@ -145,6 +146,7 @@ class PhysicsWorld extends Trait {
iron.Scene.active.notifyOnRemove(function() {
sceneRemoved = true;
PhysicsCache.clearCache();
});
}
@ -303,6 +305,8 @@ class PhysicsWorld extends Trait {
var t = Time.fixedStep * timeScale * Time.scale;
if (t == 0.0) return; // Simulation paused
PhysicsCache.clearContactsCache();
#if lnx_debug
var startTime = kha.Scheduler.realTime();
#end

Binary file not shown.

View File

@ -1,9 +1,17 @@
import importlib
import sys
import types
import bpy
# This gets cleared if this package/the __init__ module is reloaded
_module_cache: dict[str, types.ModuleType] = {}
if bpy.app.version < (2, 92, 0):
from typing import Dict
ModuleCacheType = Dict[str, types.ModuleType]
else:
ModuleCacheType = dict[str, types.ModuleType]
_module_cache: ModuleCacheType = {}
def enable_reload(module_name: str):

View File

@ -15,7 +15,14 @@ from enum import Enum, unique
import math
import os
import time
from typing import Any, Dict, List, Tuple, Union, Optional
from typing import Any, Dict, List, Tuple, Union, Optional, TYPE_CHECKING
import bpy
if bpy.app.version >= (3, 0, 0):
VertexColorType = bpy.types.Attribute
else:
VertexColorType = bpy.types.MeshLoopColorLayer
import numpy as np
@ -138,7 +145,7 @@ class LeenkxExporter:
self.world_array = []
self.particle_system_array = {}
self.referenced_collections: list[bpy.types.Collection] = []
self.referenced_collections: List[bpy.types.Collection] = []
"""Collections referenced by collection instances"""
self.has_spawning_camera = False
@ -1449,31 +1456,38 @@ class LeenkxExporter:
@staticmethod
def get_num_vertex_colors(mesh: bpy.types.Mesh) -> int:
"""Return the amount of vertex color attributes of the given mesh."""
num = 0
for attr in mesh.attributes:
if attr.data_type in ('BYTE_COLOR', 'FLOAT_COLOR'):
if attr.domain == 'CORNER':
num += 1
else:
log.warn(f'Only vertex colors with domain "Face Corner" are supported for now, ignoring "{attr.name}"')
return num
if bpy.app.version >= (3, 0, 0):
num = 0
for attr in mesh.attributes:
if attr.data_type in ('BYTE_COLOR', 'FLOAT_COLOR'):
if attr.domain == 'CORNER':
num += 1
else:
log.warn(f'Only vertex colors with domain "Face Corner" are supported for now, ignoring "{attr.name}"')
return num
else:
return len(mesh.vertex_colors)
@staticmethod
def get_nth_vertex_colors(mesh: bpy.types.Mesh, n: int) -> Optional[bpy.types.Attribute]:
def get_nth_vertex_colors(mesh: bpy.types.Mesh, n: int) -> Optional[VertexColorType]:
"""Return the n-th vertex color attribute from the given mesh,
ignoring all other attribute types and unsupported domains.
"""
i = 0
for attr in mesh.attributes:
if attr.data_type in ('BYTE_COLOR', 'FLOAT_COLOR'):
if attr.domain != 'CORNER':
log.warn(f'Only vertex colors with domain "Face Corner" are supported for now, ignoring "{attr.name}"')
continue
if i == n:
return attr
i += 1
return None
if bpy.app.version >= (3, 0, 0):
i = 0
for attr in mesh.attributes:
if attr.data_type in ('BYTE_COLOR', 'FLOAT_COLOR'):
if attr.domain != 'CORNER':
log.warn(f'Only vertex colors with domain "Face Corner" are supported for now, ignoring "{attr.name}"')
continue
if i == n:
return attr
i += 1
return None
else:
if 0 <= n < len(mesh.vertex_colors):
return mesh.vertex_colors[n]
return None
@staticmethod
def check_uv_precision(mesh: bpy.types.Mesh, uv_max_dim: float, max_dim_uvmap: bpy.types.MeshUVLoopLayer, invscale_tex: float):
@ -1727,6 +1741,7 @@ class LeenkxExporter:
tangdata = np.array(tangdata, dtype='<i2')
# Output
o['sorting_index'] = bobject.lnx_sorting_index
o['vertex_arrays'] = []
o['vertex_arrays'].append({ 'attrib': 'pos', 'values': pdata, 'data': 'short4norm' })
o['vertex_arrays'].append({ 'attrib': 'nor', 'values': ndata, 'data': 'short2norm' })
@ -1979,7 +1994,7 @@ class LeenkxExporter:
if bobject.parent is None or bobject.parent.name not in collection.objects:
asset_name = lnx.utils.asset_name(bobject)
if collection.library:
if collection.library and not collection.name in self.scene.collection.children:
# Add external linked objects
# Iron differentiates objects based on their names,
# so errors will happen if two objects with the
@ -2208,6 +2223,9 @@ class LeenkxExporter:
elif material.lnx_cull_mode != 'clockwise':
o['override_context'] = {}
o['override_context']['cull_mode'] = material.lnx_cull_mode
if material.lnx_compare_mode != 'less':
o['override_context'] = {}
o['override_context']['compare_mode'] = material.lnx_compare_mode
o['contexts'] = []
@ -2330,6 +2348,7 @@ class LeenkxExporter:
'name': particleRef[1]["structName"],
'type': 0 if psettings.type == 'EMITTER' else 1, # HAIR
'auto_start': psettings.lnx_auto_start,
'dynamic_emitter': psettings.lnx_dynamic_emitter,
'is_unique': psettings.lnx_is_unique,
'loop': psettings.lnx_loop,
# Emission
@ -2395,7 +2414,7 @@ class LeenkxExporter:
world = self.scene.world
if world is not None:
world_name = lnx.utils.safestr(world.name)
world_name = lnx.utils.safestr(lnx.utils.asset_name(world) if world.library else world.name)
if world_name not in self.world_array:
self.world_array.append(world_name)
@ -2544,12 +2563,12 @@ class LeenkxExporter:
if collection.name.startswith(('RigidBodyWorld', 'Trait|')):
continue
if self.scene.user_of_id(collection) or collection.library or collection in self.referenced_collections:
if self.scene.user_of_id(collection) or collection in self.referenced_collections:
self.export_collection(collection)
if not LeenkxExporter.option_mesh_only:
if self.scene.camera is not None:
self.output['camera_ref'] = self.scene.camera.name
self.output['camera_ref'] = lnx.utils.asset_name(self.scene.camera) if self.scene.library else self.scene.camera.name
else:
if self.scene.name == lnx.utils.get_project_scene_name():
log.warn(f'Scene "{self.scene.name}" is missing a camera')
@ -2573,7 +2592,7 @@ class LeenkxExporter:
self.export_tilesheets()
if self.scene.world is not None:
self.output['world_ref'] = lnx.utils.safestr(self.scene.world.name)
self.output['world_ref'] = lnx.utils.safestr(lnx.utils.asset_name(self.scene.world) if self.scene.world.library else self.scene.world.name)
if self.scene.use_gravity:
self.output['gravity'] = [self.scene.gravity[0], self.scene.gravity[1], self.scene.gravity[2]]
@ -3057,6 +3076,8 @@ class LeenkxExporter:
if trait_prop.type.endswith("Object"):
value = lnx.utils.asset_name(trait_prop.value_object)
elif trait_prop.type == "TSceneFormat":
value = lnx.utils.asset_name(trait_prop.value_scene)
else:
value = trait_prop.get_value()
@ -3087,7 +3108,18 @@ class LeenkxExporter:
rbw = self.scene.rigidbody_world
if rbw is not None and rbw.enabled:
out_trait['parameters'] = [str(rbw.time_scale), str(rbw.substeps_per_frame), str(rbw.solver_iterations), str(wrd.lnx_physics_fixed_step)]
if hasattr(rbw, 'substeps_per_frame'):
substeps = str(rbw.substeps_per_frame)
elif hasattr(rbw, 'steps_per_second'):
scene_fps = bpy.context.scene.render.fps
substeps_per_frame = rbw.steps_per_second / scene_fps
substeps = str(int(round(substeps_per_frame)))
else:
print("WARNING: Physics rigid body world cannot determine steps/substeps. Please report this for further investigation.")
print("Setting steps to 10 [ low ]")
substeps = '10'
out_trait['parameters'] = [str(rbw.time_scale), substeps, str(rbw.solver_iterations), str(wrd.lnx_physics_fixed_step)]
if phys_pkg == 'bullet' or phys_pkg == 'oimo':
debug_draw_mode = 1 if wrd.lnx_physics_dbg_draw_wireframe else 0
@ -3374,7 +3406,7 @@ class LeenkxExporter:
if mobile_mat:
lnx_radiance = False
out_probe = {'name': world.name}
out_probe = {'name': lnx.utils.asset_name(world) if world.library else world.name}
if lnx_irradiance:
ext = '' if wrd.lnx_minimize else '.json'
out_probe['irradiance'] = irrsharmonics + '_irradiance' + ext

View File

@ -1,445 +1,450 @@
"""
Exports smaller geometry but is slower.
To be replaced with https://github.com/zeux/meshoptimizer
"""
from typing import Optional
import bpy
from mathutils import Vector
import numpy as np
import lnx.utils
from lnx import log
if lnx.is_reload(__name__):
log = lnx.reload_module(log)
lnx.utils = lnx.reload_module(lnx.utils)
else:
lnx.enable_reload(__name__)
class Vertex:
__slots__ = ("co", "normal", "uvs", "col", "loop_indices", "index", "bone_weights", "bone_indices", "bone_count", "vertex_index")
def __init__(self, mesh: bpy.types.Mesh, loop: bpy.types.MeshLoop, vcol0: Optional[bpy.types.Attribute]):
self.vertex_index = loop.vertex_index
loop_idx = loop.index
self.co = mesh.vertices[self.vertex_index].co[:]
self.normal = loop.normal[:]
self.uvs = tuple(layer.data[loop_idx].uv[:] for layer in mesh.uv_layers)
self.col = [0.0, 0.0, 0.0] if vcol0 is None else vcol0.data[loop_idx].color[:]
self.loop_indices = [loop_idx]
self.index = 0
def __hash__(self):
return hash((self.co, self.normal, self.uvs))
def __eq__(self, other):
eq = (
(self.co == other.co) and
(self.normal == other.normal) and
(self.uvs == other.uvs) and
(self.col == other.col)
)
if eq:
indices = self.loop_indices + other.loop_indices
self.loop_indices = indices
other.loop_indices = indices
return eq
def calc_tangents(posa, nora, uva, ias, scale_pos):
num_verts = int(len(posa) / 4)
tangents = np.empty(num_verts * 3, dtype='<f4')
# bitangents = np.empty(num_verts * 3, dtype='<f4')
for ar in ias:
ia = ar['values']
num_tris = int(len(ia) / 3)
for i in range(0, num_tris):
i0 = ia[i * 3 ]
i1 = ia[i * 3 + 1]
i2 = ia[i * 3 + 2]
v0 = Vector((posa[i0 * 4], posa[i0 * 4 + 1], posa[i0 * 4 + 2]))
v1 = Vector((posa[i1 * 4], posa[i1 * 4 + 1], posa[i1 * 4 + 2]))
v2 = Vector((posa[i2 * 4], posa[i2 * 4 + 1], posa[i2 * 4 + 2]))
uv0 = Vector((uva[i0 * 2], uva[i0 * 2 + 1]))
uv1 = Vector((uva[i1 * 2], uva[i1 * 2 + 1]))
uv2 = Vector((uva[i2 * 2], uva[i2 * 2 + 1]))
deltaPos1 = v1 - v0
deltaPos2 = v2 - v0
deltaUV1 = uv1 - uv0
deltaUV2 = uv2 - uv0
d = (deltaUV1.x * deltaUV2.y - deltaUV1.y * deltaUV2.x)
if d != 0:
r = 1.0 / d
else:
r = 1.0
tangent = (deltaPos1 * deltaUV2.y - deltaPos2 * deltaUV1.y) * r
# bitangent = (deltaPos2 * deltaUV1.x - deltaPos1 * deltaUV2.x) * r
tangents[i0 * 3 ] += tangent.x
tangents[i0 * 3 + 1] += tangent.y
tangents[i0 * 3 + 2] += tangent.z
tangents[i1 * 3 ] += tangent.x
tangents[i1 * 3 + 1] += tangent.y
tangents[i1 * 3 + 2] += tangent.z
tangents[i2 * 3 ] += tangent.x
tangents[i2 * 3 + 1] += tangent.y
tangents[i2 * 3 + 2] += tangent.z
# bitangents[i0 * 3 ] += bitangent.x
# bitangents[i0 * 3 + 1] += bitangent.y
# bitangents[i0 * 3 + 2] += bitangent.z
# bitangents[i1 * 3 ] += bitangent.x
# bitangents[i1 * 3 + 1] += bitangent.y
# bitangents[i1 * 3 + 2] += bitangent.z
# bitangents[i2 * 3 ] += bitangent.x
# bitangents[i2 * 3 + 1] += bitangent.y
# bitangents[i2 * 3 + 2] += bitangent.z
# Orthogonalize
for i in range(0, num_verts):
t = Vector((tangents[i * 3], tangents[i * 3 + 1], tangents[i * 3 + 2]))
# b = Vector((bitangents[i * 3], bitangents[i * 3 + 1], bitangents[i * 3 + 2]))
n = Vector((nora[i * 2], nora[i * 2 + 1], posa[i * 4 + 3] / scale_pos))
v = t - n * n.dot(t)
v.normalize()
# Calculate handedness
# cnv = n.cross(v)
# if cnv.dot(b) < 0.0:
# v = v * -1.0
tangents[i * 3 ] = v.x
tangents[i * 3 + 1] = v.y
tangents[i * 3 + 2] = v.z
return tangents
def export_mesh_data(self, export_mesh: bpy.types.Mesh, bobject: bpy.types.Object, o, has_armature=False):
if bpy.app.version < (4, 1, 0):
export_mesh.calc_normals_split()
else:
updated_normals = export_mesh.corner_normals
# exportMesh.calc_loop_triangles()
vcol0 = self.get_nth_vertex_colors(export_mesh, 0)
vert_list = {Vertex(export_mesh, loop, vcol0): 0 for loop in export_mesh.loops}.keys()
num_verts = len(vert_list)
num_uv_layers = len(export_mesh.uv_layers)
# Check if shape keys were exported
has_morph_target = self.get_shape_keys(bobject.data)
if has_morph_target:
# Shape keys UV are exported separately, so reduce UV count by 1
num_uv_layers -= 1
morph_uv_index = self.get_morph_uv_index(bobject.data)
has_tex = self.get_export_uvs(export_mesh) and num_uv_layers > 0
if self.has_baked_material(bobject, export_mesh.materials):
has_tex = True
has_tex1 = has_tex and num_uv_layers > 1
num_colors = self.get_num_vertex_colors(export_mesh)
has_col = self.get_export_vcols(export_mesh) and num_colors > 0
has_tang = self.has_tangents(export_mesh)
pdata = np.empty(num_verts * 4, dtype='<f4') # p.xyz, n.z
ndata = np.empty(num_verts * 2, dtype='<f4') # n.xy
if has_tex or has_morph_target:
uv_layers = export_mesh.uv_layers
maxdim = 1.0
maxdim_uvlayer = None
if has_tex:
t0map = 0 # Get active uvmap
t0data = np.empty(num_verts * 2, dtype='<f4')
if uv_layers is not None:
if 'UVMap_baked' in uv_layers:
for i in range(0, len(uv_layers)):
if uv_layers[i].name == 'UVMap_baked':
t0map = i
break
else:
for i in range(0, len(uv_layers)):
if uv_layers[i].active_render and uv_layers[i].name != 'UVMap_shape_key':
t0map = i
break
if has_tex1:
for i in range(0, len(uv_layers)):
# Not UVMap 0
if i != t0map:
# Not Shape Key UVMap
if has_morph_target and uv_layers[i].name == 'UVMap_shape_key':
continue
# Neither UVMap 0 Nor Shape Key Map
t1map = i
t1data = np.empty(num_verts * 2, dtype='<f4')
# Scale for packed coords
lay0 = uv_layers[t0map]
maxdim_uvlayer = lay0
for v in lay0.data:
if abs(v.uv[0]) > maxdim:
maxdim = abs(v.uv[0])
if abs(v.uv[1]) > maxdim:
maxdim = abs(v.uv[1])
if has_tex1:
lay1 = uv_layers[t1map]
for v in lay1.data:
if abs(v.uv[0]) > maxdim:
maxdim = abs(v.uv[0])
maxdim_uvlayer = lay1
if abs(v.uv[1]) > maxdim:
maxdim = abs(v.uv[1])
maxdim_uvlayer = lay1
if has_morph_target:
morph_data = np.empty(num_verts * 2, dtype='<f4')
lay2 = uv_layers[morph_uv_index]
for v in lay2.data:
if abs(v.uv[0]) > maxdim:
maxdim = abs(v.uv[0])
maxdim_uvlayer = lay2
if abs(v.uv[1]) > maxdim:
maxdim = abs(v.uv[1])
maxdim_uvlayer = lay2
if maxdim > 1:
o['scale_tex'] = maxdim
invscale_tex = (1 / o['scale_tex']) * 32767
else:
invscale_tex = 1 * 32767
self.check_uv_precision(export_mesh, maxdim, maxdim_uvlayer, invscale_tex)
if has_col:
cdata = np.empty(num_verts * 3, dtype='<f4')
# Save aabb
self.calc_aabb(bobject)
# Scale for packed coords
maxdim = max(bobject.data.lnx_aabb[0], max(bobject.data.lnx_aabb[1], bobject.data.lnx_aabb[2]))
if maxdim > 2:
o['scale_pos'] = maxdim / 2
else:
o['scale_pos'] = 1.0
if has_armature: # Allow up to 2x bigger bounds for skinned mesh
o['scale_pos'] *= 2.0
scale_pos = o['scale_pos']
invscale_pos = (1 / scale_pos) * 32767
# Make arrays
for i, v in enumerate(vert_list):
v.index = i
co = v.co
normal = v.normal
i4 = i * 4
i2 = i * 2
pdata[i4 ] = co[0]
pdata[i4 + 1] = co[1]
pdata[i4 + 2] = co[2]
pdata[i4 + 3] = normal[2] * scale_pos # Cancel scale
ndata[i2 ] = normal[0]
ndata[i2 + 1] = normal[1]
if has_tex:
uv = v.uvs[t0map]
t0data[i2 ] = uv[0]
t0data[i2 + 1] = 1.0 - uv[1] # Reverse Y
if has_tex1:
uv = v.uvs[t1map]
t1data[i2 ] = uv[0]
t1data[i2 + 1] = 1.0 - uv[1]
if has_morph_target:
uv = v.uvs[morph_uv_index]
morph_data[i2 ] = uv[0]
morph_data[i2 + 1] = 1.0 - uv[1]
if has_col:
i3 = i * 3
cdata[i3 ] = v.col[0]
cdata[i3 + 1] = v.col[1]
cdata[i3 + 2] = v.col[2]
# Indices
# Create dict for every material slot
prims = {ma.name if ma else '': [] for ma in export_mesh.materials}
v_maps = {ma.name if ma else '': [] for ma in export_mesh.materials}
if not prims:
# No materials
prims = {'': []}
v_maps = {'': []}
# Create dict of {loop_indices : vertex} with each loop_index in each vertex in Vertex_list
vert_dict = {i : v for v in vert_list for i in v.loop_indices}
# For each polygon in a mesh
for poly in export_mesh.polygons:
# Index of the first loop of this polygon
first = poly.loop_start
# No materials assigned
if len(export_mesh.materials) == 0:
# Get prim
prim = prims['']
v_map = v_maps['']
else:
# First material
mat = export_mesh.materials[min(poly.material_index, len(export_mesh.materials) - 1)]
# Get prim for this material
prim = prims[mat.name if mat else '']
v_map = v_maps[mat.name if mat else '']
# List of indices for each loop_index belonging to this polygon
indices = [vert_dict[i].index for i in range(first, first+poly.loop_total)]
v_indices = [vert_dict[i].vertex_index for i in range(first, first+poly.loop_total)]
# If 3 loops per polygon (Triangle?)
if poly.loop_total == 3:
prim += indices
v_map += v_indices
# If > 3 loops per polygon (Non-Triangular?)
elif poly.loop_total > 3:
for i in range(poly.loop_total-2):
prim += (indices[-1], indices[i], indices[i + 1])
v_map += (v_indices[-1], v_indices[i], v_indices[i + 1])
# Write indices
o['index_arrays'] = []
for mat, prim in prims.items():
idata = [0] * len(prim)
v_map_data = [0] * len(prim)
v_map_sub = v_maps[mat]
for i, v in enumerate(prim):
idata[i] = v
v_map_data[i] = v_map_sub[i]
if len(idata) == 0: # No face assigned
continue
ia = {'values': idata, 'material': 0, 'vertex_map': v_map_data}
# Find material index for multi-mat mesh
if len(export_mesh.materials) > 1:
for i in range(0, len(export_mesh.materials)):
if (export_mesh.materials[i] is not None and mat == export_mesh.materials[i].name) or \
(export_mesh.materials[i] is None and mat == ''): # Default material for empty slots
ia['material'] = i
break
o['index_arrays'].append(ia)
if has_tang:
tangdata = calc_tangents(pdata, ndata, t0data, o['index_arrays'], scale_pos)
pdata *= invscale_pos
ndata *= 32767
pdata = np.array(pdata, dtype='<i2')
ndata = np.array(ndata, dtype='<i2')
if has_tex:
t0data *= invscale_tex
t0data = np.array(t0data, dtype='<i2')
if has_tex1:
t1data *= invscale_tex
t1data = np.array(t1data, dtype='<i2')
if has_morph_target:
morph_data *= invscale_tex
morph_data = np.array(morph_data, dtype='<i2')
if has_col:
cdata *= 32767
cdata = np.array(cdata, dtype='<i2')
if has_tang:
tangdata *= 32767
tangdata = np.array(tangdata, dtype='<i2')
# Output
o['vertex_arrays'] = []
o['vertex_arrays'].append({ 'attrib': 'pos', 'values': pdata, 'data': 'short4norm' })
o['vertex_arrays'].append({ 'attrib': 'nor', 'values': ndata, 'data': 'short2norm' })
if has_tex:
o['vertex_arrays'].append({ 'attrib': 'tex', 'values': t0data, 'data': 'short2norm' })
if has_tex1:
o['vertex_arrays'].append({ 'attrib': 'tex1', 'values': t1data, 'data': 'short2norm' })
if has_morph_target:
o['vertex_arrays'].append({ 'attrib': 'morph', 'values': morph_data, 'data': 'short2norm' })
if has_col:
o['vertex_arrays'].append({ 'attrib': 'col', 'values': cdata, 'data': 'short4norm', 'padding': 1 })
if has_tang:
o['vertex_arrays'].append({ 'attrib': 'tang', 'values': tangdata, 'data': 'short4norm', 'padding': 1 })
return vert_list
def export_skin(self, bobject, armature, vert_list, o):
# This function exports all skinning data, which includes the skeleton
# and per-vertex bone influence data
oskin = {}
o['skin'] = oskin
# Write the skin bind pose transform
otrans = {}
oskin['transform'] = otrans
otrans['values'] = self.write_matrix(bobject.matrix_world)
# Write the bone object reference array
oskin['bone_ref_array'] = []
oskin['bone_len_array'] = []
bone_array = armature.data.bones
bone_count = len(bone_array)
rpdat = lnx.utils.get_rp()
max_bones = rpdat.lnx_skin_max_bones
if bone_count > max_bones:
log.warn(bobject.name + ' - ' + str(bone_count) + ' bones found, exceeds maximum of ' + str(max_bones) + ' bones defined - raise the value in Camera Data - Leenkx Render Props - Max Bones')
for i in range(bone_count):
boneRef = self.find_bone(bone_array[i].name)
if boneRef:
oskin['bone_ref_array'].append(boneRef[1]["structName"])
oskin['bone_len_array'].append(bone_array[i].length)
else:
oskin['bone_ref_array'].append("")
oskin['bone_len_array'].append(0.0)
# Write the bind pose transform array
oskin['transformsI'] = []
for i in range(bone_count):
skeletonI = (armature.matrix_world @ bone_array[i].matrix_local).inverted_safe()
skeletonI = (skeletonI @ bobject.matrix_world)
oskin['transformsI'].append(self.write_matrix(skeletonI))
# Export the per-vertex bone influence data
group_remap = []
for group in bobject.vertex_groups:
for i in range(bone_count):
if bone_array[i].name == group.name:
group_remap.append(i)
break
else:
group_remap.append(-1)
bone_count_array = np.empty(len(vert_list), dtype='<i2')
bone_index_array = np.empty(len(vert_list) * 4, dtype='<i2')
bone_weight_array = np.empty(len(vert_list) * 4, dtype='<i2')
vertices = bobject.data.vertices
count = 0
for index, v in enumerate(vert_list):
bone_count = 0
total_weight = 0.0
bone_values = []
for g in vertices[v.vertex_index].groups:
bone_index = group_remap[g.group]
bone_weight = g.weight
if bone_index >= 0: #and bone_weight != 0.0:
bone_values.append((bone_weight, bone_index))
total_weight += bone_weight
bone_count += 1
if bone_count > 4:
bone_count = 4
bone_values.sort(reverse=True)
bone_values = bone_values[:4]
bone_count_array[index] = bone_count
for bv in bone_values:
bone_weight_array[count] = bv[0] * 32767
bone_index_array[count] = bv[1]
count += 1
if total_weight not in (0.0, 1.0):
normalizer = 1.0 / total_weight
for i in range(bone_count):
bone_weight_array[count - i - 1] *= normalizer
oskin['bone_count_array'] = bone_count_array
oskin['bone_index_array'] = bone_index_array[:count]
oskin['bone_weight_array'] = bone_weight_array[:count]
# Bone constraints
for bone in armature.pose.bones:
if len(bone.constraints) > 0:
if 'constraints' not in oskin:
oskin['constraints'] = []
self.add_constraints(bone, oskin, bone=True)
"""
Exports smaller geometry but is slower.
To be replaced with https://github.com/zeux/meshoptimizer
"""
from typing import Optional, TYPE_CHECKING
import bpy
from mathutils import Vector
import numpy as np
import lnx.utils
from lnx import log
if lnx.is_reload(__name__):
log = lnx.reload_module(log)
lnx.utils = lnx.reload_module(lnx.utils)
else:
lnx.enable_reload(__name__)
class Vertex:
__slots__ = ("co", "normal", "uvs", "col", "loop_indices", "index", "bone_weights", "bone_indices", "bone_count", "vertex_index")
def __init__(
self,
mesh: 'bpy.types.Mesh',
loop: 'bpy.types.MeshLoop',
vcol0: Optional['bpy.types.MeshLoopColor' if bpy.app.version < (3, 0, 0) else 'bpy.types.Attribute']
):
self.vertex_index = loop.vertex_index
loop_idx = loop.index
self.co = mesh.vertices[self.vertex_index].co[:]
self.normal = loop.normal[:]
self.uvs = tuple(layer.data[loop_idx].uv[:] for layer in mesh.uv_layers)
self.col = [0.0, 0.0, 0.0] if vcol0 is None else vcol0.data[loop_idx].color[:]
self.loop_indices = [loop_idx]
self.index = 0
def __hash__(self):
return hash((self.co, self.normal, self.uvs))
def __eq__(self, other):
eq = (
(self.co == other.co) and
(self.normal == other.normal) and
(self.uvs == other.uvs) and
(self.col == other.col)
)
if eq:
indices = self.loop_indices + other.loop_indices
self.loop_indices = indices
other.loop_indices = indices
return eq
def calc_tangents(posa, nora, uva, ias, scale_pos):
num_verts = int(len(posa) / 4)
tangents = np.empty(num_verts * 3, dtype='<f4')
# bitangents = np.empty(num_verts * 3, dtype='<f4')
for ar in ias:
ia = ar['values']
num_tris = int(len(ia) / 3)
for i in range(0, num_tris):
i0 = ia[i * 3 ]
i1 = ia[i * 3 + 1]
i2 = ia[i * 3 + 2]
v0 = Vector((posa[i0 * 4], posa[i0 * 4 + 1], posa[i0 * 4 + 2]))
v1 = Vector((posa[i1 * 4], posa[i1 * 4 + 1], posa[i1 * 4 + 2]))
v2 = Vector((posa[i2 * 4], posa[i2 * 4 + 1], posa[i2 * 4 + 2]))
uv0 = Vector((uva[i0 * 2], uva[i0 * 2 + 1]))
uv1 = Vector((uva[i1 * 2], uva[i1 * 2 + 1]))
uv2 = Vector((uva[i2 * 2], uva[i2 * 2 + 1]))
deltaPos1 = v1 - v0
deltaPos2 = v2 - v0
deltaUV1 = uv1 - uv0
deltaUV2 = uv2 - uv0
d = (deltaUV1.x * deltaUV2.y - deltaUV1.y * deltaUV2.x)
if d != 0:
r = 1.0 / d
else:
r = 1.0
tangent = (deltaPos1 * deltaUV2.y - deltaPos2 * deltaUV1.y) * r
# bitangent = (deltaPos2 * deltaUV1.x - deltaPos1 * deltaUV2.x) * r
tangents[i0 * 3 ] += tangent.x
tangents[i0 * 3 + 1] += tangent.y
tangents[i0 * 3 + 2] += tangent.z
tangents[i1 * 3 ] += tangent.x
tangents[i1 * 3 + 1] += tangent.y
tangents[i1 * 3 + 2] += tangent.z
tangents[i2 * 3 ] += tangent.x
tangents[i2 * 3 + 1] += tangent.y
tangents[i2 * 3 + 2] += tangent.z
# bitangents[i0 * 3 ] += bitangent.x
# bitangents[i0 * 3 + 1] += bitangent.y
# bitangents[i0 * 3 + 2] += bitangent.z
# bitangents[i1 * 3 ] += bitangent.x
# bitangents[i1 * 3 + 1] += bitangent.y
# bitangents[i1 * 3 + 2] += bitangent.z
# bitangents[i2 * 3 ] += bitangent.x
# bitangents[i2 * 3 + 1] += bitangent.y
# bitangents[i2 * 3 + 2] += bitangent.z
# Orthogonalize
for i in range(0, num_verts):
t = Vector((tangents[i * 3], tangents[i * 3 + 1], tangents[i * 3 + 2]))
# b = Vector((bitangents[i * 3], bitangents[i * 3 + 1], bitangents[i * 3 + 2]))
n = Vector((nora[i * 2], nora[i * 2 + 1], posa[i * 4 + 3] / scale_pos))
v = t - n * n.dot(t)
v.normalize()
# Calculate handedness
# cnv = n.cross(v)
# if cnv.dot(b) < 0.0:
# v = v * -1.0
tangents[i * 3 ] = v.x
tangents[i * 3 + 1] = v.y
tangents[i * 3 + 2] = v.z
return tangents
def export_mesh_data(self, export_mesh: bpy.types.Mesh, bobject: bpy.types.Object, o, has_armature=False):
if bpy.app.version < (4, 1, 0):
export_mesh.calc_normals_split()
else:
updated_normals = export_mesh.corner_normals
# exportMesh.calc_loop_triangles()
vcol0 = self.get_nth_vertex_colors(export_mesh, 0)
vert_list = {Vertex(export_mesh, loop, vcol0): 0 for loop in export_mesh.loops}.keys()
num_verts = len(vert_list)
num_uv_layers = len(export_mesh.uv_layers)
# Check if shape keys were exported
has_morph_target = self.get_shape_keys(bobject.data)
if has_morph_target:
# Shape keys UV are exported separately, so reduce UV count by 1
num_uv_layers -= 1
morph_uv_index = self.get_morph_uv_index(bobject.data)
has_tex = self.get_export_uvs(export_mesh) or num_uv_layers > 0 # TODO FIXME: this should use an `and` instead of `or`. Workaround to completely ignore if the mesh has the `export_uvs` flag. Only checking the `uv_layers` to bypass issues with materials in linked objects.
if self.has_baked_material(bobject, export_mesh.materials):
has_tex = True
has_tex1 = has_tex and num_uv_layers > 1
num_colors = self.get_num_vertex_colors(export_mesh)
has_col = self.get_export_vcols(export_mesh) and num_colors > 0
has_tang = self.has_tangents(export_mesh)
pdata = np.empty(num_verts * 4, dtype='<f4') # p.xyz, n.z
ndata = np.empty(num_verts * 2, dtype='<f4') # n.xy
if has_tex or has_morph_target:
uv_layers = export_mesh.uv_layers
maxdim = 1.0
maxdim_uvlayer = None
if has_tex:
t0map = 0 # Get active uvmap
t0data = np.empty(num_verts * 2, dtype='<f4')
if uv_layers is not None:
if 'UVMap_baked' in uv_layers:
for i in range(0, len(uv_layers)):
if uv_layers[i].name == 'UVMap_baked':
t0map = i
break
else:
for i in range(0, len(uv_layers)):
if uv_layers[i].active_render and uv_layers[i].name != 'UVMap_shape_key':
t0map = i
break
if has_tex1:
for i in range(0, len(uv_layers)):
# Not UVMap 0
if i != t0map:
# Not Shape Key UVMap
if has_morph_target and uv_layers[i].name == 'UVMap_shape_key':
continue
# Neither UVMap 0 Nor Shape Key Map
t1map = i
t1data = np.empty(num_verts * 2, dtype='<f4')
# Scale for packed coords
lay0 = uv_layers[t0map]
maxdim_uvlayer = lay0
for v in lay0.data:
if abs(v.uv[0]) > maxdim:
maxdim = abs(v.uv[0])
if abs(v.uv[1]) > maxdim:
maxdim = abs(v.uv[1])
if has_tex1:
lay1 = uv_layers[t1map]
for v in lay1.data:
if abs(v.uv[0]) > maxdim:
maxdim = abs(v.uv[0])
maxdim_uvlayer = lay1
if abs(v.uv[1]) > maxdim:
maxdim = abs(v.uv[1])
maxdim_uvlayer = lay1
if has_morph_target:
morph_data = np.empty(num_verts * 2, dtype='<f4')
lay2 = uv_layers[morph_uv_index]
for v in lay2.data:
if abs(v.uv[0]) > maxdim:
maxdim = abs(v.uv[0])
maxdim_uvlayer = lay2
if abs(v.uv[1]) > maxdim:
maxdim = abs(v.uv[1])
maxdim_uvlayer = lay2
if maxdim > 1:
o['scale_tex'] = maxdim
invscale_tex = (1 / o['scale_tex']) * 32767
else:
invscale_tex = 1 * 32767
self.check_uv_precision(export_mesh, maxdim, maxdim_uvlayer, invscale_tex)
if has_col:
cdata = np.empty(num_verts * 3, dtype='<f4')
# Save aabb
self.calc_aabb(bobject)
# Scale for packed coords
maxdim = max(bobject.data.lnx_aabb[0], max(bobject.data.lnx_aabb[1], bobject.data.lnx_aabb[2]))
if maxdim > 2:
o['scale_pos'] = maxdim / 2
else:
o['scale_pos'] = 1.0
if has_armature: # Allow up to 2x bigger bounds for skinned mesh
o['scale_pos'] *= 2.0
scale_pos = o['scale_pos']
invscale_pos = (1 / scale_pos) * 32767
# Make arrays
for i, v in enumerate(vert_list):
v.index = i
co = v.co
normal = v.normal
i4 = i * 4
i2 = i * 2
pdata[i4 ] = co[0]
pdata[i4 + 1] = co[1]
pdata[i4 + 2] = co[2]
pdata[i4 + 3] = normal[2] * scale_pos # Cancel scale
ndata[i2 ] = normal[0]
ndata[i2 + 1] = normal[1]
if has_tex:
uv = v.uvs[t0map]
t0data[i2 ] = uv[0]
t0data[i2 + 1] = 1.0 - uv[1] # Reverse Y
if has_tex1:
uv = v.uvs[t1map]
t1data[i2 ] = uv[0]
t1data[i2 + 1] = 1.0 - uv[1]
if has_morph_target:
uv = v.uvs[morph_uv_index]
morph_data[i2 ] = uv[0]
morph_data[i2 + 1] = 1.0 - uv[1]
if has_col:
i3 = i * 3
cdata[i3 ] = v.col[0]
cdata[i3 + 1] = v.col[1]
cdata[i3 + 2] = v.col[2]
# Indices
# Create dict for every material slot
prims = {ma.name if ma else '': [] for ma in export_mesh.materials}
v_maps = {ma.name if ma else '': [] for ma in export_mesh.materials}
if not prims:
# No materials
prims = {'': []}
v_maps = {'': []}
# Create dict of {loop_indices : vertex} with each loop_index in each vertex in Vertex_list
vert_dict = {i : v for v in vert_list for i in v.loop_indices}
# For each polygon in a mesh
for poly in export_mesh.polygons:
# Index of the first loop of this polygon
first = poly.loop_start
# No materials assigned
if len(export_mesh.materials) == 0:
# Get prim
prim = prims['']
v_map = v_maps['']
else:
# First material
mat = export_mesh.materials[min(poly.material_index, len(export_mesh.materials) - 1)]
# Get prim for this material
prim = prims[mat.name if mat else '']
v_map = v_maps[mat.name if mat else '']
# List of indices for each loop_index belonging to this polygon
indices = [vert_dict[i].index for i in range(first, first+poly.loop_total)]
v_indices = [vert_dict[i].vertex_index for i in range(first, first+poly.loop_total)]
# If 3 loops per polygon (Triangle?)
if poly.loop_total == 3:
prim += indices
v_map += v_indices
# If > 3 loops per polygon (Non-Triangular?)
elif poly.loop_total > 3:
for i in range(poly.loop_total-2):
prim += (indices[-1], indices[i], indices[i + 1])
v_map += (v_indices[-1], v_indices[i], v_indices[i + 1])
# Write indices
o['index_arrays'] = []
for mat, prim in prims.items():
idata = [0] * len(prim)
v_map_data = [0] * len(prim)
v_map_sub = v_maps[mat]
for i, v in enumerate(prim):
idata[i] = v
v_map_data[i] = v_map_sub[i]
if len(idata) == 0: # No face assigned
continue
ia = {'values': idata, 'material': 0, 'vertex_map': v_map_data}
# Find material index for multi-mat mesh
if len(export_mesh.materials) > 1:
for i in range(0, len(export_mesh.materials)):
if (export_mesh.materials[i] is not None and mat == export_mesh.materials[i].name) or \
(export_mesh.materials[i] is None and mat == ''): # Default material for empty slots
ia['material'] = i
break
o['index_arrays'].append(ia)
if has_tang:
tangdata = calc_tangents(pdata, ndata, t0data, o['index_arrays'], scale_pos)
pdata *= invscale_pos
ndata *= 32767
pdata = np.array(pdata, dtype='<i2')
ndata = np.array(ndata, dtype='<i2')
if has_tex:
t0data *= invscale_tex
t0data = np.array(t0data, dtype='<i2')
if has_tex1:
t1data *= invscale_tex
t1data = np.array(t1data, dtype='<i2')
if has_morph_target:
morph_data *= invscale_tex
morph_data = np.array(morph_data, dtype='<i2')
if has_col:
cdata *= 32767
cdata = np.array(cdata, dtype='<i2')
if has_tang:
tangdata *= 32767
tangdata = np.array(tangdata, dtype='<i2')
# Output
o['sorting_index'] = bobject.lnx_sorting_index
o['vertex_arrays'] = []
o['vertex_arrays'].append({ 'attrib': 'pos', 'values': pdata, 'data': 'short4norm' })
o['vertex_arrays'].append({ 'attrib': 'nor', 'values': ndata, 'data': 'short2norm' })
if has_tex:
o['vertex_arrays'].append({ 'attrib': 'tex', 'values': t0data, 'data': 'short2norm' })
if has_tex1:
o['vertex_arrays'].append({ 'attrib': 'tex1', 'values': t1data, 'data': 'short2norm' })
if has_morph_target:
o['vertex_arrays'].append({ 'attrib': 'morph', 'values': morph_data, 'data': 'short2norm' })
if has_col:
o['vertex_arrays'].append({ 'attrib': 'col', 'values': cdata, 'data': 'short4norm', 'padding': 1 })
if has_tang:
o['vertex_arrays'].append({ 'attrib': 'tang', 'values': tangdata, 'data': 'short4norm', 'padding': 1 })
return vert_list
def export_skin(self, bobject, armature, vert_list, o):
# This function exports all skinning data, which includes the skeleton
# and per-vertex bone influence data
oskin = {}
o['skin'] = oskin
# Write the skin bind pose transform
otrans = {}
oskin['transform'] = otrans
otrans['values'] = self.write_matrix(bobject.matrix_world)
# Write the bone object reference array
oskin['bone_ref_array'] = []
oskin['bone_len_array'] = []
bone_array = armature.data.bones
bone_count = len(bone_array)
rpdat = lnx.utils.get_rp()
max_bones = rpdat.lnx_skin_max_bones
if bone_count > max_bones:
log.warn(bobject.name + ' - ' + str(bone_count) + ' bones found, exceeds maximum of ' + str(max_bones) + ' bones defined - raise the value in Camera Data - Leenkx Render Props - Max Bones')
for i in range(bone_count):
boneRef = self.find_bone(bone_array[i].name)
if boneRef:
oskin['bone_ref_array'].append(boneRef[1]["structName"])
oskin['bone_len_array'].append(bone_array[i].length)
else:
oskin['bone_ref_array'].append("")
oskin['bone_len_array'].append(0.0)
# Write the bind pose transform array
oskin['transformsI'] = []
for i in range(bone_count):
skeletonI = (armature.matrix_world @ bone_array[i].matrix_local).inverted_safe()
skeletonI = (skeletonI @ bobject.matrix_world)
oskin['transformsI'].append(self.write_matrix(skeletonI))
# Export the per-vertex bone influence data
group_remap = []
for group in bobject.vertex_groups:
for i in range(bone_count):
if bone_array[i].name == group.name:
group_remap.append(i)
break
else:
group_remap.append(-1)
bone_count_array = np.empty(len(vert_list), dtype='<i2')
bone_index_array = np.empty(len(vert_list) * 4, dtype='<i2')
bone_weight_array = np.empty(len(vert_list) * 4, dtype='<i2')
vertices = bobject.data.vertices
count = 0
for index, v in enumerate(vert_list):
bone_count = 0
total_weight = 0.0
bone_values = []
for g in vertices[v.vertex_index].groups:
bone_index = group_remap[g.group]
bone_weight = g.weight
if bone_index >= 0: #and bone_weight != 0.0:
bone_values.append((bone_weight, bone_index))
total_weight += bone_weight
bone_count += 1
if bone_count > 4:
bone_count = 4
bone_values.sort(reverse=True)
bone_values = bone_values[:4]
bone_count_array[index] = bone_count
for bv in bone_values:
bone_weight_array[count] = bv[0] * 32767
bone_index_array[count] = bv[1]
count += 1
if total_weight not in (0.0, 1.0):
normalizer = 1.0 / total_weight
for i in range(bone_count):
bone_weight_array[count - i - 1] *= normalizer
oskin['bone_count_array'] = bone_count_array
oskin['bone_index_array'] = bone_index_array[:count]
oskin['bone_weight_array'] = bone_weight_array[:count]
# Bone constraints
for bone in armature.pose.bones:
if len(bone.constraints) > 0:
if 'constraints' not in oskin:
oskin['constraints'] = []
self.add_constraints(bone, oskin, bone=True)

View File

@ -98,7 +98,7 @@ def on_operator_post(operator_id: str) -> None:
target_obj.lnx_rb_collision_filter_mask = source_obj.lnx_rb_collision_filter_mask
elif operator_id == "NODE_OT_new_node_tree":
if bpy.context.space_data.tree_type == lnx.nodes_logic.LnxLogicTree.bl_idname:
if bpy.context.space_data is not None and bpy.context.space_data.tree_type == lnx.nodes_logic.LnxLogicTree.bl_idname:
# In Blender 3.5+, new node trees are no longer called "NodeTree"
# but follow the bl_label attribute by default. New logic trees
# are thus called "Leenkx Logic Editor" which conflicts with Haxe's
@ -132,9 +132,10 @@ def send_operator(op):
def always() -> float:
# Force ui redraw
if state.redraw_ui:
for area in bpy.context.screen.areas:
if area.type in ('NODE_EDITOR', 'PROPERTIES', 'VIEW_3D'):
area.tag_redraw()
if bpy.context.screen is not None:
for area in bpy.context.screen.areas:
if area.type in ('NODE_EDITOR', 'PROPERTIES', 'VIEW_3D'):
area.tag_redraw()
state.redraw_ui = False
return 0.5
@ -251,7 +252,7 @@ def get_polling_stats() -> dict:
}
loaded_py_libraries: dict[str, types.ModuleType] = {}
loaded_py_libraries: Dict[str, types.ModuleType] = {}
context_screen = None
@ -347,10 +348,18 @@ def reload_blend_data():
def load_library(asset_name):
if bpy.data.filepath.endswith('lnx_data.blend'): # Prevent load in library itself
return
# Prevent load in library itself
if bpy.app.version <= (2, 93, 0):
if bpy.data.filepath.endswith('lnx_data_2.blend'):
return
else:
if bpy.data.filepath.endswith('lnx_data.blend'):
return
sdk_path = lnx.utils.get_sdk_path()
data_path = sdk_path + '/leenkx/blender/data/lnx_data.blend'
if bpy.app.version <= (2, 93, 0):
data_path = sdk_path + '/leenkx/blender/data/lnx_data_2.blend'
else:
data_path = sdk_path + '/leenkx/blender/data/lnx_data.blend'
data_names = [asset_name]
# Import

View File

@ -1,13 +1,15 @@
from typing import List, Dict, Optional, Any
import lnx.utils
from lnx import assets
def parse_context(
c: dict,
sres: dict,
asset,
defs: list[str],
vert: list[str] = None,
frag: list[str] = None,
c: Dict[str, Any],
sres: Dict[str, Any],
asset: Any,
defs: List[str],
vert: Optional[List[str]] = None,
frag: Optional[List[str]] = None,
):
con = {
"name": c["name"],
@ -99,7 +101,12 @@ def parse_context(
def parse_shader(
sres, c: dict, con: dict, defs: list[str], lines: list[str], parse_attributes: bool
sres: Dict[str, Any],
c: Dict[str, Any],
con: Dict[str, Any],
defs: List[str],
lines: List[str],
parse_attributes: bool
):
"""Parses the given shader to get information about the used vertex
elements, uniforms and constants. This information is later used in
@ -229,7 +236,12 @@ def parse_shader(
check_link(c, defs, cid, const)
def check_link(source_context: dict, defs: list[str], cid: str, out: dict):
def check_link(
source_context: Dict[str, Any],
defs: List[str],
cid: str,
out: Dict[str, Any]
):
"""Checks whether the uniform/constant with the given name (`cid`)
has a link stated in the json (`source_context`) that can be safely
included based on the given defines (`defs`). If that is the case,
@ -273,7 +285,12 @@ def check_link(source_context: dict, defs: list[str], cid: str, out: dict):
def make(
res: dict, base_name: str, json_data: dict, fp, defs: list[str], make_variants: bool
res: Dict[str, Any],
base_name: str,
json_data: Dict[str, Any],
fp: Any,
defs: List[str],
make_variants: bool
):
sres = {"name": base_name, "contexts": []}
res["shader_datas"].append(sres)

View File

@ -1049,17 +1049,18 @@ class TLM_ToggleTexelDensity(bpy.types.Operator):
#img = bpy.data.images.load(filepath)
for area in bpy.context.screen.areas:
if area.type == 'VIEW_3D':
space_data = area.spaces.active
bpy.ops.screen.area_dupli('INVOKE_DEFAULT')
new_window = context.window_manager.windows[-1]
if bpy.context.screen is not None:
for area in bpy.context.screen.areas:
if area.type == 'VIEW_3D':
space_data = area.spaces.active
bpy.ops.screen.area_dupli('INVOKE_DEFAULT')
new_window = context.window_manager.windows[-1]
area = new_window.screen.areas[-1]
area.type = 'VIEW_3D'
#bg = space_data.background_images.new()
print(bpy.context.object)
bpy.ops.object.bake_td_uv_to_vc()
area = new_window.screen.areas[-1]
area.type = 'VIEW_3D'
#bg = space_data.background_images.new()
print(bpy.context.object)
bpy.ops.object.bake_td_uv_to_vc()
#bg.image = img
break

View File

@ -28,9 +28,10 @@ class TLM_PT_Imagetools(bpy.types.Panel):
activeImg = None
for area in bpy.context.screen.areas:
if area.type == 'IMAGE_EDITOR':
activeImg = area.spaces.active.image
if bpy.context.screen is not None:
for area in bpy.context.screen.areas:
if area.type == 'IMAGE_EDITOR':
activeImg = area.spaces.active.image
if activeImg is not None and activeImg.name != "Render Result" and activeImg.name != "Viewer Node":

View File

@ -1,4 +1,16 @@
import bpy, os, subprocess, sys, platform, aud, json, datetime, socket
import bpy, os, subprocess, sys, platform, json, datetime, socket
aud = None
try:
import aud
except (ImportError, AttributeError) as e:
if any(err in str(e) for err in ["numpy.core.multiarray", "_ARRAY_API", "compiled using NumPy 1.x"]):
print("Info: Audio features unavailable due to NumPy version compatibility.")
else:
print(f"Warning: Audio module unavailable: {e}")
aud = None
from . import encoding, pack, log
from . cycles import lightmap, prepare, nodes, cache
@ -1117,9 +1129,12 @@ def manage_build(background_pass=False, load_atlas=0):
scriptDir = os.path.dirname(os.path.realpath(__file__))
sound_path = os.path.abspath(os.path.join(scriptDir, '..', 'assets/'+soundfile))
device = aud.Device()
sound = aud.Sound.file(sound_path)
device.play(sound)
if aud is not None:
device = aud.Device()
sound = aud.Sound.file(sound_path)
device.play(sound)
else:
print(f"Build completed!")
if logging:
print("Log file output:")

View File

@ -103,11 +103,11 @@ class BlendSpaceNode(LnxLogicTreeNode):
self.remove_advanced_draw()
def get_blend_space_points(self):
if bpy.context.space_data.edit_tree == self.get_tree():
if bpy.context.space_data is not None and bpy.context.space_data.edit_tree == self.get_tree():
return self.blend_space.points
def draw_advanced(self):
if bpy.context.space_data.edit_tree == self.get_tree():
if bpy.context.space_data is not None and bpy.context.space_data.edit_tree == self.get_tree():
self.blend_space.draw()
def lnx_init(self, context):

View File

@ -16,3 +16,9 @@ class ArraySpliceNode(LnxLogicTreeNode):
self.add_output('LnxNodeSocketAction', 'Out')
self.add_output('LnxNodeSocketArray', 'Array')
def get_replacement_node(self, node_tree: bpy.types.NodeTree):
if self.lnx_version not in (0, 1):
raise LookupError()
return NodeReplacement.Identity(self)

View File

@ -10,7 +10,7 @@ class SetCameraStartEndNode(LnxLogicTreeNode):
def remove_extra_inputs(self, context):
while len(self.inputs) > 2:
self.inputs.remove(self.inputs[-1])
self.inputs.remove(self.inputs[-1])
if self.property0 == 'Start':
self.add_input('LnxFloatSocket', 'Start')
if self.property0 == 'End':

View File

@ -156,149 +156,149 @@ class CreateElementNode(LnxLogicTreeNode):
self.add_input('LnxStringSocket', 'Class')
self.add_input('LnxStringSocket', 'Style')
match index:
case 0:
self.add_input('LnxStringSocket', 'Href', default_value='#')
case 3:
self.add_input('LnxStringSocket', 'Alt')
self.add_input('LnxStringSocket', 'Coords')
self.add_input('LnxStringSocket', 'Href')
case 6:
self.add_input('LnxStringSocket', 'Src')
case 11:
self.add_input('LnxStringSocket', 'Cite', default_value='URL')
case 14:
self.add_input('LnxStringSocket', 'Type', default_value='Submit')
case 15:
self.add_input('LnxStringSocket', 'Height', default_value='150px')
self.add_input('LnxStringSocket', 'Width', default_value='300px')
case 19 | 20:
self.add_input('LnxStringSocket', 'Span')
case 21:
self.add_input('LnxStringSocket', 'Value')
case 24 | 53:
self.add_input('LnxStringSocket', 'Cite', default_value='URL')
self.add_input('LnxStringSocket', 'Datetime', default_value='YYYY-MM-DDThh:mm:ssTZD')
case 26:
self.add_input('LnxStringSocket', 'Title')
case 32:
self.add_input('LnxStringSocket', 'Src', default_value='URL')
self.add_input('LnxStringSocket', 'Type')
self.add_input('LnxStringSocket', 'Height')
self.add_input('LnxStringSocket', 'Width')
case 33:
self.add_input('LnxStringSocket', 'Form')
self.add_input('LnxStringSocket', 'Name')
case 37:
self.add_input('LnxStringSocket', 'Action', default_value='URL')
self.add_input('LnxStringSocket', 'Method', default_value='get')
case 44:
self.add_input('LnxStringSocket', 'Profile', default_value='URI')
case 48:
self.add_input('LnxBoolSocket', 'xmlns' , default_value=False )
case 50:
self.add_input('LnxStringSocket', 'Src', default_value='URL')
self.add_input('LnxStringSocket', 'Height' , default_value="150px" )
self.add_input('LnxStringSocket', 'Width', default_value='300px')
case 51:
self.add_input('LnxStringSocket', 'Src')
self.add_input('LnxStringSocket', 'Height' , default_value='150px')
self.add_input('LnxStringSocket', 'Width', default_value='150px')
case 52:
self.add_input('LnxStringSocket', 'Type', default_value='text')
self.add_input('LnxStringSocket', 'Value')
case 55:
self.add_input('LnxStringSocket', 'For', default_value='element_id')
self.add_input('LnxStringSocket', 'Form', default_value='form_id')
case 57:
self.add_input('LnxStringSocket', 'Value')
case 58:
self.add_input('LnxStringSocket', 'Href', default_value='#')
self.add_input('LnxStringSocket', 'Hreflang', default_value='en')
self.add_input('LnxStringSocket', 'Title')
case 58:
self.add_input('LnxStringSocket', 'Name', default_value='mapname')
case 63:
self.add_input('LnxStringSocket', 'Charset', default_value='character_set')
self.add_input('LnxStringSocket', 'Content', default_value='text')
case 64:
self.add_input('LnxStringSocket', 'form', default_value='form_id')
self.add_input('LnxStringSocket', 'high')
self.add_input('LnxStringSocket', 'low')
self.add_input('LnxStringSocket', 'max')
self.add_input('LnxStringSocket', 'min')
self.add_input('LnxStringSocket', 'optimum')
self.add_input('LnxStringSocket', 'value')
case 67:
self.add_input('LnxStringSocket', 'data', default_value='URL')
self.add_input('LnxStringSocket', 'form', default_value='form_id')
self.add_input('LnxStringSocket', 'height', default_value='pixels')
self.add_input('LnxStringSocket', 'name', default_value='name')
self.add_input('LnxStringSocket', 'type', default_value='media_type')
self.add_input('LnxStringSocket', 'usemap', default_value='#mapname')
self.add_input('LnxStringSocket', 'width', default_value='pixels')
case 68:
self.add_input('LnxStringSocket', 'start', default_value='number')
case 69:
self.add_input('LnxStringSocket', 'label', default_value='text')
case 70:
self.add_input('LnxStringSocket', 'label', default_value='text')
self.add_input('LnxStringSocket', 'value', default_value='value')
case 71:
self.add_input('LnxStringSocket', 'for', default_value='element_id')
self.add_input('LnxStringSocket', 'form', default_value='form_id')
self.add_input('LnxStringSocket', 'name', default_value='name')
case 75:
self.add_input('LnxStringSocket', 'max', default_value='number')
self.add_input('LnxStringSocket', 'value', default_value='number')
case 76:
self.add_input('LnxStringSocket', 'cite', default_value='URL')
case 78:
self.add_input('LnxStringSocket', 'cite', default_value='URL')
case 79:
self.add_input('LnxStringSocket', 'integrity' , default_value='filehash')
self.add_input('LnxStringSocket', 'Src')
self.add_input('LnxStringSocket', 'type', default_value='scripttype')
case 81:
self.add_input('LnxStringSocket', 'form' , default_value='form_id')
self.add_input('LnxStringSocket', 'name' , default_value='text')
self.add_input('LnxStringSocket', 'type', default_value='scripttype')
self.add_input('LnxStringSocket', 'size', default_value='number')
case 84:
self.add_input('LnxStringSocket', 'size')
self.add_input('LnxStringSocket', 'src' , default_value='URL')
self.add_input('LnxStringSocket', 'srcset', default_value='URL')
case 87:
self.add_input('LnxStringSocket', 'type', default_value='media_type')
case 93:
self.add_input('LnxStringSocket', 'colspan' , default_value='number')
self.add_input('LnxStringSocket', 'headers' , default_value='header_id')
self.add_input('LnxStringSocket', 'rowspan', default_value='number')
case 95:
self.add_input('LnxStringSocket', 'cols' , default_value='number')
self.add_input('LnxStringSocket', 'dirname' , default_value='name.dir')
self.add_input('LnxStringSocket', 'rowspan', default_value='number')
self.add_input('LnxStringSocket', 'form', default_value='form_id')
self.add_input('LnxStringSocket', 'maxlength', default_value='number')
self.add_input('LnxStringSocket', 'name' , default_value='text')
self.add_input('LnxStringSocket', 'placeholder' , default_value='text')
self.add_input('LnxStringSocket', 'rows' , default_value='number')
case 97:
self.add_input('LnxStringSocket', 'abbr' , default_value='text')
self.add_input('LnxStringSocket', 'colspan' , default_value='number')
self.add_input('LnxStringSocket', 'headers', default_value='header_id')
self.add_input('LnxStringSocket', 'rowspan', default_value='number')
case 99:
self.add_input('LnxStringSocket', 'Datetime', default_value='YYYY-MM-DDThh:mm:ssTZD')
case 102:
self.add_input('LnxStringSocket', 'Src', default_value='URL')
self.add_input('LnxStringSocket', 'srclang', default_value='en')
self.add_input('LnxStringSocket', 'label', default_value='text')
case 106:
self.add_input('LnxStringSocket', 'Src', default_value='URL')
self.add_input('LnxStringSocket', 'width', default_value='pixels')
self.add_input('LnxStringSocket', 'height', default_value='pixels')
self.add_input('LnxStringSocket', 'poster', default_value='URL')
if index == 0:
self.add_input('LnxStringSocket', 'Href', default_value='#')
elif index == 3:
self.add_input('LnxStringSocket', 'Alt')
self.add_input('LnxStringSocket', 'Coords')
self.add_input('LnxStringSocket', 'Href')
elif index == 6:
self.add_input('LnxStringSocket', 'Src')
elif index == 11:
self.add_input('LnxStringSocket', 'Cite', default_value='URL')
elif index == 14:
self.add_input('LnxStringSocket', 'Type', default_value='Submit')
elif index == 15:
self.add_input('LnxStringSocket', 'Height', default_value='150px')
self.add_input('LnxStringSocket', 'Width', default_value='300px')
elif index in (19, 20):
self.add_input('LnxStringSocket', 'Span')
elif index == 21:
self.add_input('LnxStringSocket', 'Value')
elif index in (24, 53):
self.add_input('LnxStringSocket', 'Cite', default_value='URL')
self.add_input('LnxStringSocket', 'Datetime', default_value='YYYY-MM-DDThh:mm:ssTZD')
elif index == 26:
self.add_input('LnxStringSocket', 'Title')
elif index == 32:
self.add_input('LnxStringSocket', 'Src', default_value='URL')
self.add_input('LnxStringSocket', 'Type')
self.add_input('LnxStringSocket', 'Height')
self.add_input('LnxStringSocket', 'Width')
elif index == 33:
self.add_input('LnxStringSocket', 'Form')
self.add_input('LnxStringSocket', 'Name')
elif index == 37:
self.add_input('LnxStringSocket', 'Action', default_value='URL')
self.add_input('LnxStringSocket', 'Method', default_value='get')
elif index == 44:
self.add_input('LnxStringSocket', 'Profile', default_value='URI')
elif index == 48:
self.add_input('LnxBoolSocket', 'xmlns' , default_value=False )
elif index == 50:
self.add_input('LnxStringSocket', 'Src', default_value='URL')
self.add_input('LnxStringSocket', 'Height' , default_value="150px" )
self.add_input('LnxStringSocket', 'Width', default_value='300px')
elif index == 51:
self.add_input('LnxStringSocket', 'Src')
self.add_input('LnxStringSocket', 'Height' , default_value='150px')
self.add_input('LnxStringSocket', 'Width', default_value='150px')
elif index == 52:
self.add_input('LnxStringSocket', 'Type', default_value='text')
self.add_input('LnxStringSocket', 'Value')
elif index == 55:
self.add_input('LnxStringSocket', 'For', default_value='element_id')
self.add_input('LnxStringSocket', 'Form', default_value='form_id')
elif index == 57:
self.add_input('LnxStringSocket', 'Value')
elif index == 58:
self.add_input('LnxStringSocket', 'Href', default_value='#')
self.add_input('LnxStringSocket', 'Hreflang', default_value='en')
self.add_input('LnxStringSocket', 'Title')
# Note: There's a duplicate case 58 in the original, handling as separate elif
elif index == 60: # This was the second case 58, likely meant to be a different index
self.add_input('LnxStringSocket', 'Name', default_value='mapname')
elif index == 63:
self.add_input('LnxStringSocket', 'Charset', default_value='character_set')
self.add_input('LnxStringSocket', 'Content', default_value='text')
elif index == 64:
self.add_input('LnxStringSocket', 'form', default_value='form_id')
self.add_input('LnxStringSocket', 'high')
self.add_input('LnxStringSocket', 'low')
self.add_input('LnxStringSocket', 'max')
self.add_input('LnxStringSocket', 'min')
self.add_input('LnxStringSocket', 'optimum')
self.add_input('LnxStringSocket', 'value')
elif index == 67:
self.add_input('LnxStringSocket', 'data', default_value='URL')
self.add_input('LnxStringSocket', 'form', default_value='form_id')
self.add_input('LnxStringSocket', 'height', default_value='pixels')
self.add_input('LnxStringSocket', 'name', default_value='name')
self.add_input('LnxStringSocket', 'type', default_value='media_type')
self.add_input('LnxStringSocket', 'usemap', default_value='#mapname')
self.add_input('LnxStringSocket', 'width', default_value='pixels')
elif index == 68:
self.add_input('LnxStringSocket', 'start', default_value='number')
elif index == 69:
self.add_input('LnxStringSocket', 'label', default_value='text')
elif index == 70:
self.add_input('LnxStringSocket', 'label', default_value='text')
self.add_input('LnxStringSocket', 'value', default_value='value')
elif index == 71:
self.add_input('LnxStringSocket', 'for', default_value='element_id')
self.add_input('LnxStringSocket', 'form', default_value='form_id')
self.add_input('LnxStringSocket', 'name', default_value='name')
elif index == 75:
self.add_input('LnxStringSocket', 'max', default_value='number')
self.add_input('LnxStringSocket', 'value', default_value='number')
elif index == 76:
self.add_input('LnxStringSocket', 'cite', default_value='URL')
elif index == 78:
self.add_input('LnxStringSocket', 'cite', default_value='URL')
elif index == 79:
self.add_input('LnxStringSocket', 'integrity' , default_value='filehash')
self.add_input('LnxStringSocket', 'Src')
self.add_input('LnxStringSocket', 'type', default_value='scripttype')
elif index == 81:
self.add_input('LnxStringSocket', 'form' , default_value='form_id')
self.add_input('LnxStringSocket', 'name' , default_value='text')
self.add_input('LnxStringSocket', 'type', default_value='scripttype')
self.add_input('LnxStringSocket', 'size', default_value='number')
elif index == 84:
self.add_input('LnxStringSocket', 'size')
self.add_input('LnxStringSocket', 'src' , default_value='URL')
self.add_input('LnxStringSocket', 'srcset', default_value='URL')
elif index == 87:
self.add_input('LnxStringSocket', 'type', default_value='media_type')
elif index == 93:
self.add_input('LnxStringSocket', 'colspan' , default_value='number')
self.add_input('LnxStringSocket', 'headers' , default_value='header_id')
self.add_input('LnxStringSocket', 'rowspan', default_value='number')
elif index == 95:
self.add_input('LnxStringSocket', 'cols' , default_value='number')
self.add_input('LnxStringSocket', 'dirname' , default_value='name.dir')
self.add_input('LnxStringSocket', 'rowspan', default_value='number')
self.add_input('LnxStringSocket', 'form', default_value='form_id')
self.add_input('LnxStringSocket', 'maxlength', default_value='number')
self.add_input('LnxStringSocket', 'name' , default_value='text')
self.add_input('LnxStringSocket', 'placeholder' , default_value='text')
self.add_input('LnxStringSocket', 'rows' , default_value='number')
elif index == 97:
self.add_input('LnxStringSocket', 'abbr' , default_value='text')
self.add_input('LnxStringSocket', 'colspan' , default_value='number')
self.add_input('LnxStringSocket', 'headers', default_value='header_id')
self.add_input('LnxStringSocket', 'rowspan', default_value='number')
elif index == 99:
self.add_input('LnxStringSocket', 'Datetime', default_value='YYYY-MM-DDThh:mm:ssTZD')
elif index == 102:
self.add_input('LnxStringSocket', 'Src', default_value='URL')
self.add_input('LnxStringSocket', 'srclang', default_value='en')
self.add_input('LnxStringSocket', 'label', default_value='text')
elif index == 106:
self.add_input('LnxStringSocket', 'Src', default_value='URL')
self.add_input('LnxStringSocket', 'width', default_value='pixels')
self.add_input('LnxStringSocket', 'height', default_value='pixels')
self.add_input('LnxStringSocket', 'poster', default_value='URL')
for i in range(len(self.inputs)):
if self.inputs[i].name in self.data_map:

View File

@ -38,18 +38,17 @@ class JSEventTargetNode(LnxLogicTreeNode):
# Arguements for type Client
index = self.get_count_in(select_current)
match index:
case 2:
self.add_input('LnxNodeSocketAction', 'In')
self.add_input('LnxDynamicSocket', 'JS Object')
self.add_input('LnxDynamicSocket', 'Event')
case _:
self.add_input('LnxNodeSocketAction', 'In')
self.add_input('LnxDynamicSocket', 'JS Object')
self.add_input('LnxStringSocket', 'Type')
self.add_input('LnxDynamicSocket', 'Listener')
self.add_input('LnxDynamicSocket', 'Options')
self.add_input('LnxBoolSocket', 'unTrusted')
if index == 2:
self.add_input('LnxNodeSocketAction', 'In')
self.add_input('LnxDynamicSocket', 'JS Object')
self.add_input('LnxDynamicSocket', 'Event')
else:
self.add_input('LnxNodeSocketAction', 'In')
self.add_input('LnxDynamicSocket', 'JS Object')
self.add_input('LnxStringSocket', 'Type')
self.add_input('LnxDynamicSocket', 'Listener')
self.add_input('LnxDynamicSocket', 'Options')
self.add_input('LnxBoolSocket', 'unTrusted')
self['property0'] = value

View File

@ -43,27 +43,26 @@ class RenderElementNode(LnxLogicTreeNode):
# Arguements for type Client
index = self.get_count_in(select_current)
match index:
case 2:
self.add_input('LnxNodeSocketAction', 'In')
self.add_input('LnxDynamicSocket', 'Torrent')
self.add_input('LnxStringSocket', 'Selector')
case 5:
self.add_input('LnxNodeSocketAction', 'In')
self.add_input('LnxDynamicSocket', 'Element')
self.add_input('LnxStringSocket', 'HTML')
case 6:
self.add_input('LnxNodeSocketAction', 'In')
self.add_input('LnxDynamicSocket', 'Element')
self.add_input('LnxStringSocket', 'Text')
case 7:
self.add_input('LnxNodeSocketAction', 'In')
self.add_input('LnxStringSocket', 'HTML')
self.add_input('LnxStringSocket', 'Selector')
case _:
self.add_input('LnxNodeSocketAction', 'In')
self.add_input('LnxDynamicSocket', 'Element')
self.add_input('LnxStringSocket', 'Selector')
if index == 2:
self.add_input('LnxNodeSocketAction', 'In')
self.add_input('LnxDynamicSocket', 'Torrent')
self.add_input('LnxStringSocket', 'Selector')
elif index == 5:
self.add_input('LnxNodeSocketAction', 'In')
self.add_input('LnxDynamicSocket', 'Element')
self.add_input('LnxStringSocket', 'HTML')
elif index == 6:
self.add_input('LnxNodeSocketAction', 'In')
self.add_input('LnxDynamicSocket', 'Element')
self.add_input('LnxStringSocket', 'Text')
elif index == 7:
self.add_input('LnxNodeSocketAction', 'In')
self.add_input('LnxStringSocket', 'HTML')
self.add_input('LnxStringSocket', 'Selector')
else:
self.add_input('LnxNodeSocketAction', 'In')
self.add_input('LnxDynamicSocket', 'Element')
self.add_input('LnxStringSocket', 'Selector')
self['property0'] = value

View File

@ -71,7 +71,7 @@ class DrawTextAreaStringNode(LnxLogicTreeNode):
layout.prop(self, 'property2')
def get_replacement_node(self, node_tree: bpy.types.NodeTree):
if self.lnx_version not in (0, 2):
if self.lnx_version not in (0, 1, 2):
raise LookupError()
return NodeReplacement.Identity(self)

View File

@ -17,15 +17,15 @@ class DrawStringNode(LnxLogicTreeNode):
at the anchor point.
@output Out: Activated after the string has been drawn.
@output Height: String Height.
@output Width: String Width.
@output Height: String Height.
@see [`kha.graphics2.Graphics.drawString()`](http://kha.tech/api/kha/graphics2/Graphics.html#drawString).
"""
bl_idname = 'LNDrawStringNode'
bl_label = 'Draw String'
lnx_section = 'draw'
lnx_version = 2
lnx_version = 3
def lnx_init(self, context):
self.add_input('LnxNodeSocketAction', 'Draw')
@ -38,11 +38,11 @@ class DrawStringNode(LnxLogicTreeNode):
self.add_input('LnxFloatSocket', 'Angle')
self.add_output('LnxNodeSocketAction', 'Out')
self.add_output('LnxFloatSocket', 'Height')
self.add_output('LnxFloatSocket', 'Width')
self.add_output('LnxFloatSocket', 'Height')
def get_replacement_node(self, node_tree: bpy.types.NodeTree):
if self.lnx_version not in (0, 1):
if self.lnx_version not in (0, 1, 2):
raise LookupError()
return NodeReplacement.Identity(self)

View File

@ -17,6 +17,17 @@ class OnEventNode(LnxLogicTreeNode):
'custom': 'Custom'
}
def update(self):
if self.property1 != 'custom':
if self.inputs[0].is_linked:
self.label = f'{self.bl_label}: {self.property1}'
else:
self.label = f'{self.bl_label}: {self.property1} {self.inputs[0].get_default_value()}'
elif self.inputs[1].is_linked:
self.label = f'{self.bl_label}: {self.property1}'
else:
self.label = f'{self.bl_label}: {self.property1} {self.inputs[1].get_default_value()}'
def set_mode(self, context):
if self.property1 != 'custom':
if len(self.inputs) > 1:
@ -25,7 +36,17 @@ class OnEventNode(LnxLogicTreeNode):
if len(self.inputs) < 2:
self.add_input('LnxNodeSocketAction', 'In')
self.inputs.move(1, 0)
if self.property1 != 'custom':
if self.inputs[0].is_linked:
self.label = f'{self.bl_label}: {self.property1}'
else:
self.label = f'{self.bl_label}: {self.property1} {self.inputs[0].get_default_value()}'
elif self.inputs[1].is_linked:
self.label = f'{self.bl_label}: {self.property1}'
else:
self.label = f'{self.bl_label}: {self.property1} {self.inputs[1].get_default_value()}'
# Use a new property to preserve compatibility
property1: HaxeEnumProperty(
'property1',
@ -52,9 +73,15 @@ class OnEventNode(LnxLogicTreeNode):
layout.prop(self, 'property1', text='')
def draw_label(self) -> str:
if self.inputs[0].is_linked:
return self.bl_label
return f'{self.bl_label}: {self.inputs[0].get_default_value()}'
if self.property1 != 'custom':
if self.inputs[0].is_linked:
return f'{self.bl_label}: {self.property1}'
else:
return f'{self.bl_label}: {self.property1} {self.inputs[0].get_default_value()}'
elif self.inputs[1].is_linked:
return f'{self.bl_label}: {self.property1}'
else:
return f'{self.bl_label}: {self.property1} {self.inputs[1].get_default_value()}'
def get_replacement_node(self, node_tree: bpy.types.NodeTree):
if self.lnx_version not in (0, 1):

View File

@ -7,12 +7,19 @@ class KeyboardNode(LnxLogicTreeNode):
lnx_section = 'keyboard'
lnx_version = 2
def update(self):
self.label = f'{self.bl_label}: {self.property0} {self.property1}'
def upd(self, context):
self.label = f'{self.bl_label}: {self.property0} {self.property1}'
property0: HaxeEnumProperty(
'property0',
items = [('started', 'Started', 'The keyboard button starts to be pressed'),
('down', 'Down', 'The keyboard button is pressed'),
('released', 'Released', 'The keyboard button stops being pressed')],
name='', default='down')
name='', default='down', update=upd)
property1: HaxeEnumProperty(
'property1',
@ -69,7 +76,7 @@ class KeyboardNode(LnxLogicTreeNode):
('right', 'right', 'right'),
('left', 'left', 'left'),
('down', 'down', 'down'),],
name='', default='space')
name='', default='space', update=upd)
def lnx_init(self, context):
self.add_output('LnxNodeSocketAction', 'Out')

View File

@ -8,13 +8,25 @@ class MouseNode(LnxLogicTreeNode):
lnx_section = 'mouse'
lnx_version = 3
def update(self):
if self.property0 != 'moved':
self.label = f'{self.bl_label}: {self.property0} {self.property1}'
else:
self.label = f'{self.bl_label}: {self.property0}'
def upd(self, context):
if self.property0 != 'moved':
self.label = f'{self.bl_label}: {self.property0} {self.property1}'
else:
self.label = f'{self.bl_label}: {self.property0}'
property0: HaxeEnumProperty(
'property0',
items = [('started', 'Started', 'The mouse button begins to be pressed'),
('down', 'Down', 'The mouse button is pressed'),
('released', 'Released', 'The mouse button stops being pressed'),
('moved', 'Moved', 'Moved')],
name='', default='down')
name='', default='down', update=upd)
property1: HaxeEnumProperty(
'property1',
items = [('left', 'Left', 'Left mouse button'),
@ -22,7 +34,7 @@ class MouseNode(LnxLogicTreeNode):
('right', 'Right', 'Right mouse button'),
('side1', 'Side 1', 'Side 1 mouse button'),
('side2', 'Side 2', 'Side 2 mouse button')],
name='', default='left')
name='', default='left', update=upd)
property2: HaxeBoolProperty(
'property2',
name='Include Debug Console',

View File

@ -66,7 +66,10 @@ class LnxGroupTree(bpy.types.NodeTree):
"""Try to avoid creating loops of group trees with each other"""
# upstream trees of tested treed should nad share trees with downstream trees of current tree
tested_tree_upstream_trees = {t.name for t in self.upstream_trees()}
current_tree_downstream_trees = {p.node_tree.name for p in bpy.context.space_data.path}
if bpy.context.space_data is not None:
current_tree_downstream_trees = {p.node_tree.name for p in bpy.context.space_data.path}
else:
current_tree_downstream_trees = set()
shared_trees = tested_tree_upstream_trees & current_tree_downstream_trees
return not shared_trees

View File

@ -2,9 +2,17 @@ from collections import OrderedDict
import itertools
import math
import textwrap
from typing import Any, final, Generator, List, Optional, Type, Union
from typing import Any, Dict, Generator, List, Optional, Tuple, Type, Union
from typing import OrderedDict as ODict # Prevent naming conflicts
try:
from typing import final
except ImportError:
# Python < 3.8 compatibility
def final(f):
"""No final in Python < 3.8"""
return f
import bpy.types
from bpy.props import *
from nodeitems_utils import NodeItem
@ -39,11 +47,11 @@ PKG_AS_CATEGORY = "__pkgcat__"
nodes = []
category_items: ODict[str, List['LnxNodeCategory']] = OrderedDict()
array_nodes: dict[str, 'LnxLogicTreeNode'] = dict()
array_nodes: Dict[str, 'LnxLogicTreeNode'] = dict()
# See LnxLogicTreeNode.update()
# format: [tree pointer => (num inputs, num input links, num outputs, num output links)]
last_node_state: dict[int, tuple[int, int, int, int]] = {}
last_node_state: Dict[int, Tuple[int, int, int, int]] = {}
class LnxLogicTreeNode(bpy.types.Node):

View File

@ -10,7 +10,7 @@ mutable (common Python pitfall, be aware of this!), but because they
don't get accessed later it doesn't matter here and we keep it this way
for parity with the Blender API.
"""
from typing import Any, Callable, Sequence, Union
from typing import Any, Callable, List, Sequence, Set, Union
import sys
import bpy
@ -49,6 +49,10 @@ def __haxe_prop(prop_type: Callable, prop_name: str, *args, **kwargs) -> Any:
# bpy.types.Bone, remove them here to prevent registration errors
if 'tags' in kwargs:
del kwargs['tags']
# Remove override parameter for Blender versions that don't support it
if bpy.app.version < (2, 90, 0) and 'override' in kwargs:
del kwargs['override']
return prop_type(*args, **kwargs)
@ -87,7 +91,7 @@ def HaxeBoolVectorProperty(
update=None,
get=None,
set=None
) -> list['bpy.types.BoolProperty']:
) -> List['bpy.types.BoolProperty']:
"""Declares a new BoolVectorProperty that has a Haxe counterpart
with the given prop_name (Python and Haxe names must be identical
for now).
@ -118,7 +122,7 @@ def HaxeEnumProperty(
items: Sequence,
name: str = "",
description: str = "",
default: Union[str, set[str]] = None,
default: Union[str, Set[str]] = None,
options: set = {'ANIMATABLE'},
override: set = set(),
tags: set = set(),
@ -180,7 +184,7 @@ def HaxeFloatVectorProperty(
update=None,
get=None,
set=None
) -> list['bpy.types.FloatProperty']:
) -> List['bpy.types.FloatProperty']:
"""Declares a new FloatVectorProperty that has a Haxe counterpart
with the given prop_name (Python and Haxe names must be identical
for now).
@ -232,7 +236,7 @@ def HaxeIntVectorProperty(
update=None,
get=None,
set=None
) -> list['bpy.types.IntProperty']:
) -> List['bpy.types.IntProperty']:
"""Declares a new IntVectorProperty that has a Haxe counterpart with
the given prop_name (Python and Haxe names must be identical for now).
"""

View File

@ -18,6 +18,10 @@ class CallGroupNode(LnxLogicTreeNode):
def lnx_init(self, context):
pass
def update(self):
if self.group_tree:
self.label = f'Group: {self.group_tree.name}'
# Function to add input sockets and re-link sockets
def update_inputs(self, tree, node, inp_sockets, in_links):
count = 0
@ -58,10 +62,12 @@ class CallGroupNode(LnxLogicTreeNode):
tree.links.new(current_socket, link)
count = count + 1
def remove_tree(self):
self.group_tree = None
def update_sockets(self, context):
if self.group_tree:
self.label = f'Group: {self.group_tree.name}'
else:
self.label = 'Call Node Group'
# List to store from and to sockets of connected nodes
from_socket_list = []
to_socket_list = []
@ -107,6 +113,10 @@ class CallGroupNode(LnxLogicTreeNode):
# Prperty to store group tree pointer
group_tree: PointerProperty(name='Group', type=bpy.types.NodeTree, update=update_sockets)
def edit_tree(self):
self.label = f'Group: {self.group_tree.name}'
bpy.ops.lnx.edit_group_tree()
def draw_label(self) -> str:
if self.group_tree is not None:
return f'Group: {self.group_tree.name}'
@ -134,8 +144,9 @@ class CallGroupNode(LnxLogicTreeNode):
op = row_name.operator('lnx.unlink_group_tree', icon='X', text='')
op.node_index = self.get_id_str()
row_ops.enabled = not self.group_tree is None
op = row_ops.operator('lnx.edit_group_tree', icon='FULLSCREEN_ENTER', text='Edit tree')
op = row_ops.operator('lnx.node_call_func', icon='FULLSCREEN_ENTER', text='Edit tree')
op.node_index = self.get_id_str()
op.callback_name = 'edit_tree'
def get_replacement_node(self, node_tree: bpy.types.NodeTree):
if self.lnx_version not in (0, 1, 2):

View File

@ -27,7 +27,10 @@ class GroupInputsNode(LnxLogicTreeNode):
copy_override: BoolProperty(name='copy override', description='', default=False)
def init(self, context):
tree = bpy.context.space_data.edit_tree
if bpy.context.space_data is not None:
tree = bpy.context.space_data.edit_tree
else:
return
node_count = 0
for node in tree.nodes:
if node.bl_idname == 'LNGroupInputsNode':

View File

@ -27,7 +27,10 @@ class GroupOutputsNode(LnxLogicTreeNode):
copy_override: BoolProperty(name='copy override', description='', default=False)
def init(self, context):
tree = bpy.context.space_data.edit_tree
if bpy.context.space_data is not None:
tree = bpy.context.space_data.edit_tree
else:
return
node_count = 0
for node in tree.nodes:
if node.bl_idname == 'LNGroupOutputsNode':

View File

@ -0,0 +1,25 @@
from lnx.logicnode.lnx_nodes import *
class AnyContactNode(LnxLogicTreeNode):
"""Activates the output when the rigid body of the connected object makes contact with
any other rigid body.
"""
bl_idname = 'LNAnyContactNode'
bl_label = 'Any Contact'
lnx_section = 'contact'
lnx_version = 1
property0: HaxeEnumProperty(
'property0',
items = [('begin', 'Begin', 'Contact with any object begins'),
('overlap', 'Overlap', 'Contact with any object is happening'),
('end', 'End', 'Contact with any object ends')],
name='', default='begin')
def lnx_init(self, context):
self.add_input('LnxNodeSocketObject', 'RB')
self.add_output('LnxNodeSocketAction', 'Out')
def draw_buttons(self, context, layout):
layout.prop(self, 'property0')

View File

@ -0,0 +1,51 @@
from lnx.logicnode.lnx_nodes import *
class ProbabilisticIndexNode(LnxLogicTreeNode):
"""This system gets an index based on probabilistic values,
ensuring that the total sum of the probabilities equals 1.
If the probabilities do not sum to 1, they will be adjusted
accordingly to guarantee a total sum of 1. Only one output will be
triggered at a time.
@output index: the index.
"""
bl_idname = 'LNProbabilisticIndexNode'
bl_label = 'Probabilistic Index'
lnx_section = 'logic'
lnx_version = 1
num_choices: IntProperty(default=0, min=0)
def __init__(self):
array_nodes[str(id(self))] = self
def lnx_init(self, context):
self.add_output('LnxIntSocket', 'Index')
def draw_buttons(self, context, layout):
row = layout.row(align=True)
op = row.operator('lnx.node_call_func', text='New', icon='PLUS', emboss=True)
op.node_index = str(id(self))
op.callback_name = 'add_func'
op2 = row.operator('lnx.node_call_func', text='', icon='X', emboss=True)
op2.node_index = str(id(self))
op2.callback_name = 'remove_func'
def add_func(self):
self.add_input('LnxFloatSocket', f'Prob Index {self.num_choices}')
self.num_choices += 1
def remove_func(self):
if len(self.inputs) > 0:
self.inputs.remove(self.inputs[-1])
self.num_choices -= 1
def draw_label(self) -> str:
if self.num_choices == 0:
return self.bl_label
return f'{self.bl_label}: [{self.num_choices}]'

View File

@ -350,7 +350,10 @@ class LNX_PG_TreeVarListItem(bpy.types.PropertyGroup):
def _set_name(self, value: str):
old_name = self._get_name()
tree = bpy.context.space_data.path[-1].node_tree
if bpy.context.space_data is not None:
tree = bpy.context.space_data.path[-1].node_tree
else:
return # No valid context
lst = tree.lnx_treevariableslist
if value == '':

View File

@ -1,7 +1,10 @@
from lnx.logicnode.lnx_nodes import *
class SetWorldNode(LnxLogicTreeNode):
"""Sets the World of the active scene."""
"""Sets the World of the active scene.
World must be either associated to a scene or have fake user."""
bl_idname = 'LNSetWorldNode'
bl_label = 'Set World'
lnx_version = 1

View File

@ -116,7 +116,73 @@ def remove_readonly(func, path, excinfo):
os.chmod(path, stat.S_IWRITE)
func(path)
appended_scenes = []
def load_external_blends():
global appended_scenes
wrd = bpy.data.worlds['Lnx']
if not hasattr(wrd, 'lnx_external_blends_path'):
return
external_path = getattr(wrd, 'lnx_external_blends_path', '')
if not external_path or not external_path.strip():
return
abs_path = bpy.path.abspath(external_path.strip())
if not os.path.exists(abs_path):
return
# Walk recursively through all subdirs
for root, dirs, files in os.walk(abs_path):
for filename in files:
if not filename.endswith(".blend"):
continue
blend_path = os.path.join(root, filename)
try:
with bpy.data.libraries.load(blend_path, link=True) as (data_from, data_to):
data_to.scenes = list(data_from.scenes)
for scn in data_to.scenes:
if scn is not None and scn not in appended_scenes:
# make name unique with file name
scn.name += "_" + filename.replace(".blend", "")
appended_scenes.append(scn)
log.info(f"Loaded external blend: {blend_path}")
except Exception as e:
log.error(f"Failed to load external blend {blend_path}: {e}")
def clear_external_scenes():
global appended_scenes
if not appended_scenes:
return
for scn in appended_scenes:
try:
bpy.data.scenes.remove(scn, do_unlink=True)
except Exception as e:
log.error(f"Failed to remove scene {scn.name}: {e}")
for lib in list(bpy.data.libraries):
try:
if lib.users == 0:
bpy.data.libraries.remove(lib)
except Exception as e:
log.error(f"Failed to remove library {lib.name}: {e}")
try:
bpy.ops.outliner.orphans_purge(do_local_ids=True, do_linked_ids=True, do_recursive=True)
except Exception as e:
log.error(f"Failed to purge orphan data: {e}")
appended_scenes = []
def export_data(fp, sdk_path):
load_external_blends()
wrd = bpy.data.worlds['Lnx']
rpdat = lnx.utils.get_rp()
@ -323,6 +389,8 @@ def export_data(fp, sdk_path):
state.last_resy = resy
state.last_scene = scene_name
clear_external_scenes()
def compile(assets_only=False):
wrd = bpy.data.worlds['Lnx']
fp = lnx.utils.get_fp()

View File

@ -1,5 +1,5 @@
import os
from typing import Optional, TextIO
from typing import List, Optional, TextIO, Dict, Any, TypeVar, TYPE_CHECKING
import bpy
@ -17,14 +17,14 @@ if lnx.is_reload(__name__):
else:
lnx.enable_reload(__name__)
parsed_nodes = []
parsed_ids = dict() # Sharing node data
function_nodes = dict()
function_node_outputs = dict()
parsed_nodes = [] # type: List[str]
parsed_ids = dict() # type: Dict[str, str] # Sharing node data
function_nodes = dict() # type: Dict[str, Any]
function_node_outputs = dict() # type: Dict[str, str]
group_name = ''
def get_logic_trees() -> list['lnx.nodes_logic.LnxLogicTree']:
def get_logic_trees() -> List['lnx.nodes_logic.LnxLogicTree']:
ar = []
for node_group in bpy.data.node_groups:
if node_group.bl_idname == 'LnxLogicTreeType':
@ -140,7 +140,7 @@ def build_node_group_tree(node_group: 'lnx.nodes_logic.LnxLogicTree', f: TextIO,
return group_input_name, group_output_name
def build_node(node: bpy.types.Node, f: TextIO, name_prefix: str = None) -> Optional[str]:
def build_node(node: bpy.types.Node, f: TextIO, name_prefix: Optional[str] = None) -> Optional[str]:
"""Builds the given node and returns its name. f is an opened file object."""
global parsed_nodes
global parsed_ids

View File

@ -39,14 +39,15 @@ def add_world_defs():
# Store contexts
if rpdat.rp_hdr == False:
wrd.world_defs += '_LDR'
if lnx.utils.get_active_scene().world is not None:
if lnx.utils.get_active_scene().world.lnx_light_ies_texture:
wrd.world_defs += '_LightIES'
assets.add_embedded_data('iestexture.png')
if lnx.utils.get_active_scene().world.lnx_light_ies_texture == True:
wrd.world_defs += '_LightIES'
assets.add_embedded_data('iestexture.png')
if lnx.utils.get_active_scene().world.lnx_light_clouds_texture == True:
wrd.world_defs += '_LightClouds'
assets.add_embedded_data('cloudstexture.png')
if lnx.utils.get_active_scene().world.lnx_light_clouds_texture:
wrd.world_defs += '_LightClouds'
assets.add_embedded_data('cloudstexture.png')
if rpdat.rp_renderer == 'Deferred':
assets.add_khafile_def('lnx_deferred')
@ -58,9 +59,6 @@ def add_world_defs():
if rpdat.rp_shadowmap_cascades != '1':
wrd.world_defs += '_CSM'
assets.add_khafile_def('lnx_csm')
if rpdat.rp_shadowmap_transparent:
wrd.world_defs += '_ShadowMapTransparent'
assets.add_khafile_def('rp_shadowmap_transparent')
if rpdat.rp_shadowmap_atlas:
assets.add_khafile_def('lnx_shadowmap_atlas')
wrd.world_defs += '_ShadowMapAtlas'
@ -121,15 +119,17 @@ def add_world_defs():
if rpdat.lnx_voxelgi_shadows and (point_lights > 0 or '_Sun' in wrd.world_defs):
wrd.world_defs += '_VoxelShadow'
assets.add_khafile_def('lnx_voxelgi_shadows')
#assets.add_shader_external(lnx.utils.get_sdk_path() + '/leenkx/Shaders/voxel_resolve_shadows/voxel_resolve_shadows.comp.glsl')
if voxelgi:
assets.add_shader_external(lnx.utils.get_sdk_path() + '/leenkx/Shaders/voxel_light/voxel_light.comp.glsl')
assets.add_shader_external(lnx.utils.get_sdk_path() + '/leenkx/Shaders/voxel_resolve_diffuse/voxel_resolve_diffuse.comp.glsl')
assets.add_shader_external(lnx.utils.get_sdk_path() + '/leenkx/Shaders/voxel_resolve_specular/voxel_resolve_specular.comp.glsl')
assets.add_shader_external(lnx.utils.get_sdk_path() + '/leenkx/Shaders/voxel_light/voxel_light.comp.glsl')
wrd.world_defs += '_VoxelGI'
if rpdat.lnx_voxelgi_refract:
wrd.world_defs += '_VoxelRefract'
assets.add_khafile_def('lnx_voxelgi_refract')
#assets.add_shader_external(lnx.utils.get_sdk_path() + '/leenkx/Shaders/voxel_resolve_refraction/voxel_resolve_refraction.comp.glsl')
elif voxelao:
assets.add_shader_external(lnx.utils.get_sdk_path() + '/leenkx/Shaders/voxel_resolve_ao/voxel_resolve_ao.comp.glsl')
@ -241,7 +241,7 @@ def build():
compo_depth = True
focus_distance = 0.0
if len(bpy.data.cameras) > 0 and lnx.utils.get_active_scene().camera.data.dof.use_dof:
if lnx.utils.get_active_scene().camera and lnx.utils.get_active_scene().camera.data.dof.use_dof:
focus_distance = lnx.utils.get_active_scene().camera.data.dof.focus_distance
if focus_distance > 0.0:
@ -307,14 +307,10 @@ def build():
assets.add_khafile_def('rp_ssgi={0}'.format(rpdat.rp_ssgi))
if rpdat.rp_ssgi != 'Off':
wrd.world_defs += '_SSAO'
if rpdat.rp_ssgi == 'SSAO':
wrd.world_defs += '_SSAO'
assets.add_shader_pass('ssao_pass')
assets.add_shader_pass('blur_edge_pass')
elif rpdat.rp_ssgi == 'SSGI':
wrd.world_defs += '_SSGI'
assets.add_shader_pass('ssgi_pass')
assets.add_shader_pass('blur_edge_pass')
else:
assets.add_shader_pass('ssgi_pass')
assets.add_shader_pass('blur_edge_pass')
@ -461,8 +457,7 @@ def build():
if ignoreIrr:
wrd.world_defs += '_IgnoreIrr'
gbuffer2 = '_Veloc' in wrd.world_defs or '_IgnoreIrr' in wrd.world_defs or '_VoxelGI' in wrd.world_defs or '_VoxelShadow' in wrd.world_defs or '_SSGI' in wrd.world_defs
gbuffer2 = '_Veloc' in wrd.world_defs or '_IgnoreIrr' in wrd.world_defs
if gbuffer2:
assets.add_khafile_def('rp_gbuffer2')
wrd.world_defs += '_gbuffer2'

View File

@ -69,7 +69,7 @@ def build():
if rpdat.lnx_irradiance:
# Plain background color
if '_EnvCol' in world.world_defs:
world_name = lnx.utils.safestr(world.name)
world_name = lnx.utils.safestr(lnx.utils.asset_name(world) if world.library else world.name)
# Irradiance json file name
world.lnx_envtex_name = world_name
world.lnx_envtex_irr_name = world_name
@ -99,7 +99,7 @@ def build():
def create_world_shaders(world: bpy.types.World):
"""Creates fragment and vertex shaders for the given world."""
global shader_datas
world_name = lnx.utils.safestr(world.name)
world_name = lnx.utils.safestr(lnx.utils.asset_name(world) if world.library else world.name)
pass_name = 'World_' + world_name
shader_props = {
@ -160,7 +160,7 @@ def create_world_shaders(world: bpy.types.World):
def build_node_tree(world: bpy.types.World, frag: Shader, vert: Shader, con: ShaderContext):
"""Generates the shader code for the given world."""
world_name = lnx.utils.safestr(world.name)
world_name = lnx.utils.safestr(lnx.utils.asset_name(world) if world.library else world.name)
world.world_defs = ''
rpdat = lnx.utils.get_rp()
wrd = bpy.data.worlds['Lnx']
@ -175,7 +175,7 @@ def build_node_tree(world: bpy.types.World, frag: Shader, vert: Shader, con: Sha
frag.write('fragColor.rgb = backgroundCol;')
return
parser_state = ParserState(ParserContext.WORLD, world.name, world)
parser_state = ParserState(ParserContext.WORLD, lnx.utils.asset_name(world) if world.library else world.name, world)
parser_state.con = con
parser_state.curshader = frag
parser_state.frag = frag
@ -199,7 +199,6 @@ def build_node_tree(world: bpy.types.World, frag: Shader, vert: Shader, con: Sha
world.lnx_envtex_color = [col[0], col[1], col[2], 1.0]
world.lnx_envtex_strength = 1.0
world.world_defs += '_EnvCol'
assets.add_khafile_def("lnx_envcol")
# Clouds enabled
if rpdat.lnx_clouds and world.lnx_use_clouds:
@ -281,7 +280,6 @@ def parse_surface(world: bpy.types.World, node_surface: bpy.types.Node, frag: Sh
# Append irradiance define
if rpdat.lnx_irradiance and not solid_mat:
wrd.world_defs += '_Irr'
assets.add_khafile_def("lnx_irradiance")
# Extract environment strength
# Todo: follow/parse strength input
@ -295,7 +293,6 @@ def parse_surface(world: bpy.types.World, node_surface: bpy.types.Node, frag: Sh
solid_mat = rpdat.lnx_material_model == 'Solid'
if rpdat.lnx_irradiance and not solid_mat:
world.world_defs += '_Irr'
assets.add_khafile_def("lnx_irradiance")
world.lnx_envtex_color = node_surface.inputs[0].default_value
world.lnx_envtex_strength = 1.0

View File

@ -53,9 +53,112 @@ else:
# Particle info export
particle_info: Dict[str, bool] = {}
texture_resize_cache = {}
state: Optional[ParserState]
def resize_texture_if_needed(image: bpy.types.Image, filepath: str, max_size: int) -> str:
"""
Resize texture if it exceeds max_size. Returns path to resized texture or original.
Caches results to avoid re-processing unchanged textures.
"""
if max_size <= 0:
return filepath
if image.size[0] <= max_size and image.size[1] <= max_size:
return filepath
wrd = bpy.data.worlds['Lnx']
texture_quality = wrd.lnx_texture_quality
cache_key = (filepath, max_size, texture_quality, os.path.getmtime(filepath) if os.path.exists(filepath) else 0)
if cache_key in _texture_resize_cache:
cached_path = _texture_resize_cache[cache_key]
if os.path.exists(cached_path):
return cached_path
width, height = image.size[0], image.size[1]
if width > height:
new_width = max_size
new_height = int((height / width) * max_size)
else:
new_height = max_size
new_width = int((width / height) * max_size)
build_dir = lnx.utils.get_fp_build()
resized_dir = os.path.join(build_dir, 'compiled', 'Assets', 'unpacked')
os.makedirs(resized_dir, exist_ok=True)
basename = os.path.basename(filepath)
name, ext = os.path.splitext(basename)
quality_suffix = f"q{int(texture_quality * 100)}"
resized_path = os.path.join(resized_dir, f"{name}_{max_size}px_{quality_suffix}{ext}")
if os.path.exists(resized_path):
src_mtime = os.path.getmtime(filepath)
dst_mtime = os.path.getmtime(resized_path)
if dst_mtime >= src_mtime:
_texture_resize_cache[cache_key] = resized_path
return resized_path
try:
ffmpeg_path = lnx.utils.get_ffmpeg_path()
if ffmpeg_path is None or ffmpeg_path == '':
print(f"[Texture Optimizer] WARNING: FFmpeg not found. Please set FFmpeg path in addon preferences.")
print(f"[Texture Optimizer] Skipping resize for: {basename}")
return filepath
file_ext = os.path.splitext(filepath)[1].lower().lstrip('.')
cmd = [
ffmpeg_path,
'-y',
'-i', filepath,
'-vf', f'scale={new_width}:{new_height}:flags=lanczos',
]
if file_ext in ('png', 'tga', 'bmp'):
compression_level = round((1.0 - texture_quality) * 9)
cmd.extend(['-compression_level', str(compression_level)])
else:
qscale = round(2 + (1.0 - texture_quality) * 29)
cmd.extend(['-q:v', str(qscale)])
cmd.append(resized_path)
startupinfo = None
if os.name == 'nt':
startupinfo = subprocess.STARTUPINFO()
startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
startupinfo.wShowWindow = subprocess.SW_HIDE
result = subprocess.run(
cmd,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
startupinfo=startupinfo,
timeout=60
)
if result.returncode == 0 and os.path.exists(resized_path):
print(f"[Texture Optimizer] Resized: {basename} {width}x{height} -> {new_width}x{new_height}")
_texture_resize_cache[cache_key] = resized_path
return resized_path
else:
error_msg = result.stderr.decode('utf-8', errors='ignore') if result.stderr else 'Unknown error'
print(f"[Texture Optimizer] WARNING: FFmpeg failed to resize {basename}: {error_msg}")
return filepath
except subprocess.TimeoutExpired:
print(f"[Texture Optimizer] WARNING: FFmpeg timeout while resizing {basename}")
return filepath
except Exception as e:
print(f"[Texture Optimizer] WARNING: Failed to resize {basename}: {e}")
return filepath
def parse(nodes, con: ShaderContext,
vert: Shader, frag: Shader, geom: Shader, tesc: Shader, tese: Shader,
parse_surface=True, parse_opacity=True, parse_displacement=True, basecol_only=False):
@ -94,6 +197,7 @@ def parse_material_output(node: bpy.types.Node, custom_particle_node: bpy.types.
parse_displacement = state.parse_displacement
particle_info = {
'index': False,
'random': False,
'age': False,
'lifetime': False,
'location': False,
@ -896,6 +1000,11 @@ def make_texture(
lnx.utils.convert_image(image, converted_path, file_format=fmt)
lnx.assets.add(converted_path)
else:
wrd = bpy.data.worlds['Lnx']
max_size = int(wrd.lnx_max_texture_size)
if max_size > 0 and image is not None:
filepath = resize_texture_if_needed(image, filepath, max_size)
# Link image path to assets
# TODO: Khamake converts .PNG to .jpg? Convert ext to lowercase on windows
if lnx.utils.get_os() == 'win':

View File

@ -254,9 +254,10 @@ def parse_particleinfo(node: bpy.types.ShaderNodeParticleInfo, out_socket: bpy.t
c.particle_info['index'] = True
return 'p_index' if particles_on else '0.0'
# TODO: Random
# Random
if out_socket == node.outputs[1]:
return '0.0'
c.particle_info['random'] = True
return 'p_random' if particles_on else '0.0'
# Age
elif out_socket == node.outputs[2]:
@ -276,7 +277,7 @@ def parse_particleinfo(node: bpy.types.ShaderNodeParticleInfo, out_socket: bpy.t
# Size
elif out_socket == node.outputs[5]:
c.particle_info['size'] = True
return '1.0'
return 'p_size' if particles_on else '1.0'
# Velocity
elif out_socket == node.outputs[6]:

View File

@ -76,7 +76,7 @@ def parse_addshader(node: bpy.types.ShaderNodeAddShader, out_socket: NodeSocket,
state.out_ior = '({0} * 0.5 + {1} * 0.5)'.format(ior1, ior2)
if bpy.app.version < (3, 0, 0):
if bpy.app.version < (2, 92, 0):
def parse_bsdfprincipled(node: bpy.types.ShaderNodeBsdfPrincipled, out_socket: NodeSocket, state: ParserState) -> None:
if state.parse_surface:
c.write_normal(node.inputs[20])
@ -84,18 +84,20 @@ if bpy.app.version < (3, 0, 0):
state.out_metallic = c.parse_value_input(node.inputs[4])
state.out_specular = c.parse_value_input(node.inputs[5])
state.out_roughness = c.parse_value_input(node.inputs[7])
if (node.inputs['Emission Strength'].is_linked or node.inputs['Emission Strength'].default_value != 0.0)\
and (node.inputs['Emission'].is_linked or not mat_utils.equals_color_socket(node.inputs['Emission'], (0.0, 0.0, 0.0), comp_alpha=False)):
if node.inputs['Emission'].is_linked or not mat_utils.equals_color_socket(node.inputs['Emission'], (0.0, 0.0, 0.0), comp_alpha=False):
emission_col = c.parse_vector_input(node.inputs[17])
emission_strength = c.parse_value_input(node.inputs[18])
state.out_emission_col = '({0} * {1})'.format(emission_col, emission_strength)
state.out_emission_col = emission_col
mat_state.emission_type = mat_state.EmissionType.SHADED
else:
mat_state.emission_type = mat_state.EmissionType.NO_EMISSION
if state.parse_opacity:
state.out_ior = c.parse_value_input(node.inputs[14])
state.out_opacity = c.parse_value_input(node.inputs[19])
if bpy.app.version >= (3, 0, 0) and bpy.app.version <= (4, 1, 0):
# In Blender 2.83, Alpha socket is at index 18, not 19
if 'Alpha' in node.inputs:
state.out_opacity = c.parse_value_input(node.inputs['Alpha'])
else:
state.out_opacity = '1.0'
if bpy.app.version >= (2, 92, 0) and bpy.app.version <= (4, 1, 0):
def parse_bsdfprincipled(node: bpy.types.ShaderNodeBsdfPrincipled, out_socket: NodeSocket, state: ParserState) -> None:
if state.parse_surface:
c.write_normal(node.inputs[22])
@ -250,7 +252,7 @@ def parse_bsdfglass(node: bpy.types.ShaderNodeBsdfGlass, out_socket: NodeSocket,
c.write_normal(node.inputs[3])
state.out_roughness = c.parse_value_input(node.inputs[1])
if state.parse_opacity:
state.out_opacity = '1.0'
state.out_opacity = '0.1'
state.out_ior = c.parse_value_input(node.inputs[2])
@ -270,7 +272,7 @@ def parse_bsdfrefraction(node: bpy.types.ShaderNodeBsdfRefraction, out_socket: N
c.write_normal(node.inputs[3])
state.out_roughness = c.parse_value_input(node.inputs[1])
if state.parse_opacity:
state.out_opacity = '1.0'
state.out_opacity = '0.0'
state.out_ior = c.parse_value_input(node.inputs[2])
def parse_subsurfacescattering(node: bpy.types.ShaderNodeSubsurfaceScattering, out_socket: NodeSocket, state: ParserState) -> None:

View File

@ -342,6 +342,9 @@ def parse_sky_hosekwilkie(node: bpy.types.ShaderNodeTexSky, state: ParserState)
world = state.world
curshader = state.curshader
# Match to cycles
world.lnx_envtex_strength *= 0.1
assets.add_khafile_def('lnx_hosek')
curshader.add_uniform('vec3 A', link="_hosekA")
curshader.add_uniform('vec3 B', link="_hosekB")

View File

@ -19,7 +19,6 @@ def write(vert: shader.Shader, frag: shader.Shader):
parse_opacity = blend or mat_utils.is_transluc(mat_state.material)
is_mobile = rpdat.lnx_material_model == 'Mobile'
is_shadows = '_ShadowMap' in wrd.world_defs
is_transparent_shadows = '_ShadowMapTransparent' in wrd.world_defs
is_shadows_atlas = '_ShadowMapAtlas' in wrd.world_defs
is_single_atlas = '_SingleAtlas' in wrd.world_defs
@ -34,17 +33,14 @@ def write(vert: shader.Shader, frag: shader.Shader):
if is_shadows_atlas:
if not is_single_atlas:
frag.add_uniform('sampler2DShadow shadowMapAtlasPoint', included=True)
if is_transparent_shadows:
frag.add_uniform('sampler2D shadowMapAtlasPointTransparent', included=True)
frag.add_uniform('sampler2D shadowMapAtlasPointTransparent', included=True)
else:
frag.add_uniform('sampler2DShadow shadowMapAtlas', top=True)
if is_transparent_shadows:
frag.add_uniform('sampler2D shadowMapAtlasTransparent', top=True)
frag.add_uniform('sampler2D shadowMapAtlasTransparent', top=True)
frag.add_uniform('vec4 pointLightDataArray[maxLightsCluster]', link='_pointLightsAtlasArray', included=True)
else:
frag.add_uniform('samplerCubeShadow shadowMapPoint[4]', included=True)
if is_transparent_shadows:
frag.add_uniform('samplerCube shadowMapPointTransparent[4]', included=True)
frag.add_uniform('samplerCube shadowMapPointTransparent[4]', included=True)
if not '_VoxelAOvar' in wrd.world_defs and not '_VoxelGI' in wrd.world_defs or ((parse_opacity or '_VoxelShadow' in wrd.world_defs) and ('_VoxelAOvar' in wrd.world_defs or '_VoxelGI' in wrd.world_defs)):
vert.add_out('vec4 wvpposition')
@ -66,16 +62,13 @@ def write(vert: shader.Shader, frag: shader.Shader):
if is_shadows_atlas:
if not is_single_atlas:
frag.add_uniform('sampler2DShadow shadowMapAtlasSpot', included=True)
if is_transparent_shadows:
frag.add_uniform('sampler2D shadowMapAtlasSpotTransparent', included=True)
frag.add_uniform('sampler2D shadowMapAtlasSpotTransparent', included=True)
else:
frag.add_uniform('sampler2DShadow shadowMapAtlas', top=True)
if is_transparent_shadows:
frag.add_uniform('sampler2D shadowMapAtlasTransparent', top=True)
frag.add_uniform('sampler2D shadowMapAtlasTransparent', top=True)
else:
frag.add_uniform('sampler2DShadow shadowMapSpot[4]', included=True)
if is_transparent_shadows:
frag.add_uniform('sampler2D shadowMapSpotTransparent[4]', included=True)
frag.add_uniform('sampler2D shadowMapSpotTransparent[4]', included=True)
frag.add_uniform('mat4 LWVPSpotArray[maxLightsCluster]', link='_biasLightWorldViewProjectionMatrixSpotArray', included=True)
frag.write('for (int i = 0; i < min(numLights, maxLightsCluster); i++) {')
@ -91,10 +84,12 @@ def write(vert: shader.Shader, frag: shader.Shader):
frag.write(' roughness,')
frag.write(' specular,')
frag.write(' f0')
if is_shadows:
frag.write('\t, li, lightsArray[li * 3 + 2].x, lightsArray[li * 3 + 2].z != 0.0') # bias
if is_transparent_shadows:
frag.write('\t, opacity != 1.0')
if parse_opacity:
frag.write('\t, li, lightsArray[li * 3 + 2].x, lightsArray[li * 3 + 2].z != 0.0, opacity != 1.0') # bias
else:
frag.write('\t, li, lightsArray[li * 3 + 2].x, lightsArray[li * 3 + 2].z != 0.0, false') # bias
if '_Spot' in wrd.world_defs:
frag.write('\t, lightsArray[li * 3 + 2].y != 0.0')
frag.write('\t, lightsArray[li * 3 + 2].y') # spot size (cutoff)
@ -103,13 +98,13 @@ def write(vert: shader.Shader, frag: shader.Shader):
frag.write('\t, vec2(lightsArray[li * 3].w, lightsArray[li * 3 + 1].w)') # scale
frag.write('\t, lightsArraySpot[li * 2 + 1].xyz') # right
if '_VoxelShadow' in wrd.world_defs:
frag.write(', voxels, voxelsSDF, clipmaps, velocity')
frag.write(', voxels, voxelsSDF, clipmaps')
if '_MicroShadowing' in wrd.world_defs and not is_mobile:
frag.write('\t, occlusion')
if '_SSRS' in wrd.world_defs:
frag.add_uniform('sampler2D gbufferD')
frag.add_uniform('mat4 invVP', '_inverseViewProjectionMatrix')
frag.add_uniform('vec3 eye', '_cameraPosition')
frag.add_uniform('sampler2D gbufferD', link='_gbufferD', top=True)
frag.write(', gbufferD, invVP, eye')
frag.write(');')

View File

@ -1,161 +1,155 @@
import bpy
import lnx
import lnx.material.mat_state as mat_state
import lnx.material.make_tess as make_tess
from lnx.material.shader import ShaderContext
if lnx.is_reload(__name__):
mat_state = lnx.reload_module(mat_state)
make_tess = lnx.reload_module(make_tess)
lnx.material.shader = lnx.reload_module(lnx.material.shader)
from lnx.material.shader import ShaderContext
else:
lnx.enable_reload(__name__)
def make(con_mesh: ShaderContext):
vert = con_mesh.vert
frag = con_mesh.frag
geom = con_mesh.geom
tesc = con_mesh.tesc
tese = con_mesh.tese
# Additional values referenced in cycles
# TODO: enable from cycles.py
if frag.contains('dotNV') and not frag.contains('float dotNV'):
frag.write_init('float dotNV = max(dot(n, vVec), 0.0);')
# n is not always defined yet (in some shadowmap shaders e.g.)
if not frag.contains('vec3 n'):
vert.add_out('vec3 wnormal')
billboard = mat_state.material.lnx_billboard
if billboard == 'spherical':
vert.add_uniform('mat3 N', '_normalMatrixSphere')
elif billboard == 'cylindrical':
vert.add_uniform('mat3 N', '_normalMatrixCylinder')
else:
vert.add_uniform('mat3 N', '_normalMatrix')
vert.write_attrib('wnormal = normalize(N * vec3(nor.xy, pos.w));')
frag.write_attrib('vec3 n = normalize(wnormal);')
# If not yet added, add nor vertex data
vertex_elems = con_mesh.data['vertex_elements']
has_normals = False
for elem in vertex_elems:
if elem['name'] == 'nor':
has_normals = True
break
if not has_normals:
vertex_elems.append({'name': 'nor', 'data': 'short2norm'})
write_wpos = False
export_wpos = False
if frag.contains('wposition') and not frag.contains('vec3 wposition'):
export_wpos = True
if tese is not None:
export_wpos = True
if vert.contains('wposition'):
write_wpos = True
if frag.contains('vVec') and not frag.contains('vec3 vVec'):
if tese is not None:
tese.add_out('vec3 eyeDir')
tese.add_uniform('vec3 eye', '_cameraPosition')
tese.write('eyeDir = eye - wposition;')
else:
if not vert.contains('wposition'):
write_wpos = True
vert.add_out('vec3 eyeDir')
vert.add_uniform('vec3 eye', '_cameraPosition')
vert.write('eyeDir = eye - wposition;')
frag.write_attrib('vec3 vVec = normalize(eyeDir);')
if export_wpos:
vert.add_uniform('mat4 W', '_worldMatrix')
vert.add_out('vec3 wposition')
vert.write_attrib('wposition = vec4(W * spos).xyz;')
elif write_wpos:
vert.add_uniform('mat4 W', '_worldMatrix')
vert.write_attrib('vec3 wposition = vec4(W * spos).xyz;')
if frag.contains('dotNV') and not frag.contains('float dotNV'):
frag.write_attrib('float dotNV = max(dot(n, vVec), 0.0);')
frag_mpos = (frag.contains('mposition') and not frag.contains('vec3 mposition')) or vert.contains('mposition')
if frag_mpos:
vert.add_out('vec3 mposition')
vert.add_uniform('float posUnpack', link='_posUnpack')
vert.write_attrib('mposition = spos.xyz * posUnpack;')
if tese is not None:
if frag_mpos:
make_tess.interpolate(tese, 'mposition', 3, declare_out=True)
elif tese.contains('mposition') and not tese.contains('vec3 mposition'):
vert.add_out('vec3 mposition')
vert.write_pre = True
vert.add_uniform('float posUnpack', link='_posUnpack')
vert.write('mposition = spos.xyz * posUnpack;')
vert.write_pre = False
make_tess.interpolate(tese, 'mposition', 3, declare_out=False)
frag_bpos = (frag.contains('bposition') and not frag.contains('vec3 bposition')) or vert.contains('bposition')
if frag_bpos:
vert.add_out('vec3 bposition')
vert.add_uniform('vec3 dim', link='_dim')
vert.add_uniform('vec3 hdim', link='_halfDim')
vert.add_uniform('float posUnpack', link='_posUnpack')
vert.write_attrib('bposition = (spos.xyz * posUnpack + hdim) / dim;')
vert.write_attrib('if (dim.z == 0) bposition.z = 0;')
vert.write_attrib('if (dim.y == 0) bposition.y = 0;')
vert.write_attrib('if (dim.x == 0) bposition.x = 0;')
if tese is not None:
if frag_bpos:
make_tess.interpolate(tese, 'bposition', 3, declare_out=True)
elif tese.contains('bposition') and not tese.contains('vec3 bposition'):
vert.add_out('vec3 bposition')
vert.add_uniform('vec3 dim', link='_dim')
vert.add_uniform('vec3 hdim', link='_halfDim')
vert.add_uniform('float posUnpack', link='_posUnpack')
vert.write_attrib('bposition = (spos.xyz * posUnpack + hdim) / dim;')
make_tess.interpolate(tese, 'bposition', 3, declare_out=False)
frag_wtan = (frag.contains('wtangent') and not frag.contains('vec3 wtangent')) or vert.contains('wtangent')
if frag_wtan:
# Indicate we want tang attrib in finalizer to prevent TBN generation
con_mesh.add_elem('tex', 'short2norm')
con_mesh.add_elem('tang', 'short4norm')
vert.add_out('vec3 wtangent')
vert.write_pre = True
vert.write('wtangent = normalize(N * tang.xyz);')
vert.write_pre = False
if tese is not None:
if frag_wtan:
make_tess.interpolate(tese, 'wtangent', 3, declare_out=True)
elif tese.contains('wtangent') and not tese.contains('vec3 wtangent'):
vert.add_out('vec3 wtangent')
vert.write_pre = True
vert.write('wtangent = normalize(N * tang.xyz);')
vert.write_pre = False
make_tess.interpolate(tese, 'wtangent', 3, declare_out=False)
if frag.contains('vVecCam'):
vert.add_out('vec3 eyeDirCam')
vert.add_uniform('mat4 WV', '_worldViewMatrix')
vert.write('eyeDirCam = vec4(WV * spos).xyz; eyeDirCam.z *= -1;')
frag.write_attrib('vec3 vVecCam = normalize(eyeDirCam);')
if frag.contains('nAttr'):
vert.add_out('vec3 nAttr')
vert.write_attrib('nAttr = vec3(nor.xy, pos.w);')
wrd = bpy.data.worlds['Lnx']
if '_Legacy' in wrd.world_defs:
frag.replace('sampler2DShadow', 'sampler2D')
frag.replace('samplerCubeShadow', 'samplerCube')
import bpy
import lnx
import lnx.material.mat_state as mat_state
import lnx.material.make_tess as make_tess
from lnx.material.shader import ShaderContext
if lnx.is_reload(__name__):
mat_state = lnx.reload_module(mat_state)
make_tess = lnx.reload_module(make_tess)
lnx.material.shader = lnx.reload_module(lnx.material.shader)
from lnx.material.shader import ShaderContext
else:
lnx.enable_reload(__name__)
def make(con_mesh: ShaderContext):
vert = con_mesh.vert
frag = con_mesh.frag
geom = con_mesh.geom
tesc = con_mesh.tesc
tese = con_mesh.tese
# Additional values referenced in cycles
# TODO: enable from cycles.py
if frag.contains('dotNV') and not frag.contains('float dotNV'):
frag.write_init('float dotNV = max(dot(n, vVec), 0.0);')
# n is not always defined yet (in some shadowmap shaders e.g.)
if not frag.contains('vec3 n'):
vert.add_out('vec3 wnormal')
billboard = mat_state.material.lnx_billboard
if billboard == 'spherical':
vert.add_uniform('mat3 N', '_normalMatrixSphere')
elif billboard == 'cylindrical':
vert.add_uniform('mat3 N', '_normalMatrixCylinder')
else:
vert.add_uniform('mat3 N', '_normalMatrix')
vert.write_attrib('wnormal = normalize(N * vec3(nor.xy, pos.w));')
frag.write_attrib('vec3 n = normalize(wnormal);')
# If not yet added, add nor vertex data
vertex_elems = con_mesh.data['vertex_elements']
has_normals = False
for elem in vertex_elems:
if elem['name'] == 'nor':
has_normals = True
break
if not has_normals:
vertex_elems.append({'name': 'nor', 'data': 'short2norm'})
write_wpos = False
if frag.contains('vVec') and not frag.contains('vec3 vVec'):
if tese is not None:
tese.add_out('vec3 eyeDir')
tese.add_uniform('vec3 eye', '_cameraPosition')
tese.write('eyeDir = eye - wposition;')
else:
if not vert.contains('wposition'):
write_wpos = True
vert.add_out('vec3 eyeDir')
vert.add_uniform('vec3 eye', '_cameraPosition')
vert.write('eyeDir = eye - wposition;')
frag.write_attrib('vec3 vVec = normalize(eyeDir);')
export_wpos = False
if frag.contains('wposition') and not frag.contains('vec3 wposition'):
export_wpos = True
if tese is not None:
export_wpos = True
if vert.contains('wposition'):
write_wpos = True
if export_wpos:
vert.add_uniform('mat4 W', '_worldMatrix')
vert.add_out('vec3 wposition')
vert.write('wposition = vec4(W * spos).xyz;')
elif write_wpos:
vert.add_uniform('mat4 W', '_worldMatrix')
vert.write_attrib('vec3 wposition = vec4(W * spos).xyz;')
frag_mpos = (frag.contains('mposition') and not frag.contains('vec3 mposition')) or vert.contains('mposition')
if frag_mpos:
vert.add_out('vec3 mposition')
vert.add_uniform('float posUnpack', link='_posUnpack')
vert.write_attrib('mposition = spos.xyz * posUnpack;')
if tese is not None:
if frag_mpos:
make_tess.interpolate(tese, 'mposition', 3, declare_out=True)
elif tese.contains('mposition') and not tese.contains('vec3 mposition'):
vert.add_out('vec3 mposition')
vert.write_pre = True
vert.add_uniform('float posUnpack', link='_posUnpack')
vert.write('mposition = spos.xyz * posUnpack;')
vert.write_pre = False
make_tess.interpolate(tese, 'mposition', 3, declare_out=False)
frag_bpos = (frag.contains('bposition') and not frag.contains('vec3 bposition')) or vert.contains('bposition')
if frag_bpos:
vert.add_out('vec3 bposition')
vert.add_uniform('vec3 dim', link='_dim')
vert.add_uniform('vec3 hdim', link='_halfDim')
vert.add_uniform('float posUnpack', link='_posUnpack')
vert.write_attrib('bposition = (spos.xyz * posUnpack + hdim) / dim;')
vert.write_attrib('if (dim.z == 0) bposition.z = 0;')
vert.write_attrib('if (dim.y == 0) bposition.y = 0;')
vert.write_attrib('if (dim.x == 0) bposition.x = 0;')
if tese is not None:
if frag_bpos:
make_tess.interpolate(tese, 'bposition', 3, declare_out=True)
elif tese.contains('bposition') and not tese.contains('vec3 bposition'):
vert.add_out('vec3 bposition')
vert.add_uniform('vec3 dim', link='_dim')
vert.add_uniform('vec3 hdim', link='_halfDim')
vert.add_uniform('float posUnpack', link='_posUnpack')
vert.write_attrib('bposition = (spos.xyz * posUnpack + hdim) / dim;')
make_tess.interpolate(tese, 'bposition', 3, declare_out=False)
frag_wtan = (frag.contains('wtangent') and not frag.contains('vec3 wtangent')) or vert.contains('wtangent')
if frag_wtan:
# Indicate we want tang attrib in finalizer to prevent TBN generation
con_mesh.add_elem('tex', 'short2norm')
con_mesh.add_elem('tang', 'short4norm')
vert.add_out('vec3 wtangent')
vert.write_pre = True
vert.write('wtangent = normalize(N * tang.xyz);')
vert.write_pre = False
if tese is not None:
if frag_wtan:
make_tess.interpolate(tese, 'wtangent', 3, declare_out=True)
elif tese.contains('wtangent') and not tese.contains('vec3 wtangent'):
vert.add_out('vec3 wtangent')
vert.write_pre = True
vert.write('wtangent = normalize(N * tang.xyz);')
vert.write_pre = False
make_tess.interpolate(tese, 'wtangent', 3, declare_out=False)
if frag.contains('vVecCam'):
vert.add_out('vec3 eyeDirCam')
vert.add_uniform('mat4 WV', '_worldViewMatrix')
vert.write('eyeDirCam = vec4(WV * spos).xyz; eyeDirCam.z *= -1;')
frag.write_attrib('vec3 vVecCam = normalize(eyeDirCam);')
if frag.contains('nAttr'):
vert.add_out('vec3 nAttr')
vert.write_attrib('nAttr = vec3(nor.xy, pos.w);')
wrd = bpy.data.worlds['Lnx']
if '_Legacy' in wrd.world_defs:
frag.replace('sampler2DShadow', 'sampler2D')
frag.replace('samplerCubeShadow', 'samplerCube')

View File

@ -1,4 +1,4 @@
from typing import Any, Callable, Optional
from typing import Any, Callable, Dict, List, Optional, TypeVar, Union
import bpy
@ -32,8 +32,8 @@ else:
is_displacement = False
# User callbacks
write_material_attribs: Optional[Callable[[dict[str, Any], shader.Shader], bool]] = None
write_material_attribs_post: Optional[Callable[[dict[str, Any], shader.Shader], None]] = None
write_material_attribs: Optional[Callable[[Dict[str, Any], shader.Shader], bool]] = None
write_material_attribs_post: Optional[Callable[[Dict[str, Any], shader.Shader], None]] = None
write_vertex_attribs: Optional[Callable[[shader.Shader], bool]] = None
@ -58,7 +58,6 @@ def make(context_id, rpasses):
con['alpha_blend_destination'] = mat.lnx_blending_destination_alpha
con['alpha_blend_operation'] = mat.lnx_blending_operation_alpha
con['depth_write'] = False
con['compare_mode'] = 'less'
elif particle:
pass
# Depth prepass was performed, exclude mat with depth read that
@ -66,6 +65,9 @@ def make(context_id, rpasses):
elif dprepass and not (rpdat.rp_depth_texture and mat.lnx_depth_read):
con['depth_write'] = False
con['compare_mode'] = 'equal'
else:
con['depth_write'] = mat.lnx_depth_write
con['compare_mode'] = mat.lnx_compare_mode
attachment_format = 'RGBA32' if '_LDR' in wrd.world_defs else 'RGBA64'
con['color_attachments'] = [attachment_format, attachment_format]
@ -557,7 +559,7 @@ def make_forward(con_mesh):
frag.write('fragColor[0] = vec4(direct + indirect, packFloat2(occlusion, specular));')
frag.write('fragColor[1] = vec4(n.xy, roughness, metallic);')
if rpdat.rp_ss_refraction or rpdat.lnx_voxelgi_refract:
frag.write(f'fragColor[2] = vec4(1.0, 1.0, 0.0, 0.0);')
frag.write(f'fragColor[2] = vec4(1.0, 1.0, 0.0, 1.0);')
else:
frag.add_out('vec4 fragColor[1]')
@ -611,42 +613,10 @@ def make_forward_base(con_mesh, parse_opacity=False, transluc_pass=False):
sh = tese if tese is not None else vert
sh.add_out('vec3 eyeDir')
sh.add_uniform('vec3 eye', '_cameraPosition')
sh.write('eyeDir = eye - wposition;')
if '_VoxelGI' in wrd.world_defs or '_VoxelShadow' in wrd.world_defs:
if '_gbuffer2' in wrd.world_defs:
if '_Veloc' in wrd.world_defs:
if tese is None:
vert.add_uniform('mat4 prevWVP', link='_prevWorldViewProjectionMatrix')
vert.add_out('vec4 wvpposition')
vert.add_out('vec4 prevwvpposition')
vert.write('wvpposition = gl_Position;')
if is_displacement:
vert.add_uniform('mat4 invW', link='_inverseWorldMatrix')
vert.write('prevwvpposition = prevWVP * (invW * vec4(wposition, 1.0));')
else:
vert.write('prevwvpposition = prevWVP * spos;')
else:
tese.add_out('vec4 wvpposition')
tese.add_out('vec4 prevwvpposition')
tese.write('wvpposition = gl_Position;')
if is_displacement:
tese.add_uniform('mat4 invW', link='_inverseWorldMatrix')
tese.add_uniform('mat4 prevWVP', '_prevWorldViewProjectionMatrix')
tese.write('prevwvpposition = prevWVP * (invW * vec4(wposition, 1.0));')
else:
vert.add_uniform('mat4 prevW', link='_prevWorldMatrix')
vert.add_out('vec3 prevwposition')
vert.write('prevwposition = vec4(prevW * spos).xyz;')
tese.add_uniform('mat4 prevVP', '_prevViewProjectionMatrix')
make_tess.interpolate(tese, 'prevwposition', 3)
tese.write('prevwvpposition = prevVP * vec4(prevwposition, 1.0);')
frag.write('vec2 posa = (wvpposition.xy / wvpposition.w) * 0.5 + 0.5;')
frag.write('vec2 posb = (prevwvpposition.xy / prevwvpposition.w) * 0.5 + 0.5;')
frag.write('vec2 velocity = -vec2(posa - posb);')
sh.write('eyeDir = eye - spos.xyz;')
frag.add_include('std/light.glsl')
is_shadows = '_ShadowMap' in wrd.world_defs
is_transparent_shadows = '_ShadowMapTransparent' in wrd.world_defs
is_shadows_atlas = '_ShadowMapAtlas' in wrd.world_defs
is_single_atlas = is_shadows_atlas and '_SingleAtlas' in wrd.world_defs
shadowmap_sun = 'shadowMap'
@ -662,10 +632,7 @@ def make_forward_base(con_mesh, parse_opacity=False, transluc_pass=False):
if '_Brdf' in wrd.world_defs:
frag.add_uniform('sampler2D senvmapBrdf', link='$brdf.png')
frag.write('vec2 envBRDF = texelFetch(senvmapBrdf, ivec2(vec2(dotNV, 1.0 - roughness) * 256.0), 0).xy;')
frag.write('vec3 F = f0 * envBRDF.x + envBRDF.y;')
else:
frag.write('vec3 F = f0;')
if '_Irr' in wrd.world_defs:
frag.add_include('std/shirr.glsl')
frag.add_uniform('vec4 shirr[7]', link='_envmapIrradiance')
@ -690,36 +657,51 @@ def make_forward_base(con_mesh, parse_opacity=False, transluc_pass=False):
frag.write('envl *= albedo;')
if '_Brdf' in wrd.world_defs:
frag.write('envl.rgb *= 1.0 - F;')
frag.write('envl.rgb *= 1.0 - (f0 * envBRDF.x + envBRDF.y);')
if '_Rad' in wrd.world_defs:
frag.write('envl += prefilteredColor * F;')
frag.write('envl += prefilteredColor * (f0 * envBRDF.x + envBRDF.y);')
elif '_EnvCol' in wrd.world_defs:
frag.add_uniform('vec3 backgroundCol', link='_backgroundCol')
frag.write('envl += backgroundCol * F;')
frag.write('envl += backgroundCol * (f0 * envBRDF.x + envBRDF.y);')
frag.add_uniform('float envmapStrength', link='_envmapStrength')
frag.write('envl *= envmapStrength * occlusion;')
if '_VoxelAOvar' in wrd.world_defs or '_VoxelGI' in wrd.world_defs:
frag.add_include('std/conetrace.glsl')
frag.add_uniform('sampler3D voxels')
frag.add_uniform('sampler3D voxelsSDF')
frag.add_uniform('float clipmaps[10 * voxelgiClipmapCount]', '_clipmaps')
if parse_opacity or '_VoxelShadow' in wrd.world_defs:
frag.add_include('std/conetrace.glsl')
frag.add_uniform('sampler3D voxels')
frag.add_uniform('sampler3D voxelsSDF')
frag.add_uniform('vec3 eye', "_cameraPosition")
frag.add_uniform('float clipmaps[10 * voxelgiClipmapCount]', '_clipmaps')
vert.add_out('vec4 wvpposition')
vert.write('wvpposition = gl_Position;')
frag.write('vec2 texCoord = (wvpposition.xy / wvpposition.w) * 0.5 + 0.5;')
if '_VoxelAOvar' in wrd.world_defs:
frag.write('envl *= (1.0 - traceAO(wposition, n, voxels, clipmaps));')
if '_VoxelAOvar' in wrd.world_defs and not parse_opacity:
frag.add_uniform("sampler2D voxels_ao");
frag.write('envl *= textureLod(voxels_ao, texCoord, 0.0).rrr;')
if '_VoxelGI' in wrd.world_defs:
frag.write('vec3 indirect = vec3(0.0);')
else:
frag.write('vec3 indirect = envl;')
if '_VoxelGI' in wrd.world_defs:
frag.write('vec4 diffuse_indirect = traceDiffuse(wposition, n, voxels, clipmaps);')
frag.write('indirect = (diffuse_indirect.rgb * albedo * (1.0 - F) + envl * (1.0 - diffuse_indirect.a)) * voxelgiDiff;')
frag.write('if (roughness < 1.0 && specular > 0.0) {')
frag.write(' indirect += traceSpecular(wposition, n, voxels, voxelsSDF, vVec, roughness * roughness, clipmaps, gl_FragCoord.xy, velocity).rgb * F * voxelgiRefl;')
frag.write('}')
if parse_opacity:
frag.write('vec4 trace = traceDiffuse(wposition, wnormal, voxels, clipmaps);')
frag.write('indirect = ((trace.rgb * albedo + envl * (1.0 - trace.a)) * voxelgiDiff);')
frag.write('if (roughness < 1.0 && specular > 0.0){')
frag.add_uniform('sampler2D sveloc')
frag.write(' vec2 velocity = -textureLod(sveloc, gl_FragCoord.xy, 0.0).rg;')
frag.write(' indirect += traceSpecular(wposition, n, voxels, voxelsSDF, vVec, roughness, clipmaps, gl_FragCoord.xy, velocity).rgb * specular * voxelgiRefl;}')
else:
frag.add_uniform("sampler2D voxels_diffuse")
frag.add_uniform("sampler2D voxels_specular")
frag.write("indirect = textureLod(voxels_diffuse, texCoord, 0.0).rgb * albedo * voxelgiDiff;")
frag.write("if (roughness < 1.0 && specular > 0.0)")
frag.write(" indirect += textureLod(voxels_specular, texCoord, 0.0).rgb * specular * voxelgiRefl;")
frag.write('vec3 direct = vec3(0.0);')
if '_Sun' in wrd.world_defs:
@ -733,21 +715,18 @@ def make_forward_base(con_mesh, parse_opacity=False, transluc_pass=False):
if is_shadows:
frag.add_uniform('bool receiveShadow')
frag.add_uniform(f'sampler2DShadow {shadowmap_sun}', top=True)
if is_transparent_shadows:
frag.add_uniform(f'sampler2D {shadowmap_sun_tr}', top=True)
frag.add_uniform(f'sampler2D {shadowmap_sun_tr}', top=True)
frag.add_uniform('float shadowsBias', '_sunShadowsBias')
frag.write('if (receiveShadow) {')
if '_CSM' in wrd.world_defs:
frag.add_include('std/shadows.glsl')
frag.add_uniform('vec4 casData[shadowmapCascades * 4 + 4]', '_cascadeData', included=True)
frag.add_uniform('vec3 eye', '_cameraPosition')
frag.write(f'svisibility = shadowTestCascade({shadowmap_sun},')
if is_transparent_shadows:
frag.write(f'{shadowmap_sun_tr},')
frag.write('eye, wposition + n * shadowsBias * 10, shadowsBias')
if is_transparent_shadows:
frag.write(', false')
frag.write(');')
if parse_opacity:
frag.write(f'svisibility = shadowTestCascade({shadowmap_sun}, {shadowmap_sun_tr}, eye, wposition + n * shadowsBias * 10, shadowsBias, true);')
else:
frag.write(f'svisibility = shadowTestCascade({shadowmap_sun}, {shadowmap_sun_tr}, eye, wposition + n * shadowsBias * 10, shadowsBias, false);')
else:
if tese is not None:
tese.add_out('vec4 lightPosition')
@ -761,18 +740,15 @@ def make_forward_base(con_mesh, parse_opacity=False, transluc_pass=False):
else:
vert.add_out('vec4 lightPosition')
vert.add_uniform('mat4 LWVP', '_biasLightWorldViewProjectionMatrixSun')
vert.write('lightPosition = LWVP * pos;')
vert.write('lightPosition = LWVP * lightPosition;')
frag.write('vec3 lPos = lightPosition.xyz / lightPosition.w;')
frag.write('const vec2 smSize = shadowmapSize;')
frag.write(f'svisibility = PCF({shadowmap_sun},')
if is_transparent_shadows:
frag.write(f'{shadowmap_sun_tr},')
frag.write('lPos.xy, lPos.z - shadowsBias, smSize')
if is_transparent_shadows:
frag.write(', false')
frag.write(');')
if parse_opacity:
frag.write(f'svisibility = PCF({shadowmap_sun}, {shadowmap_sun_tr}, lPos.xy, lPos.z - shadowsBias, smSize, true);')
else:
frag.write(f'svisibility = PCF({shadowmap_sun}, {shadowmap_sun_tr}, lPos.xy, lPos.z - shadowsBias, smSize, false);')
if '_VoxelShadow' in wrd.world_defs:
frag.write('svisibility *= (1.0 - traceShadow(wposition, n, voxels, voxelsSDF, sunDir, clipmaps, gl_FragCoord.xy, velocity).r) * voxelgiShad;')
frag.write('svisibility *= (1.0 - traceShadow(wposition, n, voxels, voxelsSDF, sunDir, clipmaps, gl_FragCoord.xy).r) * voxelgiShad;')
frag.write('}') # receiveShadow
frag.write('direct += (lambertDiffuseBRDF(albedo, sdotNL) + specularBRDF(f0, roughness, sdotNL, sdotNH, dotNV, sdotVH) * specular) * sunCol * svisibility;')
# sun
@ -791,8 +767,7 @@ def make_forward_base(con_mesh, parse_opacity=False, transluc_pass=False):
# Skip world matrix, already in world-space
frag.add_uniform('mat4 LWVPSpot[1]', link='_biasLightViewProjectionMatrixSpotArray', included=True)
frag.add_uniform('sampler2DShadow shadowMapSpot[1]', included=True)
if is_transparent_shadows:
frag.add_uniform('sampler2D shadowMapSpotTransparent[1]', included=True)
frag.add_uniform('sampler2D shadowMapSpotTransparent[1]', included=True)
else:
frag.add_uniform('vec2 lightProj', link='_lightPlaneProj', included=True)
frag.add_uniform('samplerCubeShadow shadowMapPoint[1]', included=True)
@ -800,19 +775,18 @@ def make_forward_base(con_mesh, parse_opacity=False, transluc_pass=False):
frag.write('direct += sampleLight(')
frag.write(' wposition, n, vVec, dotNV, pointPos, pointCol, albedo, roughness, specular, f0')
if is_shadows:
frag.write(', 0, pointBias, receiveShadow')
if is_transparent_shadows:
frag.write(', opacity != 1.0')
if parse_opacity:
frag.write(', 0, pointBias, receiveShadow, opacity != 1.0')
else:
frag.write(', 0, pointBias, receiveShadow, false')
if '_Spot' in wrd.world_defs:
frag.write(', true, spotData.x, spotData.y, spotDir, spotData.zw, spotRight')
if '_VoxelShadow' in wrd.world_defs:
frag.write(', voxels, voxelsSDF, clipmaps')
if '_Veloc' in wrd.world_defs or '_VoxelShadow' in wrd.world_defs:
frag.write(', velocity')
if '_MicroShadowing' in wrd.world_defs:
frag.write(', occlusion')
if '_SSRS' in wrd.world_defs:
frag.add_uniform('sampler2D gbufferD')
frag.add_uniform('sampler2D gbufferD', top=True)
frag.add_uniform('mat4 invVP', '_inverseViewProjectionMatrix')
frag.add_uniform('vec3 eye', '_cameraPosition')
frag.write(', gbufferD, invVP, eye')
@ -828,9 +802,10 @@ def make_forward_base(con_mesh, parse_opacity=False, transluc_pass=False):
if '_VoxelRefract' in wrd.world_defs and parse_opacity:
frag.write('if (opacity < 1.0) {')
frag.write(' vec3 refraction = traceRefraction(wposition, n, voxels, voxelsSDF, vVec, ior, roughness * roughness, clipmaps, gl_FragCoord.xy, velocity, opacity).rgb * (1.0 - F) * voxelgiRefr;')
frag.write(' indirect = mix(refraction, indirect, opacity);')
frag.write(' direct = mix(refraction, direct, opacity);')
frag.write(' vec2 velocity = -textureLod(sveloc, gl_FragCoord.xy, 0.0).rg;')
frag.write(' vec3 refraction = traceRefraction(wposition, n, voxels, voxelsSDF, vVec, ior, roughness, clipmaps, gl_FragCoord.xy,velocity).rgb;')
frag.write(' indirect = mix(refraction, indirect, opacity) * voxelgiRefr;')
frag.write(' direct = mix(refraction, direct, opacity) * voxelgiRefr;')
frag.write('}')
def _write_material_attribs_default(frag: shader.Shader, parse_opacity: bool):
@ -844,4 +819,4 @@ def _write_material_attribs_default(frag: shader.Shader, parse_opacity: bool):
frag.write('vec3 emissionCol;')
if parse_opacity:
frag.write('float opacity;')
frag.write('float ior = 1.45;')
frag.write('float ior;')

View File

@ -55,6 +55,7 @@ def write(vert, particle_info=None, shadowmap=False):
# Outs
out_index = True if particle_info != None and particle_info['index'] else False
out_random = True if particle_info != None and particle_info['random'] else False
out_age = True if particle_info != None and particle_info['age'] else False
out_lifetime = True if particle_info != None and particle_info['lifetime'] else False
out_location = True if particle_info != None and particle_info['location'] else False
@ -168,58 +169,57 @@ def write(vert, particle_info=None, shadowmap=False):
vert.write('float s = sin(p_angle);')
vert.write('vec3 center = spos.xyz - p_location;')
match rotation_mode:
case 'OB_X':
vert.write('vec3 rz = vec3(center.y, -center.x, center.z);')
vert.write('vec2 rotation = vec2(rz.y * c - rz.z * s, rz.y * s + rz.z * c);')
vert.write('spos.xyz = vec3(rz.x, rotation.x, rotation.y) + p_location;')
if rotation_mode == 'OB_X':
vert.write('vec3 rz = vec3(center.y, -center.x, center.z);')
vert.write('vec2 rotation = vec2(rz.y * c - rz.z * s, rz.y * s + rz.z * c);')
vert.write('spos.xyz = vec3(rz.x, rotation.x, rotation.y) + p_location;')
if (not shadowmap):
vert.write('wnormal = vec3(wnormal.y, -wnormal.x, wnormal.z);')
vert.write('vec2 n_rot = vec2(wnormal.y * c - wnormal.z * s, wnormal.y * s + wnormal.z * c);')
vert.write('wnormal = normalize(vec3(wnormal.x, n_rot.x, n_rot.y));')
case 'OB_Y':
vert.write('vec2 rotation = vec2(center.x * c + center.z * s, -center.x * s + center.z * c);')
vert.write('spos.xyz = vec3(rotation.x, center.y, rotation.y) + p_location;')
if (not shadowmap):
vert.write('wnormal = vec3(wnormal.y, -wnormal.x, wnormal.z);')
vert.write('vec2 n_rot = vec2(wnormal.y * c - wnormal.z * s, wnormal.y * s + wnormal.z * c);')
vert.write('wnormal = normalize(vec3(wnormal.x, n_rot.x, n_rot.y));')
elif rotation_mode == 'OB_Y':
vert.write('vec2 rotation = vec2(center.x * c + center.z * s, -center.x * s + center.z * c);')
vert.write('spos.xyz = vec3(rotation.x, center.y, rotation.y) + p_location;')
if (not shadowmap):
vert.write('wnormal = normalize(vec3(wnormal.x * c + wnormal.z * s, wnormal.y, -wnormal.x * s + wnormal.z * c));')
case 'OB_Z':
vert.write('vec3 rz = vec3(center.y, -center.x, center.z);')
vert.write('vec3 ry = vec3(-rz.z, rz.y, rz.x);')
vert.write('vec2 rotation = vec2(ry.x * c - ry.y * s, ry.x * s + ry.y * c);')
vert.write('spos.xyz = vec3(rotation.x, rotation.y, ry.z) + p_location;')
if (not shadowmap):
vert.write('wnormal = normalize(vec3(wnormal.x * c + wnormal.z * s, wnormal.y, -wnormal.x * s + wnormal.z * c));')
elif rotation_mode == 'OB_Z':
vert.write('vec3 rz = vec3(center.y, -center.x, center.z);')
vert.write('vec3 ry = vec3(-rz.z, rz.y, rz.x);')
vert.write('vec2 rotation = vec2(ry.x * c - ry.y * s, ry.x * s + ry.y * c);')
vert.write('spos.xyz = vec3(rotation.x, rotation.y, ry.z) + p_location;')
if (not shadowmap):
vert.write('wnormal = vec3(wnormal.y, -wnormal.x, wnormal.z);')
vert.write('wnormal = vec3(-wnormal.z, wnormal.y, wnormal.x);')
vert.write('vec2 n_rot = vec2(wnormal.x * c - wnormal.y * s, wnormal.x * s + wnormal.y * c);')
vert.write('wnormal = normalize(vec3(n_rot.x, n_rot.y, wnormal.z));')
case 'VEL':
vert.write('vec3 forward = -normalize(p_velocity);')
vert.write('if (length(forward) > 1e-5) {')
vert.write('vec3 world_up = vec3(0.0, 0.0, 1.0);')
if (not shadowmap):
vert.write('wnormal = vec3(wnormal.y, -wnormal.x, wnormal.z);')
vert.write('wnormal = vec3(-wnormal.z, wnormal.y, wnormal.x);')
vert.write('vec2 n_rot = vec2(wnormal.x * c - wnormal.y * s, wnormal.x * s + wnormal.y * c);')
vert.write('wnormal = normalize(vec3(n_rot.x, n_rot.y, wnormal.z));')
elif rotation_mode == 'VEL':
vert.write('vec3 forward = -normalize(p_velocity);')
vert.write('if (length(forward) > 1e-5) {')
vert.write('vec3 world_up = vec3(0.0, 0.0, 1.0);')
vert.write('if (abs(dot(forward, world_up)) > 0.999) {')
vert.write('world_up = vec3(-1.0, 0.0, 0.0);')
vert.write('}')
vert.write('if (abs(dot(forward, world_up)) > 0.999) {')
vert.write('world_up = vec3(-1.0, 0.0, 0.0);')
vert.write('}')
vert.write('vec3 right = cross(world_up, forward);')
vert.write('if (length(right) < 1e-5) {')
vert.write('forward = -forward;')
vert.write('right = cross(world_up, forward);')
vert.write('}')
vert.write('right = normalize(right);')
vert.write('vec3 up = normalize(cross(forward, right));')
vert.write('vec3 right = cross(world_up, forward);')
vert.write('if (length(right) < 1e-5) {')
vert.write('forward = -forward;')
vert.write('right = cross(world_up, forward);')
vert.write('}')
vert.write('right = normalize(right);')
vert.write('vec3 up = normalize(cross(forward, right));')
vert.write('mat3 rot = mat3(right, -forward, up);')
vert.write('mat3 phase = mat3(vec3(c, 0.0, -s), vec3(0.0, 1.0, 0.0), vec3(s, 0.0, c));')
vert.write('mat3 final_rot = rot * phase;')
vert.write('spos.xyz = final_rot * center + p_location;')
vert.write('mat3 rot = mat3(right, -forward, up);')
vert.write('mat3 phase = mat3(vec3(c, 0.0, -s), vec3(0.0, 1.0, 0.0), vec3(s, 0.0, c));')
vert.write('mat3 final_rot = rot * phase;')
vert.write('spos.xyz = final_rot * center + p_location;')
if (not shadowmap):
vert.write('wnormal = normalize(final_rot * wnormal);')
vert.write('}')
if (not shadowmap):
vert.write('wnormal = normalize(final_rot * wnormal);')
vert.write('}')
if rotation_factor_random != 0:
str_rotate_around = '''vec3 rotate_around(vec3 v, vec3 angle) {
@ -258,6 +258,11 @@ def write(vert, particle_info=None, shadowmap=False):
vert.add_out('float p_index')
vert.write('p_index = gl_InstanceID;')
if out_random:
vert.add_out('float p_random')
vert.write('p_random = fract(sin(gl_InstanceID) * 43758.5453);')
def write_tilesheet(vert):
# tilesx, tilesy, framerate - pd[3][0], pd[3][1], pd[3][2]
vert.write('int frame = int((p_age) / pd[3][2]);')

View File

@ -58,7 +58,6 @@ def make(context_id):
frag.write('fragColor[1] = vec4(n.xy, roughness, metallic);')
frag.write('fragColor[2] = vec4(ior, opacity, 0.0, 1.0);')
# frag.write('fragColor[2] = vec4(ior, opacity, packFloat2(basecol.r, basecol.g), basecol.b);')
make_finalize.make(con_refract)

View File

@ -0,0 +1,49 @@
import bpy
import lnx
import lnx.material.cycles as cycles
import lnx.material.mat_state as mat_state
import lnx.material.mat_utils as mat_utils
import lnx.material.make_mesh as make_mesh
import lnx.material.make_finalize as make_finalize
import lnx.assets as assets
if lnx.is_reload(__name__):
cycles = lnx.reload_module(cycles)
mat_state = lnx.reload_module(mat_state)
make_mesh = lnx.reload_module(make_mesh)
make_finalize = lnx.reload_module(make_finalize)
assets = lnx.reload_module(assets)
else:
lnx.enable_reload(__name__)
def make(context_id):
con_refraction_buffer = mat_state.data.add_context({ 'name': context_id, 'depth_write': False, 'compare_mode': 'less', 'cull_mode': 'clockwise' })
lnx_discard = mat_state.material.lnx_discard
blend = mat_state.material.lnx_blending
parse_opacity = blend or mat_utils.is_transluc(mat_state.material) or lnx_discard
make_mesh.make_base(con_refraction_buffer, parse_opacity)
vert = con_refraction_buffer.vert
frag = con_refraction_buffer.frag
frag.add_out('vec4 fragColor')
# Remove fragColor = ...;
frag.main = frag.main[:frag.main.rfind('fragColor')]
frag.write('\n')
if parse_opacity:
frag.write('fragColor = vec4(ior, opacity, 0.0, 1.0);')
else:
frag.write('fragColor = vec4(1.0, 1.0, 0.0, 1.0);')
make_finalize.make(con_refraction_buffer)
# assets.vs_equal(con_refract, assets.shader_cons['transluc_vert']) # shader_cons has no transluc yet
# assets.fs_equal(con_refract, assets.shader_cons['transluc_frag'])
return con_refraction_buffer

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,4 @@
from typing import Generator
from typing import Generator, Tuple
import bpy
@ -101,7 +101,7 @@ def iter_nodes_leenkxpbr(node_group: bpy.types.NodeTree) -> Generator[bpy.types.
yield node
def equals_color_socket(socket: bpy.types.NodeSocketColor, value: tuple[float, ...], *, comp_alpha=True) -> bool:
def equals_color_socket(socket: bpy.types.NodeSocketColor, value: Tuple[float, ...], *, comp_alpha=True) -> bool:
# NodeSocketColor.default_value is of bpy_prop_array type that doesn't
# support direct comparison
return (

View File

@ -4,7 +4,7 @@ This module contains a list of all material nodes that Leenkx supports
"""
from enum import IntEnum, unique
from dataclasses import dataclass
from typing import Any, Callable, Optional
from typing import Any, Callable, Optional, Dict, List, Tuple, TypeVar, Union
import bpy
@ -62,7 +62,7 @@ class MaterialNodeMeta:
"""
ALL_NODES: dict[str, MaterialNodeMeta] = {
ALL_NODES: Dict[str, MaterialNodeMeta] = {
# --- nodes_color
'BRIGHTCONTRAST': MaterialNodeMeta(parse_func=nodes_color.parse_brightcontrast),
'CURVE_RGB': MaterialNodeMeta(parse_func=nodes_color.parse_curvergb),

View File

@ -23,6 +23,7 @@ class ShaderData:
self.data = {'shader_datas': [self.sd]}
self.matname = lnx.utils.safesrc(lnx.utils.asset_name(material))
self.sd['name'] = self.matname + '_data'
self.sd['next_pass'] = material.lnx_next_pass
self.sd['contexts'] = []
def add_context(self, props) -> 'ShaderContext':

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