Compare commits
	
		
			657 Commits
		
	
	
		
			9c5fc5d1d3
			...
			main
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 48f5575e4e | |||
| f2c4be6336 | |||
| 2ddc938db8 | |||
| 5eb735ada2 | |||
| 9894cc20f2 | |||
| dbe6d0829a | |||
| 6f383e2ab2 | |||
| 5c2d29d7ce | |||
| 28579e14d7 | |||
| 2ec6f43cc5 | |||
| 027021815a | |||
| b9b387803f | |||
| e05d9d0237 | |||
| c908e6cad2 | |||
| 506a0a0245 | |||
| 5cf33724e4 | |||
| ac5aa3d19c | |||
| 0c534ee632 | |||
| e3e7855d26 | |||
| f7917974f8 | |||
| fa2d8f05d5 | |||
| 73fcb55acc | |||
| c24baa3364 | |||
| 4517c4863f | |||
| 1299306e09 | |||
| f97d8fd846 | |||
| 8f8d4b1376 | |||
| a926fa8dbb | |||
| 6c3efa6c83 | |||
| 21afad6d09 | |||
| 04c6983a09 | |||
| 45966ef0bb | |||
| a72edc6203 | |||
| 6af1ef2df1 | |||
| 46e3047877 | |||
| de74af215a | |||
| b6e96553c2 | |||
| 58e009f709 | |||
| e88f101ca6 | |||
| d28d59b9e6 | |||
| a4398c7279 | |||
| abedfd799e | |||
| 4520422f6b | |||
| 88418c06c3 | |||
| aedc2783ab | |||
| 1505414c4c | |||
| fa818602c4 | |||
| 79dc458671 | |||
| 8e635fb1e9 | |||
| 4c2e6ab26a | |||
| 2371e3777e | |||
| b458b77e5c | |||
| 9b76f8cca9 | |||
| 5f2acb209e | |||
| 6fc446e7a9 | |||
| 71e57026e1 | |||
| 5288a98440 | |||
| 35e346be39 | |||
| 843ef0b058 | |||
| 177890bf39 | |||
| 9ac37e6dc7 | |||
| e697437778 | |||
| c94fc0fd97 | |||
| cd0a6f6788 | |||
| 4400e0e9c8 | |||
| 20cf07cfc3 | |||
| 1939f19c05 | |||
| 0d2b152ccb | |||
| 7f58e0fc85 | |||
| 0e4a6575c7 | |||
| 024676f43a | |||
| 8fe758862c | |||
| 1f3d1b47ae | |||
| f659a3c2be | |||
| 6eeb9017d4 | |||
| afe89c3834 | |||
| 8b695f72bb | |||
| 3d99fa60c0 | |||
| 43be7729ba | |||
| de0b1075c2 | |||
| c7aba23fa4 | |||
| 881f3267cc | |||
| 19b79d61c7 | |||
| fcbab54a0c | |||
| 8fd05d5514 | |||
| ad4013ed75 | |||
| 8ac567b57b | |||
| 43b7ae7060 | |||
| 29e9e71a6a | |||
| bfb85b0a3b | |||
| ef99b800e0 | |||
| 7cca955fc5 | |||
| 7e7bbd5eae | |||
| c31b2a18ad | |||
| fb47bf2564 | |||
| 7ae6750620 | |||
| 5b87010f76 | |||
| 97e952fc15 | |||
| b440539d65 | |||
| 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 | |||
| 9d7613be8f | |||
| c989f3254f | |||
| f8d76929ae | |||
| a51c28b607 | |||
| 9142371f88 | |||
| 35f92341fa | |||
| a8d1eebdaf | |||
| 87922c9389 | |||
| 38287f56b0 | |||
| 42ad5b01c1 | |||
| 2b46d6ebca | |||
| 953ad8391c | |||
| 1458ecd84f | |||
| 288b085d0c | |||
| aa0b2b1b7e | |||
| acede01167 | |||
| 92a2b0305e | |||
| 4886953722 | |||
| 601860b242 | |||
| f00cef2e21 | |||
| e733dca053 | |||
| 387be05826 | |||
| c5b21a9bb3 | |||
| 5b2c7bfc84 | |||
| 47d913ab64 | |||
| 878ce14254 | |||
| 9075931c51 | |||
| 39ab42f2da | |||
| 30cab4d79b | |||
| 8ee2aaa70a | |||
| 2f9694632d | |||
| 7b8d73cd84 | |||
| 3d8bd77c59 | |||
| 290289f413 | |||
| 2732210fc9 | |||
| 68900fb992 | |||
| ef724ae323 | |||
| 6d4c1a680b | |||
| 8aeaa0368e | |||
| 7969286fdc | |||
| cde2bead97 | |||
| c42e4c09a8 | |||
| b8c8ccad9d | |||
| 335f3541f1 | |||
| 808dcef817 | |||
| 6ac06cc504 | |||
| 86de9617f3 | |||
| 29761ec9e6 | |||
| ea7cf849b8 | |||
| 5d559734b9 | |||
| 939346c896 | |||
| f2dcfc0ffa | |||
| e78bd17d93 | |||
| f9a02f03cb | |||
| 21a4ee0af7 | |||
| 06319131fd | |||
| c08e1d3835 | |||
| 27bd360317 | |||
| 82a53a868a | |||
| 94eaba7319 | |||
| 8d1c2c51bd | |||
| 07d98639f2 | |||
| 1944fc97b8 | |||
| d69e3438ff | |||
| 62e52a7316 | |||
| c4b48c2d87 | |||
| 96f69a7cfe | |||
| 90950970f0 | |||
| e71b0849b3 | |||
| e079c94832 | |||
| 5572494f3d | |||
| 8f03927391 | |||
| 6586d90177 | |||
| 952cee36a3 | |||
| 35bfdf3715 | |||
| 135aaa0669 | |||
| c6b776a329 | |||
| 67c7443985 | |||
| 42273470c3 | |||
| 4dfc6be702 | |||
| 92e1abcfdc | |||
| 764eefeb06 | |||
| 979dfc605d | |||
| 55fb300901 | |||
| c59e6a66d4 | |||
| 659802b888 | |||
| 33f6fcaaaf | |||
| d37b41b181 | |||
| 70742b823f | |||
| da8f9d23e4 | |||
| ff886e6d46 | |||
| 70a603cf7a | |||
| bfc4c2644f | |||
| f101124d24 | |||
| c6f672bd61 | |||
| 7f38b15fe7 | |||
| 5628493493 | |||
| f8a08a41b1 | |||
| 2cd91f598c | |||
| a20d6b581c | |||
| e34ed0794f | |||
| bdcf4c980b | |||
| 30e5acf9bf | |||
| c3435b9533 | |||
| 8765e894f5 | |||
| 2c5c8d0e4f | |||
| 4805dd06a7 | |||
| 2bc2ab43a1 | |||
| c798f122d0 | |||
| d1edb1464e | |||
| ee8d3314b5 | |||
| c6139282ed | |||
| 559a3b8b39 | |||
| 8d072ae481 | |||
| 293a612e9c | |||
| 8020599513 | |||
| 4fbc1aba7f | |||
| 1380585297 | |||
| beee036a3d | |||
| 76b2ba8f80 | |||
| e7143cc740 | |||
| bd0886b1d7 | |||
| 8eb8c0bd98 | |||
| dd06e490a7 | |||
| 5e4ac6dd0b | |||
| be0a9149ad | |||
| 7ab3398941 | |||
| c10e988d2d | |||
| caafeea2f1 | |||
| 2edee3fe68 | |||
| af147c9c63 | |||
| 8e4c20647b | |||
| 594cbeffbe | |||
| e1d48e4289 | |||
| 7960ca94a6 | |||
| 72f14f59bf | |||
| d994d040ef | |||
| 1e18795d29 | |||
| 9557b82970 | |||
| 88949c63c5 | |||
| 7cab325397 | |||
| 8792ef5cee | |||
| 761b1832ad | |||
| 074a51866f | |||
| c382460355 | |||
| 4a3544b5d8 | |||
| 7cebe8340f | |||
| ecaea8ad9d | |||
| cae7963b21 | |||
| 7a2976ced1 | |||
| aae64aa8f8 | |||
| ef25d15aed | |||
| 01bcf029f9 | |||
| 2b9baef712 | |||
| 489057018e | |||
| 501a064d25 | |||
| 9478e4e957 | |||
| 32c6535f8a | |||
| 21c2abe67c | |||
| 2b1b56bc0a | |||
| 9e47c1db6d | |||
| 6be977da7e | |||
| 693fa36ee1 | |||
| 6ec480930a | |||
| a627a52d46 | |||
| be63323c09 | |||
| 38595db46b | |||
| 48d28f6873 | |||
| eb033149a0 | |||
| db1c3bdf4c | |||
| 2b16b565a8 | |||
| 8c758bf51f | |||
| 1764081740 | |||
| c90bde8719 | |||
| bfb11275df | |||
| 3185f61f39 | |||
| fb00ca88ff | |||
| 78109cfee2 | |||
| fa65c02e8a | |||
| cb93ede7d7 | |||
| 2a3f8db0bc | |||
| 3ab9123000 | |||
| d6b4b6eeea | |||
| 2f1e6783a4 | |||
| 603a976adf | |||
| 1e0d32a88d | |||
| 82a7075308 | |||
| de8f7d08ed | |||
| caf95e2611 | |||
| b639f06aba | |||
| de41800e1c | |||
| d2746fb087 | |||
| 62d3e65796 | |||
| 74473087b5 | |||
| 392da64d1f | |||
| 30748390ca | |||
| f9d463ca1d | |||
| b8771e9d81 | |||
| 1f52eed66c | |||
| e562b626cb | |||
| ccb554ba16 | |||
| 972775b432 | |||
| ee984c332e | |||
| 038e5123c9 | |||
| b5575e5a48 | |||
| 76d79e6e18 | |||
| ca8fd0b357 | |||
| 87fa82e3d3 | |||
| 9fa58d9d7c | |||
| 864568d66b | |||
| f3e96546ae | |||
| fcc114aed3 | |||
| df33848bee | |||
| c17516a4e2 | |||
| 9b9acd5e15 | |||
| 3b0eb0e075 | |||
| f5b3da3a15 | |||
| b71275d0d5 | |||
| 6aeb9b0f22 | |||
| 609477737e | |||
| a747f89438 | |||
| 0bd0c57a00 | |||
| f3ab801928 | |||
| 9b821d4b8e | |||
| f2a0dbe36c | |||
| 12e66d8cf1 | |||
| 4b6424b702 | |||
| e66e082cc7 | |||
| 42eff22cbb | |||
| 448898a634 | |||
| 15ec41b1f0 | |||
| c5a7746c84 | |||
| 51aa8ea1d4 | |||
| ccdfc4246f | |||
| aead4c4903 | |||
| d2f4c0e221 | |||
| 887eeebe27 | |||
| 83077e3f15 | |||
| f5c20c5a13 | |||
| 4bd4995b6e | |||
| 381744e678 | |||
| 34c15c29cd | |||
| 0346e27657 | |||
| 9087b9c98c | |||
| f2cbcc88a7 | |||
| b8d9ea2aff | |||
| 37a09d0a21 | |||
| ed932e3fa4 | |||
| f00edd882f | |||
| f403955fcd | |||
| a562af9b60 | |||
| bdd9b0197f | |||
| f200933375 | |||
| 829ef5f269 | |||
| 0d73133f36 | |||
| 181b860360 | |||
| 20db9cc24d | |||
| c67b38ea1e | |||
| adb95eab1e | |||
| 93addd1200 | |||
| 7f9a7c9b4a | |||
| 7433409cda | |||
| 2a6e05aff1 | |||
| 348d7bf343 | |||
| 4c6df16aa7 | |||
| 8a7f5ee519 | |||
| 1958342a74 | |||
| 16e8e00783 | |||
| bcdcbfb106 | |||
| 28afefbbf7 | |||
| 3d2130e26a | |||
| 9761719dd5 | |||
| b2a781c7c2 | |||
| 7121737297 | |||
| b0991a8928 | |||
| b84fc93899 | |||
| 06c0c430a8 | |||
| 0f0c67dc07 | |||
| 3bb2d65faa | |||
| 20e33afeac | 
							
								
								
									
										2
									
								
								.gitattributes
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,2 @@
 | 
			
		||||
*.hdr binary
 | 
			
		||||
blender/lnx/props.py ident
 | 
			
		||||
							
								
								
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,3 @@
 | 
			
		||||
__pycache__/
 | 
			
		||||
*.pyc
 | 
			
		||||
*.DS_Store
 | 
			
		||||
