forked from LeenkxTeam/LNXSDK
Compare commits
300 Commits
7b17633c28
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 394ab38a80 | |||
| cc95912a7e | |||
| 85a44b930d | |||
| cd3090817a | |||
| d45c632dcd | |||
| 1c3c30e6ce | |||
| c9839c9be6 | |||
| 6a17251520 | |||
| 0adcafd697 | |||
| 423807c62f | |||
| 5598161b40 | |||
| e6ac30e57f | |||
| 5d1132b24c | |||
| 232ae3e7bc | |||
| a861665c98 | |||
| 4852a40848 | |||
| a00f3506ed | |||
| 9aa01e6436 | |||
| 18c564048f | |||
| da19096658 | |||
| 06343dcca1 | |||
| 4375087d3a | |||
| 825f783cbe | |||
| 2f9a24753e | |||
| 07efcaffdb | |||
| a6bef6b35a | |||
| b88f471f0f | |||
| 7e31a73d28 | |||
| 7fa4be0b07 | |||
| d5f3f05ab6 | |||
| 3bee97a560 | |||
| 4f4f28d62f | |||
| 7076fb6b7e | |||
| b72a22b5e9 | |||
| b265ab863c | |||
| f5fa754e17 | |||
| 48f5575e4e | |||
| f2c4be6336 | |||
| 2ddc938db8 | |||
| 5eb735ada2 | |||
| 9894cc20f2 | |||
| dbe6d0829a | |||
| 6f383e2ab2 | |||
| 5c2d29d7ce | |||
| 28579e14d7 | |||
| 2ec6f43cc5 | |||
| 027021815a | |||
| b9b387803f | |||
| e05d9d0237 | |||
| c908e6cad2 | |||
| 506a0a0245 | |||
| 5cf33724e4 | |||
| ac5aa3d19c | |||
| 0c534ee632 | |||
| 69a2bb1e7e | |||
| e3e7855d26 | |||
| f7917974f8 | |||
| fa2d8f05d5 | |||
| 5b86f32b51 | |||
| 73fcb55acc | |||
| c24baa3364 | |||
| 4517c4863f | |||
| 1299306e09 | |||
| f97d8fd846 | |||
| 8f8d4b1376 | |||
| a926fa8dbb | |||
| 6c3efa6c83 | |||
| 21afad6d09 | |||
| 04c6983a09 | |||
| 45966ef0bb | |||
| a72edc6203 | |||
| 6af1ef2df1 | |||
| 46e3047877 | |||
| de74af215a | |||
| b6e96553c2 | |||
| 58e009f709 | |||
| e88f101ca6 | |||
| d28d59b9e6 | |||
| a4398c7279 | |||
| abedfd799e | |||
| 4520422f6b | |||
| 88418c06c3 | |||
| aedc2783ab | |||
| 1505414c4c | |||
| fa818602c4 | |||
| 79dc458671 | |||
| 8e635fb1e9 | |||
| 4c2e6ab26a | |||
| 2371e3777e | |||
| b458b77e5c | |||
| 9b76f8cca9 | |||
| 5f2acb209e | |||
| 6fc446e7a9 | |||
| 71e57026e1 | |||
| 5288a98440 | |||
| 35e346be39 | |||
| 843ef0b058 | |||
| 177890bf39 | |||
| 9ac37e6dc7 | |||
| e697437778 | |||
| c94fc0fd97 | |||
| cd0a6f6788 | |||
| 4400e0e9c8 | |||
| 20cf07cfc3 | |||
| 1939f19c05 | |||
| 0d2b152ccb | |||
| 7f58e0fc85 | |||
| 0e4a6575c7 | |||
| 024676f43a | |||
| 8fe758862c | |||
| 1f3d1b47ae | |||
| f659a3c2be | |||
| 6eeb9017d4 | |||
| afe89c3834 | |||
| 8b695f72bb | |||
| 3d99fa60c0 | |||
| 43be7729ba | |||
| de0b1075c2 | |||
| c7aba23fa4 | |||
| 881f3267cc | |||
| 19b79d61c7 | |||
| fcbab54a0c | |||
| 8fd05d5514 | |||
| ad4013ed75 | |||
| 590e6219d5 | |||
| 8ac567b57b | |||
| 43b7ae7060 | |||
| 662981fa03 | |||
| a3866fb604 | |||
| 29e9e71a6a | |||
| bfb85b0a3b | |||
| ef99b800e0 | |||
| 7cca955fc5 | |||
| 7e7bbd5eae | |||
| c31b2a18ad | |||
| fb47bf2564 | |||
| 7ae6750620 | |||
| 5b87010f76 | |||
| 97e952fc15 | |||
| b440539d65 | |||
| fbf63e4f17 | |||
| a318e08072 | |||
| 7ae458a9dd | |||
| 60a9db6459 | |||
| 3b5a93c92a | |||
| 4af990796e | |||
| 9fb4916c3c | |||
| f61d5833bb | |||
| 40b52be713 | |||
| 07d8422f22 | |||
| 7179d42b27 | |||
| 9984615f8c | |||
| 99a5d7d445 | |||
| 5e0acd3d5d | |||
| 0430e06acd | |||
| f4077e461b | |||
| 28943f1522 | |||
| df4feac132 | |||
| 82412dbf81 | |||
| 6afc209db7 | |||
| e9aae53be9 | |||
| a65675ef75 | |||
| 8f073c5ae1 | |||
| 08d08e42d9 | |||
| a1ee335c68 | |||
| de6bf8a08a | |||
| 9622f35b73 | |||
| da666e6d23 | |||
| 6ff7804ec1 | |||
| 0265ef5b64 | |||
| 53c5000975 | |||
| 7647231696 | |||
| 1e583a795d | |||
| da25d8c313 | |||
| 70695b3b31 | |||
| 858537d54c | |||
| 2998a5585e | |||
| c35c59e6a9 | |||
| 15ac833f2c | |||
| 8077f00ada | |||
| b9848cd2dc | |||
| 58140ad583 | |||
| e922cc38e6 | |||
| 9e2b601445 | |||
| 0439dde4a8 | |||
| 57f0e937d0 | |||
| e234c8615c | |||
| 2e7ccb5151 | |||
| e594518e57 | |||
| a41be0f436 | |||
| 1306033b36 | |||
| eee0011cdd | |||
| 315ac0bd16 | |||
| f289e6f89c | |||
| b89ebfd9c6 | |||
| a142b248ef | |||
| 700d236bf1 | |||
| f228eab8d3 | |||
| 863d884b76 | |||
| 34e0f5a282 | |||
| 45e2e52008 | |||
| 444a215e63 | |||
| fb2d2a1a7c | |||
| f88c04abea | |||
| 6fdd4b3f70 | |||
| a389c27d75 | |||
| 1909c3da9f | |||
| 1c648b0433 | |||
| 5824bd91aa | |||
| 43fe559eef | |||
| 12c09545ce | |||
| 0e60951ec9 | |||
| ccb8b358d3 | |||
| 3103a976a6 | |||
| 1a8586777b | |||
| 3721c774a1 | |||
| a58fba408d | |||
| 82fa7bcfe3 | |||
| 268fba6cd5 | |||
| 4ab14ce6c8 | |||
| 9023e8d1da | |||
| b58c7a9632 | |||
| 99b70622f5 | |||
| 647b73b746 | |||
| 935c30ec08 | |||
| 0b0d597f89 | |||
| d5878afb30 | |||
| 96b55a1a56 | |||
| 91b3072305 | |||
| 1d0b338d92 | |||
| 8e83c0d0d0 | |||
| 927baec4df | |||
| f5c9e70d1a | |||
| 709e0ed9cb | |||
| 0423a735fc | |||
| 7bcf985023 | |||
| bd413917fc | |||
| 852377f60d | |||
| e17e9a8e35 | |||
| 4055c979a1 | |||
| 06b003ecdb | |||
| fd7f215bb2 | |||
| 6a1df9ec46 | |||
| deccac3c46 | |||
| 432b0210b2 | |||
| 14cf5cebed | |||
| d23232205b | |||
| 2307e1504f | |||
| 1ebfecb644 | |||
| 175b575b23 | |||
| 63943a9cbf | |||
| 224d9be76f | |||
| 62d3c8757b | |||
| 3785f93573 | |||
| ffb276745f | |||
| d1c9258da5 | |||
| 3e0cd2be35 | |||
| 1fd1973470 | |||
| a01c72ef76 | |||
| 4b01a562c9 | |||
| 88c7c5b99e | |||
| 6972d9abc4 | |||
| 63c6b9d98b | |||
| 313d24bbc8 | |||
| 6d2812306d | |||
| e84d6ada84 | |||
| 5057f2b946 | |||
| 2715fe3398 | |||
| 7cb8b8a2d2 | |||
| cd606009e0 | |||
| 965162b101 | |||
| c61a57bfb3 | |||
| 2434ad07f2 | |||
| cdc425fbcb | |||
| 846bb28c86 | |||
| 7fabd77ef8 | |||
| fb1a5c88bf | |||
| e05c83a8bb | |||
| ee4f62e881 | |||
| 59f8dff22f | |||
| 5572226ac5 | |||
| d04874e0b3 | |||
| ef8b3a99ab | |||
| 1d3254a237 | |||
| 188af4a50f | |||
| c45baaf396 | |||
| 4b1da08819 | |||
| aeb353fb20 | |||
| 65961b1593 | |||
| 1c472155e2 | |||
| 4238f0b2a0 | |||
| b40aadf76c | |||
| 00493bed9c | |||
| 7277987335 | |||
| a48ec4d034 | |||
| 7f0153f816 | |||
| 40d893e139 | |||
| 9b9289d27d | |||
| 8786798edd | |||
| fa425a98a5 |
2
.gitattributes
vendored
Normal file
2
.gitattributes
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
*.hdr binary
|
||||||
|
blender/lnx/props.py ident
|
||||||
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
__pycache__/
|
||||||
|
*.pyc
|
||||||
|
*.DS_Store
|
||||||
|
**/workspace.xml
|
||||||
|
**/vcs.xml
|
||||||
@ -227,7 +227,7 @@ class SystemImpl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static inline var maxGamepads: Int = 4;
|
static inline var maxGamepads: Int = 4;
|
||||||
static var frame: Framebuffer;
|
public static var frame: Framebuffer;
|
||||||
static var keyboard: Keyboard = null;
|
static var keyboard: Keyboard = null;
|
||||||
static var mouse: kha.input.Mouse;
|
static var mouse: kha.input.Mouse;
|
||||||
static var surface: Surface;
|
static var surface: Surface;
|
||||||
@ -388,7 +388,8 @@ class SystemImpl {
|
|||||||
{
|
{
|
||||||
alpha: false,
|
alpha: false,
|
||||||
antialias: options.framebuffer.samplesPerPixel > 1,
|
antialias: options.framebuffer.samplesPerPixel > 1,
|
||||||
stencil: true
|
stencil: true,
|
||||||
|
xrCompatible: true
|
||||||
}); // preserveDrawingBuffer: true } ); Warning: preserveDrawingBuffer can cause huge performance issues on mobile browsers
|
}); // preserveDrawingBuffer: true } ); Warning: preserveDrawingBuffer can cause huge performance issues on mobile browsers
|
||||||
SystemImpl.gl.pixelStorei(GL.UNPACK_PREMULTIPLY_ALPHA_WEBGL, 1);
|
SystemImpl.gl.pixelStorei(GL.UNPACK_PREMULTIPLY_ALPHA_WEBGL, 1);
|
||||||
|
|
||||||
@ -417,7 +418,8 @@ class SystemImpl {
|
|||||||
{
|
{
|
||||||
alpha: false,
|
alpha: false,
|
||||||
antialias: options.framebuffer.samplesPerPixel > 1,
|
antialias: options.framebuffer.samplesPerPixel > 1,
|
||||||
stencil: true
|
stencil: true,
|
||||||
|
xrCompatible: true
|
||||||
}); // preserveDrawingBuffer: true } ); WARNING: preserveDrawingBuffer causes huge performance issues (on mobile browser)!
|
}); // preserveDrawingBuffer: true } ); WARNING: preserveDrawingBuffer causes huge performance issues (on mobile browser)!
|
||||||
SystemImpl.gl.pixelStorei(GL.UNPACK_PREMULTIPLY_ALPHA_WEBGL, 1);
|
SystemImpl.gl.pixelStorei(GL.UNPACK_PREMULTIPLY_ALPHA_WEBGL, 1);
|
||||||
SystemImpl.gl.getExtension("OES_texture_float");
|
SystemImpl.gl.getExtension("OES_texture_float");
|
||||||
@ -547,6 +549,12 @@ class SystemImpl {
|
|||||||
];
|
];
|
||||||
|
|
||||||
function animate(timestamp) {
|
function animate(timestamp) {
|
||||||
|
if (untyped Browser.window._khaSkipWindowRender == true) {
|
||||||
|
if (requestAnimationFrame != null)
|
||||||
|
requestAnimationFrame(animate);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (requestAnimationFrame == null)
|
if (requestAnimationFrame == null)
|
||||||
Browser.window.setTimeout(animate, 1000.0 / 60.0);
|
Browser.window.setTimeout(animate, 1000.0 / 60.0);
|
||||||
else
|
else
|
||||||
|
|||||||
@ -47,6 +47,7 @@ class Graphics implements kha.graphics4.Graphics {
|
|||||||
|
|
||||||
static var current: Graphics = null;
|
static var current: Graphics = null;
|
||||||
static var useVertexAttributes: Int = 0;
|
static var useVertexAttributes: Int = 0;
|
||||||
|
public static var vrFramebufferBound: Bool = false;
|
||||||
|
|
||||||
public function new(renderTarget: Canvas = null) {
|
public function new(renderTarget: Canvas = null) {
|
||||||
this.renderTarget = renderTarget;
|
this.renderTarget = renderTarget;
|
||||||
@ -89,8 +90,10 @@ class Graphics implements kha.graphics4.Graphics {
|
|||||||
SystemImpl.gl.enable(GL.BLEND);
|
SystemImpl.gl.enable(GL.BLEND);
|
||||||
SystemImpl.gl.blendFunc(GL.SRC_ALPHA, GL.ONE_MINUS_SRC_ALPHA);
|
SystemImpl.gl.blendFunc(GL.SRC_ALPHA, GL.ONE_MINUS_SRC_ALPHA);
|
||||||
if (renderTarget == null) {
|
if (renderTarget == null) {
|
||||||
SystemImpl.gl.bindFramebuffer(GL.FRAMEBUFFER, null);
|
if (!vrFramebufferBound) {
|
||||||
SystemImpl.gl.viewport(0, 0, System.windowWidth(), System.windowHeight());
|
SystemImpl.gl.bindFramebuffer(GL.FRAMEBUFFER, null);
|
||||||
|
SystemImpl.gl.viewport(0, 0, System.windowWidth(), System.windowHeight());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
SystemImpl.gl.bindFramebuffer(GL.FRAMEBUFFER, renderTargetFrameBuffer);
|
SystemImpl.gl.bindFramebuffer(GL.FRAMEBUFFER, renderTargetFrameBuffer);
|
||||||
|
|||||||
@ -13,10 +13,29 @@ import kha.SystemImpl;
|
|||||||
|
|
||||||
class VrInterface extends kha.vr.VrInterface {
|
class VrInterface extends kha.vr.VrInterface {
|
||||||
var vrEnabled: Bool = false;
|
var vrEnabled: Bool = false;
|
||||||
|
var isWebXR: Bool = false;
|
||||||
|
|
||||||
var vrDisplay: Dynamic;
|
var vrDisplay: Dynamic;
|
||||||
var frameData: Dynamic;
|
var frameData: Dynamic;
|
||||||
|
|
||||||
|
var xrSession: Dynamic;
|
||||||
|
var xrRefSpace: Dynamic;
|
||||||
|
public var xrGLLayer: Dynamic;
|
||||||
|
public var currentFrame: Dynamic;
|
||||||
|
public var currentViews: Dynamic;
|
||||||
|
public var currentViewerPose: Dynamic;
|
||||||
|
public var currentInputSources: Dynamic;
|
||||||
|
var xrAnimationFrameHandle: Int = -1;
|
||||||
|
|
||||||
|
public var _glContext: Dynamic;
|
||||||
|
public var _leftViewport: Dynamic;
|
||||||
|
public var _rightViewport: Dynamic;
|
||||||
|
public var _cachedViewsLength: Int = 0;
|
||||||
|
|
||||||
|
var savedCanvasWidth: Int = 0;
|
||||||
|
var savedCanvasHeight: Int = 0;
|
||||||
|
var browserRAFId: Int = -1;
|
||||||
|
|
||||||
var leftProjectionMatrix: FastMatrix4 = FastMatrix4.identity();
|
var leftProjectionMatrix: FastMatrix4 = FastMatrix4.identity();
|
||||||
var rightProjectionMatrix: FastMatrix4 = FastMatrix4.identity();
|
var rightProjectionMatrix: FastMatrix4 = FastMatrix4.identity();
|
||||||
var leftViewMatrix: FastMatrix4 = FastMatrix4.identity();
|
var leftViewMatrix: FastMatrix4 = FastMatrix4.identity();
|
||||||
@ -30,7 +49,20 @@ class VrInterface extends kha.vr.VrInterface {
|
|||||||
public function new() {
|
public function new() {
|
||||||
super();
|
super();
|
||||||
#if kha_webvr
|
#if kha_webvr
|
||||||
var displayEnabled: Bool = Syntax.code("navigator.getVRDisplays");
|
var webXREnabled: Bool = Syntax.code("navigator.xr");
|
||||||
|
if (webXREnabled) {
|
||||||
|
isWebXR = true;
|
||||||
|
vrEnabled = true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
var displayEnabled: Bool = Syntax.code("navigator.getVRDisplays");
|
||||||
|
if (displayEnabled) {
|
||||||
|
isWebXR = false;
|
||||||
|
vrEnabled = true;
|
||||||
|
getVRDisplays();
|
||||||
|
trace("WebVR 1.1 API detected");
|
||||||
|
}
|
||||||
|
}
|
||||||
#else
|
#else
|
||||||
var displayEnabled = false;
|
var displayEnabled = false;
|
||||||
#end
|
#end
|
||||||
@ -64,27 +96,475 @@ class VrInterface extends kha.vr.VrInterface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public override function onVRRequestPresent() {
|
public override function onVRRequestPresent() {
|
||||||
|
if (isWebXR) {
|
||||||
|
requestWebXRSession();
|
||||||
|
} else {
|
||||||
|
// WebVR 1.1
|
||||||
|
try {
|
||||||
|
vrDisplay.requestPresent([{source: SystemImpl.khanvas}]).then(function() {
|
||||||
|
onResize();
|
||||||
|
vrDisplay.requestAnimationFrame(onAnimationFrame);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
catch (err:Dynamic) {
|
||||||
|
trace("Failed to requestPresent WebVR: " + err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function requestWebXRSession() {
|
||||||
|
var vrScaleFactor = 1.0;
|
||||||
|
#if lnx_vr
|
||||||
|
vrScaleFactor = leenkx.renderpath.Inc.getSuperSampling();
|
||||||
|
trace("[VR] Using renderpath superSample as framebufferScaleFactor: " + vrScaleFactor);
|
||||||
|
#end
|
||||||
|
|
||||||
try {
|
try {
|
||||||
vrDisplay.requestPresent([{source: SystemImpl.khanvas}]).then(function() {
|
Syntax.code("
|
||||||
onResize();
|
|
||||||
vrDisplay.requestAnimationFrame(onAnimationFrame);
|
|
||||||
});
|
let gl = null;
|
||||||
|
let canvas = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (typeof kha_SystemImpl !== 'undefined') {
|
||||||
|
gl = kha_SystemImpl.gl;
|
||||||
|
canvas = kha_SystemImpl.khanvas;
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
trace('kha_SystemImpl access failed: ' + e.message;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!canvas) {
|
||||||
|
canvas = document.querySelector('canvas');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (canvas && !gl) {
|
||||||
|
const contextAttributes = { xrCompatible: true, antialias: true, alpha: false };
|
||||||
|
gl = canvas.getContext('webgl2', contextAttributes) ||
|
||||||
|
canvas.getContext('webgl', contextAttributes) ||
|
||||||
|
canvas.getContext('experimental-webgl', contextAttributes);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!canvas) {
|
||||||
|
canvas = document.getElementById('khanvas');
|
||||||
|
if (canvas && !gl) {
|
||||||
|
gl = canvas.getContext('webgl2') || canvas.getContext('webgl');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!gl) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const self = this;
|
||||||
|
const glContext = gl;
|
||||||
|
|
||||||
|
self._glContext = glContext;
|
||||||
|
");
|
||||||
|
|
||||||
|
Syntax.code("
|
||||||
|
self._vrRenderCallback = function() {
|
||||||
|
self.vrRenderCallback();
|
||||||
|
};
|
||||||
|
|
||||||
|
const checkAndRequestSession = async () => {
|
||||||
|
try {
|
||||||
|
const supported = await navigator.xr.isSessionSupported('immersive-vr');
|
||||||
|
if (!supported) {
|
||||||
|
trace('immersive-vr session not supported');
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
trace('WARN: isSessionSupported failed: ' + e.message);
|
||||||
|
// Continue anyway as some browsers do not support the check itself
|
||||||
|
}
|
||||||
|
|
||||||
|
return await navigator.xr.requestSession('immersive-vr', {
|
||||||
|
optionalFeatures: ['local-floor', 'hand-tracking', 'bounded-floor']
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
checkAndRequestSession().then(async (session) => {
|
||||||
|
self.xrSession = session;
|
||||||
|
if (typeof window !== 'undefined') {
|
||||||
|
window._khaSkipWindowRender = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const contextAttributes = glContext.getContextAttributes();
|
||||||
|
if (!contextAttributes || !contextAttributes.xrCompatible) {
|
||||||
|
await glContext.makeXRCompatible();
|
||||||
|
}
|
||||||
|
self.xrGLLayer = new XRWebGLLayer(session, glContext, {
|
||||||
|
depth: true, // Essential for depth testing
|
||||||
|
stencil: false, // Not needed, wastes memory
|
||||||
|
alpha: false, // Not needed in VR wastes
|
||||||
|
antialias: true, // Smooth rendering
|
||||||
|
framebufferScaleFactor: {0} // VR resolution quality from renderpath
|
||||||
|
});
|
||||||
|
|
||||||
|
if (self.xrGLLayer.framebufferWidth === 0 || self.xrGLLayer.framebufferHeight === 0) {
|
||||||
|
trace('XRWebGLLayer framebuffer has invalid dimensions');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
session.updateRenderState({
|
||||||
|
baseLayer: self.xrGLLayer
|
||||||
|
});
|
||||||
|
const handlers = {};
|
||||||
|
|
||||||
|
handlers.end = () => {
|
||||||
|
self.onSessionEnd();
|
||||||
|
};
|
||||||
|
session.addEventListener('end', handlers.end);
|
||||||
|
|
||||||
|
handlers.select = (event) => {
|
||||||
|
if (self.onSelect) self.onSelect(event);
|
||||||
|
};
|
||||||
|
session.addEventListener('select', handlers.select);
|
||||||
|
|
||||||
|
handlers.selectstart = (event) => {
|
||||||
|
if (self.onSelectStart) self.onSelectStart(event);
|
||||||
|
};
|
||||||
|
session.addEventListener('selectstart', handlers.selectstart);
|
||||||
|
|
||||||
|
handlers.selectend = (event) => {
|
||||||
|
if (self.onSelectEnd) self.onSelectEnd(event);
|
||||||
|
};
|
||||||
|
session.addEventListener('selectend', handlers.selectend);
|
||||||
|
|
||||||
|
handlers.squeeze = (event) => {
|
||||||
|
if (self.onSqueeze) self.onSqueeze(event);
|
||||||
|
};
|
||||||
|
session.addEventListener('squeeze', handlers.squeeze);
|
||||||
|
|
||||||
|
handlers.squeezestart = (event) => {
|
||||||
|
if (self.onSqueezeStart) self.onSqueezeStart(event);
|
||||||
|
};
|
||||||
|
session.addEventListener('squeezestart', handlers.squeezestart);
|
||||||
|
|
||||||
|
handlers.squeezeend = (event) => {
|
||||||
|
if (self.onSqueezeEnd) self.onSqueezeEnd(event);
|
||||||
|
};
|
||||||
|
session.addEventListener('squeezeend', handlers.squeezeend);
|
||||||
|
|
||||||
|
session.addEventListener('inputsourceschange', handlers.inputsourceschange);
|
||||||
|
|
||||||
|
handlers.visibilitychange = (event) => {
|
||||||
|
const state = event.session.visibilityState;
|
||||||
|
|
||||||
|
};
|
||||||
|
session.addEventListener('visibilitychange', handlers.visibilitychange);
|
||||||
|
|
||||||
|
self._eventHandlers = handlers;
|
||||||
|
|
||||||
|
const requestRefSpace = async () => {
|
||||||
|
const spaces = ['local-floor', 'local'];
|
||||||
|
for (const space of spaces) {
|
||||||
|
try {
|
||||||
|
const refSpace = await session.requestReferenceSpace(space);
|
||||||
|
return refSpace;
|
||||||
|
} catch (e) {
|
||||||
|
trace(space + ' not supported');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
trace('No reference space supported');
|
||||||
|
};
|
||||||
|
|
||||||
|
requestRefSpace().then((refSpace) => {
|
||||||
|
self.xrRefSpace = refSpace;
|
||||||
|
|
||||||
|
if (canvas && canvas.width) {
|
||||||
|
self.savedCanvasWidth = canvas.width;
|
||||||
|
self.savedCanvasHeight = canvas.height;
|
||||||
|
} else {
|
||||||
|
const canvasFallback = document.querySelector('canvas');
|
||||||
|
if (canvasFallback) {
|
||||||
|
self.savedCanvasWidth = canvasFallback.width;
|
||||||
|
self.savedCanvasHeight = canvasFallback.height;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const onFrame = (time, frame) => {
|
||||||
|
try {
|
||||||
|
if (self.xrSession) {
|
||||||
|
self.xrAnimationFrameHandle = self.xrSession.requestAnimationFrame(onFrame);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!self._lastFrameTime) self._lastFrameTime = time;
|
||||||
|
const deltaTime = time - self._lastFrameTime;
|
||||||
|
self._lastFrameTime = time;
|
||||||
|
|
||||||
|
if (!window._xrFrameCount) window._xrFrameCount = 0;
|
||||||
|
window._xrFrameCount++;
|
||||||
|
|
||||||
|
if (glContext && self.xrSession && self.xrSession.renderState && self.xrSession.renderState.baseLayer) {
|
||||||
|
const layer = self.xrSession.renderState.baseLayer;
|
||||||
|
if (layer.framebuffer) {
|
||||||
|
const pose = frame.getViewerPose(self.xrRefSpace);
|
||||||
|
if (pose && pose.views && pose.views.length > 0) {
|
||||||
|
glContext.bindFramebuffer(glContext.FRAMEBUFFER, layer.framebuffer);
|
||||||
|
|
||||||
|
let bgR = 0, bgG = 0, bgB = 0;
|
||||||
|
if (typeof iron !== 'undefined' && iron.Scene && iron.Scene.active && iron.Scene.active.world && iron.Scene.active.world.raw) {
|
||||||
|
const bgColor = iron.Scene.active.world.raw.background_color;
|
||||||
|
if (bgColor !== undefined) {
|
||||||
|
bgR = ((bgColor >> 16) & 255) / 255;
|
||||||
|
bgG = ((bgColor >> 8) & 255) / 255;
|
||||||
|
bgB = (bgColor & 255) / 255;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const view of pose.views) {
|
||||||
|
const vp = layer.getViewport(view);
|
||||||
|
glContext.viewport(vp.x, vp.y, vp.width, vp.height);
|
||||||
|
glContext.scissor(vp.x, vp.y, vp.width, vp.height);
|
||||||
|
glContext.enable(glContext.SCISSOR_TEST);
|
||||||
|
|
||||||
|
glContext.clearColor(bgR, bgG, bgB, 1.0);
|
||||||
|
glContext.clear(glContext.COLOR_BUFFER_BIT | glContext.DEPTH_BUFFER_BIT);
|
||||||
|
|
||||||
|
}
|
||||||
|
glContext.disable(glContext.SCISSOR_TEST);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!self.xrSession) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const pose = frame.getViewerPose(self.xrRefSpace);
|
||||||
|
if (!pose) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pose.emulatedPosition && !self._emulatedPosLogged) {
|
||||||
|
self._emulatedPosLogged = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const views = pose.views;
|
||||||
|
|
||||||
|
if (!self.xrSession.renderState || !self.xrSession.renderState.baseLayer) {
|
||||||
|
if (!self._noRenderStateLogged) {
|
||||||
|
self._noRenderStateLogged = true;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const glLayer = self.xrSession.renderState.baseLayer;
|
||||||
|
|
||||||
|
if (!views || views.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self.xrSession.visibilityState === 'hidden') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.currentFrame = frame;
|
||||||
|
self.currentViews = views;
|
||||||
|
self.currentViewerPose = pose; /
|
||||||
|
if (self.xrSession && self.xrSession.inputSources) {
|
||||||
|
self.currentInputSources = self.xrSession.inputSources;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (glContext.isContextLost()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!glContext || !glLayer || !glLayer.framebuffer) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (glContext.bindVertexArray) {
|
||||||
|
glContext.bindVertexArray(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
while (glContext.getError() !== glContext.NO_ERROR) {
|
||||||
|
// Drain error queue
|
||||||
|
}
|
||||||
|
|
||||||
|
glContext.bindFramebuffer(glContext.FRAMEBUFFER, glLayer.framebuffer);
|
||||||
|
|
||||||
|
|
||||||
|
const bindError = glContext.getError();
|
||||||
|
if (bindError !== glContext.NO_ERROR && !self._bindErrorLogged) {
|
||||||
|
self._bindErrorLogged = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const fbStatus = glContext.checkFramebufferStatus(glContext.FRAMEBUFFER);
|
||||||
|
if (fbStatus !== glContext.FRAMEBUFFER_COMPLETE) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
glContext.enable(glContext.DEPTH_TEST);
|
||||||
|
glContext.depthFunc(glContext.LEQUAL);
|
||||||
|
glContext.depthMask(true);
|
||||||
|
glContext.colorMask(true, true, true, true);
|
||||||
|
glContext.disable(glContext.BLEND);
|
||||||
|
glContext.enable(glContext.CULL_FACE);
|
||||||
|
glContext.cullFace(glContext.BACK);
|
||||||
|
glContext.frontFace(glContext.CCW);
|
||||||
|
glContext.disable(glContext.STENCIL_TEST);
|
||||||
|
glContext.disable(glContext.POLYGON_OFFSET_FILL);
|
||||||
|
|
||||||
|
|
||||||
|
if (!self._fbLogged && p) {
|
||||||
|
const depthTest = glContext.isEnabled(glContext.DEPTH_TEST);
|
||||||
|
const cullFace = glContext.isEnabled(glContext.CULL_FACE);
|
||||||
|
const blend = glContext.isEnabled(glContext.BLEND);
|
||||||
|
self._fbLogged = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (views.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (views.length >= 1) {
|
||||||
|
try {
|
||||||
|
self._leftViewport = glLayer.getViewport(views[0]);
|
||||||
|
self._rightViewport = views.length >= 2 ? glLayer.getViewport(views[1]) : null;
|
||||||
|
self._cachedViewsLength = views.length;
|
||||||
|
|
||||||
|
if (!self._leftViewport) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (views.length >= 1) {
|
||||||
|
self.leftProjectionMatrix = self.createMatrixFromArray(views[0].projectionMatrix);
|
||||||
|
self.leftViewMatrix = self.createMatrixFromArray(views[0].transform.inverse.matrix);
|
||||||
|
}
|
||||||
|
if (views.length >= 2) {
|
||||||
|
self.rightProjectionMatrix = self.createMatrixFromArray(views[1].projectionMatrix);
|
||||||
|
self.rightViewMatrix = self.createMatrixFromArray(views[1].transform.inverse.matrix);
|
||||||
|
} else if (views.length === 1) {
|
||||||
|
self.rightProjectionMatrix = self.leftProjectionMatrix;
|
||||||
|
self.rightViewMatrix = self.leftViewMatrix;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self._vrRenderCallback) {
|
||||||
|
self._vrRenderCallback();
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (err) {
|
||||||
|
console.error('XR Frame Error:', err);
|
||||||
|
} finally {
|
||||||
|
self.currentFrame = null;
|
||||||
|
self.currentViews = null;
|
||||||
|
self.currentInputSources = null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
self.xrAnimationFrameHandle = session.requestAnimationFrame(onFrame);
|
||||||
|
}).catch((err) => {
|
||||||
|
trace('REF SPACE FAILED: ' + err.message );
|
||||||
|
});
|
||||||
|
}).catch((err) => {
|
||||||
|
trace('SESSION FAILED: ' + err);
|
||||||
|
});
|
||||||
|
", vrScaleFactor);
|
||||||
}
|
}
|
||||||
catch (err:Dynamic) {
|
catch (err:Dynamic) {
|
||||||
trace("Failed to requestPresent.");
|
trace("Failed to requestSession (WebXR).");
|
||||||
trace(err);
|
trace(err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public override function onVRExitPresent() {
|
function onSessionEnd() {
|
||||||
try {
|
var canvas = SystemImpl.khanvas;
|
||||||
vrDisplay.exitPresent([{source: SystemImpl.khanvas}]).then(function() {
|
if (canvas == null) {
|
||||||
onResize();
|
canvas = Syntax.code("document.querySelector('canvas')");
|
||||||
});
|
|
||||||
}
|
}
|
||||||
catch (err:Dynamic) {
|
|
||||||
trace("Failed to exitPresent.");
|
if (canvas != null && savedCanvasWidth > 0 && savedCanvasHeight > 0) {
|
||||||
trace(err);
|
canvas.width = savedCanvasWidth;
|
||||||
|
canvas.height = savedCanvasHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (xrSession != null) {
|
||||||
|
Syntax.code("
|
||||||
|
if (this.xrAnimationFrameHandle !== -1 && this.xrSession) {
|
||||||
|
this.xrSession.cancelAnimationFrame(this.xrAnimationFrameHandle);
|
||||||
|
this.xrAnimationFrameHandle = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._eventHandlers && this.xrSession) {
|
||||||
|
const handlers = this._eventHandlers;
|
||||||
|
const events = ['end', 'select', 'selectstart', 'selectend', 'squeeze', 'squeezestart', 'squeezeend', 'inputsourceschange', 'visibilitychange'];
|
||||||
|
for (const event of events) {
|
||||||
|
if (handlers[event]) {
|
||||||
|
this.xrSession.removeEventListener(event, handlers[event]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this._eventHandlers = null;
|
||||||
|
}
|
||||||
|
");
|
||||||
|
}
|
||||||
|
|
||||||
|
Syntax.code("
|
||||||
|
const gl = this._glContext;
|
||||||
|
if (gl) {
|
||||||
|
gl.bindFramebuffer(gl.FRAMEBUFFER, null); // Restore default framebuffer
|
||||||
|
gl.disable(gl.SCISSOR_TEST);
|
||||||
|
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof window !== 'undefined') {
|
||||||
|
window._khaSkipWindowRender = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.xrSession = null;
|
||||||
|
this.xrRefSpace = null;
|
||||||
|
this.xrGLLayer = null;
|
||||||
|
this.currentFrame = null;
|
||||||
|
this.currentViews = null;
|
||||||
|
this.currentInputSources = null;
|
||||||
|
this._lastFrameTime = null;
|
||||||
|
|
||||||
|
if (typeof window !== 'undefined') {
|
||||||
|
delete window._xrFrameCount;
|
||||||
|
delete window._ironRenderCount;
|
||||||
|
delete window._slowFrameCount;
|
||||||
|
}
|
||||||
|
");
|
||||||
|
}
|
||||||
|
|
||||||
|
public override function onVRExitPresent() {
|
||||||
|
if (isWebXR) {
|
||||||
|
try {
|
||||||
|
if (xrSession != null) {
|
||||||
|
Syntax.code("
|
||||||
|
if (this.xrSession) {
|
||||||
|
this.xrSession.end().then(() => {
|
||||||
|
trace('Session ended');
|
||||||
|
}).catch((err) => {
|
||||||
|
trace('Session.end() failed:', err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
");
|
||||||
|
xrSession = null;
|
||||||
|
xrRefSpace = null;
|
||||||
|
xrGLLayer = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (err:Dynamic) {
|
||||||
|
trace("Failed to exitPresent in WebXR");
|
||||||
|
trace(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// WebVR 1.1
|
||||||
|
try {
|
||||||
|
vrDisplay.exitPresent([{source: SystemImpl.khanvas}]).then(function() {
|
||||||
|
onResize();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
catch (err:Dynamic) {
|
||||||
|
trace("Failed to exitPresent");
|
||||||
|
trace(err);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -116,13 +596,25 @@ class VrInterface extends kha.vr.VrInterface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function onResize() {
|
function onResize() {
|
||||||
if (vrDisplay != null && vrDisplay.isPresenting) {
|
if (isWebXR) {
|
||||||
SystemImpl.khanvas.width = vrWidth;
|
return;
|
||||||
SystemImpl.khanvas.height = vrHeight;
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
SystemImpl.khanvas.width = width;
|
// WebVR 1.1
|
||||||
SystemImpl.khanvas.height = height;
|
if (vrDisplay != null && vrDisplay.isPresenting) {
|
||||||
|
var canvas = SystemImpl.khanvas;
|
||||||
|
if (canvas != null) {
|
||||||
|
canvas.width = vrWidth;
|
||||||
|
canvas.height = vrHeight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
var canvas = SystemImpl.khanvas;
|
||||||
|
if (canvas != null) {
|
||||||
|
canvas.width = width;
|
||||||
|
canvas.height = height;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -174,9 +666,11 @@ class VrInterface extends kha.vr.VrInterface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public override function IsPresenting(): Bool {
|
public override function IsPresenting(): Bool {
|
||||||
|
var presenting = false;
|
||||||
if (vrDisplay != null)
|
if (vrDisplay != null)
|
||||||
return vrDisplay.isPresenting;
|
presenting = vrDisplay.isPresenting;
|
||||||
return false;
|
}
|
||||||
|
return presenting;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override function IsVrEnabled(): Bool {
|
public override function IsVrEnabled(): Bool {
|
||||||
@ -207,6 +701,10 @@ class VrInterface extends kha.vr.VrInterface {
|
|||||||
|
|
||||||
function createMatrixFromArray(array: Float32Array): FastMatrix4 {
|
function createMatrixFromArray(array: Float32Array): FastMatrix4 {
|
||||||
var matrix: FastMatrix4 = FastMatrix4.identity();
|
var matrix: FastMatrix4 = FastMatrix4.identity();
|
||||||
|
if (array == null || array.length < 16) {
|
||||||
|
trace("Warning: Invalid matrix array, using identity");
|
||||||
|
return matrix;
|
||||||
|
}
|
||||||
matrix._00 = array[0];
|
matrix._00 = array[0];
|
||||||
matrix._01 = array[1];
|
matrix._01 = array[1];
|
||||||
matrix._02 = array[2];
|
matrix._02 = array[2];
|
||||||
@ -246,4 +744,63 @@ class VrInterface extends kha.vr.VrInterface {
|
|||||||
}
|
}
|
||||||
return quaternion;
|
return quaternion;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function vrRenderCallback(): Void {
|
||||||
|
var g4 = kha.SystemImpl.frame != null ? kha.SystemImpl.frame.g4 : null;
|
||||||
|
|
||||||
|
|
||||||
|
if (g4 != null && iron.Scene.active != null && iron.RenderPath.active != null) {
|
||||||
|
|
||||||
|
if (untyped window._vrUpdateStarted == null) {
|
||||||
|
untyped window._vrUpdateStarted = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
iron.system.Time.update();
|
||||||
|
|
||||||
|
iron.Scene.active.updateFrame();
|
||||||
|
|
||||||
|
|
||||||
|
js.Syntax.code("
|
||||||
|
const App = iron.App;
|
||||||
|
if (App) {
|
||||||
|
const frame = window._vrCallbackCount;
|
||||||
|
const inits = App.traitInits;
|
||||||
|
if (inits && inits.length > 0) {
|
||||||
|
for (let i = 0; i < inits.length; i++) {
|
||||||
|
inits[i]();
|
||||||
|
}
|
||||||
|
inits.length = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const fixedUpdates = App.traitFixedUpdates;
|
||||||
|
if (fixedUpdates) {
|
||||||
|
for (let i = 0; i < fixedUpdates.length; i++) {
|
||||||
|
fixedUpdates[i]();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const updates = App.traitUpdates;
|
||||||
|
if (updates) {
|
||||||
|
for (let i = 0; i < updates.length; i++) {
|
||||||
|
updates[i]();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const lateUpdates = App.traitLateUpdates;
|
||||||
|
if (lateUpdates) {
|
||||||
|
for (let i = 0; i < lateUpdates.length; i++) {
|
||||||
|
lateUpdates[i]();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
");
|
||||||
|
iron.Scene.active.renderFrame(g4);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (untyped window._vrSkipLogged == null) {
|
||||||
|
untyped window._vrSkipLogged = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -157,4 +157,5 @@ extern class Krom {
|
|||||||
static function getConstantLocationCompute(shader: Dynamic, name: String): Dynamic;
|
static function getConstantLocationCompute(shader: Dynamic, name: String): Dynamic;
|
||||||
static function getTextureUnitCompute(shader: Dynamic, name: String): Dynamic;
|
static function getTextureUnitCompute(shader: Dynamic, name: String): Dynamic;
|
||||||
static function compute(x: Int, y: Int, z: Int): Void;
|
static function compute(x: Int, y: Int, z: Int): Void;
|
||||||
|
static function viewportSetCamera(posX: Float, posY: Float, posZ: Float, rotX: Float, rotY: Float, rotZ: Float, rotW: Float): Void;
|
||||||
}
|
}
|
||||||
|
|||||||
BIN
Krom/Krom.exe
BIN
Krom/Krom.exe
Binary file not shown.
@ -2,10 +2,12 @@
|
|||||||
-cp ../Kha/Backends/Krom
|
-cp ../Kha/Backends/Krom
|
||||||
-cp ../leenkx/Sources
|
-cp ../leenkx/Sources
|
||||||
-cp ../iron/Sources
|
-cp ../iron/Sources
|
||||||
|
-cp ../lib/aura/Sources
|
||||||
-cp ../lib/haxebullet/Sources
|
-cp ../lib/haxebullet/Sources
|
||||||
-cp ../lib/haxerecast/Sources
|
-cp ../lib/haxerecast/Sources
|
||||||
-cp ../lib/zui/Sources
|
-cp ../lib/zui/Sources
|
||||||
--macro include('iron', true, null, ['../iron/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('haxebullet', true, null, ['../lib/haxebullet/Sources'])
|
||||||
--macro include('haxerecast', true, null, ['../lib/haxerecast/Sources'])
|
--macro include('haxerecast', true, null, ['../lib/haxerecast/Sources'])
|
||||||
--macro include('leenkx', true, ['leenkx.network'], ['../leenkx/Sources','../iron/Sources'])
|
--macro include('leenkx', true, ['leenkx.network'], ['../leenkx/Sources','../iron/Sources'])
|
||||||
|
|||||||
26
leenkx.py
26
leenkx.py
@ -24,7 +24,7 @@ import textwrap
|
|||||||
import threading
|
import threading
|
||||||
import traceback
|
import traceback
|
||||||
import typing
|
import typing
|
||||||
from typing import Callable, Optional
|
from typing import Callable, Optional, List
|
||||||
import webbrowser
|
import webbrowser
|
||||||
|
|
||||||
import bpy
|
import bpy
|
||||||
@ -33,6 +33,12 @@ from bpy.props import *
|
|||||||
from bpy.types import Operator, AddonPreferences
|
from bpy.types import Operator, AddonPreferences
|
||||||
|
|
||||||
|
|
||||||
|
if bpy.app.version < (2, 90, 0):
|
||||||
|
ListType = List
|
||||||
|
else:
|
||||||
|
ListType = list
|
||||||
|
|
||||||
|
|
||||||
class SDKSource(IntEnum):
|
class SDKSource(IntEnum):
|
||||||
PREFS = 0
|
PREFS = 0
|
||||||
LOCAL = 1
|
LOCAL = 1
|
||||||
@ -73,9 +79,10 @@ def detect_sdk_path():
|
|||||||
area = win.screen.areas[0]
|
area = win.screen.areas[0]
|
||||||
area_type = area.type
|
area_type = area.type
|
||||||
area.type = "INFO"
|
area.type = "INFO"
|
||||||
with bpy.context.temp_override(window=win, screen=win.screen, area=area):
|
if bpy.app.version >= (2, 92, 0):
|
||||||
bpy.ops.info.select_all(action='SELECT')
|
with bpy.context.temp_override(window=win, screen=win.screen, area=area):
|
||||||
bpy.ops.info.report_copy()
|
bpy.ops.info.select_all(action='SELECT')
|
||||||
|
bpy.ops.info.report_copy()
|
||||||
area.type = area_type
|
area.type = area_type
|
||||||
clipboard = bpy.context.window_manager.clipboard
|
clipboard = bpy.context.window_manager.clipboard
|
||||||
|
|
||||||
@ -85,6 +92,7 @@ def detect_sdk_path():
|
|||||||
if match:
|
if match:
|
||||||
addon_prefs.sdk_path = os.path.dirname(match[-1])
|
addon_prefs.sdk_path = os.path.dirname(match[-1])
|
||||||
|
|
||||||
|
|
||||||
def get_link_web_server(self):
|
def get_link_web_server(self):
|
||||||
return self.get('link_web_server', 'http://localhost/')
|
return self.get('link_web_server', 'http://localhost/')
|
||||||
|
|
||||||
@ -558,7 +566,7 @@ def remove_readonly(func, path, excinfo):
|
|||||||
func(path)
|
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):
|
def fn(p, done):
|
||||||
p.wait()
|
p.wait()
|
||||||
if done is not None:
|
if done is not None:
|
||||||
@ -840,7 +848,13 @@ def update_leenkx_py(sdk_path: str, force_relink=False):
|
|||||||
else:
|
else:
|
||||||
raise err
|
raise err
|
||||||
else:
|
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)
|
shutil.copy(Path(sdk_path) / 'leenkx.py', lnx_module_file)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -14,7 +14,7 @@ out vec4 fragColor;
|
|||||||
vec2 barrelDistortion(vec2 coord, float amt) {
|
vec2 barrelDistortion(vec2 coord, float amt) {
|
||||||
vec2 cc = coord - 0.5;
|
vec2 cc = coord - 0.5;
|
||||||
float dist = dot(cc, cc);
|
float dist = dot(cc, cc);
|
||||||
return coord + cc * dist * amt;
|
return coord - cc * dist * amt;
|
||||||
}
|
}
|
||||||
float sat(float value)
|
float sat(float value)
|
||||||
{
|
{
|
||||||
@ -56,8 +56,6 @@ void main() {
|
|||||||
if (CAType == 1) {
|
if (CAType == 1) {
|
||||||
float reci_num_iter_f = 1.0 / float(num_iter);
|
float reci_num_iter_f = 1.0 / float(num_iter);
|
||||||
|
|
||||||
vec2 resolution = vec2(1,1);
|
|
||||||
vec2 uv = (texCoord.xy/resolution.xy);
|
|
||||||
vec4 sumcol = vec4(0.0);
|
vec4 sumcol = vec4(0.0);
|
||||||
vec4 sumw = vec4(0.0);
|
vec4 sumw = vec4(0.0);
|
||||||
for (int i=0; i < num_iter; ++i)
|
for (int i=0; i < num_iter; ++i)
|
||||||
@ -65,19 +63,21 @@ void main() {
|
|||||||
float t = float(i) * reci_num_iter_f;
|
float t = float(i) * reci_num_iter_f;
|
||||||
vec4 w = spectrum_offset(t);
|
vec4 w = spectrum_offset(t);
|
||||||
sumw += w;
|
sumw += w;
|
||||||
sumcol += w * texture(tex, barrelDistortion(uv, 0.6 * max_distort * t));
|
vec2 distortedUV = barrelDistortion(texCoord, 0.6 * max_distort * t);
|
||||||
|
sumcol += w * texture(tex, distortedUV);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (on == 1) fragColor = sumcol / sumw; else fragColor = texture(tex, texCoord);
|
if (on == 1) fragColor = sumcol / sumw; else fragColor = texture(tex, texCoord);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Simple
|
// inward sampling to avoid edge artifacts
|
||||||
else {
|
else {
|
||||||
vec3 col = vec3(0.0);
|
vec3 col = vec3(0.0);
|
||||||
col.x = texture(tex, texCoord + ((vec2(0.0, 1.0) * max_distort) / vec2(1000.0))).x;
|
vec2 toCenter = (vec2(0.5) - texCoord) * max_distort / 500.0;
|
||||||
col.y = texture(tex, texCoord + ((vec2(-0.85, -0.5) * max_distort) / vec2(1000.0))).y;
|
col.x = texture(tex, texCoord + toCenter * 0.0).x;
|
||||||
col.z = texture(tex, texCoord + ((vec2(0.85, -0.5) * max_distort) / vec2(1000.0))).z;
|
col.y = texture(tex, texCoord + toCenter * 0.5).y;
|
||||||
if (on == 1) fragColor = vec4(col.x, col.y, col.z, fragColor.w);
|
col.z = texture(tex, texCoord + toCenter * 1.0).z;
|
||||||
|
if (on == 1) fragColor = vec4(col.x, col.y, col.z, 1.0);
|
||||||
else fragColor = texture(tex, texCoord);
|
else fragColor = texture(tex, texCoord);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -144,7 +144,7 @@ float ConvertEV100ToExposure(float EV100) {
|
|||||||
return 1/0.8 * exp2(-EV100);
|
return 1/0.8 * exp2(-EV100);
|
||||||
}
|
}
|
||||||
float ComputeEV(float avgLuminance) {
|
float ComputeEV(float avgLuminance) {
|
||||||
const float sqAperture = PPComp1[0].x * PPComp1.x;
|
const float sqAperture = PPComp1.x * PPComp1.x;
|
||||||
const float shutterTime = 1.0 / PPComp1.y;
|
const float shutterTime = 1.0 / PPComp1.y;
|
||||||
const float ISO = PPComp1.z;
|
const float ISO = PPComp1.z;
|
||||||
const float EC = PPComp2.x;
|
const float EC = PPComp2.x;
|
||||||
@ -358,6 +358,12 @@ void main() {
|
|||||||
fragColor = textureLod(tex, texCo, 0.0);
|
fragColor = textureLod(tex, texCo, 0.0);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// TODO: re-investigate white artifacts
|
||||||
|
fragColor.rgb = clamp(fragColor.rgb, vec3(0.0), vec3(65504.0));
|
||||||
|
if (any(isnan(fragColor.rgb)) || any(isinf(fragColor.rgb))) {
|
||||||
|
fragColor.rgb = vec3(0.0);
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef _CSharpen
|
#ifdef _CSharpen
|
||||||
|
|||||||
@ -57,10 +57,10 @@ uniform vec3 backgroundCol;
|
|||||||
|
|
||||||
#ifdef _SSAO
|
#ifdef _SSAO
|
||||||
uniform sampler2D ssaotex;
|
uniform sampler2D ssaotex;
|
||||||
#else
|
|
||||||
#ifdef _SSGI
|
|
||||||
uniform sampler2D ssaotex;
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef _SSGI
|
||||||
|
uniform sampler2D ssgitex;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef _SSS
|
#ifdef _SSS
|
||||||
@ -102,8 +102,23 @@ uniform mat4 invVP;
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
uniform vec2 cameraProj;
|
uniform vec2 cameraProj;
|
||||||
|
#ifdef _VRStereo
|
||||||
|
uniform vec3 eye; // center camera position
|
||||||
|
uniform vec3 eyeLook; // center camera look
|
||||||
|
uniform vec3 eyeLeft;
|
||||||
|
uniform vec3 eyeRight;
|
||||||
|
uniform vec3 eyeLookLeft;
|
||||||
|
uniform vec3 eyeLookRight;
|
||||||
|
uniform mat4 invVPLeft;
|
||||||
|
uniform mat4 invVPRight;
|
||||||
|
#ifdef _SinglePoint
|
||||||
|
uniform vec3 pointPosLeft;
|
||||||
|
uniform vec3 pointPosRight;
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
uniform vec3 eye;
|
uniform vec3 eye;
|
||||||
uniform vec3 eyeLook;
|
uniform vec3 eyeLook;
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef _Clusters
|
#ifdef _Clusters
|
||||||
uniform vec4 lightsArray[maxLights * 3];
|
uniform vec4 lightsArray[maxLights * 3];
|
||||||
@ -200,7 +215,9 @@ uniform vec3 sunCol;
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef _SinglePoint // Fast path for single light
|
#ifdef _SinglePoint // Fast path for single light
|
||||||
|
#ifndef _VRStereo
|
||||||
uniform vec3 pointPos;
|
uniform vec3 pointPos;
|
||||||
|
#endif
|
||||||
uniform vec3 pointCol;
|
uniform vec3 pointCol;
|
||||||
#ifdef _ShadowMap
|
#ifdef _ShadowMap
|
||||||
uniform float pointBias;
|
uniform float pointBias;
|
||||||
@ -225,6 +242,8 @@ out vec4 fragColor;
|
|||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
vec4 g0 = textureLod(gbuffer0, texCoord, 0.0); // Normal.xy, roughness, metallic/matid
|
vec4 g0 = textureLod(gbuffer0, texCoord, 0.0); // Normal.xy, roughness, metallic/matid
|
||||||
|
vec4 g1 = textureLod(gbuffer1, texCoord, 0.0); // Basecolor.rgb, spec/occ
|
||||||
|
float depth = textureLod(gbufferD, texCoord, 0.0).r * 2.0 - 1.0;
|
||||||
|
|
||||||
vec3 n;
|
vec3 n;
|
||||||
n.z = 1.0 - abs(g0.x) - abs(g0.y);
|
n.z = 1.0 - abs(g0.x) - abs(g0.y);
|
||||||
@ -236,14 +255,28 @@ void main() {
|
|||||||
uint matid;
|
uint matid;
|
||||||
unpackFloatInt16(g0.a, metallic, matid);
|
unpackFloatInt16(g0.a, metallic, matid);
|
||||||
|
|
||||||
vec4 g1 = textureLod(gbuffer1, texCoord, 0.0); // Basecolor.rgb, spec/occ
|
|
||||||
vec2 occspec = unpackFloat2(g1.a);
|
vec2 occspec = unpackFloat2(g1.a);
|
||||||
vec3 albedo = surfaceAlbedo(g1.rgb, metallic); // g1.rgb - basecolor
|
// re-investigate clamp basecolor to prevent extreme values causing glitches
|
||||||
vec3 f0 = surfaceF0(g1.rgb, metallic);
|
vec3 basecolor = min(g1.rgb, vec3(2.0));
|
||||||
|
vec3 albedo = surfaceAlbedo(basecolor, metallic);
|
||||||
|
vec3 f0 = surfaceF0(basecolor, metallic);
|
||||||
|
|
||||||
float depth = textureLod(gbufferD, texCoord, 0.0).r * 2.0 - 1.0;
|
#ifdef _VRStereo
|
||||||
|
bool isLeftEye = texCoord.x < 0.5;
|
||||||
|
vec3 eyePos = isLeftEye ? eyeLeft : eyeRight;
|
||||||
|
mat4 invVP_eye = isLeftEye ? invVPLeft : invVPRight;
|
||||||
|
|
||||||
|
vec2 eyeTexCoord = vec2(
|
||||||
|
isLeftEye ? texCoord.x * 2.0 : (texCoord.x - 0.5) * 2.0,
|
||||||
|
texCoord.y
|
||||||
|
);
|
||||||
|
|
||||||
|
vec3 p = getPos2(invVP_eye, depth, eyeTexCoord);
|
||||||
|
vec3 v = normalize(eyePos - p);
|
||||||
|
#else
|
||||||
vec3 p = getPos(eye, eyeLook, normalize(viewRay), depth, cameraProj);
|
vec3 p = getPos(eye, eyeLook, normalize(viewRay), depth, cameraProj);
|
||||||
vec3 v = normalize(eye - p);
|
vec3 v = normalize(eye - p);
|
||||||
|
#endif
|
||||||
float dotNV = max(dot(n, v), 0.0);
|
float dotNV = max(dot(n, v), 0.0);
|
||||||
|
|
||||||
#ifdef _gbuffer2
|
#ifdef _gbuffer2
|
||||||
@ -287,6 +320,7 @@ void main() {
|
|||||||
vec3 reflectionWorld = reflect(-v, n);
|
vec3 reflectionWorld = reflect(-v, n);
|
||||||
float lod = getMipFromRoughness(roughness, envmapNumMipmaps);
|
float lod = getMipFromRoughness(roughness, envmapNumMipmaps);
|
||||||
vec3 prefilteredColor = textureLod(senvmapRadiance, envMapEquirect(reflectionWorld), lod).rgb;
|
vec3 prefilteredColor = textureLod(senvmapRadiance, envMapEquirect(reflectionWorld), lod).rgb;
|
||||||
|
prefilteredColor = min(prefilteredColor, vec3(20.0));
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef _EnvLDR
|
#ifdef _EnvLDR
|
||||||
@ -319,7 +353,7 @@ void main() {
|
|||||||
#ifdef _VoxelGI
|
#ifdef _VoxelGI
|
||||||
fragColor.rgb = textureLod(voxels_diffuse, texCoord, 0.0).rgb * voxelgiDiff;
|
fragColor.rgb = textureLod(voxels_diffuse, texCoord, 0.0).rgb * voxelgiDiff;
|
||||||
if(roughness < 1.0 && occspec.y > 0.0)
|
if(roughness < 1.0 && occspec.y > 0.0)
|
||||||
fragColor.rgb += textureLod(voxels_specular, texCoord, 0.0).rgb * F * voxelgiRefl;
|
fragColor.rgb += textureLod(voxels_specular, texCoord, 0.0).rgb * occspec.y * voxelgiRefl;
|
||||||
#else
|
#else
|
||||||
#ifdef _VoxelAOvar
|
#ifdef _VoxelAOvar
|
||||||
fragColor.rgb = textureLod(voxels_ao, texCoord, 0.0).rgb * voxelgiOcc;
|
fragColor.rgb = textureLod(voxels_ao, texCoord, 0.0).rgb * voxelgiOcc;
|
||||||
@ -340,15 +374,12 @@ void main() {
|
|||||||
// fragColor.rgb = texture(ssaotex, texCoord).rrr;
|
// fragColor.rgb = texture(ssaotex, texCoord).rrr;
|
||||||
|
|
||||||
#ifdef _SSAO
|
#ifdef _SSAO
|
||||||
// #ifdef _RTGI
|
|
||||||
// fragColor.rgb *= textureLod(ssaotex, texCoord, 0.0).rgb;
|
|
||||||
// #else
|
|
||||||
fragColor.rgb *= textureLod(ssaotex, texCoord, 0.0).r;
|
fragColor.rgb *= textureLod(ssaotex, texCoord, 0.0).r;
|
||||||
// #endif
|
|
||||||
#else
|
|
||||||
#ifdef _SSGI
|
|
||||||
fragColor.rgb += textureLod(ssaotex, texCoord, 0.0).rgb;
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef _SSGI
|
||||||
|
vec3 ssgiColor = textureLod(ssgitex, texCoord, 0.0).rgb;
|
||||||
|
fragColor.rgb += ssgiColor * albedo;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef _EmissionShadeless
|
#ifdef _EmissionShadeless
|
||||||
@ -381,62 +412,62 @@ void main() {
|
|||||||
#ifdef _ShadowMap
|
#ifdef _ShadowMap
|
||||||
#ifdef _CSM
|
#ifdef _CSM
|
||||||
svisibility = shadowTestCascade(
|
svisibility = shadowTestCascade(
|
||||||
#ifdef _ShadowMapAtlas
|
#ifdef _ShadowMapAtlas
|
||||||
#ifdef _ShadowMapTransparent
|
#ifdef _ShadowMapTransparent
|
||||||
#ifndef _SingleAtlas
|
#ifndef _SingleAtlas
|
||||||
shadowMapAtlasSun, shadowMapAtlasSunTransparent
|
shadowMapAtlasSun, shadowMapAtlasSunTransparent
|
||||||
#else
|
#else
|
||||||
shadowMapAtlas, shadowMapAtlasTransparent
|
shadowMapAtlas, shadowMapAtlasTransparent
|
||||||
#endif
|
#endif
|
||||||
#else
|
#else
|
||||||
#ifndef _SingleAtlas
|
#ifndef _SingleAtlas
|
||||||
shadowMapAtlasSun
|
shadowMapAtlasSun
|
||||||
#else
|
#else
|
||||||
shadowMapAtlas
|
shadowMapAtlas
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
#else
|
#else
|
||||||
#ifdef _ShadowMapTransparent
|
#ifdef _ShadowMapTransparent
|
||||||
shadowMap, shadowMapTransparent
|
shadowMap, shadowMapTransparent
|
||||||
#else
|
#else
|
||||||
shadowMap
|
shadowMap
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
, eye, p + n * shadowsBias * 10, shadowsBias
|
, eye, p + n * shadowsBias * 2, shadowsBias
|
||||||
#ifdef _ShadowMapTransparent
|
#ifdef _ShadowMapTransparent
|
||||||
, false
|
, false
|
||||||
#endif
|
#endif
|
||||||
);
|
);
|
||||||
#else
|
#else
|
||||||
vec4 lPos = LWVP * vec4(p + n * shadowsBias * 100, 1.0);
|
vec4 lPos = LWVP * vec4(p + n * shadowsBias * 2, 1.0);
|
||||||
if (lPos.w > 0.0) {
|
if (lPos.w > 0.0) {
|
||||||
svisibility = shadowTest(
|
svisibility = shadowTest(
|
||||||
#ifdef _ShadowMapAtlas
|
#ifdef _ShadowMapAtlas
|
||||||
#ifdef _ShadowMapTransparent
|
#ifdef _ShadowMapTransparent
|
||||||
#ifndef _SingleAtlas
|
#ifndef _SingleAtlas
|
||||||
shadowMapAtlasSun, shadowMapAtlasSunTransparent
|
shadowMapAtlasSun, shadowMapAtlasSunTransparent
|
||||||
#else
|
#else
|
||||||
shadowMapAtlas, shadowMapAtlasTransparent
|
shadowMapAtlas, shadowMapAtlasTransparent
|
||||||
#endif
|
#endif
|
||||||
#else
|
#else
|
||||||
#ifndef _SingleAtlas
|
#ifndef _SingleAtlas
|
||||||
shadowMapAtlasSun
|
shadowMapAtlasSun
|
||||||
#else
|
#else
|
||||||
shadowMapAtlas
|
shadowMapAtlas
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
#else
|
#else
|
||||||
#ifdef _ShadowMapTransparent
|
#ifdef _ShadowMapTransparent
|
||||||
shadowMap, shadowMapTransparent
|
shadowMap, shadowMapTransparent
|
||||||
#else
|
#else
|
||||||
shadowMap
|
shadowMap
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
, lPos.xyz / lPos.w, shadowsBias
|
, lPos.xyz / lPos.w, shadowsBias
|
||||||
#ifdef _ShadowMapTransparent
|
#ifdef _ShadowMapTransparent
|
||||||
, false
|
, false
|
||||||
#endif
|
#endif
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
@ -498,8 +529,14 @@ void main() {
|
|||||||
|
|
||||||
#ifdef _SinglePoint
|
#ifdef _SinglePoint
|
||||||
|
|
||||||
|
#ifdef _VRStereo
|
||||||
|
vec3 lightPos = pointPosLeft;
|
||||||
|
#else
|
||||||
|
vec3 lightPos = pointPos;
|
||||||
|
#endif
|
||||||
|
|
||||||
fragColor.rgb += sampleLight(
|
fragColor.rgb += sampleLight(
|
||||||
p, n, v, dotNV, pointPos, pointCol, albedo, roughness, occspec.y, f0
|
p, n, v, dotNV, lightPos, pointCol, albedo, roughness, occspec.y, f0
|
||||||
#ifdef _ShadowMap
|
#ifdef _ShadowMap
|
||||||
, 0, pointBias, true
|
, 0, pointBias, true
|
||||||
#ifdef _ShadowMapTransparent
|
#ifdef _ShadowMapTransparent
|
||||||
@ -522,7 +559,9 @@ void main() {
|
|||||||
|
|
||||||
#ifdef _Spot
|
#ifdef _Spot
|
||||||
#ifdef _SSS
|
#ifdef _SSS
|
||||||
if (matid == 2) fragColor.rgb += fragColor.rgb * SSSSTransmittance(LWVPSpot[0], p, n, normalize(pointPos - p), lightPlane.y, shadowMapSpot[0]);//TODO implement transparent shadowmaps into the SSSSTransmittance()
|
#ifdef _ShadowMap
|
||||||
|
if (matid == 2) fragColor.rgb += fragColor.rgb * SSSSTransmittance(LWVPSpot[0], p, n, normalize(lightPos - p), lightPlane.y, shadowMapSpot[0]);//TODO implement transparent shadowmaps into the SSSSTransmittance()
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -582,5 +621,11 @@ void main() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
#endif // _Clusters
|
#endif // _Clusters
|
||||||
|
|
||||||
|
fragColor.rgb = clamp(fragColor.rgb, vec3(0.0), vec3(65504.0));
|
||||||
|
if (any(isnan(fragColor.rgb)) || any(isinf(fragColor.rgb))) {
|
||||||
|
fragColor.rgb = vec3(0.0);
|
||||||
|
}
|
||||||
|
|
||||||
fragColor.a = 1.0; // Mark as opaque
|
fragColor.a = 1.0; // Mark as opaque
|
||||||
}
|
}
|
||||||
|
|||||||
@ -20,6 +20,36 @@
|
|||||||
"name": "eyeLook",
|
"name": "eyeLook",
|
||||||
"link": "_cameraLook"
|
"link": "_cameraLook"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "eyeLeft",
|
||||||
|
"link": "_eyeLeft",
|
||||||
|
"ifdef": ["_VRStereo"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "eyeRight",
|
||||||
|
"link": "_eyeRight",
|
||||||
|
"ifdef": ["_VRStereo"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "eyeLookLeft",
|
||||||
|
"link": "_eyeLookLeft",
|
||||||
|
"ifdef": ["_VRStereo"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "eyeLookRight",
|
||||||
|
"link": "_eyeLookRight",
|
||||||
|
"ifdef": ["_VRStereo"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "invVPLeft",
|
||||||
|
"link": "_inverseViewProjectionMatrixLeft",
|
||||||
|
"ifdef": ["_VRStereo"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "invVPRight",
|
||||||
|
"link": "_inverseViewProjectionMatrixRight",
|
||||||
|
"ifdef": ["_VRStereo"]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "clipmaps",
|
"name": "clipmaps",
|
||||||
"link": "_clipmaps",
|
"link": "_clipmaps",
|
||||||
@ -176,8 +206,19 @@
|
|||||||
{
|
{
|
||||||
"name": "pointPos",
|
"name": "pointPos",
|
||||||
"link": "_pointPosition",
|
"link": "_pointPosition",
|
||||||
|
"ifndef": ["_VRStereo"],
|
||||||
"ifdef": ["_SinglePoint"]
|
"ifdef": ["_SinglePoint"]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "pointPosLeft",
|
||||||
|
"link": "_pointPositionLeft",
|
||||||
|
"ifdef": ["_VRStereo", "_SinglePoint"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "pointPosRight",
|
||||||
|
"link": "_pointPositionRight",
|
||||||
|
"ifdef": ["_VRStereo", "_SinglePoint"]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "pointCol",
|
"name": "pointCol",
|
||||||
"link": "_pointColor",
|
"link": "_pointColor",
|
||||||
|
|||||||
@ -97,6 +97,31 @@
|
|||||||
"link": "_cascadeData",
|
"link": "_cascadeData",
|
||||||
"ifdef": ["_Sun", "_ShadowMap", "_CSM"]
|
"ifdef": ["_Sun", "_ShadowMap", "_CSM"]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "eyeLookRight",
|
||||||
|
"link": "_eyeLookRight",
|
||||||
|
"ifdef": ["_VRStereo"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "invVPLeft",
|
||||||
|
"link": "_inverseViewProjectionMatrixLeft",
|
||||||
|
"ifdef": ["_VRStereo"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "invVPRight",
|
||||||
|
"link": "_inverseViewProjectionMatrixRight",
|
||||||
|
"ifdef": ["_VRStereo"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "invVP",
|
||||||
|
"link": "_viewProjectionMatrix",
|
||||||
|
"ifdef": ["_SSRS"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "smSizeUniform",
|
||||||
|
"link": "_shadowMapSize",
|
||||||
|
"ifdef": ["_SMSizeUniform"]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "lightPlane",
|
"name": "lightPlane",
|
||||||
"link": "_lightPlane",
|
"link": "_lightPlane",
|
||||||
@ -108,8 +133,6 @@
|
|||||||
"ifdef": ["_SSRS"]
|
"ifdef": ["_SSRS"]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "smSizeUniform",
|
|
||||||
"link": "_shadowMapSize",
|
|
||||||
"ifdef": ["_SMSizeUniform"]
|
"ifdef": ["_SMSizeUniform"]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -120,8 +143,19 @@
|
|||||||
{
|
{
|
||||||
"name": "pointPos",
|
"name": "pointPos",
|
||||||
"link": "_pointPosition",
|
"link": "_pointPosition",
|
||||||
|
"ifndef": ["_VRStereo"],
|
||||||
"ifdef": ["_SinglePoint"]
|
"ifdef": ["_SinglePoint"]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "pointPosLeft",
|
||||||
|
"link": "_pointPositionLeft",
|
||||||
|
"ifdef": ["_VRStereo", "_SinglePoint"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "pointPosRight",
|
||||||
|
"link": "_pointPositionRight",
|
||||||
|
"ifdef": ["_VRStereo", "_SinglePoint"]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "pointCol",
|
"name": "pointCol",
|
||||||
"link": "_pointColor",
|
"link": "_pointColor",
|
||||||
|
|||||||
9
leenkx/Shaders/fsr1_easu_pass/LICENSE.txt
Normal file
9
leenkx/Shaders/fsr1_easu_pass/LICENSE.txt
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
https://gpuopen.com/manuals/fidelityfx_sdk/license/
|
||||||
|
|
||||||
|
Copyright © 2024 Advanced Micro Devices, Inc.
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files(the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and /or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions :
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
157
leenkx/Shaders/fsr1_easu_pass/fsr1_easu_pass.frag.glsl
Normal file
157
leenkx/Shaders/fsr1_easu_pass/fsr1_easu_pass.frag.glsl
Normal file
@ -0,0 +1,157 @@
|
|||||||
|
#version 450
|
||||||
|
|
||||||
|
// AMD FidelityFX Super Resolution 1.0.2 - EASU (Edge Adaptive Spatial Upsampling)
|
||||||
|
|
||||||
|
#include "compiled.inc"
|
||||||
|
|
||||||
|
uniform sampler2D tex;
|
||||||
|
uniform vec2 screenSize;
|
||||||
|
|
||||||
|
in vec2 texCoord;
|
||||||
|
out vec4 fragColor;
|
||||||
|
|
||||||
|
// Helper functions from AMD ffx_a.h
|
||||||
|
float APrxLoRcpF1(float a) {
|
||||||
|
return uintBitsToFloat(uint(0x7ef07ebb) - floatBitsToUint(a));
|
||||||
|
}
|
||||||
|
|
||||||
|
float AMax3F1(float x, float y, float z) {
|
||||||
|
return max(x, max(y, z));
|
||||||
|
}
|
||||||
|
|
||||||
|
float AMin3F1(float x, float y, float z) {
|
||||||
|
return min(x, min(y, z));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attempt to use textureGather for efficiency when available
|
||||||
|
#if __VERSION__ >= 400
|
||||||
|
|
||||||
|
void FsrEasuTap(
|
||||||
|
inout vec3 aC,
|
||||||
|
inout float aW,
|
||||||
|
vec2 off,
|
||||||
|
vec2 dir,
|
||||||
|
vec2 len,
|
||||||
|
float lob,
|
||||||
|
float clp,
|
||||||
|
vec3 c
|
||||||
|
) {
|
||||||
|
vec2 v = off * dir;
|
||||||
|
float d2 = v.x + v.y;
|
||||||
|
d2 = clamp(d2 * APrxLoRcpF1(max(abs(v.x), abs(v.y))), 0.0, 1.0);
|
||||||
|
d2 = d2 * d2;
|
||||||
|
d2 = d2 * len.x + len.y;
|
||||||
|
float wB = 2.0 / 5.0 * d2 - 1.0;
|
||||||
|
float wA = lob * d2 - 1.0;
|
||||||
|
wB *= wB;
|
||||||
|
wA *= wA;
|
||||||
|
float w = 25.0 / 16.0 * wA * wB;
|
||||||
|
w = min(w, clp);
|
||||||
|
w = max(w, 0.0);
|
||||||
|
aC += c * w;
|
||||||
|
aW += w;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3 FsrEasuF(vec2 ip) {
|
||||||
|
vec2 inputSize = textureSize(tex, 0);
|
||||||
|
vec2 inputRcp = 1.0 / inputSize;
|
||||||
|
|
||||||
|
// Position in input pixels
|
||||||
|
vec2 pp = ip * inputSize - 0.5;
|
||||||
|
vec2 fp = floor(pp);
|
||||||
|
pp -= fp;
|
||||||
|
|
||||||
|
// 12-tap kernel
|
||||||
|
// b c
|
||||||
|
// e f g h
|
||||||
|
// i j k l
|
||||||
|
// n o
|
||||||
|
ivec2 sp = ivec2(fp);
|
||||||
|
|
||||||
|
vec3 b = texelFetch(tex, sp + ivec2(0, -1), 0).rgb;
|
||||||
|
vec3 c = texelFetch(tex, sp + ivec2(1, -1), 0).rgb;
|
||||||
|
vec3 e = texelFetch(tex, sp + ivec2(-1, 0), 0).rgb;
|
||||||
|
vec3 f = texelFetch(tex, sp + ivec2(0, 0), 0).rgb;
|
||||||
|
vec3 g = texelFetch(tex, sp + ivec2(1, 0), 0).rgb;
|
||||||
|
vec3 h = texelFetch(tex, sp + ivec2(2, 0), 0).rgb;
|
||||||
|
vec3 i = texelFetch(tex, sp + ivec2(-1, 1), 0).rgb;
|
||||||
|
vec3 j = texelFetch(tex, sp + ivec2(0, 1), 0).rgb;
|
||||||
|
vec3 k = texelFetch(tex, sp + ivec2(1, 1), 0).rgb;
|
||||||
|
vec3 l = texelFetch(tex, sp + ivec2(2, 1), 0).rgb;
|
||||||
|
vec3 n = texelFetch(tex, sp + ivec2(0, 2), 0).rgb;
|
||||||
|
vec3 o = texelFetch(tex, sp + ivec2(1, 2), 0).rgb;
|
||||||
|
|
||||||
|
// Luma for edge detection (using green channel approximation)
|
||||||
|
float bL = b.g + 0.5 * (b.r + b.b);
|
||||||
|
float cL = c.g + 0.5 * (c.r + c.b);
|
||||||
|
float eL = e.g + 0.5 * (e.r + e.b);
|
||||||
|
float fL = f.g + 0.5 * (f.r + f.b);
|
||||||
|
float gL = g.g + 0.5 * (g.r + g.b);
|
||||||
|
float hL = h.g + 0.5 * (h.r + h.b);
|
||||||
|
float iL = i.g + 0.5 * (i.r + i.b);
|
||||||
|
float jL = j.g + 0.5 * (j.r + j.b);
|
||||||
|
float kL = k.g + 0.5 * (k.r + k.b);
|
||||||
|
float lL = l.g + 0.5 * (l.r + l.b);
|
||||||
|
float nL = n.g + 0.5 * (n.r + n.b);
|
||||||
|
float oL = o.g + 0.5 * (o.r + o.b);
|
||||||
|
|
||||||
|
// Gradient detection
|
||||||
|
float dirX = (cL - bL) + (gL - fL) + (kL - jL) + (oL - nL);
|
||||||
|
float dirY = (eL - iL) + (fL - jL) + (gL - kL) + (hL - lL);
|
||||||
|
|
||||||
|
// Normalize direction
|
||||||
|
float dirR = APrxLoRcpF1(max(abs(dirX), abs(dirY)));
|
||||||
|
dirX *= dirR;
|
||||||
|
dirY *= dirR;
|
||||||
|
|
||||||
|
// Calculate stretch based on edge direction
|
||||||
|
float len = length(vec2(dirX, dirY));
|
||||||
|
len = len * 0.5;
|
||||||
|
len *= len;
|
||||||
|
float stretch = (dirX * dirX + dirY * dirY) * APrxLoRcpF1(max(abs(dirX), abs(dirY)));
|
||||||
|
vec2 len2 = vec2(1.0 + (stretch - 1.0) * len, 1.0 - 0.5 * len);
|
||||||
|
float lob = 0.5 + (0.25 - 0.04 - 0.5) * len;
|
||||||
|
float clp = APrxLoRcpF1(lob);
|
||||||
|
|
||||||
|
// Accumulate samples
|
||||||
|
vec3 aC = vec3(0.0);
|
||||||
|
float aW = 0.0;
|
||||||
|
vec2 dir = vec2(dirX, dirY);
|
||||||
|
|
||||||
|
FsrEasuTap(aC, aW, vec2(0.0, -1.0) - pp, dir, len2, lob, clp, b);
|
||||||
|
FsrEasuTap(aC, aW, vec2(1.0, -1.0) - pp, dir, len2, lob, clp, c);
|
||||||
|
FsrEasuTap(aC, aW, vec2(-1.0, 0.0) - pp, dir, len2, lob, clp, e);
|
||||||
|
FsrEasuTap(aC, aW, vec2(0.0, 0.0) - pp, dir, len2, lob, clp, f);
|
||||||
|
FsrEasuTap(aC, aW, vec2(1.0, 0.0) - pp, dir, len2, lob, clp, g);
|
||||||
|
FsrEasuTap(aC, aW, vec2(2.0, 0.0) - pp, dir, len2, lob, clp, h);
|
||||||
|
FsrEasuTap(aC, aW, vec2(-1.0, 1.0) - pp, dir, len2, lob, clp, i);
|
||||||
|
FsrEasuTap(aC, aW, vec2(0.0, 1.0) - pp, dir, len2, lob, clp, j);
|
||||||
|
FsrEasuTap(aC, aW, vec2(1.0, 1.0) - pp, dir, len2, lob, clp, k);
|
||||||
|
FsrEasuTap(aC, aW, vec2(2.0, 1.0) - pp, dir, len2, lob, clp, l);
|
||||||
|
FsrEasuTap(aC, aW, vec2(0.0, 2.0) - pp, dir, len2, lob, clp, n);
|
||||||
|
FsrEasuTap(aC, aW, vec2(1.0, 2.0) - pp, dir, len2, lob, clp, o);
|
||||||
|
|
||||||
|
// Normalize
|
||||||
|
vec3 pix = aC / aW;
|
||||||
|
|
||||||
|
// Clamp to neighborhood min/max to prevent ringing
|
||||||
|
vec3 mn = min(min(min(f, g), j), k);
|
||||||
|
vec3 mx = max(max(max(f, g), j), k);
|
||||||
|
pix = clamp(pix, mn, mx);
|
||||||
|
|
||||||
|
return pix;
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
// Fallback for older GLSL - simple bilinear
|
||||||
|
vec3 FsrEasuF(vec2 ip) {
|
||||||
|
return texture(tex, ip).rgb;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
vec3 col = FsrEasuF(texCoord);
|
||||||
|
fragColor = vec4(col, 1.0);
|
||||||
|
}
|
||||||
19
leenkx/Shaders/fsr1_easu_pass/fsr1_easu_pass.json
Normal file
19
leenkx/Shaders/fsr1_easu_pass/fsr1_easu_pass.json
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"contexts": [
|
||||||
|
{
|
||||||
|
"name": "fsr1_easu_pass",
|
||||||
|
"depth_write": false,
|
||||||
|
"compare_mode": "always",
|
||||||
|
"cull_mode": "none",
|
||||||
|
"links": [
|
||||||
|
{
|
||||||
|
"name": "screenSize",
|
||||||
|
"link": "_screenSize"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"texture_params": [],
|
||||||
|
"vertex_shader": "../include/pass.vert.glsl",
|
||||||
|
"fragment_shader": "fsr1_easu_pass.frag.glsl"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
9
leenkx/Shaders/fsr1_rcas_pass/LICENSE.txt
Normal file
9
leenkx/Shaders/fsr1_rcas_pass/LICENSE.txt
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
https://gpuopen.com/manuals/fidelityfx_sdk/license/
|
||||||
|
|
||||||
|
Copyright © 2024 Advanced Micro Devices, Inc.
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files(the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and /or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions :
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
116
leenkx/Shaders/fsr1_rcas_pass/fsr1_rcas_pass.frag.glsl
Normal file
116
leenkx/Shaders/fsr1_rcas_pass/fsr1_rcas_pass.frag.glsl
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
#version 450
|
||||||
|
|
||||||
|
// AMD FidelityFX Super Resolution 1.0.2 - RCAS (Robust Contrast Adaptive Sharpening)
|
||||||
|
|
||||||
|
#include "compiled.inc"
|
||||||
|
|
||||||
|
uniform sampler2D tex;
|
||||||
|
|
||||||
|
// Sharpness in "stops": 0.0 = maximum sharpness, higher = less sharp
|
||||||
|
// Converted to linear via exp2(-sharpness)
|
||||||
|
#ifdef _FSR1_Ultra_Quality
|
||||||
|
const float SHARPNESS_STOPS = 0.0;
|
||||||
|
#elif defined(_FSR1_Balanced)
|
||||||
|
const float SHARPNESS_STOPS = 1.0;
|
||||||
|
#elif defined(_FSR1_Performance)
|
||||||
|
const float SHARPNESS_STOPS = 2.0;
|
||||||
|
#elif defined(_FSR1_Custom)
|
||||||
|
uniform vec4 PPComp15;
|
||||||
|
#define SHARPNESS_STOPS (PPComp15.x * 2.0)
|
||||||
|
#else
|
||||||
|
const float SHARPNESS_STOPS = 0.5; // Quality (default)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// FSR RCAS limit - prevents unnatural sharpening artifacts
|
||||||
|
#define FSR_RCAS_LIMIT (0.25 - (1.0 / 16.0))
|
||||||
|
|
||||||
|
in vec2 texCoord;
|
||||||
|
out vec4 fragColor;
|
||||||
|
|
||||||
|
// AMD helper functions from ffx_a.h
|
||||||
|
float AMin3F1(float x, float y, float z) { return min(x, min(y, z)); }
|
||||||
|
float AMax3F1(float x, float y, float z) { return max(x, max(y, z)); }
|
||||||
|
|
||||||
|
// High precision reciprocal (required for limiters per AMD docs)
|
||||||
|
// Added epsilon to prevent division by zero in dark areas
|
||||||
|
float ARcpF1(float a) {
|
||||||
|
return 1.0 / max(a, 1e-8);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Medium precision reciprocal approximation (from AMD ffx_a.h)
|
||||||
|
// Only used for noise detection and final resolve
|
||||||
|
float APrxMedRcpF1(float a) {
|
||||||
|
return uintBitsToFloat(uint(0x7ef19fff) - floatBitsToUint(a));
|
||||||
|
}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
// Get texture size and texel offset
|
||||||
|
vec2 texSize = vec2(textureSize(tex, 0));
|
||||||
|
vec2 texelSize = 1.0 / texSize;
|
||||||
|
|
||||||
|
// Algorithm uses minimal 3x3 pixel neighborhood
|
||||||
|
// b
|
||||||
|
// d e f
|
||||||
|
// h
|
||||||
|
// Clamp inputs to [0,1] - FSR expects sRGB normalized input
|
||||||
|
vec3 b = clamp(texture(tex, texCoord + vec2(0.0, -texelSize.y)).rgb, 0.0, 1.0);
|
||||||
|
vec3 d = clamp(texture(tex, texCoord + vec2(-texelSize.x, 0.0)).rgb, 0.0, 1.0);
|
||||||
|
vec4 ee = texture(tex, texCoord);
|
||||||
|
vec3 e = clamp(ee.rgb, 0.0, 1.0);
|
||||||
|
vec3 f = clamp(texture(tex, texCoord + vec2(texelSize.x, 0.0)).rgb, 0.0, 1.0);
|
||||||
|
vec3 h = clamp(texture(tex, texCoord + vec2(0.0, texelSize.y)).rgb, 0.0, 1.0);
|
||||||
|
|
||||||
|
// Luma times 2 (AMD's luma calculation: B*0.5 + R*0.5 + G)
|
||||||
|
float bL = b.b * 0.5 + (b.r * 0.5 + b.g);
|
||||||
|
float dL = d.b * 0.5 + (d.r * 0.5 + d.g);
|
||||||
|
float eL = e.b * 0.5 + (e.r * 0.5 + e.g);
|
||||||
|
float fL = f.b * 0.5 + (f.r * 0.5 + f.g);
|
||||||
|
float hL = h.b * 0.5 + (h.r * 0.5 + h.g);
|
||||||
|
|
||||||
|
// Noise detection (official AMD algorithm with safety for flat areas)
|
||||||
|
float nz = 0.25 * bL + 0.25 * dL + 0.25 * fL + 0.25 * hL - eL;
|
||||||
|
float range = AMax3F1(AMax3F1(bL, dL, eL), fL, hL) - AMin3F1(AMin3F1(bL, dL, eL), fL, hL);
|
||||||
|
// Use safe division instead of APrxMedRcpF1 for range to avoid NaN in flat areas
|
||||||
|
nz = clamp(abs(nz) / max(range, 1e-5), 0.0, 1.0);
|
||||||
|
nz = -0.5 * nz + 1.0;
|
||||||
|
|
||||||
|
// Min and max of ring (per channel)
|
||||||
|
float mn4R = min(AMin3F1(b.r, d.r, f.r), h.r);
|
||||||
|
float mn4G = min(AMin3F1(b.g, d.g, f.g), h.g);
|
||||||
|
float mn4B = min(AMin3F1(b.b, d.b, f.b), h.b);
|
||||||
|
float mx4R = max(AMax3F1(b.r, d.r, f.r), h.r);
|
||||||
|
float mx4G = max(AMax3F1(b.g, d.g, f.g), h.g);
|
||||||
|
float mx4B = max(AMax3F1(b.b, d.b, f.b), h.b);
|
||||||
|
|
||||||
|
// Immediate constants for peak range
|
||||||
|
vec2 peakC = vec2(1.0, -4.0);
|
||||||
|
|
||||||
|
// Limiters - these need HIGH PRECISION reciprocals (per AMD docs)
|
||||||
|
float hitMinR = min(mn4R, e.r) * ARcpF1(4.0 * mx4R);
|
||||||
|
float hitMinG = min(mn4G, e.g) * ARcpF1(4.0 * mx4G);
|
||||||
|
float hitMinB = min(mn4B, e.b) * ARcpF1(4.0 * mx4B);
|
||||||
|
float hitMaxR = (peakC.x - max(mx4R, e.r)) * ARcpF1(4.0 * mn4R + peakC.y);
|
||||||
|
float hitMaxG = (peakC.x - max(mx4G, e.g)) * ARcpF1(4.0 * mn4G + peakC.y);
|
||||||
|
float hitMaxB = (peakC.x - max(mx4B, e.b)) * ARcpF1(4.0 * mn4B + peakC.y);
|
||||||
|
float lobeR = max(-hitMinR, hitMaxR);
|
||||||
|
float lobeG = max(-hitMinG, hitMaxG);
|
||||||
|
float lobeB = max(-hitMinB, hitMaxB);
|
||||||
|
|
||||||
|
// Apply sharpness (convert from stops to linear)
|
||||||
|
float sharpness = exp2(-SHARPNESS_STOPS);
|
||||||
|
float lobe = max(-FSR_RCAS_LIMIT, min(AMax3F1(lobeR, lobeG, lobeB), 0.0)) * sharpness;
|
||||||
|
|
||||||
|
// Apply noise removal
|
||||||
|
lobe *= nz;
|
||||||
|
|
||||||
|
// Resolve using safe reciprocal to avoid any edge case issues
|
||||||
|
float denom = 4.0 * lobe + 1.0;
|
||||||
|
float rcpL = 1.0 / max(denom, 0.25); // denom should be in [0.25, 1.0] range
|
||||||
|
vec3 pix;
|
||||||
|
pix.r = (lobe * b.r + lobe * d.r + lobe * h.r + lobe * f.r + e.r) * rcpL;
|
||||||
|
pix.g = (lobe * b.g + lobe * d.g + lobe * h.g + lobe * f.g + e.g) * rcpL;
|
||||||
|
pix.b = (lobe * b.b + lobe * d.b + lobe * h.b + lobe * f.b + e.b) * rcpL;
|
||||||
|
|
||||||
|
// Ensure output is clamped to valid range
|
||||||
|
fragColor = vec4(clamp(pix, 0.0, 1.0), ee.a);
|
||||||
|
}
|
||||||
24
leenkx/Shaders/fsr1_rcas_pass/fsr1_rcas_pass.json
Normal file
24
leenkx/Shaders/fsr1_rcas_pass/fsr1_rcas_pass.json
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
{
|
||||||
|
"contexts": [
|
||||||
|
{
|
||||||
|
"name": "fsr1_rcas_pass",
|
||||||
|
"depth_write": false,
|
||||||
|
"compare_mode": "always",
|
||||||
|
"cull_mode": "none",
|
||||||
|
"links": [
|
||||||
|
{
|
||||||
|
"name": "screenSize",
|
||||||
|
"link": "_screenSize"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "PPComp15",
|
||||||
|
"link": "_PPComp15",
|
||||||
|
"ifdef": ["_FSR1_Custom"]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"texture_params": [],
|
||||||
|
"vertex_shader": "../include/pass.vert.glsl",
|
||||||
|
"fragment_shader": "fsr1_rcas_pass.frag.glsl"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@ -8,6 +8,7 @@ uniform sampler2D gbufferD;
|
|||||||
uniform sampler2D gbuffer0;
|
uniform sampler2D gbuffer0;
|
||||||
uniform sampler2D gbuffer1;
|
uniform sampler2D gbuffer1;
|
||||||
uniform mat4 invVP;
|
uniform mat4 invVP;
|
||||||
|
uniform mat4 invW;
|
||||||
uniform vec3 probep;
|
uniform vec3 probep;
|
||||||
uniform vec3 eye;
|
uniform vec3 eye;
|
||||||
|
|
||||||
@ -25,19 +26,27 @@ void main() {
|
|||||||
|
|
||||||
float roughness = g0.b;
|
float roughness = g0.b;
|
||||||
if (roughness > 0.95) {
|
if (roughness > 0.95) {
|
||||||
fragColor.rgb = vec3(0.0);
|
fragColor = vec4(0.0);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
float spec = fract(textureLod(gbuffer1, texCoord, 0.0).a);
|
float spec = fract(textureLod(gbuffer1, texCoord, 0.0).a);
|
||||||
if (spec == 0.0) {
|
if (spec == 0.0) {
|
||||||
fragColor.rgb = vec3(0.0);
|
fragColor = vec4(0.0);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
float depth = textureLod(gbufferD, texCoord, 0.0).r * 2.0 - 1.0;
|
float depth = textureLod(gbufferD, texCoord, 0.0).r * 2.0 - 1.0;
|
||||||
vec3 wp = getPos2(invVP, depth, texCoord);
|
vec3 wp = getPos2(invVP, depth, texCoord);
|
||||||
|
|
||||||
|
vec3 localPos = (invW * vec4(wp, 1.0)).xyz;
|
||||||
|
|
||||||
|
// return if surface is inside probe volume bounds
|
||||||
|
if (abs(localPos.x) > 1.0 || abs(localPos.y) > 1.0 || abs(localPos.z) > 1.0) {
|
||||||
|
fragColor = vec4(0.0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
vec2 enc = g0.rg;
|
vec2 enc = g0.rg;
|
||||||
vec3 n;
|
vec3 n;
|
||||||
n.z = 1.0 - abs(enc.x) - abs(enc.y);
|
n.z = 1.0 - abs(enc.x) - abs(enc.y);
|
||||||
@ -50,5 +59,5 @@ void main() {
|
|||||||
r.y = -r.y;
|
r.y = -r.y;
|
||||||
#endif
|
#endif
|
||||||
float intensity = clamp((1.0 - roughness) * dot(wp - probep, n), 0.0, 1.0);
|
float intensity = clamp((1.0 - roughness) * dot(wp - probep, n), 0.0, 1.0);
|
||||||
fragColor.rgb = texture(probeTex, r).rgb * intensity;
|
fragColor = vec4(texture(probeTex, r).rgb * intensity, 1.0);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -20,6 +20,10 @@
|
|||||||
"name": "invVP",
|
"name": "invVP",
|
||||||
"link": "_inverseViewProjectionMatrix"
|
"link": "_inverseViewProjectionMatrix"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "invW",
|
||||||
|
"link": "_inverseWorldMatrix"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "probep",
|
"name": "probep",
|
||||||
"link": "_probePosition"
|
"link": "_probePosition"
|
||||||
|
|||||||
@ -25,13 +25,13 @@ void main() {
|
|||||||
|
|
||||||
float roughness = g0.b;
|
float roughness = g0.b;
|
||||||
if (roughness > 0.95) {
|
if (roughness > 0.95) {
|
||||||
fragColor.rgb = vec3(0.0);
|
fragColor = vec4(0.0);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
float spec = fract(textureLod(gbuffer1, texCoord, 0.0).a);
|
float spec = fract(textureLod(gbuffer1, texCoord, 0.0).a);
|
||||||
if (spec == 0.0) {
|
if (spec == 0.0) {
|
||||||
fragColor.rgb = vec3(0.0);
|
fragColor = vec4(0.0);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -50,5 +50,5 @@ void main() {
|
|||||||
n = normalize(n);
|
n = normalize(n);
|
||||||
|
|
||||||
float intensity = clamp((1.0 - roughness) * dot(n, proben), 0.0, 1.0);
|
float intensity = clamp((1.0 - roughness) * dot(n, proben), 0.0, 1.0);
|
||||||
fragColor.rgb = texture(probeTex, tc).rgb * intensity;
|
fragColor = vec4(texture(probeTex, tc).rgb * intensity, 1.0);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,42 +5,56 @@
|
|||||||
|
|
||||||
uniform sampler2D tex;
|
uniform sampler2D tex;
|
||||||
uniform sampler2D gbuffer0;
|
uniform sampler2D gbuffer0;
|
||||||
|
uniform sampler2D gbufferD;
|
||||||
|
|
||||||
uniform vec2 dirInv; // texStep
|
uniform vec2 dirInv; // texStep
|
||||||
|
|
||||||
in vec2 texCoord;
|
in vec2 texCoord;
|
||||||
out float fragColor;
|
out vec3 fragColor;
|
||||||
|
|
||||||
const float blurWeights[5] = float[] (0.227027, 0.1945946, 0.1216216, 0.054054, 0.016216);
|
const int KERNEL_SIZE = 13;
|
||||||
// const float blurWeights[10] = float[] (0.132572, 0.125472, 0.106373, 0.08078, 0.05495, 0.033482, 0.018275, 0.008934, 0.003912, 0.001535);
|
const float blurWeights[13] = float[](0.1, 0.09, 0.08, 0.07, 0.06, 0.05, 0.04, 0.03, 0.025, 0.02, 0.015, 0.01, 0.005);
|
||||||
const float discardThreshold = 0.95;
|
|
||||||
|
|
||||||
float doBlur(const float blurWeight, const int pos, const vec3 nor, const vec2 texCoord) {
|
|
||||||
const float posadd = pos + 0.5;
|
|
||||||
|
|
||||||
vec3 nor2 = getNor(textureLod(gbuffer0, texCoord + pos * dirInv, 0.0).rg);
|
|
||||||
float influenceFactor = step(discardThreshold, dot(nor2, nor));
|
|
||||||
float col = textureLod(tex, texCoord + posadd * dirInv, 0.0).r;
|
|
||||||
fragColor += col * blurWeight * influenceFactor;
|
|
||||||
float weight = blurWeight * influenceFactor;
|
|
||||||
|
|
||||||
nor2 = getNor(textureLod(gbuffer0, texCoord - pos * dirInv, 0.0).rg);
|
|
||||||
influenceFactor = step(discardThreshold, dot(nor2, nor));
|
|
||||||
col = textureLod(tex, texCoord - posadd * dirInv, 0.0).r;
|
|
||||||
fragColor += col * blurWeight * influenceFactor;
|
|
||||||
weight += blurWeight * influenceFactor;
|
|
||||||
|
|
||||||
return weight;
|
|
||||||
}
|
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
vec3 nor = getNor(textureLod(gbuffer0, texCoord, 0.0).rg);
|
vec3 centerNor = getNor(textureLod(gbuffer0, texCoord, 0.0).rg);
|
||||||
|
float centerDepth = textureLod(gbufferD, texCoord, 0.0).r;
|
||||||
|
|
||||||
fragColor = textureLod(tex, texCoord, 0.0).r * blurWeights[0];
|
// skip sky pixels
|
||||||
float weight = blurWeights[0];
|
if (centerDepth == 1.0) {
|
||||||
for (int i = 1; i < 5; i++) {
|
fragColor = vec3(0.0);
|
||||||
weight += doBlur(blurWeights[i], i, nor, texCoord);
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
fragColor = fragColor / weight;
|
fragColor = textureLod(tex, texCoord, 0.0).rgb * blurWeights[0];
|
||||||
|
float totalWeight = blurWeights[0];
|
||||||
|
|
||||||
|
for (int i = 1; i < KERNEL_SIZE; i++) {
|
||||||
|
vec2 offset = float(i) * dirInv;
|
||||||
|
|
||||||
|
vec2 uvPos = texCoord + offset;
|
||||||
|
vec3 norPos = getNor(textureLod(gbuffer0, uvPos, 0.0).rg);
|
||||||
|
float depthPos = textureLod(gbufferD, uvPos, 0.0).r;
|
||||||
|
|
||||||
|
float normalWeight = max(0.0, dot(norPos, centerNor));
|
||||||
|
normalWeight = pow(normalWeight, 8.0); // Softer normal falloff for better blending
|
||||||
|
float depthWeight = 1.0 - smoothstep(0.0, 0.02, abs(depthPos - centerDepth));
|
||||||
|
float w = blurWeights[i] * normalWeight * depthWeight;
|
||||||
|
|
||||||
|
fragColor += textureLod(tex, uvPos, 0.0).rgb * w;
|
||||||
|
totalWeight += w;
|
||||||
|
|
||||||
|
vec2 uvNeg = texCoord - offset;
|
||||||
|
vec3 norNeg = getNor(textureLod(gbuffer0, uvNeg, 0.0).rg);
|
||||||
|
float depthNeg = textureLod(gbufferD, uvNeg, 0.0).r;
|
||||||
|
|
||||||
|
normalWeight = max(0.0, dot(norNeg, centerNor));
|
||||||
|
normalWeight = pow(normalWeight, 8.0);
|
||||||
|
depthWeight = 1.0 - smoothstep(0.0, 0.02, abs(depthNeg - centerDepth));
|
||||||
|
w = blurWeights[i] * normalWeight * depthWeight;
|
||||||
|
|
||||||
|
fragColor += textureLod(tex, uvNeg, 0.0).rgb * w;
|
||||||
|
totalWeight += w;
|
||||||
|
}
|
||||||
|
|
||||||
|
fragColor /= totalWeight;
|
||||||
}
|
}
|
||||||
@ -1,25 +1,8 @@
|
|||||||
#version 450
|
#version 450
|
||||||
|
|
||||||
#include "compiled.inc"
|
#include "compiled.inc"
|
||||||
#include "std/gbuffer.glsl"
|
|
||||||
#include "std/brdf.glsl"
|
|
||||||
#include "std/math.glsl"
|
#include "std/math.glsl"
|
||||||
#ifdef _Clusters
|
#include "std/gbuffer.glsl"
|
||||||
#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"
|
|
||||||
|
|
||||||
uniform sampler2D gbuffer0;
|
uniform sampler2D gbuffer0;
|
||||||
uniform sampler2D gbuffer1;
|
uniform sampler2D gbuffer1;
|
||||||
@ -27,480 +10,179 @@ uniform sampler2D gbufferD;
|
|||||||
#ifdef _EmissionShaded
|
#ifdef _EmissionShaded
|
||||||
uniform sampler2D gbufferEmission;
|
uniform sampler2D gbufferEmission;
|
||||||
#endif
|
#endif
|
||||||
uniform sampler2D sveloc;
|
|
||||||
uniform vec2 cameraProj;
|
|
||||||
uniform vec3 eye;
|
|
||||||
uniform vec3 eyeLook;
|
|
||||||
uniform vec2 screenSize;
|
|
||||||
uniform mat4 invVP;
|
|
||||||
|
|
||||||
in vec2 texCoord;
|
uniform mat4 P;
|
||||||
in vec3 viewRay;
|
uniform mat4 invP;
|
||||||
out vec3 fragColor;
|
uniform mat3 V3;
|
||||||
|
|
||||||
float metallic;
|
|
||||||
uint matid;
|
|
||||||
|
|
||||||
#ifdef _SMSizeUniform
|
|
||||||
//!uniform vec2 smSizeUniform;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef _Clusters
|
|
||||||
uniform vec4 lightsArray[maxLights * 3];
|
|
||||||
#ifdef _Spot
|
|
||||||
uniform vec4 lightsArraySpot[maxLights * 2];
|
|
||||||
#endif
|
|
||||||
uniform sampler2D clustersData;
|
|
||||||
uniform vec2 cameraPlane;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#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
|
#ifdef _Sun
|
||||||
uniform vec3 sunDir;
|
uniform vec3 sunDir;
|
||||||
uniform vec3 sunCol;
|
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
|
#endif
|
||||||
|
|
||||||
vec3 sampleLight(const vec3 p, const vec3 n, const vec3 lp, const vec3 lightCol
|
#ifdef _CPostprocess
|
||||||
#ifdef _ShadowMap
|
uniform vec3 PPComp12;
|
||||||
, 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
|
|
||||||
}
|
|
||||||
#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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
#endif
|
||||||
|
|
||||||
#ifdef _SinglePoint
|
in vec2 texCoord;
|
||||||
visibility += sampleLight(
|
out vec4 fragColor;
|
||||||
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 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 GOLDEN_ANGLE = 2.39996323;
|
||||||
const float MAX_DEPTH_DIFFERENCE = 0.9; // More conservative depth threshold
|
const int RAY_STEPS = 12;
|
||||||
const float SAMPLE_BIAS = 0.01; // Small offset to avoid self-occlusion
|
|
||||||
|
vec2 getProjectedCoord(const vec3 viewPos) {
|
||||||
|
vec4 projectedCoord = P * vec4(viewPos, 1.0);
|
||||||
|
projectedCoord.xy /= projectedCoord.w;
|
||||||
|
projectedCoord.xy = projectedCoord.xy * 0.5 + 0.5;
|
||||||
|
#ifdef _InvY
|
||||||
|
projectedCoord.y = 1.0 - projectedCoord.y;
|
||||||
|
#endif
|
||||||
|
return projectedCoord.xy;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3 cosineSampleHemisphere(vec3 n, vec2 rand) {
|
||||||
|
float phi = PI * 2.0 * rand.x;
|
||||||
|
float cosTheta = sqrt(1.0 - rand.y);
|
||||||
|
float sinTheta = sqrt(rand.y);
|
||||||
|
|
||||||
|
vec3 h = vec3(cos(phi) * sinTheta, sin(phi) * sinTheta, cosTheta);
|
||||||
|
|
||||||
|
vec3 tangent, bitangent;
|
||||||
|
vec3 absN = abs(n);
|
||||||
|
|
||||||
|
if (absN.x <= absN.y && absN.x <= absN.z) {
|
||||||
|
tangent = normalize(cross(n, vec3(1.0, 0.0, 0.0)));
|
||||||
|
} else if (absN.y <= absN.z) {
|
||||||
|
tangent = normalize(cross(n, vec3(0.0, 1.0, 0.0)));
|
||||||
|
} else {
|
||||||
|
tangent = normalize(cross(n, vec3(0.0, 0.0, 1.0)));
|
||||||
|
}
|
||||||
|
bitangent = cross(n, tangent);
|
||||||
|
|
||||||
|
return normalize(tangent * h.x + bitangent * h.y + n * h.z);
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3 traceRay(vec3 origin, vec3 dir, float maxDist, float minDist) {
|
||||||
|
float stepSize = maxDist / float(RAY_STEPS);
|
||||||
|
vec3 pos = origin + dir * minDist;
|
||||||
|
|
||||||
|
float prevDepthDiff = 0.0;
|
||||||
|
float hadValidPrev = 0.0;
|
||||||
|
|
||||||
|
for (int i = 1; i <= RAY_STEPS; i++) {
|
||||||
|
pos += dir * stepSize;
|
||||||
|
vec2 uv = getProjectedCoord(pos);
|
||||||
|
|
||||||
|
vec2 sampleUV = clamp(uv, vec2(0.001), vec2(0.999));
|
||||||
|
|
||||||
|
float sampleDepth = textureLod(gbufferD, sampleUV, 0.0).r * 2.0 - 1.0;
|
||||||
|
if (sampleDepth == 1.0) {
|
||||||
|
hadValidPrev = 0.0;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3 sampleViewPos = getPosView2(invP, sampleDepth, sampleUV);
|
||||||
|
float depthDiff = pos.z - sampleViewPos.z;
|
||||||
|
float rayDist = length(pos - origin);
|
||||||
|
float thickness = 0.15 + rayDist * 0.25;
|
||||||
|
|
||||||
|
float crossed = hadValidPrev * step(0.0, prevDepthDiff) * step(depthDiff, 0.0);
|
||||||
|
float withinThickness = step(abs(depthDiff), thickness);
|
||||||
|
|
||||||
|
if (crossed > 0.5 || withinThickness > 0.5) {
|
||||||
|
float distWeight = 1.0 - (rayDist / maxDist);
|
||||||
|
distWeight = max(0.0, distWeight * distWeight);
|
||||||
|
|
||||||
|
return vec3(sampleUV, distWeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
prevDepthDiff = depthDiff;
|
||||||
|
hadValidPrev = 1.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return vec3(-1.0);
|
||||||
|
}
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
float depth = textureLod(gbufferD, texCoord, 0.0).r;
|
float depth = textureLod(gbufferD, texCoord, 0.0).r * 2.0 - 1.0;
|
||||||
if (depth >= 1.0) {
|
if (depth == 1.0) {
|
||||||
fragColor = vec3(0.0);
|
fragColor = vec4(0.0, 0.0, 0.0, 1.0);
|
||||||
return;
|
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;
|
|
||||||
|
|
||||||
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 pos = getWorldPos(texCoord, depth);
|
|
||||||
vec3 normal = getNormal(texCoord);
|
|
||||||
vec3 centerColor = textureLod(gbuffer1, texCoord, 0.0).rgb;
|
|
||||||
|
|
||||||
float radius = ssaoRadius;
|
|
||||||
|
|
||||||
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
|
vec4 g0 = textureLod(gbuffer0, texCoord, 0.0);
|
||||||
if (totalWeight > 0.0) {
|
vec2 enc = g0.rg;
|
||||||
gi /= totalWeight;
|
vec3 n;
|
||||||
#ifdef _CPostprocess
|
n.z = 1.0 - abs(enc.x) - abs(enc.y);
|
||||||
gi *= PPComp12.x;
|
n.xy = n.z >= 0.0 ? enc.xy : octahedronWrap(enc.xy);
|
||||||
#else
|
n = normalize(n);
|
||||||
gi *= ssaoStrength;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef _EmissionShadeless
|
vec3 viewNormal = V3 * n;
|
||||||
if (matid == 1) { // pure emissive material, color stored in basecol
|
vec3 viewPos = getPosView2(invP, depth, texCoord);
|
||||||
gi += textureLod(gbuffer1, texCoord, 0.0).rgb;
|
|
||||||
}
|
#ifdef _CPostprocess
|
||||||
|
float radius = PPComp12.y;
|
||||||
|
float strength = PPComp12.x;
|
||||||
|
#else
|
||||||
|
float radius = ssgiRadius;
|
||||||
|
float strength = ssgiStrength;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
float noise = fract(52.9829189 * fract(0.06711056 * texCoord.x * 1000.0 + 0.00583715 * texCoord.y * 1000.0));
|
||||||
|
|
||||||
|
vec3 gi = vec3(0.0);
|
||||||
|
int validSamples = 0;
|
||||||
|
|
||||||
|
// min distance to avoid self shadowing artiffacts
|
||||||
|
float minDist = radius * 0.05;
|
||||||
|
|
||||||
|
for (int i = 0; i < ssgiSamples; i++) {
|
||||||
|
float fi = float(i) + noise;
|
||||||
|
vec2 rand = vec2(
|
||||||
|
fract(fi * 0.7548776662 + noise),
|
||||||
|
fract(fi * 0.5698402909 + noise * 1.5)
|
||||||
|
);
|
||||||
|
|
||||||
|
vec3 rayDir = cosineSampleHemisphere(viewNormal, rand);
|
||||||
|
vec3 hitResult = traceRay(viewPos, rayDir, radius, minDist);
|
||||||
|
|
||||||
|
if (hitResult.x < 0.0) continue;
|
||||||
|
|
||||||
|
vec2 hitUV = hitResult.xy;
|
||||||
|
float distWeight = hitResult.z;
|
||||||
|
|
||||||
|
vec3 hitAlbedo = textureLod(gbuffer1, hitUV, 1.0).rgb;
|
||||||
|
|
||||||
|
#ifdef _Sun
|
||||||
|
vec4 hitG0 = textureLod(gbuffer0, hitUV, 0.0);
|
||||||
|
vec2 hitEnc = hitG0.rg;
|
||||||
|
vec3 hitN;
|
||||||
|
hitN.z = 1.0 - abs(hitEnc.x) - abs(hitEnc.y);
|
||||||
|
hitN.xy = hitN.z >= 0.0 ? hitEnc.xy : octahedronWrap(hitEnc.xy);
|
||||||
|
hitN = normalize(hitN);
|
||||||
|
float hitNdotL = max(0.0, dot(hitN, sunDir));
|
||||||
|
vec3 hitRadiance = hitAlbedo * sunCol * hitNdotL;
|
||||||
|
#else
|
||||||
|
vec3 hitRadiance = hitAlbedo * 0.5;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef _EmissionShaded
|
||||||
|
hitRadiance += textureLod(gbufferEmission, hitUV, 0.0).rgb;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
gi += hitRadiance * distWeight;
|
||||||
|
validSamples++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (validSamples > 0) {
|
||||||
|
gi /= float(validSamples);
|
||||||
|
}
|
||||||
|
|
||||||
|
gi *= strength;
|
||||||
|
|
||||||
#ifdef _EmissionShaded
|
#ifdef _EmissionShaded
|
||||||
#ifdef _EmissionShadeless
|
gi += textureLod(gbufferEmission, texCoord, 0.0).rgb * 0.3;
|
||||||
else {
|
|
||||||
#endif
|
|
||||||
gi += textureLod(gbufferEmission, texCoord, 0.0).rgb;
|
|
||||||
#ifdef _EmissionShadeless
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
#endif
|
#endif
|
||||||
fragColor = gi / (gi + vec3(1.0)); // Reinhard tone mapping
|
|
||||||
|
fragColor = vec4(min(gi, vec3(2.0)), 1.0);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,60 +6,18 @@
|
|||||||
"compare_mode": "always",
|
"compare_mode": "always",
|
||||||
"cull_mode": "none",
|
"cull_mode": "none",
|
||||||
"links": [
|
"links": [
|
||||||
{
|
|
||||||
"name": "invVP",
|
|
||||||
"link": "_inverseViewProjectionMatrix"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "P",
|
"name": "P",
|
||||||
"link": "_projectionMatrix"
|
"link": "_projectionMatrix"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "invP",
|
||||||
|
"link": "_inverseProjectionMatrix"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "V3",
|
"name": "V3",
|
||||||
"link": "_viewMatrix3"
|
"link": "_viewMatrix3"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "eye",
|
|
||||||
"link": "_cameraPosition"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "eyeLook",
|
|
||||||
"link": "_cameraLook"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"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",
|
"name": "sunDir",
|
||||||
"link": "_sunDirection",
|
"link": "_sunDirection",
|
||||||
@ -71,128 +29,13 @@
|
|||||||
"ifdef": ["_Sun"]
|
"ifdef": ["_Sun"]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "shadowsBias",
|
"name": "PPComp12",
|
||||||
"link": "_sunShadowsBias",
|
"link": "_PPComp12",
|
||||||
"ifdef": ["_Sun", "_ShadowMap"]
|
"ifdef": ["_CPostprocess"]
|
||||||
},
|
|
||||||
{
|
|
||||||
"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": [],
|
"texture_params": [],
|
||||||
"vertex_shader": "../include/pass_viewray.vert.glsl",
|
"vertex_shader": "../include/pass.vert.glsl",
|
||||||
"fragment_shader": "ssgi_pass.frag.glsl"
|
"fragment_shader": "ssgi_pass.frag.glsl"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
@ -64,20 +64,26 @@ vec4 rayCast(vec3 dir) {
|
|||||||
ddepth = getDeltaDepth(hitCoord);
|
ddepth = getDeltaDepth(hitCoord);
|
||||||
if (ddepth > 0.0) return binarySearch(dir);
|
if (ddepth > 0.0) return binarySearch(dir);
|
||||||
}
|
}
|
||||||
return vec4(getProjectedCoord(hitCoord), 0.0, 1.0);
|
return vec4(texCoord, 0.0, 0.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
vec4 g0 = textureLod(gbuffer0, texCoord, 0.0);
|
|
||||||
float roughness = g0.z;
|
|
||||||
vec4 gr = textureLod(gbuffer_refraction, texCoord, 0.0);
|
vec4 gr = textureLod(gbuffer_refraction, texCoord, 0.0);
|
||||||
float ior = gr.x;
|
float ior = gr.x;
|
||||||
float opac = gr.y;
|
float transmittance = gr.y;
|
||||||
float d = textureLod(gbufferD, texCoord, 0.0).r * 2.0 - 1.0;
|
float surfaceDepth = gr.z;
|
||||||
if (d == 0.0 || d == 1.0 || opac == 1.0 || ior == 1.0) {
|
float d = surfaceDepth * 2.0 - 1.0;
|
||||||
fragColor.rgb = textureLod(tex1, texCoord, 0.0).rgb;
|
|
||||||
|
vec4 sceneSample = textureLod(tex, texCoord, 0.0);
|
||||||
|
if (surfaceDepth == 0.0 || transmittance == 0.0 || ior == 1.0) {
|
||||||
|
vec3 background = textureLod(tex1, texCoord, 0.0).rgb;
|
||||||
|
fragColor.rgb = sceneSample.rgb + background * (1.0 - sceneSample.a);
|
||||||
|
fragColor.a = 1.0;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
vec4 g0 = textureLod(gbuffer0, texCoord, 0.0);
|
||||||
|
float roughness = g0.z;
|
||||||
vec2 enc = g0.rg;
|
vec2 enc = g0.rg;
|
||||||
vec3 n;
|
vec3 n;
|
||||||
n.z = 1.0 - abs(enc.x) - abs(enc.y);
|
n.z = 1.0 - abs(enc.x) - abs(enc.y);
|
||||||
@ -86,21 +92,32 @@ void main() {
|
|||||||
|
|
||||||
vec3 viewNormal = V3 * n;
|
vec3 viewNormal = V3 * n;
|
||||||
vec3 viewPos = getPosView(viewRay, d, cameraProj);
|
vec3 viewPos = getPosView(viewRay, d, cameraProj);
|
||||||
vec3 refracted = refract(viewPos, viewNormal, 1.0 / ior);
|
vec3 incident = normalize(viewPos);
|
||||||
|
vec3 refracted = refract(incident, viewNormal, 1.0 / ior);
|
||||||
|
if (length(refracted) < 0.001) {
|
||||||
|
vec3 background = textureLod(tex1, texCoord, 0.0).rgb;
|
||||||
|
fragColor.rgb = sceneSample.rgb + background * (1.0 - sceneSample.a);
|
||||||
|
fragColor.a = 1.0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
hitCoord = viewPos;
|
hitCoord = viewPos;
|
||||||
|
|
||||||
vec3 dir = refracted * (1.0 - rand(texCoord) * ss_refractionJitter * roughness) * 2.0;
|
vec3 dir = refracted * (1.0 - rand(texCoord) * ss_refractionJitter * roughness) * 2.0;
|
||||||
vec4 coords = rayCast(dir);
|
vec4 coords = rayCast(dir);
|
||||||
vec2 deltaCoords = abs(vec2(0.5, 0.5) - coords.xy);
|
|
||||||
float screenEdgeFactor = clamp(1.0 - (deltaCoords.x + deltaCoords.y), 0.0, 1.0);
|
vec2 screenEdge = smoothstep(0.0, 0.1, coords.xy) * smoothstep(0.0, 0.1, 1.0 - coords.xy);
|
||||||
|
float screenEdgeFactor = screenEdge.x * screenEdge.y;
|
||||||
float refractivity = 1.0 - roughness;
|
float refractivity = 1.0 - roughness;
|
||||||
float intensity = pow(refractivity, ss_refractionFalloffExp) * screenEdgeFactor * \
|
|
||||||
clamp(-refracted.z, 0.0, 1.0) * clamp((length(viewPos - hitCoord)), 0.0, 1.0) * coords.w;
|
float intensity = pow(refractivity, ss_refractionFalloffExp) * screenEdgeFactor * coords.w;
|
||||||
intensity = clamp(intensity, 0.0, 1.0);
|
intensity = clamp(intensity, 0.0, 1.0);
|
||||||
|
|
||||||
vec3 refractionCol = textureLod(tex1, coords.xy, 0.0).rgb;
|
vec3 refractedBackground = textureLod(tex1, coords.xy, 0.0).rgb;
|
||||||
refractionCol *= intensity;
|
vec3 straightBackground = textureLod(tex1, texCoord, 0.0).rgb;
|
||||||
vec3 color = textureLod(tex, texCoord.xy, 0.0).rgb;
|
|
||||||
|
|
||||||
fragColor.rgb = mix(refractionCol, color, opac);
|
vec3 behindColor = mix(straightBackground, refractedBackground, intensity);
|
||||||
|
|
||||||
|
fragColor.rgb = sceneSample.rgb + behindColor * (1.0 - sceneSample.a);
|
||||||
|
fragColor.a = 1.0;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,6 +5,9 @@
|
|||||||
"depth_write": false,
|
"depth_write": false,
|
||||||
"compare_mode": "always",
|
"compare_mode": "always",
|
||||||
"cull_mode": "none",
|
"cull_mode": "none",
|
||||||
|
"blend_source": "blend_one",
|
||||||
|
"blend_destination": "blend_zero",
|
||||||
|
"blend_operation": "add",
|
||||||
"links": [
|
"links": [
|
||||||
{
|
{
|
||||||
"name": "P",
|
"name": "P",
|
||||||
|
|||||||
@ -36,6 +36,7 @@
|
|||||||
#version 450
|
#version 450
|
||||||
|
|
||||||
#include "compiled.inc"
|
#include "compiled.inc"
|
||||||
|
#include "std/gbuffer.glsl"
|
||||||
|
|
||||||
uniform sampler2D gbufferD;
|
uniform sampler2D gbufferD;
|
||||||
uniform sampler2D gbuffer0;
|
uniform sampler2D gbuffer0;
|
||||||
@ -47,69 +48,92 @@ uniform vec2 cameraProj;
|
|||||||
in vec2 texCoord;
|
in vec2 texCoord;
|
||||||
out vec4 fragColor;
|
out vec4 fragColor;
|
||||||
|
|
||||||
const float SSSS_FOVY = 108.0;
|
const vec3 SKIN_SSS_RADIUS = vec3(4.8, 2.4, 1.5);
|
||||||
|
const float SSS_DISTANCE_SCALE = 0.001;
|
||||||
|
|
||||||
|
// Temp hash func -
|
||||||
|
float hash13(vec3 p3) {
|
||||||
|
p3 = fract(p3 * vec3(0.1031, 0.1030, 0.0973));
|
||||||
|
p3 += dot(p3, p3.yzx + 33.33);
|
||||||
|
return fract((p3.x + p3.y) * p3.z);
|
||||||
|
}
|
||||||
|
|
||||||
// Separable SSS Reflectance
|
|
||||||
// const float sssWidth = 0.005;
|
|
||||||
vec4 SSSSBlur() {
|
vec4 SSSSBlur() {
|
||||||
// Quality = 0
|
const int SSSS_N_SAMPLES = 15;
|
||||||
const int SSSS_N_SAMPLES = 11;
|
|
||||||
vec4 kernel[SSSS_N_SAMPLES];
|
vec4 kernel[SSSS_N_SAMPLES];
|
||||||
kernel[0] = vec4(0.560479, 0.669086, 0.784728, 0);
|
|
||||||
kernel[1] = vec4(0.00471691, 0.000184771, 5.07566e-005, -2);
|
kernel[0] = vec4(0.233, 0.455, 0.649, 0.0); // Center sample
|
||||||
kernel[2] = vec4(0.0192831, 0.00282018, 0.00084214, -1.28);
|
kernel[1] = vec4(0.100, 0.336, 0.344, 0.37); // +0.37mm
|
||||||
kernel[3] = vec4(0.03639, 0.0130999, 0.00643685, -0.72);
|
kernel[2] = vec4(0.118, 0.198, 0.0, 0.97); // +0.97mm
|
||||||
kernel[4] = vec4(0.0821904, 0.0358608, 0.0209261, -0.32);
|
kernel[3] = vec4(0.113, 0.007, 0.007, 1.93); // +1.93mm
|
||||||
kernel[5] = vec4(0.0771802, 0.113491, 0.0793803, -0.08);
|
kernel[4] = vec4(0.358, 0.004, 0.0, 3.87); // +3.87mm
|
||||||
kernel[6] = vec4(0.0771802, 0.113491, 0.0793803, 0.08);
|
kernel[5] = vec4(0.078, 0.0, 0.0, 6.53); // +6.53mm (red only)
|
||||||
kernel[7] = vec4(0.0821904, 0.0358608, 0.0209261, 0.32);
|
kernel[6] = vec4(0.0, 0.0, 0.0, 0.0); // Unused
|
||||||
kernel[8] = vec4(0.03639, 0.0130999, 0.00643685, 0.72);
|
kernel[7] = vec4(0.0, 0.0, 0.0, 0.0); // Unused
|
||||||
kernel[9] = vec4(0.0192831, 0.00282018, 0.00084214, 1.28);
|
kernel[8] = vec4(0.100, 0.336, 0.344, -0.37); // -0.37mm
|
||||||
kernel[10] = vec4(0.00471691, 0.000184771, 5.07565e-005, 2);
|
kernel[9] = vec4(0.118, 0.198, 0.0, -0.97); // -0.97mm
|
||||||
|
kernel[10] = vec4(0.113, 0.007, 0.007, -1.93); // -1.93mm
|
||||||
|
kernel[11] = vec4(0.358, 0.004, 0.0, -3.87); // -3.87mm
|
||||||
|
kernel[12] = vec4(0.078, 0.0, 0.0, -6.53); // -6.53mm (red only)
|
||||||
|
kernel[13] = vec4(0.0, 0.0, 0.0, 0.0); // Unused
|
||||||
|
kernel[14] = vec4(0.0, 0.0, 0.0, 0.0); // Unused
|
||||||
|
|
||||||
vec4 colorM = textureLod(tex, texCoord, 0.0);
|
vec4 colorM = textureLod(tex, texCoord, 0.0);
|
||||||
|
|
||||||
// Fetch linear depth of current pixel
|
|
||||||
float depth = textureLod(gbufferD, texCoord, 0.0).r;
|
float depth = textureLod(gbufferD, texCoord, 0.0).r;
|
||||||
float depthM = cameraProj.y / (depth - cameraProj.x);
|
float depthM = cameraProj.y / (depth - cameraProj.x);
|
||||||
|
|
||||||
// Calculate the sssWidth scale (1.0 for a unit plane sitting on the projection window)
|
float distanceScale = 1.0 / max(depthM, 0.1);
|
||||||
float distanceToProjectionWindow = 1.0 / tan(0.5 * radians(SSSS_FOVY));
|
|
||||||
float scale = distanceToProjectionWindow / depthM;
|
|
||||||
|
|
||||||
// Calculate the final step to fetch the surrounding pixels
|
vec2 finalStep = sssWidth * distanceScale * dir * SSS_DISTANCE_SCALE;
|
||||||
vec2 finalStep = sssWidth * scale * dir;
|
|
||||||
finalStep *= 1.0;//SSSS_STREGTH_SOURCE; // Modulate it using the alpha channel.
|
|
||||||
finalStep *= 1.0 / 3.0; // Divide by 3 as the kernels range from -3 to 3.
|
|
||||||
finalStep *= 0.05; //
|
|
||||||
|
|
||||||
// Accumulate the center sample:
|
|
||||||
vec4 colorBlurred = colorM;
|
|
||||||
colorBlurred.rgb *= kernel[0].rgb;
|
|
||||||
|
|
||||||
// Accumulate the other samples
|
vec3 jitterSeed = vec3(texCoord.xy * 1000.0, fract(cameraProj.x * 0.0001));
|
||||||
|
float jitterOffset = (hash13(jitterSeed) * 2.0 - 1.0) * 0.15;
|
||||||
|
|
||||||
|
finalStep *= (1.0 + jitterOffset);
|
||||||
|
vec3 colorBlurred = vec3(0.0);
|
||||||
|
vec3 weightSum = vec3(0.0);
|
||||||
|
colorBlurred += colorM.rgb * kernel[0].rgb;
|
||||||
|
weightSum += kernel[0].rgb;
|
||||||
|
|
||||||
for (int i = 1; i < SSSS_N_SAMPLES; i++) {
|
for (int i = 1; i < SSSS_N_SAMPLES; i++) {
|
||||||
// Fetch color and depth for current sample
|
float sampleJitter = hash13(vec3(texCoord.xy * 720.0, float(i) * 37.45)) * 0.1 - 0.05;
|
||||||
vec2 offset = texCoord + kernel[i].a * finalStep;
|
vec2 offset = texCoord + (kernel[i].a + sampleJitter) * finalStep;
|
||||||
vec4 color = textureLod(tex, offset, 0.0);
|
vec4 color = textureLod(tex, offset, 0.0);
|
||||||
//#if SSSS_FOLLOW_SURFACE == 1
|
const float DEPTH_THRESHOLD = 0.05;
|
||||||
// If the difference in depth is huge, we lerp color back to "colorM":
|
float sampleDepth = textureLod(gbufferD, offset, 0.0).r;
|
||||||
//float depth = textureLod(tex, offset, 0.0).r;
|
float sampleDepthM = cameraProj.y / (sampleDepth - cameraProj.x);
|
||||||
//float s = clamp(300.0f * distanceToProjectionWindow * sssWidth * abs(depthM - depth),0.0,1.0);
|
|
||||||
//color.rgb = mix(color.rgb, colorM.rgb, s);
|
|
||||||
//#endif
|
|
||||||
// Accumulate
|
|
||||||
colorBlurred.rgb += kernel[i].rgb * color.rgb;
|
|
||||||
}
|
|
||||||
|
|
||||||
return colorBlurred;
|
float depthDiff = abs(depthM - sampleDepthM);
|
||||||
|
float depthWeight = exp(-depthDiff * 10.0);
|
||||||
|
|
||||||
|
if (depthDiff > DEPTH_THRESHOLD) {
|
||||||
|
color.rgb = mix(colorM.rgb, color.rgb, depthWeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
colorBlurred += color.rgb * kernel[i].rgb;
|
||||||
|
weightSum += kernel[i].rgb;
|
||||||
|
}
|
||||||
|
vec3 normalizedColor = colorBlurred / max(weightSum, vec3(0.00001));
|
||||||
|
float dither = hash13(vec3(texCoord * 1333.0, 0.0)) * 0.003 - 0.0015;
|
||||||
|
normalizedColor = max(normalizedColor + vec3(dither), vec3(0.0));
|
||||||
|
return vec4(normalizedColor, colorM.a);
|
||||||
}
|
}
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
if (textureLod(gbuffer0, texCoord, 0.0).a == 8192.0) {
|
vec4 g0 = textureLod(gbuffer0, texCoord, 0.0);
|
||||||
fragColor = clamp(SSSSBlur(), 0.0, 1.0);
|
float metallic;
|
||||||
}
|
uint matid;
|
||||||
else {
|
unpackFloatInt16(g0.a, metallic, matid);
|
||||||
|
|
||||||
|
if (matid == 2u) {
|
||||||
|
vec4 originalColor = textureLod(tex, texCoord, 0.0);
|
||||||
|
vec4 blurredColor = SSSSBlur();
|
||||||
|
vec4 sssContribution = blurredColor - originalColor;
|
||||||
|
vec4 combined = originalColor + max(vec4(0.0), sssContribution) * 0.8;
|
||||||
|
fragColor = max(vec4(0.0), min(combined, vec4(10.0)));
|
||||||
|
} else {
|
||||||
fragColor = textureLod(tex, texCoord, 0.0);
|
fragColor = textureLod(tex, texCoord, 0.0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -36,7 +36,8 @@ float d_ggx(const float nh, const float a) {
|
|||||||
|
|
||||||
vec3 specularBRDF(const vec3 f0, const float roughness, const float nl, const float nh, const float nv, const float vh) {
|
vec3 specularBRDF(const vec3 f0, const float roughness, const float nl, const float nh, const float nv, const float vh) {
|
||||||
float a = roughness * roughness;
|
float a = roughness * roughness;
|
||||||
return d_ggx(nh, a) * g2_approx(nl, nv, a) * f_schlick(f0, vh) / max(4.0 * nv, 1e-5); //NdotL cancels out later
|
vec3 result = d_ggx(nh, a) * g2_approx(nl, nv, a) * f_schlick(f0, vh) / max(4.0 * nv, 1e-5); //NdotL cancels out later
|
||||||
|
return min(result, vec3(200.0));
|
||||||
}
|
}
|
||||||
|
|
||||||
// John Hable - Optimizing GGX Shaders
|
// John Hable - Optimizing GGX Shaders
|
||||||
|
|||||||
@ -97,9 +97,9 @@ vec4 traceCone(const sampler3D voxels, const sampler3D voxelsSDF, const vec3 ori
|
|||||||
|
|
||||||
vec3 aniso_direction = -dir;
|
vec3 aniso_direction = -dir;
|
||||||
vec3 face_offset = vec3(
|
vec3 face_offset = vec3(
|
||||||
aniso_direction.x > 0.0 ? 0 : 1,
|
aniso_direction.x > 0.0 ? 0.0 : 1.0,
|
||||||
aniso_direction.y > 0.0 ? 2 : 3,
|
aniso_direction.y > 0.0 ? 2.0 : 3.0,
|
||||||
aniso_direction.z > 0.0 ? 4 : 5
|
aniso_direction.z > 0.0 ? 4.0 : 5.0
|
||||||
) / (6 + DIFFUSE_CONE_COUNT);
|
) / (6 + DIFFUSE_CONE_COUNT);
|
||||||
vec3 direction_weight = abs(dir);
|
vec3 direction_weight = abs(dir);
|
||||||
|
|
||||||
@ -202,9 +202,9 @@ float traceConeAO(const sampler3D voxels, const vec3 origin, const vec3 n, const
|
|||||||
|
|
||||||
vec3 aniso_direction = -dir;
|
vec3 aniso_direction = -dir;
|
||||||
vec3 face_offset = vec3(
|
vec3 face_offset = vec3(
|
||||||
aniso_direction.x > 0.0 ? 0 : 1,
|
aniso_direction.x > 0.0 ? 0.0 : 1.0,
|
||||||
aniso_direction.y > 0.0 ? 2 : 3,
|
aniso_direction.y > 0.0 ? 2.0 : 3.0,
|
||||||
aniso_direction.z > 0.0 ? 4 : 5
|
aniso_direction.z > 0.0 ? 4.0 : 5.0
|
||||||
) / (6 + DIFFUSE_CONE_COUNT);
|
) / (6 + DIFFUSE_CONE_COUNT);
|
||||||
vec3 direction_weight = abs(dir);
|
vec3 direction_weight = abs(dir);
|
||||||
|
|
||||||
@ -272,9 +272,9 @@ float traceConeShadow(const sampler3D voxels, const sampler3D voxelsSDF, const v
|
|||||||
|
|
||||||
vec3 aniso_direction = -dir;
|
vec3 aniso_direction = -dir;
|
||||||
vec3 face_offset = vec3(
|
vec3 face_offset = vec3(
|
||||||
aniso_direction.x > 0.0 ? 0 : 1,
|
aniso_direction.x > 0.0 ? 0.0 : 1.0,
|
||||||
aniso_direction.y > 0.0 ? 2 : 3,
|
aniso_direction.y > 0.0 ? 2.0 : 3.0,
|
||||||
aniso_direction.z > 0.0 ? 4 : 5
|
aniso_direction.z > 0.0 ? 4.0 : 5.0
|
||||||
) / (6 + DIFFUSE_CONE_COUNT);
|
) / (6 + DIFFUSE_CONE_COUNT);
|
||||||
vec3 direction_weight = abs(dir);
|
vec3 direction_weight = abs(dir);
|
||||||
float coneCoefficient = 2.0 * tan(aperture * 0.5);
|
float coneCoefficient = 2.0 * tan(aperture * 0.5);
|
||||||
|
|||||||
@ -24,37 +24,44 @@ const int DIFFUSE_CONE_COUNT = 16;
|
|||||||
|
|
||||||
const float SHADOW_CONE_APERTURE = radians(15.0);
|
const float SHADOW_CONE_APERTURE = radians(15.0);
|
||||||
|
|
||||||
const float DIFFUSE_CONE_APERTURE = radians(50.0);
|
const float DIFFUSE_CONE_APERTURE = 0.872665;
|
||||||
|
|
||||||
const vec3 DIFFUSE_CONE_DIRECTIONS[DIFFUSE_CONE_COUNT] = vec3[](
|
mat3 makeTangentBasis(const vec3 normal) {
|
||||||
vec3(0.0, 0.0, 1.0), // center
|
// Create a tangent basis from normal vector
|
||||||
|
vec3 tangent;
|
||||||
|
vec3 bitangent;
|
||||||
|
|
||||||
vec3(0.0, 0.5, 0.866),
|
// Compute tangent (Frisvad's method)
|
||||||
vec3(0.5, 0.0, 0.866),
|
if (abs(normal.z) < 0.999) {
|
||||||
vec3(0.0, -0.5, 0.866),
|
tangent = normalize(cross(vec3(0, 1, 0), normal));
|
||||||
vec3(-0.5, 0.0, 0.866),
|
} else {
|
||||||
|
tangent = normalize(cross(normal, vec3(1, 0, 0)));
|
||||||
|
}
|
||||||
|
bitangent = cross(normal, tangent);
|
||||||
|
|
||||||
vec3(0.353, 0.353, 0.866),
|
|
||||||
vec3(0.353, -0.353, 0.866),
|
|
||||||
vec3(-0.353, -0.353, 0.866),
|
|
||||||
vec3(-0.353, 0.353, 0.866),
|
|
||||||
|
|
||||||
vec3(0.707, 0.0, 0.707),
|
|
||||||
vec3(0.0, 0.707, 0.707),
|
|
||||||
vec3(-0.707, 0.0, 0.707),
|
|
||||||
vec3(0.0, -0.707, 0.707),
|
|
||||||
|
|
||||||
vec3(0.5, 0.5, 0.707),
|
|
||||||
vec3(-0.5, 0.5, 0.707),
|
|
||||||
vec3(-0.5, -0.5, 0.707)
|
|
||||||
);
|
|
||||||
|
|
||||||
mat3 makeTangentBasis(vec3 normal) {
|
|
||||||
vec3 tangent = normalize(abs(normal.y) < 0.999 ? cross(normal, vec3(0, 1, 0)) : cross(normal, vec3(1, 0, 0)));
|
|
||||||
vec3 bitangent = cross(normal, tangent);
|
|
||||||
return mat3(tangent, bitangent, normal);
|
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
|
// TO DO - Disabled momentarily instead of changing formulas
|
||||||
const float off_BayerMatrix8[8][8] =
|
const float off_BayerMatrix8[8][8] =
|
||||||
{
|
{
|
||||||
|
|||||||
@ -8,10 +8,10 @@
|
|||||||
// const float compoDOFLength = 160.0; // Focal length in mm 18-200
|
// const float compoDOFLength = 160.0; // Focal length in mm 18-200
|
||||||
// const float compoDOFFstop = 128.0; // F-stop value
|
// const float compoDOFFstop = 128.0; // F-stop value
|
||||||
|
|
||||||
const int samples = 6; // Samples on the first ring
|
const int samples = 8; // Samples on the first ring
|
||||||
const int rings = 6; // Ring count
|
const int rings = 6; // Ring count
|
||||||
const vec2 focus = vec2(0.5, 0.5);
|
const vec2 focus = vec2(0.5, 0.5);
|
||||||
const float coc = 0.11; // Circle of confusion size in mm (35mm film = 0.03mm)
|
const float coc = 0.03; // Circle of confusion size in mm (35mm film = 0.03mm)
|
||||||
const float maxblur = 1.0;
|
const float maxblur = 1.0;
|
||||||
const float threshold = 0.5; // Highlight threshold
|
const float threshold = 0.5; // Highlight threshold
|
||||||
const float gain = 2.0; // Highlight gain
|
const float gain = 2.0; // Highlight gain
|
||||||
@ -55,21 +55,26 @@ vec3 dof(
|
|||||||
float f = DOFLength; // Focal length in mm
|
float f = DOFLength; // Focal length in mm
|
||||||
float d = fDepth * 1000.0; // Focal plane in mm
|
float d = fDepth * 1000.0; // Focal plane in mm
|
||||||
float o = depth * 1000.0; // Depth in mm
|
float o = depth * 1000.0; // Depth in mm
|
||||||
float a = (o * f) / (o - f);
|
float a = (o > f) ? (o * f) / (o - f) : 0.0;
|
||||||
float b = (d * f) / (d - f);
|
float b = (d > f) ? (d * f) / (d - f) : 0.0;
|
||||||
float c = (d - f) / (d * DOFFStop * coc);
|
float sensorSize = max(DOFFStop, 10.0);
|
||||||
|
float c = (d - f) / (d * sensorSize * coc);
|
||||||
|
|
||||||
float blur = abs(a - b) * c;
|
float blur = abs(a - b) * c;
|
||||||
blur = clamp(blur, 0.0, 1.0);
|
blur = clamp(blur, 0.0, 1.0);
|
||||||
|
|
||||||
vec2 noise = rand2(texCoord) * namount * blur;
|
vec2 noise = rand2(texCoord) * namount * blur;
|
||||||
float w = (texStep.x) * blur * maxblur + noise.x;
|
float w = (texStep.x) * blur * maxblur + noise.x;
|
||||||
float h = (texStep.y) * blur * maxblur + noise.y;
|
float h = (texStep.y) * blur * maxblur + noise.y;
|
||||||
vec3 col = vec3(0.0);
|
vec3 sharpCol = textureLod(tex, texCoord, 0.0).rgb;
|
||||||
if (blur < 0.05) {
|
vec3 col = sharpCol;
|
||||||
col = textureLod(tex, texCoord, 0.0).rgb;
|
float blurThreshold = 0.02;
|
||||||
}
|
float blurRange = 0.06;
|
||||||
else {
|
|
||||||
col = textureLod(tex, texCoord, 0.0).rgb;
|
if (blur > blurThreshold) {
|
||||||
|
float blurAmount = smoothstep(blurThreshold, blurThreshold + blurRange, blur);
|
||||||
|
|
||||||
|
vec3 blurredCol = sharpCol;
|
||||||
float s = 1.0;
|
float s = 1.0;
|
||||||
int ringsamples;
|
int ringsamples;
|
||||||
|
|
||||||
@ -81,11 +86,12 @@ vec3 dof(
|
|||||||
float ph = (sin(float(j) * step) * float(i));
|
float ph = (sin(float(j) * step) * float(i));
|
||||||
float p = 1.0;
|
float p = 1.0;
|
||||||
// if (pentagon) p = penta(vec2(pw, ph));
|
// if (pentagon) p = penta(vec2(pw, ph));
|
||||||
col += color(texCoord + vec2(pw * w, ph * h), blur, tex, texStep) * mix(1.0, (float(i)) / (float(rings)), bias) * p;
|
blurredCol += color(texCoord + vec2(pw * w, ph * h), blur, tex, texStep) * mix(1.0, (float(i)) / (float(rings)), bias) * p;
|
||||||
s += 1.0 * mix(1.0, (float(i)) / (float(rings)), bias) * p;
|
s += 1.0 * mix(1.0, (float(i)) / (float(rings)), bias) * p;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
col /= s;
|
blurredCol /= s;
|
||||||
|
col = mix(sharpCol, blurredCol, blurAmount);
|
||||||
}
|
}
|
||||||
return col;
|
return col;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,11 +1,11 @@
|
|||||||
#ifndef _GBUFFER_GLSL_
|
#ifndef _GBUFFER_GLSL_
|
||||||
#define _GBUFFER_GLSL_
|
#define _GBUFFER_GLSL_
|
||||||
|
|
||||||
vec2 octahedronWrap(const vec2 v) {
|
vec2 octahedronWrap(vec2 v) {
|
||||||
return (1.0 - abs(v.yx)) * (vec2(v.x >= 0.0 ? 1.0 : -1.0, v.y >= 0.0 ? 1.0 : -1.0));
|
return (1.0 - abs(v.yx)) * (vec2(v.x >= 0.0 ? 1.0 : -1.0, v.y >= 0.0 ? 1.0 : -1.0));
|
||||||
}
|
}
|
||||||
|
|
||||||
vec3 getNor(const vec2 enc) {
|
vec3 getNor(vec2 enc) {
|
||||||
vec3 n;
|
vec3 n;
|
||||||
n.z = 1.0 - abs(enc.x) - abs(enc.y);
|
n.z = 1.0 - abs(enc.x) - abs(enc.y);
|
||||||
n.xy = n.z >= 0.0 ? enc.xy : octahedronWrap(enc.xy);
|
n.xy = n.z >= 0.0 ? enc.xy : octahedronWrap(enc.xy);
|
||||||
@ -13,13 +13,13 @@ vec3 getNor(const vec2 enc) {
|
|||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
vec3 getPosView(const vec3 viewRay, const float depth, const vec2 cameraProj) {
|
vec3 getPosView(vec3 viewRay, float depth, vec2 cameraProj) {
|
||||||
float linearDepth = cameraProj.y / (cameraProj.x - depth);
|
float linearDepth = cameraProj.y / (cameraProj.x - depth);
|
||||||
//float linearDepth = cameraProj.y / ((depth * 0.5 + 0.5) - cameraProj.x);
|
//float linearDepth = cameraProj.y / ((depth * 0.5 + 0.5) - cameraProj.x);
|
||||||
return viewRay * linearDepth;
|
return viewRay * linearDepth;
|
||||||
}
|
}
|
||||||
|
|
||||||
vec3 getPos(const vec3 eye, const vec3 eyeLook, const vec3 viewRay, const float depth, const vec2 cameraProj) {
|
vec3 getPos(vec3 eye, vec3 eyeLook, vec3 viewRay, float depth, vec2 cameraProj) {
|
||||||
// eyeLook, viewRay should be normalized
|
// eyeLook, viewRay should be normalized
|
||||||
float linearDepth = cameraProj.y / ((depth * 0.5 + 0.5) - cameraProj.x);
|
float linearDepth = cameraProj.y / ((depth * 0.5 + 0.5) - cameraProj.x);
|
||||||
float viewZDist = dot(eyeLook, viewRay);
|
float viewZDist = dot(eyeLook, viewRay);
|
||||||
@ -27,7 +27,7 @@ vec3 getPos(const vec3 eye, const vec3 eyeLook, const vec3 viewRay, const float
|
|||||||
return wposition;
|
return wposition;
|
||||||
}
|
}
|
||||||
|
|
||||||
vec3 getPosNoEye(const vec3 eyeLook, const vec3 viewRay, const float depth, const vec2 cameraProj) {
|
vec3 getPosNoEye(vec3 eyeLook, vec3 viewRay, float depth, vec2 cameraProj) {
|
||||||
// eyeLook, viewRay should be normalized
|
// eyeLook, viewRay should be normalized
|
||||||
float linearDepth = cameraProj.y / ((depth * 0.5 + 0.5) - cameraProj.x);
|
float linearDepth = cameraProj.y / ((depth * 0.5 + 0.5) - cameraProj.x);
|
||||||
float viewZDist = dot(eyeLook, viewRay);
|
float viewZDist = dot(eyeLook, viewRay);
|
||||||
@ -36,10 +36,10 @@ vec3 getPosNoEye(const vec3 eyeLook, const vec3 viewRay, const float depth, cons
|
|||||||
}
|
}
|
||||||
|
|
||||||
#if defined(HLSL) || defined(METAL)
|
#if defined(HLSL) || defined(METAL)
|
||||||
vec3 getPos2(const mat4 invVP, const float depth, vec2 coord) {
|
vec3 getPos2(mat4 invVP, float depth, vec2 coord) {
|
||||||
coord.y = 1.0 - coord.y;
|
coord.y = 1.0 - coord.y;
|
||||||
#else
|
#else
|
||||||
vec3 getPos2(const mat4 invVP, const float depth, const vec2 coord) {
|
vec3 getPos2(mat4 invVP, float depth, vec2 coord) {
|
||||||
#endif
|
#endif
|
||||||
vec4 pos = vec4(coord * 2.0 - 1.0, depth, 1.0);
|
vec4 pos = vec4(coord * 2.0 - 1.0, depth, 1.0);
|
||||||
pos = invVP * pos;
|
pos = invVP * pos;
|
||||||
@ -48,10 +48,10 @@ vec3 getPos2(const mat4 invVP, const float depth, const vec2 coord) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#if defined(HLSL) || defined(METAL)
|
#if defined(HLSL) || defined(METAL)
|
||||||
vec3 getPosView2(const mat4 invP, const float depth, vec2 coord) {
|
vec3 getPosView2(mat4 invP, float depth, vec2 coord) {
|
||||||
coord.y = 1.0 - coord.y;
|
coord.y = 1.0 - coord.y;
|
||||||
#else
|
#else
|
||||||
vec3 getPosView2(const mat4 invP, const float depth, const vec2 coord) {
|
vec3 getPosView2(mat4 invP, float depth, vec2 coord) {
|
||||||
#endif
|
#endif
|
||||||
vec4 pos = vec4(coord * 2.0 - 1.0, depth, 1.0);
|
vec4 pos = vec4(coord * 2.0 - 1.0, depth, 1.0);
|
||||||
pos = invP * pos;
|
pos = invP * pos;
|
||||||
@ -60,10 +60,10 @@ vec3 getPosView2(const mat4 invP, const float depth, const vec2 coord) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#if defined(HLSL) || defined(METAL)
|
#if defined(HLSL) || defined(METAL)
|
||||||
vec3 getPos2NoEye(const vec3 eye, const mat4 invVP, const float depth, vec2 coord) {
|
vec3 getPos2NoEye(vec3 eye, mat4 invVP, float depth, vec2 coord) {
|
||||||
coord.y = 1.0 - coord.y;
|
coord.y = 1.0 - coord.y;
|
||||||
#else
|
#else
|
||||||
vec3 getPos2NoEye(const vec3 eye, const mat4 invVP, const float depth, const vec2 coord) {
|
vec3 getPos2NoEye(vec3 eye, mat4 invVP, float depth, vec2 coord) {
|
||||||
#endif
|
#endif
|
||||||
vec4 pos = vec4(coord * 2.0 - 1.0, depth, 1.0);
|
vec4 pos = vec4(coord * 2.0 - 1.0, depth, 1.0);
|
||||||
pos = invVP * pos;
|
pos = invVP * pos;
|
||||||
@ -71,24 +71,24 @@ vec3 getPos2NoEye(const vec3 eye, const mat4 invVP, const float depth, const vec
|
|||||||
return pos.xyz - eye;
|
return pos.xyz - eye;
|
||||||
}
|
}
|
||||||
|
|
||||||
float packFloat(const float f1, const float f2) {
|
float packFloat(float f1, float f2) {
|
||||||
return floor(f1 * 100.0) + min(f2, 1.0 - 1.0 / 100.0);
|
return floor(f1 * 100.0) + min(f2, 1.0 - 1.0 / 100.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
vec2 unpackFloat(const float f) {
|
vec2 unpackFloat(float f) {
|
||||||
return vec2(floor(f) / 100.0, fract(f));
|
return vec2(floor(f) / 100.0, fract(f));
|
||||||
}
|
}
|
||||||
|
|
||||||
float packFloat2(const float f1, const float f2) {
|
float packFloat2(float f1, float f2) {
|
||||||
// Higher f1 = less precise f2
|
// Higher f1 = less precise f2
|
||||||
return floor(f1 * 255.0) + min(f2, 1.0 - 1.0 / 100.0);
|
return floor(f1 * 255.0) + min(f2, 1.0 - 1.0 / 100.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
vec2 unpackFloat2(const float f) {
|
vec2 unpackFloat2(float f) {
|
||||||
return vec2(floor(f) / 255.0, fract(f));
|
return vec2(floor(f) / 255.0, fract(f));
|
||||||
}
|
}
|
||||||
|
|
||||||
vec4 encodeRGBM(const vec3 rgb) {
|
vec4 encodeRGBM(vec3 rgb) {
|
||||||
const float maxRange = 6.0;
|
const float maxRange = 6.0;
|
||||||
float maxRGB = max(rgb.x, max(rgb.g, rgb.b));
|
float maxRGB = max(rgb.x, max(rgb.g, rgb.b));
|
||||||
float m = maxRGB / maxRange;
|
float m = maxRGB / maxRange;
|
||||||
@ -96,7 +96,7 @@ vec4 encodeRGBM(const vec3 rgb) {
|
|||||||
return vec4(rgb / (m * maxRange), m);
|
return vec4(rgb / (m * maxRange), m);
|
||||||
}
|
}
|
||||||
|
|
||||||
vec3 decodeRGBM(const vec4 rgbm) {
|
vec3 decodeRGBM(vec4 rgbm) {
|
||||||
const float maxRange = 6.0;
|
const float maxRange = 6.0;
|
||||||
return rgbm.rgb * rgbm.a * maxRange;
|
return rgbm.rgb * rgbm.a * maxRange;
|
||||||
}
|
}
|
||||||
@ -150,7 +150,7 @@ vec3 decNor(uint val) {
|
|||||||
/**
|
/**
|
||||||
Packs a float in [0, 1] and an integer in [0..15] into a single 16 bit float value.
|
Packs a float in [0, 1] and an integer in [0..15] into a single 16 bit float value.
|
||||||
**/
|
**/
|
||||||
float packFloatInt16(const float f, const uint i) {
|
float packFloatInt16(float f, uint i) {
|
||||||
const uint numBitFloat = 12;
|
const uint numBitFloat = 12;
|
||||||
const float maxValFloat = float((1 << numBitFloat) - 1);
|
const float maxValFloat = float((1 << numBitFloat) - 1);
|
||||||
|
|
||||||
@ -160,7 +160,7 @@ float packFloatInt16(const float f, const uint i) {
|
|||||||
return float(bitsInt | bitsFloat);
|
return float(bitsInt | bitsFloat);
|
||||||
}
|
}
|
||||||
|
|
||||||
void unpackFloatInt16(const float val, out float f, out uint i) {
|
void unpackFloatInt16(float val, out float f, out uint i) {
|
||||||
const uint numBitFloat = 12;
|
const uint numBitFloat = 12;
|
||||||
const float maxValFloat = float((1 << numBitFloat) - 1);
|
const float maxValFloat = float((1 << numBitFloat) - 1);
|
||||||
|
|
||||||
|
|||||||
@ -158,7 +158,7 @@ vec3 sampleLight(const vec3 p, const vec3 n, const vec3 v, const float dotNV, co
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
direct *= attenuate(distance(p, lp));
|
direct *= attenuate(distance(p, lp));
|
||||||
direct *= lightCol;
|
direct *= min(lightCol, vec3(100.0));
|
||||||
|
|
||||||
#ifdef _MicroShadowing
|
#ifdef _MicroShadowing
|
||||||
direct *= clamp(dotNL + 2.0 * occ * occ - 1.0, 0.0, 1.0);
|
direct *= clamp(dotNL + 2.0 * occ * occ - 1.0, 0.0, 1.0);
|
||||||
@ -181,55 +181,55 @@ vec3 sampleLight(const vec3 p, const vec3 n, const vec3 v, const float dotNV, co
|
|||||||
#ifdef _ShadowMap
|
#ifdef _ShadowMap
|
||||||
if (receiveShadow) {
|
if (receiveShadow) {
|
||||||
#ifdef _SinglePoint
|
#ifdef _SinglePoint
|
||||||
vec4 lPos = LWVPSpot[0] * vec4(p + n * bias * 10, 1.0);
|
vec4 lPos = LWVPSpot[0] * vec4(p + n * bias * 2, 1.0);
|
||||||
direct *= shadowTest(shadowMapSpot[0],
|
direct *= shadowTest(shadowMapSpot[0],
|
||||||
#ifdef _ShadowMapTransparent
|
#ifdef _ShadowMapTransparent
|
||||||
shadowMapSpotTransparent[0],
|
shadowMapSpotTransparent[0],
|
||||||
#endif
|
#endif
|
||||||
lPos.xyz / lPos.w, bias
|
lPos.xyz / lPos.w, bias
|
||||||
#ifdef _ShadowMapTransparent
|
#ifdef _ShadowMapTransparent
|
||||||
, transparent
|
, transparent
|
||||||
#endif
|
#endif
|
||||||
);
|
);
|
||||||
#endif
|
#endif
|
||||||
#ifdef _Clusters
|
#ifdef _Clusters
|
||||||
vec4 lPos = LWVPSpot[index] * vec4(p + n * bias * 10, 1.0);
|
vec4 lPos = LWVPSpot[index] * vec4(p + n * bias * 2, 1.0);
|
||||||
if (index == 0) direct *= shadowTest(shadowMapSpot[0],
|
if (index == 0) direct *= shadowTest(shadowMapSpot[0],
|
||||||
#ifdef _ShadowMapTransparent
|
#ifdef _ShadowMapTransparent
|
||||||
shadowMapSpotTransparent[0],
|
shadowMapSpotTransparent[0],
|
||||||
#endif
|
#endif
|
||||||
lPos.xyz / lPos.w, bias
|
lPos.xyz / lPos.w, bias
|
||||||
#ifdef _ShadowMapTransparent
|
#ifdef _ShadowMapTransparent
|
||||||
, transparent
|
, transparent
|
||||||
#endif
|
#endif
|
||||||
);
|
);
|
||||||
else if (index == 1) direct *= shadowTest(shadowMapSpot[1],
|
else if (index == 1) direct *= shadowTest(shadowMapSpot[1],
|
||||||
#ifdef _ShadowMapTransparent
|
#ifdef _ShadowMapTransparent
|
||||||
shadowMapSpotTransparent[1],
|
shadowMapSpotTransparent[1],
|
||||||
#endif
|
#endif
|
||||||
lPos.xyz / lPos.w, bias
|
lPos.xyz / lPos.w, bias
|
||||||
#ifdef _ShadowMapTransparent
|
#ifdef _ShadowMapTransparent
|
||||||
, transparent
|
, transparent
|
||||||
#endif
|
#endif
|
||||||
);
|
);
|
||||||
else if (index == 2) direct *= shadowTest(shadowMapSpot[2],
|
else if (index == 2) direct *= shadowTest(shadowMapSpot[2],
|
||||||
#ifdef _ShadowMapTransparent
|
#ifdef _ShadowMapTransparent
|
||||||
shadowMapSpotTransparent[2],
|
shadowMapSpotTransparent[2],
|
||||||
#endif
|
#endif
|
||||||
lPos.xyz / lPos.w, bias
|
lPos.xyz / lPos.w, bias
|
||||||
#ifdef _ShadowMapTransparent
|
#ifdef _ShadowMapTransparent
|
||||||
, transparent
|
, transparent
|
||||||
#endif
|
#endif
|
||||||
);
|
);
|
||||||
else if (index == 3) direct *= shadowTest(shadowMapSpot[3],
|
else if (index == 3) direct *= shadowTest(shadowMapSpot[3],
|
||||||
#ifdef _ShadowMapTransparent
|
#ifdef _ShadowMapTransparent
|
||||||
shadowMapSpotTransparent[3],
|
shadowMapSpotTransparent[3],
|
||||||
#endif
|
#endif
|
||||||
lPos.xyz / lPos.w, bias
|
lPos.xyz / lPos.w, bias
|
||||||
#ifdef _ShadowMapTransparent
|
#ifdef _ShadowMapTransparent
|
||||||
, transparent
|
, transparent
|
||||||
#endif
|
#endif
|
||||||
);
|
);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@ -243,76 +243,76 @@ vec3 sampleLight(const vec3 p, const vec3 n, const vec3 v, const float dotNV, co
|
|||||||
#ifdef _ShadowMap
|
#ifdef _ShadowMap
|
||||||
if (receiveShadow) {
|
if (receiveShadow) {
|
||||||
#ifdef _SinglePoint
|
#ifdef _SinglePoint
|
||||||
vec4 lPos = LWVPSpotArray[0] * vec4(p + n * bias * 10, 1.0);
|
vec4 lPos = LWVPSpotArray[0] * vec4(p + n * bias * 2, 1.0);
|
||||||
direct *= shadowTest(shadowMapSpot[0],
|
direct *= shadowTest(shadowMapSpot[0],
|
||||||
#ifdef _ShadowMapTransparent
|
#ifdef _ShadowMapTransparent
|
||||||
shadowMapSpotTransparent[0],
|
shadowMapSpotTransparent[0],
|
||||||
#endif
|
#endif
|
||||||
lPos.xyz / lPos.w, bias
|
lPos.xyz / lPos.w, bias
|
||||||
#ifdef _ShadowMapTransparent
|
#ifdef _ShadowMapTransparent
|
||||||
, transparent
|
, transparent
|
||||||
#endif
|
#endif
|
||||||
);
|
);
|
||||||
#endif
|
#endif
|
||||||
#ifdef _Clusters
|
#ifdef _Clusters
|
||||||
vec4 lPos = LWVPSpotArray[index] * vec4(p + n * bias * 10, 1.0);
|
vec4 lPos = LWVPSpotArray[index] * vec4(p + n * bias * 2, 1.0);
|
||||||
#ifdef _ShadowMapAtlas
|
#ifdef _ShadowMapAtlas
|
||||||
direct *= shadowTest(
|
direct *= shadowTest(
|
||||||
#ifdef _ShadowMapTransparent
|
#ifdef _ShadowMapTransparent
|
||||||
#ifndef _SingleAtlas
|
#ifndef _SingleAtlas
|
||||||
shadowMapAtlasSpot, shadowMapAtlasSpotTransparent
|
shadowMapAtlasSpot, shadowMapAtlasSpotTransparent
|
||||||
#else
|
#else
|
||||||
shadowMapAtlas, shadowMapAtlasTransparent
|
shadowMapAtlas, shadowMapAtlasTransparent
|
||||||
#endif
|
#endif
|
||||||
#else
|
#else
|
||||||
#ifndef _SingleAtlas
|
#ifndef _SingleAtlas
|
||||||
shadowMapAtlasSpot
|
shadowMapAtlasSpot
|
||||||
#else
|
#else
|
||||||
shadowMapAtlas
|
shadowMapAtlas
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
, lPos.xyz / lPos.w, bias
|
, lPos.xyz / lPos.w, bias
|
||||||
#ifdef _ShadowMapTransparent
|
#ifdef _ShadowMapTransparent
|
||||||
, transparent
|
, transparent
|
||||||
#endif
|
#endif
|
||||||
);
|
);
|
||||||
#else
|
#else
|
||||||
if (index == 0) direct *= shadowTest(shadowMapSpot[0],
|
if (index == 0) direct *= shadowTest(shadowMapSpot[0],
|
||||||
#ifdef _ShadowMapTransparent
|
#ifdef _ShadowMapTransparent
|
||||||
shadowMapSpotTransparent[0],
|
shadowMapSpotTransparent[0],
|
||||||
#endif
|
#endif
|
||||||
lPos.xyz / lPos.w, bias
|
lPos.xyz / lPos.w, bias
|
||||||
#ifdef _ShadowMapTransparent
|
#ifdef _ShadowMapTransparent
|
||||||
, transparent
|
, transparent
|
||||||
#endif
|
#endif
|
||||||
);
|
);
|
||||||
else if (index == 1) direct *= shadowTest(shadowMapSpot[1],
|
else if (index == 1) direct *= shadowTest(shadowMapSpot[1],
|
||||||
#ifdef _ShadowMapTransparent
|
#ifdef _ShadowMapTransparent
|
||||||
shadowMapSpotTransparent[1],
|
shadowMapSpotTransparent[1],
|
||||||
#endif
|
#endif
|
||||||
lPos.xyz / lPos.w, bias
|
lPos.xyz / lPos.w, bias
|
||||||
#ifdef _ShadowMapTransparent
|
#ifdef _ShadowMapTransparent
|
||||||
, transparent
|
, transparent
|
||||||
#endif
|
#endif
|
||||||
);
|
);
|
||||||
else if (index == 2) direct *= shadowTest(shadowMapSpot[2],
|
else if (index == 2) direct *= shadowTest(shadowMapSpot[2],
|
||||||
#ifdef _ShadowMapTransparent
|
#ifdef _ShadowMapTransparent
|
||||||
shadowMapSpotTransparent[2],
|
shadowMapSpotTransparent[2],
|
||||||
#endif
|
#endif
|
||||||
lPos.xyz / lPos.w, bias
|
lPos.xyz / lPos.w, bias
|
||||||
#ifdef _ShadowMapTransparent
|
#ifdef _ShadowMapTransparent
|
||||||
, transparent
|
, transparent
|
||||||
#endif
|
#endif
|
||||||
);
|
);
|
||||||
else if (index == 3) direct *= shadowTest(shadowMapSpot[3],
|
else if (index == 3) direct *= shadowTest(shadowMapSpot[3],
|
||||||
#ifdef _ShadowMapTransparent
|
#ifdef _ShadowMapTransparent
|
||||||
shadowMapSpotTransparent[3],
|
shadowMapSpotTransparent[3],
|
||||||
#endif
|
#endif
|
||||||
lPos.xyz / lPos.w, bias
|
lPos.xyz / lPos.w, bias
|
||||||
#ifdef _ShadowMapTransparent
|
#ifdef _ShadowMapTransparent
|
||||||
, transparent
|
, transparent
|
||||||
#endif
|
#endif
|
||||||
);
|
);
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
@ -330,74 +330,74 @@ vec3 sampleLight(const vec3 p, const vec3 n, const vec3 v, const float dotNV, co
|
|||||||
#ifdef _SinglePoint
|
#ifdef _SinglePoint
|
||||||
#ifndef _Spot
|
#ifndef _Spot
|
||||||
direct *= PCFCube(shadowMapPoint[0],
|
direct *= PCFCube(shadowMapPoint[0],
|
||||||
#ifdef _ShadowMapTransparent
|
#ifdef _ShadowMapTransparent
|
||||||
shadowMapPointTransparent[0],
|
shadowMapPointTransparent[0],
|
||||||
#endif
|
#endif
|
||||||
ld, -l, bias, lightProj, n
|
ld, -l, bias, lightProj, n
|
||||||
#ifdef _ShadowMapTransparent
|
#ifdef _ShadowMapTransparent
|
||||||
, transparent
|
, transparent
|
||||||
#endif
|
#endif
|
||||||
);
|
);
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
#ifdef _Clusters
|
#ifdef _Clusters
|
||||||
#ifdef _ShadowMapAtlas
|
#ifdef _ShadowMapAtlas
|
||||||
direct *= PCFFakeCube(
|
direct *= PCFFakeCube(
|
||||||
#ifdef _ShadowMapTransparent
|
#ifdef _ShadowMapTransparent
|
||||||
#ifndef _SingleAtlas
|
#ifndef _SingleAtlas
|
||||||
shadowMapAtlasPoint, shadowMapAtlasPointTransparent
|
shadowMapAtlasPoint, shadowMapAtlasPointTransparent
|
||||||
#else
|
#else
|
||||||
shadowMapAtlas, shadowMapAtlasTransparent
|
shadowMapAtlas, shadowMapAtlasTransparent
|
||||||
#endif
|
#endif
|
||||||
#else
|
#else
|
||||||
#ifndef _SingleAtlas
|
#ifndef _SingleAtlas
|
||||||
shadowMapAtlasPoint
|
shadowMapAtlasPoint
|
||||||
#else
|
#else
|
||||||
shadowMapAtlas
|
shadowMapAtlas
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
, ld, -l, bias, lightProj, n, index
|
, ld, -l, bias, lightProj, n, index
|
||||||
#ifdef _ShadowMapTransparent
|
#ifdef _ShadowMapTransparent
|
||||||
, transparent
|
, transparent
|
||||||
#endif
|
#endif
|
||||||
);
|
);
|
||||||
#else
|
#else
|
||||||
if (index == 0) direct *= PCFCube(shadowMapPoint[0],
|
if (index == 0) direct *= PCFCube(shadowMapPoint[0],
|
||||||
#ifdef _ShadowMapTransparent
|
#ifdef _ShadowMapTransparent
|
||||||
shadowMapPointTransparent[0],
|
shadowMapPointTransparent[0],
|
||||||
#endif
|
#endif
|
||||||
ld, -l, bias, lightProj, n
|
ld, -l, bias, lightProj, n
|
||||||
#ifdef _ShadowMapTransparent
|
#ifdef _ShadowMapTransparent
|
||||||
, transparent
|
, transparent
|
||||||
#endif
|
#endif
|
||||||
);
|
);
|
||||||
else if (index == 1) direct *= PCFCube(shadowMapPoint[1],
|
else if (index == 1) direct *= PCFCube(shadowMapPoint[1],
|
||||||
#ifdef _ShadowMapTransparent
|
#ifdef _ShadowMapTransparent
|
||||||
shadowMapPointTransparent[1],
|
shadowMapPointTransparent[1],
|
||||||
#endif
|
#endif
|
||||||
ld, -l, bias, lightProj, n
|
ld, -l, bias, lightProj, n
|
||||||
#ifdef _ShadowMapTransparent
|
#ifdef _ShadowMapTransparent
|
||||||
, transparent
|
, transparent
|
||||||
#endif
|
#endif
|
||||||
);
|
);
|
||||||
else if (index == 2) direct *= PCFCube(shadowMapPoint[2],
|
else if (index == 2) direct *= PCFCube(shadowMapPoint[2],
|
||||||
#ifdef _ShadowMapTransparent
|
#ifdef _ShadowMapTransparent
|
||||||
shadowMapPointTransparent[2],
|
shadowMapPointTransparent[2],
|
||||||
#endif
|
#endif
|
||||||
ld, -l, bias, lightProj, n
|
ld, -l, bias, lightProj, n
|
||||||
#ifdef _ShadowMapTransparent
|
#ifdef _ShadowMapTransparent
|
||||||
, transparent
|
, transparent
|
||||||
#endif
|
#endif
|
||||||
);
|
);
|
||||||
else if (index == 3) direct *= PCFCube(shadowMapPoint[3],
|
else if (index == 3) direct *= PCFCube(shadowMapPoint[3],
|
||||||
#ifdef _ShadowMapTransparent
|
#ifdef _ShadowMapTransparent
|
||||||
shadowMapPointTransparent[3],
|
shadowMapPointTransparent[3],
|
||||||
#endif
|
#endif
|
||||||
ld, -l, bias, lightProj, n
|
ld, -l, bias, lightProj, n
|
||||||
#ifdef _ShadowMapTransparent
|
#ifdef _ShadowMapTransparent
|
||||||
, transparent
|
, transparent
|
||||||
#endif
|
#endif
|
||||||
);
|
);
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
@ -445,61 +445,62 @@ vec3 sampleLightVoxels(const vec3 p, const vec3 n, const vec3 v, const float dot
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
direct *= attenuate(distance(p, lp));
|
direct *= attenuate(distance(p, lp));
|
||||||
direct *= lightCol;
|
// CRITICAL: Clamp light color to prevent extreme HDR values causing white sphere artifacts
|
||||||
|
direct *= min(lightCol, vec3(100.0));
|
||||||
|
|
||||||
#ifdef _LTC
|
#ifdef _LTC
|
||||||
#ifdef _ShadowMap
|
#ifdef _ShadowMap
|
||||||
if (receiveShadow) {
|
if (receiveShadow) {
|
||||||
#ifdef _SinglePoint
|
#ifdef _SinglePoint
|
||||||
vec4 lPos = LWVPSpot[0] * vec4(p + n * bias * 10, 1.0);
|
vec4 lPos = LWVPSpot[0] * vec4(p + n * bias * 2, 1.0);
|
||||||
direct *= shadowTest(shadowMapSpot[0],
|
direct *= shadowTest(shadowMapSpot[0],
|
||||||
#ifdef _ShadowMapTransparent
|
#ifdef _ShadowMapTransparent
|
||||||
shadowMapSpotTransparent[0],
|
shadowMapSpotTransparent[0],
|
||||||
#endif
|
#endif
|
||||||
lPos.xyz / lPos.w, bias
|
lPos.xyz / lPos.w, bias
|
||||||
#ifdef _ShadowMapTransparent
|
#ifdef _ShadowMapTransparent
|
||||||
, transparent
|
, transparent
|
||||||
#endif
|
#endif
|
||||||
);
|
);
|
||||||
#endif
|
#endif
|
||||||
#ifdef _Clusters
|
#ifdef _Clusters
|
||||||
vec4 lPos = LWVPSpot[index] * vec4(p + n * bias * 10, 1.0);
|
vec4 lPos = LWVPSpot[index] * vec4(p + n * bias * 2, 1.0);
|
||||||
if (index == 0) direct *= shadowTest(shadowMapSpot[0],
|
if (index == 0) direct *= shadowTest(shadowMapSpot[0],
|
||||||
#ifdef _ShadowMapTransparent
|
#ifdef _ShadowMapTransparent
|
||||||
shadowMapSpotTransparent[0],
|
shadowMapSpotTransparent[0],
|
||||||
#endif
|
#endif
|
||||||
lPos.xyz / lPos.w, bias
|
lPos.xyz / lPos.w, bias
|
||||||
#ifdef _ShadowMapTransparent
|
#ifdef _ShadowMapTransparent
|
||||||
, transparent
|
, transparent
|
||||||
#endif
|
#endif
|
||||||
);
|
);
|
||||||
else if (index == 1) direct *= shadowTest(shadowMapSpot[1],
|
else if (index == 1) direct *= shadowTest(shadowMapSpot[1],
|
||||||
#ifdef _ShadowMapTransparent
|
#ifdef _ShadowMapTransparent
|
||||||
shadowMapSpotTransparent[1],
|
shadowMapSpotTransparent[1],
|
||||||
#endif
|
#endif
|
||||||
lPos.xyz / lPos.w, bias
|
lPos.xyz / lPos.w, bias
|
||||||
#ifdef _ShadowMapTransparent
|
#ifdef _ShadowMapTransparent
|
||||||
, transparent
|
, transparent
|
||||||
#endif
|
#endif
|
||||||
);
|
);
|
||||||
else if (index == 2) direct *= shadowTest(shadowMapSpot[2],
|
else if (index == 2) direct *= shadowTest(shadowMapSpot[2],
|
||||||
#ifdef _ShadowMapTransparent
|
#ifdef _ShadowMapTransparent
|
||||||
shadowMapSpotTransparent[2],
|
shadowMapSpotTransparent[2],
|
||||||
#endif
|
#endif
|
||||||
lPos.xyz / lPos.w, bias
|
lPos.xyz / lPos.w, bias
|
||||||
#ifdef _ShadowMapTransparent
|
#ifdef _ShadowMapTransparent
|
||||||
, transparent
|
, transparent
|
||||||
#endif
|
#endif
|
||||||
);
|
);
|
||||||
else if (index == 3) direct *= shadowTest(shadowMapSpot[3],
|
else if (index == 3) direct *= shadowTest(shadowMapSpot[3],
|
||||||
#ifdef _ShadowMapTransparent
|
#ifdef _ShadowMapTransparent
|
||||||
shadowMapSpotTransparent[3],
|
shadowMapSpotTransparent[3],
|
||||||
#endif
|
#endif
|
||||||
lPos.xyz / lPos.w, bias
|
lPos.xyz / lPos.w, bias
|
||||||
#ifdef _ShadowMapTransparent
|
#ifdef _ShadowMapTransparent
|
||||||
, transparent
|
, transparent
|
||||||
#endif
|
#endif
|
||||||
);
|
);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@ -513,76 +514,76 @@ vec3 sampleLightVoxels(const vec3 p, const vec3 n, const vec3 v, const float dot
|
|||||||
#ifdef _ShadowMap
|
#ifdef _ShadowMap
|
||||||
if (receiveShadow) {
|
if (receiveShadow) {
|
||||||
#ifdef _SinglePoint
|
#ifdef _SinglePoint
|
||||||
vec4 lPos = LWVPSpotArray[0] * vec4(p + n * bias * 10, 1.0);
|
vec4 lPos = LWVPSpotArray[0] * vec4(p + n * bias * 2, 1.0);
|
||||||
direct *= shadowTest(shadowMapSpot[0],
|
direct *= shadowTest(shadowMapSpot[0],
|
||||||
#ifdef _ShadowMapTransparent
|
#ifdef _ShadowMapTransparent
|
||||||
shadowMapSpotTransparent[0],
|
shadowMapSpotTransparent[0],
|
||||||
#endif
|
#endif
|
||||||
lPos.xyz / lPos.w, bias
|
lPos.xyz / lPos.w, bias
|
||||||
#ifdef _ShadowMapTransparent
|
#ifdef _ShadowMapTransparent
|
||||||
, transparent
|
, transparent
|
||||||
#endif
|
#endif
|
||||||
);
|
);
|
||||||
#endif
|
#endif
|
||||||
#ifdef _Clusters
|
#ifdef _Clusters
|
||||||
vec4 lPos = LWVPSpotArray[index] * vec4(p + n * bias * 10, 1.0);
|
vec4 lPos = LWVPSpotArray[index] * vec4(p + n * bias * 2, 1.0);
|
||||||
#ifdef _ShadowMapAtlas
|
#ifdef _ShadowMapAtlas
|
||||||
direct *= shadowTest(
|
direct *= shadowTest(
|
||||||
#ifdef _ShadowMapTransparent
|
#ifdef _ShadowMapTransparent
|
||||||
#ifndef _SingleAtlas
|
#ifndef _SingleAtlas
|
||||||
shadowMapAtlasSpot, shadowMapAtlasSpotTransparent
|
shadowMapAtlasSpot, shadowMapAtlasSpotTransparent
|
||||||
#else
|
#else
|
||||||
shadowMapAtlas, shadowMapAtlasTransparent
|
shadowMapAtlas, shadowMapAtlasTransparent
|
||||||
#endif
|
#endif
|
||||||
#else
|
#else
|
||||||
#ifndef _SingleAtlas
|
#ifndef _SingleAtlas
|
||||||
shadowMapAtlasSpot
|
shadowMapAtlasSpot
|
||||||
#else
|
#else
|
||||||
shadowMapAtlas
|
shadowMapAtlas
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
, lPos.xyz / lPos.w, bias
|
, lPos.xyz / lPos.w, bias
|
||||||
#ifdef _ShadowMapTransparent
|
#ifdef _ShadowMapTransparent
|
||||||
, transparent
|
, transparent
|
||||||
#endif
|
#endif
|
||||||
);
|
);
|
||||||
#else
|
#else
|
||||||
if (index == 0) direct *= shadowTest(shadowMapSpot[0],
|
if (index == 0) direct *= shadowTest(shadowMapSpot[0],
|
||||||
#ifdef _ShadowMapTransparent
|
#ifdef _ShadowMapTransparent
|
||||||
shadowMapSpotTransparent[0],
|
shadowMapSpotTransparent[0],
|
||||||
#endif
|
#endif
|
||||||
lPos.xyz / lPos.w, bias
|
lPos.xyz / lPos.w, bias
|
||||||
#ifdef _ShadowMapTransparent
|
#ifdef _ShadowMapTransparent
|
||||||
, transparent
|
, transparent
|
||||||
#endif
|
#endif
|
||||||
);
|
);
|
||||||
else if (index == 1) direct *= shadowTest(shadowMapSpot[1],
|
else if (index == 1) direct *= shadowTest(shadowMapSpot[1],
|
||||||
#ifdef _ShadowMapTransparent
|
#ifdef _ShadowMapTransparent
|
||||||
shadowMapSpotTransparent[1],
|
shadowMapSpotTransparent[1],
|
||||||
#endif
|
#endif
|
||||||
lPos.xyz / lPos.w, bias
|
lPos.xyz / lPos.w, bias
|
||||||
#ifdef _ShadowMapTransparent
|
#ifdef _ShadowMapTransparent
|
||||||
, transparent
|
, transparent
|
||||||
#endif
|
#endif
|
||||||
);
|
);
|
||||||
else if (index == 2) direct *= shadowTest(shadowMapSpot[2],
|
else if (index == 2) direct *= shadowTest(shadowMapSpot[2],
|
||||||
#ifdef _ShadowMapTransparent
|
#ifdef _ShadowMapTransparent
|
||||||
shadowMapSpotTransparent[2],
|
shadowMapSpotTransparent[2],
|
||||||
#endif
|
#endif
|
||||||
lPos.xyz / lPos.w, bias
|
lPos.xyz / lPos.w, bias
|
||||||
#ifdef _ShadowMapTransparent
|
#ifdef _ShadowMapTransparent
|
||||||
, transparent
|
, transparent
|
||||||
#endif
|
#endif
|
||||||
);
|
);
|
||||||
else if (index == 3) direct *= shadowTest(shadowMapSpot[3],
|
else if (index == 3) direct *= shadowTest(shadowMapSpot[3],
|
||||||
#ifdef _ShadowMapTransparent
|
#ifdef _ShadowMapTransparent
|
||||||
shadowMapSpotTransparent[3],
|
shadowMapSpotTransparent[3],
|
||||||
#endif
|
#endif
|
||||||
lPos.xyz / lPos.w, bias
|
lPos.xyz / lPos.w, bias
|
||||||
#ifdef _ShadowMapTransparent
|
#ifdef _ShadowMapTransparent
|
||||||
, transparent
|
, transparent
|
||||||
#endif
|
#endif
|
||||||
);
|
);
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
@ -600,74 +601,74 @@ vec3 sampleLightVoxels(const vec3 p, const vec3 n, const vec3 v, const float dot
|
|||||||
#ifdef _SinglePoint
|
#ifdef _SinglePoint
|
||||||
#ifndef _Spot
|
#ifndef _Spot
|
||||||
direct *= PCFCube(shadowMapPoint[0],
|
direct *= PCFCube(shadowMapPoint[0],
|
||||||
#ifdef _ShadowMapTransparent
|
#ifdef _ShadowMapTransparent
|
||||||
shadowMapPointTransparent[0],
|
shadowMapPointTransparent[0],
|
||||||
#endif
|
#endif
|
||||||
ld, -l, bias, lightProj, n
|
ld, -l, bias, lightProj, n
|
||||||
#ifdef _ShadowMapTransparent
|
#ifdef _ShadowMapTransparent
|
||||||
, transparent
|
, transparent
|
||||||
#endif
|
#endif
|
||||||
);
|
);
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
#ifdef _Clusters
|
#ifdef _Clusters
|
||||||
#ifdef _ShadowMapAtlas
|
#ifdef _ShadowMapAtlas
|
||||||
direct *= PCFFakeCube(
|
direct *= PCFFakeCube(
|
||||||
#ifdef _ShadowMapTransparent
|
#ifdef _ShadowMapTransparent
|
||||||
#ifndef _SingleAtlas
|
#ifndef _SingleAtlas
|
||||||
shadowMapAtlasPoint, shadowMapAtlasPointTransparent
|
shadowMapAtlasPoint, shadowMapAtlasPointTransparent
|
||||||
#else
|
#else
|
||||||
shadowMapAtlas, shadowMapAtlasTransparent
|
shadowMapAtlas, shadowMapAtlasTransparent
|
||||||
#endif
|
#endif
|
||||||
#else
|
#else
|
||||||
#ifndef _SingleAtlas
|
#ifndef _SingleAtlas
|
||||||
shadowMapAtlasPoint
|
shadowMapAtlasPoint
|
||||||
#else
|
#else
|
||||||
shadowMapAtlas
|
shadowMapAtlas
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
, ld, -l, bias, lightProj, n, index
|
, ld, -l, bias, lightProj, n, index
|
||||||
#ifdef _ShadowMapTransparent
|
#ifdef _ShadowMapTransparent
|
||||||
, transparent
|
, transparent
|
||||||
#endif
|
#endif
|
||||||
);
|
);
|
||||||
#else
|
#else
|
||||||
if (index == 0) direct *= PCFCube(shadowMapPoint[0],
|
if (index == 0) direct *= PCFCube(shadowMapPoint[0],
|
||||||
#ifdef _ShadowMapTransparent
|
#ifdef _ShadowMapTransparent
|
||||||
shadowMapPointTransparent[0],
|
shadowMapPointTransparent[0],
|
||||||
#endif
|
#endif
|
||||||
ld, -l, bias, lightProj, n
|
ld, -l, bias, lightProj, n
|
||||||
#ifdef _ShadowMapTransparent
|
#ifdef _ShadowMapTransparent
|
||||||
, transparent
|
, transparent
|
||||||
#endif
|
#endif
|
||||||
);
|
);
|
||||||
else if (index == 1) direct *= PCFCube(shadowMapPoint[1],
|
else if (index == 1) direct *= PCFCube(shadowMapPoint[1],
|
||||||
#ifdef _ShadowMapTransparent
|
#ifdef _ShadowMapTransparent
|
||||||
shadowMapPointTransparent[1],
|
shadowMapPointTransparent[1],
|
||||||
#endif
|
#endif
|
||||||
ld, -l, bias, lightProj, n
|
ld, -l, bias, lightProj, n
|
||||||
#ifdef _ShadowMapTransparent
|
#ifdef _ShadowMapTransparent
|
||||||
, transparent
|
, transparent
|
||||||
#endif
|
#endif
|
||||||
);
|
);
|
||||||
else if (index == 2) direct *= PCFCube(shadowMapPoint[2],
|
else if (index == 2) direct *= PCFCube(shadowMapPoint[2],
|
||||||
#ifdef _ShadowMapTransparent
|
#ifdef _ShadowMapTransparent
|
||||||
shadowMapPointTransparent[2],
|
shadowMapPointTransparent[2],
|
||||||
#endif
|
#endif
|
||||||
ld, -l, bias, lightProj, n
|
ld, -l, bias, lightProj, n
|
||||||
#ifdef _ShadowMapTransparent
|
#ifdef _ShadowMapTransparent
|
||||||
, transparent
|
, transparent
|
||||||
#endif
|
#endif
|
||||||
);
|
);
|
||||||
else if (index == 3) direct *= PCFCube(shadowMapPoint[3],
|
else if (index == 3) direct *= PCFCube(shadowMapPoint[3],
|
||||||
#ifdef _ShadowMapTransparent
|
#ifdef _ShadowMapTransparent
|
||||||
shadowMapPointTransparent[3],
|
shadowMapPointTransparent[3],
|
||||||
#endif
|
#endif
|
||||||
ld, -l, bias, lightProj, n
|
ld, -l, bias, lightProj, n
|
||||||
#ifdef _ShadowMapTransparent
|
#ifdef _ShadowMapTransparent
|
||||||
, transparent
|
, transparent
|
||||||
#endif
|
#endif
|
||||||
);
|
);
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,6 +5,7 @@ uniform vec2 morphDataDim;
|
|||||||
uniform vec4 morphWeights[8];
|
uniform vec4 morphWeights[8];
|
||||||
|
|
||||||
void getMorphedVertex(vec2 uvCoord, inout vec3 A){
|
void getMorphedVertex(vec2 uvCoord, inout vec3 A){
|
||||||
|
vec3 totalDelta = vec3(0.0);
|
||||||
for(int i = 0; i<8; i++ )
|
for(int i = 0; i<8; i++ )
|
||||||
{
|
{
|
||||||
vec4 tempCoordY = vec4( uvCoord.y - (i * 4) * morphDataDim.y,
|
vec4 tempCoordY = vec4( uvCoord.y - (i * 4) * morphDataDim.y,
|
||||||
@ -13,21 +14,28 @@ void getMorphedVertex(vec2 uvCoord, inout vec3 A){
|
|||||||
uvCoord.y - (i * 4 + 3) * morphDataDim.y);
|
uvCoord.y - (i * 4 + 3) * morphDataDim.y);
|
||||||
|
|
||||||
vec3 morph = texture(morphDataPos, vec2(uvCoord.x, tempCoordY.x)).rgb * morphScaleOffset.x + morphScaleOffset.y;
|
vec3 morph = texture(morphDataPos, vec2(uvCoord.x, tempCoordY.x)).rgb * morphScaleOffset.x + morphScaleOffset.y;
|
||||||
A += morphWeights[i].x * morph;
|
totalDelta += morphWeights[i].x * morph;
|
||||||
|
|
||||||
morph = texture(morphDataPos, vec2(uvCoord.x, tempCoordY.y)).rgb * morphScaleOffset.x + morphScaleOffset.y;
|
morph = texture(morphDataPos, vec2(uvCoord.x, tempCoordY.y)).rgb * morphScaleOffset.x + morphScaleOffset.y;
|
||||||
A += morphWeights[i].y * morph;
|
totalDelta += morphWeights[i].y * morph;
|
||||||
|
|
||||||
morph = texture(morphDataPos, vec2(uvCoord.x, tempCoordY.z)).rgb * morphScaleOffset.x + morphScaleOffset.y;
|
morph = texture(morphDataPos, vec2(uvCoord.x, tempCoordY.z)).rgb * morphScaleOffset.x + morphScaleOffset.y;
|
||||||
A += morphWeights[i].z * morph;
|
totalDelta += morphWeights[i].z * morph;
|
||||||
|
|
||||||
morph = texture(morphDataPos, vec2(uvCoord.x, tempCoordY.w)).rgb * morphScaleOffset.x + morphScaleOffset.y;
|
morph = texture(morphDataPos, vec2(uvCoord.x, tempCoordY.w)).rgb * morphScaleOffset.x + morphScaleOffset.y;
|
||||||
A += morphWeights[i].w * morph;
|
totalDelta += morphWeights[i].w * morph;
|
||||||
}
|
}
|
||||||
|
//float deltaLength = length(totalDelta);
|
||||||
|
//if (deltaLength > 5.0) {
|
||||||
|
// clamp corrupted data
|
||||||
|
//totalDelta = normalize(totalDelta) * 5.0;
|
||||||
|
//}
|
||||||
|
A += totalDelta;
|
||||||
}
|
}
|
||||||
|
|
||||||
void getMorphedNormal(vec2 uvCoord, vec3 oldNor, inout vec3 morphNor){
|
void getMorphedNormal(vec2 uvCoord, vec3 oldNor, inout vec3 morphNor){
|
||||||
|
|
||||||
|
vec3 normalDelta = vec3(0.0);
|
||||||
for(int i = 0; i<8; i++ )
|
for(int i = 0; i<8; i++ )
|
||||||
{
|
{
|
||||||
vec4 tempCoordY = vec4( uvCoord.y - (i * 4) * morphDataDim.y,
|
vec4 tempCoordY = vec4( uvCoord.y - (i * 4) * morphDataDim.y,
|
||||||
@ -35,19 +43,11 @@ void getMorphedNormal(vec2 uvCoord, vec3 oldNor, inout vec3 morphNor){
|
|||||||
uvCoord.y - (i * 4 + 2) * morphDataDim.y,
|
uvCoord.y - (i * 4 + 2) * morphDataDim.y,
|
||||||
uvCoord.y - (i * 4 + 3) * morphDataDim.y);
|
uvCoord.y - (i * 4 + 3) * morphDataDim.y);
|
||||||
|
|
||||||
vec3 norm = oldNor + morphWeights[i].x * (texture(morphDataNor, vec2(uvCoord.x, tempCoordY.x)).rgb * 2.0 - 1.0);
|
normalDelta += morphWeights[i].x * (texture(morphDataNor, vec2(uvCoord.x, tempCoordY.x)).rgb * 2.0 - 1.0);
|
||||||
morphNor += norm;
|
normalDelta += morphWeights[i].y * (texture(morphDataNor, vec2(uvCoord.x, tempCoordY.y)).rgb * 2.0 - 1.0);
|
||||||
|
normalDelta += morphWeights[i].z * (texture(morphDataNor, vec2(uvCoord.x, tempCoordY.z)).rgb * 2.0 - 1.0);
|
||||||
norm = oldNor + morphWeights[i].y * (texture(morphDataNor, vec2(uvCoord.x, tempCoordY.y)).rgb * 2.0 - 1.0);
|
normalDelta += morphWeights[i].w * (texture(morphDataNor, vec2(uvCoord.x, tempCoordY.w)).rgb * 2.0 - 1.0);
|
||||||
morphNor += norm;
|
|
||||||
|
|
||||||
norm = oldNor + morphWeights[i].z * (texture(morphDataNor, vec2(uvCoord.x, tempCoordY.z)).rgb * 2.0 - 1.0);
|
|
||||||
morphNor += norm;
|
|
||||||
|
|
||||||
norm = oldNor + morphWeights[i].w * (texture(morphDataNor, vec2(uvCoord.x, tempCoordY.w)).rgb * 2.0 - 1.0);
|
|
||||||
morphNor += norm;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
morphNor = normalize(morphNor);
|
morphNor = normalize(oldNor + normalDelta);
|
||||||
}
|
}
|
||||||
1
leenkx/Shaders/std/shader_datas.lnx
Normal file
1
leenkx/Shaders/std/shader_datas.lnx
Normal file
@ -0,0 +1 @@
|
|||||||
|
<EFBFBD><EFBFBD>shader_datas<EFBFBD><EFBFBD><EFBFBD>name<EFBFBD>copy_pass<EFBFBD>contexts<EFBFBD><EFBFBD><EFBFBD>name<EFBFBD>copy_pass<EFBFBD>constants<EFBFBD><EFBFBD>texture_units<EFBFBD><EFBFBD><EFBFBD>name<EFBFBD>tex<EFBFBD>vertex_elements<EFBFBD><EFBFBD><EFBFBD>data<EFBFBD>float2<EFBFBD>name<EFBFBD>pos<EFBFBD>vertex_shader<EFBFBD>pass.vert<72>fragment_shader<65>pass_copy.frag<61>depth_write¬compare_mode<64>always<79>cull_mode<64>none<6E><65>name<6D>compositor_pass<73>contexts<74><73><EFBFBD>name<6D>compositor_pass<73>constants<74><73>texture_units<74><73><EFBFBD>name<6D>tex<65>vertex_elements<74><73><EFBFBD>data<74>float2<74>name<6D>pos<6F>vertex_shader<65>compositor_pass.vert<72>fragment_shader<65>compositor_pass.frag<61>depth_write¬compare_mode<64>always<79>cull_mode<64>none<6E><65>name<6D>deferred_light<68>contexts<74><73><EFBFBD>name<6D>deferred_light<68>constants<74><73><EFBFBD>type<70>mat4<74>name<6D>invVP<56>link<6E>_inverseViewProjectionMatrix<69><78>type<70>vec3<63>name<6D>eye<79>link<6E>_cameraPosition<6F><6E>type<70>float<61>name<6D>envmapStrength<74>link<6E>_envmapStrength<74><68>type<70>floats<74>name<6D>shirr<72>link<6E>_envmapIrradiance<63><65>type<70>int<6E>name<6D>envmapNumMipmaps<70>link<6E>_envmapNumMipmaps<70><73>type<70>vec2<63>name<6D>cameraProj<6F>link<6E>_cameraPlaneProj<6F><6A>type<70>vec3<63>name<6D>eyeLook<6F>link<6E>_cameraLook<6F><6B>type<70>vec2<63>name<6D>lightProj<6F>link<6E>_lightPlaneProj<6F><6A>type<70>vec3<63>name<6D>pointPos<6F>link<6E>_pointPosition<6F><6E>type<70>vec3<63>name<6D>pointCol<6F>link<6E>_pointColor<6F><72>type<70>float<61>name<6D>pointBias<61>link<6E>_pointShadowsBias<61>texture_units<74><73><EFBFBD>name<6D>gbufferD<72><44>name<6D>gbuffer0<72><30>name<6D>gbuffer1<72><31>name<6D>senvmapBrdf<64>link<6E>$brdf.png<6E><67>name<6D>senvmapRadiance<63>link<6E>_envmapRadiance<63><65>name<6D>shadowMapPoint[0]<5D>vertex_elements<74><73><EFBFBD>data<74>float2<74>name<6D>pos<6F>vertex_shader<65>pass_viewray.vert<72>fragment_shader<65>deferred_light.frag<61>color_attachments<74><73>RGBA64<36>depth_write¬compare_mode<64>always<79>cull_mode<64>none<6E><65>name<6D>water_pass<73>contexts<74><73><EFBFBD>name<6D>water_pass<73>constants<74><73><EFBFBD>type<70>mat4<74>name<6D>invVP<56><50>type<70>vec3<63>name<6D>eye<79>link<6E>_cameraPosition<6F><6E>type<70>float<61>name<6D>time<6D>link<6E>_time<6D><65>type<70>float<61>name<6D>holoOverallStrength<74><68>type<70>vec2<63>name<6D>cameraProj<6F>link<6E>_cameraPlaneProj<6F><6A>type<70>vec3<63>name<6D>eyeLook<6F>link<6E>_cameraLook<6F><6B>type<70>vec3<63>name<6D>ld<6C>link<6E>_lightDirection<6F>texture_units<74><73><EFBFBD>name<6D>tex<65><78>name<6D>gbufferD<72><44>name<6D>gbuffer0<72>vertex_elements<74><73><EFBFBD>data<74>float2<74>name<6D>pos<6F>vertex_shader<65>pass_viewray.vert<72>fragment_shader<65>water_pass.frag<61>depth_write¬compare_mode<64>always<79>cull_mode<64>none<6E>blend_source<63>source_alpha<68>blend_destination<6F>inverse_source_alpha<68>blend_operation<6F>add<64>alpha_blend_source<63>blend_one<6E>alpha_blend_destination<6F>blend_one<6E>alpha_blend_operation<6F>add<64><64>contexts<74><73><EFBFBD>name<6D>World_World<6C>depth_write¬compare_mode<64>less<73>cull_mode<64>clockwise<73>vertex_elements<74><73><EFBFBD>name<6D>pos<6F>data<74>float3<74><33>name<6D>nor<6F>data<74>float3<74>color_attachments<74><73>_HDR<44>texture_units<74><73>constants<74><73><EFBFBD>name<6D>SMVP<56>type<70>mat4<74>link<6E>_skydomeMatrix<69>vertex_shader<65>World_World.vert<72>fragment_shader<65>World_World.frag<61>name<6D>World_World
|
||||||
@ -23,6 +23,59 @@ uniform vec2 smSizeUniform;
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef _ShadowMapAtlas
|
#ifdef _ShadowMapAtlas
|
||||||
|
// PCF that clamps samples to tile boundaries to prevent bleeding
|
||||||
|
vec3 PCFTileAware(sampler2DShadow shadowMap,
|
||||||
|
#ifdef _ShadowMapTransparent
|
||||||
|
sampler2D shadowMapTransparent,
|
||||||
|
#endif
|
||||||
|
const vec2 uv, const float compare, const vec2 smSize,
|
||||||
|
const vec2 tileMin, const vec2 tileMax
|
||||||
|
#ifdef _ShadowMapTransparent
|
||||||
|
, const bool transparent
|
||||||
|
#endif
|
||||||
|
) {
|
||||||
|
vec3 result = vec3(0.0);
|
||||||
|
vec2 offset;
|
||||||
|
|
||||||
|
offset = vec2(-1.0, -1.0) / smSize;
|
||||||
|
result.x = texture(shadowMap, vec3(clamp(uv + offset, tileMin, tileMax), compare));
|
||||||
|
|
||||||
|
offset = vec2(-1.0, 0.0) / smSize;
|
||||||
|
result.x += texture(shadowMap, vec3(clamp(uv + offset, tileMin, tileMax), compare));
|
||||||
|
|
||||||
|
offset = vec2(-1.0, 1.0) / smSize;
|
||||||
|
result.x += texture(shadowMap, vec3(clamp(uv + offset, tileMin, tileMax), compare));
|
||||||
|
|
||||||
|
offset = vec2(0.0, -1.0) / smSize;
|
||||||
|
result.x += texture(shadowMap, vec3(clamp(uv + offset, tileMin, tileMax), compare));
|
||||||
|
|
||||||
|
result.x += texture(shadowMap, vec3(uv, compare));
|
||||||
|
|
||||||
|
offset = vec2(0.0, 1.0) / smSize;
|
||||||
|
result.x += texture(shadowMap, vec3(clamp(uv + offset, tileMin, tileMax), compare));
|
||||||
|
|
||||||
|
offset = vec2(1.0, -1.0) / smSize;
|
||||||
|
result.x += texture(shadowMap, vec3(clamp(uv + offset, tileMin, tileMax), compare));
|
||||||
|
|
||||||
|
offset = vec2(1.0, 0.0) / smSize;
|
||||||
|
result.x += texture(shadowMap, vec3(clamp(uv + offset, tileMin, tileMax), compare));
|
||||||
|
|
||||||
|
offset = vec2(1.0, 1.0) / smSize;
|
||||||
|
result.x += texture(shadowMap, vec3(clamp(uv + offset, tileMin, tileMax), compare));
|
||||||
|
|
||||||
|
result = result.xxx / 9.0;
|
||||||
|
|
||||||
|
#ifdef _ShadowMapTransparent
|
||||||
|
if (transparent == false) {
|
||||||
|
vec4 shadowmap_transparent = texture(shadowMapTransparent, uv);
|
||||||
|
if (shadowmap_transparent.a < compare)
|
||||||
|
result *= shadowmap_transparent.rgb;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
// https://www.khronos.org/registry/OpenGL/specs/gl/glspec20.pdf // p:168
|
// https://www.khronos.org/registry/OpenGL/specs/gl/glspec20.pdf // p:168
|
||||||
// https://www.gamedev.net/forums/topic/687535-implementing-a-cube-map-lookup-function/5337472/
|
// https://www.gamedev.net/forums/topic/687535-implementing-a-cube-map-lookup-function/5337472/
|
||||||
vec2 sampleCube(vec3 dir, out int faceIndex) {
|
vec2 sampleCube(vec3 dir, out int faceIndex) {
|
||||||
@ -251,28 +304,69 @@ vec3 PCFFakeCube(sampler2DShadow shadowMap,
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (any(lessThan(uvtiled, vec2(0.0))) || any(greaterThan(uvtiled, vec2(1.0)))) {
|
if (any(lessThan(uvtiled, vec2(0.0))) || any(greaterThan(uvtiled, vec2(1.0)))) {
|
||||||
return vec3(1.0); // Or handle edge cases differently
|
return vec3(1.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
vec3 result = vec3(0.0);
|
vec3 result = vec3(0.0);
|
||||||
// In PCFFakeCube(), modify the sampling pattern to be more robust:
|
result.x += texture(shadowMap, vec3(uvtiled, compare));
|
||||||
const vec2 offsets[9] = vec2[](
|
// soft shadowing
|
||||||
vec2(0, 0),
|
int newFaceIndex = 0;
|
||||||
vec2(1, 0), vec2(-1, 0), vec2(0, 1), vec2(0, -1),
|
uvtiled = transformOffsetedUV(faceIndex, newFaceIndex, vec2(uv + (vec2(-1.0, 0.0) / smSize)));
|
||||||
vec2(1, 1), vec2(-1, 1), vec2(1, -1), vec2(-1, -1)
|
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;
|
||||||
|
#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(-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;
|
||||||
|
#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(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(1.0, 0.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(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));
|
||||||
|
|
||||||
for (int i = 0; i < 9; i++) {
|
|
||||||
vec2 sampleUV = uv + offsets[i] / smSize;
|
|
||||||
int newFaceIndex;
|
|
||||||
vec2 transformedUV = transformOffsetedUV(faceIndex, newFaceIndex, sampleUV);
|
|
||||||
pointLightTile = pointLightDataArray[lightIndex + newFaceIndex];
|
|
||||||
uvtiled = pointLightTile.z * transformedUV + pointLightTile.xy;
|
|
||||||
#ifdef _FlipY
|
|
||||||
uvtiled.y = 1.0 - uvtiled.y;
|
|
||||||
#endif
|
|
||||||
result.x += texture(shadowMap, vec3(uvtiled, compare));
|
|
||||||
}
|
|
||||||
result = result.xxx / 9.0;
|
result = result.xxx / 9.0;
|
||||||
|
|
||||||
pointLightTile = pointLightDataArray[lightIndex + faceIndex]; // x: tile X offset, y: tile Y offset, z: tile size relative to atlas
|
pointLightTile = pointLightDataArray[lightIndex + faceIndex]; // x: tile X offset, y: tile Y offset, z: tile size relative to atlas
|
||||||
@ -293,30 +387,55 @@ vec3 PCFFakeCube(sampler2DShadow shadowMap,
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef _ShadowMapAtlas
|
||||||
|
uniform vec4 tileBounds;
|
||||||
|
#endif
|
||||||
|
|
||||||
vec3 shadowTest(sampler2DShadow shadowMap,
|
vec3 shadowTest(sampler2DShadow shadowMap,
|
||||||
#ifdef _ShadowMapTransparent
|
#ifdef _ShadowMapTransparent
|
||||||
sampler2D shadowMapTransparent,
|
sampler2D shadowMapTransparent,
|
||||||
#endif
|
#endif
|
||||||
const vec3 lPos, const float shadowsBias
|
const vec3 lPos, const float shadowsBias
|
||||||
#ifdef _ShadowMapTransparent
|
#ifdef _ShadowMapTransparent
|
||||||
, const bool transparent
|
, const bool transparent
|
||||||
#endif
|
#endif
|
||||||
) {
|
) {
|
||||||
|
if (lPos.x < 0.0 || lPos.y < 0.0 || lPos.x > 1.0 || lPos.y > 1.0) return vec3(1.0);
|
||||||
|
|
||||||
|
#ifdef _ShadowMapAtlas
|
||||||
|
// use tile PCF
|
||||||
|
#ifdef _SMSizeUniform
|
||||||
|
vec2 smSizeAtlas = smSizeUniform;
|
||||||
|
#else
|
||||||
|
const vec2 smSizeAtlas = shadowmapSize;
|
||||||
|
#endif
|
||||||
|
return PCFTileAware(shadowMap,
|
||||||
|
#ifdef _ShadowMapTransparent
|
||||||
|
shadowMapTransparent,
|
||||||
|
#endif
|
||||||
|
lPos.xy, lPos.z - shadowsBias, smSizeAtlas,
|
||||||
|
tileBounds.xy, tileBounds.zw
|
||||||
|
#ifdef _ShadowMapTransparent
|
||||||
|
, transparent
|
||||||
|
#endif
|
||||||
|
);
|
||||||
|
#else
|
||||||
|
// use PCF for non-atlas shadows
|
||||||
#ifdef _SMSizeUniform
|
#ifdef _SMSizeUniform
|
||||||
vec2 smSize = smSizeUniform;
|
vec2 smSize = smSizeUniform;
|
||||||
#else
|
#else
|
||||||
const vec2 smSize = shadowmapSize;
|
const vec2 smSize = shadowmapSize;
|
||||||
#endif
|
#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,
|
return PCF(shadowMap,
|
||||||
#ifdef _ShadowMapTransparent
|
#ifdef _ShadowMapTransparent
|
||||||
shadowMapTransparent,
|
shadowMapTransparent,
|
||||||
#endif
|
#endif
|
||||||
lPos.xy, lPos.z - shadowsBias, smSize
|
lPos.xy, lPos.z - shadowsBias, smSize
|
||||||
#ifdef _ShadowMapTransparent
|
#ifdef _ShadowMapTransparent
|
||||||
, transparent
|
, transparent
|
||||||
#endif
|
#endif
|
||||||
);
|
);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef _CSM
|
#ifdef _CSM
|
||||||
@ -349,14 +468,14 @@ mat4 getCascadeMat(const float d, out int casi, out int casIndex) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
vec3 shadowTestCascade(sampler2DShadow shadowMap,
|
vec3 shadowTestCascade(sampler2DShadow shadowMap,
|
||||||
#ifdef _ShadowMapTransparent
|
#ifdef _ShadowMapTransparent
|
||||||
sampler2D shadowMapTransparent,
|
sampler2D shadowMapTransparent,
|
||||||
#endif
|
#endif
|
||||||
const vec3 eye, const vec3 p, const float shadowsBias
|
const vec3 eye, const vec3 p, const float shadowsBias
|
||||||
#ifdef _ShadowMapTransparent
|
#ifdef _ShadowMapTransparent
|
||||||
, const bool transparent
|
, const bool transparent
|
||||||
#endif
|
#endif
|
||||||
) {
|
) {
|
||||||
#ifdef _SMSizeUniform
|
#ifdef _SMSizeUniform
|
||||||
vec2 smSize = smSizeUniform;
|
vec2 smSize = smSizeUniform;
|
||||||
#else
|
#else
|
||||||
@ -372,14 +491,14 @@ vec3 shadowTestCascade(sampler2DShadow shadowMap,
|
|||||||
|
|
||||||
vec3 visibility = vec3(1.0);
|
vec3 visibility = vec3(1.0);
|
||||||
if (lPos.w > 0.0) visibility = PCF(shadowMap,
|
if (lPos.w > 0.0) visibility = PCF(shadowMap,
|
||||||
#ifdef _ShadowMapTransparent
|
#ifdef _ShadowMapTransparent
|
||||||
shadowMapTransparent,
|
shadowMapTransparent,
|
||||||
#endif
|
#endif
|
||||||
lPos.xy, lPos.z - shadowsBias, smSize
|
lPos.xy, lPos.z - shadowsBias, smSize
|
||||||
#ifdef _ShadowMapTransparent
|
#ifdef _ShadowMapTransparent
|
||||||
, transparent
|
, transparent
|
||||||
#endif
|
#endif
|
||||||
);
|
);
|
||||||
|
|
||||||
// Blend cascade
|
// Blend cascade
|
||||||
// https://github.com/TheRealMJP/Shadows
|
// https://github.com/TheRealMJP/Shadows
|
||||||
@ -398,15 +517,16 @@ vec3 shadowTestCascade(sampler2DShadow shadowMap,
|
|||||||
vec4 lPos2 = LWVP2 * vec4(p, 1.0);
|
vec4 lPos2 = LWVP2 * vec4(p, 1.0);
|
||||||
lPos2.xyz /= lPos2.w;
|
lPos2.xyz /= lPos2.w;
|
||||||
vec3 visibility2 = vec3(1.0);
|
vec3 visibility2 = vec3(1.0);
|
||||||
|
// use lPos2 coordinates for second cascade, not lPos
|
||||||
if (lPos2.w > 0.0) visibility2 = PCF(shadowMap,
|
if (lPos2.w > 0.0) visibility2 = PCF(shadowMap,
|
||||||
#ifdef _ShadowMapTransparent
|
#ifdef _ShadowMapTransparent
|
||||||
shadowMapTransparent,
|
shadowMapTransparent,
|
||||||
#endif
|
#endif
|
||||||
lPos.xy, lPos.z - shadowsBias, smSize
|
lPos2.xy, lPos2.z - shadowsBias, smSize
|
||||||
#ifdef _ShadowMapTransparent
|
#ifdef _ShadowMapTransparent
|
||||||
, transparent
|
, transparent
|
||||||
#endif
|
#endif
|
||||||
);
|
);
|
||||||
|
|
||||||
float lerpAmt = smoothstep(0.0, blendThres, splitDist);
|
float lerpAmt = smoothstep(0.0, blendThres, splitDist);
|
||||||
return mix(visibility2, visibility, lerpAmt);
|
return mix(visibility2, visibility, lerpAmt);
|
||||||
|
|||||||
56
leenkx/Shaders/std/voxels_constants.glsl
Normal file
56
leenkx/Shaders/std/voxels_constants.glsl
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2024 Turánszki János
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const int DIFFUSE_CONE_COUNT = 16;
|
||||||
|
const float DIFFUSE_CONE_APERTURE = radians(45.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 BayerMatrix8[8][8] =
|
||||||
|
{
|
||||||
|
{ 1.0 / 65.0, 49.0 / 65.0, 13.0 / 65.0, 61.0 / 65.0, 4.0 / 65.0, 52.0 / 65.0, 16.0 / 65.0, 64.0 / 65.0 },
|
||||||
|
{ 33.0 / 65.0, 17.0 / 65.0, 45.0 / 65.0, 29.0 / 65.0, 36.0 / 65.0, 20.0 / 65.0, 48.0 / 65.0, 32.0 / 65.0 },
|
||||||
|
{ 9.0 / 65.0, 57.0 / 65.0, 5.0 / 65.0, 53.0 / 65.0, 12.0 / 65.0, 60.0 / 65.0, 8.0 / 65.0, 56.0 / 65.0 },
|
||||||
|
{ 41.0 / 65.0, 25.0 / 65.0, 37.0 / 65.0, 21.0 / 65.0, 44.0 / 65.0, 28.0 / 65.0, 40.0 / 65.0, 24.0 / 65.0 },
|
||||||
|
{ 3.0 / 65.0, 51.0 / 65.0, 15.0 / 65.0, 63.0 / 65.0, 2.0 / 65.0, 50.0 / 65.0, 14.0 / 65.0, 62.0 / 65.0 },
|
||||||
|
{ 35.0 / 65.0, 19.0 / 65.0, 47.0 / 65.0, 31.0 / 65.0, 34.0 / 65.0, 18.0 / 65.0, 46.0 / 65.0, 30.0 / 65.0 },
|
||||||
|
{ 11.0 / 65.0, 59.0 / 65.0, 7.0 / 65.0, 55.0 / 65.0, 10.0 / 65.0, 58.0 / 65.0, 6.0 / 65.0, 54.0 / 65.0 },
|
||||||
|
{ 43.0 / 65.0, 27.0 / 65.0, 39.0 / 65.0, 23.0 / 65.0, 42.0 / 65.0, 26.0 / 65.0, 38.0 / 65.0, 22.0 / 65.0 }
|
||||||
|
};
|
||||||
@ -13,32 +13,80 @@ out vec4 fragColor;
|
|||||||
|
|
||||||
const float SMAA_REPROJECTION_WEIGHT_SCALE = 30.0;
|
const float SMAA_REPROJECTION_WEIGHT_SCALE = 30.0;
|
||||||
|
|
||||||
|
// TODO: Move to a utility
|
||||||
|
bool isInvalidValue(float v) {
|
||||||
|
return (v != v) || (v > 65000.0) || (v < -65000.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool hasInvalidValues(vec3 v) {
|
||||||
|
return isInvalidValue(v.x) || isInvalidValue(v.y) || isInvalidValue(v.z);
|
||||||
|
}
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
vec4 current = textureLod(tex, texCoord, 0.0);
|
vec4 current = textureLod(tex, texCoord, 0.0);
|
||||||
|
|
||||||
|
current.rgb = clamp(current.rgb, vec3(0.0), vec3(10.0));
|
||||||
|
current.a = clamp(current.a, 0.0, 1.0);
|
||||||
|
if (hasInvalidValues(current.rgb)) {
|
||||||
|
current = vec4(0.0, 0.0, 0.0, 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef _Veloc
|
#ifdef _Veloc
|
||||||
// Velocity is assumed to be calculated for motion blur, so we need to inverse it for reprojection
|
// Velocity is assumed to be calculated for motion blur, so we need to inverse it for reprojection
|
||||||
vec2 velocity = -textureLod(sveloc, texCoord, 0.0).rg;
|
vec2 velocity = -textureLod(sveloc, texCoord, 0.0).rg;
|
||||||
|
|
||||||
|
velocity = clamp(velocity, vec2(-1.0), vec2(1.0));
|
||||||
|
if (isInvalidValue(velocity.x) || isInvalidValue(velocity.y)) {
|
||||||
|
velocity = vec2(0.0);
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef _InvY
|
#ifdef _InvY
|
||||||
velocity.y = -velocity.y;
|
velocity.y = -velocity.y;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Reproject current coordinates and fetch previous pixel
|
// Reproject current coordinates and fetch previous pixel
|
||||||
vec4 previous = textureLod(tex2, texCoord + velocity, 0.0);
|
vec2 prevCoord = texCoord + velocity;
|
||||||
|
prevCoord = clamp(prevCoord, vec2(0.0), vec2(1.0));
|
||||||
|
vec4 previous = textureLod(tex2, prevCoord, 0.0);
|
||||||
|
|
||||||
|
previous.rgb = clamp(previous.rgb, vec3(0.0), vec3(10.0));
|
||||||
|
previous.a = clamp(previous.a, 0.0, 1.0);
|
||||||
|
if (hasInvalidValues(previous.rgb)) {
|
||||||
|
previous = current; // Fallback to current frame if previous is corrupted
|
||||||
|
}
|
||||||
|
|
||||||
// Attenuate the previous pixel if the velocity is different
|
|
||||||
#ifdef _SMAA
|
#ifdef _SMAA
|
||||||
float delta = abs(current.a * current.a - previous.a * previous.a) / 5.0;
|
float currentAlpha = clamp(current.a, 0.0, 1.0);
|
||||||
|
float previousAlpha = clamp(previous.a, 0.0, 1.0);
|
||||||
|
float delta = abs(currentAlpha * currentAlpha - previousAlpha * previousAlpha) / 5.0;
|
||||||
|
delta = clamp(delta, 0.0, 1.0); // Ensure delta is in valid range
|
||||||
#else
|
#else
|
||||||
const float delta = 0.0;
|
const float delta = 0.0;
|
||||||
#endif
|
#endif
|
||||||
float weight = 0.5 * clamp(1.0 - sqrt(delta) * SMAA_REPROJECTION_WEIGHT_SCALE, 0.0, 1.0);
|
|
||||||
|
|
||||||
// Blend the pixels according to the calculated weight:
|
float weight = 0.5 * clamp(1.0 - sqrt(max(delta, 0.0)) * SMAA_REPROJECTION_WEIGHT_SCALE, 0.0, 1.0);
|
||||||
fragColor = vec4(mix(current.rgb, previous.rgb, weight), 1.0);
|
|
||||||
|
vec3 blended = mix(current.rgb, previous.rgb, weight);
|
||||||
|
blended = clamp(blended, vec3(0.0), vec3(10.0));
|
||||||
|
if (hasInvalidValues(blended)) {
|
||||||
|
blended = current.rgb;
|
||||||
|
}
|
||||||
|
|
||||||
|
fragColor = vec4(blended, 1.0);
|
||||||
#else
|
#else
|
||||||
vec4 previous = textureLod(tex2, texCoord, 0.0);
|
vec4 previous = textureLod(tex2, texCoord, 0.0);
|
||||||
fragColor = vec4(mix(current.rgb, previous.rgb, 0.5), 1.0);
|
|
||||||
|
previous.rgb = clamp(previous.rgb, vec3(0.0), vec3(10.0));
|
||||||
|
if (hasInvalidValues(previous.rgb)) {
|
||||||
|
previous.rgb = current.rgb;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3 blended = mix(current.rgb, previous.rgb, 0.5);
|
||||||
|
blended = clamp(blended, vec3(0.0), vec3(10.0));
|
||||||
|
if (hasInvalidValues(blended)) {
|
||||||
|
blended = current.rgb;
|
||||||
|
}
|
||||||
|
|
||||||
|
fragColor = vec4(blended, 1.0);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|||||||
@ -106,8 +106,11 @@ void main() {
|
|||||||
#ifdef _Brdf
|
#ifdef _Brdf
|
||||||
vec2 envBRDF = texelFetch(senvmapBrdf, ivec2(vec2(dotNV, 1.0 - roughness) * 256.0), 0).xy;
|
vec2 envBRDF = texelFetch(senvmapBrdf, ivec2(vec2(dotNV, 1.0 - roughness) * 256.0), 0).xy;
|
||||||
vec3 F = f0 * envBRDF.x + envBRDF.y;
|
vec3 F = f0 * envBRDF.x + envBRDF.y;
|
||||||
|
#else
|
||||||
|
vec3 F = f0;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
// Envmap
|
// Envmap
|
||||||
#ifdef _Irr
|
#ifdef _Irr
|
||||||
vec4 shPacked[7];
|
vec4 shPacked[7];
|
||||||
|
|||||||
@ -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));
|
||||||
|
}
|
||||||
@ -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));
|
||||||
|
}
|
||||||
@ -74,8 +74,9 @@ void main() {
|
|||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
mat3 TBN = mat3(1.0);
|
int nor_count = 0;
|
||||||
vec3 avgNormal = vec3(0.0);
|
vec3 avgNormal = vec3(0.0);
|
||||||
|
mat3 TBN = mat3(0.0);
|
||||||
|
|
||||||
for (int i = 0; i < 6 + DIFFUSE_CONE_COUNT; i++)
|
for (int i = 0; i < 6 + DIFFUSE_CONE_COUNT; i++)
|
||||||
{
|
{
|
||||||
@ -116,10 +117,18 @@ void main() {
|
|||||||
N.g = float(imageLoad(voxels, src + ivec3(0, 0, voxelgiResolution.x * 8))) / 255;
|
N.g = float(imageLoad(voxels, src + ivec3(0, 0, voxelgiResolution.x * 8))) / 255;
|
||||||
N /= count;
|
N /= count;
|
||||||
N = decode_oct(N.rg * 2.0 - 1.0);
|
N = decode_oct(N.rg * 2.0 - 1.0);
|
||||||
avgNormal += N;
|
|
||||||
|
|
||||||
|
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)
|
if (i == 5)
|
||||||
TBN = makeTangentBasis(normalize(avgNormal));
|
{
|
||||||
|
avgNormal = normalize(avgNormal);
|
||||||
|
TBN = makeTangentBasis(avgNormal);
|
||||||
|
}
|
||||||
|
|
||||||
vec3 envl = vec3(0.0);
|
vec3 envl = vec3(0.0);
|
||||||
envl.r = float(imageLoad(voxels, src + ivec3(0, 0, voxelgiResolution.x * 9))) / 255;
|
envl.r = float(imageLoad(voxels, src + ivec3(0, 0, voxelgiResolution.x * 9))) / 255;
|
||||||
|
|||||||
@ -75,17 +75,16 @@ vec4 binarySearch(vec3 dir) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
vec4 rayCast(vec3 dir) {
|
vec4 rayCast(vec3 dir) {
|
||||||
float ddepth;
|
#ifdef _CPostprocess
|
||||||
dir *= ss_refractionRayStep;
|
dir *= PPComp9.x;
|
||||||
for (int i = 0; i < maxSteps; i++) {
|
#else
|
||||||
hitCoord += dir;
|
dir *= ssrRayStep;
|
||||||
ddepth = getDeltaDepth(hitCoord);
|
#endif
|
||||||
if (ddepth > 0.0)
|
for (int i = 0; i < maxSteps; i++) {
|
||||||
return binarySearch(dir);
|
hitCoord += dir;
|
||||||
}
|
if (getDeltaDepth(hitCoord) > 0.0) return binarySearch(dir);
|
||||||
// No hit — fallback to projecting the ray to UV space
|
}
|
||||||
vec2 fallbackUV = getProjectedCoord(hitCoord);
|
return vec4(0.0);
|
||||||
return vec4(fallbackUV, 0.0, 0.5); // We set .w lower to indicate fallback
|
|
||||||
}
|
}
|
||||||
#endif //SSR
|
#endif //SSR
|
||||||
|
|
||||||
|
|||||||
@ -53,7 +53,38 @@ class App {
|
|||||||
static function update() {
|
static function update() {
|
||||||
if (Scene.active == null || !Scene.active.ready) return;
|
if (Scene.active == null || !Scene.active.ready) return;
|
||||||
|
|
||||||
|
// VR is handling it so we prevent double updates
|
||||||
|
// TODO: avoid js.Syntax
|
||||||
|
#if (kha_webgl && lnx_vr)
|
||||||
|
var vrActive = false;
|
||||||
|
js.Syntax.code("
|
||||||
|
if (typeof kha !== 'undefined' && kha.vr && kha.vr.VrInterface) {
|
||||||
|
const vr = kha.vr.VrInterface.instance;
|
||||||
|
if (vr && vr.IsPresenting && vr.IsPresenting()) {
|
||||||
|
{0} = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
", vrActive);
|
||||||
|
if (vrActive) return;
|
||||||
|
#end
|
||||||
|
|
||||||
iron.system.Time.update();
|
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 (pauseUpdates) return;
|
||||||
|
|
||||||
#if lnx_debug
|
#if lnx_debug
|
||||||
@ -98,22 +129,6 @@ class App {
|
|||||||
for (cb in endFrameCallbacks) cb();
|
for (cb in endFrameCallbacks) cb();
|
||||||
updateTime = kha.Scheduler.realTime() - startTime;
|
updateTime = kha.Scheduler.realTime() - startTime;
|
||||||
#end
|
#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>) {
|
static function render(frames: Array<kha.Framebuffer>) {
|
||||||
@ -138,6 +153,21 @@ class App {
|
|||||||
traitInits.splice(0, traitInits.length);
|
traitInits.splice(0, traitInits.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// skip for XR callback to handle rendering
|
||||||
|
// TODO: avoid js Syntax
|
||||||
|
#if (kha_webgl && lnx_vr)
|
||||||
|
var vrActive = false;
|
||||||
|
js.Syntax.code("
|
||||||
|
if (typeof kha !== 'undefined' && kha.vr && kha.vr.VrInterface) {
|
||||||
|
const vr = kha.vr.VrInterface.instance;
|
||||||
|
if (vr && vr.IsPresenting && vr.IsPresenting()) {
|
||||||
|
{0} = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
", vrActive);
|
||||||
|
|
||||||
|
if (!vrActive) {
|
||||||
|
#end
|
||||||
Scene.active.renderFrame(frame.g4);
|
Scene.active.renderFrame(frame.g4);
|
||||||
|
|
||||||
for (f in traitRenders) {
|
for (f in traitRenders) {
|
||||||
@ -146,6 +176,10 @@ class App {
|
|||||||
|
|
||||||
render2D(frame);
|
render2D(frame);
|
||||||
|
|
||||||
|
#if (kha_webgl && lnx_vr)
|
||||||
|
}
|
||||||
|
#end
|
||||||
|
|
||||||
#if lnx_debug
|
#if lnx_debug
|
||||||
renderPathTime = kha.Scheduler.realTime() - startTime;
|
renderPathTime = kha.Scheduler.realTime() - startTime;
|
||||||
#end
|
#end
|
||||||
|
|||||||
@ -18,10 +18,44 @@ import iron.object.LightObject;
|
|||||||
import iron.object.MeshObject;
|
import iron.object.MeshObject;
|
||||||
import iron.object.Uniforms;
|
import iron.object.Uniforms;
|
||||||
import iron.object.Clipmap;
|
import iron.object.Clipmap;
|
||||||
|
#if lnx_vr
|
||||||
|
import iron.math.Vec4;
|
||||||
|
import iron.math.Mat4;
|
||||||
|
import iron.math.Quat;
|
||||||
|
#end
|
||||||
|
|
||||||
class RenderPath {
|
class RenderPath {
|
||||||
|
|
||||||
public static var active: RenderPath;
|
public static var active: RenderPath;
|
||||||
|
#if lnx_vr
|
||||||
|
static var vrSimulateMode: Bool = false;
|
||||||
|
static var vrCameraOffsetSet:Bool = false;
|
||||||
|
static var vrCameraOffset:Vec4 = new Vec4();
|
||||||
|
static var wasVRPresenting:Bool = false;
|
||||||
|
public static var vrCalibrationPosition:Vec4 = null;
|
||||||
|
public static var vrCalibrationRotation:iron.math.Quat = null;
|
||||||
|
public static var vrCalibrationSaved:Bool = false;
|
||||||
|
|
||||||
|
public static var vrCenterCameraWorld:Mat4 = null;
|
||||||
|
|
||||||
|
static var vrOriginalSuperSample:Float = -1.0;
|
||||||
|
public static inline function isVRPresenting(): Bool {
|
||||||
|
#if (kha_webgl && lnx_vr)
|
||||||
|
return kha.vr.VrInterface.instance != null && kha.vr.VrInterface.instance.IsPresenting();
|
||||||
|
#else
|
||||||
|
return false;
|
||||||
|
#end
|
||||||
|
}
|
||||||
|
|
||||||
|
public static inline function isVRSimulateMode(): Bool {
|
||||||
|
return vrSimulateMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: done remove safely
|
||||||
|
public static inline function debugLog(msg: String, once: Bool = true): Void {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#end
|
||||||
|
|
||||||
public var frameScissor = false;
|
public var frameScissor = false;
|
||||||
public var frameScissorX = 0;
|
public var frameScissorX = 0;
|
||||||
@ -43,9 +77,15 @@ class RenderPath {
|
|||||||
public var isProbe = false;
|
public var isProbe = false;
|
||||||
public var currentG: Graphics = null;
|
public var currentG: Graphics = null;
|
||||||
public var frameG: Graphics;
|
public var frameG: Graphics;
|
||||||
|
#if lnx_vr
|
||||||
|
var beginCalled = false;
|
||||||
|
var scissorSet = false;
|
||||||
|
var viewportScaled = false;
|
||||||
|
var renderToXRFramebuffer = false;
|
||||||
|
#end
|
||||||
public var drawOrder = DrawOrder.Distance;
|
public var drawOrder = DrawOrder.Distance;
|
||||||
public var paused = false;
|
public var paused = false;
|
||||||
public var ready(get, null): Bool;
|
public var ready(get, never): Bool;
|
||||||
function get_ready(): Bool { return loading == 0; }
|
function get_ready(): Bool { return loading == 0; }
|
||||||
public var commands: Void->Void = null;
|
public var commands: Void->Void = null;
|
||||||
public var setupDepthTexture: Void->Void = null;
|
public var setupDepthTexture: Void->Void = null;
|
||||||
@ -123,9 +163,93 @@ class RenderPath {
|
|||||||
public function renderFrame(g: Graphics) {
|
public function renderFrame(g: Graphics) {
|
||||||
if (!ready || paused || iron.App.w() == 0 || iron.App.h() == 0) return;
|
if (!ready || paused || iron.App.w() == 0 || iron.App.h() == 0) return;
|
||||||
|
|
||||||
if (lastW > 0 && (lastW != iron.App.w() || lastH != iron.App.h())) resize();
|
var appW = iron.App.w();
|
||||||
lastW = iron.App.w();
|
var appH = iron.App.h();
|
||||||
lastH = iron.App.h();
|
|
||||||
|
// use native XR framebuffer dimensions
|
||||||
|
#if (kha_webgl && lnx_vr)
|
||||||
|
if (kha.vr.VrInterface.instance != null) {
|
||||||
|
var vr = kha.vr.VrInterface.instance;
|
||||||
|
var isPresenting = vr != null && vr.IsPresenting();
|
||||||
|
|
||||||
|
// save/restore camera position between modes
|
||||||
|
if (!wasVRPresenting && isPresenting) {
|
||||||
|
if (Scene.active != null && Scene.active.camera != null) {
|
||||||
|
if (vrCalibrationPosition == null) vrCalibrationPosition = new Vec4();
|
||||||
|
if (vrCalibrationRotation == null) vrCalibrationRotation = new Quat();
|
||||||
|
|
||||||
|
vrCalibrationPosition.setFrom(Scene.active.camera.transform.loc);
|
||||||
|
vrCalibrationRotation.setFrom(Scene.active.camera.transform.rot);
|
||||||
|
vrCalibrationSaved = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// save original super sampling for later
|
||||||
|
vrOriginalSuperSample = leenkx.renderpath.Inc.superSample;
|
||||||
|
|
||||||
|
// compositeToXR function handles blitting to VR framebuffer
|
||||||
|
|
||||||
|
var xrVr: kha.js.vr.VrInterface = cast vr;
|
||||||
|
if (xrVr.xrGLLayer != null) {
|
||||||
|
var vrWidth = untyped xrVr.xrGLLayer.framebufferWidth;
|
||||||
|
var vrHeight = untyped xrVr.xrGLLayer.framebufferHeight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (wasVRPresenting && !isPresenting) {
|
||||||
|
// reset VR frame time before anything else
|
||||||
|
#if (kha_webgl && lnx_vr)
|
||||||
|
iron.system.Time.vrFrameTime = -1.0;
|
||||||
|
#end
|
||||||
|
|
||||||
|
if (vrCalibrationSaved && Scene.active != null && Scene.active.camera != null) {
|
||||||
|
Scene.active.camera.transform.loc.setFrom(vrCalibrationPosition);
|
||||||
|
Scene.active.camera.transform.rot.setFrom(vrCalibrationRotation);
|
||||||
|
Scene.active.camera.buildMatrix();
|
||||||
|
Scene.active.camera.buildProjection();
|
||||||
|
}
|
||||||
|
|
||||||
|
// restore original super sampling from simulate mode
|
||||||
|
if (vrOriginalSuperSample >= 0.0) {
|
||||||
|
leenkx.renderpath.Inc.superSample = vrOriginalSuperSample;
|
||||||
|
|
||||||
|
for (rt in renderTargets) {
|
||||||
|
if (rt.raw.width == 0 && rt.raw.scale != null) {
|
||||||
|
rt.raw.scale = vrOriginalSuperSample;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
resize();
|
||||||
|
vrOriginalSuperSample = -1.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// reset offset for next session
|
||||||
|
vrCameraOffsetSet = false;
|
||||||
|
vrCameraOffset = null;
|
||||||
|
}
|
||||||
|
wasVRPresenting = isPresenting;
|
||||||
|
|
||||||
|
if (isPresenting) {
|
||||||
|
// TODO: re-investigate using super sampling to avoid pixelation in simulate mode while giving max quality in headset
|
||||||
|
if (vrOriginalSuperSample >= 0.0 && leenkx.renderpath.Inc.superSample != 4.0) {
|
||||||
|
leenkx.renderpath.Inc.superSample = 4.0;
|
||||||
|
for (rt in renderTargets) {
|
||||||
|
if (rt.raw.width == 0 && rt.raw.scale != null) {
|
||||||
|
rt.raw.scale = 4.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
resize();
|
||||||
|
}
|
||||||
|
|
||||||
|
var xrVr: kha.js.vr.VrInterface = cast vr;
|
||||||
|
if (xrVr.xrGLLayer != null) {
|
||||||
|
appW = xrVr.xrGLLayer.framebufferWidth;
|
||||||
|
appH = xrVr.xrGLLayer.framebufferHeight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#end
|
||||||
|
|
||||||
|
if (lastW > 0 && (lastW != appW || lastH != appH)) resize();
|
||||||
|
lastW = appW;
|
||||||
|
lastH = appH;
|
||||||
|
|
||||||
frameTime = Time.time() - lastFrameTime;
|
frameTime = Time.time() - lastFrameTime;
|
||||||
lastFrameTime = Time.time();
|
lastFrameTime = Time.time();
|
||||||
@ -191,7 +315,9 @@ class RenderPath {
|
|||||||
}
|
}
|
||||||
light = Scene.active.lights[0];
|
light = Scene.active.lights[0];
|
||||||
|
|
||||||
commands();
|
if (commands != null) {
|
||||||
|
commands();
|
||||||
|
}
|
||||||
|
|
||||||
if (!isProbe) frame++;
|
if (!isProbe) frame++;
|
||||||
}
|
}
|
||||||
@ -207,13 +333,13 @@ class RenderPath {
|
|||||||
begin(frameG, Scene.active.camera.currentFace);
|
begin(frameG, Scene.active.camera.currentFace);
|
||||||
}
|
}
|
||||||
else { // Screen, planar probe
|
else { // Screen, planar probe
|
||||||
currentW = iron.App.w();
|
currentW = kha.System.windowWidth();
|
||||||
currentH = iron.App.h();
|
currentH = kha.System.windowHeight();
|
||||||
if (frameScissor) setFrameScissor();
|
if (frameScissor) setFrameScissor();
|
||||||
begin(frameG);
|
begin(frameG);
|
||||||
if (!isProbe) {
|
if (!isProbe) {
|
||||||
setCurrentViewport(iron.App.w(), iron.App.h());
|
setCurrentViewport(kha.System.windowWidth(), kha.System.windowHeight());
|
||||||
setCurrentScissor(iron.App.w(), iron.App.h());
|
setCurrentScissor(kha.System.windowWidth(), kha.System.windowHeight());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -258,16 +384,42 @@ class RenderPath {
|
|||||||
if (currentG != null) end();
|
if (currentG != null) end();
|
||||||
currentG = g;
|
currentG = g;
|
||||||
additionalTargets = additionalRenderTargets;
|
additionalTargets = additionalRenderTargets;
|
||||||
face >= 0 ? g.beginFace(face) : g.begin(additionalRenderTargets);
|
|
||||||
|
// we still bind but skip begin() when explicitly rendering to XR framebuffer (renderToXRFramebuffer flag)
|
||||||
|
#if lnx_vr
|
||||||
|
if (!renderToXRFramebuffer) {
|
||||||
|
face >= 0 ? g.beginFace(face) : g.begin(additionalRenderTargets);
|
||||||
|
beginCalled = true;
|
||||||
|
} else {
|
||||||
|
// XR framebuffer is already bound by VrInterface so we dont rebind
|
||||||
|
beginCalled = false;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
face >= 0 ? g.beginFace(face) : g.begin(additionalRenderTargets);
|
||||||
|
#end
|
||||||
}
|
}
|
||||||
|
|
||||||
inline function end() {
|
inline function end() {
|
||||||
|
if (currentG == null) return;
|
||||||
if (scissorSet) {
|
if (scissorSet) {
|
||||||
currentG.disableScissor();
|
currentG.disableScissor();
|
||||||
scissorSet = false;
|
scissorSet = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if lnx_vr
|
||||||
|
if (beginCalled) {
|
||||||
|
currentG.end();
|
||||||
|
beginCalled = false;
|
||||||
|
}
|
||||||
|
// persist for rendering both eyes
|
||||||
|
if (!isVRPresenting()) {
|
||||||
|
currentG = null;
|
||||||
|
additionalTargets = null;
|
||||||
|
}
|
||||||
|
#else
|
||||||
currentG.end();
|
currentG.end();
|
||||||
currentG = null;
|
currentG = null;
|
||||||
|
#end
|
||||||
bindParams = null;
|
bindParams = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -331,14 +483,17 @@ class RenderPath {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function sortMeshesShader(meshes: Array<MeshObject>) {
|
public static function sortMeshesIndex(meshes: Array<MeshObject>) {
|
||||||
meshes.sort(function(a, b): Int {
|
meshes.sort(function(a, b): Int {
|
||||||
#if rp_depth_texture
|
#if rp_depth_texture
|
||||||
var depthDiff = boolToInt(a.depthRead) - boolToInt(b.depthRead);
|
var depthDiff = boolToInt(a.depthRead) - boolToInt(b.depthRead);
|
||||||
if (depthDiff != 0) return depthDiff;
|
if (depthDiff != 0) return depthDiff;
|
||||||
#end
|
#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;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -399,7 +554,7 @@ class RenderPath {
|
|||||||
#if lnx_batch
|
#if lnx_batch
|
||||||
sortMeshesDistance(Scene.active.meshBatch.nonBatched);
|
sortMeshesDistance(Scene.active.meshBatch.nonBatched);
|
||||||
#else
|
#else
|
||||||
drawOrder == DrawOrder.Shader ? sortMeshesShader(meshes) : sortMeshesDistance(meshes);
|
drawOrder == DrawOrder.Index ? sortMeshesIndex(meshes) : sortMeshesDistance(meshes);
|
||||||
#end
|
#end
|
||||||
meshesSorted = true;
|
meshesSorted = true;
|
||||||
}
|
}
|
||||||
@ -518,12 +673,208 @@ class RenderPath {
|
|||||||
return Reflect.field(kha.Shaders, handle + "_comp");
|
return Reflect.field(kha.Shaders, handle + "_comp");
|
||||||
}
|
}
|
||||||
|
|
||||||
#if (kha_krom && lnx_vr)
|
#if lnx_vr
|
||||||
public function drawStereo(drawMeshes: Int->Void) {
|
// blits to each eyes viewport in the XR framebuffer.
|
||||||
for (eye in 0...2) {
|
public function compositeToXR(sourceTarget: String) {
|
||||||
Krom.vrBeginRender(eye);
|
#if (kha_webgl && lnx_vr)
|
||||||
drawMeshes(eye);
|
|
||||||
Krom.vrEndRender(eye);
|
var vr: kha.js.vr.VrInterface = cast kha.vr.VrInterface.instance;
|
||||||
|
if (vr == null || vr._glContext == null || vr.xrGLLayer == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var gl: js.html.webgl.WebGL2RenderingContext = cast vr._glContext;
|
||||||
|
var source = renderTargets.get(sourceTarget);
|
||||||
|
if (source == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var sourceFB: js.html.webgl.Framebuffer = untyped source.image.g4.renderTargetFrameBuffer;
|
||||||
|
if (sourceFB == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// trace('Framebuffer OK');
|
||||||
|
|
||||||
|
renderToXRFramebuffer = true;
|
||||||
|
gl.bindFramebuffer(js.html.webgl.WebGL2RenderingContext.DRAW_FRAMEBUFFER, vr.xrGLLayer.framebuffer);
|
||||||
|
gl.bindFramebuffer(js.html.webgl.WebGL2RenderingContext.READ_FRAMEBUFFER, sourceFB);
|
||||||
|
|
||||||
|
var readStatus = gl.checkFramebufferStatus(js.html.webgl.WebGL2RenderingContext.READ_FRAMEBUFFER);
|
||||||
|
var drawStatus = gl.checkFramebufferStatus(js.html.webgl.WebGL2RenderingContext.DRAW_FRAMEBUFFER);
|
||||||
|
if (readStatus != js.html.webgl.WebGL2RenderingContext.FRAMEBUFFER_COMPLETE ||
|
||||||
|
drawStatus != js.html.webgl.WebGL2RenderingContext.FRAMEBUFFER_COMPLETE) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var halfWidth = Std.int(source.image.width / 2);
|
||||||
|
var fullHeight = source.image.height;
|
||||||
|
|
||||||
|
if (vr._leftViewport != null) {
|
||||||
|
var vp = vr._leftViewport;
|
||||||
|
gl.blitFramebuffer(
|
||||||
|
0, 0, halfWidth, fullHeight,
|
||||||
|
vp.x, vp.y, vp.x + vp.width, vp.y + vp.height,
|
||||||
|
js.html.webgl.WebGL2RenderingContext.COLOR_BUFFER_BIT,
|
||||||
|
js.html.webgl.WebGL2RenderingContext.LINEAR
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (vr._rightViewport != null) {
|
||||||
|
var vp = vr._rightViewport;
|
||||||
|
gl.blitFramebuffer(
|
||||||
|
halfWidth, 0, source.image.width, fullHeight,
|
||||||
|
vp.x, vp.y, vp.x + vp.width, vp.y + vp.height,
|
||||||
|
js.html.webgl.WebGL2RenderingContext.COLOR_BUFFER_BIT,
|
||||||
|
js.html.webgl.WebGL2RenderingContext.LINEAR
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
gl.bindFramebuffer(js.html.webgl.WebGL2RenderingContext.FRAMEBUFFER, null);
|
||||||
|
renderToXRFramebuffer = false;
|
||||||
|
#end
|
||||||
|
}
|
||||||
|
#end
|
||||||
|
#if lnx_vr
|
||||||
|
public function drawStereo(drawMeshes: Void->Void) {
|
||||||
|
vrSimulateMode = false;
|
||||||
|
|
||||||
|
if (currentG == null && frameG != null) {
|
||||||
|
currentG = frameG;
|
||||||
|
}
|
||||||
|
|
||||||
|
var appw = iron.App.w();
|
||||||
|
var apph = iron.App.h();
|
||||||
|
var g = currentG;
|
||||||
|
|
||||||
|
// get render target dimensions not App.w/h gbuffer is scaled in simulate mode with supersampling
|
||||||
|
|
||||||
|
var gbuffer0 = renderTargets.get("gbuffer0");
|
||||||
|
var actualWidth = (gbuffer0 != null && gbuffer0.image != null) ? gbuffer0.image.width : appw;
|
||||||
|
var actualHeight = (gbuffer0 != null && gbuffer0.image != null) ? gbuffer0.image.height : apph;
|
||||||
|
var actualHalfWidth = Std.int(actualWidth / 2);
|
||||||
|
|
||||||
|
var vrFBWidth = actualWidth;
|
||||||
|
var vrFBHeight = actualHeight;
|
||||||
|
var vrHalfWidth = actualHalfWidth;
|
||||||
|
var isVRPresenting = false;
|
||||||
|
vrSimulateMode = false;
|
||||||
|
|
||||||
|
var vr:Dynamic = null;
|
||||||
|
var vrExists = false;
|
||||||
|
#if (kha_webgl && lnx_vr)
|
||||||
|
if (kha.vr.VrInterface.instance != null) {
|
||||||
|
vr = kha.vr.VrInterface.instance;
|
||||||
|
vrExists = true;
|
||||||
|
}
|
||||||
|
#end
|
||||||
|
|
||||||
|
if (vrExists && vr != null && vr.IsPresenting()) {
|
||||||
|
vrSimulateMode = false;
|
||||||
|
isVRPresenting = true;
|
||||||
|
|
||||||
|
// get framebuffer dimensions from XR layer
|
||||||
|
#if (kha_webgl && lnx_vr)
|
||||||
|
var xrVr: kha.js.vr.VrInterface = cast vr;
|
||||||
|
if (xrVr.xrGLLayer != null) {
|
||||||
|
vrFBWidth = untyped xrVr.xrGLLayer.framebufferWidth;
|
||||||
|
vrFBHeight = untyped xrVr.xrGLLayer.framebufferHeight;
|
||||||
|
vrHalfWidth = Std.int(vrFBWidth / 2);
|
||||||
|
}
|
||||||
|
#end
|
||||||
|
|
||||||
|
if (Scene.active == null || Scene.active.camera == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if (kha_webgl && lnx_vr)
|
||||||
|
if (vrCenterCameraWorld == null) vrCenterCameraWorld = Mat4.identity();
|
||||||
|
vrCenterCameraWorld.setFrom(Scene.active.camera.transform.world);
|
||||||
|
#end
|
||||||
|
|
||||||
|
// LEFT EYE
|
||||||
|
// HMD center for room scale position tracking
|
||||||
|
#if (kha_webgl && lnx_vr)
|
||||||
|
var xrVr: kha.js.vr.VrInterface = cast vr;
|
||||||
|
if (xrVr.currentViewerPose != null) {
|
||||||
|
var viewerTransform = untyped xrVr.currentViewerPose.transform;
|
||||||
|
if (viewerTransform != null && viewerTransform.position != null) {
|
||||||
|
// VR present calibration is used to position objects in world space not the camera
|
||||||
|
var pos = viewerTransform.position;
|
||||||
|
|
||||||
|
// camera follows headset directly in local floor space
|
||||||
|
Scene.active.camera.transform.loc.set(pos.x, pos.y, pos.z);
|
||||||
|
|
||||||
|
if (viewerTransform.orientation != null) {
|
||||||
|
Scene.active.camera.transform.rot.set(
|
||||||
|
viewerTransform.orientation.x,
|
||||||
|
viewerTransform.orientation.y,
|
||||||
|
viewerTransform.orientation.z,
|
||||||
|
viewerTransform.orientation.w
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Scene.active.camera.transform.buildMatrix();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
iron.system.VRController.updatePoses();
|
||||||
|
#end
|
||||||
|
|
||||||
|
Scene.active.camera.V.self = vr.GetViewMatrix(0);
|
||||||
|
Scene.active.camera.P.self = vr.GetProjectionMatrix(0);
|
||||||
|
Scene.active.camera.VP.setFrom(Scene.active.camera.P);
|
||||||
|
Scene.active.camera.VP.multmat(Scene.active.camera.V);
|
||||||
|
Scene.active.camera.buildMatrix(); // update frustum for culling
|
||||||
|
|
||||||
|
var renderWidth = actualWidth;
|
||||||
|
var renderHeight = actualHeight;
|
||||||
|
var renderHalfWidth = actualHalfWidth;
|
||||||
|
|
||||||
|
// left half of render target
|
||||||
|
g.viewport(0, 0, renderHalfWidth, renderHeight);
|
||||||
|
g.scissor(0, 0, renderHalfWidth, renderHeight);
|
||||||
|
drawMeshes();
|
||||||
|
|
||||||
|
// RIGHT EYE
|
||||||
|
Scene.active.camera.V.self = vr.GetViewMatrix(1);
|
||||||
|
Scene.active.camera.P.self = vr.GetProjectionMatrix(1);
|
||||||
|
Scene.active.camera.VP.setFrom(Scene.active.camera.P);
|
||||||
|
Scene.active.camera.VP.multmat(Scene.active.camera.V);
|
||||||
|
Scene.active.camera.buildMatrix();
|
||||||
|
|
||||||
|
// right half of render target
|
||||||
|
g.viewport(renderHalfWidth, 0, renderHalfWidth, renderHeight);
|
||||||
|
g.scissor(renderHalfWidth, 0, renderHalfWidth, renderHeight);
|
||||||
|
drawMeshes();
|
||||||
|
|
||||||
|
// restore for post-processing
|
||||||
|
g.disableScissor();
|
||||||
|
g.viewport(0, 0, renderWidth, renderHeight);
|
||||||
|
}
|
||||||
|
else { // Simulate
|
||||||
|
vrSimulateMode = true;
|
||||||
|
var ipd_offset = 0.032 * 35.0;
|
||||||
|
|
||||||
|
#if (kha_webgl && lnx_vr)
|
||||||
|
if (vrCenterCameraWorld == null) vrCenterCameraWorld = Mat4.identity();
|
||||||
|
vrCenterCameraWorld.setFrom(Scene.active.camera.transform.world);
|
||||||
|
#end
|
||||||
|
|
||||||
|
Scene.active.camera.buildProjection(actualHalfWidth / actualHeight);
|
||||||
|
|
||||||
|
Scene.active.camera.transform.move(Scene.active.camera.right(), -ipd_offset);
|
||||||
|
Scene.active.camera.buildMatrix();
|
||||||
|
g.viewport(0, 0, actualHalfWidth, actualHeight);
|
||||||
|
g.scissor(0, 0, actualHalfWidth, actualHeight);
|
||||||
|
drawMeshes();
|
||||||
|
|
||||||
|
begin(g, additionalTargets);
|
||||||
|
Scene.active.camera.transform.move(Scene.active.camera.right(), ipd_offset * 2.0);
|
||||||
|
Scene.active.camera.buildMatrix();
|
||||||
|
g.viewport(actualHalfWidth, 0, actualHalfWidth, actualHeight);
|
||||||
|
g.scissor(actualHalfWidth, 0, actualHalfWidth, actualHeight);
|
||||||
|
drawMeshes();
|
||||||
|
|
||||||
|
Scene.active.camera.transform.move(Scene.active.camera.right(), -ipd_offset);
|
||||||
|
Scene.active.camera.buildMatrix();
|
||||||
|
g.disableScissor();
|
||||||
|
g.viewport(0, 0, actualWidth, actualHeight);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#end
|
#end
|
||||||
@ -882,6 +1233,6 @@ class CachedShaderContext {
|
|||||||
|
|
||||||
@:enum abstract DrawOrder(Int) from Int {
|
@:enum abstract DrawOrder(Int) from Int {
|
||||||
var Distance = 0; // Early-z
|
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
|
// var Mix = 2; // Distance buckets sorted by shader
|
||||||
}
|
}
|
||||||
|
|||||||
@ -783,6 +783,11 @@ class Scene {
|
|||||||
if (o.tilesheet_ref != null) {
|
if (o.tilesheet_ref != null) {
|
||||||
cast(object, MeshObject).setupTilesheet(sceneName, o.tilesheet_ref, o.tilesheet_action_ref);
|
cast(object, MeshObject).setupTilesheet(sceneName, o.tilesheet_ref, o.tilesheet_action_ref);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (o.camera_list != null){
|
||||||
|
cast(object, MeshObject).cameraList = o.camera_list;
|
||||||
|
}
|
||||||
|
|
||||||
returnObject(object, o, done);
|
returnObject(object, o, done);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -882,8 +887,12 @@ class Scene {
|
|||||||
var ptype: String = t.props[i * 3 + 1];
|
var ptype: String = t.props[i * 3 + 1];
|
||||||
var pval: Dynamic = t.props[i * 3 + 2];
|
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));
|
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 {
|
else {
|
||||||
switch (ptype) {
|
switch (ptype) {
|
||||||
@ -945,7 +954,14 @@ class Scene {
|
|||||||
static function createTraitClassInstance(traitName: String, args: Array<Dynamic>): Dynamic {
|
static function createTraitClassInstance(traitName: String, args: Array<Dynamic>): Dynamic {
|
||||||
var cname = Type.resolveClass(traitName);
|
var cname = Type.resolveClass(traitName);
|
||||||
if (cname == null) return null;
|
if (cname == null) return null;
|
||||||
return Type.createInstance(cname, args);
|
var trait:Dynamic;
|
||||||
|
try {
|
||||||
|
trait = Type.createInstance(cname, args);
|
||||||
|
} catch(e) {
|
||||||
|
trace("Error creating trait: " + traitName + " - " + e);
|
||||||
|
trait = null;
|
||||||
|
}
|
||||||
|
return trait;
|
||||||
}
|
}
|
||||||
|
|
||||||
function loadEmbeddedData(datas: Array<String>, done: Void->Void) {
|
function loadEmbeddedData(datas: Array<String>, done: Void->Void) {
|
||||||
|
|||||||
@ -37,7 +37,9 @@ class Armature {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function getAction(name: String): TAction {
|
public function getAction(name: String): TAction {
|
||||||
for (a in actions) if (a.name == name) return a;
|
for (a in actions) {
|
||||||
|
if (a.name == name) return a;
|
||||||
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -9,6 +9,7 @@ import iron.data.SceneFormat;
|
|||||||
class MeshData {
|
class MeshData {
|
||||||
|
|
||||||
public var name: String;
|
public var name: String;
|
||||||
|
public var sortingIndex: Int;
|
||||||
public var raw: TMeshData;
|
public var raw: TMeshData;
|
||||||
public var format: TSceneFormat;
|
public var format: TSceneFormat;
|
||||||
public var geom: Geometry;
|
public var geom: Geometry;
|
||||||
@ -23,6 +24,7 @@ class MeshData {
|
|||||||
public function new(raw: TMeshData, done: MeshData->Void) {
|
public function new(raw: TMeshData, done: MeshData->Void) {
|
||||||
this.raw = raw;
|
this.raw = raw;
|
||||||
this.name = raw.name;
|
this.name = raw.name;
|
||||||
|
this.sortingIndex = raw.sorting_index;
|
||||||
|
|
||||||
if (raw.scale_pos != null) scalePos = raw.scale_pos;
|
if (raw.scale_pos != null) scalePos = raw.scale_pos;
|
||||||
if (raw.scale_tex != null) scaleTex = raw.scale_tex;
|
if (raw.scale_tex != null) scaleTex = raw.scale_tex;
|
||||||
|
|||||||
@ -49,6 +49,7 @@ typedef TMeshData = {
|
|||||||
@:structInit class TMeshData {
|
@:structInit class TMeshData {
|
||||||
#end
|
#end
|
||||||
public var name: String;
|
public var name: String;
|
||||||
|
public var sorting_index: Int;
|
||||||
public var vertex_arrays: Array<TVertexArray>;
|
public var vertex_arrays: Array<TVertexArray>;
|
||||||
public var index_arrays: Array<TIndexArray>;
|
public var index_arrays: Array<TIndexArray>;
|
||||||
@:optional public var dynamic_usage: Null<Bool>;
|
@:optional public var dynamic_usage: Null<Bool>;
|
||||||
@ -222,6 +223,7 @@ typedef TShaderData = {
|
|||||||
@:structInit class TShaderData {
|
@:structInit class TShaderData {
|
||||||
#end
|
#end
|
||||||
public var name: String;
|
public var name: String;
|
||||||
|
public var next_pass: String;
|
||||||
public var contexts: Array<TShaderContext>;
|
public var contexts: Array<TShaderContext>;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -393,6 +395,7 @@ typedef TParticleData = {
|
|||||||
public var name: String;
|
public var name: String;
|
||||||
public var type: Int; // 0 - Emitter, Hair
|
public var type: Int; // 0 - Emitter, Hair
|
||||||
public var auto_start: Bool;
|
public var auto_start: Bool;
|
||||||
|
public var dynamic_emitter: Bool;
|
||||||
public var is_unique: Bool;
|
public var is_unique: Bool;
|
||||||
public var loop: Bool;
|
public var loop: Bool;
|
||||||
public var count: Int;
|
public var count: Int;
|
||||||
@ -441,6 +444,7 @@ typedef TObj = {
|
|||||||
@:optional public var traits: Array<TTrait>;
|
@:optional public var traits: Array<TTrait>;
|
||||||
@:optional public var properties: Array<TProperty>;
|
@:optional public var properties: Array<TProperty>;
|
||||||
@:optional public var vertex_groups: Array<TVertex_groups>;
|
@:optional public var vertex_groups: Array<TVertex_groups>;
|
||||||
|
@:optional public var camera_list: Array<String>;
|
||||||
@:optional public var constraints: Array<TConstraint>;
|
@:optional public var constraints: Array<TConstraint>;
|
||||||
@:optional public var dimensions: Float32Array; // Geometry objects
|
@:optional public var dimensions: Float32Array; // Geometry objects
|
||||||
@:optional public var object_actions: Array<String>;
|
@:optional public var object_actions: Array<String>;
|
||||||
|
|||||||
@ -22,6 +22,7 @@ using StringTools;
|
|||||||
class ShaderData {
|
class ShaderData {
|
||||||
|
|
||||||
public var name: String;
|
public var name: String;
|
||||||
|
public var nextPass: String;
|
||||||
public var raw: TShaderData;
|
public var raw: TShaderData;
|
||||||
public var contexts: Array<ShaderContext> = [];
|
public var contexts: Array<ShaderContext> = [];
|
||||||
|
|
||||||
@ -33,6 +34,7 @@ class ShaderData {
|
|||||||
public function new(raw: TShaderData, done: ShaderData->Void, overrideContext: TShaderOverride = null) {
|
public function new(raw: TShaderData, done: ShaderData->Void, overrideContext: TShaderOverride = null) {
|
||||||
this.raw = raw;
|
this.raw = raw;
|
||||||
this.name = raw.name;
|
this.name = raw.name;
|
||||||
|
this.nextPass = raw.next_pass;
|
||||||
|
|
||||||
for (c in raw.contexts) contexts.push(null);
|
for (c in raw.contexts) contexts.push(null);
|
||||||
var contextsLoaded = 0;
|
var contextsLoaded = 0;
|
||||||
|
|||||||
@ -141,6 +141,7 @@ class Animation {
|
|||||||
sampler.cacheSet = false;
|
sampler.cacheSet = false;
|
||||||
sampler.trackEnd = false;
|
sampler.trackEnd = false;
|
||||||
|
|
||||||
|
if (anim == null || anim.tracks == null || anim.tracks.length == 0) return;
|
||||||
var track = anim.tracks[0];
|
var track = anim.tracks[0];
|
||||||
|
|
||||||
if (frameIndex == -1) {
|
if (frameIndex == -1) {
|
||||||
@ -442,7 +443,12 @@ class ActionSampler {
|
|||||||
*/
|
*/
|
||||||
public inline function setObjectAction(actionData: TObj) {
|
public inline function setObjectAction(actionData: TObj) {
|
||||||
this.actionData = [actionData];
|
this.actionData = [actionData];
|
||||||
this.totalFrames = actionData.anim.tracks[0].frames.length;
|
if (actionData != null && actionData.anim != null && actionData.anim.tracks != null && actionData.anim.tracks.length > 0) {
|
||||||
|
this.totalFrames = actionData.anim.tracks[0].frames.length;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.totalFrames = 0;
|
||||||
|
}
|
||||||
actionDataInit = true;
|
actionDataInit = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -108,9 +108,11 @@ class BoneAnimation extends Animation {
|
|||||||
object.transform.rot.set(0, 0, 0, 1);
|
object.transform.rot.set(0, 0, 0, 1);
|
||||||
object.transform.buildMatrix();
|
object.transform.buildMatrix();
|
||||||
|
|
||||||
var refs = mo.parent.raw.bone_actions;
|
if (mo.parent != null && mo.parent.raw != null && mo.parent.raw.bone_actions != null) {
|
||||||
if (refs != null && refs.length > 0) {
|
var refs = mo.parent.raw.bone_actions;
|
||||||
Data.getSceneRaw(refs[0], function(action: TSceneFormat) { play(action.name); });
|
if (refs.length > 0) {
|
||||||
|
Data.getSceneRaw(refs[0], function(action: TSceneFormat) { play(action.name); });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (armatureObject.raw.relative_bone_constraints) relativeBoneConstraints = true;
|
if (armatureObject.raw.relative_bone_constraints) relativeBoneConstraints = true;
|
||||||
@ -183,8 +185,10 @@ class BoneAnimation extends Animation {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function setAction(action: String) {
|
function setAction(action: String) {
|
||||||
|
if (armature == null) return;
|
||||||
armature.initMats();
|
armature.initMats();
|
||||||
var a = armature.getAction(action);
|
var a = armature.getAction(action);
|
||||||
|
if (a == null) return;
|
||||||
skeletonBones = a.bones;
|
skeletonBones = a.bones;
|
||||||
skeletonMats = a.mats;
|
skeletonMats = a.mats;
|
||||||
if(! rootMotionCacheInit) skeletonMats.push(Mat4.identity());
|
if(! rootMotionCacheInit) skeletonMats.push(Mat4.identity());
|
||||||
@ -193,8 +197,11 @@ class BoneAnimation extends Animation {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getAction(action: String): Array<TObj> {
|
function getAction(action: String): Array<TObj> {
|
||||||
|
if (armature == null) return null;
|
||||||
armature.initMats();
|
armature.initMats();
|
||||||
return armature.getAction(action).bones;
|
var a = armature.getAction(action);
|
||||||
|
if (a == null) return null;
|
||||||
|
return a.bones;
|
||||||
}
|
}
|
||||||
|
|
||||||
function multParent(i: Int, fasts: Array<Mat4>, bones: Array<TObj>, mats: Array<Mat4>) {
|
function multParent(i: Int, fasts: Array<Mat4>, bones: Array<TObj>, mats: Array<Mat4>) {
|
||||||
@ -225,9 +232,9 @@ class BoneAnimation extends Animation {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override public function play(action = "", onComplete: Void->Void = null, blendTime = 0.2, speed = 1.0, loop = true) {
|
override public function play(action = "", onComplete: Void->Void = null, blendTime = 0.2, speed = 1.0, loop = true) {
|
||||||
super.play(action, onComplete, blendTime, speed, loop);
|
|
||||||
if (action != "") {
|
if (action != "") {
|
||||||
setAction(action);
|
setAction(action);
|
||||||
|
super.play(action, onComplete, blendTime, speed, loop);
|
||||||
var tempAnimParam = new ActionSampler(action);
|
var tempAnimParam = new ActionSampler(action);
|
||||||
registerAction("tempAction", tempAnimParam);
|
registerAction("tempAction", tempAnimParam);
|
||||||
updateAnimation = function(mats){
|
updateAnimation = function(mats){
|
||||||
@ -239,6 +246,10 @@ class BoneAnimation extends Animation {
|
|||||||
override public function update(delta: FastFloat) {
|
override public function update(delta: FastFloat) {
|
||||||
this.delta = delta;
|
this.delta = delta;
|
||||||
if (!isSkinned && skeletonBones == null) setAction(armature.actions[0].name);
|
if (!isSkinned && skeletonBones == null) setAction(armature.actions[0].name);
|
||||||
|
|
||||||
|
// TODO: double check skip culling for skinned meshes if they need animation updates for bounds
|
||||||
|
// if (object != null && !object.visible) return;
|
||||||
|
|
||||||
if (object != null && (!object.visible || object.culled)) return;
|
if (object != null && (!object.visible || object.culled)) return;
|
||||||
if (skeletonBones == null || skeletonBones.length == 0) return;
|
if (skeletonBones == null || skeletonBones.length == 0) return;
|
||||||
|
|
||||||
@ -248,7 +259,6 @@ class BoneAnimation extends Animation {
|
|||||||
|
|
||||||
super.update(delta);
|
super.update(delta);
|
||||||
if(updateAnimation != null) {
|
if(updateAnimation != null) {
|
||||||
|
|
||||||
updateAnimation(skeletonMats);
|
updateAnimation(skeletonMats);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -401,6 +411,7 @@ class BoneAnimation extends Animation {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var bones = sampler.getBoneAction();
|
var bones = sampler.getBoneAction();
|
||||||
|
if (bones == null) return;
|
||||||
for(b in bones){
|
for(b in bones){
|
||||||
if (b.anim != null) {
|
if (b.anim != null) {
|
||||||
updateTrack(b.anim, sampler);
|
updateTrack(b.anim, sampler);
|
||||||
@ -410,13 +421,14 @@ class BoneAnimation extends Animation {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function sampleAction(sampler: ActionSampler, actionMats: Array<Mat4>) {
|
public function sampleAction(sampler: ActionSampler, actionMats: Array<Mat4>) {
|
||||||
|
|
||||||
if(! sampler.actionDataInit) {
|
if(! sampler.actionDataInit) {
|
||||||
var bones = getAction(sampler.action);
|
var bones = getAction(sampler.action);
|
||||||
sampler.setBoneAction(bones);
|
sampler.setBoneAction(bones);
|
||||||
}
|
}
|
||||||
|
|
||||||
var bones = sampler.getBoneAction();
|
var bones = sampler.getBoneAction();
|
||||||
|
if (bones == null) return;
|
||||||
|
|
||||||
actionMats[skeletonBones.length].setIdentity();
|
actionMats[skeletonBones.length].setIdentity();
|
||||||
var rootMotionEnabled = sampler.rootMotionPos || sampler.rootMotionRot;
|
var rootMotionEnabled = sampler.rootMotionPos || sampler.rootMotionRot;
|
||||||
for (i in 0...bones.length) {
|
for (i in 0...bones.length) {
|
||||||
@ -427,7 +439,6 @@ class BoneAnimation extends Animation {
|
|||||||
updateAnimSampled(bones[i].anim, actionMats[i], sampler);
|
updateAnimSampled(bones[i].anim, actionMats[i], sampler);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateAnimSampled(anim: TAnimation, mm: Mat4, sampler: ActionSampler) {
|
function updateAnimSampled(anim: TAnimation, mm: Mat4, sampler: ActionSampler) {
|
||||||
@ -588,6 +599,9 @@ class BoneAnimation extends Animation {
|
|||||||
|
|
||||||
public override function getTotalFrames(sampler: ActionSampler): Int {
|
public override function getTotalFrames(sampler: ActionSampler): Int {
|
||||||
var bones = getAction(sampler.action);
|
var bones = getAction(sampler.action);
|
||||||
|
if (bones == null){
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
var track = bones[0].anim.tracks[0];
|
var track = bones[0].anim.tracks[0];
|
||||||
return Std.int(track.frames[track.frames.length - 1] - track.frames[0]);
|
return Std.int(track.frames[track.frames.length - 1] - track.frames[0]);
|
||||||
}
|
}
|
||||||
@ -1048,9 +1062,9 @@ class BoneAnimation extends Animation {
|
|||||||
var rootLen = root.bone_length * rootMat.getScale().x;
|
var rootLen = root.bone_length * rootMat.getScale().x;
|
||||||
|
|
||||||
// Get distance form root to goal
|
// Get distance form root to goal
|
||||||
var goalLen = Math.abs(Vec4.distance(rootMat.getLoc(), goal));
|
var goalLen: FastFloat = Math.abs(Vec4.distance(rootMat.getLoc(), goal));
|
||||||
|
|
||||||
var totalLength = effectorLen + rootLen;
|
var totalLength: FastFloat = effectorLen + rootLen;
|
||||||
|
|
||||||
// Get tip location of effector bone
|
// Get tip location of effector bone
|
||||||
var effectorTipPos = new Vec4().setFrom(effectorMat.look()).normalize();
|
var effectorTipPos = new Vec4().setFrom(effectorMat.look()).normalize();
|
||||||
|
|||||||
@ -31,11 +31,22 @@ class CameraObject extends Object {
|
|||||||
static var vcenter = new Vec4();
|
static var vcenter = new Vec4();
|
||||||
static var vup = new Vec4();
|
static var vup = new Vec4();
|
||||||
|
|
||||||
|
#if lnx_vr
|
||||||
|
var helpMat = Mat4.identity();
|
||||||
|
public var leftV = Mat4.identity();
|
||||||
|
public var rightV = Mat4.identity();
|
||||||
|
#end
|
||||||
|
|
||||||
public function new(data: CameraData) {
|
public function new(data: CameraData) {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
this.data = data;
|
this.data = data;
|
||||||
|
|
||||||
|
// dont just auto initialize VR button - headset trait controls VR
|
||||||
|
// #if lnx_vr
|
||||||
|
// iron.system.VR.initButton();
|
||||||
|
// #end
|
||||||
|
|
||||||
buildProjection();
|
buildProjection();
|
||||||
|
|
||||||
V = Mat4.identity();
|
V = Mat4.identity();
|
||||||
@ -75,7 +86,14 @@ class CameraObject extends Object {
|
|||||||
projectionJitter();
|
projectionJitter();
|
||||||
#end
|
#end
|
||||||
|
|
||||||
|
// matrices are set by VR system so avoid rebuilding transforms unless its in preview/not presenting
|
||||||
|
#if (kha_webgl && lnx_vr)
|
||||||
|
if (@:privateAccess !RenderPath.isVRPresenting()) {
|
||||||
|
buildMatrix();
|
||||||
|
}
|
||||||
|
#else
|
||||||
buildMatrix();
|
buildMatrix();
|
||||||
|
#end
|
||||||
|
|
||||||
RenderPath.active.renderFrame(g);
|
RenderPath.active.renderFrame(g);
|
||||||
|
|
||||||
@ -117,6 +135,26 @@ class CameraObject extends Object {
|
|||||||
V.getInverse(transform.world);
|
V.getInverse(transform.world);
|
||||||
VP.multmats(P, V);
|
VP.multmats(P, V);
|
||||||
|
|
||||||
|
|
||||||
|
#if lnx_vr
|
||||||
|
var vr = kha.vr.VrInterface.instance;
|
||||||
|
if (vr != null && vr.IsPresenting()) {
|
||||||
|
leftV.setFrom(V);
|
||||||
|
helpMat.self = vr.GetViewMatrix(0);
|
||||||
|
leftV.multmat(helpMat);
|
||||||
|
|
||||||
|
rightV.setFrom(V);
|
||||||
|
helpMat.self = vr.GetViewMatrix(1);
|
||||||
|
rightV.multmat(helpMat);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
leftV.setFrom(V);
|
||||||
|
}
|
||||||
|
VP.multmats(P, leftV);
|
||||||
|
#else
|
||||||
|
VP.multmats(P, V);
|
||||||
|
#end
|
||||||
|
|
||||||
if (data.raw.frustum_culling) {
|
if (data.raw.frustum_culling) {
|
||||||
buildViewFrustum(VP, frustumPlanes);
|
buildViewFrustum(VP, frustumPlanes);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -59,6 +59,9 @@ class LightObject extends Object {
|
|||||||
public static var clustersData: kha.Image = null;
|
public static var clustersData: kha.Image = null;
|
||||||
static var lpos = new Vec4();
|
static var lpos = new Vec4();
|
||||||
public static var LWVPMatrixArray: Float32Array = null;
|
public static var LWVPMatrixArray: Float32Array = null;
|
||||||
|
#if lnx_vr
|
||||||
|
static var originalLightPositions: Float32Array = null;
|
||||||
|
#end
|
||||||
#end // lnx_clusters
|
#end // lnx_clusters
|
||||||
|
|
||||||
public var V: Mat4 = Mat4.identity();
|
public var V: Mat4 = Mat4.identity();
|
||||||
@ -155,7 +158,12 @@ class LightObject extends Object {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function setCascade(camera: CameraObject, cascade: Int) {
|
public function setCascade(camera: CameraObject, cascade: Int) {
|
||||||
|
|
||||||
|
#if lnx_vr
|
||||||
|
m.setFrom(camera.leftV);
|
||||||
|
#else
|
||||||
m.setFrom(camera.V);
|
m.setFrom(camera.V);
|
||||||
|
#end
|
||||||
|
|
||||||
#if lnx_csm
|
#if lnx_csm
|
||||||
if (camSlicedP == null) {
|
if (camSlicedP == null) {
|
||||||
@ -514,7 +522,7 @@ class LightObject extends Object {
|
|||||||
updateLightsArray(); // TODO: only update on light change
|
updateLightsArray(); // TODO: only update on light change
|
||||||
}
|
}
|
||||||
|
|
||||||
static function updateLightsArray() {
|
public static function updateLightsArray() {
|
||||||
if (lightsArray == null) { // vec4x3 - 1: pos, a, color, b, 2: dir, c
|
if (lightsArray == null) { // vec4x3 - 1: pos, a, color, b, 2: dir, c
|
||||||
lightsArray = new Float32Array(maxLights * 4 * 3);
|
lightsArray = new Float32Array(maxLights * 4 * 3);
|
||||||
#if lnx_spot
|
#if lnx_spot
|
||||||
@ -573,6 +581,49 @@ class LightObject extends Object {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// VR deferred stereo we save original light positions before adjusting for per-eye rendering
|
||||||
|
#if lnx_vr
|
||||||
|
public static function saveOriginalLightPositions() {
|
||||||
|
if (lightsArray == null) return;
|
||||||
|
|
||||||
|
if (originalLightPositions == null) {
|
||||||
|
originalLightPositions = new Float32Array(lightsArray.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i in 0...lightsArray.length) {
|
||||||
|
originalLightPositions[i] = lightsArray[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// negative for left eye, positive for right eye
|
||||||
|
public static function adjustLightPositionsForVREye(offsetX: Float, rightVec: Vec4) {
|
||||||
|
if (lightsArray == null) return;
|
||||||
|
|
||||||
|
var lights = Scene.active.lights;
|
||||||
|
var n = lights.length > maxLights ? maxLights : lights.length;
|
||||||
|
var i = 0;
|
||||||
|
|
||||||
|
for (l in lights) {
|
||||||
|
if (discardLightCulled(l)) continue;
|
||||||
|
if (i >= n) break;
|
||||||
|
|
||||||
|
lightsArray[i * 12 ] = originalLightPositions[i * 12 ] + rightVec.x * offsetX;
|
||||||
|
lightsArray[i * 12 + 1] = originalLightPositions[i * 12 + 1] + rightVec.y * offsetX;
|
||||||
|
lightsArray[i * 12 + 2] = originalLightPositions[i * 12 + 2] + rightVec.z * offsetX;
|
||||||
|
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function restoreOriginalLightPositions() {
|
||||||
|
if (lightsArray == null || originalLightPositions == null) return;
|
||||||
|
|
||||||
|
for (i in 0...lightsArray.length) {
|
||||||
|
lightsArray[i] = originalLightPositions[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#end
|
||||||
|
|
||||||
public static function updateLWVPMatrixArray(object: Object, type: String) {
|
public static function updateLWVPMatrixArray(object: Object, type: String) {
|
||||||
if (LWVPMatrixArray == null) {
|
if (LWVPMatrixArray == null) {
|
||||||
LWVPMatrixArray = new Float32Array(maxLightsCluster * 16);
|
LWVPMatrixArray = new Float32Array(maxLightsCluster * 16);
|
||||||
@ -624,8 +675,8 @@ class LightObject extends Object {
|
|||||||
LWVPMatrixArray[i * 16 + 13] = m._31;
|
LWVPMatrixArray[i * 16 + 13] = m._31;
|
||||||
LWVPMatrixArray[i * 16 + 14] = m._32;
|
LWVPMatrixArray[i * 16 + 14] = m._32;
|
||||||
LWVPMatrixArray[i * 16 + 15] = m._33;
|
LWVPMatrixArray[i * 16 + 15] = m._33;
|
||||||
|
i++; // only increment in light type
|
||||||
}
|
}
|
||||||
i++;
|
|
||||||
}
|
}
|
||||||
return LWVPMatrixArray;
|
return LWVPMatrixArray;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -24,6 +24,7 @@ class MeshObject extends Object {
|
|||||||
public var render_emitter = true;
|
public var render_emitter = true;
|
||||||
#end
|
#end
|
||||||
public var cameraDistance: Float;
|
public var cameraDistance: Float;
|
||||||
|
public var cameraList: Array<String> = null;
|
||||||
public var screenSize = 0.0;
|
public var screenSize = 0.0;
|
||||||
public var frustumCulling = true;
|
public var frustumCulling = true;
|
||||||
public var activeTilesheet: Tilesheet = null;
|
public var activeTilesheet: Tilesheet = null;
|
||||||
@ -235,6 +236,8 @@ class MeshObject extends Object {
|
|||||||
if (cullMesh(context, Scene.active.camera, RenderPath.active.light)) return;
|
if (cullMesh(context, Scene.active.camera, RenderPath.active.light)) return;
|
||||||
var meshContext = raw != null ? context == "mesh" : false;
|
var meshContext = raw != null ? context == "mesh" : false;
|
||||||
|
|
||||||
|
if (cameraList != null && cameraList.indexOf(Scene.active.camera.name) < 0) return;
|
||||||
|
|
||||||
#if lnx_particles
|
#if lnx_particles
|
||||||
if (raw != null && raw.is_particle && particleOwner == null) return; // Instancing not yet set-up by particle system owner
|
if (raw != null && raw.is_particle && particleOwner == null) return; // Instancing not yet set-up by particle system owner
|
||||||
if (particleSystems != null && meshContext) {
|
if (particleSystems != null && meshContext) {
|
||||||
@ -245,6 +248,7 @@ class MeshObject extends Object {
|
|||||||
Scene.active.spawnObject(psys.data.raw.instance_object, null, function(o: Object) {
|
Scene.active.spawnObject(psys.data.raw.instance_object, null, function(o: Object) {
|
||||||
if (o != null) {
|
if (o != null) {
|
||||||
var c: MeshObject = cast o;
|
var c: MeshObject = cast o;
|
||||||
|
c.cameraList = this.cameraList;
|
||||||
particleChildren.push(c);
|
particleChildren.push(c);
|
||||||
c.particleOwner = this;
|
c.particleOwner = this;
|
||||||
c.particleIndex = particleChildren.length - 1;
|
c.particleIndex = particleChildren.length - 1;
|
||||||
@ -298,6 +302,10 @@ class MeshObject extends Object {
|
|||||||
|
|
||||||
// Render mesh
|
// Render mesh
|
||||||
var ldata = lod.data;
|
var ldata = lod.data;
|
||||||
|
|
||||||
|
// Next pass rendering first (inverse order)
|
||||||
|
renderNextPass(g, context, bindParams, lod);
|
||||||
|
|
||||||
for (i in 0...ldata.geom.indexBuffers.length) {
|
for (i in 0...ldata.geom.indexBuffers.length) {
|
||||||
|
|
||||||
var mi = ldata.geom.materialIndices[i];
|
var mi = ldata.geom.materialIndices[i];
|
||||||
@ -401,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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -21,6 +21,13 @@ class MorphTarget {
|
|||||||
public var morphDataNor: Image;
|
public var morphDataNor: Image;
|
||||||
public var morphMap: Map<String, Int> = null;
|
public var morphMap: Map<String, Int> = null;
|
||||||
|
|
||||||
|
public var isDirty: Bool = true;
|
||||||
|
var previousWeights: Float32Array;
|
||||||
|
var changeThreshold: FastFloat = 0.001; // skip smaller
|
||||||
|
var pendingUpdates: Map<Int, Float> = null;
|
||||||
|
var batchUpdateEnabled: Bool = true;
|
||||||
|
var lastFlushFrame: Int = 0;
|
||||||
|
|
||||||
public function new(data: TMorphTarget) {
|
public function new(data: TMorphTarget) {
|
||||||
initWeights(data.morph_target_defaults);
|
initWeights(data.morph_target_defaults);
|
||||||
scaling = data.morph_scale;
|
scaling = data.morph_scale;
|
||||||
@ -42,6 +49,14 @@ class MorphTarget {
|
|||||||
morphMap.set(name, i);
|
morphMap.set(name, i);
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
previousWeights = new Float32Array(morphWeights.length);
|
||||||
|
for (i in 0...morphWeights.length) {
|
||||||
|
previousWeights.set(i, morphWeights.get(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
// batch system
|
||||||
|
pendingUpdates = new Map<Int, Float>();
|
||||||
}
|
}
|
||||||
|
|
||||||
inline function initWeights(defaults: Float32Array) {
|
inline function initWeights(defaults: Float32Array) {
|
||||||
@ -54,9 +69,96 @@ class MorphTarget {
|
|||||||
public function setMorphValue(name: String, value: Float) {
|
public function setMorphValue(name: String, value: Float) {
|
||||||
var i = morphMap.get(name);
|
var i = morphMap.get(name);
|
||||||
if (i != null) {
|
if (i != null) {
|
||||||
morphWeights.set(i, value);
|
if (batchUpdateEnabled) {
|
||||||
|
pendingUpdates.set(i, value);
|
||||||
|
} else {
|
||||||
|
setMorphValueDirect(i, value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// faster indexed access
|
||||||
|
public inline function setMorphValueDirect(index: Int, value: Float) {
|
||||||
|
var current = morphWeights.get(index);
|
||||||
|
|
||||||
|
// allow explicit zero values to reset
|
||||||
|
if (value == 0.0 && current != 0.0) {
|
||||||
|
morphWeights.set(index, value);
|
||||||
|
isDirty = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var delta = value - current;
|
||||||
|
|
||||||
|
if (delta < -changeThreshold || delta > changeThreshold) {
|
||||||
|
morphWeights.set(index, value);
|
||||||
|
isDirty = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// flush pending batch
|
||||||
|
public function flushBatchedUpdates() {
|
||||||
|
if (pendingUpdates.keys().hasNext()) {
|
||||||
|
var anyChanged = false;
|
||||||
|
var hasZeros = false;
|
||||||
|
|
||||||
|
for (index in pendingUpdates.keys()) {
|
||||||
|
var value = pendingUpdates.get(index);
|
||||||
|
if (value == null) continue;
|
||||||
|
|
||||||
|
if (value == 0.0) hasZeros = true;
|
||||||
|
|
||||||
|
var current = morphWeights.get(index);
|
||||||
|
var delta = value - current;
|
||||||
|
|
||||||
|
if (value == 0.0 && current != 0.0) {
|
||||||
|
try{
|
||||||
|
morphWeights.set(index, cast value);
|
||||||
|
}catch(e){
|
||||||
|
trace("ERROR: " + e);
|
||||||
|
}
|
||||||
|
anyChanged = true;
|
||||||
|
}
|
||||||
|
else if (delta < -changeThreshold || delta > changeThreshold) {
|
||||||
|
morphWeights.set(index, cast value);
|
||||||
|
anyChanged = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pendingUpdates.clear();
|
||||||
|
|
||||||
|
if (anyChanged || hasZeros) {
|
||||||
|
isDirty = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public inline function markClean() {
|
||||||
|
isDirty = false;
|
||||||
|
for (i in 0...morphWeights.length) {
|
||||||
|
previousWeights.set(i, morphWeights.get(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public inline function markDirty() {
|
||||||
|
isDirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// toggle batch mode
|
||||||
|
public inline function setBatchMode(enabled: Bool) {
|
||||||
|
if (!enabled && batchUpdateEnabled) {
|
||||||
|
flushBatchedUpdates();
|
||||||
|
}
|
||||||
|
batchUpdateEnabled = enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function resetAllWeights() {
|
||||||
|
for (i in 0...morphWeights.length) {
|
||||||
|
morphWeights.set(i, 0.0);
|
||||||
|
}
|
||||||
|
pendingUpdates.clear();
|
||||||
|
isDirty = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#end
|
#end
|
||||||
|
|||||||
@ -210,8 +210,12 @@ class Object {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#if lnx_skin
|
#if lnx_skin
|
||||||
public function getBoneAnimation(armatureUid): BoneAnimation {
|
public function getBoneAnimation(armatureUid: Int): BoneAnimation {
|
||||||
for (a in Scene.active.animations) if (a.armature != null && a.armature.uid == armatureUid) return cast a;
|
for (a in Scene.active.animations) {
|
||||||
|
if (a.armature != null && a.armature.uid == armatureUid) {
|
||||||
|
return cast a;
|
||||||
|
}
|
||||||
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
|
|||||||
@ -24,6 +24,9 @@ class ObjectAnimation extends Animation {
|
|||||||
|
|
||||||
public var transformMap: Map<String, FastFloat>;
|
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",
|
public static var trackNames: Array<String> = [ "xloc", "yloc", "zloc",
|
||||||
"xrot", "yrot", "zrot",
|
"xrot", "yrot", "zrot",
|
||||||
"qwrot", "qxrot", "qyrot", "qzrot",
|
"qwrot", "qxrot", "qyrot", "qzrot",
|
||||||
@ -39,7 +42,6 @@ class ObjectAnimation extends Animation {
|
|||||||
isSkinned = false;
|
isSkinned = false;
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
function getAction(action: String): TObj {
|
function getAction(action: String): TObj {
|
||||||
for (a in oactions) if (a != null && a.objects[0].name == action) return a.objects[0];
|
for (a in oactions) if (a != null && a.objects[0].name == action) return a.objects[0];
|
||||||
return null;
|
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) {
|
override public function play(action = "", onComplete: Void->Void = null, blendTime = 0.0, speed = 1.0, loop = true) {
|
||||||
super.play(action, onComplete, blendTime, speed, loop);
|
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);
|
oaction = getAction(this.action);
|
||||||
if (oaction != null) {
|
if (oaction != null) {
|
||||||
isSampled = oaction.sampled != null && oaction.sampled;
|
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();
|
Animation.beginProfile();
|
||||||
#end
|
#end
|
||||||
|
|
||||||
if(transformMap == null) transformMap = new Map();
|
if (transformMap == null) transformMap = new Map();
|
||||||
transformMap = initTransformMap();
|
transformMap = initTransformMap();
|
||||||
|
|
||||||
super.update(delta);
|
super.update(delta);
|
||||||
|
if (defaultSampler != null) defaultSampler.paused = paused;
|
||||||
if (paused) return;
|
if (paused) return;
|
||||||
if(updateAnimation == null) return;
|
if (updateAnimation == null) return;
|
||||||
if (!isSkinned) updateObjectAnimation();
|
if (!isSkinned) updateObjectAnimation();
|
||||||
|
|
||||||
#if lnx_debug
|
#if lnx_debug
|
||||||
@ -75,7 +97,9 @@ class ObjectAnimation extends Animation {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public override function getTotalFrames(sampler: ActionSampler): Int {
|
public override function getTotalFrames(sampler: ActionSampler): Int {
|
||||||
var track = getAction(sampler.action).anim.tracks[0];
|
var action = getAction(sampler.action);
|
||||||
|
if (action == null || action.anim == null || action.anim.tracks == null || action.anim.tracks.length == 0) return 0;
|
||||||
|
var track = action.anim.tracks[0];
|
||||||
return Std.int(track.frames[track.frames.length - 1] - track.frames[0]);
|
return Std.int(track.frames[track.frames.length - 1] - track.frames[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -8,6 +8,8 @@ import kha.arrays.Float32Array;
|
|||||||
import iron.data.Data;
|
import iron.data.Data;
|
||||||
import iron.data.ParticleData;
|
import iron.data.ParticleData;
|
||||||
import iron.data.SceneFormat;
|
import iron.data.SceneFormat;
|
||||||
|
import iron.data.Geometry;
|
||||||
|
import iron.data.MeshData;
|
||||||
import iron.system.Time;
|
import iron.system.Time;
|
||||||
import iron.math.Mat4;
|
import iron.math.Mat4;
|
||||||
import iron.math.Quat;
|
import iron.math.Quat;
|
||||||
@ -17,6 +19,7 @@ import iron.math.Vec4;
|
|||||||
class ParticleSystem {
|
class ParticleSystem {
|
||||||
public var data: ParticleData;
|
public var data: ParticleData;
|
||||||
public var speed = 1.0;
|
public var speed = 1.0;
|
||||||
|
public var dynamicEmitter: Bool = true;
|
||||||
var currentSpeed = 0.0;
|
var currentSpeed = 0.0;
|
||||||
var particles: Array<Particle>;
|
var particles: Array<Particle>;
|
||||||
var ready: Bool;
|
var ready: Bool;
|
||||||
@ -52,6 +55,12 @@ class ParticleSystem {
|
|||||||
|
|
||||||
var random = 0.0;
|
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) {
|
public function new(sceneName: String, pref: TParticleReference) {
|
||||||
seed = pref.seed;
|
seed = pref.seed;
|
||||||
currentSpeed = speed;
|
currentSpeed = speed;
|
||||||
@ -62,6 +71,12 @@ class ParticleSystem {
|
|||||||
Data.getParticle(sceneName, pref.particle, function(b: ParticleData) {
|
Data.getParticle(sceneName, pref.particle, function(b: ParticleData) {
|
||||||
data = b;
|
data = b;
|
||||||
r = data.raw;
|
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) {
|
if (Scene.active.raw.gravity != null) {
|
||||||
gx = Scene.active.raw.gravity[0] * r.weight_gravity;
|
gx = Scene.active.raw.gravity[0] * r.weight_gravity;
|
||||||
gy = Scene.active.raw.gravity[1] * r.weight_gravity;
|
gy = Scene.active.raw.gravity[1] * r.weight_gravity;
|
||||||
@ -98,6 +113,8 @@ class ParticleSystem {
|
|||||||
lap = 0;
|
lap = 0;
|
||||||
lapTime = 0;
|
lapTime = 0;
|
||||||
speed = currentSpeed;
|
speed = currentSpeed;
|
||||||
|
lastSpawnedCount = 0;
|
||||||
|
instancedData = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function pause() {
|
public function pause() {
|
||||||
@ -130,8 +147,13 @@ class ParticleSystem {
|
|||||||
|
|
||||||
// Copy owner world transform but discard scale
|
// Copy owner world transform but discard scale
|
||||||
owner.transform.world.decompose(ownerLoc, ownerRot, ownerScl);
|
owner.transform.world.decompose(ownerLoc, ownerRot, ownerScl);
|
||||||
object.transform.loc = ownerLoc;
|
if (dynamicEmitter) {
|
||||||
object.transform.rot = ownerRot;
|
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
|
// Set particle size per particle system
|
||||||
object.transform.scale = new Vec4(r.particle_size, r.particle_size, r.particle_size, 1);
|
object.transform.scale = new Vec4(r.particle_size, r.particle_size, r.particle_size, 1);
|
||||||
@ -159,12 +181,17 @@ class ParticleSystem {
|
|||||||
end();
|
end();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (lap > prevLap && r.loop) {
|
||||||
|
lastSpawnedCount = 0;
|
||||||
|
}
|
||||||
|
|
||||||
updateGpu(object, owner);
|
updateGpu(object, owner);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getData(): Mat4 {
|
public function getData(): Mat4 {
|
||||||
var hair = r.type == 1;
|
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._01 = hair ? 1 / particles.length : spawnRate;
|
||||||
m._02 = hair ? 1 : lifetime;
|
m._02 = hair ? 1 : lifetime;
|
||||||
m._03 = particles.length;
|
m._03 = particles.length;
|
||||||
@ -187,17 +214,26 @@ class ParticleSystem {
|
|||||||
return r.size_random;
|
return r.size_random;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getRandom(): FastFloat {
|
public inline function getRandom(): FastFloat {
|
||||||
return random;
|
return random;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getSize(): FastFloat {
|
public inline function getSize(): FastFloat {
|
||||||
return r.particle_size;
|
return r.particle_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateGpu(object: MeshObject, owner: MeshObject) {
|
function updateGpu(object: MeshObject, owner: MeshObject) {
|
||||||
if (!object.data.geom.instanced) setupGeomGpu(object, owner);
|
if (dynamicEmitter) {
|
||||||
// GPU particles transform is attached to owner object
|
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) {
|
function setupGeomGpu(object: MeshObject, owner: MeshObject) {
|
||||||
@ -258,13 +294,129 @@ class ParticleSystem {
|
|||||||
object.data.geom.setupInstanced(instancedData, 1, Usage.StaticUsage);
|
object.data.geom.setupInstanced(instancedData, 1, Usage.StaticUsage);
|
||||||
}
|
}
|
||||||
|
|
||||||
function fhash(n: Int): Float {
|
// allocate instanced VB once for this object
|
||||||
var s = n + 1.0;
|
function setupGeomGpuDynamic(object: MeshObject, owner: MeshObject) {
|
||||||
s *= 9301.0 % s;
|
if (instancedData == null) instancedData = new Float32Array(particles.length * 3);
|
||||||
s = (s * 9301.0 + 49297.0) % 233280.0;
|
lastSpawnedCount = 0;
|
||||||
return s / 233280.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() {}
|
public function remove() {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -7,16 +7,20 @@ import kha.graphics4.TextureFilter;
|
|||||||
import kha.graphics4.MipMapFilter;
|
import kha.graphics4.MipMapFilter;
|
||||||
import kha.arrays.Float32Array;
|
import kha.arrays.Float32Array;
|
||||||
import iron.math.Vec4;
|
import iron.math.Vec4;
|
||||||
|
import iron.math.Mat4;
|
||||||
import iron.math.Quat;
|
import iron.math.Quat;
|
||||||
import iron.math.Mat3;
|
import iron.math.Mat3;
|
||||||
import iron.math.Mat4;
|
|
||||||
import iron.data.WorldData;
|
|
||||||
import iron.data.MaterialData;
|
import iron.data.MaterialData;
|
||||||
import iron.data.ShaderData;
|
import iron.data.ShaderData;
|
||||||
import iron.data.SceneFormat;
|
import iron.data.WorldData;
|
||||||
|
import iron.data.SceneFormat.TShaderConstant;
|
||||||
|
import iron.data.SceneFormat.TBindConstant;
|
||||||
|
import iron.object.Transform;
|
||||||
|
import iron.object.LightObject;
|
||||||
|
import iron.Scene;
|
||||||
|
import iron.RenderPath;
|
||||||
import iron.system.Input;
|
import iron.system.Input;
|
||||||
import iron.system.Time;
|
import iron.system.Time;
|
||||||
import iron.RenderPath;
|
|
||||||
using StringTools;
|
using StringTools;
|
||||||
|
|
||||||
// Structure for setting shader uniforms
|
// Structure for setting shader uniforms
|
||||||
@ -38,6 +42,7 @@ class Uniforms {
|
|||||||
public static var helpMat = Mat4.identity();
|
public static var helpMat = Mat4.identity();
|
||||||
public static var helpMat2 = Mat4.identity();
|
public static var helpMat2 = Mat4.identity();
|
||||||
public static var helpMat3 = Mat3.identity();
|
public static var helpMat3 = Mat3.identity();
|
||||||
|
public static var helpMat4 = Mat4.identity();
|
||||||
public static var helpVec = new Vec4();
|
public static var helpVec = new Vec4();
|
||||||
public static var helpVec2 = new Vec4();
|
public static var helpVec2 = new Vec4();
|
||||||
public static var helpQuat = new Quat(); // Keep at identity
|
public static var helpQuat = new Quat(); // Keep at identity
|
||||||
@ -47,6 +52,10 @@ class Uniforms {
|
|||||||
public static var externalVec4Links: Array<Object->MaterialData->String->Vec4> = null;
|
public static var externalVec4Links: Array<Object->MaterialData->String->Vec4> = null;
|
||||||
public static var externalVec3Links: Array<Object->MaterialData->String->Vec4> = null;
|
public static var externalVec3Links: Array<Object->MaterialData->String->Vec4> = null;
|
||||||
public static var externalVec2Links: Array<Object->MaterialData->String->Vec4> = null;
|
public static var externalVec2Links: Array<Object->MaterialData->String->Vec4> = null;
|
||||||
|
|
||||||
|
public static var eyeLeftCallCount = 0;
|
||||||
|
public static var lastFrameChecked = -1;
|
||||||
|
|
||||||
public static var externalFloatLinks: Array<Object->MaterialData->String->Null<kha.FastFloat>> = null;
|
public static var externalFloatLinks: Array<Object->MaterialData->String->Null<kha.FastFloat>> = null;
|
||||||
public static var externalFloatsLinks: Array<Object->MaterialData->String->Float32Array> = null;
|
public static var externalFloatsLinks: Array<Object->MaterialData->String->Float32Array> = null;
|
||||||
public static var externalIntLinks: Array<Object->MaterialData->String->Null<Int>> = null;
|
public static var externalIntLinks: Array<Object->MaterialData->String->Null<Int>> = null;
|
||||||
@ -59,6 +68,10 @@ class Uniforms {
|
|||||||
public static var defaultFilter = TextureFilter.LinearFilter;
|
public static var defaultFilter = TextureFilter.LinearFilter;
|
||||||
#end
|
#end
|
||||||
|
|
||||||
|
#if lnx_morph_target
|
||||||
|
public static var forceUploadMorphWeights: Bool = false;
|
||||||
|
#end
|
||||||
|
|
||||||
public static function setContextConstants(g: Graphics, context: ShaderContext, bindParams: Array<String>) {
|
public static function setContextConstants(g: Graphics, context: ShaderContext, bindParams: Array<String>) {
|
||||||
if (context.raw.constants != null) {
|
if (context.raw.constants != null) {
|
||||||
for (i in 0...context.raw.constants.length) {
|
for (i in 0...context.raw.constants.length) {
|
||||||
@ -290,6 +303,89 @@ class Uniforms {
|
|||||||
helpMat.getInverse(helpMat);
|
helpMat.getInverse(helpMat);
|
||||||
m = helpMat;
|
m = helpMat;
|
||||||
}
|
}
|
||||||
|
#if lnx_vr
|
||||||
|
case "_inverseViewProjectionMatrixLeft": {
|
||||||
|
var vr = kha.vr.VrInterface.instance;
|
||||||
|
if (vr != null && vr.IsPresenting()) {
|
||||||
|
var leftView = vr.GetViewMatrix(0);
|
||||||
|
var leftProj = vr.GetProjectionMatrix(0);
|
||||||
|
helpMat._00 = leftView._00; helpMat._01 = leftView._01; helpMat._02 = leftView._02; helpMat._03 = leftView._03;
|
||||||
|
helpMat._10 = leftView._10; helpMat._11 = leftView._11; helpMat._12 = leftView._12; helpMat._13 = leftView._13;
|
||||||
|
helpMat._20 = leftView._20; helpMat._21 = leftView._21; helpMat._22 = leftView._22; helpMat._23 = leftView._23;
|
||||||
|
helpMat._30 = leftView._30; helpMat._31 = leftView._31; helpMat._32 = leftView._32; helpMat._33 = leftView._33;
|
||||||
|
helpMat2._00 = leftProj._00; helpMat2._01 = leftProj._01; helpMat2._02 = leftProj._02; helpMat2._03 = leftProj._03;
|
||||||
|
helpMat2._10 = leftProj._10; helpMat2._11 = leftProj._11; helpMat2._12 = leftProj._12; helpMat2._13 = leftProj._13;
|
||||||
|
helpMat2._20 = leftProj._20; helpMat2._21 = leftProj._21; helpMat2._22 = leftProj._22; helpMat2._23 = leftProj._23;
|
||||||
|
helpMat2._30 = leftProj._30; helpMat2._31 = leftProj._31; helpMat2._32 = leftProj._32; helpMat2._33 = leftProj._33;
|
||||||
|
helpMat.multmat(helpMat2);
|
||||||
|
helpMat.getInverse(helpMat);
|
||||||
|
} else if (iron.RenderPath.isVRSimulateMode()) {
|
||||||
|
var ipd_offset = 0.032 * 35.0; // Match eye offset
|
||||||
|
var rightVec = camera.rightWorld();
|
||||||
|
|
||||||
|
var eyeLeftX = camera.transform.worldx() - rightVec.x * ipd_offset;
|
||||||
|
var eyeLeftY = camera.transform.worldy() - rightVec.y * ipd_offset;
|
||||||
|
var eyeLeftZ = camera.transform.worldz() - rightVec.z * ipd_offset;
|
||||||
|
|
||||||
|
helpMat.setFrom(camera.transform.world);
|
||||||
|
helpMat._30 = eyeLeftX;
|
||||||
|
helpMat._31 = eyeLeftY;
|
||||||
|
helpMat._32 = eyeLeftZ;
|
||||||
|
helpMat.getInverse(helpMat); // Now it's a view matrix
|
||||||
|
|
||||||
|
helpMat.multmat(camera.P);
|
||||||
|
helpMat.getInverse(helpMat);
|
||||||
|
} else {
|
||||||
|
helpMat.setFrom(camera.V);
|
||||||
|
helpMat.multmat(camera.P);
|
||||||
|
helpMat.getInverse(helpMat);
|
||||||
|
}
|
||||||
|
m = helpMat;
|
||||||
|
}
|
||||||
|
case "_inverseViewProjectionMatrixRight": {
|
||||||
|
var vr = kha.vr.VrInterface.instance;
|
||||||
|
if (vr != null && vr.IsPresenting()) {
|
||||||
|
var rightView = vr.GetViewMatrix(1);
|
||||||
|
var rightProj = vr.GetProjectionMatrix(1);
|
||||||
|
// kha.math.FastMatrix4 to iron.math.Mat4
|
||||||
|
helpMat2._00 = rightView._00; helpMat2._01 = rightView._01; helpMat2._02 = rightView._02; helpMat2._03 = rightView._03;
|
||||||
|
helpMat2._10 = rightView._10; helpMat2._11 = rightView._11; helpMat2._12 = rightView._12; helpMat2._13 = rightView._13;
|
||||||
|
helpMat2._20 = rightView._20; helpMat2._21 = rightView._21; helpMat2._22 = rightView._22; helpMat2._23 = rightView._23;
|
||||||
|
helpMat2._30 = rightView._30; helpMat2._31 = rightView._31; helpMat2._32 = rightView._32; helpMat2._33 = rightView._33;
|
||||||
|
helpMat4._00 = rightProj._00; helpMat4._01 = rightProj._01; helpMat4._02 = rightProj._02; helpMat4._03 = rightProj._03;
|
||||||
|
helpMat4._10 = rightProj._10; helpMat4._11 = rightProj._11; helpMat4._12 = rightProj._12; helpMat4._13 = rightProj._13;
|
||||||
|
helpMat4._20 = rightProj._20; helpMat4._21 = rightProj._21; helpMat4._22 = rightProj._22; helpMat4._23 = rightProj._23;
|
||||||
|
helpMat4._30 = rightProj._30; helpMat4._31 = rightProj._31; helpMat4._32 = rightProj._32; helpMat4._33 = rightProj._33;
|
||||||
|
helpMat2.multmat(helpMat4);
|
||||||
|
helpMat2.getInverse(helpMat2);
|
||||||
|
m = helpMat2;
|
||||||
|
} else if (iron.RenderPath.isVRSimulateMode()) {
|
||||||
|
var ipd_offset = 0.032 * 35.0;
|
||||||
|
var rightVec = camera.rightWorld();
|
||||||
|
|
||||||
|
// calculate right eye position in world space
|
||||||
|
var eyeRightX = camera.transform.worldx() + rightVec.x * ipd_offset;
|
||||||
|
var eyeRightY = camera.transform.worldy() + rightVec.y * ipd_offset;
|
||||||
|
var eyeRightZ = camera.transform.worldz() + rightVec.z * ipd_offset;
|
||||||
|
|
||||||
|
helpMat2.setFrom(camera.transform.world);
|
||||||
|
helpMat2._30 = eyeRightX;
|
||||||
|
helpMat2._31 = eyeRightY;
|
||||||
|
helpMat2._32 = eyeRightZ;
|
||||||
|
helpMat2.getInverse(helpMat2);
|
||||||
|
|
||||||
|
helpMat2.multmat(camera.P);
|
||||||
|
helpMat2.getInverse(helpMat2);
|
||||||
|
m = helpMat2;
|
||||||
|
} else {
|
||||||
|
// fallback to center camera
|
||||||
|
helpMat2.setFrom(camera.V);
|
||||||
|
helpMat2.multmat(camera.P);
|
||||||
|
helpMat2.getInverse(helpMat2);
|
||||||
|
m = helpMat2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#end
|
||||||
case "_viewProjectionMatrix": {
|
case "_viewProjectionMatrix": {
|
||||||
#if lnx_centerworld
|
#if lnx_centerworld
|
||||||
m = vmat(camera.V);
|
m = vmat(camera.V);
|
||||||
@ -402,6 +498,28 @@ class Uniforms {
|
|||||||
v = helpVec;
|
v = helpVec;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#if lnx_vr
|
||||||
|
case "_pointPositionLeft": {
|
||||||
|
var point = RenderPath.active.point;
|
||||||
|
if (point != null) {
|
||||||
|
var lightWorldX = point.transform.worldx();
|
||||||
|
var lightWorldY = point.transform.worldy();
|
||||||
|
var lightWorldZ = point.transform.worldz();
|
||||||
|
helpVec.set(lightWorldX, lightWorldY, lightWorldZ);
|
||||||
|
v = helpVec;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case "_pointPositionRight": {
|
||||||
|
var point = RenderPath.active.point;
|
||||||
|
if (point != null) {
|
||||||
|
var lightWorldX = point.transform.worldx();
|
||||||
|
var lightWorldY = point.transform.worldy();
|
||||||
|
var lightWorldZ = point.transform.worldz();
|
||||||
|
helpVec.set(lightWorldX, lightWorldY, lightWorldZ);
|
||||||
|
v = helpVec;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#end
|
||||||
#if lnx_spot
|
#if lnx_spot
|
||||||
case "_spotDirection": {
|
case "_spotDirection": {
|
||||||
var point = RenderPath.active.point;
|
var point = RenderPath.active.point;
|
||||||
@ -488,6 +606,84 @@ class Uniforms {
|
|||||||
helpVec = camera.rightWorld().normalize();
|
helpVec = camera.rightWorld().normalize();
|
||||||
v = helpVec;
|
v = helpVec;
|
||||||
}
|
}
|
||||||
|
#if lnx_vr
|
||||||
|
case "_eyeLeft": {
|
||||||
|
var currentFrame = iron.RenderPath.active.frame;
|
||||||
|
if (currentFrame != lastFrameChecked) {
|
||||||
|
eyeLeftCallCount = 0;
|
||||||
|
lastFrameChecked = currentFrame;
|
||||||
|
}
|
||||||
|
eyeLeftCallCount++;
|
||||||
|
|
||||||
|
var vr = kha.vr.VrInterface.instance;
|
||||||
|
if (vr != null && vr.IsPresenting()) {
|
||||||
|
var leftViewMatrix = vr.GetViewMatrix(0);
|
||||||
|
var invLeft = leftViewMatrix.inverse();
|
||||||
|
helpVec.set(invLeft._30, invLeft._31, invLeft._32);
|
||||||
|
// trace("eyeLeft: " + helpVec.x + ", " + helpVec.y + ", " + helpVec.z);
|
||||||
|
} else if (iron.RenderPath.isVRSimulateMode()) {
|
||||||
|
var ipd_offset = 0.032 * 35.0;
|
||||||
|
var rightVec = camera.rightWorld();
|
||||||
|
var centerX = camera.transform.worldx();
|
||||||
|
var centerY = camera.transform.worldy();
|
||||||
|
var centerZ = camera.transform.worldz();
|
||||||
|
helpVec.set(
|
||||||
|
centerX - rightVec.x * ipd_offset,
|
||||||
|
centerY - rightVec.y * ipd_offset,
|
||||||
|
centerZ - rightVec.z * ipd_offset
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
helpVec.set(camera.transform.worldx(), camera.transform.worldy(), camera.transform.worldz());
|
||||||
|
}
|
||||||
|
v = helpVec;
|
||||||
|
}
|
||||||
|
case "_eyeRight": {
|
||||||
|
var vr = kha.vr.VrInterface.instance;
|
||||||
|
if (vr != null && vr.IsPresenting()) {
|
||||||
|
var rightViewMatrix = vr.GetViewMatrix(1);
|
||||||
|
var invRight = rightViewMatrix.inverse();
|
||||||
|
helpVec.set(invRight._30, invRight._31, invRight._32);
|
||||||
|
} else if (iron.RenderPath.isVRSimulateMode()) {
|
||||||
|
var ipd_offset = 0.032 * 35.0;
|
||||||
|
var rightVec = camera.rightWorld();
|
||||||
|
var centerX = camera.transform.worldx();
|
||||||
|
var centerY = camera.transform.worldy();
|
||||||
|
var centerZ = camera.transform.worldz();
|
||||||
|
helpVec.set(
|
||||||
|
centerX + rightVec.x * ipd_offset,
|
||||||
|
centerY + rightVec.y * ipd_offset,
|
||||||
|
centerZ + rightVec.z * ipd_offset
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
helpVec.set(camera.transform.worldx(), camera.transform.worldy(), camera.transform.worldz());
|
||||||
|
}
|
||||||
|
v = helpVec;
|
||||||
|
}
|
||||||
|
case "_eyeLookLeft": {
|
||||||
|
var vr = kha.vr.VrInterface.instance;
|
||||||
|
if (vr != null && vr.IsPresenting()) {
|
||||||
|
var leftViewMatrix = vr.GetViewMatrix(0);
|
||||||
|
var invLeft = leftViewMatrix.inverse();
|
||||||
|
helpVec.set(-invLeft._20, -invLeft._21, -invLeft._22);
|
||||||
|
helpVec.normalize();
|
||||||
|
} else {
|
||||||
|
helpVec = camera.lookWorld().normalize();
|
||||||
|
}
|
||||||
|
v = helpVec;
|
||||||
|
}
|
||||||
|
case "_eyeLookRight": {
|
||||||
|
var vr = kha.vr.VrInterface.instance;
|
||||||
|
if (vr != null && vr.IsPresenting()) {
|
||||||
|
var rightViewMatrix = vr.GetViewMatrix(1);
|
||||||
|
var invRight = rightViewMatrix.inverse();
|
||||||
|
helpVec.set(-invRight._20, -invRight._21, -invRight._22);
|
||||||
|
helpVec.normalize();
|
||||||
|
} else {
|
||||||
|
helpVec = camera.lookWorld().normalize();
|
||||||
|
}
|
||||||
|
v = helpVec;
|
||||||
|
}
|
||||||
|
#end
|
||||||
case "_backgroundCol": {
|
case "_backgroundCol": {
|
||||||
if (camera.data.raw.clear_color != null) helpVec.set(camera.data.raw.clear_color[0], camera.data.raw.clear_color[1], camera.data.raw.clear_color[2]);
|
if (camera.data.raw.clear_color != null) helpVec.set(camera.data.raw.clear_color[0], camera.data.raw.clear_color[1], camera.data.raw.clear_color[2]);
|
||||||
v = helpVec;
|
v = helpVec;
|
||||||
@ -1165,7 +1361,19 @@ class Uniforms {
|
|||||||
#end // lnx_clusters
|
#end // lnx_clusters
|
||||||
#if lnx_morph_target
|
#if lnx_morph_target
|
||||||
case "_morphWeights": {
|
case "_morphWeights": {
|
||||||
fa = cast(object, MeshObject).morphTarget.morphWeights;
|
var morphTarget = cast(object, MeshObject).morphTarget;
|
||||||
|
morphTarget.flushBatchedUpdates();
|
||||||
|
if (forceUploadMorphWeights) {
|
||||||
|
fa = morphTarget.morphWeights;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (morphTarget.isDirty) {
|
||||||
|
fa = morphTarget.morphWeights;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#end
|
#end
|
||||||
}
|
}
|
||||||
@ -1179,6 +1387,12 @@ class Uniforms {
|
|||||||
|
|
||||||
if (fa == null) return;
|
if (fa == null) return;
|
||||||
g.setFloats(location, fa);
|
g.setFloats(location, fa);
|
||||||
|
|
||||||
|
#if lnx_morph_target
|
||||||
|
if (c.link == "_morphWeights") {
|
||||||
|
cast(object, MeshObject).morphTarget.markClean();
|
||||||
|
}
|
||||||
|
#end
|
||||||
}
|
}
|
||||||
else if (c.type == "int") {
|
else if (c.type == "int") {
|
||||||
var i: Null<Int> = null;
|
var i: Null<Int> = null;
|
||||||
@ -1207,6 +1421,7 @@ class Uniforms {
|
|||||||
if (materialContext.raw.bind_constants != null) {
|
if (materialContext.raw.bind_constants != null) {
|
||||||
for (i in 0...materialContext.raw.bind_constants.length) {
|
for (i in 0...materialContext.raw.bind_constants.length) {
|
||||||
var matc = materialContext.raw.bind_constants[i];
|
var matc = materialContext.raw.bind_constants[i];
|
||||||
|
if (matc == null) continue;
|
||||||
var pos = -1;
|
var pos = -1;
|
||||||
for (i in 0...context.raw.constants.length) {
|
for (i in 0...context.raw.constants.length) {
|
||||||
if (context.raw.constants[i].name == matc.name) {
|
if (context.raw.constants[i].name == matc.name) {
|
||||||
|
|||||||
@ -3,6 +3,14 @@ package iron.system;
|
|||||||
class Time {
|
class Time {
|
||||||
public static var scale = 1.0;
|
public static var scale = 1.0;
|
||||||
|
|
||||||
|
// TODO: VR Frame Time Override - used to sync physics with VR headset refresh rate
|
||||||
|
#if lnx_vr
|
||||||
|
public static var vrFrameTime: Float = -1.0; // VR frame time in seconds (-1 = not in VR)
|
||||||
|
static var lastVRFrameTime: Float = 0.0;
|
||||||
|
static var vrFrameCount: Int = 0;
|
||||||
|
static var normalModeLogged: Bool = false;
|
||||||
|
#end
|
||||||
|
|
||||||
static var frequency: Null<Int> = null;
|
static var frequency: Null<Int> = null;
|
||||||
static function initFrequency() {
|
static function initFrequency() {
|
||||||
frequency = kha.Display.primary != null ? kha.Display.primary.frequency : 60;
|
frequency = kha.Display.primary != null ? kha.Display.primary.frequency : 60;
|
||||||
@ -14,7 +22,7 @@ class Time {
|
|||||||
return 1 / frequency;
|
return 1 / frequency;
|
||||||
}
|
}
|
||||||
|
|
||||||
static var _fixedStep: Null<Float>;
|
static var _fixedStep: Null<Float> = 1/60;
|
||||||
public static var fixedStep(get, never): Float;
|
public static var fixedStep(get, never): Float;
|
||||||
static function get_fixedStep(): Float {
|
static function get_fixedStep(): Float {
|
||||||
return _fixedStep;
|
return _fixedStep;
|
||||||
@ -39,14 +47,32 @@ class Time {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static inline function time(): Float {
|
public static inline function time(): Float {
|
||||||
return kha.Scheduler.time();
|
return kha.Scheduler.time() * scale;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static inline function realTime(): Float {
|
public static inline function realTime(): Float {
|
||||||
return kha.Scheduler.realTime();
|
return kha.Scheduler.realTime() * scale;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function update() {
|
public static function update() {
|
||||||
|
#if lnx_vr
|
||||||
|
// TODO: use VR frame time when in VR present mode to sync physics with headset refresh
|
||||||
|
if (vrFrameTime >= 0.0) {
|
||||||
|
if (lastVRFrameTime > 0.0) {
|
||||||
|
_delta = vrFrameTime - lastVRFrameTime;
|
||||||
|
} else {
|
||||||
|
_delta = 1.0 / 90.0; // Default to 90Hz for first VR frame
|
||||||
|
}
|
||||||
|
lastVRFrameTime = vrFrameTime;
|
||||||
|
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
if (!normalModeLogged) {
|
||||||
|
normalModeLogged = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#end
|
||||||
|
|
||||||
_delta = realTime() - lastTime;
|
_delta = realTime() - lastTime;
|
||||||
lastTime = realTime();
|
lastTime = realTime();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -94,34 +94,34 @@ class Tween {
|
|||||||
|
|
||||||
// Way too much Reflect trickery..
|
// Way too much Reflect trickery..
|
||||||
var ps = Reflect.fields(a.props);
|
var ps = Reflect.fields(a.props);
|
||||||
for (i in 0...ps.length) {
|
for (j in 0...ps.length) {
|
||||||
var p = ps[i];
|
var p = ps[j];
|
||||||
var k = a._time / a.duration;
|
var k = a._time / a.duration;
|
||||||
if (k > 1) k = 1;
|
if (k > 1) k = 1;
|
||||||
|
|
||||||
if (a._comps[i] == 1) {
|
if (a._comps[j] == 1) {
|
||||||
var fromVal: Float = a._x[i];
|
var fromVal: Float = a._x[j];
|
||||||
var toVal: Float = Reflect.getProperty(a.props, p);
|
var toVal: Float = Reflect.getProperty(a.props, p);
|
||||||
var val: Float = fromVal + (toVal - fromVal) * eases[a.ease](k);
|
var val: Float = fromVal + (toVal - fromVal) * eases[a.ease](k);
|
||||||
Reflect.setProperty(a.target, p, val);
|
Reflect.setProperty(a.target, p, val);
|
||||||
}
|
}
|
||||||
else { // _comps[i] == 4
|
else { // _comps[j] == 4
|
||||||
var obj = Reflect.getProperty(a.props, p);
|
var obj = Reflect.getProperty(a.props, p);
|
||||||
var toX: Float = Reflect.getProperty(obj, "x");
|
var toX: Float = Reflect.getProperty(obj, "x");
|
||||||
var toY: Float = Reflect.getProperty(obj, "y");
|
var toY: Float = Reflect.getProperty(obj, "y");
|
||||||
var toZ: Float = Reflect.getProperty(obj, "z");
|
var toZ: Float = Reflect.getProperty(obj, "z");
|
||||||
var toW: Float = Reflect.getProperty(obj, "w");
|
var toW: Float = Reflect.getProperty(obj, "w");
|
||||||
if (a._normalize[i]) {
|
if (a._normalize[j]) {
|
||||||
var qdot = (a._x[i] * toX) + (a._y[i] * toY) + (a._z[i] * toZ) + (a._w[i] * toW);
|
var qdot = (a._x[j] * toX) + (a._y[j] * toY) + (a._z[j] * toZ) + (a._w[j] * toW);
|
||||||
if (qdot < 0.0) {
|
if (qdot < 0.0) {
|
||||||
toX = -toX; toY = -toY; toZ = -toZ; toW = -toW;
|
toX = -toX; toY = -toY; toZ = -toZ; toW = -toW;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var x: Float = a._x[i] + (toX - a._x[i]) * eases[a.ease](k);
|
var x: Float = a._x[j] + (toX - a._x[j]) * eases[a.ease](k);
|
||||||
var y: Float = a._y[i] + (toY - a._y[i]) * eases[a.ease](k);
|
var y: Float = a._y[j] + (toY - a._y[j]) * eases[a.ease](k);
|
||||||
var z: Float = a._z[i] + (toZ - a._z[i]) * eases[a.ease](k);
|
var z: Float = a._z[j] + (toZ - a._z[j]) * eases[a.ease](k);
|
||||||
var w: Float = a._w[i] + (toW - a._w[i]) * eases[a.ease](k);
|
var w: Float = a._w[j] + (toW - a._w[j]) * eases[a.ease](k);
|
||||||
if (a._normalize[i]) {
|
if (a._normalize[j]) {
|
||||||
var l = Math.sqrt(x * x + y * y + z * z + w * w);
|
var l = Math.sqrt(x * x + y * y + z * z + w * w);
|
||||||
if (l > 0.0) {
|
if (l > 0.0) {
|
||||||
l = 1.0 / l;
|
l = 1.0 / l;
|
||||||
|
|||||||
52
leenkx/Sources/iron/system/VR.hx
Normal file
52
leenkx/Sources/iron/system/VR.hx
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
package iron.system;
|
||||||
|
|
||||||
|
import iron.math.Mat4;
|
||||||
|
|
||||||
|
#if lnx_vr
|
||||||
|
class VR {
|
||||||
|
|
||||||
|
static var undistortionMatrix: Mat4 = null;
|
||||||
|
|
||||||
|
public function new() {}
|
||||||
|
|
||||||
|
public static function getUndistortionMatrix(): Mat4 {
|
||||||
|
if (undistortionMatrix == null) {
|
||||||
|
undistortionMatrix = Mat4.identity();
|
||||||
|
}
|
||||||
|
|
||||||
|
return undistortionMatrix;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getMaxRadiusSq(): Float {
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function initButton() {
|
||||||
|
function vrDownListener(index: Int, x: Float, y: Float) {
|
||||||
|
var vr = kha.vr.VrInterface.instance;
|
||||||
|
if (vr == null || !vr.IsVrEnabled() || vr.IsPresenting()) return;
|
||||||
|
var w: Float = iron.App.w();
|
||||||
|
var h: Float = iron.App.h();
|
||||||
|
if (x < w - 150 || y < h - 150) return;
|
||||||
|
vr.onVRRequestPresent();
|
||||||
|
}
|
||||||
|
|
||||||
|
function vrRender2D(g: kha.graphics2.Graphics) {
|
||||||
|
var vr = kha.vr.VrInterface.instance;
|
||||||
|
if (vr == null || !vr.IsVrEnabled() || vr.IsPresenting()) return;
|
||||||
|
var w: Float = iron.App.w();
|
||||||
|
var h: Float = iron.App.h();
|
||||||
|
g.color = 0xffff0000;
|
||||||
|
g.fillRect(w - 150, h - 150, 140, 140);
|
||||||
|
}
|
||||||
|
|
||||||
|
kha.input.Mouse.get().notify(vrDownListener, null, null, null);
|
||||||
|
iron.App.notifyOnRender2D(vrRender2D);
|
||||||
|
|
||||||
|
var vr = kha.vr.VrInterface.instance; // Straight to VR (Oculus Carmel)
|
||||||
|
if (vr != null && vr.IsVrEnabled()) {
|
||||||
|
vr.onVRRequestPresent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#end
|
||||||
138
leenkx/Sources/iron/system/VRController.hx
Normal file
138
leenkx/Sources/iron/system/VRController.hx
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
package iron.system;
|
||||||
|
|
||||||
|
#if lnx_vr
|
||||||
|
import iron.math.Vec4;
|
||||||
|
import iron.math.Quat;
|
||||||
|
|
||||||
|
class VRController {
|
||||||
|
|
||||||
|
public static var leftHandPosition: Vec4 = new Vec4();
|
||||||
|
public static var leftHandRotation: Quat = new Quat();
|
||||||
|
public static var rightHandPosition: Vec4 = new Vec4();
|
||||||
|
public static var rightHandRotation: Quat = new Quat();
|
||||||
|
|
||||||
|
public static var leftHandActive: Bool = false;
|
||||||
|
public static var rightHandActive: Bool = false;
|
||||||
|
|
||||||
|
public static var leftThumbstickX: Float = 0.0;
|
||||||
|
public static var leftThumbstickY: Float = 0.0;
|
||||||
|
public static var rightThumbstickX: Float = 0.0;
|
||||||
|
public static var rightThumbstickY: Float = 0.0;
|
||||||
|
|
||||||
|
public static var leftTrigger: Float = 0.0;
|
||||||
|
public static var rightTrigger: Float = 0.0;
|
||||||
|
public static var leftGrip: Float = 0.0;
|
||||||
|
public static var rightGrip: Float = 0.0;
|
||||||
|
|
||||||
|
public static var leftButtonX: Bool = false;
|
||||||
|
public static var leftButtonY: Bool = false;
|
||||||
|
public static var rightButtonA: Bool = false;
|
||||||
|
public static var rightButtonB: Bool = false;
|
||||||
|
|
||||||
|
public static var debugLog:Bool = false;
|
||||||
|
|
||||||
|
public static function enableDebug() {
|
||||||
|
debugLog = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function disableDebug() {
|
||||||
|
debugLog = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function updatePoses() {
|
||||||
|
var vr: kha.js.vr.VrInterface = cast kha.vr.VrInterface.instance;
|
||||||
|
if (vr == null || !vr.IsPresenting()) {
|
||||||
|
if (debugLog) trace("[VRController] Not presenting or VR null");
|
||||||
|
leftHandActive = false;
|
||||||
|
rightHandActive = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
untyped window._vrControllerFrame = (untyped window._vrControllerFrame || 0) + 1;
|
||||||
|
|
||||||
|
leftHandActive = false;
|
||||||
|
rightHandActive = false;
|
||||||
|
|
||||||
|
leftButtonX = false;
|
||||||
|
leftButtonY = false;
|
||||||
|
rightButtonA = false;
|
||||||
|
rightButtonB = false;
|
||||||
|
|
||||||
|
var refSpace = untyped vr.xrRefSpace;
|
||||||
|
if (vr.currentInputSources == null || vr.currentFrame == null || refSpace == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var inputSources = vr.currentInputSources;
|
||||||
|
var frame = vr.currentFrame;
|
||||||
|
|
||||||
|
var sourceCount:Int = untyped inputSources.length;
|
||||||
|
|
||||||
|
for (i in 0...sourceCount) {
|
||||||
|
var inputSource = untyped inputSources[i];
|
||||||
|
if (inputSource == null) continue;
|
||||||
|
|
||||||
|
var handedness = untyped inputSource.handedness; // "left", "right", or "none"
|
||||||
|
|
||||||
|
var gripSpace = untyped inputSource.gripSpace;
|
||||||
|
var targetRaySpace = untyped inputSource.targetRaySpace;
|
||||||
|
// use targetRaySpace first laser/pointer and fall back to gripSpace
|
||||||
|
var space = (targetRaySpace != null) ? targetRaySpace : gripSpace;
|
||||||
|
|
||||||
|
if (space == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var pose = untyped frame.getPose(space, refSpace);
|
||||||
|
if (pose == null || pose.transform == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var transform = pose.transform;
|
||||||
|
var pos = transform.position;
|
||||||
|
var orient = transform.orientation;
|
||||||
|
|
||||||
|
if (handedness == "left") {
|
||||||
|
leftHandPosition.set(pos.x, pos.y, pos.z);
|
||||||
|
leftHandRotation.set(orient.x, orient.y, orient.z, orient.w);
|
||||||
|
leftHandActive = true;
|
||||||
|
|
||||||
|
var gamepad = untyped inputSource.gamepad;
|
||||||
|
if (gamepad != null) {
|
||||||
|
// [0]=thumbstickX [1]=thumbstickY [2]=touchpadX [3]=touchpadY
|
||||||
|
if (gamepad.axes != null && gamepad.axes.length >= 2) {
|
||||||
|
leftThumbstickX = gamepad.axes[0];
|
||||||
|
leftThumbstickY = gamepad.axes[1];
|
||||||
|
}
|
||||||
|
// [0]=trigger [1]=grip [4]=X [5]=Y
|
||||||
|
if (gamepad.buttons != null) {
|
||||||
|
if (gamepad.buttons.length > 0) leftTrigger = gamepad.buttons[0].value;
|
||||||
|
if (gamepad.buttons.length > 1) leftGrip = gamepad.buttons[1].value;
|
||||||
|
if (gamepad.buttons.length > 4) leftButtonX = gamepad.buttons[4].pressed;
|
||||||
|
if (gamepad.buttons.length > 5) leftButtonY = gamepad.buttons[5].pressed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (handedness == "right") {
|
||||||
|
rightHandPosition.set(pos.x, pos.y, pos.z);
|
||||||
|
rightHandRotation.set(orient.x, orient.y, orient.z, orient.w);
|
||||||
|
rightHandActive = true;
|
||||||
|
|
||||||
|
var gamepad = untyped inputSource.gamepad;
|
||||||
|
if (gamepad != null) {
|
||||||
|
if (gamepad.axes != null && gamepad.axes.length >= 2) {
|
||||||
|
rightThumbstickX = gamepad.axes[0];
|
||||||
|
rightThumbstickY = gamepad.axes[1];
|
||||||
|
}
|
||||||
|
if (gamepad.buttons != null) {
|
||||||
|
if (gamepad.buttons.length > 0) rightTrigger = gamepad.buttons[0].value;
|
||||||
|
if (gamepad.buttons.length > 1) rightGrip = gamepad.buttons[1].value;
|
||||||
|
if (gamepad.buttons.length > 4) rightButtonA = gamepad.buttons[4].pressed;
|
||||||
|
if (gamepad.buttons.length > 5) rightButtonB = gamepad.buttons[5].pressed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#end
|
||||||
@ -44,6 +44,7 @@ typedef TConfig = {
|
|||||||
@:optional var rp_supersample: Null<Float>;
|
@:optional var rp_supersample: Null<Float>;
|
||||||
@:optional var rp_shadowmap_cube: Null<Int>; // size
|
@:optional var rp_shadowmap_cube: Null<Int>; // size
|
||||||
@:optional var rp_shadowmap_cascade: Null<Int>; // size for single cascade
|
@:optional var rp_shadowmap_cascade: Null<Int>; // size for single cascade
|
||||||
|
@:optional var rp_ssao: Null<Bool>;
|
||||||
@:optional var rp_ssgi: Null<Bool>;
|
@:optional var rp_ssgi: Null<Bool>;
|
||||||
@:optional var rp_ssr: Null<Bool>;
|
@:optional var rp_ssr: Null<Bool>;
|
||||||
@:optional var rp_ssrefr: Null<Bool>;
|
@:optional var rp_ssrefr: Null<Bool>;
|
||||||
|
|||||||
@ -2,13 +2,16 @@ package leenkx.logicnode;
|
|||||||
|
|
||||||
import iron.object.Object;
|
import iron.object.Object;
|
||||||
|
|
||||||
#if lnx_bullet
|
#if lnx_physics
|
||||||
import leenkx.trait.physics.PhysicsConstraint;
|
import leenkx.trait.physics.PhysicsConstraint;
|
||||||
import leenkx.trait.physics.bullet.PhysicsConstraint.ConstraintType;
|
#if lnx_bullet
|
||||||
#elseif lnx_oimo
|
import leenkx.trait.physics.bullet.PhysicsConstraint.ConstraintType;
|
||||||
// TODO
|
#elseif lnx_jolt
|
||||||
|
import leenkx.trait.physics.jolt.PhysicsConstraint.ConstraintType;
|
||||||
|
#else
|
||||||
|
import leenkx.trait.physics.oimo.PhysicsConstraint.ConstraintType;
|
||||||
|
#end
|
||||||
#end
|
#end
|
||||||
|
|
||||||
class AddPhysicsConstraintNode extends LogicNode {
|
class AddPhysicsConstraintNode extends LogicNode {
|
||||||
|
|
||||||
public var property0: String;//Type
|
public var property0: String;//Type
|
||||||
@ -27,7 +30,7 @@ class AddPhysicsConstraintNode extends LogicNode {
|
|||||||
|
|
||||||
if (pivotObject == null || rb1 == null || rb2 == null) return;
|
if (pivotObject == null || rb1 == null || rb2 == null) return;
|
||||||
|
|
||||||
#if lnx_bullet
|
#if lnx_physics
|
||||||
|
|
||||||
var disableCollisions: Bool = inputs[4].get();
|
var disableCollisions: Bool = inputs[4].get();
|
||||||
var breakable: Bool = inputs[5].get();
|
var breakable: Bool = inputs[5].get();
|
||||||
@ -110,8 +113,6 @@ class AddPhysicsConstraintNode extends LogicNode {
|
|||||||
}
|
}
|
||||||
pivotObject.addTrait(con);
|
pivotObject.addTrait(con);
|
||||||
}
|
}
|
||||||
#elseif lnx_oimo
|
|
||||||
// TODO
|
|
||||||
#end
|
#end
|
||||||
runOutput(0);
|
runOutput(0);
|
||||||
}
|
}
|
||||||
|
|||||||
48
leenkx/Sources/leenkx/logicnode/AnyContactNode.hx
Normal file
48
leenkx/Sources/leenkx/logicnode/AnyContactNode.hx
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -37,86 +37,90 @@ class CreateLeenkxNode extends LogicNode {
|
|||||||
|
|
||||||
function onEvent() {
|
function onEvent() {
|
||||||
#if js
|
#if js
|
||||||
var window:haxe.DynamicAccess<Dynamic> = untyped js.Browser.window;
|
var lnxjs:Dynamic = js.Lib.global;
|
||||||
var lxCxNew = window.get('lxNew');
|
var lnxCxNew = lnxjs.lnxNew;
|
||||||
|
|
||||||
var lxCn:Dynamic = lxCxNew(net_Url);
|
if (lnxCxNew == null) {
|
||||||
|
trace("ERROR: lnxNew not available");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
lxCn.on("connections", function(c) {
|
var lnxCn:Dynamic = lnxCxNew(net_Url);
|
||||||
leenkx.network.Leenkx.data.set(net_Url, c + 1);
|
|
||||||
leenkx.network.Leenkx.connections[net_Url].onconnections();
|
|
||||||
});
|
|
||||||
lxCn.on("message", function(address,message) {
|
|
||||||
leenkx.network.Leenkx.data.set(net_Url, message);
|
|
||||||
leenkx.network.Leenkx.id.set(net_Url, address);
|
|
||||||
leenkx.network.Leenkx.connections[net_Url].onmessage();
|
|
||||||
});
|
|
||||||
lxCn.on("seen", function(address) {
|
|
||||||
leenkx.network.Leenkx.id.set(net_Url, address);
|
|
||||||
leenkx.network.Leenkx.connections[net_Url].onseen();
|
|
||||||
});
|
|
||||||
lxCn.on("left", function(address) {
|
|
||||||
leenkx.network.Leenkx.id.set(net_Url, address);
|
|
||||||
leenkx.network.Leenkx.connections[net_Url].onleft();
|
|
||||||
});
|
|
||||||
lxCn.on("server", function(address) {
|
|
||||||
leenkx.network.Leenkx.id.set(net_Url, address);
|
|
||||||
leenkx.network.Leenkx.connections[net_Url].onserver();
|
|
||||||
});
|
|
||||||
lxCn.on("ping", function(address) {
|
|
||||||
leenkx.network.Leenkx.id.set(net_Url, address);
|
|
||||||
leenkx.network.Leenkx.connections[net_Url].onping();
|
|
||||||
});
|
|
||||||
lxCn.on("timeout", function(address) {
|
|
||||||
leenkx.network.Leenkx.id.set(net_Url, address);
|
|
||||||
leenkx.network.Leenkx.connections[net_Url].ontimeout();
|
|
||||||
});
|
|
||||||
lxCn.on("rpc", function(address, call, args, nonce) {
|
|
||||||
leenkx.network.Leenkx.data.set(net_Url, call);
|
|
||||||
leenkx.network.Leenkx.id.set(net_Url, address);
|
|
||||||
call(args);
|
|
||||||
leenkx.network.Leenkx.connections[net_Url].onrpc();
|
|
||||||
});
|
|
||||||
lxCn.on("rpc-response", function(address, nonce, response) {
|
|
||||||
leenkx.network.Leenkx.id.set(net_Url, address);
|
|
||||||
leenkx.network.Leenkx.connections[net_Url].onrpcresponse();
|
|
||||||
});
|
|
||||||
lxCn.on("wireleft", function(wirecount, wire) {
|
|
||||||
leenkx.network.Leenkx.data.set(net_Url, wirecount);
|
|
||||||
leenkx.network.Leenkx.id.set(net_Url, wire.peerId);
|
|
||||||
leenkx.network.Leenkx.connections[net_Url].onwireleft();
|
|
||||||
});
|
|
||||||
lxCn.on("wireseen", function(wirecount, wire) {
|
|
||||||
leenkx.network.Leenkx.data.set(net_Url, wirecount);
|
|
||||||
leenkx.network.Leenkx.id.set(net_Url, wire.peerId);
|
|
||||||
leenkx.network.Leenkx.connections[net_Url].onwireseen();
|
|
||||||
});
|
|
||||||
lxCn.on("torrent", function(identifier, torrent) {
|
|
||||||
leenkx.network.Leenkx.data.set(net_Url, torrent);
|
|
||||||
leenkx.network.Leenkx.id.set(net_Url, identifier);
|
|
||||||
leenkx.network.Leenkx.connections[net_Url].ontorrent();
|
|
||||||
});
|
|
||||||
|
|
||||||
lxCn.on("tracker", function(identifier) {
|
lnxCn.on("connections", function(c) {
|
||||||
leenkx.network.Leenkx.id.set(net_Url, identifier);
|
leenkx.network.Leenkx.data.set(net_Url, c + 1);
|
||||||
leenkx.network.Leenkx.connections[net_Url].ontracker();
|
leenkx.network.Leenkx.connections[net_Url].onconnections();
|
||||||
});
|
});
|
||||||
|
lnxCn.on("message", function(address, message) {
|
||||||
|
leenkx.network.Leenkx.data.set(net_Url, message);
|
||||||
|
leenkx.network.Leenkx.id.set(net_Url, address);
|
||||||
|
leenkx.network.Leenkx.connections[net_Url].onmessage();
|
||||||
|
});
|
||||||
|
lnxCn.on("seen", function(address) {
|
||||||
|
leenkx.network.Leenkx.id.set(net_Url, address);
|
||||||
|
leenkx.network.Leenkx.connections[net_Url].onseen();
|
||||||
|
});
|
||||||
|
lnxCn.on("left", function(address) {
|
||||||
|
leenkx.network.Leenkx.id.set(net_Url, address);
|
||||||
|
leenkx.network.Leenkx.connections[net_Url].onleft();
|
||||||
|
});
|
||||||
|
lnxCn.on("server", function(address) {
|
||||||
|
leenkx.network.Leenkx.id.set(net_Url, address);
|
||||||
|
leenkx.network.Leenkx.connections[net_Url].onserver();
|
||||||
|
});
|
||||||
|
lnxCn.on("ping", function(address) {
|
||||||
|
leenkx.network.Leenkx.id.set(net_Url, address);
|
||||||
|
leenkx.network.Leenkx.connections[net_Url].onping();
|
||||||
|
});
|
||||||
|
lnxCn.on("timeout", function(address) {
|
||||||
|
leenkx.network.Leenkx.id.set(net_Url, address);
|
||||||
|
leenkx.network.Leenkx.connections[net_Url].ontimeout();
|
||||||
|
});
|
||||||
|
lnxCn.on("rpc", function(address, call, args, nonce) {
|
||||||
|
leenkx.network.Leenkx.data.set(net_Url, call);
|
||||||
|
leenkx.network.Leenkx.id.set(net_Url, address);
|
||||||
|
call(args);
|
||||||
|
leenkx.network.Leenkx.connections[net_Url].onrpc();
|
||||||
|
});
|
||||||
|
lnxCn.on("rpc-response", function(address, nonce, response) {
|
||||||
|
leenkx.network.Leenkx.id.set(net_Url, address);
|
||||||
|
leenkx.network.Leenkx.connections[net_Url].onrpcresponse();
|
||||||
|
});
|
||||||
|
lnxCn.on("wireleft", function(wirecount, wire) {
|
||||||
|
leenkx.network.Leenkx.data.set(net_Url, wirecount);
|
||||||
|
leenkx.network.Leenkx.id.set(net_Url, wire.peerId);
|
||||||
|
leenkx.network.Leenkx.connections[net_Url].onwireleft();
|
||||||
|
});
|
||||||
|
lnxCn.on("wireseen", function(wirecount, wire) {
|
||||||
|
leenkx.network.Leenkx.data.set(net_Url, wirecount);
|
||||||
|
leenkx.network.Leenkx.id.set(net_Url, wire.peerId);
|
||||||
|
leenkx.network.Leenkx.connections[net_Url].onwireseen();
|
||||||
|
});
|
||||||
|
lnxCn.on("torrent", function(identifier, torrent) {
|
||||||
|
leenkx.network.Leenkx.data.set(net_Url, torrent);
|
||||||
|
leenkx.network.Leenkx.id.set(net_Url, identifier);
|
||||||
|
leenkx.network.Leenkx.connections[net_Url].ontorrent();
|
||||||
|
});
|
||||||
|
lnxCn.on("tracker", function(identifier) {
|
||||||
|
leenkx.network.Leenkx.id.set(net_Url, identifier);
|
||||||
|
leenkx.network.Leenkx.connections[net_Url].ontracker();
|
||||||
|
});
|
||||||
|
lnxCn.on("announce", function(identifier) {
|
||||||
|
leenkx.network.Leenkx.id.set(net_Url, identifier);
|
||||||
|
leenkx.network.Leenkx.connections[net_Url].onannounce();
|
||||||
|
});
|
||||||
|
|
||||||
lxCn.on("announce", function(identifier) {
|
Reflect.setField(lnxjs, "lnx_" + net_Url, lnxCn);
|
||||||
leenkx.network.Leenkx.id.set(net_Url, identifier);
|
Leenkx.connections[net_Url].client = lnxCn;
|
||||||
leenkx.network.Leenkx.connections[net_Url].onannounce();
|
|
||||||
});
|
var script = 'globalThis.addEventListener("beforeunload", function (e) {
|
||||||
window.set("lx_" + net_Url, lxCn);
|
leenkx.network.Leenkx.connections.h["' + net_Url + '"].client.destroy();
|
||||||
Leenkx.connections[net_Url].client = lxCn;
|
delete e["returnValue"];
|
||||||
var script = '
|
});';
|
||||||
window.addEventListener("beforeunload", function (e) {
|
js.Syntax.code('(1, eval)({0})', script);
|
||||||
leenkx.network.Leenkx.connections.h["' + net_Url + '"].client.destroy();
|
|
||||||
delete e["returnValue"];
|
runOutput(0);
|
||||||
});
|
#end
|
||||||
';
|
|
||||||
js.Syntax.code('(1, eval)({0})', script.toString());
|
|
||||||
runOutput(0);
|
|
||||||
#end
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -62,7 +62,7 @@ class DrawStringNode extends LogicNode {
|
|||||||
|
|
||||||
override function get(from: Int): Dynamic {
|
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);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
17
leenkx/Sources/leenkx/logicnode/GetAudioPositionNode.hx
Normal file
17
leenkx/Sources/leenkx/logicnode/GetAudioPositionNode.hx
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
package leenkx.logicnode;
|
||||||
|
|
||||||
|
import aura.Aura;
|
||||||
|
import aura.Types;
|
||||||
|
|
||||||
|
class GetAudioPositionNode extends LogicNode {
|
||||||
|
|
||||||
|
public function new(tree: LogicTree) {
|
||||||
|
super(tree);
|
||||||
|
}
|
||||||
|
|
||||||
|
override function get(from: Int): Dynamic {
|
||||||
|
var audio = inputs[0].get();
|
||||||
|
if (audio == null || audio.channel == null) return 0.0;
|
||||||
|
return audio.channel.floatPosition / audio.channel.sampleRate;
|
||||||
|
}
|
||||||
|
}
|
||||||
19
leenkx/Sources/leenkx/logicnode/GetCameraRenderFilterNode.hx
Normal file
19
leenkx/Sources/leenkx/logicnode/GetCameraRenderFilterNode.hx
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
package leenkx.logicnode;
|
||||||
|
|
||||||
|
import iron.object.MeshObject;
|
||||||
|
import iron.object.CameraObject;
|
||||||
|
|
||||||
|
class GetCameraRenderFilterNode extends LogicNode {
|
||||||
|
|
||||||
|
public function new(tree: LogicTree) {
|
||||||
|
super(tree);
|
||||||
|
}
|
||||||
|
|
||||||
|
override function get(from: Int): Dynamic {
|
||||||
|
var mo: MeshObject = cast inputs[0].get();
|
||||||
|
|
||||||
|
if (mo == null) return null;
|
||||||
|
|
||||||
|
return mo.cameraList;
|
||||||
|
}
|
||||||
|
}
|
||||||
33
leenkx/Sources/leenkx/logicnode/GetPositionSpeakerNode.hx
Normal file
33
leenkx/Sources/leenkx/logicnode/GetPositionSpeakerNode.hx
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
package leenkx.logicnode;
|
||||||
|
|
||||||
|
#if lnx_audio
|
||||||
|
import iron.object.SpeakerObject;
|
||||||
|
import kha.audio1.AudioChannel;
|
||||||
|
#end
|
||||||
|
|
||||||
|
class GetPositionSpeakerNode extends LogicNode {
|
||||||
|
|
||||||
|
public function new(tree: LogicTree) {
|
||||||
|
super(tree);
|
||||||
|
}
|
||||||
|
|
||||||
|
override function get(from: Int): Dynamic {
|
||||||
|
#if lnx_audio
|
||||||
|
var object: SpeakerObject = cast(inputs[0].get(), SpeakerObject);
|
||||||
|
if (object == null || object.sound == null) return 0.0;
|
||||||
|
|
||||||
|
if (object.channels.length == 0) return 0.0;
|
||||||
|
|
||||||
|
var channel = object.channels[0];
|
||||||
|
|
||||||
|
var position = 0.0;
|
||||||
|
if (channel != null) {
|
||||||
|
position = @:privateAccess channel.get_position();
|
||||||
|
}
|
||||||
|
|
||||||
|
return position;
|
||||||
|
#else
|
||||||
|
return 0.0;
|
||||||
|
#end
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,26 +1,12 @@
|
|||||||
package leenkx.logicnode;
|
package leenkx.logicnode;
|
||||||
|
|
||||||
import iron.object.Object;
|
|
||||||
import iron.math.Vec4;
|
|
||||||
|
|
||||||
class GetWorldNode extends LogicNode {
|
class GetWorldNode extends LogicNode {
|
||||||
|
|
||||||
public var property0: String;
|
|
||||||
|
|
||||||
public function new(tree: LogicTree) {
|
public function new(tree: LogicTree) {
|
||||||
super(tree);
|
super(tree);
|
||||||
}
|
}
|
||||||
|
|
||||||
override function get(from: Int): Dynamic {
|
override function get(from: Int): Dynamic {
|
||||||
var object: Object = inputs[0].get();
|
return iron.Scene.active.raw.world_ref;
|
||||||
|
|
||||||
if (object == null) return null;
|
|
||||||
|
|
||||||
return switch (property0) {
|
|
||||||
case "Right": object.transform.world.right();
|
|
||||||
case "Look": object.transform.world.look();
|
|
||||||
case "Up": object.transform.world.up();
|
|
||||||
default: null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
26
leenkx/Sources/leenkx/logicnode/GetWorldOrientationNode.hx
Normal file
26
leenkx/Sources/leenkx/logicnode/GetWorldOrientationNode.hx
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
package leenkx.logicnode;
|
||||||
|
|
||||||
|
import iron.object.Object;
|
||||||
|
import iron.math.Vec4;
|
||||||
|
|
||||||
|
class GetWorldOrientationNode extends LogicNode {
|
||||||
|
|
||||||
|
public var property0: String;
|
||||||
|
|
||||||
|
public function new(tree: LogicTree) {
|
||||||
|
super(tree);
|
||||||
|
}
|
||||||
|
|
||||||
|
override function get(from: Int): Dynamic {
|
||||||
|
var object: Object = inputs[0].get();
|
||||||
|
|
||||||
|
if (object == null) return null;
|
||||||
|
|
||||||
|
return switch (property0) {
|
||||||
|
case "Right": object.transform.world.right();
|
||||||
|
case "Look": object.transform.world.look();
|
||||||
|
case "Up": object.transform.world.up();
|
||||||
|
default: null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,7 +1,10 @@
|
|||||||
package leenkx.logicnode;
|
package leenkx.logicnode;
|
||||||
|
|
||||||
import iron.object.Object;
|
import iron.object.Object;
|
||||||
|
|
||||||
|
#if lnx_physics
|
||||||
|
import leenkx.trait.physics.PhysicsCache;
|
||||||
import leenkx.trait.physics.RigidBody;
|
import leenkx.trait.physics.RigidBody;
|
||||||
|
#end
|
||||||
|
|
||||||
class HasContactNode extends LogicNode {
|
class HasContactNode extends LogicNode {
|
||||||
|
|
||||||
@ -15,12 +18,15 @@ class HasContactNode extends LogicNode {
|
|||||||
|
|
||||||
if (object1 == null || object2 == null) return false;
|
if (object1 == null || object2 == null) return false;
|
||||||
|
|
||||||
#if lnx_physics
|
#if lnx_physics
|
||||||
var physics = leenkx.trait.physics.PhysicsWorld.active;
|
var rb1 = PhysicsCache.getCachedRigidBody(object1);
|
||||||
var rb2 = object2.getTrait(RigidBody);
|
var rb2 = PhysicsCache.getCachedRigidBody(object2);
|
||||||
var rbs = physics.getContacts(object1.getTrait(RigidBody));
|
|
||||||
if (rbs != null) for (rb in rbs) if (rb == rb2) return true;
|
if (rb1 != null && rb2 != null) {
|
||||||
#end
|
var rbs = PhysicsCache.getCachedContacts(rb1);
|
||||||
|
return PhysicsCache.hasContactWith(rbs, rb2);
|
||||||
|
}
|
||||||
|
#end
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -32,10 +32,15 @@ class LeenkxCloseConnectionNode extends LogicNode {
|
|||||||
} else {
|
} else {
|
||||||
var script = '
|
var script = '
|
||||||
try{
|
try{
|
||||||
leenkx.network.Leenkx.connections.h["1008"].client.torrent._peers[p].conn._pc.close();
|
var lnxConn = leenkx.network.Leenkx.connections.h["' + connection._url + '"];
|
||||||
leenkx.network.Leenkx.connections.h["1008"].client.torrent._peers[p].conn.destroy();
|
if (lnxConn && lnxConn.client && lnxConn.client.torrent && lnxConn.client.torrent._peers) {
|
||||||
leenkx.network.Leenkx.id.set("1008",p);
|
for (var p in lnxConn.client.torrent._peers) {
|
||||||
leenkx.network.Leenkx.connections.h["1008"].onclose();
|
lnxConn.client.torrent._peers[p].conn._pc.close();
|
||||||
|
lnxConn.client.torrent._peers[p].conn.destroy();
|
||||||
|
leenkx.network.Leenkx.id.set("' + connection._url + '", p);
|
||||||
|
lnxConn.onclose();
|
||||||
|
}
|
||||||
|
}
|
||||||
}catch(error){
|
}catch(error){
|
||||||
console.log("Error: " + error);
|
console.log("Error: " + error);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -40,7 +40,7 @@ class LeenkxEventNode extends LogicNode {
|
|||||||
default: throw "Failed to set client event type.";
|
default: throw "Failed to set client event type.";
|
||||||
}
|
}
|
||||||
} else if (property0 == "host") {
|
} else if (property0 == "host") {
|
||||||
#if sys
|
#if (sys || kha_krom)
|
||||||
var net_Domain = inputs[0].get();
|
var net_Domain = inputs[0].get();
|
||||||
var net_Port = inputs[1].get();
|
var net_Port = inputs[1].get();
|
||||||
net_Url = "ws://" + net_Domain + ":" + Std.string(net_Port);
|
net_Url = "ws://" + net_Domain + ":" + Std.string(net_Port);
|
||||||
@ -53,7 +53,7 @@ class LeenkxEventNode extends LogicNode {
|
|||||||
}
|
}
|
||||||
#end
|
#end
|
||||||
} else if (property0 == "securehost"){
|
} else if (property0 == "securehost"){
|
||||||
#if sys
|
#if (sys || kha_krom)
|
||||||
var net_Domain = inputs[0].get();
|
var net_Domain = inputs[0].get();
|
||||||
var net_Port = inputs[1].get();
|
var net_Port = inputs[1].get();
|
||||||
net_Url = "wss://" + net_Domain + ":" + Std.string(net_Port);
|
net_Url = "wss://" + net_Domain + ":" + Std.string(net_Port);
|
||||||
|
|||||||
@ -27,9 +27,9 @@ class LeenkxSendMessageNode extends LogicNode {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
if(inputs[5].get() == true){
|
if(inputs[5].get() == true){
|
||||||
var script = 'for (p in lx_' + connection._url +'.torrent._peers){
|
var script = 'for (p in lnx_' + connection._url +'.torrent._peers){
|
||||||
try{
|
try{
|
||||||
lx_' + connection._url +'.torrent._peers[p].conn.send(`'+ api + message + '`);
|
lnx_' + connection._url +'.torrent._peers[p].conn.send(`'+ api + message + '`);
|
||||||
}catch(error){
|
}catch(error){
|
||||||
console.log("Error: " + error);
|
console.log("Error: " + error);
|
||||||
}
|
}
|
||||||
@ -37,7 +37,7 @@ class LeenkxSendMessageNode extends LogicNode {
|
|||||||
js.Syntax.code('(1, eval)({0})', script.toString());
|
js.Syntax.code('(1, eval)({0})', script.toString());
|
||||||
runOutput(0);
|
runOutput(0);
|
||||||
} else {
|
} else {
|
||||||
var script = 'lx_' + connection._url +'.send(`' + inputs[4].get() + '`, `'+ api + message + '` );';
|
var script = 'lnx_' + connection._url +'.send(`' + inputs[4].get() + '`, `'+ api + message + '` );';
|
||||||
js.Syntax.code('(1, eval)({0})', script.toString());
|
js.Syntax.code('(1, eval)({0})', script.toString());
|
||||||
runOutput(0);
|
runOutput(0);
|
||||||
}
|
}
|
||||||
@ -58,9 +58,9 @@ class LeenkxSendMessageNode extends LogicNode {
|
|||||||
try {
|
try {
|
||||||
connection.buffer = buffer;
|
connection.buffer = buffer;
|
||||||
if(inputs[5].get() == true){
|
if(inputs[5].get() == true){
|
||||||
var script = 'for (p in lx_' + connection._url +'.torrent._peers){
|
var script = 'for (p in lnx_' + connection._url +'.torrent._peers){
|
||||||
try{
|
try{
|
||||||
lx_' + connection._url +'.torrent._peers[p].conn.send(leenkx.network.Leenkx.connections.h["'+ connection._url +'"].buffer );
|
lnx_' + connection._url +'.torrent._peers[p].conn.send(leenkx.network.Leenkx.connections.h["'+ connection._url +'"].buffer );
|
||||||
}catch(error){
|
}catch(error){
|
||||||
console.log("Error: " + error);
|
console.log("Error: " + error);
|
||||||
}
|
}
|
||||||
@ -68,7 +68,7 @@ class LeenkxSendMessageNode extends LogicNode {
|
|||||||
js.Syntax.code('(1, eval)({0})', script.toString());
|
js.Syntax.code('(1, eval)({0})', script.toString());
|
||||||
runOutput(0);
|
runOutput(0);
|
||||||
} else {
|
} else {
|
||||||
var script = 'lx_' + connection._url +'.send("' + inputs[4].get() + '",leenkx.network.Leenkx.connections.h["'+ connection._url +'"].buffer );';
|
var script = 'lnx_' + connection._url +'.send("' + inputs[4].get() + '",leenkx.network.Leenkx.connections.h["'+ connection._url +'"].buffer );';
|
||||||
js.Syntax.code('(1, eval)({0})', script.toString());
|
js.Syntax.code('(1, eval)({0})', script.toString());
|
||||||
runOutput(0);
|
runOutput(0);
|
||||||
}
|
}
|
||||||
@ -77,15 +77,15 @@ class LeenkxSendMessageNode extends LogicNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
var window:haxe.DynamicAccess<Dynamic> = untyped js.Browser.window;
|
if(inputs[5].get() == true){
|
||||||
var lxCn = window.get('lx_' + connection._url);
|
var script = 'lnx_' + connection._url + '.send(`' + api + message + '`);';
|
||||||
if(inputs[5].get() == true){
|
js.Syntax.code('(1, eval)({0})', script);
|
||||||
lxCn.send(api+message);
|
}else{
|
||||||
}else{
|
var script = 'lnx_' + connection._url + '.send(`' + inputs[4].get() + '`, `' + api + message + '`);';
|
||||||
lxCn.send(inputs[4].get(), api+message);
|
js.Syntax.code('(1, eval)({0})', script);
|
||||||
}
|
}
|
||||||
runOutput(0);
|
runOutput(0);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
case "vector":
|
case "vector":
|
||||||
if(property0 == "client"){
|
if(property0 == "client"){
|
||||||
@ -103,9 +103,9 @@ class LeenkxSendMessageNode extends LogicNode {
|
|||||||
try {
|
try {
|
||||||
connection.buffer = buffer;
|
connection.buffer = buffer;
|
||||||
if(inputs[5].get() == true){
|
if(inputs[5].get() == true){
|
||||||
var script = 'for (p in lx_' + connection._url +'.torrent._peers){
|
var script = 'for (p in lnx_' + connection._url +'.torrent._peers){
|
||||||
try{
|
try{
|
||||||
lx_' + connection._url +'.torrent._peers[p].conn.send("' + api + message + '");
|
lnx_' + connection._url +'.torrent._peers[p].conn.send("' + api + message + '");
|
||||||
}catch(error){
|
}catch(error){
|
||||||
console.log("Error: " + error);
|
console.log("Error: " + error);
|
||||||
}
|
}
|
||||||
@ -113,7 +113,7 @@ class LeenkxSendMessageNode extends LogicNode {
|
|||||||
js.Syntax.code('(1, eval)({0})', script.toString());
|
js.Syntax.code('(1, eval)({0})', script.toString());
|
||||||
runOutput(0);
|
runOutput(0);
|
||||||
} else {
|
} else {
|
||||||
var script = 'lx_' + connection._url +'.send("' + inputs[4].get() + '",leenkx.network.Leenkx.connections.h["'+ connection._url +'"].buffer );';
|
var script = 'lnx_' + connection._url +'.send("' + inputs[4].get() + '",leenkx.network.Leenkx.connections.h["'+ connection._url +'"].buffer );';
|
||||||
js.Syntax.code('(1, eval)({0})', script.toString());
|
js.Syntax.code('(1, eval)({0})', script.toString());
|
||||||
runOutput(0);
|
runOutput(0);
|
||||||
}
|
}
|
||||||
@ -121,12 +121,12 @@ class LeenkxSendMessageNode extends LogicNode {
|
|||||||
trace("Error: " + error);
|
trace("Error: " + error);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
var window:haxe.DynamicAccess<Dynamic> = untyped js.Browser.window;
|
|
||||||
var lxCn = window.get('lx_' + connection._url);
|
|
||||||
if(inputs[5].get() == true){
|
if(inputs[5].get() == true){
|
||||||
lxCn.send(api+message);
|
var script = 'lnx_' + connection._url + '.send(`' + api + message + '`);';
|
||||||
|
js.Syntax.code('(1, eval)({0})', script);
|
||||||
}else{
|
}else{
|
||||||
lxCn.send(inputs[4].get(), api+message);
|
var script = 'lnx_' + connection._url + '.send(`' + inputs[4].get() + '`, `' + api + message + '`);';
|
||||||
|
js.Syntax.code('(1, eval)({0})', script);
|
||||||
}
|
}
|
||||||
runOutput(0);
|
runOutput(0);
|
||||||
return;
|
return;
|
||||||
@ -143,9 +143,9 @@ class LeenkxSendMessageNode extends LogicNode {
|
|||||||
try {
|
try {
|
||||||
connection.buffer = buffer;
|
connection.buffer = buffer;
|
||||||
if(inputs[5].get() == true){
|
if(inputs[5].get() == true){
|
||||||
var script = 'for (p in lx_' + connection._url +'.torrent._peers){
|
var script = 'for (p in lnx_' + connection._url +'.torrent._peers){
|
||||||
try{
|
try{
|
||||||
lx_' + connection._url +'.torrent._peers[p].conn.send(leenkx.network.Leenkx.connections.h["'+ connection._url +'"].buffer );
|
lnx_' + connection._url +'.torrent._peers[p].conn.send(leenkx.network.Leenkx.connections.h["'+ connection._url +'"].buffer );
|
||||||
}catch(error){
|
}catch(error){
|
||||||
console.log("Error: " + error);
|
console.log("Error: " + error);
|
||||||
}
|
}
|
||||||
@ -153,7 +153,7 @@ class LeenkxSendMessageNode extends LogicNode {
|
|||||||
js.Syntax.code('(1, eval)({0})', script.toString());
|
js.Syntax.code('(1, eval)({0})', script.toString());
|
||||||
runOutput(0);
|
runOutput(0);
|
||||||
} else {
|
} else {
|
||||||
var script = 'lx_' + connection._url +'.send("' + inputs[4].get() + '",leenkx.network.Leenkx.connections.h["'+ connection._url +'"].buffer );';
|
var script = 'lnx_' + connection._url +'.send("' + inputs[4].get() + '",leenkx.network.Leenkx.connections.h["'+ connection._url +'"].buffer );';
|
||||||
js.Syntax.code('(1, eval)({0})', script.toString());
|
js.Syntax.code('(1, eval)({0})', script.toString());
|
||||||
runOutput(0);
|
runOutput(0);
|
||||||
}
|
}
|
||||||
@ -161,12 +161,12 @@ class LeenkxSendMessageNode extends LogicNode {
|
|||||||
trace("Error: " + error);
|
trace("Error: " + error);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
var window:haxe.DynamicAccess<Dynamic> = untyped js.Browser.window;
|
|
||||||
var lxCn = window.get('lx_' + connection._url);
|
|
||||||
if(inputs[5].get() == true){
|
if(inputs[5].get() == true){
|
||||||
lxCn.send(api+message);
|
var script = 'lnx_' + connection._url + '.send(`' + api + message + '`);';
|
||||||
|
js.Syntax.code('(1, eval)({0})', script);
|
||||||
}else{
|
}else{
|
||||||
lxCn.send(inputs[4].get(), api+message);
|
var script = 'lnx_' + connection._url + '.send(`' + inputs[4].get() + '`, `' + api + message + '`);';
|
||||||
|
js.Syntax.code('(1, eval)({0})', script);
|
||||||
}
|
}
|
||||||
runOutput(0);
|
runOutput(0);
|
||||||
return;
|
return;
|
||||||
@ -183,9 +183,9 @@ class LeenkxSendMessageNode extends LogicNode {
|
|||||||
try {
|
try {
|
||||||
connection.buffer = buffer;
|
connection.buffer = buffer;
|
||||||
if(inputs[5].get() == true){
|
if(inputs[5].get() == true){
|
||||||
var script = 'for (p in lx_' + connection._url +'.torrent._peers){
|
var script = 'for (p in lnx_' + connection._url +'.torrent._peers){
|
||||||
try{
|
try{
|
||||||
lx_' + connection._url +'.torrent._peers[p].conn.send(leenkx.network.Leenkx.connections.h["'+ connection._url +'"].buffer );
|
lnx_' + connection._url +'.torrent._peers[p].conn.send(leenkx.network.Leenkx.connections.h["'+ connection._url +'"].buffer );
|
||||||
}catch(error){
|
}catch(error){
|
||||||
console.log("Error: " + error);
|
console.log("Error: " + error);
|
||||||
}
|
}
|
||||||
@ -193,7 +193,7 @@ class LeenkxSendMessageNode extends LogicNode {
|
|||||||
js.Syntax.code('(1, eval)({0})', script.toString());
|
js.Syntax.code('(1, eval)({0})', script.toString());
|
||||||
runOutput(0);
|
runOutput(0);
|
||||||
} else {
|
} else {
|
||||||
var script = 'lx_' + connection._url +'.send("' + inputs[4].get() + '",leenkx.network.Leenkx.connections.h["'+ connection._url +'"].buffer );';
|
var script = 'lnx_' + connection._url +'.send("' + inputs[4].get() + '",leenkx.network.Leenkx.connections.h["'+ connection._url +'"].buffer );';
|
||||||
js.Syntax.code('(1, eval)({0})', script.toString());
|
js.Syntax.code('(1, eval)({0})', script.toString());
|
||||||
runOutput(0);
|
runOutput(0);
|
||||||
}
|
}
|
||||||
@ -201,12 +201,12 @@ class LeenkxSendMessageNode extends LogicNode {
|
|||||||
trace("Error: " + error);
|
trace("Error: " + error);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
var window:haxe.DynamicAccess<Dynamic> = untyped js.Browser.window;
|
|
||||||
var lxCn = window.get('lx_' + connection._url);
|
|
||||||
if(inputs[5].get() == true){
|
if(inputs[5].get() == true){
|
||||||
lxCn.send(api+message);
|
var script = 'lnx_' + connection._url + '.send(`' + api + message + '`);';
|
||||||
|
js.Syntax.code('(1, eval)({0})', script);
|
||||||
}else{
|
}else{
|
||||||
lxCn.send(inputs[4].get(), api+message);
|
var script = 'lnx_' + connection._url + '.send(`' + inputs[4].get() + '`, `' + api + message + '`);';
|
||||||
|
js.Syntax.code('(1, eval)({0})', script);
|
||||||
}
|
}
|
||||||
runOutput(0);
|
runOutput(0);
|
||||||
return;
|
return;
|
||||||
@ -225,9 +225,9 @@ class LeenkxSendMessageNode extends LogicNode {
|
|||||||
try {
|
try {
|
||||||
connection.buffer = buffer;
|
connection.buffer = buffer;
|
||||||
if(inputs[5].get() == true){
|
if(inputs[5].get() == true){
|
||||||
var script = 'for (p in lx_' + connection._url +'.torrent._peers){
|
var script = 'for (p in lnx_' + connection._url +'.torrent._peers){
|
||||||
try{
|
try{
|
||||||
lx_' + connection._url +'.torrent._peers[p].conn.send(leenkx.network.Leenkx.connections.h["'+ connection._url +'"].buffer );
|
lnx_' + connection._url +'.torrent._peers[p].conn.send(leenkx.network.Leenkx.connections.h["'+ connection._url +'"].buffer );
|
||||||
}catch(error){
|
}catch(error){
|
||||||
console.log("Error: " + error);
|
console.log("Error: " + error);
|
||||||
}
|
}
|
||||||
@ -235,7 +235,7 @@ class LeenkxSendMessageNode extends LogicNode {
|
|||||||
js.Syntax.code('(1, eval)({0})', script.toString());
|
js.Syntax.code('(1, eval)({0})', script.toString());
|
||||||
runOutput(0);
|
runOutput(0);
|
||||||
} else {
|
} else {
|
||||||
var script = 'lx_' + connection._url +'.send("' + inputs[4].get() + '",leenkx.network.Leenkx.connections.h["'+ connection._url +'"].buffer );';
|
var script = 'lnx_' + connection._url +'.send("' + inputs[4].get() + '",leenkx.network.Leenkx.connections.h["'+ connection._url +'"].buffer );';
|
||||||
js.Syntax.code('(1, eval)({0})', script.toString());
|
js.Syntax.code('(1, eval)({0})', script.toString());
|
||||||
runOutput(0);
|
runOutput(0);
|
||||||
}
|
}
|
||||||
@ -243,12 +243,12 @@ class LeenkxSendMessageNode extends LogicNode {
|
|||||||
trace("Error: " + error);
|
trace("Error: " + error);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
var window:haxe.DynamicAccess<Dynamic> = untyped js.Browser.window;
|
|
||||||
var lxCn = window.get('lx_' + connection._url);
|
|
||||||
if(inputs[5].get() == true){
|
if(inputs[5].get() == true){
|
||||||
lxCn.send(api+message);
|
var script = 'lnx_' + connection._url + '.send(`' + api + message + '`);';
|
||||||
|
js.Syntax.code('(1, eval)({0})', script);
|
||||||
}else{
|
}else{
|
||||||
lxCn.send(inputs[4].get(), api+message);
|
var script = 'lnx_' + connection._url + '.send(`' + inputs[4].get() + '`, `' + api + message + '`);';
|
||||||
|
js.Syntax.code('(1, eval)({0})', script);
|
||||||
}
|
}
|
||||||
runOutput(0);
|
runOutput(0);
|
||||||
return;
|
return;
|
||||||
@ -283,10 +283,10 @@ class LeenkxSendMessageNode extends LogicNode {
|
|||||||
connection.buffer = buffer;
|
connection.buffer = buffer;
|
||||||
|
|
||||||
if(inputs[5].get() == true){
|
if(inputs[5].get() == true){
|
||||||
var script = 'for (p in lx_' + connection._url +'.torrent._peers){
|
var script = 'for (p in lnx_' + connection._url +'.torrent._peers){
|
||||||
try{
|
try{
|
||||||
//console.log("Mine: " + lx_8001.torrent.discovery.peerId + " || Incomming: " + p);
|
//console.log("Mine: " + lx_8001.torrent.discovery.peerId + " || Incomming: " + p);
|
||||||
lx_' + connection._url +'.torrent._peers[p].conn.send(leenkx.network.Leenkx.connections.h["'+ connection._url +'"].buffer );
|
lnx_' + connection._url +'.torrent._peers[p].conn.send(leenkx.network.Leenkx.connections.h["'+ connection._url +'"].buffer );
|
||||||
}catch(error){
|
}catch(error){
|
||||||
console.log("Error: " + error);
|
console.log("Error: " + error);
|
||||||
}
|
}
|
||||||
@ -294,8 +294,8 @@ class LeenkxSendMessageNode extends LogicNode {
|
|||||||
js.Syntax.code('(1, eval)({0})', script.toString());
|
js.Syntax.code('(1, eval)({0})', script.toString());
|
||||||
runOutput(0);
|
runOutput(0);
|
||||||
} else {
|
} else {
|
||||||
//var script = 'lx_' + connection._url +'.send("' + inputs[4].get() + '",leenkx.network.Leenkx.connections.h["'+ connection._url +'"].buffer );';
|
//var script = 'lnx_' + connection._url +'.send("' + inputs[4].get() + '",leenkx.network.Leenkx.connections.h["'+ connection._url +'"].buffer );';
|
||||||
var script = 'lx_' + connection._url +'.send(leenkx.network.Leenkx.connections.h["'+ connection._url +'"].buffer );';
|
var script = 'lnx_' + connection._url +'.send(leenkx.network.Leenkx.connections.h["'+ connection._url +'"].buffer );';
|
||||||
js.Syntax.code('(1, eval)({0})', script.toString());
|
js.Syntax.code('(1, eval)({0})', script.toString());
|
||||||
runOutput(0);
|
runOutput(0);
|
||||||
}
|
}
|
||||||
@ -319,9 +319,9 @@ class LeenkxSendMessageNode extends LogicNode {
|
|||||||
try {
|
try {
|
||||||
connection.buffer = buffer;
|
connection.buffer = buffer;
|
||||||
if(inputs[5].get() == true){
|
if(inputs[5].get() == true){
|
||||||
var script = 'for (p in lx_' + connection._url +'.torrent._peers){
|
var script = 'for (p in lnx_' + connection._url +'.torrent._peers){
|
||||||
try{
|
try{
|
||||||
lx_' + connection._url +'.torrent._peers[p].conn.send(leenkx.network.Leenkx.connections.h["'+ connection._url +'"].buffer );
|
lnx_' + connection._url +'.torrent._peers[p].conn.send(leenkx.network.Leenkx.connections.h["'+ connection._url +'"].buffer );
|
||||||
}catch(error){
|
}catch(error){
|
||||||
console.log("Error: " + error);
|
console.log("Error: " + error);
|
||||||
}
|
}
|
||||||
@ -329,7 +329,7 @@ class LeenkxSendMessageNode extends LogicNode {
|
|||||||
js.Syntax.code('(1, eval)({0})', script.toString());
|
js.Syntax.code('(1, eval)({0})', script.toString());
|
||||||
runOutput(0);
|
runOutput(0);
|
||||||
} else {
|
} else {
|
||||||
var script = 'lx_' + connection._url +'.send("' + inputs[4].get() + '",leenkx.network.Leenkx.connections.h["'+ connection._url +'"].buffer );';
|
var script = 'lnx_' + connection._url +'.send("' + inputs[4].get() + '",leenkx.network.Leenkx.connections.h["'+ connection._url +'"].buffer );';
|
||||||
js.Syntax.code('(1, eval)({0})', script.toString());
|
js.Syntax.code('(1, eval)({0})', script.toString());
|
||||||
runOutput(0);
|
runOutput(0);
|
||||||
}
|
}
|
||||||
@ -337,12 +337,12 @@ class LeenkxSendMessageNode extends LogicNode {
|
|||||||
trace("Error: " + error);
|
trace("Error: " + error);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
var window:haxe.DynamicAccess<Dynamic> = untyped js.Browser.window;
|
|
||||||
var lxCn = window.get('lx_' + connection._url);
|
|
||||||
if(inputs[5].get() == true){
|
if(inputs[5].get() == true){
|
||||||
lxCn.send(api+message);
|
var script = 'lnx_' + connection._url + '.send(`' + api + message + '`);';
|
||||||
|
js.Syntax.code('(1, eval)({0})', script);
|
||||||
}else{
|
}else{
|
||||||
lxCn.send(inputs[4].get(), api+message);
|
var script = 'lnx_' + connection._url + '.send(`' + inputs[4].get() + '`, `' + api + message + '`);';
|
||||||
|
js.Syntax.code('(1, eval)({0})', script);
|
||||||
}
|
}
|
||||||
runOutput(0);
|
runOutput(0);
|
||||||
return;
|
return;
|
||||||
@ -360,9 +360,9 @@ class LeenkxSendMessageNode extends LogicNode {
|
|||||||
try {
|
try {
|
||||||
|
|
||||||
if(inputs[5].get() == true){
|
if(inputs[5].get() == true){
|
||||||
var script = 'for (p in lx_' + connection._url +'.torrent._peers){
|
var script = 'for (p in lnx_' + connection._url +'.torrent._peers){
|
||||||
try{
|
try{
|
||||||
lx_' + connection._url +'.torrent._peers[p].conn.send("' + api + message + '");
|
lnx_' + connection._url +'.torrent._peers[p].conn.send("' + api + message + '");
|
||||||
}catch(error){
|
}catch(error){
|
||||||
console.log("Error: " + error);
|
console.log("Error: " + error);
|
||||||
}
|
}
|
||||||
@ -370,7 +370,7 @@ class LeenkxSendMessageNode extends LogicNode {
|
|||||||
js.Syntax.code('(1, eval)({0})', script.toString());
|
js.Syntax.code('(1, eval)({0})', script.toString());
|
||||||
runOutput(0);
|
runOutput(0);
|
||||||
} else {
|
} else {
|
||||||
var script = 'for (p in lx_' + connection._url +'.torrent._peers){
|
var script = 'for (p in lnx_' + connection._url +'.torrent._peers){
|
||||||
try{
|
try{
|
||||||
|
|
||||||
lx_' + connection._url +'.torrent._peers[' + inputs[4].get() + '].conn.send("' + api + message + '");
|
lx_' + connection._url +'.torrent._peers[' + inputs[4].get() + '].conn.send("' + api + message + '");
|
||||||
|
|||||||
233
leenkx/Sources/leenkx/logicnode/MouseLookNode.hx
Normal file
233
leenkx/Sources/leenkx/logicnode/MouseLookNode.hx
Normal file
@ -0,0 +1,233 @@
|
|||||||
|
package leenkx.logicnode;
|
||||||
|
|
||||||
|
import iron.math.Vec4;
|
||||||
|
import iron.system.Input;
|
||||||
|
import iron.object.Object;
|
||||||
|
import kha.System;
|
||||||
|
import kha.FastFloat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MouseLookNode - FPS-style mouse look camera controller
|
||||||
|
*
|
||||||
|
* This node provides smooth, resolution-independent mouse look functionality for
|
||||||
|
* first-person perspective controls. It supports separate body and head objects,
|
||||||
|
* allowing for realistic FPS camera movement where the body rotates horizontally
|
||||||
|
* and the head/camera rotates vertically.
|
||||||
|
*
|
||||||
|
* Key Features:
|
||||||
|
* - Resolution-adaptive scaling for consistent feel across different screen sizes
|
||||||
|
* - Configurable axis orientations (X, Y, Z as front)
|
||||||
|
* - Optional mouse cursor locking and hiding
|
||||||
|
* - Invertible X/Y axes
|
||||||
|
* - Rotation capping/limiting for both horizontal and vertical movement
|
||||||
|
* - Smoothing support for smoother camera movement
|
||||||
|
* - Physics integration with automatic rigid body synchronization
|
||||||
|
* - Support for both local and world space head rotation
|
||||||
|
*/
|
||||||
|
class MouseLookNode extends LogicNode {
|
||||||
|
// Configuration properties (set from Blender node interface)
|
||||||
|
public var property0: String; // Front axis: "X", "Y", or "Z"
|
||||||
|
public var property1: Bool; // Hide Locked: auto-lock mouse cursor
|
||||||
|
public var property2: Bool; // Invert X: invert horizontal mouse movement
|
||||||
|
public var property3: Bool; // Invert Y: invert vertical mouse movement
|
||||||
|
public var property4: Bool; // Cap Left/Right: limit horizontal rotation
|
||||||
|
public var property5: Bool; // Cap Up/Down: limit vertical rotation
|
||||||
|
public var property6: Bool; // Head Local Space: use local space for head rotation
|
||||||
|
|
||||||
|
// Smoothing state variables - maintain previous frame values for interpolation
|
||||||
|
var smoothX: Float = 0.0; // Smoothed horizontal mouse delta
|
||||||
|
var smoothY: Float = 0.0; // Smoothed vertical mouse delta
|
||||||
|
|
||||||
|
// Rotation limits (in radians)
|
||||||
|
var maxHorizontal: Float = Math.PI; // Maximum horizontal rotation (180 degrees)
|
||||||
|
var maxVertical: Float = Math.PI / 2; // Maximum vertical rotation (90 degrees)
|
||||||
|
|
||||||
|
// Current rotation tracking for capping calculations
|
||||||
|
var currentHorizontal: Float = 0.0; // Accumulated horizontal rotation
|
||||||
|
var currentVertical: Float = 0.0; // Accumulated vertical rotation
|
||||||
|
|
||||||
|
// Resolution scaling reference - base resolution for consistent sensitivity
|
||||||
|
var baseResolutionWidth: Float = 1920.0;
|
||||||
|
|
||||||
|
// Sensitivity scaling constants
|
||||||
|
static inline var BASE_SCALE: Float = 1500.0; // Base sensitivity scale factor
|
||||||
|
static var RADIAN_SCALING_FACTOR: Float = Math.PI * 50.0 / 180.0; // Degrees to radians conversion with sensitivity scaling
|
||||||
|
|
||||||
|
public function new(tree: LogicTree) {
|
||||||
|
super(tree);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Main execution function called every frame when the node is active
|
||||||
|
*
|
||||||
|
* Input connections:
|
||||||
|
* [0] - Action trigger (not used in current implementation)
|
||||||
|
* [1] - Body Object: the main object that rotates horizontally
|
||||||
|
* [2] - Head Object: optional object that rotates vertically (typically camera)
|
||||||
|
* [3] - Sensitivity: mouse sensitivity multiplier
|
||||||
|
* [4] - Smoothing: movement smoothing factor (0.0 = no smoothing, 0.99 = maximum smoothing)
|
||||||
|
*/
|
||||||
|
override function run(from: Int) {
|
||||||
|
// Get input values from connected nodes
|
||||||
|
var bodyObject: Object = inputs[1].get();
|
||||||
|
var headObject: Object = inputs[2].get();
|
||||||
|
var sensitivity: FastFloat = inputs[3].get();
|
||||||
|
var smoothing: FastFloat = inputs[4].get();
|
||||||
|
|
||||||
|
// Early exit if no body object is provided
|
||||||
|
if (bodyObject == null) {
|
||||||
|
runOutput(0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get mouse input state
|
||||||
|
var mouse = Input.getMouse();
|
||||||
|
|
||||||
|
// Handle automatic mouse cursor locking for FPS controls
|
||||||
|
if (property1) {
|
||||||
|
if (mouse.started() && !mouse.locked) {
|
||||||
|
mouse.lock(); // Center and hide cursor, enable unlimited movement
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only process mouse look when cursor is locked or mouse button is held
|
||||||
|
// This prevents unwanted camera movement when UI elements are being used
|
||||||
|
if (!mouse.locked && !mouse.down()) {
|
||||||
|
runOutput(0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get raw mouse movement delta (pixels moved since last frame)
|
||||||
|
var deltaX: Float = mouse.movementX;
|
||||||
|
var deltaY: Float = mouse.movementY;
|
||||||
|
|
||||||
|
// Apply axis inversion if configured
|
||||||
|
if (property2) deltaX = -deltaX; // Invert horizontal movement
|
||||||
|
if (property3) deltaY = -deltaY; // Invert vertical movement
|
||||||
|
|
||||||
|
// Calculate resolution-adaptive scaling to maintain consistent sensitivity
|
||||||
|
// across different screen resolutions. Higher resolutions will have proportionally
|
||||||
|
// higher scaling to compensate for increased pixel density.
|
||||||
|
var resolutionMultiplier: Float = System.windowWidth() / baseResolutionWidth;
|
||||||
|
|
||||||
|
// Apply movement smoothing if enabled
|
||||||
|
// This creates a weighted average between current and previous movement values
|
||||||
|
// to reduce jittery camera movement, especially useful for low framerates
|
||||||
|
if (smoothing > 0.0) {
|
||||||
|
var smoothingFactor: Float = Math.min(smoothing, 0.99); // Cap smoothing to prevent complete freeze
|
||||||
|
smoothX = smoothX * smoothingFactor + deltaX * (1.0 - smoothingFactor);
|
||||||
|
smoothY = smoothY * smoothingFactor + deltaY * (1.0 - smoothingFactor);
|
||||||
|
deltaX = smoothX;
|
||||||
|
deltaY = smoothY;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Define rotation axes based on the configured front axis
|
||||||
|
// These determine which 3D axes are used for horizontal and vertical rotation
|
||||||
|
var horizontalAxis = new Vec4(); // Axis for left/right body rotation
|
||||||
|
var verticalAxis = new Vec4(); // Axis for up/down head rotation
|
||||||
|
|
||||||
|
switch (property0) {
|
||||||
|
case "X": // X-axis forward (e.g., for side-scrolling or specific orientations)
|
||||||
|
horizontalAxis.set(0, 0, 1); // Z-axis for horizontal rotation
|
||||||
|
verticalAxis.set(0, 1, 0); // Y-axis for vertical rotation
|
||||||
|
case "Y": // Y-axis forward (most common for 3D games)
|
||||||
|
#if lnx_yaxisup
|
||||||
|
// Y-up coordinate system (Blender default)
|
||||||
|
horizontalAxis.set(0, 0, 1); // Z-axis for horizontal rotation
|
||||||
|
verticalAxis.set(1, 0, 0); // X-axis for vertical rotation
|
||||||
|
#else
|
||||||
|
// Z-up coordinate system
|
||||||
|
horizontalAxis.set(0, 0, 1); // Z-axis for horizontal rotation
|
||||||
|
verticalAxis.set(1, 0, 0); // X-axis for vertical rotation
|
||||||
|
#end
|
||||||
|
case "Z": // Z-axis forward (top-down or specific orientations)
|
||||||
|
horizontalAxis.set(0, 1, 0); // Y-axis for horizontal rotation
|
||||||
|
verticalAxis.set(1, 0, 0); // X-axis for vertical rotation
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate final sensitivity scaling combining base scale and resolution adaptation
|
||||||
|
var finalScale: Float = BASE_SCALE * resolutionMultiplier;
|
||||||
|
|
||||||
|
// Apply user-defined sensitivity multiplier
|
||||||
|
deltaX *= sensitivity;
|
||||||
|
deltaY *= sensitivity;
|
||||||
|
|
||||||
|
// Convert pixel movement to rotation angles (radians)
|
||||||
|
// Negative values ensure natural movement direction (moving mouse right rotates right)
|
||||||
|
var horizontalRotation: Float = (-deltaX / finalScale) * RADIAN_SCALING_FACTOR;
|
||||||
|
var verticalRotation: Float = (-deltaY / finalScale) * RADIAN_SCALING_FACTOR;
|
||||||
|
|
||||||
|
// Apply horizontal rotation capping if enabled
|
||||||
|
// This prevents the character from rotating beyond specified limits
|
||||||
|
if (property4) {
|
||||||
|
currentHorizontal += horizontalRotation;
|
||||||
|
// Clamp rotation to maximum horizontal range and adjust current frame rotation
|
||||||
|
if (currentHorizontal > maxHorizontal) {
|
||||||
|
horizontalRotation -= (currentHorizontal - maxHorizontal);
|
||||||
|
currentHorizontal = maxHorizontal;
|
||||||
|
} else if (currentHorizontal < -maxHorizontal) {
|
||||||
|
horizontalRotation -= (currentHorizontal + maxHorizontal);
|
||||||
|
currentHorizontal = -maxHorizontal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply vertical rotation capping if enabled
|
||||||
|
// This prevents looking too far up or down (like human neck limitations)
|
||||||
|
if (property5) {
|
||||||
|
currentVertical += verticalRotation;
|
||||||
|
// Clamp rotation to maximum vertical range and adjust current frame rotation
|
||||||
|
if (currentVertical > maxVertical) {
|
||||||
|
verticalRotation -= (currentVertical - maxVertical);
|
||||||
|
currentVertical = maxVertical;
|
||||||
|
} else if (currentVertical < -maxVertical) {
|
||||||
|
verticalRotation -= (currentVertical + maxVertical);
|
||||||
|
currentVertical = -maxVertical;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply horizontal rotation to body object (character turning left/right)
|
||||||
|
if (horizontalRotation != 0.0) {
|
||||||
|
bodyObject.transform.rotate(horizontalAxis, horizontalRotation);
|
||||||
|
|
||||||
|
// Synchronize physics rigid body if present
|
||||||
|
// This ensures physics simulation stays in sync with visual transform
|
||||||
|
#if lnx_physics
|
||||||
|
var rigidBody = bodyObject.getTrait(leenkx.trait.physics.RigidBody);
|
||||||
|
if (rigidBody != null) rigidBody.syncTransform();
|
||||||
|
#end
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply vertical rotation to head object (camera looking up/down)
|
||||||
|
if (headObject != null && verticalRotation != 0.0) {
|
||||||
|
if (property6) {
|
||||||
|
// Local space rotation - recommended when head is a child of body
|
||||||
|
// This prevents gimbal lock and rotation inheritance issues
|
||||||
|
headObject.transform.rotate(verticalAxis, verticalRotation);
|
||||||
|
} else {
|
||||||
|
// World space rotation - uses head object's current right vector
|
||||||
|
// More accurate for independent head objects but can cause issues with parenting
|
||||||
|
var headVerticalAxis = headObject.transform.world.right();
|
||||||
|
headObject.transform.rotate(headVerticalAxis, verticalRotation);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Synchronize head physics rigid body if present
|
||||||
|
#if lnx_physics
|
||||||
|
var headRigidBody = headObject.getTrait(leenkx.trait.physics.RigidBody);
|
||||||
|
if (headRigidBody != null) headRigidBody.syncTransform();
|
||||||
|
#end
|
||||||
|
} else if (headObject == null && verticalRotation != 0.0) {
|
||||||
|
// Fallback: if no separate head object, apply vertical rotation to body
|
||||||
|
// This creates a simpler single-object camera control
|
||||||
|
bodyObject.transform.rotate(verticalAxis, verticalRotation);
|
||||||
|
|
||||||
|
// Synchronize body physics rigid body
|
||||||
|
#if lnx_physics
|
||||||
|
var rigidBody = bodyObject.getTrait(leenkx.trait.physics.RigidBody);
|
||||||
|
if (rigidBody != null) rigidBody.syncTransform();
|
||||||
|
#end
|
||||||
|
}
|
||||||
|
|
||||||
|
// Continue to next connected node in the logic tree
|
||||||
|
runOutput(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -16,7 +16,7 @@ class NetworkCloseConnectionNode extends LogicNode {
|
|||||||
if(property1 == "client") {
|
if(property1 == "client") {
|
||||||
var connection = cast(inputs[1].get(), leenkx.network.WebSocket);
|
var connection = cast(inputs[1].get(), leenkx.network.WebSocket);
|
||||||
if (connection == null) return;
|
if (connection == null) return;
|
||||||
#if sys
|
#if (sys || kha_krom)
|
||||||
try{
|
try{
|
||||||
var net_Url = connection._protocol + "://" + connection._host + ":" + connection._port;
|
var net_Url = connection._protocol + "://" + connection._host + ":" + connection._port;
|
||||||
connection.close();
|
connection.close();
|
||||||
@ -44,7 +44,7 @@ class NetworkCloseConnectionNode extends LogicNode {
|
|||||||
}
|
}
|
||||||
#end
|
#end
|
||||||
} else if(property1 == "securehost"){
|
} else if(property1 == "securehost"){
|
||||||
#if sys
|
#if (sys || kha_krom)
|
||||||
var connection = cast(inputs[1].get(), leenkx.network.WebSocketSecureServer<leenkx.network.Connect.HostHandler>);
|
var connection = cast(inputs[1].get(), leenkx.network.WebSocketSecureServer<leenkx.network.Connect.HostHandler>);
|
||||||
if (connection == null) return;
|
if (connection == null) return;
|
||||||
var net_Url = "wss://" + @:privateAccess connection._host + ":" + @:privateAccess connection._port;
|
var net_Url = "wss://" + @:privateAccess connection._host + ":" + @:privateAccess connection._port;
|
||||||
@ -56,7 +56,7 @@ class NetworkCloseConnectionNode extends LogicNode {
|
|||||||
}
|
}
|
||||||
#end
|
#end
|
||||||
} else {
|
} else {
|
||||||
#if sys
|
#if (sys || kha_krom)
|
||||||
var connection = cast(inputs[1].get(), leenkx.network.WebSocketServer<leenkx.network.Connect.HostHandler>);
|
var connection = cast(inputs[1].get(), leenkx.network.WebSocketServer<leenkx.network.Connect.HostHandler>);
|
||||||
if (connection == null) return;
|
if (connection == null) return;
|
||||||
var net_Url = "ws://" + @:privateAccess connection._host + ":" + @:privateAccess connection._port;
|
var net_Url = "ws://" + @:privateAccess connection._host + ":" + @:privateAccess connection._port;
|
||||||
|
|||||||
@ -28,7 +28,7 @@ class NetworkEventNode extends LogicNode {
|
|||||||
default: throw "Failed to set client event type.";
|
default: throw "Failed to set client event type.";
|
||||||
}
|
}
|
||||||
} else if (property0 == "host") {
|
} else if (property0 == "host") {
|
||||||
#if sys
|
#if (sys || kha_krom)
|
||||||
var net_Domain = inputs[0].get();
|
var net_Domain = inputs[0].get();
|
||||||
var net_Port = inputs[1].get();
|
var net_Port = inputs[1].get();
|
||||||
net_Url = "ws://" + net_Domain + ":" + Std.string(net_Port);
|
net_Url = "ws://" + net_Domain + ":" + Std.string(net_Port);
|
||||||
@ -41,7 +41,7 @@ class NetworkEventNode extends LogicNode {
|
|||||||
}
|
}
|
||||||
#end
|
#end
|
||||||
} else if (property0 == "securehost"){
|
} else if (property0 == "securehost"){
|
||||||
#if sys
|
#if (sys || kha_krom)
|
||||||
var net_Domain = inputs[0].get();
|
var net_Domain = inputs[0].get();
|
||||||
var net_Port = inputs[1].get();
|
var net_Port = inputs[1].get();
|
||||||
net_Url = "wss://" + net_Domain + ":" + Std.string(net_Port);
|
net_Url = "wss://" + net_Domain + ":" + Std.string(net_Port);
|
||||||
|
|||||||
@ -11,7 +11,7 @@ class NetworkHostCloseClientNode extends LogicNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override function run(from:Int) {
|
override function run(from:Int) {
|
||||||
#if sys
|
#if (sys || kha_krom)
|
||||||
if(property0 == false){
|
if(property0 == false){
|
||||||
var connection = cast(inputs[1].get(), leenkx.network.WebSocketServer<HostHandler>);
|
var connection = cast(inputs[1].get(), leenkx.network.WebSocketServer<HostHandler>);
|
||||||
if (connection == null) return;
|
if (connection == null) return;
|
||||||
|
|||||||
@ -12,7 +12,7 @@ class NetworkHostGetIpNode extends LogicNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override function run(from:Int) {
|
override function run(from:Int) {
|
||||||
#if sys
|
#if (sys || kha_krom)
|
||||||
if(property0 == false){
|
if(property0 == false){
|
||||||
var connection = cast(inputs[1].get(), leenkx.network.WebSocketServer<HostHandler>);
|
var connection = cast(inputs[1].get(), leenkx.network.WebSocketServer<HostHandler>);
|
||||||
if (connection == null) return;
|
if (connection == null) return;
|
||||||
|
|||||||
@ -12,7 +12,7 @@ class NetworkHostNode extends LogicNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override function run(from:Int) {
|
override function run(from:Int) {
|
||||||
#if sys
|
#if (sys || kha_krom)
|
||||||
if(property0 == false) {
|
if(property0 == false) {
|
||||||
final net_Object: Object = tree.object;
|
final net_Object: Object = tree.object;
|
||||||
var net_Domain: String = inputs[1].get();
|
var net_Domain: String = inputs[1].get();
|
||||||
@ -49,7 +49,7 @@ class NetworkHostNode extends LogicNode {
|
|||||||
#end
|
#end
|
||||||
}
|
}
|
||||||
|
|
||||||
#if sys
|
#if (sys || kha_krom)
|
||||||
override function get(from: Int): Dynamic {
|
override function get(from: Int): Dynamic {
|
||||||
if(property0 == false) {
|
if(property0 == false) {
|
||||||
return switch (from) {
|
return switch (from) {
|
||||||
|
|||||||
@ -85,10 +85,6 @@ class NetworkHttpRequestNode extends LogicNode {
|
|||||||
} catch( e : Dynamic ) {
|
} catch( e : Dynamic ) {
|
||||||
trace("Could not complete request: " + e);
|
trace("Could not complete request: " + e);
|
||||||
}
|
}
|
||||||
|
|
||||||
callbackType = 0;
|
|
||||||
runOutput(0);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override function get(from: Int): Dynamic {
|
override function get(from: Int): Dynamic {
|
||||||
|
|||||||
@ -19,7 +19,7 @@ class NetworkOpenConnectionNode extends LogicNode {
|
|||||||
var connection = cast(inputs[1].get(), leenkx.network.WebSocket);
|
var connection = cast(inputs[1].get(), leenkx.network.WebSocket);
|
||||||
if (connection == null) return;
|
if (connection == null) return;
|
||||||
var object = tree.object;
|
var object = tree.object;
|
||||||
#if sys
|
#if (sys || kha_krom)
|
||||||
net_Url = connection._protocol + "://" + connection._host + ":" + connection._port;
|
net_Url = connection._protocol + "://" + connection._host + ":" + connection._port;
|
||||||
Client.connections[net_Url] = null;
|
Client.connections[net_Url] = null;
|
||||||
var client = new leenkx.network.Connect.Client(net_Url, object);
|
var client = new leenkx.network.Connect.Client(net_Url, object);
|
||||||
@ -94,7 +94,7 @@ class NetworkOpenConnectionNode extends LogicNode {
|
|||||||
#end
|
#end
|
||||||
runOutput(0);
|
runOutput(0);
|
||||||
} else if (property0 == "securehost"){
|
} else if (property0 == "securehost"){
|
||||||
#if sys
|
#if (sys || kha_krom)
|
||||||
var connection = cast(inputs[1].get(), leenkx.network.WebSocketSecureServer<leenkx.network.Connect.HostHandler>);
|
var connection = cast(inputs[1].get(), leenkx.network.WebSocketSecureServer<leenkx.network.Connect.HostHandler>);
|
||||||
if (connection == null) return;
|
if (connection == null) return;
|
||||||
net_Url = "wss://" + @:privateAccess connection._host + ":" + @:privateAccess connection._port;
|
net_Url = "wss://" + @:privateAccess connection._host + ":" + @:privateAccess connection._port;
|
||||||
@ -108,7 +108,7 @@ class NetworkOpenConnectionNode extends LogicNode {
|
|||||||
runOutput(0);
|
runOutput(0);
|
||||||
#end
|
#end
|
||||||
} else {
|
} else {
|
||||||
#if sys
|
#if (sys || kha_krom)
|
||||||
var connection = cast(inputs[1].get(), leenkx.network.WebSocketServer<leenkx.network.Connect.HostHandler>);
|
var connection = cast(inputs[1].get(), leenkx.network.WebSocketServer<leenkx.network.Connect.HostHandler>);
|
||||||
if (connection == null) return;
|
if (connection == null) return;
|
||||||
net_Url = "ws://" + @:privateAccess connection._host + ":" + @:privateAccess connection._port;
|
net_Url = "ws://" + @:privateAccess connection._host + ":" + @:privateAccess connection._port;
|
||||||
@ -126,7 +126,7 @@ class NetworkOpenConnectionNode extends LogicNode {
|
|||||||
|
|
||||||
override function get(from: Int): Dynamic {
|
override function get(from: Int): Dynamic {
|
||||||
return switch (property0) {
|
return switch (property0) {
|
||||||
#if sys
|
#if (sys || kha_krom)
|
||||||
case "host": Host.connections[net_Url];
|
case "host": Host.connections[net_Url];
|
||||||
case "securehost": SecureHost.connections[net_Url];
|
case "securehost": SecureHost.connections[net_Url];
|
||||||
#end
|
#end
|
||||||
|
|||||||
@ -38,7 +38,7 @@ class NetworkSendMessageNode extends LogicNode {
|
|||||||
trace("Error: " + error);
|
trace("Error: " + error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#if sys
|
#if (sys || kha_krom)
|
||||||
else if(inputs[5].get() == true){
|
else if(inputs[5].get() == true){
|
||||||
if(property0 == "securehost"){
|
if(property0 == "securehost"){
|
||||||
var connection = cast(inputs[1].get(), leenkx.network.WebSocketSecureServer<SecureHostHandler>);
|
var connection = cast(inputs[1].get(), leenkx.network.WebSocketSecureServer<SecureHostHandler>);
|
||||||
@ -127,7 +127,7 @@ class NetworkSendMessageNode extends LogicNode {
|
|||||||
trace("Error: " + error);
|
trace("Error: " + error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#if sys
|
#if (sys || kha_krom)
|
||||||
else if(inputs[5].get() == true){
|
else if(inputs[5].get() == true){
|
||||||
if(property0 == "securehost"){
|
if(property0 == "securehost"){
|
||||||
var connection = cast(inputs[1].get(), leenkx.network.WebSocketSecureServer<SecureHostHandler>);
|
var connection = cast(inputs[1].get(), leenkx.network.WebSocketSecureServer<SecureHostHandler>);
|
||||||
@ -233,7 +233,7 @@ class NetworkSendMessageNode extends LogicNode {
|
|||||||
trace("Error: " + error);
|
trace("Error: " + error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#if sys
|
#if (sys || kha_krom)
|
||||||
else if(inputs[5].get() == true){
|
else if(inputs[5].get() == true){
|
||||||
if(property0 == "securehost"){
|
if(property0 == "securehost"){
|
||||||
var connection = cast(inputs[1].get(), leenkx.network.WebSocketSecureServer<SecureHostHandler>);
|
var connection = cast(inputs[1].get(), leenkx.network.WebSocketSecureServer<SecureHostHandler>);
|
||||||
@ -327,7 +327,7 @@ class NetworkSendMessageNode extends LogicNode {
|
|||||||
trace("Error: " + error);
|
trace("Error: " + error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#if sys
|
#if (sys || kha_krom)
|
||||||
else if(inputs[5].get() == true){
|
else if(inputs[5].get() == true){
|
||||||
if(property0 == "securehost"){
|
if(property0 == "securehost"){
|
||||||
var connection = cast(inputs[1].get(), leenkx.network.WebSocketSecureServer<SecureHostHandler>);
|
var connection = cast(inputs[1].get(), leenkx.network.WebSocketSecureServer<SecureHostHandler>);
|
||||||
@ -423,7 +423,7 @@ class NetworkSendMessageNode extends LogicNode {
|
|||||||
trace("Error: " + error);
|
trace("Error: " + error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#if sys
|
#if (sys || kha_krom)
|
||||||
else if(inputs[5].get() == true){
|
else if(inputs[5].get() == true){
|
||||||
if(property0 == "securehost"){
|
if(property0 == "securehost"){
|
||||||
var connection = cast(inputs[1].get(), leenkx.network.WebSocketSecureServer<SecureHostHandler>);
|
var connection = cast(inputs[1].get(), leenkx.network.WebSocketSecureServer<SecureHostHandler>);
|
||||||
@ -541,7 +541,7 @@ class NetworkSendMessageNode extends LogicNode {
|
|||||||
trace("Error: " + error);
|
trace("Error: " + error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#if sys
|
#if (sys || kha_krom)
|
||||||
else if(inputs[5].get() == true){
|
else if(inputs[5].get() == true){
|
||||||
if(property0 == "securehost"){
|
if(property0 == "securehost"){
|
||||||
var connection = cast(inputs[1].get(), leenkx.network.WebSocketSecureServer<SecureHostHandler>);
|
var connection = cast(inputs[1].get(), leenkx.network.WebSocketSecureServer<SecureHostHandler>);
|
||||||
@ -702,7 +702,7 @@ class NetworkSendMessageNode extends LogicNode {
|
|||||||
trace("Error: " + error);
|
trace("Error: " + error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#if sys
|
#if (sys || kha_krom)
|
||||||
else if(inputs[5].get() == true){
|
else if(inputs[5].get() == true){
|
||||||
if(property0 == "securehost"){
|
if(property0 == "securehost"){
|
||||||
var connection = cast(inputs[1].get(), leenkx.network.WebSocketSecureServer<SecureHostHandler>);
|
var connection = cast(inputs[1].get(), leenkx.network.WebSocketSecureServer<SecureHostHandler>);
|
||||||
|
|||||||
@ -1,7 +1,11 @@
|
|||||||
package leenkx.logicnode;
|
package leenkx.logicnode;
|
||||||
|
|
||||||
import iron.object.Object;
|
import iron.object.Object;
|
||||||
|
|
||||||
|
#if lnx_physics
|
||||||
|
import leenkx.trait.physics.PhysicsCache;
|
||||||
import leenkx.trait.physics.RigidBody;
|
import leenkx.trait.physics.RigidBody;
|
||||||
|
#end
|
||||||
|
|
||||||
|
|
||||||
class OnContactNode extends LogicNode {
|
class OnContactNode extends LogicNode {
|
||||||
|
|
||||||
@ -23,22 +27,15 @@ class OnContactNode extends LogicNode {
|
|||||||
|
|
||||||
var contact = false;
|
var contact = false;
|
||||||
|
|
||||||
#if lnx_physics
|
#if lnx_physics
|
||||||
var physics = leenkx.trait.physics.PhysicsWorld.active;
|
var rb1 = PhysicsCache.getCachedRigidBody(object1);
|
||||||
var rb1 = object1.getTrait(RigidBody);
|
var rb2 = PhysicsCache.getCachedRigidBody(object2);
|
||||||
if (rb1 != null) {
|
|
||||||
var rbs = physics.getContacts(rb1);
|
if (rb1 != null && rb2 != null) {
|
||||||
if (rbs != null) {
|
var rbs = PhysicsCache.getCachedContacts(rb1);
|
||||||
var rb2 = object2.getTrait(RigidBody);
|
contact = PhysicsCache.hasContactWith(rbs, rb2);
|
||||||
for (rb in rbs) {
|
|
||||||
if (rb == rb2) {
|
|
||||||
contact = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
#end
|
||||||
#end
|
|
||||||
|
|
||||||
var b = false;
|
var b = false;
|
||||||
switch (property0) {
|
switch (property0) {
|
||||||
|
|||||||
23
leenkx/Sources/leenkx/logicnode/OnceNode.hx
Normal file
23
leenkx/Sources/leenkx/logicnode/OnceNode.hx
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
package leenkx.logicnode;
|
||||||
|
|
||||||
|
class OnceNode extends LogicNode {
|
||||||
|
|
||||||
|
var triggered:Bool = false;
|
||||||
|
|
||||||
|
public function new(tree: LogicTree) {
|
||||||
|
super(tree);
|
||||||
|
}
|
||||||
|
|
||||||
|
override function run(from: Int) {
|
||||||
|
if(from == 1){
|
||||||
|
triggered = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!triggered) {
|
||||||
|
triggered = true;
|
||||||
|
runOutput(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -9,19 +9,38 @@ import iron.Scene;
|
|||||||
|
|
||||||
class PlayAnimationTreeNode extends LogicNode {
|
class PlayAnimationTreeNode extends LogicNode {
|
||||||
|
|
||||||
|
var object: Object;
|
||||||
|
var action: Dynamic;
|
||||||
|
var init: Bool = false;
|
||||||
|
|
||||||
public function new(tree: LogicTree) {
|
public function new(tree: LogicTree) {
|
||||||
super(tree);
|
super(tree);
|
||||||
}
|
}
|
||||||
|
|
||||||
override function run(from: Int) {
|
override function run(from: Int) {
|
||||||
var object: Object = inputs[1].get();
|
object = inputs[1].get();
|
||||||
var action: Dynamic = inputs[2].get();
|
action = inputs[2].get();
|
||||||
|
|
||||||
assert(Error, object != null, "The object input not be null");
|
assert(Error, object != null, "The object input not be null");
|
||||||
|
init = true;
|
||||||
|
tree.notifyOnUpdate(playAnim);
|
||||||
|
// TO DO: Investigate AnimAction get and PlayAnimationTree notifiers
|
||||||
|
}
|
||||||
|
|
||||||
|
function playAnim() {
|
||||||
|
if (init = false) return;
|
||||||
|
|
||||||
|
init = false;
|
||||||
|
tree.removeUpdate(playAnim);
|
||||||
|
|
||||||
var animation = object.animation;
|
var animation = object.animation;
|
||||||
if(animation == null) {
|
if(animation == null) {
|
||||||
#if lnx_skin
|
#if lnx_skin
|
||||||
animation = object.getBoneAnimation(object.uid);
|
animation = object.getBoneAnimation(object.uid);
|
||||||
|
if (animation == null) {
|
||||||
|
tree.notifyOnUpdate(playAnim);
|
||||||
|
init = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
cast(animation, BoneAnimation).setAnimationLoop(function f(mats) {
|
cast(animation, BoneAnimation).setAnimationLoop(function f(mats) {
|
||||||
action(mats);
|
action(mats);
|
||||||
});
|
});
|
||||||
@ -32,7 +51,6 @@ class PlayAnimationTreeNode extends LogicNode {
|
|||||||
action(mats);
|
action(mats);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
runOutput(0);
|
runOutput(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
41
leenkx/Sources/leenkx/logicnode/ProbabilisticIndexNode.hx
Normal file
41
leenkx/Sources/leenkx/logicnode/ProbabilisticIndexNode.hx
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
16
leenkx/Sources/leenkx/logicnode/ResolutionGetNode.hx
Normal file
16
leenkx/Sources/leenkx/logicnode/ResolutionGetNode.hx
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
package leenkx.logicnode;
|
||||||
|
|
||||||
|
class ResolutionGetNode extends LogicNode {
|
||||||
|
|
||||||
|
public function new(tree:LogicTree) {
|
||||||
|
super(tree);
|
||||||
|
}
|
||||||
|
|
||||||
|
override function get(from:Int):Dynamic {
|
||||||
|
return switch (from) {
|
||||||
|
case 0: leenkx.renderpath.Postprocess.resolution_uniforms[0];
|
||||||
|
case 1: leenkx.renderpath.Postprocess.resolution_uniforms[1];
|
||||||
|
default: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
33
leenkx/Sources/leenkx/logicnode/ResolutionSetNode.hx
Normal file
33
leenkx/Sources/leenkx/logicnode/ResolutionSetNode.hx
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
package leenkx.logicnode;
|
||||||
|
|
||||||
|
import kha.graphics4.TextureFilter;
|
||||||
|
|
||||||
|
class ResolutionSetNode extends LogicNode {
|
||||||
|
|
||||||
|
public function new(tree:LogicTree) {
|
||||||
|
super(tree);
|
||||||
|
}
|
||||||
|
|
||||||
|
override function run(from:Int) {
|
||||||
|
|
||||||
|
var size: Int = inputs[1].get();
|
||||||
|
var filter: Int = inputs[2].get();
|
||||||
|
|
||||||
|
#if rp_resolution_filter
|
||||||
|
if (filter == 0)
|
||||||
|
iron.object.Uniforms.defaultFilter = TextureFilter.LinearFilter;
|
||||||
|
else
|
||||||
|
iron.object.Uniforms.defaultFilter = TextureFilter.PointFilter;
|
||||||
|
|
||||||
|
leenkx.renderpath.Postprocess.resolution_uniforms[0] = size;
|
||||||
|
leenkx.renderpath.Postprocess.resolution_uniforms[1] = filter;
|
||||||
|
|
||||||
|
var npath = leenkx.renderpath.RenderPathCreator.get();
|
||||||
|
var world = iron.Scene.active.raw.world_ref;
|
||||||
|
npath.loadShader("shader_datas/World_" + world + "/World_" + world);
|
||||||
|
iron.RenderPath.setActive(npath);
|
||||||
|
#end
|
||||||
|
|
||||||
|
runOutput(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
23
leenkx/Sources/leenkx/logicnode/SetAudioPositionNode.hx
Normal file
23
leenkx/Sources/leenkx/logicnode/SetAudioPositionNode.hx
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
package leenkx.logicnode;
|
||||||
|
|
||||||
|
import aura.Aura;
|
||||||
|
import aura.Types;
|
||||||
|
|
||||||
|
class SetAudioPositionNode extends LogicNode {
|
||||||
|
|
||||||
|
public function new(tree: LogicTree) {
|
||||||
|
super(tree);
|
||||||
|
}
|
||||||
|
|
||||||
|
override function run(from: Int) {
|
||||||
|
var audio = inputs[1].get();
|
||||||
|
if (audio == null) return;
|
||||||
|
|
||||||
|
var positionInSeconds:Float = inputs[2].get();
|
||||||
|
if (positionInSeconds < 0.0) positionInSeconds = 0.0;
|
||||||
|
|
||||||
|
audio.channel.floatPosition = positionInSeconds * audio.channel.sampleRate;
|
||||||
|
|
||||||
|
runOutput(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
38
leenkx/Sources/leenkx/logicnode/SetCameraRenderFilterNode.hx
Normal file
38
leenkx/Sources/leenkx/logicnode/SetCameraRenderFilterNode.hx
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
package leenkx.logicnode;
|
||||||
|
|
||||||
|
import iron.object.MeshObject;
|
||||||
|
import iron.object.CameraObject;
|
||||||
|
|
||||||
|
class SetCameraRenderFilterNode extends LogicNode {
|
||||||
|
|
||||||
|
public var property0: String;
|
||||||
|
|
||||||
|
public function new(tree: LogicTree) {
|
||||||
|
super(tree);
|
||||||
|
}
|
||||||
|
|
||||||
|
override function run(from: Int) {
|
||||||
|
var mo: MeshObject = cast inputs[1].get();
|
||||||
|
var camera: CameraObject = inputs[2].get();
|
||||||
|
|
||||||
|
assert(Error, Std.isOfType(camera, CameraObject), "Camera must be a camera object!");
|
||||||
|
|
||||||
|
if (camera == null || mo == null) return;
|
||||||
|
|
||||||
|
if (property0 == 'Add'){
|
||||||
|
if (mo.cameraList == null || mo.cameraList.indexOf(camera.name) == -1){
|
||||||
|
if (mo.cameraList == null) mo.cameraList = [];
|
||||||
|
mo.cameraList.push(camera.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
if (mo.cameraList != null){
|
||||||
|
mo.cameraList.remove(camera.name);
|
||||||
|
if (mo.cameraList.length == 0)
|
||||||
|
mo.cameraList = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
runOutput(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
21
leenkx/Sources/leenkx/logicnode/SetLightShadowNode.hx
Normal file
21
leenkx/Sources/leenkx/logicnode/SetLightShadowNode.hx
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
package leenkx.logicnode;
|
||||||
|
|
||||||
|
import iron.object.LightObject;
|
||||||
|
|
||||||
|
class SetLightShadowNode extends LogicNode {
|
||||||
|
|
||||||
|
public function new(tree: LogicTree) {
|
||||||
|
super(tree);
|
||||||
|
}
|
||||||
|
|
||||||
|
override function run(from: Int) {
|
||||||
|
var light: LightObject = inputs[1].get();
|
||||||
|
var shadow: Bool = inputs[2].get();
|
||||||
|
|
||||||
|
if (light == null) return;
|
||||||
|
|
||||||
|
light.data.raw.cast_shadow = shadow;
|
||||||
|
|
||||||
|
runOutput(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
206
leenkx/Sources/leenkx/logicnode/SetLookAtRotationNode.hx
Normal file
206
leenkx/Sources/leenkx/logicnode/SetLookAtRotationNode.hx
Normal file
@ -0,0 +1,206 @@
|
|||||||
|
package leenkx.logicnode;
|
||||||
|
|
||||||
|
import iron.math.Vec4;
|
||||||
|
import iron.math.Quat;
|
||||||
|
import iron.math.Mat4;
|
||||||
|
import iron.object.Object;
|
||||||
|
|
||||||
|
class SetLookAtRotationNode extends LogicNode {
|
||||||
|
|
||||||
|
public var property0: String; // Axis to align
|
||||||
|
public var property1: String; // Use vector for target (true/false)
|
||||||
|
public var property2: String; // Use vector for source (true/false)
|
||||||
|
public var property3: String; // Damping value (backward compatibility, now input socket)
|
||||||
|
public var property4: String; // Disable rotation on aligning axis (true/false)
|
||||||
|
public var property5: String; // Use local space (true/false)
|
||||||
|
|
||||||
|
// Store the calculated rotation for output
|
||||||
|
var calculatedRotation: Quat = null;
|
||||||
|
// Store the previous rotation for smooth interpolation
|
||||||
|
var previousRotation: Quat = null;
|
||||||
|
|
||||||
|
public function new(tree: LogicTree) {
|
||||||
|
super(tree);
|
||||||
|
previousRotation = new Quat();
|
||||||
|
}
|
||||||
|
|
||||||
|
override function run(from: Int): Void {
|
||||||
|
// Determine if we're using a vector or an object as source
|
||||||
|
var useSourceVector: Bool = property2 == "true";
|
||||||
|
var objectToUse: Object = null;
|
||||||
|
var objectLoc: Vec4 = null;
|
||||||
|
|
||||||
|
if (useSourceVector) {
|
||||||
|
// Use tree.object as the object to rotate
|
||||||
|
objectToUse = tree.object;
|
||||||
|
if (objectToUse == null) {
|
||||||
|
runOutput(0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the source location directly
|
||||||
|
objectLoc = inputs[1].get();
|
||||||
|
if (objectLoc == null) {
|
||||||
|
runOutput(0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Get the source object (or fallback to tree.object)
|
||||||
|
objectToUse = (inputs.length > 1 && inputs[1] != null) ? inputs[1].get() : tree.object;
|
||||||
|
if (objectToUse == null) {
|
||||||
|
runOutput(0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get source object's WORLD position (important for child objects)
|
||||||
|
objectLoc = new Vec4(objectToUse.transform.worldx(), objectToUse.transform.worldy(), objectToUse.transform.worldz());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine if we're using a vector or an object as target
|
||||||
|
var useTargetVector: Bool = property1 == "true";
|
||||||
|
var targetLoc: Vec4 = null;
|
||||||
|
|
||||||
|
if (useTargetVector) {
|
||||||
|
// Get the target location directly
|
||||||
|
targetLoc = inputs[2].get();
|
||||||
|
if (targetLoc == null) {
|
||||||
|
runOutput(0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Get the target object
|
||||||
|
var targetObject: Object = inputs[2].get();
|
||||||
|
if (targetObject == null) {
|
||||||
|
runOutput(0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get target object's WORLD position (important for child objects)
|
||||||
|
targetLoc = new Vec4(targetObject.transform.worldx(), targetObject.transform.worldy(), targetObject.transform.worldz());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate direction to target
|
||||||
|
var direction = new Vec4(
|
||||||
|
targetLoc.x - objectLoc.x,
|
||||||
|
targetLoc.y - objectLoc.y,
|
||||||
|
targetLoc.z - objectLoc.z
|
||||||
|
);
|
||||||
|
direction.normalize();
|
||||||
|
|
||||||
|
// Calculate target rotation based on selected axis
|
||||||
|
calculatedRotation = new Quat();
|
||||||
|
switch (property0) {
|
||||||
|
case "X":
|
||||||
|
calculatedRotation.fromTo(new Vec4(1, 0, 0), direction);
|
||||||
|
case "-X":
|
||||||
|
calculatedRotation.fromTo(new Vec4(-1, 0, 0), direction);
|
||||||
|
case "Y":
|
||||||
|
calculatedRotation.fromTo(new Vec4(0, 1, 0), direction);
|
||||||
|
case "-Y":
|
||||||
|
calculatedRotation.fromTo(new Vec4(0, -1, 0), direction);
|
||||||
|
case "Z":
|
||||||
|
calculatedRotation.fromTo(new Vec4(0, 0, 1), direction);
|
||||||
|
case "-Z":
|
||||||
|
calculatedRotation.fromTo(new Vec4(0, 0, -1), direction);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If disable rotation on aligning axis is enabled, constrain the target rotation
|
||||||
|
if (property4 == "true") {
|
||||||
|
// Apply constraint to the target rotation BEFORE damping to avoid jiggling
|
||||||
|
var eulerAngles = calculatedRotation.toEulerOrdered("XYZ");
|
||||||
|
|
||||||
|
// Set the rotation around the selected axis to 0
|
||||||
|
switch (property0) {
|
||||||
|
case "X", "-X":
|
||||||
|
eulerAngles.x = 0.0;
|
||||||
|
case "Y", "-Y":
|
||||||
|
eulerAngles.y = 0.0;
|
||||||
|
case "Z", "-Z":
|
||||||
|
eulerAngles.z = 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert back to quaternion
|
||||||
|
calculatedRotation.fromEulerOrdered(eulerAngles, "XYZ");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert world rotation to local rotation if local space is enabled and object has a parent
|
||||||
|
var targetRotation = new Quat();
|
||||||
|
if (property5 == "true" && objectToUse.parent != null) {
|
||||||
|
// Get parent's world rotation
|
||||||
|
var parentWorldLoc = new Vec4();
|
||||||
|
var parentWorldRot = new Quat();
|
||||||
|
var parentWorldScale = new Vec4();
|
||||||
|
objectToUse.parent.transform.world.decompose(parentWorldLoc, parentWorldRot, parentWorldScale);
|
||||||
|
|
||||||
|
// Convert world rotation to local space by removing parent's rotation influence
|
||||||
|
// local_rotation = inverse(parent_world_rotation) * world_rotation
|
||||||
|
var invParentRot = new Quat().setFrom(parentWorldRot);
|
||||||
|
invParentRot.x = -invParentRot.x;
|
||||||
|
invParentRot.y = -invParentRot.y;
|
||||||
|
invParentRot.z = -invParentRot.z;
|
||||||
|
|
||||||
|
targetRotation.multquats(invParentRot, calculatedRotation);
|
||||||
|
} else {
|
||||||
|
// No local space conversion needed, use world rotation directly
|
||||||
|
targetRotation.setFrom(calculatedRotation);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply rotation with damping
|
||||||
|
var dampingValue: Float = 0.0;
|
||||||
|
|
||||||
|
// Try to get damping from input socket first (index 3), fallback to property
|
||||||
|
if (inputs.length > 3 && inputs[3] != null) {
|
||||||
|
var dampingInput: Dynamic = inputs[3].get();
|
||||||
|
if (dampingInput != null) {
|
||||||
|
dampingValue = dampingInput;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Fallback to property for backward compatibility
|
||||||
|
dampingValue = Std.parseFloat(property3);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dampingValue > 0.0) {
|
||||||
|
// Create a fixed interpolation rate that never reaches exactly 1.0
|
||||||
|
// Higher damping = slower rotation (smaller step)
|
||||||
|
var step = Math.max(0.001, (1.0 - dampingValue) * 0.2); // 0.001 to 0.2 range
|
||||||
|
|
||||||
|
// Get current local rotation as quaternion
|
||||||
|
var currentLocalRot = new Quat().setFrom(objectToUse.transform.rot);
|
||||||
|
|
||||||
|
// Calculate the difference between current and target rotation
|
||||||
|
var diffQuat = new Quat();
|
||||||
|
// q1 * inverse(q2) gives the rotation from q2 to q1
|
||||||
|
var invCurrent = new Quat().setFrom(currentLocalRot);
|
||||||
|
invCurrent.x = -invCurrent.x;
|
||||||
|
invCurrent.y = -invCurrent.y;
|
||||||
|
invCurrent.z = -invCurrent.z;
|
||||||
|
diffQuat.multquats(targetRotation, invCurrent);
|
||||||
|
|
||||||
|
// Convert to axis-angle representation
|
||||||
|
var axis = new Vec4();
|
||||||
|
var angle = diffQuat.toAxisAngle(axis);
|
||||||
|
|
||||||
|
// Apply only a portion of this rotation (step)
|
||||||
|
var partialAngle = angle * step;
|
||||||
|
|
||||||
|
// Create partial rotation quaternion
|
||||||
|
var partialRot = new Quat().fromAxisAngle(axis, partialAngle);
|
||||||
|
|
||||||
|
// Apply this partial rotation to current local rotation
|
||||||
|
var newLocalRot = new Quat();
|
||||||
|
newLocalRot.multquats(partialRot, currentLocalRot);
|
||||||
|
|
||||||
|
// Apply the new local rotation
|
||||||
|
objectToUse.transform.rot.setFrom(newLocalRot);
|
||||||
|
} else {
|
||||||
|
// No damping, apply instant rotation
|
||||||
|
objectToUse.transform.rot.setFrom(targetRotation);
|
||||||
|
}
|
||||||
|
|
||||||
|
objectToUse.transform.buildMatrix();
|
||||||
|
|
||||||
|
runOutput(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// No output sockets needed - this node only performs actions
|
||||||
|
}
|
||||||
@ -0,0 +1,55 @@
|
|||||||
|
package leenkx.logicnode;
|
||||||
|
|
||||||
|
import iron.object.MeshObject;
|
||||||
|
import iron.data.MaterialData;
|
||||||
|
|
||||||
|
class SetMaterialTextureFilterNode extends LogicNode {
|
||||||
|
|
||||||
|
public function new(tree: LogicTree) {
|
||||||
|
super(tree);
|
||||||
|
}
|
||||||
|
|
||||||
|
override function run(from: Int) {
|
||||||
|
var object: MeshObject = inputs[1].get();
|
||||||
|
var mat: MaterialData = inputs[2].get();
|
||||||
|
var slot: Int = inputs[3].get();
|
||||||
|
var name: String = inputs[4].get();
|
||||||
|
var filter: Int = inputs[5].get();
|
||||||
|
|
||||||
|
if (object == null) return;
|
||||||
|
if (slot >= object.materials.length) return;
|
||||||
|
|
||||||
|
var mo = cast(object, iron.object.MeshObject);
|
||||||
|
|
||||||
|
for (i => node in mo.materials[slot].contexts[0].raw.bind_textures)
|
||||||
|
if (node.name == name){
|
||||||
|
var moImgt = mo.materials[slot].contexts[0].raw.bind_textures[i];
|
||||||
|
switch(filter){
|
||||||
|
case 0: //Linear
|
||||||
|
moImgt.min_filter = null;
|
||||||
|
moImgt.mag_filter = null;
|
||||||
|
moImgt.mipmap_filter = null;
|
||||||
|
moImgt.generate_mipmaps = null;
|
||||||
|
case 1: //Closest
|
||||||
|
moImgt.min_filter = 'point';
|
||||||
|
moImgt.mag_filter = 'point';
|
||||||
|
moImgt.mipmap_filter = null;
|
||||||
|
moImgt.generate_mipmaps = null;
|
||||||
|
case 2: //Cubic
|
||||||
|
moImgt.min_filter = null;
|
||||||
|
moImgt.mag_filter = null;
|
||||||
|
moImgt.mipmap_filter = 'linear';
|
||||||
|
moImgt.generate_mipmaps = true;
|
||||||
|
case 3: //Smart
|
||||||
|
moImgt.min_filter = 'anisotropic';
|
||||||
|
moImgt.mag_filter = null;
|
||||||
|
moImgt.mipmap_filter = 'linear';
|
||||||
|
moImgt.generate_mipmaps = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
runOutput(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user