forked from LeenkxTeam/LNXSDK
Compare commits
278 Commits
Author | SHA1 | Date | |
---|---|---|---|
60a9db6459 | |||
3b5a93c92a | |||
4af990796e | |||
9fb4916c3c | |||
f61d5833bb | |||
40b52be713 | |||
07d8422f22 | |||
7179d42b27 | |||
99a5d7d445 | |||
5e0acd3d5d | |||
f4077e461b | |||
28943f1522 | |||
df4feac132 | |||
82412dbf81 | |||
6afc209db7 | |||
e9aae53be9 | |||
a65675ef75 | |||
8f073c5ae1 | |||
08d08e42d9 | |||
a1ee335c68 | |||
de6bf8a08a | |||
b9848cd2dc | |||
e922cc38e6 | |||
57f0e937d0 | |||
e234c8615c | |||
e594518e57 | |||
a41be0f436 | |||
1306033b36 | |||
eee0011cdd | |||
315ac0bd16 | |||
f289e6f89c | |||
700d236bf1 | |||
f228eab8d3 | |||
863d884b76 | |||
34e0f5a282 | |||
45e2e52008 | |||
444a215e63 | |||
fb2d2a1a7c | |||
f88c04abea | |||
6fdd4b3f70 | |||
a389c27d75 | |||
1909c3da9f | |||
5824bd91aa | |||
43fe559eef | |||
12c09545ce | |||
0e60951ec9 | |||
ccb8b358d3 | |||
1a8586777b | |||
3721c774a1 | |||
a58fba408d | |||
268fba6cd5 | |||
4ab14ce6c8 | |||
9023e8d1da | |||
b58c7a9632 | |||
99b70622f5 | |||
647b73b746 | |||
935c30ec08 | |||
0b0d597f89 | |||
d5878afb30 | |||
96b55a1a56 | |||
91b3072305 | |||
1d0b338d92 | |||
8e83c0d0d0 | |||
927baec4df | |||
f5c9e70d1a | |||
0423a735fc | |||
bd413917fc | |||
852377f60d | |||
e17e9a8e35 | |||
4055c979a1 | |||
06b003ecdb | |||
fd7f215bb2 | |||
6a1df9ec46 | |||
deccac3c46 | |||
432b0210b2 | |||
14cf5cebed | |||
2307e1504f | |||
1ebfecb644 | |||
175b575b23 | |||
63943a9cbf | |||
224d9be76f | |||
62d3c8757b | |||
3785f93573 | |||
ffb276745f | |||
d1c9258da5 | |||
3e0cd2be35 | |||
1fd1973470 | |||
a01c72ef76 | |||
4b01a562c9 | |||
6972d9abc4 | |||
63c6b9d98b | |||
313d24bbc8 | |||
6d2812306d | |||
e84d6ada84 | |||
5057f2b946 | |||
2715fe3398 | |||
7cb8b8a2d2 | |||
cd606009e0 | |||
965162b101 | |||
c61a57bfb3 | |||
cdc425fbcb | |||
846bb28c86 | |||
7fabd77ef8 | |||
fb1a5c88bf | |||
e05c83a8bb | |||
ee4f62e881 | |||
59f8dff22f | |||
5572226ac5 | |||
d04874e0b3 | |||
ef8b3a99ab | |||
1d3254a237 | |||
188af4a50f | |||
c45baaf396 | |||
4b1da08819 | |||
aeb353fb20 | |||
65961b1593 | |||
1c472155e2 | |||
4238f0b2a0 | |||
b40aadf76c | |||
7277987335 | |||
a48ec4d034 | |||
7f0153f816 | |||
40d893e139 | |||
9b9289d27d | |||
8786798edd | |||
fa425a98a5 | |||
5165769088 | |||
feabf446db | |||
e770120f7d | |||
7c1b1f2dd9 | |||
88a4f0e76a | |||
d9e613b3eb | |||
a0c84dc807 | |||
5a92920b1f | |||
30a624c857 | |||
8153d67eac | |||
9963a42c76 | |||
885385d7cb | |||
c433a7f09e | |||
c7e2c7d452 | |||
2a1235b3d8 | |||
074962d158 | |||
3ea0d7da9d | |||
00b580a4fa | |||
f779462f36 | |||
2c7343aa31 | |||
85912d84fb | |||
ad0d750dd0 | |||
9914d53045 | |||
9daf6c15ab | |||
9ac3de67aa | |||
fd02f6bca3 | |||
2cc0d3db3b | |||
fe730a65ee | |||
18ec9712fd | |||
0d80f3fb6d | |||
be06b222cb | |||
3f0984e227 | |||
15a10ea3aa | |||
012abfeaf6 | |||
dd6cd16661 | |||
d8b37efe1b | |||
a40a035c03 | |||
a318758cbf | |||
7c13a25caf | |||
141567467f | |||
047983a280 | |||
48ad4322cf | |||
eaa34308d0 | |||
741a12de78 | |||
9749467cd7 | |||
91ae053346 | |||
b7b7edb5e2 | |||
dfa99fcb14 | |||
bc0bf41b91 | |||
535e69dcd0 | |||
2eaf83d89c | |||
6e62917819 | |||
03106eff02 | |||
0c5d71ecd2 | |||
d6a7b7e305 | |||
3d91f2f1e7 | |||
d76c295786 | |||
79422337ae | |||
b0e624ef75 | |||
9d78aabf35 | |||
a3930d7761 | |||
c958113c94 | |||
385c683fe3 | |||
1050337751 | |||
114bf7544a | |||
0199ee9877 | |||
6e02aeee53 | |||
6c3d71c4c9 | |||
78592b245f | |||
80d4422c90 | |||
32df55d636 | |||
eede86e278 | |||
1b855f953f | |||
59df400b0d | |||
4af244e3e2 | |||
ae91f8801f | |||
7f5786d47c | |||
ea12d5b951 | |||
d37468a6ab | |||
d0b3dc2ff8 | |||
85bbc10d06 | |||
2fca73aebd | |||
8b187940ee | |||
00369aa90b | |||
26a10020ac | |||
421463a642 | |||
8d9f248d2f | |||
ed72206a26 | |||
6b7460dd4c | |||
447af740be | |||
3ff6c32ac9 | |||
b752d70109 | |||
a6f83e2d37 | |||
016e223fb8 | |||
502601e684 | |||
29a4bb6803 | |||
cfbe7c83cb | |||
a6d9cb9201 | |||
32cdbd8c54 | |||
1a17b646e4 | |||
cdf79de36b | |||
2aa6be6496 | |||
25c391d244 | |||
a3c2be4e79 | |||
3413e10134 | |||
fa91348428 | |||
d40d3eb96e | |||
16e019be26 | |||
2e77f67683 | |||
9824dc5a44 | |||
1c20e03e0c | |||
98f334c883 | |||
8ac8a780e1 | |||
25e5700084 | |||
28d60a652b | |||
778be03472 | |||
fff8b5c29e | |||
a70d0bd601 | |||
8fc14ac793 | |||
13add8f60b | |||
2c1605c855 | |||
3524676fcc | |||
a577d57263 | |||
58440bb347 | |||
f3808c4251 | |||
95c08e3424 | |||
ce762b3cfa | |||
15bcf4a374 | |||
634aaa0b6a | |||
69fc090f55 | |||
f42041ccb6 | |||
a8787bd315 | |||
b5e77aeef8 | |||
f379685fdd | |||
32ff286691 | |||
38ab682978 | |||
8efe115698 | |||
38f72101eb | |||
f6d03b060c | |||
f450c00ff1 | |||
751f960b82 | |||
9558ded5c4 | |||
8066756605 | |||
ac2507e0ae | |||
205d4ccc41 | |||
7565818d0e | |||
fc093eca3e | |||
61b8f21037 | |||
d988ce8c99 | |||
727d82f268 | |||
c3c89c320b | |||
0b5bb877fb |
2
.gitattributes
vendored
Normal file
2
.gitattributes
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
*.hdr binary
|
||||
blender/lnx/props.py ident
|
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
__pycache__/
|
||||
*.pyc
|
||||
*.DS_Store
|
@ -2,10 +2,12 @@
|
||||
-cp ../Kha/Backends/Krom
|
||||
-cp ../leenkx/Sources
|
||||
-cp ../iron/Sources
|
||||
-cp ../lib/aura/Sources
|
||||
-cp ../lib/haxebullet/Sources
|
||||
-cp ../lib/haxerecast/Sources
|
||||
-cp ../lib/zui/Sources
|
||||
--macro include('iron', true, null, ['../iron/Sources'])
|
||||
--macro include('aura', true, null, ['../lib/aura/Sources'])
|
||||
--macro include('haxebullet', true, null, ['../lib/haxebullet/Sources'])
|
||||
--macro include('haxerecast', true, null, ['../lib/haxerecast/Sources'])
|
||||
--macro include('leenkx', true, ['leenkx.network'], ['../leenkx/Sources','../iron/Sources'])
|
||||
|
@ -3,6 +3,10 @@
|
||||
|
||||
#include "compiled.inc"
|
||||
|
||||
#ifdef _CPostprocess
|
||||
uniform vec4 PPComp17;
|
||||
#endif
|
||||
|
||||
uniform sampler2D tex;
|
||||
uniform vec2 dir;
|
||||
uniform vec2 screenSize;
|
||||
@ -45,6 +49,12 @@ void main() {
|
||||
res += factor * col;
|
||||
}
|
||||
|
||||
#ifdef _CPostprocess
|
||||
vec3 AirColor = vec3(PPComp17.x, PPComp17.y, PPComp17.z);
|
||||
#else
|
||||
vec3 AirColor = volumAirColor;
|
||||
#endif
|
||||
|
||||
res /= sumfactor;
|
||||
fragColor = vec4(volumAirColor * res, 1.0);
|
||||
fragColor = vec4(AirColor * res, 1.0);
|
||||
}
|
||||
|
@ -19,6 +19,11 @@
|
||||
{
|
||||
"name": "screenSize",
|
||||
"link": "_screenSize"
|
||||
},
|
||||
{
|
||||
"name": "PPComp17",
|
||||
"link": "_PPComp17",
|
||||
"ifdef": ["_CPostprocess"]
|
||||
}
|
||||
],
|
||||
"texture_params": [],
|
||||
|
@ -5,7 +5,7 @@
|
||||
uniform sampler2D tex;
|
||||
|
||||
#ifdef _CPostprocess
|
||||
uniform vec3 PPComp13;
|
||||
uniform vec4 PPComp13;
|
||||
#endif
|
||||
|
||||
in vec2 texCoord;
|
||||
@ -43,13 +43,17 @@ void main() {
|
||||
#ifdef _CPostprocess
|
||||
float max_distort = PPComp13.x;
|
||||
int num_iter = int(PPComp13.y);
|
||||
int CAType = int(PPComp13.z);
|
||||
int on = int(PPComp13.w);
|
||||
#else
|
||||
float max_distort = compoChromaticStrength;
|
||||
int num_iter = compoChromaticSamples;
|
||||
int CAType = compoChromaticType;
|
||||
int on = 1;
|
||||
#endif
|
||||
|
||||
// Spectral
|
||||
if (compoChromaticType == 1) {
|
||||
if (CAType == 1) {
|
||||
float reci_num_iter_f = 1.0 / float(num_iter);
|
||||
|
||||
vec2 resolution = vec2(1,1);
|
||||
@ -64,7 +68,7 @@ void main() {
|
||||
sumcol += w * texture(tex, barrelDistortion(uv, 0.6 * max_distort * t));
|
||||
}
|
||||
|
||||
fragColor = sumcol / sumw;
|
||||
if (on == 1) fragColor = sumcol / sumw; else fragColor = texture(tex, texCoord);
|
||||
}
|
||||
|
||||
// Simple
|
||||
@ -73,6 +77,7 @@ void main() {
|
||||
col.x = texture(tex, texCoord + ((vec2(0.0, 1.0) * max_distort) / vec2(1000.0))).x;
|
||||
col.y = texture(tex, texCoord + ((vec2(-0.85, -0.5) * max_distort) / vec2(1000.0))).y;
|
||||
col.z = texture(tex, texCoord + ((vec2(0.85, -0.5) * max_distort) / vec2(1000.0))).z;
|
||||
fragColor = vec4(col.x, col.y, col.z, fragColor.w);
|
||||
if (on == 1) fragColor = vec4(col.x, col.y, col.z, fragColor.w);
|
||||
else fragColor = texture(tex, texCoord);
|
||||
}
|
||||
}
|
||||
|
@ -62,8 +62,11 @@ uniform vec3 PPComp5;
|
||||
uniform vec3 PPComp6;
|
||||
uniform vec3 PPComp7;
|
||||
uniform vec3 PPComp8;
|
||||
uniform vec3 PPComp11;
|
||||
uniform vec3 PPComp14;
|
||||
uniform vec4 PPComp15;
|
||||
uniform vec4 PPComp16;
|
||||
uniform vec4 PPComp18;
|
||||
#endif
|
||||
|
||||
// #ifdef _CPos
|
||||
@ -106,6 +109,16 @@ in vec2 texCoord;
|
||||
out vec4 fragColor;
|
||||
|
||||
#ifdef _CFog
|
||||
#ifdef _CPostprocess
|
||||
vec3 FogColor = vec3(PPComp18.x, PPComp18.y, PPComp18.z);
|
||||
float FogAmountA = PPComp18.w;
|
||||
float FogAmountB = PPComp11.z;
|
||||
#else
|
||||
vec3 FogColor = compoFogColor;
|
||||
float FogAmountA = compoFogAmountA;
|
||||
float FogAmountB = compoFogAmountB;
|
||||
#endif
|
||||
|
||||
// const vec3 compoFogColor = vec3(0.5, 0.6, 0.7);
|
||||
// const float compoFogAmountA = 1.0; // b = 0.01
|
||||
// const float compoFogAmountB = 1.0; // c = 0.1
|
||||
@ -118,8 +131,8 @@ out vec4 fragColor;
|
||||
// }
|
||||
vec3 applyFog(vec3 rgb, float distance) {
|
||||
// float fogAmount = 1.0 - exp(-distance * compoFogAmountA);
|
||||
float fogAmount = 1.0 - exp(-distance * (compoFogAmountA / 100));
|
||||
return mix(rgb, compoFogColor, fogAmount);
|
||||
float fogAmount = 1.0 - exp(-distance * (FogAmountA / 100));
|
||||
return mix(rgb, FogColor, fogAmount);
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -131,7 +144,7 @@ float ConvertEV100ToExposure(float EV100) {
|
||||
return 1/0.8 * exp2(-EV100);
|
||||
}
|
||||
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 ISO = PPComp1.z;
|
||||
const float EC = PPComp2.x;
|
||||
@ -349,16 +362,22 @@ void main() {
|
||||
|
||||
#ifdef _CSharpen
|
||||
#ifdef _CPostprocess
|
||||
float strengthSharpen = PPComp14.y;
|
||||
float strengthSharpen = PPComp14.y;
|
||||
vec3 SharpenColor = vec3(PPComp16.x, PPComp16.y, PPComp16.z);
|
||||
float SharpenSize = PPComp16.w;
|
||||
#else
|
||||
float strengthSharpen = compoSharpenStrength;
|
||||
vec3 SharpenColor = compoSharpenColor;
|
||||
float SharpenSize = compoSharpenSize;
|
||||
#endif
|
||||
vec3 col1 = textureLod(tex, texCo + vec2(-texStep.x, -texStep.y) * 1.5, 0.0).rgb;
|
||||
vec3 col2 = textureLod(tex, texCo + vec2(texStep.x, -texStep.y) * 1.5, 0.0).rgb;
|
||||
vec3 col3 = textureLod(tex, texCo + vec2(-texStep.x, texStep.y) * 1.5, 0.0).rgb;
|
||||
vec3 col4 = textureLod(tex, texCo + vec2(texStep.x, texStep.y) * 1.5, 0.0).rgb;
|
||||
vec3 col1 = textureLod(tex, texCo + vec2(-texStep.x, -texStep.y) * SharpenSize, 0.0).rgb;
|
||||
vec3 col2 = textureLod(tex, texCo + vec2(texStep.x, -texStep.y) * SharpenSize, 0.0).rgb;
|
||||
vec3 col3 = textureLod(tex, texCo + vec2(-texStep.x, texStep.y) * SharpenSize, 0.0).rgb;
|
||||
vec3 col4 = textureLod(tex, texCo + vec2(texStep.x, texStep.y) * SharpenSize, 0.0).rgb;
|
||||
vec3 colavg = (col1 + col2 + col3 + col4) * 0.25;
|
||||
fragColor.rgb += (fragColor.rgb - colavg) * strengthSharpen;
|
||||
|
||||
float edgeMagnitude = length(fragColor.rgb - colavg);
|
||||
fragColor.rgb = mix(fragColor.rgb, SharpenColor, min(edgeMagnitude * strengthSharpen * 2.0, 1.0));
|
||||
#endif
|
||||
|
||||
#ifdef _CFog
|
||||
@ -407,7 +426,11 @@ void main() {
|
||||
#endif
|
||||
|
||||
#ifdef _CExposure
|
||||
fragColor.rgb += fragColor.rgb * compoExposureStrength;
|
||||
#ifdef _CPostprocess
|
||||
fragColor.rgb+=fragColor.rgb*PPComp8.x;
|
||||
#else
|
||||
fragColor.rgb+= fragColor.rgb*compoExposureStrength;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef _CPostprocess
|
||||
@ -415,8 +438,13 @@ void main() {
|
||||
#endif
|
||||
|
||||
#ifdef _AutoExposure
|
||||
#ifdef _CPostprocess
|
||||
float AEStrength = PPComp8.y;
|
||||
#else
|
||||
float AEStrength = autoExposureStrength;
|
||||
#endif
|
||||
float expo = 2.0 - clamp(length(textureLod(histogram, vec2(0.5, 0.5), 0).rgb), 0.0, 1.0);
|
||||
fragColor.rgb *= pow(expo, autoExposureStrength * 2.0);
|
||||
fragColor.rgb *= pow(expo, AEStrength * 2.0);
|
||||
#endif
|
||||
|
||||
// Clamp color to get rid of INF values that don't work for the tone mapping below
|
||||
@ -480,9 +508,7 @@ fragColor.rgb = min(fragColor.rgb, 65504 * 0.5);
|
||||
fragColor.rgb = pow(fragColor.rgb, vec3(1.0 / 2.2)); // To gamma
|
||||
} else if (PPComp4.x == 10){
|
||||
fragColor.rgb = tonemapAgXFull(fragColor.rgb);
|
||||
} else {
|
||||
fragColor.rgb = vec3(0,1,0); //ERROR
|
||||
}
|
||||
} //else { fragColor.rgb = vec3(0,1,0); //ERROR}
|
||||
#endif
|
||||
|
||||
#else
|
||||
|
@ -235,6 +235,16 @@
|
||||
"name": "PPComp15",
|
||||
"link": "_PPComp15",
|
||||
"ifdef": ["_CPostprocess"]
|
||||
},
|
||||
{
|
||||
"name": "PPComp16",
|
||||
"link": "_PPComp16",
|
||||
"ifdef": ["_CPostprocess"]
|
||||
},
|
||||
{
|
||||
"name": "PPComp18",
|
||||
"link": "_PPComp18",
|
||||
"ifdef": ["_CPostprocess"]
|
||||
}
|
||||
],
|
||||
"texture_params": [],
|
||||
|
@ -2,13 +2,22 @@
|
||||
|
||||
#include "compiled.inc"
|
||||
|
||||
#ifdef _CPostprocess
|
||||
uniform vec3 PPComp8;
|
||||
#endif
|
||||
|
||||
uniform sampler2D tex;
|
||||
|
||||
in vec2 texCoord;
|
||||
out vec4 fragColor;
|
||||
|
||||
void main() {
|
||||
fragColor.a = 0.01 * autoExposureSpeed;
|
||||
#ifdef _CPostprocess
|
||||
fragColor.a = 0.01 * PPComp8.z;
|
||||
#else
|
||||
fragColor.a = 0.01 * autoExposureSpeed;
|
||||
#endif
|
||||
|
||||
fragColor.rgb = textureLod(tex, vec2(0.5, 0.5), 0.0).rgb +
|
||||
textureLod(tex, vec2(0.2, 0.2), 0.0).rgb +
|
||||
textureLod(tex, vec2(0.8, 0.2), 0.0).rgb +
|
||||
|
@ -8,7 +8,13 @@
|
||||
"blend_source": "source_alpha",
|
||||
"blend_destination": "inverse_source_alpha",
|
||||
"blend_operation": "add",
|
||||
"links": [],
|
||||
"links": [
|
||||
{
|
||||
"name": "PPComp8",
|
||||
"link": "_PPComp8",
|
||||
"ifdef": ["_CPostprocess"]
|
||||
}
|
||||
],
|
||||
"texture_params": [],
|
||||
"vertex_shader": "../include/pass.vert.glsl",
|
||||
"fragment_shader": "histogram_pass.frag.glsl"
|
||||
|
@ -11,6 +11,8 @@ vec3 uncharted2Tonemap(const vec3 x) {
|
||||
vec3 tonemapUncharted2(const vec3 color) {
|
||||
const float W = 11.2;
|
||||
const float exposureBias = 2.0;
|
||||
// TODO - Find out why black world value of 0.0,0.0,0.0 turns to white pixels
|
||||
if (dot(color, color) < 0.001) return vec3(0.001);
|
||||
vec3 curr = uncharted2Tonemap(exposureBias * color);
|
||||
vec3 whiteScale = 1.0 / uncharted2Tonemap(vec3(W));
|
||||
return curr * whiteScale;
|
||||
|
@ -11,6 +11,11 @@
|
||||
#include "std/light_common.glsl"
|
||||
#endif
|
||||
|
||||
#ifdef _CPostprocess
|
||||
uniform vec3 PPComp11;
|
||||
uniform vec4 PPComp17;
|
||||
#endif
|
||||
|
||||
uniform sampler2D gbufferD;
|
||||
uniform sampler2D snoise;
|
||||
|
||||
@ -87,7 +92,13 @@ out float fragColor;
|
||||
const float tScat = 0.08;
|
||||
const float tAbs = 0.0;
|
||||
const float tExt = tScat + tAbs;
|
||||
const float stepLen = 1.0 / volumSteps;
|
||||
#ifdef _CPostprocess
|
||||
float stepLen = 1.0 / int(PPComp11.y);
|
||||
float AirTurbidity = PPComp17.w;
|
||||
#else
|
||||
const float stepLen = 1.0 / volumSteps;
|
||||
float AirTurbidity = volumAirTurbidity;
|
||||
#endif
|
||||
const float lighting = 0.4;
|
||||
|
||||
void rayStep(inout vec3 curPos, inout float curOpticalDepth, inout float scatteredLightAmount, float stepLenWorld, vec3 viewVecNorm) {
|
||||
@ -162,5 +173,5 @@ void main() {
|
||||
rayStep(curPos, curOpticalDepth, scatteredLightAmount, stepLenWorld, viewVecNorm);
|
||||
}
|
||||
|
||||
fragColor = scatteredLightAmount * volumAirTurbidity;
|
||||
fragColor = scatteredLightAmount * AirTurbidity;
|
||||
}
|
||||
|
@ -140,6 +140,16 @@
|
||||
"link": "_biasLightWorldViewProjectionMatrixSpot3",
|
||||
"ifndef": ["_ShadowMapAtlas"],
|
||||
"ifdef": ["_Spot", "_ShadowMap"]
|
||||
},
|
||||
{
|
||||
"name": "PPComp11",
|
||||
"link": "_PPComp11",
|
||||
"ifdef": ["_CPostprocess"]
|
||||
},
|
||||
{
|
||||
"name": "PPComp17",
|
||||
"link": "_PPComp17",
|
||||
"ifdef": ["_CPostprocess"]
|
||||
}
|
||||
],
|
||||
"texture_params": [],
|
||||
|
@ -12,6 +12,7 @@ class App {
|
||||
static var traitInits: Array<Void->Void> = [];
|
||||
static var traitUpdates: Array<Void->Void> = [];
|
||||
static var traitLateUpdates: Array<Void->Void> = [];
|
||||
static var traitFixedUpdates: Array<Void->Void> = [];
|
||||
static var traitRenders: Array<kha.graphics4.Graphics->Void> = [];
|
||||
static var traitRenders2D: Array<kha.graphics2.Graphics->Void> = [];
|
||||
public static var framebuffer: kha.Framebuffer;
|
||||
@ -23,6 +24,8 @@ class App {
|
||||
public static var renderPathTime: Float;
|
||||
public static var endFrameCallbacks: Array<Void->Void> = [];
|
||||
#end
|
||||
static var last = 0.0;
|
||||
static var time = 0.0;
|
||||
static var lastw = -1;
|
||||
static var lasth = -1;
|
||||
public static var onResize: Void->Void = null;
|
||||
@ -34,13 +37,14 @@ class App {
|
||||
function new(done: Void->Void) {
|
||||
done();
|
||||
kha.System.notifyOnFrames(render);
|
||||
kha.Scheduler.addTimeTask(update, 0, iron.system.Time.delta);
|
||||
kha.Scheduler.addTimeTask(update, 0, iron.system.Time.step);
|
||||
}
|
||||
|
||||
public static function reset() {
|
||||
traitInits = [];
|
||||
traitUpdates = [];
|
||||
traitLateUpdates = [];
|
||||
traitFixedUpdates = [];
|
||||
traitRenders = [];
|
||||
traitRenders2D = [];
|
||||
if (onResets != null) for (f in onResets) f();
|
||||
@ -48,6 +52,8 @@ class App {
|
||||
|
||||
static function update() {
|
||||
if (Scene.active == null || !Scene.active.ready) return;
|
||||
|
||||
iron.system.Time.update();
|
||||
if (pauseUpdates) return;
|
||||
|
||||
#if lnx_debug
|
||||
@ -56,6 +62,14 @@ class App {
|
||||
|
||||
Scene.active.updateFrame();
|
||||
|
||||
|
||||
time += iron.system.Time.delta;
|
||||
|
||||
while (time >= iron.system.Time.fixedStep) {
|
||||
for (f in traitFixedUpdates) f();
|
||||
time -= iron.system.Time.fixedStep;
|
||||
}
|
||||
|
||||
var i = 0;
|
||||
var l = traitUpdates.length;
|
||||
while (i < l) {
|
||||
@ -106,7 +120,7 @@ class App {
|
||||
var frame = frames[0];
|
||||
framebuffer = frame;
|
||||
|
||||
iron.system.Time.update();
|
||||
iron.system.Time.render();
|
||||
|
||||
if (Scene.active == null || !Scene.active.ready) {
|
||||
render2D(frame);
|
||||
@ -172,6 +186,14 @@ class App {
|
||||
traitLateUpdates.remove(f);
|
||||
}
|
||||
|
||||
public static function notifyOnFixedUpdate(f: Void->Void) {
|
||||
traitFixedUpdates.push(f);
|
||||
}
|
||||
|
||||
public static function removeFixedUpdate(f: Void->Void) {
|
||||
traitFixedUpdates.remove(f);
|
||||
}
|
||||
|
||||
public static function notifyOnRender(f: kha.graphics4.Graphics->Void) {
|
||||
traitRenders.push(f);
|
||||
}
|
||||
|
@ -518,12 +518,44 @@ class RenderPath {
|
||||
return Reflect.field(kha.Shaders, handle + "_comp");
|
||||
}
|
||||
|
||||
#if (kha_krom && lnx_vr)
|
||||
public function drawStereo(drawMeshes: Int->Void) {
|
||||
for (eye in 0...2) {
|
||||
Krom.vrBeginRender(eye);
|
||||
drawMeshes(eye);
|
||||
Krom.vrEndRender(eye);
|
||||
#if lnx_vr
|
||||
public function drawStereo(drawMeshes: Void->Void) {
|
||||
var vr = kha.vr.VrInterface.instance;
|
||||
var appw = iron.App.w();
|
||||
var apph = iron.App.h();
|
||||
var halfw = Std.int(appw / 2);
|
||||
var g = currentG;
|
||||
|
||||
if (vr != null && vr.IsPresenting()) {
|
||||
// Left eye
|
||||
Scene.active.camera.V.setFrom(Scene.active.camera.leftV);
|
||||
Scene.active.camera.P.self = vr.GetProjectionMatrix(0);
|
||||
g.viewport(0, 0, halfw, apph);
|
||||
drawMeshes();
|
||||
|
||||
// Right eye
|
||||
begin(g, additionalTargets);
|
||||
Scene.active.camera.V.setFrom(Scene.active.camera.rightV);
|
||||
Scene.active.camera.P.self = vr.GetProjectionMatrix(1);
|
||||
g.viewport(halfw, 0, halfw, apph);
|
||||
drawMeshes();
|
||||
}
|
||||
else { // Simulate
|
||||
Scene.active.camera.buildProjection(halfw / apph);
|
||||
|
||||
// Left eye
|
||||
g.viewport(0, 0, halfw, apph);
|
||||
drawMeshes();
|
||||
|
||||
// Right eye
|
||||
begin(g, additionalTargets);
|
||||
Scene.active.camera.transform.move(Scene.active.camera.right(), 0.032);
|
||||
Scene.active.camera.buildMatrix();
|
||||
g.viewport(halfw, 0, halfw, apph);
|
||||
drawMeshes();
|
||||
|
||||
Scene.active.camera.transform.move(Scene.active.camera.right(), -0.032);
|
||||
Scene.active.camera.buildMatrix();
|
||||
}
|
||||
}
|
||||
#end
|
||||
|
@ -775,6 +775,7 @@ class Scene {
|
||||
// Attach particle systems
|
||||
#if lnx_particles
|
||||
if (o.particle_refs != null) {
|
||||
cast(object, MeshObject).render_emitter = o.render_emitter;
|
||||
for (ref in o.particle_refs) cast(object, MeshObject).setupParticleSystem(sceneName, ref);
|
||||
}
|
||||
#end
|
||||
@ -782,6 +783,11 @@ class Scene {
|
||||
if (o.tilesheet_ref != null) {
|
||||
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);
|
||||
});
|
||||
}
|
||||
@ -883,6 +889,10 @@ class Scene {
|
||||
|
||||
if (StringTools.endsWith(ptype, "Object") && 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 {
|
||||
switch (ptype) {
|
||||
|
@ -16,6 +16,7 @@ class Trait {
|
||||
var _remove: Array<Void->Void> = null;
|
||||
var _update: Array<Void->Void> = null;
|
||||
var _lateUpdate: Array<Void->Void> = null;
|
||||
var _fixedUpdate: Array<Void->Void> = null;
|
||||
var _render: Array<kha.graphics4.Graphics->Void> = null;
|
||||
var _render2D: Array<kha.graphics2.Graphics->Void> = null;
|
||||
|
||||
@ -87,6 +88,23 @@ class Trait {
|
||||
App.removeLateUpdate(f);
|
||||
}
|
||||
|
||||
/**
|
||||
Add fixed game logic handler.
|
||||
**/
|
||||
public function notifyOnFixedUpdate(f: Void->Void) {
|
||||
if (_fixedUpdate == null) _fixedUpdate = [];
|
||||
_fixedUpdate.push(f);
|
||||
App.notifyOnFixedUpdate(f);
|
||||
}
|
||||
|
||||
/**
|
||||
Remove fixed game logic handler.
|
||||
**/
|
||||
public function removeFixedUpdate(f: Void->Void) {
|
||||
_fixedUpdate.remove(f);
|
||||
App.removeFixedUpdate(f);
|
||||
}
|
||||
|
||||
/**
|
||||
Add render handler.
|
||||
**/
|
||||
|
@ -392,6 +392,8 @@ typedef TParticleData = {
|
||||
#end
|
||||
public var name: String;
|
||||
public var type: Int; // 0 - Emitter, Hair
|
||||
public var auto_start: Bool;
|
||||
public var is_unique: Bool;
|
||||
public var loop: Bool;
|
||||
public var count: Int;
|
||||
public var frame_start: FastFloat;
|
||||
@ -439,6 +441,7 @@ typedef TObj = {
|
||||
@:optional public var traits: Array<TTrait>;
|
||||
@:optional public var properties: Array<TProperty>;
|
||||
@:optional public var vertex_groups: Array<TVertex_groups>;
|
||||
@:optional public var camera_list: Array<String>;
|
||||
@:optional public var constraints: Array<TConstraint>;
|
||||
@:optional public var dimensions: Float32Array; // Geometry objects
|
||||
@:optional public var object_actions: Array<String>;
|
||||
|
50
leenkx/Sources/iron/format/bmp/Data.hx
Normal file
50
leenkx/Sources/iron/format/bmp/Data.hx
Normal file
@ -0,0 +1,50 @@
|
||||
/*
|
||||
* format - Haxe File Formats
|
||||
*
|
||||
* BMP File Format
|
||||
* Copyright (C) 2007-2009 Trevor McCauley, Baluta Cristian (hx port) & Robert Sköld (format conversion)
|
||||
*
|
||||
* Copyright (c) 2009, The Haxe Project Contributors
|
||||
* All rights reserved.
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* - Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE HAXE PROJECT CONTRIBUTORS "AS IS" AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE HAXE PROJECT CONTRIBUTORS BE LIABLE FOR
|
||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
|
||||
* DAMAGE.
|
||||
*/
|
||||
package iron.format.bmp;
|
||||
|
||||
typedef Data = {
|
||||
var header : iron.format.bmp.Header;
|
||||
var pixels : haxe.io.Bytes;
|
||||
#if (haxe_ver < 4)
|
||||
var colorTable : Null<haxe.io.Bytes>;
|
||||
#else
|
||||
var ?colorTable : haxe.io.Bytes;
|
||||
#end
|
||||
}
|
||||
|
||||
typedef Header = {
|
||||
var width : Int; // real width (in pixels)
|
||||
var height : Int; // real height (in pixels)
|
||||
var paddedStride : Int; // number of bytes in a stride (including padding)
|
||||
var topToBottom : Bool; // whether the bitmap is stored top to bottom
|
||||
var bpp : Int; // bits per pixel
|
||||
var dataLength : Int; // equal to `paddedStride` * `height`
|
||||
var compression : Int; // which compression is being used, 0 for no compression
|
||||
}
|
122
leenkx/Sources/iron/format/bmp/Reader.hx
Normal file
122
leenkx/Sources/iron/format/bmp/Reader.hx
Normal file
@ -0,0 +1,122 @@
|
||||
/*
|
||||
* format - Haxe File Formats
|
||||
*
|
||||
* BMP File Format
|
||||
* Copyright (C) 2007-2009 Robert Sköld
|
||||
*
|
||||
* Copyright (c) 2009, The Haxe Project Contributors
|
||||
* All rights reserved.
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* - Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE HAXE PROJECT CONTRIBUTORS "AS IS" AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE HAXE PROJECT CONTRIBUTORS BE LIABLE FOR
|
||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
|
||||
* DAMAGE.
|
||||
*/
|
||||
|
||||
package iron.format.bmp;
|
||||
|
||||
import iron.format.bmp.Data;
|
||||
|
||||
|
||||
class Reader {
|
||||
|
||||
var input : haxe.io.Input;
|
||||
|
||||
public function new( i ) {
|
||||
input = i;
|
||||
}
|
||||
|
||||
/**
|
||||
* Only supports uncompressed 24bpp bitmaps (the most common format).
|
||||
*
|
||||
* The returned bytes in `Data.pixels` will be in BGR order, and with padding (if present).
|
||||
*
|
||||
* @see https://msdn.microsoft.com/en-us/library/windows/desktop/dd318229(v=vs.85).aspx
|
||||
* @see https://en.wikipedia.org/wiki/BMP_file_format#Bitmap_file_header
|
||||
*/
|
||||
public function read() : format.bmp.Data {
|
||||
// Read Header
|
||||
for (b in ["B".code, "M".code]) {
|
||||
if (input.readByte() != b) throw "Invalid header";
|
||||
}
|
||||
|
||||
var fileSize = input.readInt32();
|
||||
input.readInt32(); // Reserved
|
||||
var offset = input.readInt32();
|
||||
|
||||
// Read InfoHeader
|
||||
var infoHeaderSize = input.readInt32(); // InfoHeader size
|
||||
if (infoHeaderSize != 40) {
|
||||
throw 'Info headers with size $infoHeaderSize not supported.';
|
||||
}
|
||||
var width = input.readInt32(); // Image width (actual, not padded)
|
||||
var height = input.readInt32(); // Image height
|
||||
var numPlanes = input.readInt16(); // Number of planes
|
||||
var bits = input.readInt16(); // Bits per pixel
|
||||
var compression = input.readInt32(); // Compression type
|
||||
var dataLength = input.readInt32(); // Image data size (includes padding!)
|
||||
input.readInt32(); // Horizontal resolution
|
||||
input.readInt32(); // Vertical resolution
|
||||
var colorsUsed = input.readInt32(); // Colors used (0 when uncompressed)
|
||||
input.readInt32(); // Important colors (0 when uncompressed)
|
||||
|
||||
// If there's no compression, the dataLength may be 0
|
||||
if ( compression == 0 && dataLength == 0 ) dataLength = fileSize - offset;
|
||||
|
||||
var bytesRead = 54; // total read above
|
||||
|
||||
var colorTable : haxe.io.Bytes = null;
|
||||
if ( bits <= 8 ) {
|
||||
if ( colorsUsed == 0 ) {
|
||||
colorsUsed = Tools.getNumColorsForBitDepth(bits);
|
||||
}
|
||||
var colorTableLength = 4 * colorsUsed;
|
||||
colorTable = haxe.io.Bytes.alloc( colorTableLength );
|
||||
input.readFullBytes( colorTable, 0, colorTableLength );
|
||||
bytesRead += colorTableLength;
|
||||
}
|
||||
|
||||
input.read( offset - bytesRead );
|
||||
|
||||
var p = haxe.io.Bytes.alloc( dataLength );
|
||||
|
||||
// Read Raster Data
|
||||
var paddedStride = Tools.computePaddedStride(width, bits);
|
||||
var topToBottom = false;
|
||||
if ( height < 0 ) { // if bitmap is stored top to bottom
|
||||
topToBottom = true;
|
||||
height = -height;
|
||||
}
|
||||
|
||||
input.readFullBytes(p, 0, dataLength);
|
||||
|
||||
return {
|
||||
header: {
|
||||
width: width,
|
||||
height: height,
|
||||
paddedStride: paddedStride,
|
||||
topToBottom: topToBottom,
|
||||
bpp: bits,
|
||||
dataLength: dataLength,
|
||||
compression: compression
|
||||
},
|
||||
pixels: p,
|
||||
colorTable: colorTable
|
||||
}
|
||||
}
|
||||
}
|
256
leenkx/Sources/iron/format/bmp/Tools.hx
Normal file
256
leenkx/Sources/iron/format/bmp/Tools.hx
Normal file
@ -0,0 +1,256 @@
|
||||
/*
|
||||
* format - Haxe File Formats
|
||||
*
|
||||
* BMP File Format
|
||||
* Copyright (C) 2007-2009 Trevor McCauley, Baluta Cristian (hx port) & Robert Sköld (format conversion)
|
||||
*
|
||||
* Copyright (c) 2009, The Haxe Project Contributors
|
||||
* All rights reserved.
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* - Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE HAXE PROJECT CONTRIBUTORS "AS IS" AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE HAXE PROJECT CONTRIBUTORS BE LIABLE FOR
|
||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
|
||||
* DAMAGE.
|
||||
*/
|
||||
package iron.format.bmp;
|
||||
|
||||
|
||||
class Tools {
|
||||
|
||||
// a r g b
|
||||
static var ARGB_MAP(default, never):Array<Int> = [0, 1, 2, 3];
|
||||
static var BGRA_MAP(default, never):Array<Int> = [3, 2, 1, 0];
|
||||
|
||||
static var COLOR_SIZE(default, never):Int = 4;
|
||||
|
||||
/**
|
||||
Extract BMP pixel data (24bpp in BGR format) and expands it to BGRA, removing any padding in the process.
|
||||
**/
|
||||
inline static public function extractBGRA( bmp : iron.format.bmp.Data ) : haxe.io.Bytes {
|
||||
return _extract32(bmp, BGRA_MAP, 0xFF);
|
||||
}
|
||||
|
||||
/**
|
||||
Extract BMP pixel data (24bpp in BGR format) and converts it to ARGB.
|
||||
**/
|
||||
inline static public function extractARGB( bmp : iron.format.bmp.Data ) : haxe.io.Bytes {
|
||||
return _extract32(bmp, ARGB_MAP, 0xFF);
|
||||
}
|
||||
|
||||
/**
|
||||
Creates BMP data from bytes in BGRA format for each pixel.
|
||||
**/
|
||||
inline static public function buildFromBGRA( width : Int, height : Int, srcBytes : haxe.io.Bytes, topToBottom : Bool = false ) : Data {
|
||||
return _buildFrom32(width, height, srcBytes, BGRA_MAP, topToBottom);
|
||||
}
|
||||
|
||||
/**
|
||||
Creates BMP data from bytes in ARGB format for each pixel.
|
||||
**/
|
||||
inline static public function buildFromARGB( width : Int, height : Int, srcBytes : haxe.io.Bytes, topToBottom : Bool = false ) : Data {
|
||||
return _buildFrom32(width, height, srcBytes, ARGB_MAP, topToBottom);
|
||||
}
|
||||
|
||||
inline static public function computePaddedStride(width:Int, bpp:Int):Int {
|
||||
return ((((width * bpp) + 31) & ~31) >> 3);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets number of colors for indexed palettes
|
||||
*/
|
||||
inline static public function getNumColorsForBitDepth(bpp:Int):Int {
|
||||
return switch (bpp) {
|
||||
case 1: 2;
|
||||
case 4: 16;
|
||||
case 8: 256;
|
||||
case 16: 65536;
|
||||
default: throw 'Unsupported bpp $bpp';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// `channelMap` contains indices to map into ARGB (f.e. the mapping for ARGB is [0,1,2,3], while for BGRA is [3,2,1,0])
|
||||
static function _extract32( bmp : iron.format.bmp.Data, channelMap : Array<Int>, alpha : Int = 0xFF) : haxe.io.Bytes {
|
||||
var srcBytes = bmp.pixels;
|
||||
var dstLen = bmp.header.width * bmp.header.height * 4;
|
||||
var dstBytes = haxe.io.Bytes.alloc( dstLen );
|
||||
var srcPaddedStride = bmp.header.paddedStride;
|
||||
|
||||
var yDir = -1;
|
||||
var dstPos = 0;
|
||||
var srcPos = srcPaddedStride * (bmp.header.height - 1);
|
||||
|
||||
if ( bmp.header.topToBottom ) {
|
||||
yDir = 1;
|
||||
srcPos = 0;
|
||||
}
|
||||
|
||||
if ( bmp.header.bpp < 8 || bmp.header.bpp == 16 ) {
|
||||
throw 'bpp ${bmp.header.bpp} not supported';
|
||||
}
|
||||
|
||||
var colorTable:haxe.io.Bytes = null;
|
||||
if ( bmp.header.bpp <= 8 ) {
|
||||
var colorTableLength = getNumColorsForBitDepth(bmp.header.bpp);
|
||||
colorTable = haxe.io.Bytes.alloc(colorTableLength * COLOR_SIZE);
|
||||
var definedColorTableLength = Std.int( bmp.colorTable.length / COLOR_SIZE );
|
||||
for( i in 0...definedColorTableLength ) {
|
||||
var b = bmp.colorTable.get( i * COLOR_SIZE);
|
||||
var g = bmp.colorTable.get( i * COLOR_SIZE + 1);
|
||||
var r = bmp.colorTable.get( i * COLOR_SIZE + 2);
|
||||
|
||||
colorTable.set(i * COLOR_SIZE + channelMap[0], alpha);
|
||||
colorTable.set(i * COLOR_SIZE + channelMap[1], r);
|
||||
colorTable.set(i * COLOR_SIZE + channelMap[2], g);
|
||||
colorTable.set(i * COLOR_SIZE + channelMap[3], b);
|
||||
}
|
||||
// We want to have the table the full length in case indices outside the range are present
|
||||
colorTable.fill(definedColorTableLength, colorTableLength - definedColorTableLength, 0);
|
||||
for( i in definedColorTableLength...colorTableLength ) {
|
||||
colorTable.set(i * COLOR_SIZE + channelMap[0], alpha);
|
||||
}
|
||||
}
|
||||
|
||||
switch bmp.header.compression {
|
||||
case 0:
|
||||
while( dstPos < dstLen ) {
|
||||
for( i in 0...bmp.header.width ) {
|
||||
if (bmp.header.bpp == 8) {
|
||||
|
||||
var currentSrcPos = srcPos + i;
|
||||
var index = srcBytes.get(currentSrcPos);
|
||||
dstBytes.blit( dstPos, colorTable, index * COLOR_SIZE, COLOR_SIZE );
|
||||
|
||||
} else if (bmp.header.bpp == 24) {
|
||||
|
||||
var currentSrcPos = srcPos + i * 3;
|
||||
var b = srcBytes.get(currentSrcPos);
|
||||
var g = srcBytes.get(currentSrcPos + 1);
|
||||
var r = srcBytes.get(currentSrcPos + 2);
|
||||
|
||||
dstBytes.set(dstPos + channelMap[0], alpha);
|
||||
dstBytes.set(dstPos + channelMap[1], r);
|
||||
dstBytes.set(dstPos + channelMap[2], g);
|
||||
dstBytes.set(dstPos + channelMap[3], b);
|
||||
|
||||
} else if (bmp.header.bpp == 32) {
|
||||
|
||||
var currentSrcPos = srcPos + i * 4;
|
||||
var b = srcBytes.get(currentSrcPos);
|
||||
var g = srcBytes.get(currentSrcPos + 1);
|
||||
var r = srcBytes.get(currentSrcPos + 2);
|
||||
|
||||
dstBytes.set(dstPos + channelMap[0], alpha);
|
||||
dstBytes.set(dstPos + channelMap[1], r);
|
||||
dstBytes.set(dstPos + channelMap[2], g);
|
||||
dstBytes.set(dstPos + channelMap[3], b);
|
||||
|
||||
}
|
||||
dstPos += 4;
|
||||
}
|
||||
srcPos += yDir * srcPaddedStride;
|
||||
}
|
||||
case 1:
|
||||
srcPos = 0;
|
||||
var x = 0;
|
||||
var y = bmp.header.topToBottom ? 0 : bmp.header.height - 1;
|
||||
while( srcPos < bmp.header.dataLength ) {
|
||||
var count = srcBytes.get(srcPos++);
|
||||
var index = srcBytes.get(srcPos++);
|
||||
if ( count == 0 ) {
|
||||
if ( index == 0 ) {
|
||||
x = 0;
|
||||
y += yDir;
|
||||
} else if ( index == 1 ) {
|
||||
break;
|
||||
} else if ( index == 2 ) {
|
||||
x += srcBytes.get(srcPos++);
|
||||
y += srcBytes.get(srcPos++);
|
||||
} else {
|
||||
count = index;
|
||||
for( i in 0...count ) {
|
||||
index = srcBytes.get(srcPos++);
|
||||
dstBytes.blit( COLOR_SIZE * ((x+i) + y * bmp.header.width), colorTable, index * COLOR_SIZE, COLOR_SIZE );
|
||||
}
|
||||
if (srcPos % 2 != 0) srcPos++;
|
||||
x += count;
|
||||
}
|
||||
} else {
|
||||
for( i in 0...count ) {
|
||||
dstBytes.blit( COLOR_SIZE * ((x+i) + y * bmp.header.width), colorTable, index * COLOR_SIZE, COLOR_SIZE );
|
||||
}
|
||||
x += count;
|
||||
}
|
||||
}
|
||||
default:
|
||||
throw 'compression ${bmp.header.compression} not supported';
|
||||
}
|
||||
|
||||
return dstBytes;
|
||||
}
|
||||
|
||||
// `channelMap` contains indices to map into ARGB (f.e. the mapping for ARGB is [0,1,2,3], while for BGRA is [3,2,1,0])
|
||||
static function _buildFrom32( width : Int, height : Int, srcBytes : haxe.io.Bytes, channelMap : Array<Int>, topToBottom : Bool = false ) : Data {
|
||||
var bpp = 24;
|
||||
var paddedStride = computePaddedStride(width, bpp);
|
||||
var bytesBGR = haxe.io.Bytes.alloc(paddedStride * height);
|
||||
var topToBottom = topToBottom;
|
||||
var dataLength = bytesBGR.length;
|
||||
|
||||
var dstStride = width * 3;
|
||||
var srcLen = width * height * 4;
|
||||
var yDir = -1;
|
||||
var dstPos = dataLength - paddedStride;
|
||||
var srcPos = 0;
|
||||
|
||||
if ( topToBottom ) {
|
||||
yDir = 1;
|
||||
dstPos = 0;
|
||||
}
|
||||
|
||||
while( srcPos < srcLen ) {
|
||||
var i = dstPos;
|
||||
while( i < dstPos + dstStride ) {
|
||||
var r = srcBytes.get(srcPos + channelMap[1]);
|
||||
var g = srcBytes.get(srcPos + channelMap[2]);
|
||||
var b = srcBytes.get(srcPos + channelMap[3]);
|
||||
|
||||
bytesBGR.set(i++, b);
|
||||
bytesBGR.set(i++, g);
|
||||
bytesBGR.set(i++, r);
|
||||
|
||||
srcPos += 4;
|
||||
}
|
||||
dstPos += yDir * paddedStride;
|
||||
}
|
||||
|
||||
return {
|
||||
header: {
|
||||
width: width,
|
||||
height: height,
|
||||
paddedStride: paddedStride,
|
||||
topToBottom: topToBottom,
|
||||
bpp: bpp,
|
||||
dataLength: dataLength,
|
||||
compression: 0
|
||||
},
|
||||
pixels: bytesBGR,
|
||||
colorTable: null
|
||||
}
|
||||
}
|
||||
}
|
74
leenkx/Sources/iron/format/bmp/Writer.hx
Normal file
74
leenkx/Sources/iron/format/bmp/Writer.hx
Normal file
@ -0,0 +1,74 @@
|
||||
/*
|
||||
* format - Haxe File Formats
|
||||
*
|
||||
* BMP File Format
|
||||
* Copyright (C) 2007-2009 Robert Sköld
|
||||
*
|
||||
* Copyright (c) 2009, The Haxe Project Contributors
|
||||
* All rights reserved.
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* - Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE HAXE PROJECT CONTRIBUTORS "AS IS" AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE HAXE PROJECT CONTRIBUTORS BE LIABLE FOR
|
||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
|
||||
* DAMAGE.
|
||||
*/
|
||||
|
||||
package iron.format.bmp;
|
||||
|
||||
import iron.format.bmp.Data;
|
||||
|
||||
|
||||
class Writer {
|
||||
|
||||
static var DATA_OFFSET : Int = 0x36;
|
||||
|
||||
var output : haxe.io.Output;
|
||||
|
||||
public function new(o) {
|
||||
output = o;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specs: http://s223767089.online.de/en/file-format-bmp
|
||||
*/
|
||||
public function write( bmp : Data ) {
|
||||
// Write Header (14 bytes)
|
||||
output.writeString( "BM" ); // Signature
|
||||
output.writeInt32(bmp.pixels.length + DATA_OFFSET ); // FileSize
|
||||
output.writeInt32( 0 ); // Reserved
|
||||
output.writeInt32( DATA_OFFSET ); // Offset
|
||||
|
||||
// Write InfoHeader (40 bytes)
|
||||
output.writeInt32( 40 ); // InfoHeader size
|
||||
output.writeInt32( bmp.header.width ); // Image width
|
||||
var height = bmp.header.height;
|
||||
if (bmp.header.topToBottom) height = -height;
|
||||
output.writeInt32( height ); // Image height
|
||||
output.writeInt16( 1 ); // Number of planes
|
||||
output.writeInt16( 24 ); // Bits per pixel (24bit RGB)
|
||||
output.writeInt32( 0 ); // Compression type (no compression)
|
||||
output.writeInt32( bmp.header.dataLength ); // Image data size (0 when uncompressed)
|
||||
output.writeInt32( 0x2e30 ); // Horizontal resolution
|
||||
output.writeInt32( 0x2e30 ); // Vertical resolution
|
||||
output.writeInt32( 0 ); // Colors used (0 when uncompressed)
|
||||
output.writeInt32( 0 ); // Important colors (0 when uncompressed)
|
||||
|
||||
// Write Raster Data
|
||||
output.write(bmp.pixels);
|
||||
}
|
||||
}
|
@ -159,9 +159,17 @@ class Animation {
|
||||
if(markerEvents.get(sampler) != null){
|
||||
for (i in 0...anim.marker_frames.length) {
|
||||
if (frameIndex == anim.marker_frames[i]) {
|
||||
var marketAct = markerEvents.get(sampler);
|
||||
var ar = marketAct.get(anim.marker_names[i]);
|
||||
var markerAct = markerEvents.get(sampler);
|
||||
var ar = markerAct.get(anim.marker_names[i]);
|
||||
if (ar != null) for (f in ar) f();
|
||||
} else {
|
||||
for (j in 0...(frameIndex - lastFrameIndex)) {
|
||||
if (lastFrameIndex + j + 1 == anim.marker_frames[i]) {
|
||||
var markerAct = markerEvents.get(sampler);
|
||||
var ar = markerAct.get(anim.marker_names[i]);
|
||||
if (ar != null) for (f in ar) f();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
lastFrameIndex = frameIndex;
|
||||
|
@ -30,12 +30,22 @@ class CameraObject extends Object {
|
||||
static var sphereCenter = new Vec4();
|
||||
static var vcenter = 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) {
|
||||
super();
|
||||
|
||||
this.data = data;
|
||||
|
||||
#if lnx_vr
|
||||
iron.system.VR.initButton();
|
||||
#end
|
||||
|
||||
buildProjection();
|
||||
|
||||
V = Mat4.identity();
|
||||
@ -117,6 +127,26 @@ class CameraObject extends Object {
|
||||
V.getInverse(transform.world);
|
||||
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) {
|
||||
buildViewFrustum(VP, frustumPlanes);
|
||||
}
|
||||
|
@ -155,8 +155,13 @@ class LightObject extends Object {
|
||||
}
|
||||
|
||||
public function setCascade(camera: CameraObject, cascade: Int) {
|
||||
m.setFrom(camera.V);
|
||||
|
||||
#if lnx_vr
|
||||
m.setFrom(camera.leftV);
|
||||
#else
|
||||
m.setFrom(camera.V);
|
||||
#end
|
||||
|
||||
#if lnx_csm
|
||||
if (camSlicedP == null) {
|
||||
camSlicedP = [];
|
||||
|
@ -21,8 +21,10 @@ class MeshObject extends Object {
|
||||
public var particleChildren: Array<MeshObject> = null;
|
||||
public var particleOwner: MeshObject = null; // Particle object
|
||||
public var particleIndex = -1;
|
||||
public var render_emitter = true;
|
||||
#end
|
||||
public var cameraDistance: Float;
|
||||
public var cameraList: Array<String> = null;
|
||||
public var screenSize = 0.0;
|
||||
public var frustumCulling = true;
|
||||
public var activeTilesheet: Tilesheet = null;
|
||||
@ -234,6 +236,8 @@ class MeshObject extends Object {
|
||||
if (cullMesh(context, Scene.active.camera, RenderPath.active.light)) return;
|
||||
var meshContext = raw != null ? context == "mesh" : false;
|
||||
|
||||
if (cameraList != null && cameraList.indexOf(Scene.active.camera.name) < 0) return;
|
||||
|
||||
#if lnx_particles
|
||||
if (raw != null && raw.is_particle && particleOwner == null) return; // Instancing not yet set-up by particle system owner
|
||||
if (particleSystems != null && meshContext) {
|
||||
@ -244,6 +248,7 @@ class MeshObject extends Object {
|
||||
Scene.active.spawnObject(psys.data.raw.instance_object, null, function(o: Object) {
|
||||
if (o != null) {
|
||||
var c: MeshObject = cast o;
|
||||
c.cameraList = this.cameraList;
|
||||
particleChildren.push(c);
|
||||
c.particleOwner = this;
|
||||
c.particleIndex = particleChildren.length - 1;
|
||||
@ -255,11 +260,11 @@ class MeshObject extends Object {
|
||||
particleSystems[i].update(particleChildren[i], this);
|
||||
}
|
||||
}
|
||||
if (particleSystems != null && particleSystems.length > 0 && !raw.render_emitter) return;
|
||||
if (particleSystems != null && particleSystems.length > 0 && !render_emitter) return;
|
||||
if (particleSystems == null && cullMaterial(context)) return;
|
||||
#else
|
||||
if (cullMaterial(context)) return;
|
||||
#end
|
||||
|
||||
if (cullMaterial(context)) return;
|
||||
|
||||
// Get lod
|
||||
var mats = materials;
|
||||
var lod = this;
|
||||
|
@ -172,6 +172,10 @@ class Object {
|
||||
for (f in t._init) App.removeInit(f);
|
||||
t._init = null;
|
||||
}
|
||||
if (t._fixedUpdate != null) {
|
||||
for (f in t._fixedUpdate) App.removeFixedUpdate(f);
|
||||
t._fixedUpdate = null;
|
||||
}
|
||||
if (t._update != null) {
|
||||
for (f in t._update) App.removeUpdate(f);
|
||||
t._update = null;
|
||||
|
@ -2,6 +2,7 @@ package iron.object;
|
||||
|
||||
#if lnx_particles
|
||||
|
||||
import kha.FastFloat;
|
||||
import kha.graphics4.Usage;
|
||||
import kha.arrays.Float32Array;
|
||||
import iron.data.Data;
|
||||
@ -16,10 +17,12 @@ import iron.math.Vec4;
|
||||
class ParticleSystem {
|
||||
public var data: ParticleData;
|
||||
public var speed = 1.0;
|
||||
var currentSpeed = 0.0;
|
||||
var particles: Array<Particle>;
|
||||
var ready: Bool;
|
||||
var frameRate = 24;
|
||||
var lifetime = 0.0;
|
||||
var looptime = 0.0;
|
||||
var animtime = 0.0;
|
||||
var time = 0.0;
|
||||
var spawnRate = 0.0;
|
||||
@ -46,9 +49,13 @@ class ParticleSystem {
|
||||
var ownerLoc = new Vec4();
|
||||
var ownerRot = new Quat();
|
||||
var ownerScl = new Vec4();
|
||||
|
||||
var random = 0.0;
|
||||
|
||||
public function new(sceneName: String, pref: TParticleReference) {
|
||||
seed = pref.seed;
|
||||
currentSpeed = speed;
|
||||
speed = 0;
|
||||
particles = [];
|
||||
ready = false;
|
||||
|
||||
@ -65,33 +72,61 @@ class ParticleSystem {
|
||||
gy = 0;
|
||||
gz = -9.81 * r.weight_gravity;
|
||||
}
|
||||
alignx = r.object_align_factor[0] / 2;
|
||||
aligny = r.object_align_factor[1] / 2;
|
||||
alignz = r.object_align_factor[2] / 2;
|
||||
alignx = r.object_align_factor[0];
|
||||
aligny = r.object_align_factor[1];
|
||||
alignz = r.object_align_factor[2];
|
||||
looptime = (r.frame_end - r.frame_start) / frameRate;
|
||||
lifetime = r.lifetime / frameRate;
|
||||
animtime = (r.frame_end - r.frame_start) / frameRate;
|
||||
animtime = r.loop ? looptime : looptime + lifetime;
|
||||
spawnRate = ((r.frame_end - r.frame_start) / r.count) / frameRate;
|
||||
|
||||
for (i in 0...r.count) {
|
||||
var particle = new Particle(i);
|
||||
particle.sr = 1 - Math.random() * r.size_random;
|
||||
particles.push(particle);
|
||||
particles.push(new Particle(i));
|
||||
}
|
||||
|
||||
ready = true;
|
||||
if (r.auto_start){
|
||||
start();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public function start() {
|
||||
if (r.is_unique) random = Math.random();
|
||||
lifetime = r.lifetime / frameRate;
|
||||
time = 0;
|
||||
lap = 0;
|
||||
lapTime = 0;
|
||||
speed = currentSpeed;
|
||||
}
|
||||
|
||||
public function pause() {
|
||||
lifetime = 0;
|
||||
speed = 0;
|
||||
}
|
||||
|
||||
public function resume() {
|
||||
lifetime = r.lifetime / frameRate;
|
||||
speed = currentSpeed;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// TODO: interrupt smoothly
|
||||
public function stop() {
|
||||
end();
|
||||
}
|
||||
|
||||
function end() {
|
||||
lifetime = 0;
|
||||
speed = 0;
|
||||
lap = 0;
|
||||
}
|
||||
|
||||
public function update(object: MeshObject, owner: MeshObject) {
|
||||
if (!ready || object == null || speed == 0.0) return;
|
||||
if (iron.App.pauseUpdates) return;
|
||||
|
||||
var prevLap = lap;
|
||||
|
||||
// Copy owner world transform but discard scale
|
||||
owner.transform.world.decompose(ownerLoc, ownerRot, ownerScl);
|
||||
@ -115,17 +150,21 @@ class ParticleSystem {
|
||||
}
|
||||
|
||||
// Animate
|
||||
time += Time.delta * speed;
|
||||
time += Time.renderDelta * speed; // realDelta to renderDelta
|
||||
lap = Std.int(time / animtime);
|
||||
lapTime = time - lap * animtime;
|
||||
count = Std.int(lapTime / spawnRate);
|
||||
|
||||
if (lap > prevLap && !r.loop) {
|
||||
end();
|
||||
}
|
||||
|
||||
updateGpu(object, owner);
|
||||
}
|
||||
|
||||
public function getData(): Mat4 {
|
||||
var hair = r.type == 1;
|
||||
m._00 = r.loop ? animtime : -animtime;
|
||||
m._00 = animtime;
|
||||
m._01 = hair ? 1 / particles.length : spawnRate;
|
||||
m._02 = hair ? 1 : lifetime;
|
||||
m._03 = particles.length;
|
||||
@ -133,9 +172,9 @@ class ParticleSystem {
|
||||
m._11 = hair ? 0 : aligny;
|
||||
m._12 = hair ? 0 : alignz;
|
||||
m._13 = hair ? 0 : r.factor_random;
|
||||
m._20 = hair ? 0 : gx * r.mass;
|
||||
m._21 = hair ? 0 : gy * r.mass;
|
||||
m._22 = hair ? 0 : gz * r.mass;
|
||||
m._20 = hair ? 0 : gx;
|
||||
m._21 = hair ? 0 : gy;
|
||||
m._22 = hair ? 0 : gz;
|
||||
m._23 = hair ? 0 : r.lifetime_random;
|
||||
m._30 = tilesx;
|
||||
m._31 = tilesy;
|
||||
@ -144,13 +183,25 @@ class ParticleSystem {
|
||||
return m;
|
||||
}
|
||||
|
||||
public function getSizeRandom(): FastFloat {
|
||||
return r.size_random;
|
||||
}
|
||||
|
||||
public function getRandom(): FastFloat {
|
||||
return random;
|
||||
}
|
||||
|
||||
public function getSize(): FastFloat {
|
||||
return r.particle_size;
|
||||
}
|
||||
|
||||
function updateGpu(object: MeshObject, owner: MeshObject) {
|
||||
if (!object.data.geom.instanced) setupGeomGpu(object, owner);
|
||||
// GPU particles transform is attached to owner object
|
||||
}
|
||||
|
||||
function setupGeomGpu(object: MeshObject, owner: MeshObject) {
|
||||
var instancedData = new Float32Array(particles.length * 6);
|
||||
var instancedData = new Float32Array(particles.length * 3);
|
||||
var i = 0;
|
||||
|
||||
var normFactor = 1 / 32767; // pa.values are not normalized
|
||||
@ -169,10 +220,6 @@ class ParticleSystem {
|
||||
instancedData.set(i, pa.values[j * pa.size ] * normFactor * scaleFactor.x); i++;
|
||||
instancedData.set(i, pa.values[j * pa.size + 1] * normFactor * scaleFactor.y); i++;
|
||||
instancedData.set(i, pa.values[j * pa.size + 2] * normFactor * scaleFactor.z); i++;
|
||||
|
||||
instancedData.set(i, p.sr); i++;
|
||||
instancedData.set(i, p.sr); i++;
|
||||
instancedData.set(i, p.sr); i++;
|
||||
}
|
||||
|
||||
case 1: // Face
|
||||
@ -196,10 +243,6 @@ class ParticleSystem {
|
||||
instancedData.set(i, pos.x * normFactor * scaleFactor.x); i++;
|
||||
instancedData.set(i, pos.y * normFactor * scaleFactor.y); i++;
|
||||
instancedData.set(i, pos.z * normFactor * scaleFactor.z); i++;
|
||||
|
||||
instancedData.set(i, p.sr); i++;
|
||||
instancedData.set(i, p.sr); i++;
|
||||
instancedData.set(i, p.sr); i++;
|
||||
}
|
||||
|
||||
case 2: // Volume
|
||||
@ -210,13 +253,9 @@ class ParticleSystem {
|
||||
instancedData.set(i, (Math.random() * 2.0 - 1.0) * scaleFactorVolume.x); i++;
|
||||
instancedData.set(i, (Math.random() * 2.0 - 1.0) * scaleFactorVolume.y); i++;
|
||||
instancedData.set(i, (Math.random() * 2.0 - 1.0) * scaleFactorVolume.z); i++;
|
||||
|
||||
instancedData.set(i, p.sr); i++;
|
||||
instancedData.set(i, p.sr); i++;
|
||||
instancedData.set(i, p.sr); i++;
|
||||
}
|
||||
}
|
||||
object.data.geom.setupInstanced(instancedData, 3, Usage.StaticUsage);
|
||||
object.data.geom.setupInstanced(instancedData, 1, Usage.StaticUsage);
|
||||
}
|
||||
|
||||
function fhash(n: Int): Float {
|
||||
@ -255,10 +294,11 @@ class ParticleSystem {
|
||||
|
||||
class Particle {
|
||||
public var i: Int;
|
||||
public var px = 0.0;
|
||||
public var py = 0.0;
|
||||
public var pz = 0.0;
|
||||
public var sr = 1.0; // Size random
|
||||
|
||||
public var x = 0.0;
|
||||
public var y = 0.0;
|
||||
public var z = 0.0;
|
||||
|
||||
public var cameraDistance: Float;
|
||||
|
||||
public function new(i: Int) {
|
||||
|
@ -80,7 +80,7 @@ class Tilesheet {
|
||||
function update() {
|
||||
if (!ready || paused || action.start >= action.end) return;
|
||||
|
||||
time += Time.realDelta;
|
||||
time += Time.renderDelta;
|
||||
|
||||
var frameTime = 1 / raw.framerate;
|
||||
var framesToAdvance = 0;
|
||||
|
@ -1109,6 +1109,26 @@ class Uniforms {
|
||||
case "_texUnpack": {
|
||||
f = texUnpack != null ? texUnpack : 1.0;
|
||||
}
|
||||
#if lnx_particles
|
||||
case "_particleSizeRandom": {
|
||||
var mo = cast(object, MeshObject);
|
||||
if (mo.particleOwner != null && mo.particleOwner.particleSystems != null) {
|
||||
f = mo.particleOwner.particleSystems[mo.particleIndex].getSizeRandom();
|
||||
}
|
||||
}
|
||||
case "_particleRandom": {
|
||||
var mo = cast(object, MeshObject);
|
||||
if (mo.particleOwner != null && mo.particleOwner.particleSystems != null) {
|
||||
f = mo.particleOwner.particleSystems[mo.particleIndex].getRandom();
|
||||
}
|
||||
}
|
||||
case "_particleSize": {
|
||||
var mo = cast(object, MeshObject);
|
||||
if (mo.particleOwner != null && mo.particleOwner.particleSystems != null) {
|
||||
f = mo.particleOwner.particleSystems[mo.particleIndex].getSize();
|
||||
}
|
||||
}
|
||||
#end
|
||||
}
|
||||
|
||||
if (f == null && externalFloatLinks != null) {
|
||||
|
@ -1,37 +1,58 @@
|
||||
package iron.system;
|
||||
|
||||
class Time {
|
||||
|
||||
public static var scale = 1.0;
|
||||
|
||||
static var frequency: Null<Int> = null;
|
||||
static function initFrequency() {
|
||||
frequency = kha.Display.primary != null ? kha.Display.primary.frequency : 60;
|
||||
}
|
||||
|
||||
public static var step(get, never): Float;
|
||||
static function get_step(): Float {
|
||||
if (frequency == null) initFrequency();
|
||||
return 1 / frequency;
|
||||
}
|
||||
|
||||
public static var scale = 1.0;
|
||||
public static var delta(get, never): Float;
|
||||
static function get_delta(): Float {
|
||||
if (frequency == null) initFrequency();
|
||||
return (1 / frequency) * scale;
|
||||
|
||||
static var _fixedStep: Null<Float> = 1/60;
|
||||
public static var fixedStep(get, never): Float;
|
||||
static function get_fixedStep(): Float {
|
||||
return _fixedStep;
|
||||
}
|
||||
|
||||
public static function initFixedStep(value: Float = 1 / 60) {
|
||||
_fixedStep = value;
|
||||
}
|
||||
|
||||
static var last = 0.0;
|
||||
public static var realDelta = 0.0;
|
||||
static var lastTime = 0.0;
|
||||
static var _delta = 0.0;
|
||||
public static var delta(get, never): Float;
|
||||
static function get_delta(): Float {
|
||||
return _delta;
|
||||
}
|
||||
|
||||
static var lastRenderTime = 0.0;
|
||||
static var _renderDelta = 0.0;
|
||||
public static var renderDelta(get, never): Float;
|
||||
static function get_renderDelta(): Float {
|
||||
return _renderDelta;
|
||||
}
|
||||
|
||||
public static inline function time(): Float {
|
||||
return kha.Scheduler.time();
|
||||
}
|
||||
|
||||
public static inline function realTime(): Float {
|
||||
return kha.Scheduler.realTime();
|
||||
}
|
||||
|
||||
static var frequency: Null<Int> = null;
|
||||
|
||||
static function initFrequency() {
|
||||
frequency = kha.Display.primary != null ? kha.Display.primary.frequency : 60;
|
||||
public static function update() {
|
||||
_delta = realTime() - lastTime;
|
||||
lastTime = realTime();
|
||||
}
|
||||
|
||||
public static function update() {
|
||||
realDelta = realTime() - last;
|
||||
last = realTime();
|
||||
public static function render() {
|
||||
_renderDelta = realTime() - lastRenderTime;
|
||||
lastRenderTime = realTime();
|
||||
}
|
||||
}
|
||||
|
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
|
@ -20,6 +20,7 @@ class Config {
|
||||
var path = iron.data.Data.dataPath + "config.lnx";
|
||||
var bytes = haxe.io.Bytes.ofString(haxe.Json.stringify(raw));
|
||||
#if kha_krom
|
||||
if (iron.data.Data.dataPath == '') path = Krom.getFilesLocation() + "/config.lnx";
|
||||
Krom.fileSaveBytes(path, bytes.getData());
|
||||
#elseif kha_kore
|
||||
sys.io.File.saveBytes(path, bytes);
|
||||
@ -47,6 +48,7 @@ typedef TConfig = {
|
||||
@:optional var rp_ssr: Null<Bool>;
|
||||
@:optional var rp_ssrefr: Null<Bool>;
|
||||
@:optional var rp_bloom: Null<Bool>;
|
||||
@:optional var rp_chromatic_aberration: Null<Bool>;
|
||||
@:optional var rp_motionblur: Null<Bool>;
|
||||
@:optional var rp_gi: Null<Bool>; // voxelao
|
||||
@:optional var rp_dynres: Null<Bool>; // dynamic resolution scaling
|
||||
|
99
leenkx/Sources/leenkx/logicnode/AddParticleToObjectNode.hx
Normal file
99
leenkx/Sources/leenkx/logicnode/AddParticleToObjectNode.hx
Normal file
@ -0,0 +1,99 @@
|
||||
package leenkx.logicnode;
|
||||
|
||||
import iron.data.SceneFormat.TSceneFormat;
|
||||
import iron.data.Data;
|
||||
import iron.object.Object;
|
||||
|
||||
class AddParticleToObjectNode extends LogicNode {
|
||||
|
||||
public var property0: String;
|
||||
|
||||
public function new(tree: LogicTree) {
|
||||
super(tree);
|
||||
}
|
||||
|
||||
override function run(from: Int) {
|
||||
#if lnx_particles
|
||||
|
||||
if (property0 == 'Scene Active'){
|
||||
var objFrom: Object = inputs[1].get();
|
||||
var slot: Int = inputs[2].get();
|
||||
var objTo: Object = inputs[3].get();
|
||||
|
||||
if (objFrom == null || objTo == null) return;
|
||||
|
||||
var mobjFrom = cast(objFrom, iron.object.MeshObject);
|
||||
|
||||
var psys = mobjFrom.particleSystems != null ? mobjFrom.particleSystems[slot] :
|
||||
mobjFrom.particleOwner != null && mobjFrom.particleOwner.particleSystems != null ? mobjFrom.particleOwner.particleSystems[slot] : null;
|
||||
|
||||
if (psys == null) return;
|
||||
|
||||
var mobjTo = cast(objTo, iron.object.MeshObject);
|
||||
|
||||
mobjTo.setupParticleSystem(iron.Scene.active.raw.name, {name: 'LnxPS', seed: 0, particle: @:privateAccess psys.r.name});
|
||||
|
||||
mobjTo.render_emitter = inputs[4].get();
|
||||
|
||||
iron.Scene.active.spawnObject(psys.data.raw.instance_object, null, function(o: Object) {
|
||||
if (o != null) {
|
||||
var c: iron.object.MeshObject = cast o;
|
||||
if (mobjTo.particleChildren == null) mobjTo.particleChildren = [];
|
||||
mobjTo.particleChildren.push(c);
|
||||
c.particleOwner = mobjTo;
|
||||
c.particleIndex = mobjTo.particleChildren.length - 1;
|
||||
}
|
||||
});
|
||||
|
||||
var oslot: Int = mobjTo.particleSystems.length-1;
|
||||
var opsys = mobjTo.particleSystems[oslot];
|
||||
@:privateAccess opsys.setupGeomGpu(mobjTo.particleChildren[oslot], mobjTo);
|
||||
|
||||
} else {
|
||||
var sceneName: String = inputs[1].get();
|
||||
var objectName: String = inputs[2].get();
|
||||
var slot: Int = inputs[3].get();
|
||||
|
||||
var mobjTo: Object = inputs[4].get();
|
||||
var mobjTo = cast(mobjTo, iron.object.MeshObject);
|
||||
|
||||
#if lnx_json
|
||||
sceneName += ".json";
|
||||
#elseif lnx_compress
|
||||
sceneName += ".lz4";
|
||||
#end
|
||||
|
||||
Data.getSceneRaw(sceneName, (rawScene: TSceneFormat) -> {
|
||||
|
||||
for (obj in rawScene.objects) {
|
||||
if (obj.name == objectName) {
|
||||
mobjTo.setupParticleSystem(sceneName, obj.particle_refs[slot]);
|
||||
mobjTo.render_emitter = inputs[5].get();
|
||||
|
||||
iron.Scene.active.spawnObject(rawScene.particle_datas[slot].instance_object, null, function(o: Object) {
|
||||
if (o != null) {
|
||||
var c: iron.object.MeshObject = cast o;
|
||||
if (mobjTo.particleChildren == null) mobjTo.particleChildren = [];
|
||||
mobjTo.particleChildren.push(c);
|
||||
c.particleOwner = mobjTo;
|
||||
c.particleIndex = mobjTo.particleChildren.length - 1;
|
||||
}
|
||||
}, true, rawScene);
|
||||
|
||||
var oslot: Int = mobjTo.particleSystems.length-1;
|
||||
var opsys = mobjTo.particleSystems[oslot];
|
||||
@:privateAccess opsys.setupGeomGpu(mobjTo.particleChildren[oslot], mobjTo);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
#end
|
||||
|
||||
runOutput(0);
|
||||
}
|
||||
}
|
16
leenkx/Sources/leenkx/logicnode/AutoExposureGetNode.hx
Normal file
16
leenkx/Sources/leenkx/logicnode/AutoExposureGetNode.hx
Normal file
@ -0,0 +1,16 @@
|
||||
package leenkx.logicnode;
|
||||
|
||||
class AutoExposureGetNode extends LogicNode {
|
||||
|
||||
public function new(tree:LogicTree) {
|
||||
super(tree);
|
||||
}
|
||||
|
||||
override function get(from:Int):Dynamic {
|
||||
return switch (from) {
|
||||
case 0: leenkx.renderpath.Postprocess.auto_exposure_uniforms[0];
|
||||
case 1: leenkx.renderpath.Postprocess.auto_exposure_uniforms[1];
|
||||
default: 0.0;
|
||||
}
|
||||
}
|
||||
}
|
15
leenkx/Sources/leenkx/logicnode/AutoExposureSetNode.hx
Normal file
15
leenkx/Sources/leenkx/logicnode/AutoExposureSetNode.hx
Normal file
@ -0,0 +1,15 @@
|
||||
package leenkx.logicnode;
|
||||
|
||||
class AutoExposureSetNode extends LogicNode {
|
||||
|
||||
public function new(tree:LogicTree) {
|
||||
super(tree);
|
||||
}
|
||||
|
||||
override function run(from:Int) {
|
||||
leenkx.renderpath.Postprocess.auto_exposure_uniforms[0] = inputs[1].get();
|
||||
leenkx.renderpath.Postprocess.auto_exposure_uniforms[1] = inputs[2].get();
|
||||
|
||||
runOutput(0);
|
||||
}
|
||||
}
|
@ -1,26 +1,49 @@
|
||||
package leenkx.logicnode;
|
||||
|
||||
class CameraSetNode extends LogicNode {
|
||||
|
||||
|
||||
public var property0: String;
|
||||
|
||||
public function new(tree:LogicTree) {
|
||||
super(tree);
|
||||
}
|
||||
|
||||
override function run(from:Int) {
|
||||
leenkx.renderpath.Postprocess.camera_uniforms[0] = inputs[1].get();//Camera: F-Number
|
||||
leenkx.renderpath.Postprocess.camera_uniforms[1] = inputs[2].get();//Camera: Shutter time
|
||||
leenkx.renderpath.Postprocess.camera_uniforms[2] = inputs[3].get();//Camera: ISO
|
||||
leenkx.renderpath.Postprocess.camera_uniforms[3] = inputs[4].get();//Camera: Exposure Compensation
|
||||
leenkx.renderpath.Postprocess.camera_uniforms[4] = inputs[5].get();//Fisheye Distortion
|
||||
leenkx.renderpath.Postprocess.camera_uniforms[5] = inputs[6].get();//DoF AutoFocus §§ If true, it ignores the DoF Distance setting
|
||||
leenkx.renderpath.Postprocess.camera_uniforms[6] = inputs[7].get();//DoF Distance
|
||||
leenkx.renderpath.Postprocess.camera_uniforms[7] = inputs[8].get();//DoF Focal Length mm
|
||||
leenkx.renderpath.Postprocess.camera_uniforms[8] = inputs[9].get();//DoF F-Stop
|
||||
leenkx.renderpath.Postprocess.camera_uniforms[9] = inputs[10].get();//Tonemapping Method
|
||||
leenkx.renderpath.Postprocess.camera_uniforms[10] = inputs[11].get();//Distort
|
||||
leenkx.renderpath.Postprocess.camera_uniforms[11] = inputs[12].get();//Film Grain
|
||||
leenkx.renderpath.Postprocess.camera_uniforms[12] = inputs[13].get();//Sharpen
|
||||
leenkx.renderpath.Postprocess.camera_uniforms[13] = inputs[14].get();//Vignette
|
||||
|
||||
switch (property0) {
|
||||
case 'F-stop':
|
||||
leenkx.renderpath.Postprocess.camera_uniforms[0] = inputs[1].get();//Camera: F-Number
|
||||
case 'Shutter Time':
|
||||
leenkx.renderpath.Postprocess.camera_uniforms[1] = inputs[1].get();//Camera: Shutter time
|
||||
case 'ISO':
|
||||
leenkx.renderpath.Postprocess.camera_uniforms[2] = inputs[1].get();//Camera: ISO
|
||||
case 'Exposure Compensation':
|
||||
leenkx.renderpath.Postprocess.camera_uniforms[3] = inputs[1].get();//Camera: Exposure Compensation
|
||||
case 'Fisheye Distortion':
|
||||
leenkx.renderpath.Postprocess.camera_uniforms[4] = inputs[1].get();//Fisheye Distortion
|
||||
case 'Auto Focus':
|
||||
leenkx.renderpath.Postprocess.camera_uniforms[5] = inputs[1].get();//DoF AutoFocus §§ If true, it ignores the DoF Distance setting
|
||||
case 'DoF Distance':
|
||||
leenkx.renderpath.Postprocess.camera_uniforms[6] = inputs[1].get();//DoF Distance
|
||||
case 'DoF Length':
|
||||
leenkx.renderpath.Postprocess.camera_uniforms[7] = inputs[1].get();//DoF Focal Length mm
|
||||
case 'DoF F-Stop':
|
||||
leenkx.renderpath.Postprocess.camera_uniforms[8] = inputs[1].get();//DoF F-Stop
|
||||
case 'Tonemapping':
|
||||
leenkx.renderpath.Postprocess.camera_uniforms[9] = inputs[1].get();//Tonemapping Method
|
||||
case 'Distort':
|
||||
leenkx.renderpath.Postprocess.camera_uniforms[10] = inputs[1].get();//Distort
|
||||
case 'Film Grain':
|
||||
leenkx.renderpath.Postprocess.camera_uniforms[11] = inputs[1].get();//Film Grain
|
||||
case 'Sharpen':
|
||||
leenkx.renderpath.Postprocess.camera_uniforms[12] = inputs[1].get();//Sharpen
|
||||
case 'Vignette':
|
||||
leenkx.renderpath.Postprocess.camera_uniforms[13] = inputs[1].get();//Vignette
|
||||
case 'Exposure':
|
||||
leenkx.renderpath.Postprocess.exposure_uniforms[0] = inputs[1].get();//Exposure
|
||||
default:
|
||||
null;
|
||||
}
|
||||
|
||||
runOutput(0);
|
||||
}
|
||||
|
@ -10,6 +10,7 @@ class ChromaticAberrationGetNode extends LogicNode {
|
||||
return switch (from) {
|
||||
case 0: leenkx.renderpath.Postprocess.chromatic_aberration_uniforms[0];
|
||||
case 1: leenkx.renderpath.Postprocess.chromatic_aberration_uniforms[1];
|
||||
case 2: leenkx.renderpath.Postprocess.chromatic_aberration_uniforms[2];
|
||||
default: 0.0;
|
||||
}
|
||||
}
|
||||
|
@ -10,6 +10,7 @@ class ChromaticAberrationSetNode extends LogicNode {
|
||||
|
||||
leenkx.renderpath.Postprocess.chromatic_aberration_uniforms[0] = inputs[1].get();
|
||||
leenkx.renderpath.Postprocess.chromatic_aberration_uniforms[1] = inputs[2].get();
|
||||
leenkx.renderpath.Postprocess.chromatic_aberration_uniforms[2] = inputs[3].get();
|
||||
|
||||
runOutput(0);
|
||||
}
|
||||
|
@ -2,6 +2,9 @@ package leenkx.logicnode;
|
||||
|
||||
import iron.Scene;
|
||||
import iron.object.CameraObject;
|
||||
import iron.math.Vec4;
|
||||
import iron.math.Quat;
|
||||
import leenkx.math.Helper;
|
||||
|
||||
import leenkx.renderpath.RenderPathCreator;
|
||||
|
||||
@ -27,11 +30,19 @@ class DrawCameraTextureNode extends LogicNode {
|
||||
final c = inputs[2].get();
|
||||
assert(Error, Std.isOfType(c, CameraObject), "Camera must be a camera object!");
|
||||
cam = cast(c, CameraObject);
|
||||
rt = kha.Image.createRenderTarget(iron.App.w(), iron.App.h());
|
||||
rt = kha.Image.createRenderTarget(iron.App.w(), iron.App.h(),
|
||||
kha.graphics4.TextureFormat.RGBA32,
|
||||
kha.graphics4.DepthStencilFormat.NoDepthAndStencil);
|
||||
|
||||
assert(Error, mo.materials[matSlot].contexts[0].textures != null, 'Object "${mo.name}" has no diffuse texture to render to');
|
||||
mo.materials[matSlot].contexts[0].textures[0] = rt; // Override diffuse texture
|
||||
|
||||
final n = inputs[5].get();
|
||||
for (i => node in mo.materials[matSlot].contexts[0].raw.bind_textures){
|
||||
if (node.name == n){
|
||||
mo.materials[matSlot].contexts[0].textures[i] = rt; // Override diffuse texture
|
||||
break;
|
||||
}
|
||||
}
|
||||
tree.notifyOnRender(render);
|
||||
runOutput(0);
|
||||
|
||||
@ -48,8 +59,20 @@ class DrawCameraTextureNode extends LogicNode {
|
||||
iron.Scene.active.camera = cam;
|
||||
cam.renderTarget = rt;
|
||||
|
||||
#if kha_html5
|
||||
var q: Quat = new Quat();
|
||||
q.fromAxisAngle(new Vec4(0, 0, 1, 1), Helper.degToRad(180));
|
||||
cam.transform.rot.mult(q);
|
||||
cam.transform.buildMatrix();
|
||||
#end
|
||||
|
||||
cam.renderFrame(g);
|
||||
|
||||
#if kha_html5
|
||||
cam.transform.rot.mult(q);
|
||||
cam.transform.buildMatrix();
|
||||
#end
|
||||
|
||||
cam.renderTarget = oldRT;
|
||||
iron.Scene.active.camera = sceneCam;
|
||||
}
|
||||
|
@ -99,8 +99,6 @@ class DrawImageSequenceNode extends LogicNode {
|
||||
final colorVec = inputs[4].get();
|
||||
g.color = Color.fromFloats(colorVec.x, colorVec.y, colorVec.z, colorVec.w);
|
||||
|
||||
trace(currentImgIdx);
|
||||
|
||||
g.drawScaledImage(images[currentImgIdx], inputs[5].get(), inputs[6].get(), inputs[7].get(), inputs[8].get());
|
||||
}
|
||||
}
|
||||
|
59
leenkx/Sources/leenkx/logicnode/DrawSubImageNode.hx
Normal file
59
leenkx/Sources/leenkx/logicnode/DrawSubImageNode.hx
Normal file
@ -0,0 +1,59 @@
|
||||
package leenkx.logicnode;
|
||||
|
||||
import iron.math.Vec4;
|
||||
import kha.Image;
|
||||
import kha.Color;
|
||||
import leenkx.renderpath.RenderToTexture;
|
||||
|
||||
class DrawSubImageNode extends LogicNode {
|
||||
var img: Image;
|
||||
var lastImgName = "";
|
||||
|
||||
public function new(tree: LogicTree) {
|
||||
super(tree);
|
||||
}
|
||||
|
||||
override function run(from: Int) {
|
||||
RenderToTexture.ensure2DContext("DrawImageNode");
|
||||
|
||||
final imgName: String = inputs[1].get();
|
||||
final colorVec: Vec4 = inputs[2].get();
|
||||
final anchorH: Int = inputs[3].get();
|
||||
final anchorV: Int = inputs[4].get();
|
||||
final x: Float = inputs[5].get();
|
||||
final y: Float = inputs[6].get();
|
||||
final width: Float = inputs[7].get();
|
||||
final height: Float = inputs[8].get();
|
||||
final sx: Float = inputs[9].get();
|
||||
final sy: Float = inputs[10].get();
|
||||
final swidth: Float = inputs[11].get();
|
||||
final sheight: Float = inputs[12].get();
|
||||
final angle: Float = inputs[13].get();
|
||||
|
||||
final drawx = x - 0.5 * width * anchorH;
|
||||
final drawy = y - 0.5 * height * anchorV;
|
||||
final sdrawx = sx - 0.5 * swidth * anchorH;
|
||||
final sdrawy = sy - 0.5 * sheight * anchorV;
|
||||
|
||||
RenderToTexture.g.rotate(angle, x, y);
|
||||
|
||||
if (imgName != lastImgName) {
|
||||
// Load new image
|
||||
lastImgName = imgName;
|
||||
iron.data.Data.getImage(imgName, (image: Image) -> {
|
||||
img = image;
|
||||
});
|
||||
}
|
||||
|
||||
if (img == null) {
|
||||
runOutput(0);
|
||||
return;
|
||||
}
|
||||
|
||||
RenderToTexture.g.color = Color.fromFloats(colorVec.x, colorVec.y, colorVec.z, colorVec.w);
|
||||
RenderToTexture.g.drawScaledSubImage(img, sdrawx, sdrawy, swidth, sheight, drawx, drawy, width, height);
|
||||
RenderToTexture.g.rotate(-angle, x, y);
|
||||
|
||||
runOutput(0);
|
||||
}
|
||||
}
|
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;
|
||||
}
|
||||
}
|
@ -8,7 +8,7 @@ class GetFPSNode extends LogicNode {
|
||||
|
||||
override function get(from: Int): Dynamic {
|
||||
if (from == 0) {
|
||||
var fps = Math.round(1 / iron.system.Time.realDelta);
|
||||
var fps = Math.round(1 / iron.system.Time.renderDelta);
|
||||
if ((fps == Math.POSITIVE_INFINITY) || (fps == Math.NEGATIVE_INFINITY) || (Math.isNaN(fps))) {
|
||||
return 0;
|
||||
}
|
||||
|
72
leenkx/Sources/leenkx/logicnode/GetParticleDataNode.hx
Normal file
72
leenkx/Sources/leenkx/logicnode/GetParticleDataNode.hx
Normal file
@ -0,0 +1,72 @@
|
||||
package leenkx.logicnode;
|
||||
|
||||
import iron.object.Object;
|
||||
|
||||
class GetParticleDataNode extends LogicNode {
|
||||
|
||||
public function new(tree: LogicTree) {
|
||||
super(tree);
|
||||
}
|
||||
|
||||
override function get(from: Int): Dynamic {
|
||||
var object: Object = inputs[0].get();
|
||||
var slot: Int = inputs[1].get();
|
||||
|
||||
if (object == null) return null;
|
||||
|
||||
#if lnx_particles
|
||||
|
||||
var mo = cast(object, iron.object.MeshObject);
|
||||
|
||||
var psys = mo.particleSystems != null ? mo.particleSystems[slot] :
|
||||
mo.particleOwner != null && mo.particleOwner.particleSystems != null ? mo.particleOwner.particleSystems[slot] : null;
|
||||
|
||||
if (psys == null) return null;
|
||||
|
||||
return switch (from) {
|
||||
case 0:
|
||||
@:privateAccess psys.r.name;
|
||||
case 1:
|
||||
@:privateAccess psys.r.particle_size;
|
||||
case 2:
|
||||
@:privateAccess psys.r.frame_start;
|
||||
case 3:
|
||||
@:privateAccess psys.r.frame_end;
|
||||
case 4:
|
||||
@:privateAccess psys.lifetime;
|
||||
case 5:
|
||||
@:privateAccess psys.r.lifetime;
|
||||
case 6:
|
||||
@:privateAccess psys.r.emit_from;
|
||||
case 7:
|
||||
@:privateAccess psys.r.auto_start;
|
||||
case 8:
|
||||
@:privateAccess psys.r.is_unique;
|
||||
case 9:
|
||||
@:privateAccess psys.r.loop;
|
||||
case 10:
|
||||
new iron.math.Vec3(@:privateAccess psys.alignx, @:privateAccess psys.aligny, @:privateAccess psys.alignz);
|
||||
case 11:
|
||||
@:privateAccess psys.r.factor_random;
|
||||
case 12:
|
||||
new iron.math.Vec3(@:privateAccess psys.gx, @:privateAccess psys.gy, @:privateAccess psys.gz);
|
||||
case 13:
|
||||
@:privateAccess psys.r.weight_gravity;
|
||||
case 14:
|
||||
psys.speed;
|
||||
case 15:
|
||||
@:privateAccess psys.time;
|
||||
case 16:
|
||||
@:privateAccess psys.lap;
|
||||
case 17:
|
||||
@:privateAccess psys.lapTime;
|
||||
case 18:
|
||||
@:privateAccess psys.count;
|
||||
default:
|
||||
null;
|
||||
}
|
||||
#end
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
38
leenkx/Sources/leenkx/logicnode/GetParticleNode.hx
Normal file
38
leenkx/Sources/leenkx/logicnode/GetParticleNode.hx
Normal file
@ -0,0 +1,38 @@
|
||||
package leenkx.logicnode;
|
||||
|
||||
import iron.object.Object;
|
||||
|
||||
class GetParticleNode extends LogicNode {
|
||||
|
||||
public function new(tree: LogicTree) {
|
||||
super(tree);
|
||||
}
|
||||
|
||||
override function get(from: Int): Dynamic {
|
||||
var object: Object = inputs[0].get();
|
||||
|
||||
if (object == null) return null;
|
||||
|
||||
#if lnx_particles
|
||||
|
||||
var mo = cast(object, iron.object.MeshObject);
|
||||
|
||||
switch (from) {
|
||||
case 0:
|
||||
var names: Array<String> = [];
|
||||
if (mo.particleSystems != null)
|
||||
for (psys in mo.particleSystems)
|
||||
names.push(@:privateAccess psys.r.name);
|
||||
return names;
|
||||
case 1:
|
||||
return mo.particleSystems != null ? mo.particleSystems.length : 0;
|
||||
case 2:
|
||||
return mo.render_emitter;
|
||||
default:
|
||||
null;
|
||||
}
|
||||
#end
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
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;
|
||||
|
||||
import iron.object.Object;
|
||||
import iron.math.Vec4;
|
||||
|
||||
class GetWorldNode 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;
|
||||
}
|
||||
return iron.Scene.active.raw.world_ref;
|
||||
}
|
||||
}
|
||||
}
|
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;
|
||||
}
|
||||
}
|
||||
}
|
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);
|
||||
}
|
||||
}
|
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 {
|
||||
|
||||
var object: Object;
|
||||
var action: Dynamic;
|
||||
var init: Bool = false;
|
||||
|
||||
public function new(tree: LogicTree) {
|
||||
super(tree);
|
||||
}
|
||||
|
||||
override function run(from: Int) {
|
||||
var object: Object = inputs[1].get();
|
||||
var action: Dynamic = inputs[2].get();
|
||||
|
||||
object = inputs[1].get();
|
||||
action = inputs[2].get();
|
||||
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;
|
||||
if(animation == null) {
|
||||
#if lnx_skin
|
||||
animation = object.getBoneAnimation(object.uid);
|
||||
if (animation == null) {
|
||||
tree.notifyOnUpdate(playAnim);
|
||||
init = true;
|
||||
return;
|
||||
}
|
||||
cast(animation, BoneAnimation).setAnimationLoop(function f(mats) {
|
||||
action(mats);
|
||||
});
|
||||
@ -32,7 +51,6 @@ class PlayAnimationTreeNode extends LogicNode {
|
||||
action(mats);
|
||||
});
|
||||
}
|
||||
|
||||
runOutput(0);
|
||||
}
|
||||
}
|
||||
|
@ -18,7 +18,6 @@ class ProbabilisticOutputNode extends LogicNode {
|
||||
}
|
||||
|
||||
if (sum > 1){
|
||||
trace(sum);
|
||||
for (p in 0...probs.length)
|
||||
probs[p] /= sum;
|
||||
}
|
||||
|
@ -0,0 +1,64 @@
|
||||
package leenkx.logicnode;
|
||||
|
||||
import iron.object.Object;
|
||||
|
||||
class RemoveParticleFromObjectNode extends LogicNode {
|
||||
|
||||
public var property0: String;
|
||||
|
||||
public function new(tree: LogicTree) {
|
||||
super(tree);
|
||||
}
|
||||
|
||||
override function run(from: Int) {
|
||||
#if lnx_particles
|
||||
var object: Object = inputs[1].get();
|
||||
|
||||
if (object == null) return;
|
||||
|
||||
var mo = cast(object, iron.object.MeshObject);
|
||||
|
||||
if (mo.particleSystems == null) return;
|
||||
|
||||
if (property0 == 'All'){
|
||||
mo.particleSystems = null;
|
||||
for (c in mo.particleChildren) c.remove();
|
||||
mo.particleChildren = null;
|
||||
mo.particleOwner = null;
|
||||
mo.render_emitter = true;
|
||||
}
|
||||
else {
|
||||
|
||||
var slot: Int = -1;
|
||||
if (property0 == 'Name'){
|
||||
var name: String = inputs[2].get();
|
||||
for (i => psys in mo.particleSystems){
|
||||
if (@:privateAccess psys.r.name == name){ slot = i; break; }
|
||||
}
|
||||
}
|
||||
else slot = inputs[2].get();
|
||||
|
||||
if (mo.particleSystems.length > slot){
|
||||
for (i in slot+1...mo.particleSystems.length){
|
||||
var mi = cast(mo.particleChildren[i], iron.object.MeshObject);
|
||||
mi.particleIndex = mi.particleIndex - 1;
|
||||
}
|
||||
mo.particleSystems.splice(slot, 1);
|
||||
mo.particleChildren[slot].remove();
|
||||
mo.particleChildren.splice(slot, 1);
|
||||
}
|
||||
|
||||
if (slot == 0){
|
||||
mo.particleSystems = null;
|
||||
mo.particleChildren = null;
|
||||
mo.particleOwner = null;
|
||||
mo.render_emitter = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#end
|
||||
|
||||
runOutput(0);
|
||||
}
|
||||
}
|
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);
|
||||
}
|
||||
}
|
@ -20,6 +20,8 @@ class RpConfigNode extends LogicNode {
|
||||
on ? leenkx.data.Config.raw.rp_ssrefr = true : leenkx.data.Config.raw.rp_ssrefr = false;
|
||||
case "Bloom":
|
||||
on ? leenkx.data.Config.raw.rp_bloom = true : leenkx.data.Config.raw.rp_bloom = false;
|
||||
case "CA":
|
||||
on ? leenkx.data.Config.raw.rp_chromatic_aberration = true : leenkx.data.Config.raw.rp_chromatic_aberration = false;
|
||||
case "GI":
|
||||
on ? leenkx.data.Config.raw.rp_gi = true : leenkx.data.Config.raw.rp_gi = false;
|
||||
case "Motion Blur":
|
||||
|
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);
|
||||
}
|
||||
}
|
@ -0,0 +1,74 @@
|
||||
package leenkx.logicnode;
|
||||
|
||||
import iron.object.Object;
|
||||
import iron.math.Vec4;
|
||||
import iron.math.Mat4;
|
||||
import iron.system.Time;
|
||||
|
||||
class SetObjectDelayedLocationNode extends LogicNode {
|
||||
public var use_local_space: Bool = false;
|
||||
|
||||
private var initialOffset: Vec4 = null;
|
||||
private var targetPos: Vec4 = new Vec4();
|
||||
private var currentPos: Vec4 = new Vec4();
|
||||
private var deltaVec: Vec4 = new Vec4();
|
||||
private var tempVec: Vec4 = new Vec4();
|
||||
|
||||
private var lastParent: Object = null;
|
||||
private var invParentMatrix: Mat4 = null;
|
||||
|
||||
public function new(tree: LogicTree) {
|
||||
super(tree);
|
||||
}
|
||||
|
||||
override function run(from: Int) {
|
||||
var follower: Object = inputs[1].get();
|
||||
var target: Object = inputs[2].get();
|
||||
var delay: Float = inputs[3].get();
|
||||
|
||||
if (follower == null || target == null) return runOutput(0);
|
||||
|
||||
if (initialOffset == null) {
|
||||
initialOffset = new Vec4();
|
||||
var followerPos = follower.transform.world.getLoc();
|
||||
var targetPos = target.transform.world.getLoc();
|
||||
initialOffset.setFrom(followerPos);
|
||||
initialOffset.sub(targetPos);
|
||||
}
|
||||
|
||||
targetPos.setFrom(target.transform.world.getLoc());
|
||||
currentPos.setFrom(follower.transform.world.getLoc());
|
||||
|
||||
tempVec.setFrom(targetPos).add(initialOffset);
|
||||
|
||||
deltaVec.setFrom(tempVec).sub(currentPos);
|
||||
|
||||
if (deltaVec.length() < 0.001 && delay < 0.01) {
|
||||
runOutput(0);
|
||||
return;
|
||||
}
|
||||
|
||||
if (delay == 0.0) {
|
||||
currentPos.setFrom(tempVec);
|
||||
} else {
|
||||
var smoothFactor = Math.exp(-Time.delta / Math.max(0.0001, delay));
|
||||
currentPos.x = tempVec.x + (currentPos.x - tempVec.x) * smoothFactor;
|
||||
currentPos.y = tempVec.y + (currentPos.y - tempVec.y) * smoothFactor;
|
||||
currentPos.z = tempVec.z + (currentPos.z - tempVec.z) * smoothFactor;
|
||||
}
|
||||
if (use_local_space && follower.parent != null) {
|
||||
if (follower.parent != lastParent || invParentMatrix == null) {
|
||||
lastParent = follower.parent;
|
||||
invParentMatrix = Mat4.identity();
|
||||
invParentMatrix.getInverse(follower.parent.transform.world);
|
||||
}
|
||||
tempVec.setFrom(currentPos);
|
||||
tempVec.applymat(invParentMatrix);
|
||||
follower.transform.loc.set(tempVec.x, tempVec.y, tempVec.z);
|
||||
} else {
|
||||
follower.transform.loc.set(currentPos.x, currentPos.y, currentPos.z);
|
||||
}
|
||||
follower.transform.buildMatrix();
|
||||
runOutput(0);
|
||||
}
|
||||
}
|
81
leenkx/Sources/leenkx/logicnode/SetParticleDataNode.hx
Normal file
81
leenkx/Sources/leenkx/logicnode/SetParticleDataNode.hx
Normal file
@ -0,0 +1,81 @@
|
||||
package leenkx.logicnode;
|
||||
|
||||
import iron.object.Object;
|
||||
|
||||
class SetParticleDataNode extends LogicNode {
|
||||
|
||||
public var property0: String;
|
||||
|
||||
public function new(tree: LogicTree) {
|
||||
super(tree);
|
||||
}
|
||||
|
||||
override function run(from: Int) {
|
||||
#if lnx_particles
|
||||
var object: Object = inputs[1].get();
|
||||
var slot: Int = inputs[2].get();
|
||||
|
||||
if (object == null) return;
|
||||
|
||||
var mo = cast(object, iron.object.MeshObject);
|
||||
|
||||
var psys = mo.particleSystems != null ? mo.particleSystems[slot] :
|
||||
mo.particleOwner != null && mo.particleOwner.particleSystems != null ? mo.particleOwner.particleSystems[slot] : null; if (psys == null) return;
|
||||
|
||||
switch (property0) {
|
||||
case 'Particle Size':
|
||||
@:privateAccess psys.r.particle_size = inputs[3].get();
|
||||
case 'Frame Start':
|
||||
@:privateAccess psys.r.frame_start = inputs[3].get();
|
||||
@:privateAccess psys.animtime = (@:privateAccess psys.r.frame_end - @:privateAccess psys.r.frame_start) / @:privateAccess psys.frameRate;
|
||||
@:privateAccess psys.spawnRate = ((@:privateAccess psys.r.frame_end - @:privateAccess psys.r.frame_start) / @:privateAccess psys.count) / @:privateAccess psys.frameRate;
|
||||
case 'Frame End':
|
||||
@:privateAccess psys.r.frame_end = inputs[3].get();
|
||||
@:privateAccess psys.animtime = (@:privateAccess psys.r.frame_end - @:privateAccess psys.r.frame_start) / @:privateAccess psys.frameRate;
|
||||
@:privateAccess psys.spawnRate = ((@:privateAccess psys.r.frame_end - @:privateAccess psys.r.frame_start) / @:privateAccess psys.count) / @:privateAccess psys.frameRate;
|
||||
case 'Lifetime':
|
||||
@:privateAccess psys.lifetime = inputs[3].get() / @:privateAccess psys.frameRate;
|
||||
case 'Lifetime Random':
|
||||
@:privateAccess psys.r.lifetime_random = inputs[3].get();
|
||||
case 'Emit From':
|
||||
var emit_from: Int = inputs[3].get();
|
||||
if (emit_from == 0 || emit_from == 1 || emit_from == 2) {
|
||||
@:privateAccess psys.r.emit_from = emit_from;
|
||||
@:privateAccess psys.setupGeomGpu(mo.particleChildren != null ? mo.particleChildren[slot] : cast(iron.Scene.active.getChild(@:privateAccess psys.data.raw.instance_object), iron.object.MeshObject), mo);
|
||||
}
|
||||
case 'Auto Start':
|
||||
@:privateAccess psys.r.auto_start = inputs[3].get();
|
||||
case 'Is Unique':
|
||||
@:privateAccess psys.r.is_unique = inputs[3].get();
|
||||
case 'Loop':
|
||||
@:privateAccess psys.r.loop = inputs[3].get();
|
||||
case 'Velocity':
|
||||
var vel: iron.math.Vec3 = inputs[3].get();
|
||||
@:privateAccess psys.alignx = vel.x;
|
||||
@:privateAccess psys.aligny = vel.y;
|
||||
@:privateAccess psys.alignz = vel.z;
|
||||
case 'Velocity Random':
|
||||
@:privateAccess psys.r.factor_random = inputs[3].get();
|
||||
case 'Weight Gravity':
|
||||
@:privateAccess psys.r.weight_gravity = inputs[3].get();
|
||||
if (iron.Scene.active.raw.gravity != null) {
|
||||
@:privateAccess psys.gx = iron.Scene.active.raw.gravity[0] * @:privateAccess psys.r.weight_gravity;
|
||||
@:privateAccess psys.gy = iron.Scene.active.raw.gravity[1] * @:privateAccess psys.r.weight_gravity;
|
||||
@:privateAccess psys.gz = iron.Scene.active.raw.gravity[2] * @:privateAccess psys.r.weight_gravity;
|
||||
}
|
||||
else {
|
||||
@:privateAccess psys.gx = 0;
|
||||
@:privateAccess psys.gy = 0;
|
||||
@:privateAccess psys.gz = -9.81 * @:privateAccess psys.r.weight_gravity;
|
||||
}
|
||||
case 'Speed':
|
||||
psys.speed = inputs[3].get();
|
||||
default:
|
||||
null;
|
||||
}
|
||||
|
||||
#end
|
||||
|
||||
runOutput(0);
|
||||
}
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
package leenkx.logicnode;
|
||||
|
||||
import iron.object.Object;
|
||||
|
||||
class SetParticleRenderEmitterNode extends LogicNode {
|
||||
|
||||
public function new(tree: LogicTree) {
|
||||
super(tree);
|
||||
}
|
||||
|
||||
override function run(from: Int) {
|
||||
#if lnx_particles
|
||||
var object: Object = inputs[1].get();
|
||||
|
||||
if (object == null) return;
|
||||
|
||||
cast(object, iron.object.MeshObject).render_emitter = inputs[2].get();
|
||||
|
||||
#end
|
||||
|
||||
runOutput(0);
|
||||
}
|
||||
}
|
@ -11,13 +11,16 @@ class SetParticleSpeedNode extends LogicNode {
|
||||
override function run(from: Int) {
|
||||
#if lnx_particles
|
||||
var object: Object = inputs[1].get();
|
||||
var speed: Float = inputs[2].get();
|
||||
var slot: Int = inputs[2].get();
|
||||
var speed: Float = inputs[3].get();
|
||||
|
||||
if (object == null) return;
|
||||
|
||||
var mo = cast(object, iron.object.MeshObject);
|
||||
var psys = mo.particleSystems.length > 0 ? mo.particleSystems[0] : null;
|
||||
if (psys == null) mo.particleOwner.particleSystems[0];
|
||||
var psys = mo.particleSystems != null ? mo.particleSystems[slot] :
|
||||
mo.particleOwner != null && mo.particleOwner.particleSystems != null ? mo.particleOwner.particleSystems[slot] : null;
|
||||
|
||||
if (psys == null) return;
|
||||
|
||||
psys.speed = speed;
|
||||
|
||||
|
39
leenkx/Sources/leenkx/logicnode/SetPositionSpeakerNode.hx
Normal file
39
leenkx/Sources/leenkx/logicnode/SetPositionSpeakerNode.hx
Normal file
@ -0,0 +1,39 @@
|
||||
package leenkx.logicnode;
|
||||
|
||||
#if lnx_audio
|
||||
import iron.object.SpeakerObject;
|
||||
import kha.audio1.AudioChannel;
|
||||
import iron.system.Audio;
|
||||
#end
|
||||
|
||||
class SetPositionSpeakerNode extends LogicNode {
|
||||
|
||||
public function new(tree: LogicTree) {
|
||||
super(tree);
|
||||
}
|
||||
|
||||
override function run(from: Int) {
|
||||
#if lnx_audio
|
||||
var object: SpeakerObject = cast(inputs[1].get(), SpeakerObject);
|
||||
if (object == null || object.sound == null) return;
|
||||
|
||||
var positionInSeconds:Float = inputs[2].get();
|
||||
if (positionInSeconds < 0) positionInSeconds = 0;
|
||||
|
||||
var volume = object.data.volume;
|
||||
var loop = object.data.loop;
|
||||
var stream = object.data.stream;
|
||||
|
||||
object.stop();
|
||||
|
||||
var channel = Audio.play(object.sound, loop, stream);
|
||||
if (channel != null) {
|
||||
object.channels.push(channel);
|
||||
channel.volume = volume;
|
||||
@:privateAccess channel.set_position(positionInSeconds);
|
||||
}
|
||||
|
||||
#end
|
||||
runOutput(0);
|
||||
}
|
||||
}
|
40
leenkx/Sources/leenkx/logicnode/SetWorldNode.hx
Normal file
40
leenkx/Sources/leenkx/logicnode/SetWorldNode.hx
Normal file
@ -0,0 +1,40 @@
|
||||
package leenkx.logicnode;
|
||||
|
||||
class SetWorldNode extends LogicNode {
|
||||
|
||||
public function new(tree: LogicTree) {
|
||||
super(tree);
|
||||
}
|
||||
|
||||
override function run(from: Int) {
|
||||
var world: String = inputs[1].get();
|
||||
|
||||
if (world != null){
|
||||
|
||||
//check if world shader data exists
|
||||
var file: String = 'World_'+world+'_data';
|
||||
#if lnx_json
|
||||
file += ".json";
|
||||
#elseif lnx_compress
|
||||
file += ".lz4";
|
||||
#else
|
||||
file += '.lnx';
|
||||
#end
|
||||
|
||||
var exists: Bool = false;
|
||||
|
||||
iron.data.Data.getBlob(file, function(b: kha.Blob) {
|
||||
if (b != null) exists = true;
|
||||
});
|
||||
|
||||
assert(Error, exists == true, "World must be either associated to a scene or have fake user");
|
||||
|
||||
iron.Scene.active.raw.world_ref = world;
|
||||
var npath = leenkx.renderpath.RenderPathCreator.get();
|
||||
npath.loadShader("shader_datas/World_" + world + "/World_" + world);
|
||||
iron.RenderPath.setActive(npath);
|
||||
}
|
||||
|
||||
runOutput(0);
|
||||
}
|
||||
}
|
17
leenkx/Sources/leenkx/logicnode/SharpenGetNode.hx
Normal file
17
leenkx/Sources/leenkx/logicnode/SharpenGetNode.hx
Normal file
@ -0,0 +1,17 @@
|
||||
package leenkx.logicnode;
|
||||
|
||||
class SharpenGetNode extends LogicNode {
|
||||
|
||||
public function new(tree:LogicTree) {
|
||||
super(tree);
|
||||
}
|
||||
|
||||
override function get(from:Int):Dynamic {
|
||||
return switch (from) {
|
||||
case 0: leenkx.renderpath.Postprocess.sharpen_uniforms[0];
|
||||
case 1: leenkx.renderpath.Postprocess.sharpen_uniforms[1][0];
|
||||
case 2: leenkx.renderpath.Postprocess.camera_uniforms[12];
|
||||
default: 0.0;
|
||||
}
|
||||
}
|
||||
}
|
18
leenkx/Sources/leenkx/logicnode/SharpenSetNode.hx
Normal file
18
leenkx/Sources/leenkx/logicnode/SharpenSetNode.hx
Normal file
@ -0,0 +1,18 @@
|
||||
package leenkx.logicnode;
|
||||
|
||||
class SharpenSetNode extends LogicNode {
|
||||
|
||||
public function new(tree:LogicTree) {
|
||||
super(tree);
|
||||
}
|
||||
|
||||
override function run(from:Int) {
|
||||
leenkx.renderpath.Postprocess.sharpen_uniforms[0][0] = inputs[1].get().x;
|
||||
leenkx.renderpath.Postprocess.sharpen_uniforms[0][1] = inputs[1].get().y;
|
||||
leenkx.renderpath.Postprocess.sharpen_uniforms[0][2] = inputs[1].get().z;
|
||||
leenkx.renderpath.Postprocess.sharpen_uniforms[1][0] = inputs[2].get();
|
||||
leenkx.renderpath.Postprocess.camera_uniforms[12] = inputs[3].get();
|
||||
|
||||
runOutput(0);
|
||||
}
|
||||
}
|
17
leenkx/Sources/leenkx/logicnode/VolumetricFogGetNode.hx
Normal file
17
leenkx/Sources/leenkx/logicnode/VolumetricFogGetNode.hx
Normal file
@ -0,0 +1,17 @@
|
||||
package leenkx.logicnode;
|
||||
|
||||
class VolumetricFogGetNode extends LogicNode {
|
||||
|
||||
public function new(tree:LogicTree) {
|
||||
super(tree);
|
||||
}
|
||||
|
||||
override function get(from:Int):Dynamic {
|
||||
return switch (from) {
|
||||
case 0: leenkx.renderpath.Postprocess.volumetric_fog_uniforms[0];
|
||||
case 1: leenkx.renderpath.Postprocess.volumetric_fog_uniforms[1][0];
|
||||
case 2: leenkx.renderpath.Postprocess.volumetric_fog_uniforms[2][0];
|
||||
default: 0.0;
|
||||
}
|
||||
}
|
||||
}
|
18
leenkx/Sources/leenkx/logicnode/VolumetricFogSetNode.hx
Normal file
18
leenkx/Sources/leenkx/logicnode/VolumetricFogSetNode.hx
Normal file
@ -0,0 +1,18 @@
|
||||
package leenkx.logicnode;
|
||||
|
||||
class VolumetricFogSetNode extends LogicNode {
|
||||
|
||||
public function new(tree:LogicTree) {
|
||||
super(tree);
|
||||
}
|
||||
|
||||
override function run(from:Int) {
|
||||
leenkx.renderpath.Postprocess.volumetric_fog_uniforms[0][0] = inputs[1].get().x;
|
||||
leenkx.renderpath.Postprocess.volumetric_fog_uniforms[0][1] = inputs[1].get().y;
|
||||
leenkx.renderpath.Postprocess.volumetric_fog_uniforms[0][2] = inputs[1].get().z;
|
||||
leenkx.renderpath.Postprocess.volumetric_fog_uniforms[1][0] = inputs[2].get();
|
||||
leenkx.renderpath.Postprocess.volumetric_fog_uniforms[2][0] = inputs[3].get();
|
||||
|
||||
runOutput(0);
|
||||
}
|
||||
}
|
17
leenkx/Sources/leenkx/logicnode/VolumetricLightGetNode.hx
Normal file
17
leenkx/Sources/leenkx/logicnode/VolumetricLightGetNode.hx
Normal file
@ -0,0 +1,17 @@
|
||||
package leenkx.logicnode;
|
||||
|
||||
class VolumetricLightGetNode extends LogicNode {
|
||||
|
||||
public function new(tree:LogicTree) {
|
||||
super(tree);
|
||||
}
|
||||
|
||||
override function get(from:Int):Dynamic {
|
||||
return switch (from) {
|
||||
case 0: leenkx.renderpath.Postprocess.volumetric_light_uniforms[0];
|
||||
case 1: leenkx.renderpath.Postprocess.volumetric_light_uniforms[1][0];
|
||||
case 2: leenkx.renderpath.Postprocess.volumetric_light_uniforms[2][0];
|
||||
default: 0.0;
|
||||
}
|
||||
}
|
||||
}
|
18
leenkx/Sources/leenkx/logicnode/VolumetricLightSetNode.hx
Normal file
18
leenkx/Sources/leenkx/logicnode/VolumetricLightSetNode.hx
Normal file
@ -0,0 +1,18 @@
|
||||
package leenkx.logicnode;
|
||||
|
||||
class VolumetricLightSetNode extends LogicNode {
|
||||
|
||||
public function new(tree:LogicTree) {
|
||||
super(tree);
|
||||
}
|
||||
|
||||
override function run(from:Int) {
|
||||
leenkx.renderpath.Postprocess.volumetric_light_uniforms[0][0] = inputs[1].get().x;
|
||||
leenkx.renderpath.Postprocess.volumetric_light_uniforms[0][1] = inputs[1].get().y;
|
||||
leenkx.renderpath.Postprocess.volumetric_light_uniforms[0][2] = inputs[1].get().z;
|
||||
leenkx.renderpath.Postprocess.volumetric_light_uniforms[1][0] = inputs[2].get();
|
||||
leenkx.renderpath.Postprocess.volumetric_light_uniforms[2][0] = inputs[3].get();
|
||||
|
||||
runOutput(0);
|
||||
}
|
||||
}
|
105
leenkx/Sources/leenkx/logicnode/WriteImageNode.hx
Normal file
105
leenkx/Sources/leenkx/logicnode/WriteImageNode.hx
Normal file
@ -0,0 +1,105 @@
|
||||
package leenkx.logicnode;
|
||||
|
||||
import iron.object.CameraObject;
|
||||
|
||||
class WriteImageNode extends LogicNode {
|
||||
|
||||
var file: String;
|
||||
var camera: CameraObject;
|
||||
var renderTarget: kha.Image;
|
||||
|
||||
public function new(tree: LogicTree) {
|
||||
super(tree);
|
||||
}
|
||||
|
||||
override function run(from: Int) {
|
||||
// Relative or absolute path to file
|
||||
file = inputs[1].get();
|
||||
|
||||
assert(Error, iron.App.w() % inputs[3].get() == 0 && iron.App.h() % inputs[4].get() == 0, "Aspect ratio must match display resolution ratio");
|
||||
|
||||
camera = inputs[2].get();
|
||||
renderTarget = kha.Image.createRenderTarget(inputs[3].get(), inputs[4].get(),
|
||||
kha.graphics4.TextureFormat.RGBA32,
|
||||
kha.graphics4.DepthStencilFormat.NoDepthAndStencil);
|
||||
|
||||
tree.notifyOnRender(render);
|
||||
|
||||
}
|
||||
|
||||
function render(g: kha.graphics4.Graphics) {
|
||||
|
||||
var ready = false;
|
||||
final sceneCam = iron.Scene.active.camera;
|
||||
final oldRT = camera.renderTarget;
|
||||
|
||||
iron.Scene.active.camera = camera;
|
||||
camera.renderTarget = renderTarget;
|
||||
|
||||
camera.renderFrame(g);
|
||||
|
||||
var tex = camera.renderTarget;
|
||||
|
||||
camera.renderTarget = oldRT;
|
||||
iron.Scene.active.camera = sceneCam;
|
||||
|
||||
var pixels = tex.getPixels();
|
||||
|
||||
for (i in 0...pixels.length){
|
||||
if (pixels.get(i) != 0){ ready = true; break; }
|
||||
}
|
||||
|
||||
//wait for getPixels ready
|
||||
if (ready) {
|
||||
|
||||
var tx = inputs[5].get();
|
||||
var ty = inputs[6].get();
|
||||
var tw = inputs[7].get();
|
||||
var th = inputs[8].get();
|
||||
|
||||
var bo = new haxe.io.BytesOutput();
|
||||
var rgb = haxe.io.Bytes.alloc(tw * th * 4);
|
||||
for (j in ty...ty + th) {
|
||||
for (i in tx...tx + tw) {
|
||||
var k = j * tex.width + i;
|
||||
var m = (j - ty) * tw + i - tx;
|
||||
|
||||
#if kha_krom
|
||||
var l = k;
|
||||
#elseif kha_html5
|
||||
var l = (tex.height - j) * tex.width + i;
|
||||
#end
|
||||
|
||||
//ARGB 0xff
|
||||
rgb.set(m * 4 + 0, pixels.get(l * 4 + 3));
|
||||
rgb.set(m * 4 + 1, pixels.get(l * 4 + 0));
|
||||
rgb.set(m * 4 + 2, pixels.get(l * 4 + 1));
|
||||
rgb.set(m * 4 + 3, pixels.get(l * 4 + 2));
|
||||
}
|
||||
}
|
||||
|
||||
var imgwriter = new iron.format.bmp.Writer(bo);
|
||||
imgwriter.write(iron.format.bmp.Tools.buildFromARGB(tw, th, rgb));
|
||||
|
||||
#if kha_krom
|
||||
Krom.fileSaveBytes(Krom.getFilesLocation() + "/" + file, bo.getBytes().getData());
|
||||
|
||||
#elseif kha_html5
|
||||
var blob = new js.html.Blob([bo.getBytes().getData()], {type: "application"});
|
||||
var url = js.html.URL.createObjectURL(blob);
|
||||
var a = cast(js.Browser.document.createElement("a"), js.html.AnchorElement);
|
||||
a.href = url;
|
||||
a.download = file;
|
||||
a.click();
|
||||
js.html.URL.revokeObjectURL(url);
|
||||
#end
|
||||
|
||||
runOutput(0);
|
||||
|
||||
tree.removeRender(render);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -529,22 +529,29 @@ class Inc {
|
||||
public static function applyConfig() {
|
||||
#if lnx_config
|
||||
var config = leenkx.data.Config.raw;
|
||||
|
||||
#if rp_chromatic_aberration
|
||||
Postprocess.chromatic_aberration_uniforms[3] = config.rp_chromatic_aberration == true ? 1 : 0;
|
||||
#end
|
||||
|
||||
// Resize shadow map
|
||||
var l = path.light;
|
||||
if (l.data.raw.type == "sun" && l.data.raw.shadowmap_size != config.rp_shadowmap_cascade) {
|
||||
l.data.raw.shadowmap_size = config.rp_shadowmap_cascade;
|
||||
var rt = path.renderTargets.get("shadowMap");
|
||||
if (rt != null) {
|
||||
rt.unload();
|
||||
path.renderTargets.remove("shadowMap");
|
||||
if (l != null){
|
||||
if (l.data.raw.type == "sun" && l.data.raw.shadowmap_size != config.rp_shadowmap_cascade) {
|
||||
l.data.raw.shadowmap_size = config.rp_shadowmap_cascade;
|
||||
var rt = path.renderTargets.get("shadowMap");
|
||||
if (rt != null) {
|
||||
rt.unload();
|
||||
path.renderTargets.remove("shadowMap");
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (l.data.raw.shadowmap_size != config.rp_shadowmap_cube) {
|
||||
l.data.raw.shadowmap_size = config.rp_shadowmap_cube;
|
||||
var rt = path.renderTargets.get("shadowMapCube");
|
||||
if (rt != null) {
|
||||
rt.unload();
|
||||
path.renderTargets.remove("shadowMapCube");
|
||||
else if (l.data.raw.shadowmap_size != config.rp_shadowmap_cube) {
|
||||
l.data.raw.shadowmap_size = config.rp_shadowmap_cube;
|
||||
var rt = path.renderTargets.get("shadowMapCube");
|
||||
if (rt != null) {
|
||||
rt.unload();
|
||||
path.renderTargets.remove("shadowMapCube");
|
||||
}
|
||||
}
|
||||
}
|
||||
if (superSample != config.rp_supersample) {
|
||||
@ -778,7 +785,11 @@ class Inc {
|
||||
|
||||
public static inline function getDisplayp(): Null<Int> {
|
||||
#if rp_resolution_filter // Custom resolution set
|
||||
#if rp_pp
|
||||
return leenkx.renderpath.Postprocess.resolution_uniforms[0];
|
||||
#else
|
||||
return Main.resolutionSize;
|
||||
#end
|
||||
#else
|
||||
return null;
|
||||
#end
|
||||
|
@ -49,15 +49,20 @@ class Postprocess {
|
||||
0.01, //4: Fisheye Distortion
|
||||
1, //5: DoF AutoFocus §§ If true, it ignores the DoF Distance setting
|
||||
10.0, //6: DoF Distance
|
||||
160.0, //7: DoF Focal Length mm
|
||||
50.0, //7: DoF Focal Length mm
|
||||
128, //8: DoF F-Stop
|
||||
0, //9: Tonemapping Method
|
||||
2.0, //10: Distort
|
||||
2.0, //11: Film Grain
|
||||
0.25, //12: Sharpen
|
||||
0.25, //12: Sharpen Strength
|
||||
0.7 //13: Vignette
|
||||
];
|
||||
|
||||
public static var sharpen_uniforms = [
|
||||
[0.0, 0.0, 0.0], //0: Sharpen Color
|
||||
[2.5] //1: Sharpen Size
|
||||
];
|
||||
|
||||
public static var tonemapper_uniforms = [
|
||||
1.0, //0: Slope
|
||||
1.0, //1: Toe
|
||||
@ -102,7 +107,35 @@ class Postprocess {
|
||||
|
||||
public static var chromatic_aberration_uniforms = [
|
||||
2.0, //0: Strength
|
||||
32 //1: Samples
|
||||
32, //1: Samples
|
||||
0, //2: Type
|
||||
1 //3: On/Off
|
||||
];
|
||||
|
||||
public static var exposure_uniforms = [
|
||||
1 //0: Exposure
|
||||
];
|
||||
|
||||
public static var auto_exposure_uniforms = [
|
||||
1, //0: Auto Exposure Strength
|
||||
1 //1: Auto Exposure Speed
|
||||
];
|
||||
|
||||
public static var volumetric_light_uniforms = [
|
||||
[1.0, 1.0, 1.0], //0: Volumetric Light Air Color
|
||||
[1.0], //1: Volumetric Light Air Turbidity
|
||||
[20.0] //2: Volumetric Light Steps
|
||||
];
|
||||
|
||||
public static var volumetric_fog_uniforms = [
|
||||
[0.5, 0.6, 0.7], //0: Volumetric Fog Color
|
||||
[0.25], //1: Volumetric Fog Amount A
|
||||
[50.0] //2: Volumetric Fog Amount B
|
||||
];
|
||||
|
||||
public static var resolution_uniforms = [
|
||||
720, //0: Size
|
||||
0 //1: Filter
|
||||
];
|
||||
|
||||
public static function vec3Link(object: Object, mat: MaterialData, link: String): iron.math.Vec4 {
|
||||
@ -284,6 +317,11 @@ class Postprocess {
|
||||
v.x = lenstexture_uniforms[2]; //Lum min
|
||||
v.y = lenstexture_uniforms[3]; //Lum max
|
||||
v.z = lenstexture_uniforms[4]; //Expo
|
||||
case "_PPComp8":
|
||||
v = iron.object.Uniforms.helpVec;
|
||||
v.x = exposure_uniforms[0]; //Exposure
|
||||
v.y = auto_exposure_uniforms[0]; //Auto Exposure Strength
|
||||
v.z = auto_exposure_uniforms[1]; //Auto Exposure Speed
|
||||
case "_PPComp9":
|
||||
v = iron.object.Uniforms.helpVec;
|
||||
v.x = ssr_uniforms[0]; //Step
|
||||
@ -297,8 +335,8 @@ class Postprocess {
|
||||
case "_PPComp11":
|
||||
v = iron.object.Uniforms.helpVec;
|
||||
v.x = bloom_uniforms[2]; // Bloom Strength
|
||||
v.y = 0; // Unused
|
||||
v.z = 0; // Unused
|
||||
v.y = volumetric_light_uniforms[2][0]; //Volumetric Light Steps
|
||||
v.z = volumetric_fog_uniforms[2][0]; //Volumetric Fog Amount B
|
||||
case "_PPComp12":
|
||||
v = iron.object.Uniforms.helpVec;
|
||||
v.x = ssao_uniforms[0]; //SSAO Strength
|
||||
@ -308,7 +346,8 @@ class Postprocess {
|
||||
v = iron.object.Uniforms.helpVec;
|
||||
v.x = chromatic_aberration_uniforms[0]; //CA Strength
|
||||
v.y = chromatic_aberration_uniforms[1]; //CA Samples
|
||||
v.z = 0;
|
||||
v.z = chromatic_aberration_uniforms[2]; //CA Type
|
||||
v.w = chromatic_aberration_uniforms[3]; //On/Off
|
||||
case "_PPComp14":
|
||||
v = iron.object.Uniforms.helpVec;
|
||||
v.x = camera_uniforms[10]; //Distort
|
||||
@ -338,6 +377,24 @@ class Postprocess {
|
||||
v.y = letterbox_uniforms[0][1];
|
||||
v.z = letterbox_uniforms[0][2];
|
||||
v.w = letterbox_uniforms[1][0]; //Size
|
||||
case "_PPComp16":
|
||||
v = iron.object.Uniforms.helpVec;
|
||||
v.x = sharpen_uniforms[0][0]; //Color
|
||||
v.y = sharpen_uniforms[0][1];
|
||||
v.z = sharpen_uniforms[0][2];
|
||||
v.w = sharpen_uniforms[1][0]; //Size
|
||||
case "_PPComp17":
|
||||
v = iron.object.Uniforms.helpVec;
|
||||
v.x = volumetric_light_uniforms[0][0]; //Air Color
|
||||
v.y = volumetric_light_uniforms[0][1];
|
||||
v.z = volumetric_light_uniforms[0][2];
|
||||
v.w = volumetric_light_uniforms[1][0]; //Air Turbidity
|
||||
case "_PPComp18":
|
||||
v = iron.object.Uniforms.helpVec;
|
||||
v.x = volumetric_fog_uniforms[0][0]; //Color
|
||||
v.y = volumetric_fog_uniforms[0][1];
|
||||
v.z = volumetric_fog_uniforms[0][2];
|
||||
v.w = volumetric_fog_uniforms[1][0]; //Amount A
|
||||
}
|
||||
|
||||
return v;
|
||||
|
@ -73,7 +73,17 @@ class PhysicsBreak extends Trait {
|
||||
collisionMargin: 0.04,
|
||||
linearDeactivationThreshold: 0.0,
|
||||
angularDeactivationThrshold: 0.0,
|
||||
deactivationTime: 0.0
|
||||
deactivationTime: 0.0,
|
||||
linearVelocityMin: 0.0,
|
||||
linearVelocityMax: 0.0,
|
||||
angularVelocityMin: 0.0,
|
||||
angularVelocityMax: 0.0,
|
||||
lockTranslationX: false,
|
||||
lockTranslationY: false,
|
||||
lockTranslationZ: false,
|
||||
lockRotationX: false,
|
||||
lockRotationY: false,
|
||||
lockRotationZ: false
|
||||
};
|
||||
o.addTrait(new RigidBody(Shape.ConvexHull, ud.mass, ud.friction, 0, 1, params));
|
||||
if (cast(o, MeshObject).data.geom.positions.values.length < 600) {
|
||||
|
@ -280,7 +280,11 @@ class DebugConsole extends Trait {
|
||||
|
||||
function drawObjectNameInList(object: iron.object.Object, selected: Bool) {
|
||||
var _y = ui._y;
|
||||
ui.text(object.uid+'_'+object.name);
|
||||
|
||||
if (object.parent.name == 'Root')
|
||||
ui.text(object.uid+'_'+object.name+' ('+iron.Scene.active.raw.world_ref+')');
|
||||
else
|
||||
ui.text(object.uid+'_'+object.name);
|
||||
|
||||
if (object == iron.Scene.active.camera) {
|
||||
var tagWidth = 100;
|
||||
|
@ -45,7 +45,7 @@ class DebugDrawHelper {
|
||||
|
||||
iron.App.notifyOnRender2D(onRender);
|
||||
if (debugDrawMode & DrawRayCast != 0) {
|
||||
iron.App.notifyOnUpdate(function () {
|
||||
iron.App.notifyOnFixedUpdate(function () {
|
||||
rayCasts.resize(0);
|
||||
});
|
||||
}
|
||||
|
@ -101,7 +101,7 @@ class PhysicsWorld extends Trait {
|
||||
|
||||
|
||||
|
||||
public function new(timeScale = 1.0, maxSteps = 10, solverIterations = 10, debugDrawMode: DebugDrawMode = NoDebug) {
|
||||
public function new(timeScale = 1.0, maxSteps = 10, solverIterations = 10, fixedStep = 1 / 60, debugDrawMode: DebugDrawMode = NoDebug) {
|
||||
super();
|
||||
|
||||
if (nullvec) {
|
||||
@ -120,6 +120,7 @@ class PhysicsWorld extends Trait {
|
||||
this.timeScale = timeScale;
|
||||
this.maxSteps = maxSteps;
|
||||
this.solverIterations = solverIterations;
|
||||
Time.initFixedStep(fixedStep);
|
||||
|
||||
// First scene
|
||||
if (active == null) {
|
||||
@ -136,10 +137,10 @@ class PhysicsWorld extends Trait {
|
||||
conMap = new Map();
|
||||
active = this;
|
||||
|
||||
// Ensure physics are updated first in the lateUpdate list
|
||||
_lateUpdate = [lateUpdate];
|
||||
@:privateAccess iron.App.traitLateUpdates.insert(0, lateUpdate);
|
||||
|
||||
// Ensure physics are updated first in the fixedUpdate list
|
||||
_fixedUpdate = [fixedUpdate];
|
||||
@:privateAccess iron.App.traitFixedUpdates.insert(0, fixedUpdate);
|
||||
|
||||
setDebugDrawMode(debugDrawMode);
|
||||
|
||||
iron.Scene.active.notifyOnRemove(function() {
|
||||
@ -298,8 +299,8 @@ class PhysicsWorld extends Trait {
|
||||
return rb;
|
||||
}
|
||||
|
||||
function lateUpdate() {
|
||||
var t = Time.delta * timeScale;
|
||||
function fixedUpdate() {
|
||||
var t = Time.fixedStep * timeScale * Time.scale;
|
||||
if (t == 0.0) return; // Simulation paused
|
||||
|
||||
#if lnx_debug
|
||||
@ -308,13 +309,10 @@ class PhysicsWorld extends Trait {
|
||||
|
||||
if (preUpdates != null) for (f in preUpdates) f();
|
||||
|
||||
//Bullet physics fixed timescale
|
||||
var fixedTime = 1.0 / 60;
|
||||
|
||||
//This condition must be satisfied to not loose time
|
||||
var currMaxSteps = t < (fixedTime * maxSteps) ? maxSteps : 1;
|
||||
var currMaxSteps = t < (Time.fixedStep * maxSteps) ? maxSteps : 1;
|
||||
|
||||
world.stepSimulation(t, currMaxSteps, fixedTime);
|
||||
world.stepSimulation(t, currMaxSteps, Time.fixedStep);
|
||||
updateContacts();
|
||||
|
||||
for (rb in rbMap) @:privateAccess rb.physicsUpdate();
|
||||
@ -436,8 +434,8 @@ class PhysicsWorld extends Trait {
|
||||
from: from,
|
||||
to: to,
|
||||
hasHit: rc.hasHit(),
|
||||
hitPoint: hitPointWorld,
|
||||
hitNormal: hitNormalWorld
|
||||
hitPoint: hitPointWorld.clone(),
|
||||
hitNormal: hitNormalWorld.clone()
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -2,11 +2,13 @@ package leenkx.trait.physics.bullet;
|
||||
|
||||
#if lnx_bullet
|
||||
|
||||
import leenkx.math.Helper;
|
||||
import iron.data.MeshData;
|
||||
import iron.math.Vec4;
|
||||
import iron.math.Quat;
|
||||
import iron.object.Transform;
|
||||
import iron.object.MeshObject;
|
||||
import iron.data.MeshData;
|
||||
import iron.system.Time;
|
||||
|
||||
/**
|
||||
RigidBody is used to allow objects to interact with Physics in your game including collisions and gravity.
|
||||
@ -34,6 +36,18 @@ class RigidBody extends iron.Trait {
|
||||
var useDeactivation: Bool;
|
||||
var deactivationParams: Array<Float>;
|
||||
var ccd = false; // Continuous collision detection
|
||||
// New velocity limiting properties
|
||||
var linearVelocityMin: Float;
|
||||
var linearVelocityMax: Float;
|
||||
var angularVelocityMin: Float;
|
||||
var angularVelocityMax: Float;
|
||||
// New lock properties
|
||||
var lockTranslationX: Bool;
|
||||
var lockTranslationY: Bool;
|
||||
var lockTranslationZ: Bool;
|
||||
var lockRotationX: Bool;
|
||||
var lockRotationY: Bool;
|
||||
var lockRotationZ: Bool;
|
||||
public var group = 1;
|
||||
public var mask = 1;
|
||||
var trigger = false;
|
||||
@ -76,6 +90,14 @@ class RigidBody extends iron.Trait {
|
||||
static var triangleMeshCache = new Map<MeshData, bullet.Bt.TriangleMesh>();
|
||||
static var usersCache = new Map<MeshData, Int>();
|
||||
|
||||
// Interpolation
|
||||
var interpolate: Bool = false;
|
||||
var time: Float = 0.0;
|
||||
var currentPos: bullet.Bt.Vector3 = new bullet.Bt.Vector3(0, 0, 0);
|
||||
var prevPos: bullet.Bt.Vector3 = new bullet.Bt.Vector3(0, 0, 0);
|
||||
var currentRot: bullet.Bt.Quaternion = new bullet.Bt.Quaternion(0, 0, 0, 1);
|
||||
var prevRot: bullet.Bt.Quaternion = new bullet.Bt.Quaternion(0, 0, 0, 1);
|
||||
|
||||
public function new(shape = Shape.Box, mass = 1.0, friction = 0.5, restitution = 0.0, group = 1, mask = 1,
|
||||
params: RigidBodyParams = null, flags: RigidBodyFlags = null) {
|
||||
super();
|
||||
@ -85,7 +107,7 @@ class RigidBody extends iron.Trait {
|
||||
vec1 = new bullet.Bt.Vector3(0, 0, 0);
|
||||
vec2 = new bullet.Bt.Vector3(0, 0, 0);
|
||||
vec3 = new bullet.Bt.Vector3(0, 0, 0);
|
||||
quat1 = new bullet.Bt.Quaternion(0, 0, 0, 0);
|
||||
quat1 = new bullet.Bt.Quaternion(0, 0, 0, 1);
|
||||
trans1 = new bullet.Bt.Transform();
|
||||
trans2 = new bullet.Bt.Transform();
|
||||
}
|
||||
@ -110,13 +132,24 @@ class RigidBody extends iron.Trait {
|
||||
collisionMargin: 0.0,
|
||||
linearDeactivationThreshold: 0.0,
|
||||
angularDeactivationThrshold: 0.0,
|
||||
deactivationTime: 0.0
|
||||
deactivationTime: 0.0,
|
||||
linearVelocityMin: 0.0,
|
||||
linearVelocityMax: 0.0,
|
||||
angularVelocityMin: 0.0,
|
||||
angularVelocityMax: 0.0,
|
||||
lockTranslationX: false,
|
||||
lockTranslationY: false,
|
||||
lockTranslationZ: false,
|
||||
lockRotationX: false,
|
||||
lockRotationY: false,
|
||||
lockRotationZ: false
|
||||
};
|
||||
|
||||
if (flags == null) flags = {
|
||||
animated: false,
|
||||
trigger: false,
|
||||
ccd: false,
|
||||
interpolate: false,
|
||||
staticObj: false,
|
||||
useDeactivation: true
|
||||
};
|
||||
@ -128,9 +161,22 @@ class RigidBody extends iron.Trait {
|
||||
this.angularFactors = [params.angularFactorsX, params.angularFactorsY, params.angularFactorsZ];
|
||||
this.collisionMargin = params.collisionMargin;
|
||||
this.deactivationParams = [params.linearDeactivationThreshold, params.angularDeactivationThrshold, params.deactivationTime];
|
||||
// New velocity limiting properties
|
||||
this.linearVelocityMin = params.linearVelocityMin;
|
||||
this.linearVelocityMax = params.linearVelocityMax;
|
||||
this.angularVelocityMin = params.angularVelocityMin;
|
||||
this.angularVelocityMax = params.angularVelocityMax;
|
||||
// New lock properties
|
||||
this.lockTranslationX = params.lockTranslationX;
|
||||
this.lockTranslationY = params.lockTranslationY;
|
||||
this.lockTranslationZ = params.lockTranslationZ;
|
||||
this.lockRotationX = params.lockRotationX;
|
||||
this.lockRotationY = params.lockRotationY;
|
||||
this.lockRotationZ = params.lockRotationZ;
|
||||
this.animated = flags.animated;
|
||||
this.trigger = flags.trigger;
|
||||
this.ccd = flags.ccd;
|
||||
this.interpolate = flags.interpolate;
|
||||
this.staticObj = flags.staticObj;
|
||||
this.useDeactivation = flags.useDeactivation;
|
||||
|
||||
@ -153,6 +199,7 @@ class RigidBody extends iron.Trait {
|
||||
if (!Std.isOfType(object, MeshObject)) return; // No mesh data
|
||||
|
||||
transform = object.transform;
|
||||
transform.buildMatrix();
|
||||
physics = leenkx.trait.physics.PhysicsWorld.active;
|
||||
|
||||
if (shape == Shape.Box) {
|
||||
@ -244,6 +291,9 @@ class RigidBody extends iron.Trait {
|
||||
quat1.setValue(quat.x, quat.y, quat.z, quat.w);
|
||||
trans1.setRotation(quat1);
|
||||
|
||||
currentPos.setValue(vec1.x(), vec1.y(), vec1.z());
|
||||
currentRot.setValue(quat.x, quat.y, quat.z, quat.w);
|
||||
|
||||
var centerOfMassOffset = trans2;
|
||||
centerOfMassOffset.setIdentity();
|
||||
motionState = new bullet.Bt.DefaultMotionState(trans1, centerOfMassOffset);
|
||||
@ -275,11 +325,25 @@ class RigidBody extends iron.Trait {
|
||||
}
|
||||
|
||||
if (linearFactors != null) {
|
||||
setLinearFactor(linearFactors[0], linearFactors[1], linearFactors[2]);
|
||||
// Apply lock properties by overriding factors
|
||||
var lx = linearFactors[0];
|
||||
var ly = linearFactors[1];
|
||||
var lz = linearFactors[2];
|
||||
if (lockTranslationX) lx = 0.0;
|
||||
if (lockTranslationY) ly = 0.0;
|
||||
if (lockTranslationZ) lz = 0.0;
|
||||
setLinearFactor(lx, ly, lz);
|
||||
}
|
||||
|
||||
if (angularFactors != null) {
|
||||
setAngularFactor(angularFactors[0], angularFactors[1], angularFactors[2]);
|
||||
// Apply lock properties by overriding factors
|
||||
var ax = angularFactors[0];
|
||||
var ay = angularFactors[1];
|
||||
var az = angularFactors[2];
|
||||
if (lockRotationX) ax = 0.0;
|
||||
if (lockRotationY) ay = 0.0;
|
||||
if (lockRotationZ) az = 0.0;
|
||||
setAngularFactor(ax, ay, az);
|
||||
}
|
||||
|
||||
if (trigger) bodyColl.setCollisionFlags(bodyColl.getCollisionFlags() | CF_NO_CONTACT_RESPONSE);
|
||||
@ -307,7 +371,8 @@ class RigidBody extends iron.Trait {
|
||||
|
||||
physics.addRigidBody(this);
|
||||
notifyOnRemove(removeFromWorld);
|
||||
|
||||
if (!animated) notifyOnUpdate(update);
|
||||
|
||||
if (onReady != null) onReady();
|
||||
|
||||
#if js
|
||||
@ -317,26 +382,71 @@ class RigidBody extends iron.Trait {
|
||||
#end
|
||||
}
|
||||
|
||||
|
||||
function update() {
|
||||
if (interpolate) {
|
||||
time += Time.delta;
|
||||
|
||||
while (time >= Time.fixedStep) {
|
||||
time -= Time.fixedStep;
|
||||
}
|
||||
|
||||
var t: Float = time / Time.fixedStep;
|
||||
t = Helper.clamp(t, 0, 1);
|
||||
|
||||
var tx: Float = prevPos.x() * (1.0 - t) + currentPos.x() * t;
|
||||
var ty: Float = prevPos.y() * (1.0 - t) + currentPos.y() * t;
|
||||
var tz: Float = prevPos.z() * (1.0 - t) + currentPos.z() * t;
|
||||
|
||||
var tRot: bullet.Bt.Quaternion = nlerp(prevRot, currentRot, t);
|
||||
|
||||
transform.loc.set(tx, ty, tz, 1.0);
|
||||
transform.rot.set(tRot.x(), tRot.y(), tRot.z(), tRot.w());
|
||||
} else {
|
||||
transform.loc.set(currentPos.x(), currentPos.y(), currentPos.z(), 1.0);
|
||||
transform.rot.set(currentRot.x(), currentRot.y(), currentRot.z(), currentRot.w());
|
||||
}
|
||||
|
||||
if (object.parent != null) {
|
||||
var ptransform = object.parent.transform;
|
||||
transform.loc.x -= ptransform.worldx();
|
||||
transform.loc.y -= ptransform.worldy();
|
||||
transform.loc.z -= ptransform.worldz();
|
||||
}
|
||||
|
||||
transform.buildMatrix();
|
||||
}
|
||||
|
||||
function nlerp(q1: bullet.Bt.Quaternion, q2: bullet.Bt.Quaternion, t: Float): bullet.Bt.Quaternion {
|
||||
var dot = q1.x() * q2.x() + q1.y() * q2.y() + q1.z() * q2.z() + q1.w() * q2.w();
|
||||
var _q2 = dot < 0 ? new bullet.Bt.Quaternion(-q2.x(), -q2.y(), -q2.z(), -q2.w()) : q2;
|
||||
|
||||
var x = q1.x() * (1.0 - t) + _q2.x() * t;
|
||||
var y = q1.y() * (1.0 - t) + _q2.y() * t;
|
||||
var z = q1.z() * (1.0 - t) + _q2.z() * t;
|
||||
var w = q1.w() * (1.0 - t) + _q2.w() * t;
|
||||
|
||||
var len = Math.sqrt(x * x + y * y + z * z + w * w);
|
||||
return new bullet.Bt.Quaternion(x / len, y / len, z / len, w / len);
|
||||
}
|
||||
|
||||
function physicsUpdate() {
|
||||
if (!ready) return;
|
||||
if (animated) {
|
||||
syncTransform();
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
if (interpolate) {
|
||||
prevPos.setValue(currentPos.x(), currentPos.y(), currentPos.z());
|
||||
prevRot.setValue(currentRot.x(), currentRot.y(), currentRot.z(), currentRot.w());
|
||||
}
|
||||
var trans = body.getWorldTransform();
|
||||
var p = trans.getOrigin();
|
||||
var q = trans.getRotation();
|
||||
|
||||
transform.loc.set(p.x(), p.y(), p.z());
|
||||
transform.rot.set(q.x(), q.y(), q.z(), q.w());
|
||||
if (object.parent != null) {
|
||||
var ptransform = object.parent.transform;
|
||||
transform.loc.x -= ptransform.worldx();
|
||||
transform.loc.y -= ptransform.worldy();
|
||||
transform.loc.z -= ptransform.worldz();
|
||||
}
|
||||
transform.clearDelta();
|
||||
transform.buildMatrix();
|
||||
// transform.buildMatrix();
|
||||
currentPos.setValue(p.x(), p.y(), p.z());
|
||||
currentRot.setValue(q.x(), q.y(), q.z(), q.w());
|
||||
|
||||
|
||||
#if hl
|
||||
p.delete();
|
||||
@ -349,6 +459,55 @@ class RigidBody extends iron.Trait {
|
||||
var rbs = physics.getContacts(this);
|
||||
if (rbs != null) for (rb in rbs) for (f in onContact) f(rb);
|
||||
}
|
||||
|
||||
// Apply velocity limiting if enabled
|
||||
if (!animated && !staticObj) {
|
||||
applyVelocityLimits();
|
||||
}
|
||||
}
|
||||
|
||||
function applyVelocityLimits() {
|
||||
if (!ready) return;
|
||||
|
||||
// Check linear velocity limits
|
||||
if (linearVelocityMin > 0.0 || linearVelocityMax > 0.0) {
|
||||
var velocity = getLinearVelocity();
|
||||
var speed = velocity.length();
|
||||
|
||||
if (linearVelocityMin > 0.0 && speed < linearVelocityMin) {
|
||||
// Increase velocity to minimum
|
||||
if (speed > 0.0) {
|
||||
velocity.normalize();
|
||||
velocity.mult(linearVelocityMin);
|
||||
setLinearVelocity(velocity.x, velocity.y, velocity.z);
|
||||
}
|
||||
} else if (linearVelocityMax > 0.0 && speed > linearVelocityMax) {
|
||||
// Clamp velocity to maximum
|
||||
velocity.normalize();
|
||||
velocity.mult(linearVelocityMax);
|
||||
setLinearVelocity(velocity.x, velocity.y, velocity.z);
|
||||
}
|
||||
}
|
||||
|
||||
// Check angular velocity limits
|
||||
if (angularVelocityMin > 0.0 || angularVelocityMax > 0.0) {
|
||||
var angularVel = getAngularVelocity();
|
||||
var angularSpeed = angularVel.length();
|
||||
|
||||
if (angularVelocityMin > 0.0 && angularSpeed < angularVelocityMin) {
|
||||
// Increase angular velocity to minimum
|
||||
if (angularSpeed > 0.0) {
|
||||
angularVel.normalize();
|
||||
angularVel.mult(angularVelocityMin);
|
||||
setAngularVelocity(angularVel.x, angularVel.y, angularVel.z);
|
||||
}
|
||||
} else if (angularVelocityMax > 0.0 && angularSpeed > angularVelocityMax) {
|
||||
// Clamp angular velocity to maximum
|
||||
angularVel.normalize();
|
||||
angularVel.mult(angularVelocityMax);
|
||||
setAngularVelocity(angularVel.x, angularVel.y, angularVel.z);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function disableCollision() {
|
||||
@ -683,12 +842,23 @@ typedef RigidBodyParams = {
|
||||
var linearDeactivationThreshold: Float;
|
||||
var angularDeactivationThrshold: Float;
|
||||
var deactivationTime: Float;
|
||||
var linearVelocityMin: Float;
|
||||
var linearVelocityMax: Float;
|
||||
var angularVelocityMin: Float;
|
||||
var angularVelocityMax: Float;
|
||||
var lockTranslationX: Bool;
|
||||
var lockTranslationY: Bool;
|
||||
var lockTranslationZ: Bool;
|
||||
var lockRotationX: Bool;
|
||||
var lockRotationY: Bool;
|
||||
var lockRotationZ: Bool;
|
||||
}
|
||||
|
||||
typedef RigidBodyFlags = {
|
||||
var animated: Bool;
|
||||
var trigger: Bool;
|
||||
var ccd: Bool;
|
||||
var interpolate: Bool;
|
||||
var staticObj: Bool;
|
||||
var useDeactivation: Bool;
|
||||
}
|
||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user