@ -18,7 +18,7 @@
 | 
			
		||||
 *
 | 
			
		||||
 *  This file is part of mbed TLS (https://tls.mbed.org)
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#define _GNU_SOURCE
 | 
			
		||||
#if !defined(MBEDTLS_CONFIG_FILE)
 | 
			
		||||
#include "mbedtls/config.h"
 | 
			
		||||
#else
 | 
			
		||||
 | 
			
		||||
@ -9,6 +9,7 @@ if (platform == Platform.OSX) project.addDefine('KORE_DEBUGDIR="osx-hl"');
 | 
			
		||||
if (platform == Platform.iOS) project.addDefine('KORE_DEBUGDIR="ios-hl"');
 | 
			
		||||
if (platform !== Platform.Windows || audio !== AudioApi.DirectSound) {
 | 
			
		||||
	project.addDefine('KORE_MULTITHREADED_AUDIO');
 | 
			
		||||
	project.addDefine('KINC_MULTITHREADED_AUDIO');
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
project.addDefine('KORE');
 | 
			
		||||
 | 
			
		||||
@ -1,16 +1,55 @@
 | 
			
		||||
package kha.graphics4;
 | 
			
		||||
 | 
			
		||||
using StringTools;
 | 
			
		||||
import kha.Blob;
 | 
			
		||||
 | 
			
		||||
class FragmentShader {
 | 
			
		||||
	public var _shader: Pointer;
 | 
			
		||||
 | 
			
		||||
	public function new(sources: Array<Blob>, files: Array<String>) {
 | 
			
		||||
		initShader(sources[0]);
 | 
			
		||||
		//initShader(sources[0]);
 | 
			
		||||
		var shaderBlob: Blob = null;
 | 
			
		||||
		var shaderFile: String = null;
 | 
			
		||||
 | 
			
		||||
		#if kha_opengl
 | 
			
		||||
		final expectedExtension = ".glsl";
 | 
			
		||||
		#elseif kha_direct3d11
 | 
			
		||||
		final expectedExtension = ".d3d11";
 | 
			
		||||
		#elseif kha_direct3d12
 | 
			
		||||
		final expectedExtension = ".d3d12";
 | 
			
		||||
		#elseif kha_metal
 | 
			
		||||
		final expectedExtension = ".metal";
 | 
			
		||||
		#elseif kha_vulkan
 | 
			
		||||
		final expectedExtension = ".spirv";
 | 
			
		||||
		#else
 | 
			
		||||
		final expectedExtension = ".glsl";
 | 
			
		||||
		#end
 | 
			
		||||
 | 
			
		||||
		if (sources != null && files != null) {
 | 
			
		||||
			for (i in 0...files.length) {
 | 
			
		||||
				if (files[i].endsWith(expectedExtension)) {
 | 
			
		||||
					shaderBlob = sources[i];
 | 
			
		||||
					shaderFile = files[i];
 | 
			
		||||
					break;
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (shaderBlob == null && sources != null && sources.length > 0) {
 | 
			
		||||
			trace('Warning: Could not find shader with extension ${expectedExtension}. Falling back to sources[0]: ${files != null && files.length > 0 ? files[0] : "Unknown"}');
 | 
			
		||||
			shaderBlob = sources[0];
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (shaderBlob != null) {
 | 
			
		||||
			initShader(shaderBlob);
 | 
			
		||||
		} else {
 | 
			
		||||
			trace('Error: No suitable fragment shader source found!');
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	function initShader(source: Blob): Void {
 | 
			
		||||
		_shader = kinc_create_fragmentshader(source.bytes.getData(), source.bytes.getData().length);
 | 
			
		||||
		//_shader = kinc_create_fragmentshader(source.bytes.getData(), source.bytes.getData().length);
 | 
			
		||||
		_shader = kinc_create_fragmentshader(source.bytes.getData(), source.length); // Use source.length here
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public static function fromSource(source: String): FragmentShader {
 | 
			
		||||
 | 
			
		||||
@ -1,179 +1,38 @@
 | 
			
		||||
#include <kinc/compute/compute.h>
 | 
			
		||||
#include <kinc/graphics4/compute.h>
 | 
			
		||||
#include <kinc/graphics4/texture.h>
 | 
			
		||||
 | 
			
		||||
#include <hl.h>
 | 
			
		||||
 | 
			
		||||
vbyte *hl_kinc_compute_create_shader(vbyte *data, int length) {
 | 
			
		||||
	kinc_compute_shader_t *shader = (kinc_compute_shader_t *)malloc(sizeof(kinc_compute_shader_t));
 | 
			
		||||
	kinc_compute_shader_init(shader, data, length);
 | 
			
		||||
	kinc_g4_compute_shader *shader = (kinc_g4_compute_shader *)malloc(sizeof(kinc_g4_compute_shader));
 | 
			
		||||
	kinc_g4_compute_shader_init(shader, data, length);
 | 
			
		||||
	return (vbyte *)shader;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void hl_kinc_compute_delete_shader(vbyte *shader) {
 | 
			
		||||
	kinc_compute_shader_t *sh = (kinc_compute_shader_t *)shader;
 | 
			
		||||
	kinc_compute_shader_destroy(sh);
 | 
			
		||||
	kinc_g4_compute_shader *sh = (kinc_g4_compute_shader *)shader;
 | 
			
		||||
	kinc_g4_compute_shader_destroy(sh);
 | 
			
		||||
	free(sh);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
vbyte *hl_kinc_compute_get_constantlocation(vbyte *shader, vbyte *name) {
 | 
			
		||||
	kinc_compute_shader_t *sh = (kinc_compute_shader_t *)shader;
 | 
			
		||||
	kinc_compute_constant_location_t *location = (kinc_compute_constant_location_t *)malloc(sizeof(kinc_compute_constant_location_t));
 | 
			
		||||
	*location = kinc_compute_shader_get_constant_location(sh, (char *)name), sizeof(kinc_compute_constant_location_t);
 | 
			
		||||
	kinc_g4_compute_shader *sh = (kinc_g4_compute_shader *)shader;
 | 
			
		||||
	kinc_g4_constant_location_t *location = (kinc_g4_constant_location_t *)malloc(sizeof(kinc_g4_constant_location_t));
 | 
			
		||||
	*location = kinc_g4_compute_shader_get_constant_location(sh, (char *)name);
 | 
			
		||||
	return (vbyte *)location;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
vbyte *hl_kinc_compute_get_textureunit(vbyte *shader, vbyte *name) {
 | 
			
		||||
	kinc_compute_shader_t *sh = (kinc_compute_shader_t *)shader;
 | 
			
		||||
	kinc_compute_texture_unit_t *unit = (kinc_compute_texture_unit_t *)malloc(sizeof(kinc_compute_texture_unit_t));
 | 
			
		||||
	*unit = kinc_compute_shader_get_texture_unit(sh, (char *)name), sizeof(kinc_compute_texture_unit_t);
 | 
			
		||||
	kinc_g4_compute_shader *sh = (kinc_g4_compute_shader *)shader;
 | 
			
		||||
	kinc_g4_texture_unit_t *unit = (kinc_g4_texture_unit_t *)malloc(sizeof(kinc_g4_texture_unit_t));
 | 
			
		||||
	*unit = kinc_g4_compute_shader_get_texture_unit(sh, (char *)name);
 | 
			
		||||
	return (vbyte *)unit;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void hl_kinc_compute_set_bool(vbyte *location, bool value) {
 | 
			
		||||
	kinc_compute_constant_location_t *loc = (kinc_compute_constant_location_t *)location;
 | 
			
		||||
	kinc_compute_set_bool(*loc, value);
 | 
			
		||||
void hl_kinc_set_compute_shader(vbyte *shader) {
 | 
			
		||||
	kinc_g4_set_compute_shader((kinc_g4_compute_shader *)shader);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void hl_kinc_compute_set_int(vbyte *location, int value) {
 | 
			
		||||
	kinc_compute_constant_location_t *loc = (kinc_compute_constant_location_t *)location;
 | 
			
		||||
	kinc_compute_set_int(*loc, value);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void hl_kinc_compute_set_float(vbyte *location, float value) {
 | 
			
		||||
	kinc_compute_constant_location_t *loc = (kinc_compute_constant_location_t *)location;
 | 
			
		||||
	kinc_compute_set_float(*loc, value);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void hl_kinc_compute_set_float2(vbyte *location, float value1, float value2) {
 | 
			
		||||
	kinc_compute_constant_location_t *loc = (kinc_compute_constant_location_t *)location;
 | 
			
		||||
	kinc_compute_set_float2(*loc, value1, value2);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void hl_kinc_compute_set_float3(vbyte *location, float value1, float value2, float value3) {
 | 
			
		||||
	kinc_compute_constant_location_t *loc = (kinc_compute_constant_location_t *)location;
 | 
			
		||||
	kinc_compute_set_float3(*loc, value1, value2, value3);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void hl_kinc_compute_set_float4(vbyte *location, float value1, float value2, float value3, float value4) {
 | 
			
		||||
	kinc_compute_constant_location_t *loc = (kinc_compute_constant_location_t *)location;
 | 
			
		||||
	kinc_compute_set_float4(*loc, value1, value2, value3, value4);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void hl_kinc_compute_set_floats(vbyte *location, vbyte *values, int count) {
 | 
			
		||||
	kinc_compute_constant_location_t *loc = (kinc_compute_constant_location_t *)location;
 | 
			
		||||
	kinc_compute_set_floats(*loc, (float *)values, count);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void hl_kinc_compute_set_matrix(vbyte *location, float _00, float _10, float _20, float _30, float _01, float _11, float _21, float _31, float _02, float _12,
 | 
			
		||||
                                float _22, float _32, float _03, float _13, float _23, float _33) {
 | 
			
		||||
	kinc_compute_constant_location_t *loc = (kinc_compute_constant_location_t *)location;
 | 
			
		||||
	kinc_matrix4x4_t value;
 | 
			
		||||
	kinc_matrix4x4_set(&value, 0, 0, _00);
 | 
			
		||||
	kinc_matrix4x4_set(&value, 0, 1, _01);
 | 
			
		||||
	kinc_matrix4x4_set(&value, 0, 2, _02);
 | 
			
		||||
	kinc_matrix4x4_set(&value, 0, 3, _03);
 | 
			
		||||
	kinc_matrix4x4_set(&value, 1, 0, _10);
 | 
			
		||||
	kinc_matrix4x4_set(&value, 1, 1, _11);
 | 
			
		||||
	kinc_matrix4x4_set(&value, 1, 2, _12);
 | 
			
		||||
	kinc_matrix4x4_set(&value, 1, 3, _13);
 | 
			
		||||
	kinc_matrix4x4_set(&value, 2, 0, _20);
 | 
			
		||||
	kinc_matrix4x4_set(&value, 2, 1, _21);
 | 
			
		||||
	kinc_matrix4x4_set(&value, 2, 2, _22);
 | 
			
		||||
	kinc_matrix4x4_set(&value, 2, 3, _23);
 | 
			
		||||
	kinc_matrix4x4_set(&value, 3, 0, _30);
 | 
			
		||||
	kinc_matrix4x4_set(&value, 3, 1, _31);
 | 
			
		||||
	kinc_matrix4x4_set(&value, 3, 2, _32);
 | 
			
		||||
	kinc_matrix4x4_set(&value, 3, 3, _33);
 | 
			
		||||
	kinc_compute_set_matrix4(*loc, &value);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void hl_kinc_compute_set_matrix3(vbyte *location, float _00, float _10, float _20, float _01, float _11, float _21, float _02, float _12, float _22) {
 | 
			
		||||
	kinc_compute_constant_location_t *loc = (kinc_compute_constant_location_t *)location;
 | 
			
		||||
	kinc_matrix3x3_t value;
 | 
			
		||||
	kinc_matrix3x3_set(&value, 0, 0, _00);
 | 
			
		||||
	kinc_matrix3x3_set(&value, 0, 1, _01);
 | 
			
		||||
	kinc_matrix3x3_set(&value, 0, 2, _02);
 | 
			
		||||
	kinc_matrix3x3_set(&value, 1, 0, _10);
 | 
			
		||||
	kinc_matrix3x3_set(&value, 1, 1, _11);
 | 
			
		||||
	kinc_matrix3x3_set(&value, 1, 2, _12);
 | 
			
		||||
	kinc_matrix3x3_set(&value, 2, 0, _20);
 | 
			
		||||
	kinc_matrix3x3_set(&value, 2, 1, _21);
 | 
			
		||||
	kinc_matrix3x3_set(&value, 2, 2, _22);
 | 
			
		||||
	kinc_compute_set_matrix3(*loc, &value);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void hl_kinc_compute_set_texture(vbyte *unit, vbyte *texture, int access) {
 | 
			
		||||
	kinc_compute_texture_unit_t *u = (kinc_compute_texture_unit_t *)unit;
 | 
			
		||||
	kinc_g4_texture_t *tex = (kinc_g4_texture_t *)texture;
 | 
			
		||||
	kinc_compute_set_texture(*u, tex, (kinc_compute_access_t)access);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void hl_kinc_compute_set_target(vbyte *unit, vbyte *renderTarget, int access) {
 | 
			
		||||
	kinc_compute_texture_unit_t *u = (kinc_compute_texture_unit_t *)unit;
 | 
			
		||||
	kinc_g4_render_target_t *rt = (kinc_g4_render_target_t *)renderTarget;
 | 
			
		||||
	kinc_compute_set_render_target(*u, rt, (kinc_compute_access_t)access);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void hl_kinc_compute_set_sampled_texture(vbyte *unit, vbyte *texture) {
 | 
			
		||||
	kinc_compute_texture_unit_t *u = (kinc_compute_texture_unit_t *)unit;
 | 
			
		||||
	kinc_g4_texture_t *tex = (kinc_g4_texture_t *)texture;
 | 
			
		||||
	kinc_compute_set_sampled_texture(*u, tex);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void hl_kinc_compute_set_sampled_target(vbyte *unit, vbyte *renderTarget) {
 | 
			
		||||
	kinc_compute_texture_unit_t *u = (kinc_compute_texture_unit_t *)unit;
 | 
			
		||||
	kinc_g4_render_target_t *rt = (kinc_g4_render_target_t *)renderTarget;
 | 
			
		||||
	kinc_compute_set_sampled_render_target(*u, rt);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void hl_kinc_compute_set_sampled_depth_target(vbyte *unit, vbyte *renderTarget) {
 | 
			
		||||
	kinc_compute_texture_unit_t *u = (kinc_compute_texture_unit_t *)unit;
 | 
			
		||||
	kinc_g4_render_target_t *rt = (kinc_g4_render_target_t *)renderTarget;
 | 
			
		||||
	kinc_compute_set_sampled_depth_from_render_target(*u, rt);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void hl_kinc_compute_set_sampled_cubemap_texture(vbyte *unit, vbyte *texture) {
 | 
			
		||||
	kinc_compute_texture_unit_t *u = (kinc_compute_texture_unit_t *)unit;
 | 
			
		||||
	kinc_g4_texture_t *tex = (kinc_g4_texture_t *)texture;
 | 
			
		||||
	kinc_compute_set_sampled_texture(*u, tex);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void hl_kinc_compute_set_sampled_cubemap_target(vbyte *unit, vbyte *renderTarget) {
 | 
			
		||||
	kinc_compute_texture_unit_t *u = (kinc_compute_texture_unit_t *)unit;
 | 
			
		||||
	kinc_g4_render_target_t *rt = (kinc_g4_render_target_t *)renderTarget;
 | 
			
		||||
	kinc_compute_set_sampled_render_target(*u, rt);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void hl_kinc_compute_set_sampled_cubemap_depth_target(vbyte *unit, vbyte *renderTarget) {
 | 
			
		||||
	kinc_compute_texture_unit_t *u = (kinc_compute_texture_unit_t *)unit;
 | 
			
		||||
	kinc_g4_render_target_t *rt = (kinc_g4_render_target_t *)renderTarget;
 | 
			
		||||
	kinc_compute_set_sampled_depth_from_render_target(*u, rt);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void hl_kinc_compute_set_texture_parameters(vbyte *unit, int uAddressing, int vAddressing, int minificationFilter, int magnificationFilter, int mipmapFilter) {
 | 
			
		||||
	kinc_compute_texture_unit_t *u = (kinc_compute_texture_unit_t *)unit;
 | 
			
		||||
	kinc_compute_set_texture_addressing(*u, KINC_G4_TEXTURE_DIRECTION_U, (kinc_g4_texture_addressing_t)uAddressing);
 | 
			
		||||
	kinc_compute_set_texture_addressing(*u, KINC_G4_TEXTURE_DIRECTION_V, (kinc_g4_texture_addressing_t)vAddressing);
 | 
			
		||||
	kinc_compute_set_texture_minification_filter(*u, (kinc_g4_texture_filter_t)minificationFilter);
 | 
			
		||||
	kinc_compute_set_texture_magnification_filter(*u, (kinc_g4_texture_filter_t)magnificationFilter);
 | 
			
		||||
	kinc_compute_set_texture_mipmap_filter(*u, (kinc_g4_mipmap_filter_t)mipmapFilter);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void hl_kinc_compute_set_texture3d_parameters(vbyte *unit, int uAddressing, int vAddressing, int wAddressing, int minificationFilter, int magnificationFilter,
 | 
			
		||||
                                              int mipmapFilter) {
 | 
			
		||||
	kinc_compute_texture_unit_t *u = (kinc_compute_texture_unit_t *)unit;
 | 
			
		||||
	kinc_compute_set_texture3d_addressing(*u, KINC_G4_TEXTURE_DIRECTION_U, (kinc_g4_texture_addressing_t)uAddressing);
 | 
			
		||||
	kinc_compute_set_texture3d_addressing(*u, KINC_G4_TEXTURE_DIRECTION_V, (kinc_g4_texture_addressing_t)vAddressing);
 | 
			
		||||
	kinc_compute_set_texture3d_addressing(*u, KINC_G4_TEXTURE_DIRECTION_W, (kinc_g4_texture_addressing_t)wAddressing);
 | 
			
		||||
	kinc_compute_set_texture3d_minification_filter(*u, (kinc_g4_texture_filter_t)minificationFilter);
 | 
			
		||||
	kinc_compute_set_texture3d_magnification_filter(*u, (kinc_g4_texture_filter_t)magnificationFilter);
 | 
			
		||||
	kinc_compute_set_texture3d_mipmap_filter(*u, (kinc_g4_mipmap_filter_t)mipmapFilter);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void hl_kinc_compute_set_shader(vbyte *shader) {
 | 
			
		||||
	kinc_compute_set_shader((kinc_compute_shader_t *)shader);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void hl_kinc_compute_compute(int x, int y, int z) {
 | 
			
		||||
	kinc_compute(x, y, z);
 | 
			
		||||
void hl_kinc_compute(int x, int y, int z) {
 | 
			
		||||
	kinc_g4_compute(x, y, z);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -42,8 +42,8 @@ static void update(void *data) {
 | 
			
		||||
 | 
			
		||||
static bool mixThreadregistered = false;
 | 
			
		||||
 | 
			
		||||
static void mix(kinc_a2_buffer_t *buffer, int samples) {
 | 
			
		||||
#ifdef KORE_MULTITHREADED_AUDIO
 | 
			
		||||
static void mix(kinc_a2_buffer_t *buffer, uint32_t samples, void *userdata) {
 | 
			
		||||
#ifdef KINC_MULTITHREADED_AUDIO
 | 
			
		||||
	if (!mixThreadregistered) {
 | 
			
		||||
		vdynamic *ret;
 | 
			
		||||
		hl_register_thread(&ret);
 | 
			
		||||
@ -54,16 +54,18 @@ static void mix(kinc_a2_buffer_t *buffer, int samples) {
 | 
			
		||||
 | 
			
		||||
	audioCallCallback(samples);
 | 
			
		||||
 | 
			
		||||
	for (int i = 0; i < samples; ++i) {
 | 
			
		||||
		float value = audioReadSample();
 | 
			
		||||
		*(float *)&buffer->data[buffer->write_location] = value;
 | 
			
		||||
		buffer->write_location += 4;
 | 
			
		||||
	for (uint32_t i = 0; i < samples; i += 2) {
 | 
			
		||||
		float left_value = audioReadSample();
 | 
			
		||||
		float right_value = audioReadSample();
 | 
			
		||||
		*(float *)&buffer->channels[0][buffer->write_location] = left_value;
 | 
			
		||||
		*(float *)&buffer->channels[1][buffer->write_location] = right_value;
 | 
			
		||||
		buffer->write_location += 1;
 | 
			
		||||
		if (buffer->write_location >= buffer->data_size) {
 | 
			
		||||
			buffer->write_location = 0;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
#ifdef KORE_MULTITHREADED_AUDIO
 | 
			
		||||
#ifdef KINC_MULTITHREADED_AUDIO
 | 
			
		||||
	hl_blocking(false);
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
@ -92,9 +94,9 @@ void hl_init_kore(vbyte *title, int width, int height, int samplesPerPixel, bool
 | 
			
		||||
void hl_kinc_init_audio(vclosure *callCallback, vclosure *readSample, int *outSamplesPerSecond) {
 | 
			
		||||
	audioCallCallback = *((FN_AUDIO_CALL_CALLBACK *)(&callCallback->fun));
 | 
			
		||||
	audioReadSample = *((FN_AUDIO_READ_SAMPLE *)(&readSample->fun));
 | 
			
		||||
	kinc_a2_set_callback(mix);
 | 
			
		||||
	kinc_a2_init();
 | 
			
		||||
	*outSamplesPerSecond = kinc_a2_samples_per_second;
 | 
			
		||||
	kinc_a2_set_callback(mix, NULL);
 | 
			
		||||
	*outSamplesPerSecond = kinc_a2_samples_per_second();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void hl_run_kore(void) {
 | 
			
		||||
 | 
			
		||||
@ -94,11 +94,11 @@ void hl_kinc_system_load_url(vbyte *url) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
vbyte *hl_kinc_get_gamepad_id(int index) {
 | 
			
		||||
	return (vbyte*)kinc_gamepad_product_name(index);
 | 
			
		||||
	return (vbyte *)kinc_gamepad_product_name(index);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
vbyte *hl_kinc_get_gamepad_vendor(int index) {
 | 
			
		||||
	return (vbyte*)kinc_gamepad_vendor(index);
 | 
			
		||||
	return (vbyte *)kinc_gamepad_vendor(index);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool hl_kinc_gamepad_connected(int index) {
 | 
			
		||||
@ -137,12 +137,12 @@ void hl_kinc_register_pen(vclosure *penDown, vclosure *penUp, vclosure *penMove)
 | 
			
		||||
	kinc_pen_set_move_callback(*((FN_PEN_MOVE *)(&penMove->fun)));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
typedef void (*FN_GAMEPAD_AXIS)(int, int, float);
 | 
			
		||||
typedef void (*FN_GAMEPAD_BUTTON)(int, int, float);
 | 
			
		||||
typedef void (*FN_GAMEPAD_AXIS)(int, int, float, void *);
 | 
			
		||||
typedef void (*FN_GAMEPAD_BUTTON)(int, int, float, void *);
 | 
			
		||||
 | 
			
		||||
void hl_kinc_register_gamepad(vclosure *gamepadAxis, vclosure *gamepadButton) {
 | 
			
		||||
	kinc_gamepad_set_axis_callback(*((FN_GAMEPAD_AXIS *)(&gamepadAxis->fun)));
 | 
			
		||||
	kinc_gamepad_set_button_callback(*((FN_GAMEPAD_BUTTON *)(&gamepadButton->fun)));
 | 
			
		||||
	kinc_gamepad_set_axis_callback(*((FN_GAMEPAD_AXIS *)(&gamepadAxis->fun)), NULL);
 | 
			
		||||
	kinc_gamepad_set_button_callback(*((FN_GAMEPAD_BUTTON *)(&gamepadButton->fun)), NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
typedef void (*FN_TOUCH_START)(int, int, int);
 | 
			
		||||
 | 
			
		||||
@ -80,6 +80,7 @@ extern class Krom {
 | 
			
		||||
	static function unloadImage(image: kha.Image): Void;
 | 
			
		||||
	static function loadSound(file: String): Dynamic;
 | 
			
		||||
	static function writeAudioBuffer(buffer: js.lib.ArrayBuffer, samples: Int): Void;
 | 
			
		||||
	static function getSamplesPerSecond(): Int;
 | 
			
		||||
	static function loadBlob(file: String): js.lib.ArrayBuffer;
 | 
			
		||||
 | 
			
		||||
	static function init(title: String, width: Int, height: Int, samplesPerPixel: Int, vSync: Bool, windowMode: Int, windowFeatures: Int, kromApi: Int): Void;
 | 
			
		||||
@ -115,6 +116,7 @@ extern class Krom {
 | 
			
		||||
	static function screenDpi(): Int;
 | 
			
		||||
	static function systemId(): String;
 | 
			
		||||
	static function requestShutdown(): Void;
 | 
			
		||||
	static function displayFrequency(): Int;
 | 
			
		||||
	static function displayCount(): Int;
 | 
			
		||||
	static function displayWidth(index: Int): Int;
 | 
			
		||||
	static function displayHeight(index: Int): Int;
 | 
			
		||||
 | 
			
		||||
@ -79,7 +79,7 @@ class Display {
 | 
			
		||||
	public var frequency(get, never): Int;
 | 
			
		||||
 | 
			
		||||
	function get_frequency(): Int {
 | 
			
		||||
		return 60;
 | 
			
		||||
		return Krom.displayFrequency();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public var pixelsPerInch(get, never): Int;
 | 
			
		||||
 | 
			
		||||
@ -171,8 +171,9 @@ class SystemImpl {
 | 
			
		||||
		Krom.setGamepadAxisCallback(gamepadAxisCallback);
 | 
			
		||||
		Krom.setGamepadButtonCallback(gamepadButtonCallback);
 | 
			
		||||
 | 
			
		||||
		kha.audio2.Audio._init();
 | 
			
		||||
		kha.audio2.Audio.samplesPerSecond = Krom.getSamplesPerSecond();
 | 
			
		||||
		kha.audio1.Audio._init();
 | 
			
		||||
		kha.audio2.Audio._init();
 | 
			
		||||
		Krom.setAudioCallback(audioCallback);
 | 
			
		||||
 | 
			
		||||
		Scheduler.start();
 | 
			
		||||
@ -207,7 +208,7 @@ class SystemImpl {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public static function getRefreshRate(): Int {
 | 
			
		||||
		return 60;
 | 
			
		||||
		return Krom.displayFrequency();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public static function getSystemId(): String {
 | 
			
		||||
 | 
			
		||||
@ -10,8 +10,7 @@ class Audio {
 | 
			
		||||
 | 
			
		||||
	public static function _init() {
 | 
			
		||||
		var bufferSize = 1024 * 2;
 | 
			
		||||
		buffer = new Buffer(bufferSize * 4, 2, 44100);
 | 
			
		||||
		Audio.samplesPerSecond = 44100;
 | 
			
		||||
		buffer = new Buffer(bufferSize * 4, 2, samplesPerSecond);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public static function _callCallback(samples: Int): Void {
 | 
			
		||||
@ -32,11 +31,11 @@ class Audio {
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public static function _readSample(): Float {
 | 
			
		||||
	public static function _readSample(): FastFloat {
 | 
			
		||||
		if (buffer == null)
 | 
			
		||||
			return 0;
 | 
			
		||||
		var value = buffer.data.get(buffer.readLocation);
 | 
			
		||||
		buffer.readLocation += 1;
 | 
			
		||||
		++buffer.readLocation;
 | 
			
		||||
		if (buffer.readLocation >= buffer.size) {
 | 
			
		||||
			buffer.readLocation = 0;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
@ -59,7 +59,7 @@ class Graphics implements kha.graphics4.Graphics {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public function refreshRate(): Int {
 | 
			
		||||
		return 60;
 | 
			
		||||
		return Krom.displayFrequency();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public function clear(?color: Color, ?depth: Float, ?stencil: Int): Void {
 | 
			
		||||
 | 
			
		||||
@ -34,22 +34,46 @@ void copySample(void *buffer) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int playback_callback(snd_pcm_sframes_t nframes) {
 | 
			
		||||
	int err = 0;
 | 
			
		||||
	if (kinc_a2_internal_callback(&a2_buffer, nframes)) {
 | 
			
		||||
		int ni = 0;
 | 
			
		||||
		while (ni < nframes) {
 | 
			
		||||
			int i = 0;
 | 
			
		||||
			for (; ni < nframes && i < 4096 * 2; ++i, ++ni) {
 | 
			
		||||
				copySample(&buf[i * 2]);
 | 
			
		||||
			}
 | 
			
		||||
			int err2;
 | 
			
		||||
			if ((err2 = snd_pcm_writei(playback_handle, buf, i)) < 0) {
 | 
			
		||||
				fprintf(stderr, "write failed (%s)\n", snd_strerror(err2));
 | 
			
		||||
			}
 | 
			
		||||
			err += err2;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return err;
 | 
			
		||||
    int err = 0; 
 | 
			
		||||
    if (kinc_a2_internal_callback(&a2_buffer, nframes)) {
 | 
			
		||||
        int ni = 0;
 | 
			
		||||
        while (ni < nframes) {
 | 
			
		||||
            int i = 0;
 | 
			
		||||
            for (; ni < nframes && i < 4096; ++i, ++ni) {
 | 
			
		||||
                copySample(&buf[i * 2]);
 | 
			
		||||
            }
 | 
			
		||||
            int err2 = snd_pcm_writei(playback_handle, buf, i);
 | 
			
		||||
            if (err2 < 0) {
 | 
			
		||||
                fprintf(stderr, "ALSA write failed in playback_callback: %s\n", snd_strerror(err2));
 | 
			
		||||
                return err2;
 | 
			
		||||
            }
 | 
			
		||||
            if (err2 < i) {
 | 
			
		||||
                fprintf(stderr, "ALSA short write in playback_callback: wrote %d of %d frames\n", err2, i);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        err = nframes;
 | 
			
		||||
    }
 | 
			
		||||
    else {
 | 
			
		||||
        // Write silence data to prevent recovery
 | 
			
		||||
        if (nframes > 4096) {
 | 
			
		||||
             fprintf(stderr, "Warning: ALSA requested %ld frames for silence, exceeding local buffer size %d. Clamping.\n", nframes, 4096);
 | 
			
		||||
             nframes = 4096; 
 | 
			
		||||
        }
 | 
			
		||||
        memset(buf, 0, nframes * 4);
 | 
			
		||||
 | 
			
		||||
        int err2 = snd_pcm_writei(playback_handle, buf, nframes);
 | 
			
		||||
 | 
			
		||||
        if (err2 < 0) {
 | 
			
		||||
            fprintf(stderr, "ALSA silence write failed in playback_callback: %s\n", snd_strerror(err2));
 | 
			
		||||
            err = err2;
 | 
			
		||||
        } else {
 | 
			
		||||
            if (err2 < nframes) {
 | 
			
		||||
                fprintf(stderr, "ALSA short silence write in playback_callback: wrote %d of %d frames\n", err2, (int)nframes);
 | 
			
		||||
            }
 | 
			
		||||
            err = err2;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return err;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool tryToRecover(snd_pcm_t *handle, int errorCode) {
 | 
			
		||||
 | 
			
		||||
| 
		 Before Width: | Height: | Size: 417 KiB After Width: | Height: | Size: 281 KiB  | 
| 
		 Before Width: | Height: | Size: 417 KiB After Width: | Height: | Size: 281 KiB  | 
| 
		 Before Width: | Height: | Size: 417 KiB After Width: | Height: | Size: 281 KiB  | 
| 
		 Before Width: | Height: | Size: 417 KiB After Width: | Height: | Size: 281 KiB  | 
| 
		 Before Width: | Height: | Size: 417 KiB After Width: | Height: | Size: 281 KiB  | 
| 
		 Before Width: | Height: | Size: 417 KiB After Width: | Height: | Size: 281 KiB  | 
@ -51,7 +51,7 @@ class Scheduler {
 | 
			
		||||
	static var vsync: Bool;
 | 
			
		||||
 | 
			
		||||
	// Html5 target can update display frequency after some delay
 | 
			
		||||
	#if kha_html5
 | 
			
		||||
	#if (kha_html5 || kha_debug_html5)
 | 
			
		||||
	static var onedifhz(get, never): Float;
 | 
			
		||||
 | 
			
		||||
	static inline function get_onedifhz(): Float {
 | 
			
		||||
@ -97,7 +97,7 @@ class Scheduler {
 | 
			
		||||
 | 
			
		||||
	public static function start(restartTimers: Bool = false): Void {
 | 
			
		||||
		vsync = Window.get(0).vSynced;
 | 
			
		||||
		#if !kha_html5
 | 
			
		||||
		#if !(kha_html5 || kha_debug_html5)
 | 
			
		||||
		var hz = Display.primary != null ? Display.primary.frequency : 60;
 | 
			
		||||
		if (hz >= 57 && hz <= 63)
 | 
			
		||||
			hz = 60;
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										
											BIN
										
									
								
								Krom/Krom.exe
									
									
									
									
									
								
							
							
						
						
							
								
								
									
										
											BIN
										
									
								
								Krom/Krom_opengl.exe
									
									
									
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										14
									
								
								README.md
									
									
									
									
									
								
							
							
						
						@ -6,22 +6,12 @@ Welcome to Leenkx!
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
<br>
 | 
			
		||||
- <a style="color:#2AE0E0;" class="text primary primary-text" href="https://leenkx.com/files/LeenkxSDK3_RC.zip">LeenkxSDKV3.6 lts</a>
 | 
			
		||||
- Run your own private p2p torrent socket changing Leenkx javascript file directly in /lnxsdk/lib/leenkx_tools/lnxjs/Leenkx.js or by configuring the OPTS map in the create Leenkx node.
 | 
			
		||||
<br> 
 | 
			
		||||
- <a style="color:#2AE0E0;" class="text primary primary-text" href="https://leenkx.com/files/lx/Leenkx.js">Leenkx.js</a>
 | 
			
		||||
<br>
 | 
			
		||||
- Run your own private p2p torrent socket changing Leenkx javascript file directly in /lnxsdk/lib/leenkx_tools/lxjs/Leenkx.js or by configuring the OPTS map in the create Leenkx node.
 | 
			
		||||
<br> 
 | 
			
		||||
- All works are open source ZLIB / MIT and any compatible licence that  guarantee the softwares freedom from my additions to the previous maintainers 
 | 
			
		||||
<br>
 | 
			
		||||
- LxJS Works as a standalone library
 | 
			
		||||
- LNXJS Works as a standalone library
 | 
			
		||||
<br>
 | 
			
		||||
- The SDK is not ready for production, use for educational purposes. Help the team by reaching out at Leenkx.com!
 | 
			
		||||
<br>
 | 
			
		||||
- This build is based on QuantumCoder's whole new core animation system packed with new features! Timmodrians Aura library is also built into this build when audio is enabled! 
 | 
			
		||||
<br>
 | 
			
		||||
- The SDK is still undergoing many changes so working on large projects is not recommended. 
 | 
			
		||||
<br>
 | 
			
		||||
- Special thanks to all contributors from the following projects!
 | 
			
		||||
<br>
 | 
			
		||||
* https://github.com/armory3d/armory/graphs/contributors<br>
 | 
			
		||||
 | 
			
		||||
@ -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'])
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										35
									
								
								leenkx.py
									
									
									
									
									
								
							
							
						
						@ -24,7 +24,7 @@ import textwrap
 | 
			
		||||
import threading
 | 
			
		||||
import traceback
 | 
			
		||||
import typing
 | 
			
		||||
from typing import Callable, Optional
 | 
			
		||||
from typing import Callable, Optional, List
 | 
			
		||||
import webbrowser
 | 
			
		||||
 | 
			
		||||
import bpy
 | 
			
		||||
@ -33,6 +33,12 @@ from bpy.props import *
 | 
			
		||||
from bpy.types import Operator, AddonPreferences
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
if bpy.app.version < (2, 90, 0):  
 | 
			
		||||
    ListType = List
 | 
			
		||||
else:
 | 
			
		||||
    ListType = list
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class SDKSource(IntEnum):
 | 
			
		||||
    PREFS = 0
 | 
			
		||||
    LOCAL = 1
 | 
			
		||||
@ -73,9 +79,10 @@ def detect_sdk_path():
 | 
			
		||||
    area = win.screen.areas[0]
 | 
			
		||||
    area_type = area.type
 | 
			
		||||
    area.type = "INFO"
 | 
			
		||||
    with bpy.context.temp_override(window=win, screen=win.screen, area=area):
 | 
			
		||||
        bpy.ops.info.select_all(action='SELECT')
 | 
			
		||||
        bpy.ops.info.report_copy()
 | 
			
		||||
    if bpy.app.version >= (2, 92, 0):
 | 
			
		||||
        with bpy.context.temp_override(window=win, screen=win.screen, area=area):
 | 
			
		||||
            bpy.ops.info.select_all(action='SELECT')
 | 
			
		||||
            bpy.ops.info.report_copy()
 | 
			
		||||
    area.type = area_type
 | 
			
		||||
    clipboard = bpy.context.window_manager.clipboard
 | 
			
		||||
 | 
			
		||||
@ -85,6 +92,7 @@ def detect_sdk_path():
 | 
			
		||||
    if match:
 | 
			
		||||
        addon_prefs.sdk_path = os.path.dirname(match[-1])
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_link_web_server(self):
 | 
			
		||||
    return self.get('link_web_server', 'http://localhost/')
 | 
			
		||||
 | 
			
		||||
@ -310,9 +318,9 @@ class LeenkxAddonPreferences(AddonPreferences):
 | 
			
		||||
        layout.label(text="Welcome to Leenkx!")
 | 
			
		||||
 | 
			
		||||
        # Compare version Blender and Leenkx (major, minor)
 | 
			
		||||
        if bpy.app.version[0] != 3 or bpy.app.version[1] != 6:
 | 
			
		||||
        if bpy.app.version[:2] not in [(4, 4), (4, 2), (3, 6), (3, 3)]:
 | 
			
		||||
            box = layout.box().column()
 | 
			
		||||
            box.label(text="Warning: For Leenkx to work correctly, you need Blender 3.6 LTS.")
 | 
			
		||||
            box.label(text="Warning: For Leenkx to work correctly, use a Blender LTS version")
 | 
			
		||||
 | 
			
		||||
        layout.prop(self, "sdk_path")
 | 
			
		||||
        sdk_path = get_sdk_path(context)
 | 
			
		||||
@ -558,7 +566,7 @@ def remove_readonly(func, path, excinfo):
 | 
			
		||||
    func(path)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def run_proc(cmd: list[str], done: Optional[Callable[[bool], None]] = None):
 | 
			
		||||
def run_proc(cmd: ListType[str], done: Optional[Callable[[bool], None]] = None):
 | 
			
		||||
    def fn(p, done):
 | 
			
		||||
        p.wait()
 | 
			
		||||
        if done is not None:
 | 
			
		||||
@ -840,7 +848,13 @@ def update_leenkx_py(sdk_path: str, force_relink=False):
 | 
			
		||||
                else:
 | 
			
		||||
                    raise err
 | 
			
		||||
    else:
 | 
			
		||||
        lnx_module_file.unlink(missing_ok=True)
 | 
			
		||||
        if bpy.app.version < (2, 92, 0):
 | 
			
		||||
            try:
 | 
			
		||||
                lnx_module_file.unlink()
 | 
			
		||||
            except FileNotFoundError:
 | 
			
		||||
                pass 
 | 
			
		||||
        else:
 | 
			
		||||
            lnx_module_file.unlink(missing_ok=True)
 | 
			
		||||
        shutil.copy(Path(sdk_path) / 'leenkx.py', lnx_module_file)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -914,7 +928,10 @@ def restart_leenkx(context):
 | 
			
		||||
 | 
			
		||||
@persistent
 | 
			
		||||
def on_load_post(context):
 | 
			
		||||
    restart_leenkx(bpy.context)  # context is None, use bpy.context instead
 | 
			
		||||
    if bpy.context is not None:
 | 
			
		||||
        restart_leenkx(bpy.context)  # context is None, use bpy.context instead
 | 
			
		||||
    else:
 | 
			
		||||
        bpy.app.timers.register(lambda: restart_leenkx(bpy.context), first_interval=0.1)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def on_register_post():
 | 
			
		||||
 | 
			
		||||
@ -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;
 | 
			
		||||
@ -350,15 +363,21 @@ void main() {
 | 
			
		||||
#ifdef _CSharpen
 | 
			
		||||
	#ifdef _CPostprocess
 | 
			
		||||
		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
 | 
			
		||||
@ -475,10 +503,12 @@ fragColor.rgb = min(fragColor.rgb, 65504 * 0.5);
 | 
			
		||||
			} else {
 | 
			
		||||
				fragColor.rgb = mix(midLumColor, maxLumColor, luminance);
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
		} else {
 | 
			
		||||
			fragColor.rgb = vec3(0,1,0); //ERROR
 | 
			
		||||
		}
 | 
			
		||||
		} else if (PPComp4.x == 9){
 | 
			
		||||
			fragColor.rgb = tonemapAgXSimple(fragColor.rgb);
 | 
			
		||||
			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}
 | 
			
		||||
	#endif
 | 
			
		||||
 | 
			
		||||
#else
 | 
			
		||||
@ -498,6 +528,13 @@ fragColor.rgb = min(fragColor.rgb, 65504 * 0.5);
 | 
			
		||||
		fragColor.rgb = pow(fragColor.rgb, vec3(1.0 / 2.2)); // To gamma
 | 
			
		||||
		fragColor.rgb = clamp(fragColor.rgb, 0.0, 2.2);
 | 
			
		||||
	#endif
 | 
			
		||||
	#ifdef _CToneAgXSimple
 | 
			
		||||
		fragColor.rgb = tonemapAgXSimple(fragColor.rgb);
 | 
			
		||||
		fragColor.rgb = pow(fragColor.rgb, vec3(1.0 / 2.2)); // To gamma
 | 
			
		||||
	#endif
 | 
			
		||||
	#ifdef _CToneAgXFull
 | 
			
		||||
		fragColor.rgb = tonemapAgXFull(fragColor.rgb);
 | 
			
		||||
	#endif
 | 
			
		||||
	#ifdef _CToneNone
 | 
			
		||||
		fragColor.rgb = pow(fragColor.rgb, vec3(1.0 / 2.2)); // To gamma
 | 
			
		||||
	#endif
 | 
			
		||||
@ -614,4 +651,37 @@ fragColor.rgb = min(fragColor.rgb, 65504 * 0.5);
 | 
			
		||||
#ifdef _CLUT
 | 
			
		||||
	fragColor = LUTlookup(fragColor, lutTexture);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#ifdef _CDitheringBlueNoise
 | 
			
		||||
	const float ditherStrength = ditherStrengthValue / 255.0;
 | 
			
		||||
	float noise = ditherBlueNoiseStyle(gl_FragCoord.xy);
 | 
			
		||||
	float noiseOffset = (noise - 0.5) * ditherStrength;
 | 
			
		||||
	fragColor.rgb += noiseOffset;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef _CDitheringWhiteNoise
 | 
			
		||||
	const float ditherStrength = ditherStrengthValue / 255.0;
 | 
			
		||||
	float noise = ditherWhiteNoise(gl_FragCoord.xy);
 | 
			
		||||
	float noiseOffset = (noise - 0.5) * ditherStrength;
 | 
			
		||||
	fragColor.rgb += noiseOffset;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef _CDitheringOrderedBayer4x4
 | 
			
		||||
	const float ditherStrength = ditherStrengthValue / 255.0;
 | 
			
		||||
	float noise = ditherOrderedBayer4x4(ivec2(gl_FragCoord.xy));
 | 
			
		||||
	float noiseOffset = (noise - 0.5) * ditherStrength;
 | 
			
		||||
	fragColor.rgb += noiseOffset;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef _CDitheringOrderedBayer8x8
 | 
			
		||||
	const float ditherStrength = ditherStrengthValue / 255.0;
 | 
			
		||||
	float noise = ditherOrderedBayer8x8(ivec2(gl_FragCoord.xy));
 | 
			
		||||
	float noiseOffset = (noise - 0.5) * ditherStrength;
 | 
			
		||||
	fragColor.rgb += noiseOffset;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	//fragColor.rgb = clamp(fragColor.rgb, 0.0, 1.0);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -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"
 | 
			
		||||
 | 
			
		||||
@ -86,7 +86,7 @@ void main() {
 | 
			
		||||
 | 
			
		||||
    vec3 viewNormal = V3 * n;
 | 
			
		||||
    vec3 viewPos = getPosView(viewRay, d, cameraProj);
 | 
			
		||||
    vec3 refracted = refract(viewPos, viewNormal, 1.0 / ior);
 | 
			
		||||
    vec3 refracted = refract(normalize(viewPos), viewNormal, 1.0 / ior);
 | 
			
		||||
    hitCoord = viewPos;
 | 
			
		||||
 | 
			
		||||
    vec3 dir = refracted * (1.0 - rand(texCoord) * ss_refractionJitter * roughness) * 2.0;
 | 
			
		||||
 | 
			
		||||
@ -97,9 +97,9 @@ vec4 traceCone(const sampler3D voxels, const sampler3D voxelsSDF, const vec3 ori
 | 
			
		||||
 | 
			
		||||
	vec3 aniso_direction = -dir;
 | 
			
		||||
	vec3 face_offset = vec3(
 | 
			
		||||
		aniso_direction.x > 0.0 ? 0 : 1,
 | 
			
		||||
		aniso_direction.y > 0.0 ? 2 : 3,
 | 
			
		||||
		aniso_direction.z > 0.0 ? 4 : 5
 | 
			
		||||
		aniso_direction.x > 0.0 ? 0.0 : 1.0,
 | 
			
		||||
		aniso_direction.y > 0.0 ? 2.0 : 3.0,
 | 
			
		||||
		aniso_direction.z > 0.0 ? 4.0 : 5.0
 | 
			
		||||
	) / (6 + DIFFUSE_CONE_COUNT);
 | 
			
		||||
	vec3 direction_weight = abs(dir);
 | 
			
		||||
 | 
			
		||||
@ -201,9 +201,9 @@ float traceConeAO(const sampler3D voxels, const vec3 origin, const vec3 n, const
 | 
			
		||||
 | 
			
		||||
	vec3 aniso_direction = -dir;
 | 
			
		||||
	vec3 face_offset = vec3(
 | 
			
		||||
		aniso_direction.x > 0.0 ? 0 : 1,
 | 
			
		||||
		aniso_direction.y > 0.0 ? 2 : 3,
 | 
			
		||||
		aniso_direction.z > 0.0 ? 4 : 5
 | 
			
		||||
		aniso_direction.x > 0.0 ? 0.0 : 1.0,
 | 
			
		||||
		aniso_direction.y > 0.0 ? 2.0 : 3.0,
 | 
			
		||||
		aniso_direction.z > 0.0 ? 4.0 : 5.0
 | 
			
		||||
	) / (6 + DIFFUSE_CONE_COUNT);
 | 
			
		||||
	vec3 direction_weight = abs(dir);
 | 
			
		||||
 | 
			
		||||
@ -272,9 +272,9 @@ float traceConeShadow(const sampler3D voxels, const sampler3D voxelsSDF, const v
 | 
			
		||||
 | 
			
		||||
	vec3 aniso_direction = -dir;
 | 
			
		||||
	vec3 face_offset = vec3(
 | 
			
		||||
		aniso_direction.x > 0.0 ? 0 : 1,
 | 
			
		||||
		aniso_direction.y > 0.0 ? 2 : 3,
 | 
			
		||||
		aniso_direction.z > 0.0 ? 4 : 5
 | 
			
		||||
		aniso_direction.x > 0.0 ? 0.0 : 1.0,
 | 
			
		||||
		aniso_direction.y > 0.0 ? 2.0 : 3.0,
 | 
			
		||||
		aniso_direction.z > 0.0 ? 4.0 : 5.0
 | 
			
		||||
	) / (6 + DIFFUSE_CONE_COUNT);
 | 
			
		||||
	vec3 direction_weight = abs(dir);
 | 
			
		||||
	float coneCoefficient = 2.0 * tan(aperture * 0.5);
 | 
			
		||||
 | 
			
		||||
@ -87,6 +87,40 @@ float lpToDepth(vec3 lp, const vec2 lightProj) {
 | 
			
		||||
	return zcomp * 0.5 + 0.5;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifndef _ShadowMapAtlas
 | 
			
		||||
vec3 PCFCube(samplerCubeShadow shadowMapCube, samplerCube shadowMapCubeTransparent, vec3 lp, vec3 ml, float bias, vec2 lightProj, vec3 n, const bool transparent) {
 | 
			
		||||
    const float s = shadowmapCubePcfSize;
 | 
			
		||||
    float compare = lpToDepth(lp, lightProj) - bias * 1.5;
 | 
			
		||||
    ml = ml + n * bias * 20;
 | 
			
		||||
    #ifdef _InvY
 | 
			
		||||
    ml.y = -ml.y;
 | 
			
		||||
    #endif
 | 
			
		||||
    
 | 
			
		||||
    float shadowFactor = 0.0;
 | 
			
		||||
    shadowFactor = texture(shadowMapCube, vec4(ml, compare));
 | 
			
		||||
    shadowFactor += texture(shadowMapCube, vec4(ml + vec3(s, s, s), compare));
 | 
			
		||||
    shadowFactor += texture(shadowMapCube, vec4(ml + vec3(-s, s, s), compare));
 | 
			
		||||
    shadowFactor += texture(shadowMapCube, vec4(ml + vec3(s, -s, s), compare));
 | 
			
		||||
    shadowFactor += texture(shadowMapCube, vec4(ml + vec3(s, s, -s), compare));
 | 
			
		||||
    shadowFactor += texture(shadowMapCube, vec4(ml + vec3(-s, -s, s), compare));
 | 
			
		||||
    shadowFactor += texture(shadowMapCube, vec4(ml + vec3(s, -s, -s), compare));
 | 
			
		||||
    shadowFactor += texture(shadowMapCube, vec4(ml + vec3(-s, s, -s), compare));
 | 
			
		||||
    shadowFactor += texture(shadowMapCube, vec4(ml + vec3(-s, -s, -s), compare));
 | 
			
		||||
    shadowFactor /= 9.0;
 | 
			
		||||
    
 | 
			
		||||
    vec3 result = vec3(shadowFactor);
 | 
			
		||||
    
 | 
			
		||||
    if (transparent == false) {
 | 
			
		||||
        vec4 shadowmap_transparent = texture(shadowMapCubeTransparent, ml);
 | 
			
		||||
        if (shadowmap_transparent.a < compare)
 | 
			
		||||
            result *= shadowmap_transparent.rgb;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return result;
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef _ShadowMapAtlas
 | 
			
		||||
vec3 PCFCube(samplerCubeShadow shadowMapCube, samplerCube shadowMapCubeTransparent, const vec3 lp, vec3 ml, const float bias, const vec2 lightProj, const vec3 n, const bool transparent) {
 | 
			
		||||
	const float s = shadowmapCubePcfSize; // TODO: incorrect...
 | 
			
		||||
	float compare = lpToDepth(lp, lightProj) - bias * 1.5;
 | 
			
		||||
@ -115,7 +149,7 @@ vec3 PCFCube(samplerCubeShadow shadowMapCube, samplerCube shadowMapCubeTranspare
 | 
			
		||||
	return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifdef _ShadowMapAtlas
 | 
			
		||||
 | 
			
		||||
// transform "out-of-bounds" coordinates to the correct face/coordinate system
 | 
			
		||||
// https://www.khronos.org/opengl/wiki/File:CubeMapAxes.png
 | 
			
		||||
vec2 transformOffsetedUV(const int faceIndex, out int newFaceIndex, vec2 uv) {
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,3 @@
 | 
			
		||||
 | 
			
		||||
vec3 uncharted2Tonemap(const vec3 x) {
 | 
			
		||||
	const float A = 0.15;
 | 
			
		||||
	const float B = 0.50;
 | 
			
		||||
@ -12,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;
 | 
			
		||||
@ -36,3 +37,106 @@ vec3 acesFilm(const vec3 x) {
 | 
			
		||||
vec3 tonemapReinhard(const vec3 color) {
 | 
			
		||||
	return color / (color + vec3(1.0));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Blender AgX Implementation 
 | 
			
		||||
// Troy Sobotka https://github.com/sobotka/AgX
 | 
			
		||||
 | 
			
		||||
// AGX Simple
 | 
			
		||||
vec3 tonemapAgXSimple(vec3 x) {
 | 
			
		||||
	// TODO CORRECT AND OPTIMIZE
 | 
			
		||||
    x = max(x, vec3(0.0));
 | 
			
		||||
    float exposure = 0.6;
 | 
			
		||||
    x *= exposure;
 | 
			
		||||
    const vec3 AgX_A = vec3(0.92, 0.92, 0.72);
 | 
			
		||||
    const vec3 AgX_B = vec3(0.24, 0.24, 0.36);
 | 
			
		||||
    const vec3 AgX_C = vec3(0.92, 0.92, 0.72);
 | 
			
		||||
    const vec3 AgX_D = vec3(0.24, 0.24, 0.36);
 | 
			
		||||
    const vec3 AgX_E = vec3(0.08, 0.08, 0.12);
 | 
			
		||||
    const vec3 AgX_F = vec3(0.0);
 | 
			
		||||
    vec3 result = (x * (AgX_A * x + AgX_B)) / (x * (AgX_C * x + AgX_D) + AgX_E) + AgX_F;
 | 
			
		||||
    float luma = dot(result, vec3(0.2126, 0.7152, 0.0722));
 | 
			
		||||
    result = mix(vec3(luma), result, 0.6);
 | 
			
		||||
    return clamp(result, vec3(0.0), vec3(1.0));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// AGX Full Contrast Approx
 | 
			
		||||
vec3 agxDefaultContrastApprox(vec3 x) {
 | 
			
		||||
    vec3 x2 = x * x;
 | 
			
		||||
    vec3 x4 = x2 * x2;
 | 
			
		||||
    return + 15.5     * x4 * x2
 | 
			
		||||
           - 40.14    * x4 * x
 | 
			
		||||
           + 31.96    * x4
 | 
			
		||||
           - 6.868    * x2 * x
 | 
			
		||||
           + 0.4298   * x2
 | 
			
		||||
           + 0.1191   * x
 | 
			
		||||
           - 0.00232;
 | 
			
		||||
}
 | 
			
		||||
// AGX Full Look
 | 
			
		||||
vec3 agxLook(vec3 x, float strength) {
 | 
			
		||||
    const vec3 slope = vec3(1.0);
 | 
			
		||||
    const vec3 power = vec3(1.35);
 | 
			
		||||
    const vec3 sat = vec3(1.4);
 | 
			
		||||
    vec3 lw = vec3(0.2126, 0.7152, 0.0722);
 | 
			
		||||
    float luma = dot(x, lw);
 | 
			
		||||
    return pow(x * slope, power) * sat - (pow(luma * slope, power) * (sat - 1.0));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// AGX Full
 | 
			
		||||
vec3 tonemapAgXFull(vec3 x) {
 | 
			
		||||
    // x *= 2.0 * (1.0/0.8); // Brightness scale to match Blender's default
 | 
			
		||||
    x = clamp(x, 0.0, 65504.0);
 | 
			
		||||
    x = log2(x + 1.0);
 | 
			
		||||
    x = agxDefaultContrastApprox(clamp(x * 0.5 - 10.5, -12.0, 12.0));    
 | 
			
		||||
    x = mix(x, agxLook(x, 0.5), 0.5);
 | 
			
		||||
    x = clamp(x, 0.0, 1.0);
 | 
			
		||||
    return pow(x, vec3(1.0/2.2)); 
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// Interleaved Gradient Noise (Pseudo-random, AKA Blue Noise style)
 | 
			
		||||
// Based on http://momentsingraphics.de/BlueNoise.html
 | 
			
		||||
float ditherBlueNoiseStyle(vec2 p) {
 | 
			
		||||
    return fract(sin(dot(p.xy, vec2(12.9898, 78.233))) * 43758.5453123);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// White Noise Dithering
 | 
			
		||||
float ditherWhiteNoise(vec2 p) {
 | 
			
		||||
    return fract(sin(dot(p, vec2(12.9898, 4.1414))) * 43758.5453);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Ordered Dithering (4x4 Bayer Matrix)
 | 
			
		||||
float ditherOrderedBayer4x4(ivec2 p) {
 | 
			
		||||
    const float bayer[16] = float[16](
 | 
			
		||||
         0.0,  8.0,  2.0, 10.0,
 | 
			
		||||
        12.0,  4.0, 14.0,  6.0,
 | 
			
		||||
         3.0, 11.0,  1.0,  9.0,
 | 
			
		||||
        15.0,  7.0, 13.0,  5.0
 | 
			
		||||
    );
 | 
			
		||||
    int index = (p.x % 4) * 4 + (p.y % 4);
 | 
			
		||||
    return bayer[index] / 16.0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Ordered Dithering (8x8 Bayer Matrix)
 | 
			
		||||
float ditherOrderedBayer8x8(ivec2 p) {
 | 
			
		||||
    const float bayer8x8[64] = float[64](
 | 
			
		||||
         0.0, 32.0,  8.0, 40.0,  2.0, 34.0, 10.0, 42.0,
 | 
			
		||||
        48.0, 16.0, 56.0, 24.0, 50.0, 18.0, 58.0, 26.0,
 | 
			
		||||
        12.0, 44.0,  4.0, 36.0, 14.0, 46.0,  6.0, 38.0,
 | 
			
		||||
        60.0, 28.0, 52.0, 20.0, 62.0, 30.0, 54.0, 22.0,
 | 
			
		||||
         3.0, 35.0, 11.0, 43.0,  1.0, 33.0,  9.0, 41.0,
 | 
			
		||||
        51.0, 19.0, 59.0, 27.0, 49.0, 17.0, 57.0, 25.0,
 | 
			
		||||
        15.0, 47.0,  7.0, 39.0, 13.0, 45.0,  5.0, 37.0,
 | 
			
		||||
        63.0, 31.0, 55.0, 23.0, 61.0, 29.0, 53.0, 21.0
 | 
			
		||||
    );
 | 
			
		||||
    int index = (p.x % 8) * 8 + (p.y % 8);
 | 
			
		||||
    return bayer8x8[index] / 64.0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
//vec3 applyDither(vec3 color, vec2 screenCoord) {
 | 
			
		||||
//    float quantizationLevels = 255.0;
 | 
			
		||||
//    float noise = randomDither(screenCoord);
 | 
			
		||||
//    float noiseOffset = (noise - 0.5) / quantizationLevels;
 | 
			
		||||
//    vec3 ditheredColor = color + noiseOffset;
 | 
			
		||||
//    return clamp(ditheredColor, 0.0, 1.0);
 | 
			
		||||
//}
 | 
			
		||||
 | 
			
		||||
@ -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": [],
 | 
			
		||||
 | 
			
		||||
@ -48,7 +48,7 @@ void main() {
 | 
			
		||||
	const vec2 pixel = gl_GlobalInvocationID.xy;
 | 
			
		||||
	vec2 uv = (pixel + 0.5) / postprocess_resolution;
 | 
			
		||||
	#ifdef _InvY
 | 
			
		||||
	uv.y = 1.0 - uv.y
 | 
			
		||||
	uv.y = 1.0 - uv.y;
 | 
			
		||||
	#endif
 | 
			
		||||
 | 
			
		||||
	float depth = textureLod(gbufferD, uv, 0.0).r * 2.0 - 1.0;
 | 
			
		||||
 | 
			
		||||
@ -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,24 @@ class App {
 | 
			
		||||
 | 
			
		||||
	static function update() {
 | 
			
		||||
		if (Scene.active == null || !Scene.active.ready) return;
 | 
			
		||||
		
 | 
			
		||||
		iron.system.Time.update();
 | 
			
		||||
		
 | 
			
		||||
		if (lastw == -1) {
 | 
			
		||||
			lastw = App.w();
 | 
			
		||||
			lasth = App.h();
 | 
			
		||||
		}
 | 
			
		||||
		if (lastw != App.w() || lasth != App.h()) {
 | 
			
		||||
			if (onResize != null) onResize();
 | 
			
		||||
			else {
 | 
			
		||||
				if (Scene.active != null && Scene.active.camera != null) {
 | 
			
		||||
					Scene.active.camera.buildProjection();
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		lastw = App.w();
 | 
			
		||||
		lasth = App.h();
 | 
			
		||||
		
 | 
			
		||||
		if (pauseUpdates) return;
 | 
			
		||||
 | 
			
		||||
		#if lnx_debug
 | 
			
		||||
@ -56,6 +78,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) {
 | 
			
		||||
@ -84,29 +114,13 @@ class App {
 | 
			
		||||
		for (cb in endFrameCallbacks) cb();
 | 
			
		||||
		updateTime = kha.Scheduler.realTime() - startTime;
 | 
			
		||||
		#end
 | 
			
		||||
 | 
			
		||||
		// Rebuild projection on window resize
 | 
			
		||||
		if (lastw == -1) {
 | 
			
		||||
			lastw = App.w();
 | 
			
		||||
			lasth = App.h();
 | 
			
		||||
		}
 | 
			
		||||
		if (lastw != App.w() || lasth != App.h()) {
 | 
			
		||||
			if (onResize != null) onResize();
 | 
			
		||||
			else {
 | 
			
		||||
				if (Scene.active != null && Scene.active.camera != null) {
 | 
			
		||||
					Scene.active.camera.buildProjection();
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		lastw = App.w();
 | 
			
		||||
		lasth = App.h();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	static function render(frames: Array<kha.Framebuffer>) {
 | 
			
		||||
		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);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@ -331,15 +331,18 @@ class RenderPath {
 | 
			
		||||
		});
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public static function sortMeshesShader(meshes: Array<MeshObject>) {
 | 
			
		||||
	public static function sortMeshesIndex(meshes: Array<MeshObject>) {
 | 
			
		||||
		meshes.sort(function(a, b): Int {
 | 
			
		||||
			#if rp_depth_texture
 | 
			
		||||
			var depthDiff = boolToInt(a.depthRead) - boolToInt(b.depthRead);
 | 
			
		||||
			if (depthDiff != 0) return depthDiff;
 | 
			
		||||
			#end
 | 
			
		||||
 | 
			
		||||
			return a.materials[0].name >= b.materials[0].name ? 1 : -1;
 | 
			
		||||
		});
 | 
			
		||||
			if (a.data.sortingIndex != b.data.sortingIndex) {
 | 
			
		||||
				return a.data.sortingIndex > b.data.sortingIndex ? 1 : -1;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			return a.data.name >= b.data.name ? 1 : -1;		});
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public function drawMeshes(context: String) {
 | 
			
		||||
@ -399,7 +402,7 @@ class RenderPath {
 | 
			
		||||
			#if lnx_batch
 | 
			
		||||
			sortMeshesDistance(Scene.active.meshBatch.nonBatched);
 | 
			
		||||
			#else
 | 
			
		||||
			drawOrder == DrawOrder.Shader ? sortMeshesShader(meshes) : sortMeshesDistance(meshes);
 | 
			
		||||
			drawOrder == DrawOrder.Index ? sortMeshesIndex(meshes) : sortMeshesDistance(meshes);
 | 
			
		||||
			#end
 | 
			
		||||
			meshesSorted = true;
 | 
			
		||||
		}
 | 
			
		||||
@ -518,12 +521,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
 | 
			
		||||
@ -882,6 +917,6 @@ class CachedShaderContext {
 | 
			
		||||
 | 
			
		||||
@:enum abstract DrawOrder(Int) from Int {
 | 
			
		||||
	var Distance = 0; // Early-z
 | 
			
		||||
	var Shader = 1; // Less state changes
 | 
			
		||||
	var Index = 1; // Less state changes
 | 
			
		||||
	// var Mix = 2; // Distance buckets sorted by shader
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -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);
 | 
			
		||||
		});
 | 
			
		||||
	}
 | 
			
		||||
@ -881,8 +887,12 @@ class Scene {
 | 
			
		||||
						var ptype: String = t.props[i * 3 + 1];
 | 
			
		||||
						var pval: Dynamic = t.props[i * 3 + 2];
 | 
			
		||||
 | 
			
		||||
						if (StringTools.endsWith(ptype, "Object") && pval != "") {
 | 
			
		||||
						if (StringTools.endsWith(ptype, "Object") && pval != "" && pval != null) {
 | 
			
		||||
							Reflect.setProperty(traitInst, pname, Scene.active.getChild(pval));
 | 
			
		||||
						} else if (ptype == "TSceneFormat" && pval != "") {
 | 
			
		||||
							Data.getSceneRaw(pval, function (r: TSceneFormat) {
 | 
			
		||||
								Reflect.setProperty(traitInst, pname, r);
 | 
			
		||||
							});
 | 
			
		||||
						}
 | 
			
		||||
						else {
 | 
			
		||||
							switch (ptype) {
 | 
			
		||||
 | 
			
		||||
@ -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.
 | 
			
		||||
    **/
 | 
			
		||||
 | 
			
		||||
@ -9,6 +9,7 @@ import iron.data.SceneFormat;
 | 
			
		||||
class MeshData {
 | 
			
		||||
 | 
			
		||||
	public var name: String;
 | 
			
		||||
	public var sortingIndex: Int;
 | 
			
		||||
	public var raw: TMeshData;
 | 
			
		||||
	public var format: TSceneFormat;
 | 
			
		||||
	public var geom: Geometry;
 | 
			
		||||
@ -23,6 +24,7 @@ class MeshData {
 | 
			
		||||
	public function new(raw: TMeshData, done: MeshData->Void) {
 | 
			
		||||
		this.raw = raw;
 | 
			
		||||
		this.name = raw.name;
 | 
			
		||||
		this.sortingIndex = raw.sorting_index;
 | 
			
		||||
		
 | 
			
		||||
		if (raw.scale_pos != null) scalePos = raw.scale_pos;
 | 
			
		||||
		if (raw.scale_tex != null) scaleTex = raw.scale_tex;
 | 
			
		||||
 | 
			
		||||
@ -49,6 +49,7 @@ typedef TMeshData = {
 | 
			
		||||
@:structInit class TMeshData {
 | 
			
		||||
#end
 | 
			
		||||
	public var name: String;
 | 
			
		||||
	public var sorting_index: Int;
 | 
			
		||||
	public var vertex_arrays: Array<TVertexArray>;
 | 
			
		||||
	public var index_arrays: Array<TIndexArray>;
 | 
			
		||||
	@:optional public var dynamic_usage: Null<Bool>;
 | 
			
		||||
@ -222,6 +223,7 @@ typedef TShaderData = {
 | 
			
		||||
@:structInit class TShaderData {
 | 
			
		||||
#end
 | 
			
		||||
	public var name: String;
 | 
			
		||||
	public var next_pass: String;
 | 
			
		||||
	public var contexts: Array<TShaderContext>;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -392,6 +394,9 @@ typedef TParticleData = {
 | 
			
		||||
#end
 | 
			
		||||
	public var name: String;
 | 
			
		||||
	public var type: Int; // 0 - Emitter, Hair
 | 
			
		||||
	public var auto_start: Bool;
 | 
			
		||||
	public var dynamic_emitter: Bool;
 | 
			
		||||
	public var is_unique: Bool;
 | 
			
		||||
	public var loop: Bool;
 | 
			
		||||
	public var count: Int;
 | 
			
		||||
	public var frame_start: FastFloat;
 | 
			
		||||
@ -439,6 +444,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>;
 | 
			
		||||
 | 
			
		||||
@ -22,6 +22,7 @@ using StringTools;
 | 
			
		||||
class ShaderData {
 | 
			
		||||
 | 
			
		||||
	public var name: String;
 | 
			
		||||
	public var nextPass: String;
 | 
			
		||||
	public var raw: TShaderData;
 | 
			
		||||
	public var contexts: Array<ShaderContext> = [];
 | 
			
		||||
 | 
			
		||||
@ -33,6 +34,7 @@ class ShaderData {
 | 
			
		||||
	public function new(raw: TShaderData, done: ShaderData->Void, overrideContext: TShaderOverride = null) {
 | 
			
		||||
		this.raw = raw;
 | 
			
		||||
		this.name = raw.name;
 | 
			
		||||
		this.nextPass = raw.next_pass;
 | 
			
		||||
 | 
			
		||||
		for (c in raw.contexts) contexts.push(null);
 | 
			
		||||
		var contextsLoaded = 0;
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										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
									
								
							
							
						
						@ -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
									
								
							
							
						
						@ -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
									
								
							
							
						
						@ -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);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@ -66,12 +66,32 @@ class Quat {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public inline function fromAxisAngle(axis: Vec4, angle: FastFloat): Quat {
 | 
			
		||||
		var s: FastFloat = Math.sin(angle * 0.5);
 | 
			
		||||
		x = axis.x * s;
 | 
			
		||||
		y = axis.y * s;
 | 
			
		||||
		z = axis.z * s;
 | 
			
		||||
		w = Math.cos(angle * 0.5);
 | 
			
		||||
		return normalize();
 | 
			
		||||
		//var s: FastFloat = Math.sin(angle * 0.5);
 | 
			
		||||
		//x = axis.x * s;
 | 
			
		||||
		//y = axis.y * s;
 | 
			
		||||
		//z = axis.z * s;
 | 
			
		||||
		//w = Math.cos(angle * 0.5);
 | 
			
		||||
		//return normalize();
 | 
			
		||||
		// Normalize the axis vector first
 | 
			
		||||
		var axisLen = Math.sqrt(axis.x * axis.x + axis.y * axis.y + axis.z * axis.z);
 | 
			
		||||
		if (axisLen > 0.00001) {
 | 
			
		||||
			var aL = 1.0 / axisLen;
 | 
			
		||||
			var nX = axis.x * aL;
 | 
			
		||||
			var nY = axis.y * aL;
 | 
			
		||||
			var nZ = axis.z * aL;
 | 
			
		||||
			var halfAngle = angle * 0.5;
 | 
			
		||||
			var s: FastFloat = Math.sin(halfAngle);
 | 
			
		||||
			x = nX * s;
 | 
			
		||||
			y = nY * s;
 | 
			
		||||
			z = nZ * s;
 | 
			
		||||
			w = Math.cos(halfAngle);
 | 
			
		||||
		} else {
 | 
			
		||||
			x = 0.0;
 | 
			
		||||
			y = 0.0;
 | 
			
		||||
			z = 0.0;
 | 
			
		||||
			w = 1.0;
 | 
			
		||||
		}
 | 
			
		||||
		return this;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public inline function toAxisAngle(axis: Vec4): FastFloat {
 | 
			
		||||
@ -379,17 +399,33 @@ class Quat {
 | 
			
		||||
		@return	This quaternion.
 | 
			
		||||
	**/
 | 
			
		||||
	public inline function fromEulerOrdered(e: Vec4, order: String): Quat {
 | 
			
		||||
		var c1 = Math.cos(e.x / 2);
 | 
			
		||||
		var c2 = Math.cos(e.y / 2);
 | 
			
		||||
		var c3 = Math.cos(e.z / 2);
 | 
			
		||||
		var s1 = Math.sin(e.x / 2);
 | 
			
		||||
		var s2 = Math.sin(e.y / 2);
 | 
			
		||||
		var s3 = Math.sin(e.z / 2);
 | 
			
		||||
		
 | 
			
		||||
		var mappedAngles = new Vec4();
 | 
			
		||||
		switch (order) {
 | 
			
		||||
			case "XYZ":
 | 
			
		||||
				mappedAngles.set(e.x, e.y, e.z);
 | 
			
		||||
			case "XZY":
 | 
			
		||||
				mappedAngles.set(e.x, e.z, e.y);
 | 
			
		||||
			case "YXZ": 
 | 
			
		||||
				mappedAngles.set(e.y, e.x, e.z);
 | 
			
		||||
			case "YZX": 
 | 
			
		||||
				mappedAngles.set(e.y, e.z, e.x);
 | 
			
		||||
			case "ZXY": 
 | 
			
		||||
				mappedAngles.set(e.z, e.x, e.y);
 | 
			
		||||
			case "ZYX": 
 | 
			
		||||
				mappedAngles.set(e.z, e.y, e.x);
 | 
			
		||||
		}
 | 
			
		||||
		var c1 = Math.cos(mappedAngles.x / 2);
 | 
			
		||||
		var c2 = Math.cos(mappedAngles.y / 2);
 | 
			
		||||
		var c3 = Math.cos(mappedAngles.z / 2);
 | 
			
		||||
		var s1 = Math.sin(mappedAngles.x / 2);
 | 
			
		||||
		var s2 = Math.sin(mappedAngles.y / 2);
 | 
			
		||||
		var s3 = Math.sin(mappedAngles.z / 2);
 | 
			
		||||
		var qx = new Quat(s1, 0, 0, c1);
 | 
			
		||||
		var qy = new Quat(0, s2, 0, c2);
 | 
			
		||||
		var qz = new Quat(0, 0, s3, c3);
 | 
			
		||||
 | 
			
		||||
		// Original multiplication sequence (implements reverse of 'order')
 | 
			
		||||
		if (order.charAt(2) == 'X')
 | 
			
		||||
			this.setFrom(qx);
 | 
			
		||||
		else if (order.charAt(2) == 'Y')
 | 
			
		||||
@ -409,6 +445,12 @@ class Quat {
 | 
			
		||||
		else
 | 
			
		||||
			this.mult(qz);
 | 
			
		||||
 | 
			
		||||
		// TO DO quick fix somethings wrong..
 | 
			
		||||
		this.x = -this.x;
 | 
			
		||||
		this.y = -this.y;
 | 
			
		||||
		this.z = -this.z;
 | 
			
		||||
		this.w = -this.w;
 | 
			
		||||
		
 | 
			
		||||
		return this;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -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;
 | 
			
		||||
@ -411,12 +419,23 @@ class ActionSampler {
 | 
			
		||||
	 */
 | 
			
		||||
	public inline function setBoneAction(actionData: Array<TObj>) {
 | 
			
		||||
		this.actionData = actionData;
 | 
			
		||||
		this.totalFrames = actionData[0].anim.tracks[0].frames.length;
 | 
			
		||||
		if(actionData[0].anim.root_motion_pos) this.rootMotionPos = true;
 | 
			
		||||
		if(actionData[0].anim.root_motion_rot) this.rootMotionRot = true;
 | 
			
		||||
		if (actionData != null && actionData.length > 0 && actionData[0] != null && actionData[0].anim != null) {
 | 
			
		||||
			if (actionData[0].anim.tracks != null && actionData[0].anim.tracks.length > 0) {
 | 
			
		||||
				this.totalFrames = actionData[0].anim.tracks[0].frames.length;
 | 
			
		||||
			}
 | 
			
		||||
			else {
 | 
			
		||||
				this.totalFrames = 0;
 | 
			
		||||
			}
 | 
			
		||||
			if(actionData[0].anim.root_motion_pos) this.rootMotionPos = true;
 | 
			
		||||
			if(actionData[0].anim.root_motion_rot) this.rootMotionRot = true;
 | 
			
		||||
		}
 | 
			
		||||
		else {
 | 
			
		||||
			this.totalFrames = 0;
 | 
			
		||||
		}
 | 
			
		||||
		actionDataInit = true;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Cache raw object data for object animation.
 | 
			
		||||
	 * @param actionData Raw object data.
 | 
			
		||||
 | 
			
		||||
@ -31,11 +31,21 @@ class CameraObject extends Object {
 | 
			
		||||
	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,7 +155,12 @@ class LightObject extends Object {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public function setCascade(camera: CameraObject, cascade: Int) {
 | 
			
		||||
 | 
			
		||||
		#if lnx_vr
 | 
			
		||||
		m.setFrom(camera.leftV);
 | 
			
		||||
		#else
 | 
			
		||||
		m.setFrom(camera.V);
 | 
			
		||||
		#end
 | 
			
		||||
		
 | 
			
		||||
		#if lnx_csm
 | 
			
		||||
		if (camSlicedP == null) {
 | 
			
		||||
 | 
			
		||||
@ -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;
 | 
			
		||||
@ -297,6 +302,10 @@ class MeshObject extends Object {
 | 
			
		||||
 | 
			
		||||
		// Render mesh
 | 
			
		||||
		var ldata = lod.data;
 | 
			
		||||
		
 | 
			
		||||
		// Next pass rendering first (inverse order)
 | 
			
		||||
		renderNextPass(g, context, bindParams, lod);
 | 
			
		||||
		
 | 
			
		||||
		for (i in 0...ldata.geom.indexBuffers.length) {
 | 
			
		||||
 | 
			
		||||
			var mi = ldata.geom.materialIndices[i];
 | 
			
		||||
@ -400,4 +409,85 @@ class MeshObject extends Object {
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	
 | 
			
		||||
	function renderNextPass(g: Graphics, context: String, bindParams: Array<String>, lod: MeshObject) {
 | 
			
		||||
		var ldata = lod.data;
 | 
			
		||||
		for (i in 0...ldata.geom.indexBuffers.length) {
 | 
			
		||||
			var mi = ldata.geom.materialIndices[i];
 | 
			
		||||
			if (mi >= materials.length) continue;
 | 
			
		||||
 | 
			
		||||
			var currentMaterial: MaterialData = materials[mi];
 | 
			
		||||
			if (currentMaterial == null || currentMaterial.shader == null) continue;
 | 
			
		||||
 | 
			
		||||
			var nextPassName: String = currentMaterial.shader.nextPass;
 | 
			
		||||
			if (nextPassName == null || nextPassName == "") continue;
 | 
			
		||||
 | 
			
		||||
			var nextMaterial: MaterialData = null;
 | 
			
		||||
			for (mat in materials) {
 | 
			
		||||
				// First try exact match
 | 
			
		||||
				if (mat.name == nextPassName) {
 | 
			
		||||
					nextMaterial = mat;
 | 
			
		||||
					break;
 | 
			
		||||
				}
 | 
			
		||||
				// If no exact match, try to match base name for linked materials
 | 
			
		||||
				if (mat.name.indexOf("_") > 0 && mat.name.substr(mat.name.length - 6) == ".blend") {
 | 
			
		||||
					var baseName = mat.name.substring(0, mat.name.indexOf("_"));
 | 
			
		||||
					if (baseName == nextPassName) {
 | 
			
		||||
						nextMaterial = mat;
 | 
			
		||||
						break;
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if (nextMaterial == null) continue;
 | 
			
		||||
 | 
			
		||||
			var nextMaterialContext: MaterialContext = null;
 | 
			
		||||
			var nextShaderContext: ShaderContext = null;
 | 
			
		||||
 | 
			
		||||
			for (j in 0...nextMaterial.raw.contexts.length) {
 | 
			
		||||
				if (nextMaterial.raw.contexts[j].name.substr(0, context.length) == context) {
 | 
			
		||||
					nextMaterialContext = nextMaterial.contexts[j];
 | 
			
		||||
					nextShaderContext = nextMaterial.shader.getContext(context);
 | 
			
		||||
					break;
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if (nextShaderContext == null) continue;
 | 
			
		||||
			if (skipContext(context, nextMaterial)) continue;
 | 
			
		||||
 | 
			
		||||
			var elems = nextShaderContext.raw.vertex_elements;
 | 
			
		||||
 | 
			
		||||
			// Uniforms
 | 
			
		||||
			if (nextShaderContext.pipeState != lastPipeline) {
 | 
			
		||||
				g.setPipeline(nextShaderContext.pipeState);
 | 
			
		||||
				lastPipeline = nextShaderContext.pipeState;
 | 
			
		||||
			}
 | 
			
		||||
			Uniforms.setContextConstants(g, nextShaderContext, bindParams);
 | 
			
		||||
			Uniforms.setObjectConstants(g, nextShaderContext, this);
 | 
			
		||||
			Uniforms.setMaterialConstants(g, nextShaderContext, nextMaterialContext);
 | 
			
		||||
 | 
			
		||||
			// VB / IB
 | 
			
		||||
			#if lnx_deinterleaved
 | 
			
		||||
			g.setVertexBuffers(ldata.geom.get(elems));
 | 
			
		||||
			#else
 | 
			
		||||
			if (ldata.geom.instancedVB != null) {
 | 
			
		||||
				g.setVertexBuffers([ldata.geom.get(elems), ldata.geom.instancedVB]);
 | 
			
		||||
			}
 | 
			
		||||
			else {
 | 
			
		||||
				g.setVertexBuffer(ldata.geom.get(elems));
 | 
			
		||||
			}
 | 
			
		||||
			#end
 | 
			
		||||
 | 
			
		||||
			g.setIndexBuffer(ldata.geom.indexBuffers[i]);
 | 
			
		||||
 | 
			
		||||
			// Draw next pass for this specific geometry section
 | 
			
		||||
			if (ldata.geom.instanced) {
 | 
			
		||||
				g.drawIndexedVerticesInstanced(ldata.geom.instanceCount, ldata.geom.start, ldata.geom.count);
 | 
			
		||||
			}
 | 
			
		||||
			else {
 | 
			
		||||
				g.drawIndexedVertices(ldata.geom.start, ldata.geom.count);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -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;
 | 
			
		||||
 | 
			
		||||
@ -24,6 +24,9 @@ class ObjectAnimation extends Animation {
 | 
			
		||||
 | 
			
		||||
	public var transformMap: Map<String, FastFloat>;
 | 
			
		||||
 | 
			
		||||
	var defaultSampler: ActionSampler = null;
 | 
			
		||||
	static inline var DEFAULT_SAMPLER_ID = "__object_default_action__";
 | 
			
		||||
 | 
			
		||||
	public static var trackNames: Array<String> = [	"xloc", "yloc", "zloc",
 | 
			
		||||
											   		"xrot", "yrot", "zrot",
 | 
			
		||||
											   		"qwrot", "qxrot", "qyrot", "qzrot",
 | 
			
		||||
@ -39,7 +42,6 @@ class ObjectAnimation extends Animation {
 | 
			
		||||
		isSkinned = false;
 | 
			
		||||
		super();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	function getAction(action: String): TObj {
 | 
			
		||||
		for (a in oactions) if (a != null && a.objects[0].name == action) return a.objects[0];
 | 
			
		||||
		return null;
 | 
			
		||||
@ -47,10 +49,29 @@ class ObjectAnimation extends Animation {
 | 
			
		||||
 | 
			
		||||
	override public function play(action = "", onComplete: Void->Void = null, blendTime = 0.0, speed = 1.0, loop = true) {
 | 
			
		||||
		super.play(action, onComplete, blendTime, speed, loop);
 | 
			
		||||
		if (this.action == "" && oactions[0] != null) this.action = oactions[0].objects[0].name;
 | 
			
		||||
		if (this.action == "" && oactions != null && oactions[0] != null){
 | 
			
		||||
			this.action = oactions[0].objects[0].name;
 | 
			
		||||
		}
 | 
			
		||||
		oaction = getAction(this.action);
 | 
			
		||||
		if (oaction != null) {
 | 
			
		||||
			isSampled = oaction.sampled != null && oaction.sampled;
 | 
			
		||||
			if (defaultSampler != null) {
 | 
			
		||||
				deRegisterAction(DEFAULT_SAMPLER_ID);
 | 
			
		||||
			}
 | 
			
		||||
			var callbacks = onComplete != null ? [onComplete] : null;
 | 
			
		||||
			defaultSampler = new ActionSampler(this.action, speed, loop, false, callbacks);
 | 
			
		||||
			registerAction(DEFAULT_SAMPLER_ID, defaultSampler);
 | 
			
		||||
			if (paused) defaultSampler.paused = true;
 | 
			
		||||
			updateAnimation = function(map: Map<String, FastFloat>) {
 | 
			
		||||
				sampleAction(defaultSampler, map);
 | 
			
		||||
			};
 | 
			
		||||
		}
 | 
			
		||||
		else {
 | 
			
		||||
			if (defaultSampler != null) {
 | 
			
		||||
				deRegisterAction(DEFAULT_SAMPLER_ID);
 | 
			
		||||
				defaultSampler = null;
 | 
			
		||||
			}
 | 
			
		||||
			updateAnimation = null;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@ -61,12 +82,13 @@ class ObjectAnimation extends Animation {
 | 
			
		||||
		Animation.beginProfile();
 | 
			
		||||
		#end
 | 
			
		||||
 | 
			
		||||
		if(transformMap == null) transformMap = new Map();
 | 
			
		||||
		if (transformMap == null) transformMap = new Map();
 | 
			
		||||
		transformMap = initTransformMap();
 | 
			
		||||
 | 
			
		||||
		super.update(delta);
 | 
			
		||||
		if (defaultSampler != null) defaultSampler.paused = paused;
 | 
			
		||||
		if (paused) return;
 | 
			
		||||
		if(updateAnimation == null) return;
 | 
			
		||||
		if (updateAnimation == null) return;
 | 
			
		||||
		if (!isSkinned) updateObjectAnimation();
 | 
			
		||||
 | 
			
		||||
		#if lnx_debug
 | 
			
		||||
 | 
			
		||||
@ -2,11 +2,14 @@ package iron.object;
 | 
			
		||||
 | 
			
		||||
#if lnx_particles
 | 
			
		||||
 | 
			
		||||
import kha.FastFloat;
 | 
			
		||||
import kha.graphics4.Usage;
 | 
			
		||||
import kha.arrays.Float32Array;
 | 
			
		||||
import iron.data.Data;
 | 
			
		||||
import iron.data.ParticleData;
 | 
			
		||||
import iron.data.SceneFormat;
 | 
			
		||||
import iron.data.Geometry;
 | 
			
		||||
import iron.data.MeshData;
 | 
			
		||||
import iron.system.Time;
 | 
			
		||||
import iron.math.Mat4;
 | 
			
		||||
import iron.math.Quat;
 | 
			
		||||
@ -16,10 +19,13 @@ import iron.math.Vec4;
 | 
			
		||||
class ParticleSystem {
 | 
			
		||||
	public var data: ParticleData;
 | 
			
		||||
	public var speed = 1.0;
 | 
			
		||||
	public var dynamicEmitter: Bool = true;
 | 
			
		||||
	var currentSpeed = 0.0;
 | 
			
		||||
	var particles: Array<Particle>;
 | 
			
		||||
	var ready: Bool;
 | 
			
		||||
	var frameRate = 24;
 | 
			
		||||
	var lifetime = 0.0;
 | 
			
		||||
	var looptime = 0.0;
 | 
			
		||||
	var animtime = 0.0;
 | 
			
		||||
	var time = 0.0;
 | 
			
		||||
	var spawnRate = 0.0;
 | 
			
		||||
@ -47,13 +53,30 @@ class ParticleSystem {
 | 
			
		||||
	var ownerRot = new Quat();
 | 
			
		||||
	var ownerScl = new Vec4();
 | 
			
		||||
	
 | 
			
		||||
	var random = 0.0;
 | 
			
		||||
 | 
			
		||||
	var tmpV4 = new Vec4();
 | 
			
		||||
 | 
			
		||||
	var instancedData: Float32Array = null;
 | 
			
		||||
	var lastSpawnedCount: Int = 0;
 | 
			
		||||
	var hasUniqueGeom: Bool = false; 
 | 
			
		||||
 | 
			
		||||
	public function new(sceneName: String, pref: TParticleReference) {
 | 
			
		||||
		seed = pref.seed;
 | 
			
		||||
		currentSpeed = speed;
 | 
			
		||||
		speed = 0;
 | 
			
		||||
		particles = [];
 | 
			
		||||
		ready = false;
 | 
			
		||||
		
 | 
			
		||||
		Data.getParticle(sceneName, pref.particle, function(b: ParticleData) {
 | 
			
		||||
			data = b;
 | 
			
		||||
			r = data.raw;
 | 
			
		||||
			var dyn: Null<Bool> = r.dynamic_emitter;
 | 
			
		||||
			var dynValue: Bool = true;
 | 
			
		||||
			if (dyn != null) {
 | 
			
		||||
				dynValue = dyn;
 | 
			
		||||
			}
 | 
			
		||||
			dynamicEmitter = dynValue;
 | 
			
		||||
			if (Scene.active.raw.gravity != null) {
 | 
			
		||||
				gx = Scene.active.raw.gravity[0] * r.weight_gravity;
 | 
			
		||||
				gy = Scene.active.raw.gravity[1] * r.weight_gravity;
 | 
			
		||||
@ -64,32 +87,73 @@ 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) particles.push(new Particle(i));
 | 
			
		||||
 | 
			
		||||
			for (i in 0...r.count) {
 | 
			
		||||
				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;
 | 
			
		||||
		lastSpawnedCount = 0;
 | 
			
		||||
		instancedData = null;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	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);
 | 
			
		||||
		object.transform.loc = ownerLoc;
 | 
			
		||||
		object.transform.rot = ownerRot;
 | 
			
		||||
		if (dynamicEmitter) {
 | 
			
		||||
			object.transform.loc.x = 0; object.transform.loc.y = 0; object.transform.loc.z = 0;
 | 
			
		||||
			object.transform.rot = new Quat();
 | 
			
		||||
		} else {
 | 
			
		||||
			object.transform.loc = ownerLoc;
 | 
			
		||||
			object.transform.rot = ownerRot;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Set particle size per particle system
 | 
			
		||||
		object.transform.scale = new Vec4(r.particle_size, r.particle_size, r.particle_size, 1);
 | 
			
		||||
@ -108,16 +172,25 @@ class ParticleSystem {
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Animate
 | 
			
		||||
		time += Time.realDelta * 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();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (lap > prevLap && r.loop) {
 | 
			
		||||
			lastSpawnedCount = 0;
 | 
			
		||||
		}
 | 
			
		||||
		
 | 
			
		||||
		updateGpu(object, owner);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public function getData(): Mat4 {
 | 
			
		||||
		var hair = r.type == 1;
 | 
			
		||||
		// Store loop flag in the sign: positive -> loop, negative -> no loop
 | 
			
		||||
		m._00 = r.loop ? animtime : -animtime;
 | 
			
		||||
		m._01 = hair ? 1 / particles.length : spawnRate;
 | 
			
		||||
		m._02 = hair ? 1 : lifetime;
 | 
			
		||||
@ -126,9 +199,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;
 | 
			
		||||
@ -137,9 +210,30 @@ class ParticleSystem {
 | 
			
		||||
		return m;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public function getSizeRandom(): FastFloat {
 | 
			
		||||
		return r.size_random;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public inline function getRandom(): FastFloat {
 | 
			
		||||
		return random;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public inline function getSize(): FastFloat {
 | 
			
		||||
		return r.particle_size;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	function updateGpu(object: MeshObject, owner: MeshObject) {
 | 
			
		||||
		if (!object.data.geom.instanced) setupGeomGpu(object, owner);
 | 
			
		||||
		// GPU particles transform is attached to owner object
 | 
			
		||||
		if (dynamicEmitter) {
 | 
			
		||||
			if (!hasUniqueGeom) ensureUniqueGeom(object);
 | 
			
		||||
			var needSetup = instancedData == null || object.data.geom.instancedVB == null;
 | 
			
		||||
			if (needSetup) setupGeomGpuDynamic(object, owner);
 | 
			
		||||
			updateSpawnedInstances(object, owner);
 | 
			
		||||
		}
 | 
			
		||||
		else {
 | 
			
		||||
			if (!hasUniqueGeom) ensureUniqueGeom(object);
 | 
			
		||||
			if (!object.data.geom.instanced) setupGeomGpu(object, owner);
 | 
			
		||||
		}
 | 
			
		||||
		// GPU particles transform is attached to owner object in static mode
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	function setupGeomGpu(object: MeshObject, owner: MeshObject) {
 | 
			
		||||
@ -200,13 +294,129 @@ class ParticleSystem {
 | 
			
		||||
		object.data.geom.setupInstanced(instancedData, 1, Usage.StaticUsage);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	function fhash(n: Int): Float {
 | 
			
		||||
		var s = n + 1.0;
 | 
			
		||||
		s *= 9301.0 % s;
 | 
			
		||||
		s = (s * 9301.0 + 49297.0) % 233280.0;
 | 
			
		||||
		return s / 233280.0;
 | 
			
		||||
	// allocate instanced VB once for this object 
 | 
			
		||||
	function setupGeomGpuDynamic(object: MeshObject, owner: MeshObject) {
 | 
			
		||||
		if (instancedData == null) instancedData = new Float32Array(particles.length * 3);
 | 
			
		||||
		lastSpawnedCount = 0; 
 | 
			
		||||
		// Create instanced VB once if missing (seed with our instancedData)
 | 
			
		||||
		if (object.data.geom.instancedVB == null) {
 | 
			
		||||
			object.data.geom.setupInstanced(instancedData, 1, Usage.DynamicUsage);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	function ensureUniqueGeom(object: MeshObject) {
 | 
			
		||||
		if (hasUniqueGeom) return;
 | 
			
		||||
		var newData: MeshData = null;
 | 
			
		||||
		new MeshData(object.data.raw, function(dat: MeshData) {
 | 
			
		||||
			dat.scalePos = object.data.scalePos;
 | 
			
		||||
			dat.scaleTex = object.data.scaleTex;
 | 
			
		||||
			dat.format = object.data.format;
 | 
			
		||||
			newData = dat;
 | 
			
		||||
		});
 | 
			
		||||
		if (newData != null) object.setData(newData);
 | 
			
		||||
		hasUniqueGeom = true;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	function updateSpawnedInstances(object: MeshObject, owner: MeshObject) {
 | 
			
		||||
		if (instancedData == null) return;
 | 
			
		||||
		var targetCount = count;
 | 
			
		||||
		if (targetCount > particles.length) targetCount = particles.length;
 | 
			
		||||
		if (targetCount <= lastSpawnedCount) return;
 | 
			
		||||
 | 
			
		||||
		var normFactor = 1 / 32767;
 | 
			
		||||
		var scalePosOwner = owner.data.scalePos;
 | 
			
		||||
		var scalePosParticle = object.data.scalePos;
 | 
			
		||||
		var particleSize = r.particle_size;
 | 
			
		||||
		var base = 1.0 / (particleSize * scalePosParticle);
 | 
			
		||||
 | 
			
		||||
		switch (r.emit_from) {
 | 
			
		||||
			case 0: // Vert
 | 
			
		||||
				var pa = owner.data.geom.positions;
 | 
			
		||||
				var osx = owner.transform.scale.x;
 | 
			
		||||
				var osy = owner.transform.scale.y;
 | 
			
		||||
				var osz = owner.transform.scale.z;
 | 
			
		||||
				var pCount = Std.int(pa.values.length / pa.size);
 | 
			
		||||
				for (idx in lastSpawnedCount...targetCount) {
 | 
			
		||||
					var j = Std.int(fhash(idx) * pCount);
 | 
			
		||||
					var lx = pa.values[j * pa.size    ] * normFactor * scalePosOwner * osx;
 | 
			
		||||
					var ly = pa.values[j * pa.size + 1] * normFactor * scalePosOwner * osy;
 | 
			
		||||
					var lz = pa.values[j * pa.size + 2] * normFactor * scalePosOwner * osz;
 | 
			
		||||
					tmpV4.x = lx; tmpV4.y = ly; tmpV4.z = lz; tmpV4.w = 1;
 | 
			
		||||
					tmpV4.applyQuat(ownerRot);
 | 
			
		||||
					var o = idx * 3;
 | 
			
		||||
					instancedData.set(o    , (tmpV4.x + ownerLoc.x) * base);
 | 
			
		||||
					instancedData.set(o + 1, (tmpV4.y + ownerLoc.y) * base);
 | 
			
		||||
					instancedData.set(o + 2, (tmpV4.z + ownerLoc.z) * base);
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
			case 1: // Face
 | 
			
		||||
				var positions = owner.data.geom.positions.values;
 | 
			
		||||
				var osx1 = owner.transform.scale.x;
 | 
			
		||||
				var osy1 = owner.transform.scale.y;
 | 
			
		||||
				var osz1 = owner.transform.scale.z;
 | 
			
		||||
				for (idx in lastSpawnedCount...targetCount) {
 | 
			
		||||
					var ia = owner.data.geom.indices[Std.random(owner.data.geom.indices.length)];
 | 
			
		||||
					var faceIndex = Std.random(Std.int(ia.length / 3));
 | 
			
		||||
					var i0 = ia[faceIndex * 3 + 0];
 | 
			
		||||
					var i1 = ia[faceIndex * 3 + 1];
 | 
			
		||||
					var i2 = ia[faceIndex * 3 + 2];
 | 
			
		||||
					var v0x = positions[i0 * 4    ], v0y = positions[i0 * 4 + 1], v0z = positions[i0 * 4 + 2];
 | 
			
		||||
					var v1x = positions[i1 * 4    ], v1y = positions[i1 * 4 + 1], v1z = positions[i1 * 4 + 2];
 | 
			
		||||
					var v2x = positions[i2 * 4    ], v2y = positions[i2 * 4 + 1], v2z = positions[i2 * 4 + 2];
 | 
			
		||||
					var rx = Math.random(); var ry = Math.random(); if (rx + ry > 1) { rx = 1 - rx; ry = 1 - ry; }
 | 
			
		||||
					var pxs = v0x + rx * (v1x - v0x) + ry * (v2x - v0x);
 | 
			
		||||
					var pys = v0y + rx * (v1y - v0y) + ry * (v2y - v0y);
 | 
			
		||||
					var pzs = v0z + rx * (v1z - v0z) + ry * (v2z - v0z);
 | 
			
		||||
					var px = pxs * normFactor * scalePosOwner * osx1;
 | 
			
		||||
					var py = pys * normFactor * scalePosOwner * osy1;
 | 
			
		||||
					var pz = pzs * normFactor * scalePosOwner * osz1;
 | 
			
		||||
					tmpV4.x = px; tmpV4.y = py; tmpV4.z = pz; tmpV4.w = 1;
 | 
			
		||||
					tmpV4.applyQuat(ownerRot);
 | 
			
		||||
					var o1 = idx * 3;
 | 
			
		||||
					instancedData.set(o1    , (tmpV4.x + ownerLoc.x) * base);
 | 
			
		||||
					instancedData.set(o1 + 1, (tmpV4.y + ownerLoc.y) * base);
 | 
			
		||||
					instancedData.set(o1 + 2, (tmpV4.z + ownerLoc.z) * base);
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
			case 2: // Volume
 | 
			
		||||
				var dim = object.transform.dim;
 | 
			
		||||
				for (idx in lastSpawnedCount...targetCount) {
 | 
			
		||||
					tmpV4.x = (Math.random() * 2.0 - 1.0) * (dim.x * 0.5);
 | 
			
		||||
					tmpV4.y = (Math.random() * 2.0 - 1.0) * (dim.y * 0.5);
 | 
			
		||||
					tmpV4.z = (Math.random() * 2.0 - 1.0) * (dim.z * 0.5);
 | 
			
		||||
					tmpV4.w = 1;
 | 
			
		||||
					tmpV4.applyQuat(ownerRot);
 | 
			
		||||
					var o2 = idx * 3;
 | 
			
		||||
					instancedData.set(o2    , (tmpV4.x + ownerLoc.x) * base);
 | 
			
		||||
					instancedData.set(o2 + 1, (tmpV4.y + ownerLoc.y) * base);
 | 
			
		||||
					instancedData.set(o2 + 2, (tmpV4.z + ownerLoc.z) * base);
 | 
			
		||||
				}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Upload full active range [0..targetCount) to this object's instanced VB
 | 
			
		||||
		var geom = object.data.geom;
 | 
			
		||||
		if (geom.instancedVB == null) {
 | 
			
		||||
			geom.setupInstanced(instancedData, 1, Usage.DynamicUsage);
 | 
			
		||||
		}
 | 
			
		||||
		var vb = geom.instancedVB.lock();
 | 
			
		||||
		var totalFloats = targetCount * 3; // xyz per instance
 | 
			
		||||
		var i = 0;
 | 
			
		||||
		while (i < totalFloats) {
 | 
			
		||||
			vb.setFloat32(i * 4, instancedData[i]);
 | 
			
		||||
			i++;
 | 
			
		||||
		}
 | 
			
		||||
		geom.instancedVB.unlock();
 | 
			
		||||
		geom.instanceCount = targetCount;
 | 
			
		||||
		lastSpawnedCount = targetCount;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	inline function fhash(n: Int): Float {
 | 
			
		||||
    var s = n + 1.0;
 | 
			
		||||
    s *= 9301.0 % s;
 | 
			
		||||
    s = (s * 9301.0 + 49297.0) % 233280.0;
 | 
			
		||||
    return s / 233280.0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
	public function remove() {}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
@ -236,9 +446,11 @@ class ParticleSystem {
 | 
			
		||||
 | 
			
		||||
class Particle {
 | 
			
		||||
	public var i: Int;
 | 
			
		||||
	
 | 
			
		||||
	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) {
 | 
			
		||||
 | 
			
		||||
@ -160,6 +160,7 @@ class LnxPack {
 | 
			
		||||
			case "anim": TAnimation;
 | 
			
		||||
			case "tracks": TTrack;
 | 
			
		||||
			case "morph_target": TMorphTarget;
 | 
			
		||||
			case "vertex_groups": TVertex_groups;
 | 
			
		||||
			case _: TSceneFormat;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@ -1,6 +1,12 @@
 | 
			
		||||
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 {
 | 
			
		||||
@ -8,30 +14,45 @@ class Time {
 | 
			
		||||
		return 1 / frequency;
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	public static var scale = 1.0;
 | 
			
		||||
	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 lastTime = 0.0;
 | 
			
		||||
	static var _delta = 0.0;
 | 
			
		||||
	public static var delta(get, never): Float;
 | 
			
		||||
	static function get_delta(): Float {
 | 
			
		||||
		if (frequency == null) initFrequency();
 | 
			
		||||
		return (1 / frequency) * scale;
 | 
			
		||||
		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;
 | 
			
		||||
	}
 | 
			
		||||
		
 | 
			
		||||
	static var last = 0.0;
 | 
			
		||||
	public static var realDelta = 0.0;
 | 
			
		||||
	public static inline function time(): Float {
 | 
			
		||||
		return kha.Scheduler.time();
 | 
			
		||||
		return kha.Scheduler.time() * scale;
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	public static inline function realTime(): Float {
 | 
			
		||||
		return kha.Scheduler.realTime();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	static var frequency: Null<Int> = null;
 | 
			
		||||
 | 
			
		||||
	static function initFrequency() {
 | 
			
		||||
		frequency = kha.Display.primary != null ? kha.Display.primary.frequency : 60;
 | 
			
		||||
		return kha.Scheduler.realTime() * scale;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public static function update() {
 | 
			
		||||
		realDelta = realTime() - last;
 | 
			
		||||
		last = realTime();
 | 
			
		||||
		_delta = realTime() - lastTime;
 | 
			
		||||
		lastTime = realTime();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public static function render() {
 | 
			
		||||
		_renderDelta = realTime() - lastRenderTime;
 | 
			
		||||
		lastRenderTime = realTime();
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -94,34 +94,34 @@ class Tween {
 | 
			
		||||
 | 
			
		||||
				// Way too much Reflect trickery..
 | 
			
		||||
				var ps = Reflect.fields(a.props);
 | 
			
		||||
				for (i in 0...ps.length) {
 | 
			
		||||
					var p = ps[i];
 | 
			
		||||
				for (j in 0...ps.length) {
 | 
			
		||||
					var p = ps[j];
 | 
			
		||||
					var k = a._time / a.duration;
 | 
			
		||||
					if (k > 1) k = 1;
 | 
			
		||||
 | 
			
		||||
					if (a._comps[i] == 1) {
 | 
			
		||||
						var fromVal: Float = a._x[i];
 | 
			
		||||
					if (a._comps[j] == 1) {
 | 
			
		||||
						var fromVal: Float = a._x[j];
 | 
			
		||||
						var toVal: Float = Reflect.getProperty(a.props, p);
 | 
			
		||||
						var val: Float = fromVal + (toVal - fromVal) * eases[a.ease](k);
 | 
			
		||||
						Reflect.setProperty(a.target, p, val);
 | 
			
		||||
					}
 | 
			
		||||
					else { // _comps[i] == 4
 | 
			
		||||
					else { // _comps[j] == 4
 | 
			
		||||
						var obj = Reflect.getProperty(a.props, p);
 | 
			
		||||
						var toX: Float = Reflect.getProperty(obj, "x");
 | 
			
		||||
						var toY: Float = Reflect.getProperty(obj, "y");
 | 
			
		||||
						var toZ: Float = Reflect.getProperty(obj, "z");
 | 
			
		||||
						var toW: Float = Reflect.getProperty(obj, "w");
 | 
			
		||||
						if (a._normalize[i]) {
 | 
			
		||||
							var qdot = (a._x[i] * toX) + (a._y[i] * toY) + (a._z[i] * toZ) + (a._w[i] * toW);
 | 
			
		||||
						if (a._normalize[j]) {
 | 
			
		||||
							var qdot = (a._x[j] * toX) + (a._y[j] * toY) + (a._z[j] * toZ) + (a._w[j] * toW);
 | 
			
		||||
							if (qdot < 0.0) {
 | 
			
		||||
								toX = -toX; toY = -toY; toZ = -toZ; toW = -toW;
 | 
			
		||||
							}
 | 
			
		||||
						}
 | 
			
		||||
						var x: Float = a._x[i] + (toX - a._x[i]) * eases[a.ease](k);
 | 
			
		||||
						var y: Float = a._y[i] + (toY - a._y[i]) * eases[a.ease](k);
 | 
			
		||||
						var z: Float = a._z[i] + (toZ - a._z[i]) * eases[a.ease](k);
 | 
			
		||||
						var w: Float = a._w[i] + (toW - a._w[i]) * eases[a.ease](k);
 | 
			
		||||
						if (a._normalize[i]) {
 | 
			
		||||
						var x: Float = a._x[j] + (toX - a._x[j]) * eases[a.ease](k);
 | 
			
		||||
						var y: Float = a._y[j] + (toY - a._y[j]) * eases[a.ease](k);
 | 
			
		||||
						var z: Float = a._z[j] + (toZ - a._z[j]) * eases[a.ease](k);
 | 
			
		||||
						var w: Float = a._w[j] + (toW - a._w[j]) * eases[a.ease](k);
 | 
			
		||||
						if (a._normalize[j]) {
 | 
			
		||||
							var l = Math.sqrt(x * x + y * y + z * z + w * w);
 | 
			
		||||
							if (l > 0.0) {
 | 
			
		||||
								l = 1.0 / l;
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										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
									
								
							
							
						
						@ -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);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@ -2,9 +2,11 @@ package leenkx.logicnode;
 | 
			
		||||
 | 
			
		||||
import iron.object.Object;
 | 
			
		||||
 | 
			
		||||
#if lnx_physics
 | 
			
		||||
#if lnx_bullet
 | 
			
		||||
import leenkx.trait.physics.PhysicsConstraint;
 | 
			
		||||
import leenkx.trait.physics.bullet.PhysicsConstraint.ConstraintType;
 | 
			
		||||
#elseif lnx_oimo
 | 
			
		||||
// TODO
 | 
			
		||||
#end
 | 
			
		||||
 | 
			
		||||
class AddPhysicsConstraintNode extends LogicNode {
 | 
			
		||||
@ -25,7 +27,7 @@ class AddPhysicsConstraintNode extends LogicNode {
 | 
			
		||||
 | 
			
		||||
		if (pivotObject == null || rb1 == null || rb2 == null) return;
 | 
			
		||||
 | 
			
		||||
#if lnx_physics
 | 
			
		||||
#if lnx_bullet
 | 
			
		||||
 | 
			
		||||
		var disableCollisions: Bool = inputs[4].get();
 | 
			
		||||
		var breakable: Bool = inputs[5].get();
 | 
			
		||||
@ -108,6 +110,8 @@ class AddPhysicsConstraintNode extends LogicNode {
 | 
			
		||||
			}
 | 
			
		||||
			pivotObject.addTrait(con);
 | 
			
		||||
		}
 | 
			
		||||
#elseif lnx_oimo
 | 
			
		||||
// TODO
 | 
			
		||||
#end
 | 
			
		||||
		runOutput(0);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@ -4,7 +4,7 @@ import iron.object.Object;
 | 
			
		||||
 | 
			
		||||
#if lnx_physics
 | 
			
		||||
import leenkx.trait.physics.RigidBody;
 | 
			
		||||
import leenkx.trait.physics.bullet.RigidBody.Shape;
 | 
			
		||||
import leenkx.trait.physics.RigidBody.Shape;
 | 
			
		||||
#end
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										48
									
								
								leenkx/Sources/leenkx/logicnode/AnyContactNode.hx
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,48 @@
 | 
			
		||||
package leenkx.logicnode;
 | 
			
		||||
import iron.object.Object;
 | 
			
		||||
 | 
			
		||||
#if lnx_physics
 | 
			
		||||
import leenkx.trait.physics.PhysicsCache;
 | 
			
		||||
import leenkx.trait.physics.RigidBody;
 | 
			
		||||
#end
 | 
			
		||||
 | 
			
		||||
class AnyContactNode extends LogicNode {
 | 
			
		||||
 | 
			
		||||
	public var property0: String; 
 | 
			
		||||
	var lastContact = false;
 | 
			
		||||
 | 
			
		||||
	public function new(tree: LogicTree) {
 | 
			
		||||
		super(tree);
 | 
			
		||||
		tree.notifyOnUpdate(update);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	function update() {
 | 
			
		||||
		var object1: Object = inputs[0].get();
 | 
			
		||||
		if (object1 == null) object1 = tree.object;
 | 
			
		||||
		if (object1 == null) return;
 | 
			
		||||
 | 
			
		||||
		var contact = false;
 | 
			
		||||
 | 
			
		||||
		#if lnx_physics
 | 
			
		||||
		var rb1 = PhysicsCache.getCachedRigidBody(object1);
 | 
			
		||||
		if (rb1 != null) {
 | 
			
		||||
			var rbs = PhysicsCache.getCachedContacts(rb1);
 | 
			
		||||
			contact = (rbs != null && rbs.length > 0);
 | 
			
		||||
		}
 | 
			
		||||
		#end
 | 
			
		||||
 | 
			
		||||
		var shouldTrigger = false;
 | 
			
		||||
		switch (property0) {
 | 
			
		||||
		case "begin":
 | 
			
		||||
			shouldTrigger = contact && !lastContact;
 | 
			
		||||
		case "overlap":
 | 
			
		||||
			shouldTrigger = contact;
 | 
			
		||||
		case "end":
 | 
			
		||||
			shouldTrigger = !contact && lastContact;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		lastContact = contact;
 | 
			
		||||
 | 
			
		||||
		if (shouldTrigger) runOutput(0);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@ -21,7 +21,7 @@ class ApplyForceNode extends LogicNode {
 | 
			
		||||
 | 
			
		||||
#if lnx_physics
 | 
			
		||||
		var rb: RigidBody = object.getTrait(RigidBody);
 | 
			
		||||
 | 
			
		||||
		if (rb == null) return;
 | 
			
		||||
		!local ? rb.applyForce(force) : rb.applyForce(object.transform.worldVecToOrientation(force));
 | 
			
		||||
#end
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										26
									
								
								leenkx/Sources/leenkx/logicnode/ArrayIndexListNode.hx
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,26 @@
 | 
			
		||||
package leenkx.logicnode;
 | 
			
		||||
 | 
			
		||||
class ArrayIndexListNode extends LogicNode {
 | 
			
		||||
 | 
			
		||||
	public function new(tree: LogicTree) {
 | 
			
		||||
		super(tree);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	override function get(from: Int): Dynamic {
 | 
			
		||||
		var array: Array<Dynamic> = inputs[0].get();
 | 
			
		||||
		array = array.map(item -> Std.string(item));
 | 
			
		||||
		var value: Dynamic = inputs[1].get();
 | 
			
		||||
		var from: Int = 0;
 | 
			
		||||
 | 
			
		||||
		var arrayList: Array<Int> = [];
 | 
			
		||||
 | 
			
		||||
		var index: Int = array.indexOf(Std.string(value), from);
 | 
			
		||||
 | 
			
		||||
		while(index != -1){
 | 
			
		||||
			arrayList.push(index);
 | 
			
		||||
			index = array.indexOf(Std.string(value), index+1);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return arrayList;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@ -1,7 +1,7 @@
 | 
			
		||||
package leenkx.logicnode;
 | 
			
		||||
import aura.Aura;
 | 
			
		||||
import aura.Types;
 | 
			
		||||
import aura.types.HRTFData;
 | 
			
		||||
import aura.types.HRTF;
 | 
			
		||||
import aura.dsp.panner.HRTFPanner;
 | 
			
		||||
 | 
			
		||||
class AudioHRTFPannerNode extends LogicNode {
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										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
									
								
							
							
						
						@ -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);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@ -2,25 +2,48 @@ 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());
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -62,7 +62,7 @@ class DrawStringNode extends LogicNode {
 | 
			
		||||
 | 
			
		||||
	override function get(from: Int): Dynamic {
 | 
			
		||||
 | 
			
		||||
		return from == 1 ? RenderToTexture.g.font.height(RenderToTexture.g.fontSize) : RenderToTexture.g.font.width(RenderToTexture.g.fontSize, string);
 | 
			
		||||
		return from == 1 ? RenderToTexture.g.font.width(RenderToTexture.g.fontSize, string) : RenderToTexture.g.font.height(RenderToTexture.g.fontSize);
 | 
			
		||||
			
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										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
									
								
							
							
						
						@ -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;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@ -26,9 +26,8 @@ class GetBoneTransformNode extends LogicNode {
 | 
			
		||||
		// Get bone in armature
 | 
			
		||||
		var bone = anim.getBone(boneName);
 | 
			
		||||
 | 
			
		||||
        //return anim.getAbsWorldMat(bone);
 | 
			
		||||
		return anim.getAbsMat(bone).clone().multmat(object.transform.world);
 | 
			
		||||
		//return anim.getAbsWorldMat(bone);
 | 
			
		||||
		return anim.getAbsWorldMat(anim.skeletonMats, bone);
 | 
			
		||||
        //return anim.getAbsMat(bone).clone().multmat(object.transform.world);
 | 
			
		||||
		
 | 
			
		||||
        #else
 | 
			
		||||
        return null;
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										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
									
								
							
							
						
						@ -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
									
								
							
							
						
						@ -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
									
								
							
							
						
						@ -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
									
								
							
							
						
						@ -0,0 +1,26 @@
 | 
			
		||||
package leenkx.logicnode;
 | 
			
		||||
 | 
			
		||||
import iron.object.Object;
 | 
			
		||||
import iron.math.Vec4;
 | 
			
		||||
 | 
			
		||||
class GetWorldOrientationNode extends LogicNode {
 | 
			
		||||
 | 
			
		||||
	public var property0: String;
 | 
			
		||||
 | 
			
		||||
	public function new(tree: LogicTree) {
 | 
			
		||||
		super(tree);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	override function get(from: Int): Dynamic {
 | 
			
		||||
		var object: Object = inputs[0].get();
 | 
			
		||||
 | 
			
		||||
		if (object == null) return null;
 | 
			
		||||
 | 
			
		||||
		return switch (property0) {
 | 
			
		||||
			case "Right": object.transform.world.right();
 | 
			
		||||
			case "Look": object.transform.world.look();
 | 
			
		||||
			case "Up": object.transform.world.up();
 | 
			
		||||
			default: null;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@ -1,7 +1,7 @@
 | 
			
		||||
package leenkx.logicnode;
 | 
			
		||||
 | 
			
		||||
#if lnx_physics
 | 
			
		||||
import leenkx.trait.physics.bullet.PhysicsWorld;
 | 
			
		||||
import leenkx.trait.physics.PhysicsWorld;
 | 
			
		||||
#end
 | 
			
		||||
import leenkx.trait.navigation.Navigation;
 | 
			
		||||
import iron.object.Object;
 | 
			
		||||
 | 
			
		||||
@ -1,7 +1,10 @@
 | 
			
		||||
package leenkx.logicnode;
 | 
			
		||||
 | 
			
		||||
import iron.object.Object;
 | 
			
		||||
 | 
			
		||||
#if lnx_physics
 | 
			
		||||
import leenkx.trait.physics.PhysicsCache;
 | 
			
		||||
import leenkx.trait.physics.RigidBody;
 | 
			
		||||
#end
 | 
			
		||||
 | 
			
		||||
class HasContactNode extends LogicNode {
 | 
			
		||||
 | 
			
		||||
@ -15,12 +18,15 @@ class HasContactNode extends LogicNode {
 | 
			
		||||
 | 
			
		||||
		if (object1 == null || object2 == null) return false;
 | 
			
		||||
 | 
			
		||||
#if lnx_physics
 | 
			
		||||
		var physics = leenkx.trait.physics.PhysicsWorld.active;
 | 
			
		||||
		var rb2 = object2.getTrait(RigidBody);
 | 
			
		||||
		var rbs = physics.getContacts(object1.getTrait(RigidBody));
 | 
			
		||||
		if (rbs != null) for (rb in rbs) if (rb == rb2) return true;
 | 
			
		||||
#end
 | 
			
		||||
		#if lnx_physics
 | 
			
		||||
		var rb1 = PhysicsCache.getCachedRigidBody(object1);
 | 
			
		||||
		var rb2 = PhysicsCache.getCachedRigidBody(object2);
 | 
			
		||||
		
 | 
			
		||||
		if (rb1 != null && rb2 != null) {
 | 
			
		||||
			var rbs = PhysicsCache.getCachedContacts(rb1);
 | 
			
		||||
			return PhysicsCache.hasContactWith(rbs, rb2);
 | 
			
		||||
		}
 | 
			
		||||
		#end
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										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);
 | 
			
		||||
	}
 | 
			
		||||
} 
 | 
			
		||||
@ -1,7 +1,11 @@
 | 
			
		||||
package leenkx.logicnode;
 | 
			
		||||
 | 
			
		||||
import iron.object.Object;
 | 
			
		||||
 | 
			
		||||
#if lnx_physics
 | 
			
		||||
import leenkx.trait.physics.PhysicsCache;
 | 
			
		||||
import leenkx.trait.physics.RigidBody;
 | 
			
		||||
#end
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class OnContactNode extends LogicNode {
 | 
			
		||||
 | 
			
		||||
@ -23,22 +27,15 @@ class OnContactNode extends LogicNode {
 | 
			
		||||
 | 
			
		||||
		var contact = false;
 | 
			
		||||
 | 
			
		||||
#if lnx_physics
 | 
			
		||||
		var physics = leenkx.trait.physics.PhysicsWorld.active;
 | 
			
		||||
		var rb1 = object1.getTrait(RigidBody);
 | 
			
		||||
		if (rb1 != null) {
 | 
			
		||||
			var rbs = physics.getContacts(rb1);
 | 
			
		||||
			if (rbs != null) {
 | 
			
		||||
				var rb2 = object2.getTrait(RigidBody);
 | 
			
		||||
				for (rb in rbs) {
 | 
			
		||||
					if (rb == rb2) {
 | 
			
		||||
						contact = true;
 | 
			
		||||
						break;
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
		#if lnx_physics
 | 
			
		||||
			var rb1 = PhysicsCache.getCachedRigidBody(object1);
 | 
			
		||||
			var rb2 = PhysicsCache.getCachedRigidBody(object2);
 | 
			
		||||
			
 | 
			
		||||
			if (rb1 != null && rb2 != null) {
 | 
			
		||||
				var rbs = PhysicsCache.getCachedContacts(rb1);
 | 
			
		||||
				contact = PhysicsCache.hasContactWith(rbs, rb2);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
#end
 | 
			
		||||
		#end
 | 
			
		||||
 | 
			
		||||
		var b = false;
 | 
			
		||||
		switch (property0) {
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										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);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -1,7 +1,7 @@
 | 
			
		||||
package leenkx.logicnode;
 | 
			
		||||
 | 
			
		||||
#if lnx_audio
 | 
			
		||||
import iron.object.SpeakerObject;
 | 
			
		||||
 | 
			
		||||
#end
 | 
			
		||||
class PauseSoundNode extends LogicNode {
 | 
			
		||||
 | 
			
		||||
	public function new(tree: LogicTree) {
 | 
			
		||||
@ -9,9 +9,11 @@ class PauseSoundNode extends LogicNode {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	override function run(from: Int) {
 | 
			
		||||
		#if lnx_audio
 | 
			
		||||
		var object: SpeakerObject = cast(inputs[1].get(), SpeakerObject);
 | 
			
		||||
		if (object == null) return;
 | 
			
		||||
		object.pause();
 | 
			
		||||
		#end
 | 
			
		||||
		runOutput(0);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -1,7 +1,7 @@
 | 
			
		||||
package leenkx.logicnode;
 | 
			
		||||
 | 
			
		||||
#if lnx_physics
 | 
			
		||||
import leenkx.trait.physics.bullet.PhysicsConstraint.ConstraintAxis;
 | 
			
		||||
import leenkx.trait.physics.PhysicsConstraint.ConstraintAxis;
 | 
			
		||||
#end
 | 
			
		||||
 | 
			
		||||
class PhysicsConstraintNode extends LogicNode {
 | 
			
		||||
 | 
			
		||||