forked from LeenkxTeam/LNXSDK
		
	Compare commits
	
		
			504 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| f5fa754e17 | |||
| 48f5575e4e | |||
| f2c4be6336 | |||
| 2ddc938db8 | |||
| 5eb735ada2 | |||
| 9894cc20f2 | |||
| dbe6d0829a | |||
| 6f383e2ab2 | |||
| 5c2d29d7ce | |||
| 28579e14d7 | |||
| 2ec6f43cc5 | |||
| 027021815a | |||
| b9b387803f | |||
| e05d9d0237 | |||
| c908e6cad2 | |||
| 506a0a0245 | |||
| 5cf33724e4 | |||
| ac5aa3d19c | |||
| 0c534ee632 | |||
| 69a2bb1e7e | |||
| e3e7855d26 | |||
| f7917974f8 | |||
| fa2d8f05d5 | |||
| 5b86f32b51 | |||
| 73fcb55acc | |||
| c24baa3364 | |||
| 4517c4863f | |||
| 1299306e09 | |||
| f97d8fd846 | |||
| 8f8d4b1376 | |||
| a926fa8dbb | |||
| 6c3efa6c83 | |||
| 21afad6d09 | |||
| 04c6983a09 | |||
| 45966ef0bb | |||
| a72edc6203 | |||
| 6af1ef2df1 | |||
| 46e3047877 | |||
| de74af215a | |||
| b6e96553c2 | |||
| 58e009f709 | |||
| e88f101ca6 | |||
| d28d59b9e6 | |||
| a4398c7279 | |||
| abedfd799e | |||
| 4520422f6b | |||
| 88418c06c3 | |||
| aedc2783ab | |||
| 1505414c4c | |||
| fa818602c4 | |||
| 79dc458671 | |||
| 8e635fb1e9 | |||
| 4c2e6ab26a | |||
| 2371e3777e | |||
| b458b77e5c | |||
| 9b76f8cca9 | |||
| 5f2acb209e | |||
| 6fc446e7a9 | |||
| 71e57026e1 | |||
| 5288a98440 | |||
| 35e346be39 | |||
| 843ef0b058 | |||
| 177890bf39 | |||
| 9ac37e6dc7 | |||
| e697437778 | |||
| c94fc0fd97 | |||
| cd0a6f6788 | |||
| 4400e0e9c8 | |||
| 20cf07cfc3 | |||
| 1939f19c05 | |||
| 0d2b152ccb | |||
| 7f58e0fc85 | |||
| 0e4a6575c7 | |||
| 024676f43a | |||
| 8fe758862c | |||
| 1f3d1b47ae | |||
| f659a3c2be | |||
| 6eeb9017d4 | |||
| afe89c3834 | |||
| 8b695f72bb | |||
| 3d99fa60c0 | |||
| 43be7729ba | |||
| de0b1075c2 | |||
| c7aba23fa4 | |||
| 881f3267cc | |||
| 19b79d61c7 | |||
| fcbab54a0c | |||
| 8fd05d5514 | |||
| ad4013ed75 | |||
| 590e6219d5 | |||
| 8ac567b57b | |||
| 43b7ae7060 | |||
| 662981fa03 | |||
| a3866fb604 | |||
| 29e9e71a6a | |||
| bfb85b0a3b | |||
| ef99b800e0 | |||
| 7cca955fc5 | |||
| 7e7bbd5eae | |||
| c31b2a18ad | |||
| fb47bf2564 | |||
| 7ae6750620 | |||
| 5b87010f76 | |||
| 97e952fc15 | |||
| b440539d65 | |||
| fbf63e4f17 | |||
| a318e08072 | |||
| 7ae458a9dd | |||
| 60a9db6459 | |||
| 3b5a93c92a | |||
| 4af990796e | |||
| 9fb4916c3c | |||
| f61d5833bb | |||
| 40b52be713 | |||
| 07d8422f22 | |||
| 7179d42b27 | |||
| 9984615f8c | |||
| 99a5d7d445 | |||
| 5e0acd3d5d | |||
| 0430e06acd | |||
| f4077e461b | |||
| 28943f1522 | |||
| df4feac132 | |||
| 82412dbf81 | |||
| 6afc209db7 | |||
| e9aae53be9 | |||
| a65675ef75 | |||
| 8f073c5ae1 | |||
| 08d08e42d9 | |||
| a1ee335c68 | |||
| de6bf8a08a | |||
| 9622f35b73 | |||
| da666e6d23 | |||
| 6ff7804ec1 | |||
| 0265ef5b64 | |||
| 53c5000975 | |||
| 7647231696 | |||
| 1e583a795d | |||
| da25d8c313 | |||
| 70695b3b31 | |||
| 858537d54c | |||
| 2998a5585e | |||
| c35c59e6a9 | |||
| 15ac833f2c | |||
| 8077f00ada | |||
| b9848cd2dc | |||
| 58140ad583 | |||
| e922cc38e6 | |||
| 9e2b601445 | |||
| 0439dde4a8 | |||
| 57f0e937d0 | |||
| e234c8615c | |||
| 2e7ccb5151 | |||
| e594518e57 | |||
| a41be0f436 | |||
| 1306033b36 | |||
| eee0011cdd | |||
| 315ac0bd16 | |||
| f289e6f89c | |||
| b89ebfd9c6 | |||
| a142b248ef | |||
| 700d236bf1 | |||
| f228eab8d3 | |||
| 863d884b76 | |||
| 34e0f5a282 | |||
| 45e2e52008 | |||
| 444a215e63 | |||
| fb2d2a1a7c | |||
| f88c04abea | |||
| 6fdd4b3f70 | |||
| a389c27d75 | |||
| 1909c3da9f | |||
| 1c648b0433 | |||
| 5824bd91aa | |||
| 43fe559eef | |||
| 12c09545ce | |||
| 0e60951ec9 | |||
| ccb8b358d3 | |||
| 3103a976a6 | |||
| 1a8586777b | |||
| 3721c774a1 | |||
| a58fba408d | |||
| 82fa7bcfe3 | |||
| 268fba6cd5 | |||
| 4ab14ce6c8 | |||
| 9023e8d1da | |||
| b58c7a9632 | |||
| 99b70622f5 | |||
| 647b73b746 | |||
| 935c30ec08 | |||
| 0b0d597f89 | |||
| d5878afb30 | |||
| 96b55a1a56 | |||
| 91b3072305 | |||
| 1d0b338d92 | |||
| 8e83c0d0d0 | |||
| 927baec4df | |||
| f5c9e70d1a | |||
| 709e0ed9cb | |||
| 0423a735fc | |||
| 7bcf985023 | |||
| bd413917fc | |||
| 852377f60d | |||
| e17e9a8e35 | |||
| 4055c979a1 | |||
| 06b003ecdb | |||
| fd7f215bb2 | |||
| 6a1df9ec46 | |||
| deccac3c46 | |||
| 432b0210b2 | |||
| 14cf5cebed | |||
| d23232205b | |||
| 2307e1504f | |||
| 1ebfecb644 | |||
| 175b575b23 | |||
| 63943a9cbf | |||
| 224d9be76f | |||
| 62d3c8757b | |||
| 3785f93573 | |||
| ffb276745f | |||
| d1c9258da5 | |||
| 3e0cd2be35 | |||
| 1fd1973470 | |||
| a01c72ef76 | |||
| 4b01a562c9 | |||
| 88c7c5b99e | |||
| 6972d9abc4 | |||
| 63c6b9d98b | |||
| 313d24bbc8 | |||
| 6d2812306d | |||
| e84d6ada84 | |||
| 5057f2b946 | |||
| 2715fe3398 | |||
| 7cb8b8a2d2 | |||
| cd606009e0 | |||
| 965162b101 | |||
| c61a57bfb3 | |||
| 2434ad07f2 | |||
| cdc425fbcb | |||
| 846bb28c86 | |||
| 7fabd77ef8 | |||
| fb1a5c88bf | |||
| e05c83a8bb | |||
| ee4f62e881 | |||
| 59f8dff22f | |||
| 5572226ac5 | |||
| d04874e0b3 | |||
| ef8b3a99ab | |||
| 1d3254a237 | |||
| 188af4a50f | |||
| c45baaf396 | |||
| 4b1da08819 | |||
| aeb353fb20 | |||
| 65961b1593 | |||
| 1c472155e2 | |||
| 4238f0b2a0 | |||
| b40aadf76c | |||
| 00493bed9c | |||
| 7277987335 | |||
| a48ec4d034 | |||
| 7f0153f816 | |||
| 40d893e139 | |||
| 9b9289d27d | |||
| 8786798edd | |||
| fa425a98a5 | |||
| 7b17633c28 | |||
| 1ef805eb0b | |||
| cb7b041fea | |||
| 5165769088 | |||
| feabf446db | |||
| e770120f7d | |||
| a0e8e1a1a6 | |||
| 7c1b1f2dd9 | |||
| 88a4f0e76a | |||
| d9e613b3eb | |||
| a0c84dc807 | |||
| 5a92920b1f | |||
| 30a624c857 | |||
| 8153d67eac | |||
| 9963a42c76 | |||
| 885385d7cb | |||
| c433a7f09e | |||
| c7e2c7d452 | |||
| 2a1235b3d8 | |||
| 074962d158 | |||
| 846c3b2c11 | |||
| b5af208766 | |||
| 63565052e3 | |||
| 38eb66a0b5 | |||
| 908efdd554 | |||
| e014484d27 | |||
| 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 | |||
| 872433cafb | |||
| 74bbb6ca87 | |||
| cbbd6fe495 | |||
| 45a48acf8a | |||
| 1c20e03e0c | |||
| c769b3ca26 | |||
| 98f334c883 | |||
| 8ac8a780e1 | |||
| 25e5700084 | |||
| c4378be891 | |||
| 6ad615a961 | |||
| 1e4510ba56 | |||
| a2714bf101 | |||
| 5639234eb9 | |||
| 1591ccdae5 | |||
| 9cb5232187 | |||
| 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 | |||
| 6b25d8c8ad | |||
| 13ca31f480 | |||
| d102e59040 | |||
| df0e24c307 | |||
| 2df86850f8 | |||
| 02ff259860 | |||
| 2e6de515ef | |||
| 41b840212c | |||
| 751f960b82 | |||
| 9558ded5c4 | |||
| 8066756605 | |||
| ac2507e0ae | |||
| 205d4ccc41 | |||
| 7565818d0e | |||
| fc093eca3e | |||
| 61b8f21037 | |||
| d988ce8c99 | |||
| 727d82f268 | |||
| c3c89c320b | |||
| 0b5bb877fb | |||
| 429e6d6768 | |||
| 2d8bfbf181 | |||
| 1ad7e0eaf4 | |||
| ae72401657 | |||
| 58b9000305 | |||
| 0cc86c41b8 | |||
| 25f8c5f64c | |||
| 109544cea9 | |||
| 4c92c4bcc9 | |||
| 3433afb1c3 | |||
| e22b522059 | |||
| 200af34740 | |||
| 6d7b0078b4 | |||
| fa501cb09b | |||
| 62a4bbb714 | |||
| eab3e3b30c | |||
| 3be7528a6c | |||
| c9dd46c5e3 | |||
| 99806b8069 | |||
| e98bfb125d | |||
| c4c0e2beaa | |||
| 36cbc934ba | |||
| 210d5ea532 | |||
| dab9a38424 | |||
| b7bbe40348 | |||
| 8b084156ff | |||
| 538c364f33 | |||
| 09eee93ac9 | |||
| 8b5a77c001 | |||
| 436b7fac02 | |||
| 9ef9f5a637 | |||
| b8ca4be56a | |||
| 29e4993f06 | |||
| 4134352688 | |||
| 25cf758a33 | |||
| 23af038a16 | |||
| 7f7878aaa6 | |||
| d7e076fb56 | |||
| dab915b60d | |||
| 74389ba76a | |||
| bd5afc797d | |||
| 27b4ec42a8 | |||
| ab7edaa9e3 | |||
| 27540ac7e9 | |||
| a5b512f20b | |||
| f8d0e67f33 | |||
| 915118617d | |||
| ea69511e67 | |||
| 3926a7f83e | |||
| 0eafd14ae2 | |||
| 08614512d7 | |||
| 08261a9335 | |||
| ce3c1cea6a | |||
| 392d12a816 | |||
| 6b423038d4 | |||
| 76628fc010 | |||
| 99f687b10c | 
							
								
								
									
										2
									
								
								.gitattributes
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								.gitattributes
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,2 @@ | |||||||
|  | *.hdr binary | ||||||
|  | blender/lnx/props.py ident | ||||||
							
								
								
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,3 @@ | |||||||
|  | __pycache__/ | ||||||
|  | *.pyc | ||||||
|  | *.DS_Store | ||||||
| @ -2,10 +2,12 @@ | |||||||
| -cp ../Kha/Backends/Krom | -cp ../Kha/Backends/Krom | ||||||
| -cp ../leenkx/Sources | -cp ../leenkx/Sources | ||||||
| -cp ../iron/Sources | -cp ../iron/Sources | ||||||
|  | -cp ../lib/aura/Sources | ||||||
| -cp ../lib/haxebullet/Sources | -cp ../lib/haxebullet/Sources | ||||||
| -cp ../lib/haxerecast/Sources | -cp ../lib/haxerecast/Sources | ||||||
| -cp ../lib/zui/Sources | -cp ../lib/zui/Sources | ||||||
| --macro include('iron', true, null, ['../iron/Sources']) | --macro include('iron', true, null, ['../iron/Sources']) | ||||||
|  | --macro include('aura', true, null, ['../lib/aura/Sources']) | ||||||
| --macro include('haxebullet', true, null, ['../lib/haxebullet/Sources']) | --macro include('haxebullet', true, null, ['../lib/haxebullet/Sources']) | ||||||
| --macro include('haxerecast', true, null, ['../lib/haxerecast/Sources']) | --macro include('haxerecast', true, null, ['../lib/haxerecast/Sources']) | ||||||
| --macro include('leenkx', true, ['leenkx.network'], ['../leenkx/Sources','../iron/Sources']) | --macro include('leenkx', true, ['leenkx.network'], ['../leenkx/Sources','../iron/Sources']) | ||||||
|  | |||||||
							
								
								
									
										26
									
								
								leenkx.py
									
									
									
									
									
								
							
							
						
						
									
										26
									
								
								leenkx.py
									
									
									
									
									
								
							| @ -24,7 +24,7 @@ import textwrap | |||||||
| import threading | import threading | ||||||
| import traceback | import traceback | ||||||
| import typing | import typing | ||||||
| from typing import Callable, Optional | from typing import Callable, Optional, List | ||||||
| import webbrowser | import webbrowser | ||||||
|  |  | ||||||
| import bpy | import bpy | ||||||
| @ -33,6 +33,12 @@ from bpy.props import * | |||||||
| from bpy.types import Operator, AddonPreferences | from bpy.types import Operator, AddonPreferences | ||||||
|  |  | ||||||
|  |  | ||||||
|  | if bpy.app.version < (2, 90, 0):   | ||||||
|  |     ListType = List | ||||||
|  | else: | ||||||
|  |     ListType = list | ||||||
|  |  | ||||||
|  |  | ||||||
| class SDKSource(IntEnum): | class SDKSource(IntEnum): | ||||||
|     PREFS = 0 |     PREFS = 0 | ||||||
|     LOCAL = 1 |     LOCAL = 1 | ||||||
| @ -73,9 +79,10 @@ def detect_sdk_path(): | |||||||
|     area = win.screen.areas[0] |     area = win.screen.areas[0] | ||||||
|     area_type = area.type |     area_type = area.type | ||||||
|     area.type = "INFO" |     area.type = "INFO" | ||||||
|     with bpy.context.temp_override(window=win, screen=win.screen, area=area): |     if bpy.app.version >= (2, 92, 0): | ||||||
|         bpy.ops.info.select_all(action='SELECT') |         with bpy.context.temp_override(window=win, screen=win.screen, area=area): | ||||||
|         bpy.ops.info.report_copy() |             bpy.ops.info.select_all(action='SELECT') | ||||||
|  |             bpy.ops.info.report_copy() | ||||||
|     area.type = area_type |     area.type = area_type | ||||||
|     clipboard = bpy.context.window_manager.clipboard |     clipboard = bpy.context.window_manager.clipboard | ||||||
|  |  | ||||||
| @ -85,6 +92,7 @@ def detect_sdk_path(): | |||||||
|     if match: |     if match: | ||||||
|         addon_prefs.sdk_path = os.path.dirname(match[-1]) |         addon_prefs.sdk_path = os.path.dirname(match[-1]) | ||||||
|  |  | ||||||
|  |  | ||||||
| def get_link_web_server(self): | def get_link_web_server(self): | ||||||
|     return self.get('link_web_server', 'http://localhost/') |     return self.get('link_web_server', 'http://localhost/') | ||||||
|  |  | ||||||
| @ -558,7 +566,7 @@ def remove_readonly(func, path, excinfo): | |||||||
|     func(path) |     func(path) | ||||||
|  |  | ||||||
|  |  | ||||||
| def run_proc(cmd: list[str], done: Optional[Callable[[bool], None]] = None): | def run_proc(cmd: ListType[str], done: Optional[Callable[[bool], None]] = None): | ||||||
|     def fn(p, done): |     def fn(p, done): | ||||||
|         p.wait() |         p.wait() | ||||||
|         if done is not None: |         if done is not None: | ||||||
| @ -840,7 +848,13 @@ def update_leenkx_py(sdk_path: str, force_relink=False): | |||||||
|                 else: |                 else: | ||||||
|                     raise err |                     raise err | ||||||
|     else: |     else: | ||||||
|         lnx_module_file.unlink(missing_ok=True) |         if bpy.app.version < (2, 92, 0): | ||||||
|  |             try: | ||||||
|  |                 lnx_module_file.unlink() | ||||||
|  |             except FileNotFoundError: | ||||||
|  |                 pass  | ||||||
|  |         else: | ||||||
|  |             lnx_module_file.unlink(missing_ok=True) | ||||||
|         shutil.copy(Path(sdk_path) / 'leenkx.py', lnx_module_file) |         shutil.copy(Path(sdk_path) / 'leenkx.py', lnx_module_file) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | |||||||
| @ -3,6 +3,10 @@ | |||||||
|  |  | ||||||
| #include "compiled.inc" | #include "compiled.inc" | ||||||
|  |  | ||||||
|  | #ifdef _CPostprocess | ||||||
|  | uniform vec4 PPComp17; | ||||||
|  | #endif | ||||||
|  |  | ||||||
| uniform sampler2D tex; | uniform sampler2D tex; | ||||||
| uniform vec2 dir; | uniform vec2 dir; | ||||||
| uniform vec2 screenSize; | uniform vec2 screenSize; | ||||||
| @ -45,6 +49,12 @@ void main() { | |||||||
| 		res += factor * col; | 		res += factor * col; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	#ifdef _CPostprocess | ||||||
|  | 		vec3 AirColor = vec3(PPComp17.x, PPComp17.y, PPComp17.z); | ||||||
|  | 	#else | ||||||
|  | 		vec3 AirColor = volumAirColor; | ||||||
|  | 	#endif | ||||||
|  | 	 | ||||||
| 	res /= sumfactor; | 	res /= sumfactor; | ||||||
| 	fragColor = vec4(volumAirColor * res, 1.0); | 	fragColor = vec4(AirColor * res, 1.0); | ||||||
| } | } | ||||||
|  | |||||||
| @ -19,6 +19,11 @@ | |||||||
| 				{ | 				{ | ||||||
| 					"name": "screenSize", | 					"name": "screenSize", | ||||||
| 					"link": "_screenSize" | 					"link": "_screenSize" | ||||||
|  | 				}, | ||||||
|  | 				{ | ||||||
|  | 					"name": "PPComp17", | ||||||
|  | 					"link": "_PPComp17", | ||||||
|  | 					"ifdef": ["_CPostprocess"] | ||||||
| 				} | 				} | ||||||
| 			], | 			], | ||||||
| 			"texture_params": [], | 			"texture_params": [], | ||||||
|  | |||||||
| @ -5,7 +5,7 @@ | |||||||
| uniform sampler2D tex; | uniform sampler2D tex; | ||||||
|  |  | ||||||
| #ifdef _CPostprocess | #ifdef _CPostprocess | ||||||
| uniform vec3 PPComp13; | uniform vec4 PPComp13; | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| in vec2 texCoord; | in vec2 texCoord; | ||||||
| @ -43,13 +43,17 @@ void main() { | |||||||
| 	#ifdef _CPostprocess | 	#ifdef _CPostprocess | ||||||
| 		float max_distort = PPComp13.x; | 		float max_distort = PPComp13.x; | ||||||
| 		int num_iter = int(PPComp13.y); | 		int num_iter = int(PPComp13.y); | ||||||
|  | 		int CAType = int(PPComp13.z); | ||||||
|  | 		int on = int(PPComp13.w); | ||||||
| 	#else | 	#else | ||||||
| 		float max_distort = compoChromaticStrength; | 		float max_distort = compoChromaticStrength; | ||||||
| 		int num_iter = compoChromaticSamples; | 		int num_iter = compoChromaticSamples; | ||||||
|  | 		int CAType = compoChromaticType; | ||||||
|  | 		int on = 1; | ||||||
| 	#endif | 	#endif | ||||||
|  |  | ||||||
| 	// Spectral | 	// Spectral | ||||||
| 	if (compoChromaticType == 1) { | 	if (CAType == 1) { | ||||||
| 		float reci_num_iter_f = 1.0 / float(num_iter); | 		float reci_num_iter_f = 1.0 / float(num_iter); | ||||||
|  |  | ||||||
| 		vec2 resolution = vec2(1,1); | 		vec2 resolution = vec2(1,1); | ||||||
| @ -64,7 +68,7 @@ void main() { | |||||||
| 			sumcol += w * texture(tex, barrelDistortion(uv, 0.6 * max_distort * t)); | 			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 | 	// Simple | ||||||
| @ -73,6 +77,7 @@ void main() { | |||||||
| 		col.x = texture(tex, texCoord + ((vec2(0.0, 1.0) * max_distort) / vec2(1000.0))).x; | 		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.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; | 		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 PPComp6; | ||||||
| uniform vec3 PPComp7; | uniform vec3 PPComp7; | ||||||
| uniform vec3 PPComp8; | uniform vec3 PPComp8; | ||||||
|  | uniform vec3 PPComp11; | ||||||
| uniform vec3 PPComp14; | uniform vec3 PPComp14; | ||||||
| uniform vec4 PPComp15; | uniform vec4 PPComp15; | ||||||
|  | uniform vec4 PPComp16; | ||||||
|  | uniform vec4 PPComp18; | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| // #ifdef _CPos | // #ifdef _CPos | ||||||
| @ -106,6 +109,16 @@ in vec2 texCoord; | |||||||
| out vec4 fragColor; | out vec4 fragColor; | ||||||
|  |  | ||||||
| #ifdef _CFog | #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 vec3 compoFogColor = vec3(0.5, 0.6, 0.7); | ||||||
| // const float compoFogAmountA = 1.0; // b = 0.01 | // const float compoFogAmountA = 1.0; // b = 0.01 | ||||||
| // const float compoFogAmountB = 1.0; // c = 0.1 | // const float compoFogAmountB = 1.0; // c = 0.1 | ||||||
| @ -118,8 +131,8 @@ out vec4 fragColor; | |||||||
| // } | // } | ||||||
| vec3 applyFog(vec3 rgb, float distance) { | vec3 applyFog(vec3 rgb, float distance) { | ||||||
| 	// float fogAmount = 1.0 - exp(-distance * compoFogAmountA); | 	// float fogAmount = 1.0 - exp(-distance * compoFogAmountA); | ||||||
| 	float fogAmount = 1.0 - exp(-distance * (compoFogAmountA / 100)); | 	float fogAmount = 1.0 - exp(-distance * (FogAmountA / 100)); | ||||||
| 	return mix(rgb, compoFogColor, fogAmount); | 	return mix(rgb, FogColor, fogAmount); | ||||||
| } | } | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| @ -131,7 +144,7 @@ float ConvertEV100ToExposure(float EV100) { | |||||||
|     return 1/0.8 * exp2(-EV100); |     return 1/0.8 * exp2(-EV100); | ||||||
| } | } | ||||||
| float ComputeEV(float avgLuminance) { | float ComputeEV(float avgLuminance) { | ||||||
|     const float sqAperture = PPComp1[0].x * PPComp1.x; |     const float sqAperture = PPComp1.x * PPComp1.x; | ||||||
|     const float shutterTime = 1.0 / PPComp1.y; |     const float shutterTime = 1.0 / PPComp1.y; | ||||||
|     const float ISO = PPComp1.z; |     const float ISO = PPComp1.z; | ||||||
|     const float EC = PPComp2.x; |     const float EC = PPComp2.x; | ||||||
| @ -349,16 +362,22 @@ void main() { | |||||||
|  |  | ||||||
| #ifdef _CSharpen | #ifdef _CSharpen | ||||||
| 	#ifdef _CPostprocess | 	#ifdef _CPostprocess | ||||||
| 		float strengthSharpen = PPComp14.y; | 		float strengthSharpen = PPComp14.y;	 | ||||||
|  | 		vec3 SharpenColor = vec3(PPComp16.x, PPComp16.y, PPComp16.z);  | ||||||
|  | 		float SharpenSize = PPComp16.w; | ||||||
| 	#else | 	#else | ||||||
| 		float strengthSharpen = compoSharpenStrength; | 		float strengthSharpen = compoSharpenStrength; | ||||||
|  | 		vec3 SharpenColor = compoSharpenColor; | ||||||
|  | 		float SharpenSize = compoSharpenSize; | ||||||
| 	#endif | 	#endif | ||||||
| 	vec3 col1 = 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) * 1.5, 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) * 1.5, 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) * 1.5, 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; | 	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 | #endif | ||||||
|  |  | ||||||
| #ifdef _CFog | #ifdef _CFog | ||||||
| @ -407,7 +426,11 @@ void main() { | |||||||
| #endif | #endif | ||||||
|  |  | ||||||
| #ifdef _CExposure | #ifdef _CExposure | ||||||
| 	fragColor.rgb += fragColor.rgb * compoExposureStrength; | 	#ifdef _CPostprocess | ||||||
|  | 		fragColor.rgb+=fragColor.rgb*PPComp8.x; | ||||||
|  | 	#else | ||||||
|  | 		fragColor.rgb+= fragColor.rgb*compoExposureStrength; | ||||||
|  | 	#endif | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| #ifdef _CPostprocess | #ifdef _CPostprocess | ||||||
| @ -415,8 +438,13 @@ void main() { | |||||||
| #endif | #endif | ||||||
|  |  | ||||||
| #ifdef _AutoExposure | #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); | 	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 | #endif | ||||||
|  |  | ||||||
| // Clamp color to get rid of INF values that don't work for the tone mapping below | // Clamp color to get rid of INF values that don't work for the tone mapping below | ||||||
| @ -480,9 +508,7 @@ fragColor.rgb = min(fragColor.rgb, 65504 * 0.5); | |||||||
| 			fragColor.rgb = pow(fragColor.rgb, vec3(1.0 / 2.2)); // To gamma | 			fragColor.rgb = pow(fragColor.rgb, vec3(1.0 / 2.2)); // To gamma | ||||||
| 		} else if (PPComp4.x == 10){ | 		} else if (PPComp4.x == 10){ | ||||||
| 			fragColor.rgb = tonemapAgXFull(fragColor.rgb); | 			fragColor.rgb = tonemapAgXFull(fragColor.rgb); | ||||||
| 		} else { | 		} //else { fragColor.rgb = vec3(0,1,0); //ERROR} | ||||||
| 			fragColor.rgb = vec3(0,1,0); //ERROR |  | ||||||
| 		} |  | ||||||
| 	#endif | 	#endif | ||||||
|  |  | ||||||
| #else | #else | ||||||
|  | |||||||
| @ -235,6 +235,16 @@ | |||||||
| 					"name": "PPComp15", | 					"name": "PPComp15", | ||||||
| 					"link": "_PPComp15", | 					"link": "_PPComp15", | ||||||
| 					"ifdef": ["_CPostprocess"] | 					"ifdef": ["_CPostprocess"] | ||||||
|  | 				}, | ||||||
|  | 				{ | ||||||
|  | 					"name": "PPComp16", | ||||||
|  | 					"link": "_PPComp16", | ||||||
|  | 					"ifdef": ["_CPostprocess"] | ||||||
|  | 				}, | ||||||
|  | 				{ | ||||||
|  | 					"name": "PPComp18", | ||||||
|  | 					"link": "_PPComp18", | ||||||
|  | 					"ifdef": ["_CPostprocess"] | ||||||
| 				} | 				} | ||||||
| 			], | 			], | ||||||
| 			"texture_params": [], | 			"texture_params": [], | ||||||
|  | |||||||
| @ -20,7 +20,7 @@ uniform sampler2D gbuffer0; | |||||||
| uniform sampler2D gbuffer1; | uniform sampler2D gbuffer1; | ||||||
|  |  | ||||||
| #ifdef _gbuffer2 | #ifdef _gbuffer2 | ||||||
| 	//!uniform sampler2D gbuffer2; | 	uniform sampler2D gbuffer2; | ||||||
| #endif | #endif | ||||||
| #ifdef _EmissionShaded | #ifdef _EmissionShaded | ||||||
| 	uniform sampler2D gbufferEmission; | 	uniform sampler2D gbufferEmission; | ||||||
| @ -29,10 +29,11 @@ uniform sampler2D gbuffer1; | |||||||
| #ifdef _VoxelGI | #ifdef _VoxelGI | ||||||
| uniform sampler2D voxels_diffuse; | uniform sampler2D voxels_diffuse; | ||||||
| uniform sampler2D voxels_specular; | uniform sampler2D voxels_specular; | ||||||
| #endif | #else | ||||||
| #ifdef _VoxelAOvar | #ifdef _VoxelAOvar | ||||||
| uniform sampler2D voxels_ao; | uniform sampler2D voxels_ao; | ||||||
| #endif | #endif | ||||||
|  | #endif | ||||||
| #ifdef _VoxelShadow | #ifdef _VoxelShadow | ||||||
| uniform sampler3D voxels; | uniform sampler3D voxels; | ||||||
| uniform sampler3D voxelsSDF; | uniform sampler3D voxelsSDF; | ||||||
| @ -56,6 +57,10 @@ uniform vec3 backgroundCol; | |||||||
|  |  | ||||||
| #ifdef _SSAO | #ifdef _SSAO | ||||||
| uniform sampler2D ssaotex; | uniform sampler2D ssaotex; | ||||||
|  | #else | ||||||
|  | #ifdef _SSGI | ||||||
|  | uniform sampler2D ssaotex; | ||||||
|  | #endif | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| #ifdef _SSS | #ifdef _SSS | ||||||
| @ -113,11 +118,15 @@ uniform vec2 cameraPlane; | |||||||
| #ifdef _SinglePoint | #ifdef _SinglePoint | ||||||
| 	#ifdef _Spot | 	#ifdef _Spot | ||||||
| 	//!uniform sampler2DShadow shadowMapSpot[1]; | 	//!uniform sampler2DShadow shadowMapSpot[1]; | ||||||
|  | 	#ifdef _ShadowMapTransparent | ||||||
| 	//!uniform sampler2D shadowMapSpotTransparent[1]; | 	//!uniform sampler2D shadowMapSpotTransparent[1]; | ||||||
|  | 	#endif | ||||||
| 	//!uniform mat4 LWVPSpot[1]; | 	//!uniform mat4 LWVPSpot[1]; | ||||||
| 	#else | 	#else | ||||||
| 	//!uniform samplerCubeShadow shadowMapPoint[1]; | 	//!uniform samplerCubeShadow shadowMapPoint[1]; | ||||||
|  | 	#ifdef _ShadowMapTransparent | ||||||
| 	//!uniform samplerCube shadowMapPointTransparent[1]; | 	//!uniform samplerCube shadowMapPointTransparent[1]; | ||||||
|  | 	#endif | ||||||
| 	//!uniform vec2 lightProj; | 	//!uniform vec2 lightProj; | ||||||
| 	#endif | 	#endif | ||||||
| #endif | #endif | ||||||
| @ -125,30 +134,40 @@ uniform vec2 cameraPlane; | |||||||
| 	#ifdef _ShadowMapAtlas | 	#ifdef _ShadowMapAtlas | ||||||
| 		#ifdef _SingleAtlas | 		#ifdef _SingleAtlas | ||||||
| 		uniform sampler2DShadow shadowMapAtlas; | 		uniform sampler2DShadow shadowMapAtlas; | ||||||
|  | 		#ifdef _ShadowMapTransparent | ||||||
| 		uniform sampler2D shadowMapAtlasTransparent; | 		uniform sampler2D shadowMapAtlasTransparent; | ||||||
| 		#endif | 		#endif | ||||||
|  | 		#endif | ||||||
| 	#endif | 	#endif | ||||||
| 	#ifdef _ShadowMapAtlas | 	#ifdef _ShadowMapAtlas | ||||||
| 		#ifndef _SingleAtlas | 		#ifndef _SingleAtlas | ||||||
| 		//!uniform sampler2DShadow shadowMapAtlasPoint; | 		//!uniform sampler2DShadow shadowMapAtlasPoint; | ||||||
|  | 		#ifdef _ShadowMapTransparent | ||||||
| 		//!uniform sampler2D shadowMapAtlasPointTransparent; | 		//!uniform sampler2D shadowMapAtlasPointTransparent; | ||||||
| 		#endif | 		#endif | ||||||
| 		//!uniform vec4 pointLightDataArray[4]; | 		#endif | ||||||
|  | 		//!uniform vec4 pointLightDataArray[maxLightsCluster * 6]; | ||||||
| 	#else | 	#else | ||||||
| 		//!uniform samplerCubeShadow shadowMapPoint[4]; | 		//!uniform samplerCubeShadow shadowMapPoint[4]; | ||||||
|  | 		#ifdef _ShadowMapTransparent | ||||||
| 		//!uniform samplerCube shadowMapPointTransparent[4]; | 		//!uniform samplerCube shadowMapPointTransparent[4]; | ||||||
|  | 		#endif | ||||||
| 	#endif | 	#endif | ||||||
| 	//!uniform vec2 lightProj; | 	//!uniform vec2 lightProj; | ||||||
| 	#ifdef _Spot | 	#ifdef _Spot | ||||||
| 		#ifdef _ShadowMapAtlas | 		#ifdef _ShadowMapAtlas | ||||||
| 		#ifndef _SingleAtlas | 		#ifndef _SingleAtlas | ||||||
| 		//!uniform sampler2DShadow shadowMapAtlasSpot; | 		//!uniform sampler2DShadow shadowMapAtlasSpot; | ||||||
|  | 		#ifdef _ShadowMapTransparent | ||||||
| 		//!uniform sampler2D shadowMapAtlasSpotTransparent; | 		//!uniform sampler2D shadowMapAtlasSpotTransparent; | ||||||
| 		#endif | 		#endif | ||||||
|  | 		#endif | ||||||
| 		#else | 		#else | ||||||
| 		//!uniform sampler2DShadow shadowMapSpot[4]; | 		//!uniform sampler2DShadow shadowMapSpot[4]; | ||||||
|  | 		#ifdef _ShadowMapTransparent | ||||||
| 		//!uniform sampler2D shadowMapSpotTransparent[4]; | 		//!uniform sampler2D shadowMapSpotTransparent[4]; | ||||||
| 		#endif | 		#endif | ||||||
|  | 		#endif | ||||||
| 	//!uniform mat4 LWVPSpotArray[maxLightsCluster]; | 	//!uniform mat4 LWVPSpotArray[maxLightsCluster]; | ||||||
| 	#endif | 	#endif | ||||||
| #endif | #endif | ||||||
| @ -161,12 +180,16 @@ uniform vec3 sunCol; | |||||||
| 	#ifdef _ShadowMapAtlas | 	#ifdef _ShadowMapAtlas | ||||||
| 	#ifndef _SingleAtlas | 	#ifndef _SingleAtlas | ||||||
| 	uniform sampler2DShadow shadowMapAtlasSun; | 	uniform sampler2DShadow shadowMapAtlasSun; | ||||||
|  | 	#ifdef _ShadowMapTransparent | ||||||
| 	uniform sampler2D shadowMapAtlasSunTransparent; | 	uniform sampler2D shadowMapAtlasSunTransparent; | ||||||
| 	#endif | 	#endif | ||||||
|  | 	#endif | ||||||
| 	#else | 	#else | ||||||
| 	uniform sampler2DShadow shadowMap; | 	uniform sampler2DShadow shadowMap; | ||||||
|  | 	#ifdef _ShadowMapTransparent | ||||||
| 	uniform sampler2D shadowMapTransparent; | 	uniform sampler2D shadowMapTransparent; | ||||||
| 	#endif | 	#endif | ||||||
|  | 	#endif | ||||||
| 	uniform float shadowsBias; | 	uniform float shadowsBias; | ||||||
| 	#ifdef _CSM | 	#ifdef _CSM | ||||||
| 	//!uniform vec4 casData[shadowmapCascades * 4 + 4]; | 	//!uniform vec4 casData[shadowmapCascades * 4 + 4]; | ||||||
| @ -227,17 +250,22 @@ void main() { | |||||||
| 	vec4 g2 = textureLod(gbuffer2, texCoord, 0.0); | 	vec4 g2 = textureLod(gbuffer2, texCoord, 0.0); | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
|  |  | ||||||
| #ifdef _MicroShadowing | #ifdef _MicroShadowing | ||||||
| 	occspec.x = mix(1.0, occspec.x, dotNV); // AO Fresnel | 	occspec.x = mix(1.0, occspec.x, dotNV); // AO Fresnel | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| #ifdef _Brdf | #ifdef _Brdf | ||||||
| 	vec2 envBRDF = texelFetch(senvmapBrdf, ivec2(vec2(dotNV, 1.0 - roughness) * 256.0), 0).xy; | 	vec2 envBRDF = texelFetch(senvmapBrdf, ivec2(vec2(dotNV, 1.0 - roughness) * 256.0), 0).xy; | ||||||
|  | 	vec3 F = f0 * envBRDF.x + envBRDF.y; | ||||||
|  | #else | ||||||
|  | 	vec3 F = f0; | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
|  | #ifndef _VoxelAOvar | ||||||
|  | #ifndef _VoxelGI | ||||||
| 	// Envmap | 	// Envmap | ||||||
| #ifdef _Irr | #ifdef _Irr | ||||||
|  |  | ||||||
| 	vec3 envl = shIrradiance(n, shirr); | 	vec3 envl = shIrradiance(n, shirr); | ||||||
|  |  | ||||||
| 	#ifdef _gbuffer2 | 	#ifdef _gbuffer2 | ||||||
| @ -271,33 +299,33 @@ void main() { | |||||||
| 	envl.rgb *= albedo; | 	envl.rgb *= albedo; | ||||||
|  |  | ||||||
| #ifdef _Brdf | #ifdef _Brdf | ||||||
| 	envl.rgb *= 1.0 - (f0 * envBRDF.x + envBRDF.y); //LV: We should take refracted light into account | 	envl.rgb *= 1.0 - F; //LV: We should take refracted light into account | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| #ifdef _Rad // Indirect specular | #ifdef _Rad // Indirect specular | ||||||
| 	envl.rgb += prefilteredColor * (f0 * envBRDF.x + envBRDF.y); //LV: Removed "1.5 * occspec.y". Specular should be weighted only by FV LUT | 	envl.rgb += prefilteredColor * F; //LV: Removed "1.5 * occspec.y". Specular should be weighted only by FV LUT | ||||||
| #else | #else | ||||||
| 	#ifdef _EnvCol | 	#ifdef _EnvCol | ||||||
| 	envl.rgb += backgroundCol * (f0 * envBRDF.x + envBRDF.y); //LV: Eh, what's the point of weighting it only by F0? | 	envl.rgb += backgroundCol * F; //LV: Eh, what's the point of weighting it only by F0? | ||||||
| 	#endif | 	#endif | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| 	envl.rgb *= envmapStrength * occspec.x; | 	envl.rgb *= envmapStrength * occspec.x; | ||||||
|  |  | ||||||
| #ifdef _VoxelGI |  | ||||||
| 	vec4 indirect_diffuse = textureLod(voxels_diffuse, texCoord, 0.0); |  | ||||||
| 	fragColor.rgb = (indirect_diffuse.rgb + envl.rgb * (1.0 - indirect_diffuse.a)) * albedo * voxelgiDiff; |  | ||||||
| 	if(roughness < 1.0 && occspec.y > 0.0) |  | ||||||
| 		fragColor.rgb += textureLod(voxels_specular, texCoord, 0.0).rgb * occspec.y * voxelgiRefl; |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
| #ifdef _VoxelAOvar |  | ||||||
| 	envl.rgb *= textureLod(voxels_ao, texCoord, 0.0).r; |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
| #ifndef _VoxelGI |  | ||||||
| 	fragColor.rgb = envl; | 	fragColor.rgb = envl; | ||||||
| #endif | #endif | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | #ifdef _VoxelGI | ||||||
|  | 	fragColor.rgb = textureLod(voxels_diffuse, texCoord, 0.0).rgb * voxelgiDiff; | ||||||
|  | 	if(roughness < 1.0 && occspec.y > 0.0) | ||||||
|  | 		fragColor.rgb += textureLod(voxels_specular, texCoord, 0.0).rgb * occspec.y * voxelgiRefl; | ||||||
|  | #else | ||||||
|  | #ifdef _VoxelAOvar | ||||||
|  | 	fragColor.rgb = textureLod(voxels_ao, texCoord, 0.0).rgb * voxelgiOcc; | ||||||
|  | #endif | ||||||
|  | #endif | ||||||
|  |  | ||||||
| 	// Show voxels | 	// Show voxels | ||||||
| 	// vec3 origin = vec3(texCoord * 2.0 - 1.0, 0.99); | 	// vec3 origin = vec3(texCoord * 2.0 - 1.0, 0.99); | ||||||
| 	// vec3 direction = vec3(0.0, 0.0, -1.0); | 	// vec3 direction = vec3(0.0, 0.0, -1.0); | ||||||
| @ -317,6 +345,10 @@ void main() { | |||||||
| 	// #else | 	// #else | ||||||
| 	fragColor.rgb *= textureLod(ssaotex, texCoord, 0.0).r; | 	fragColor.rgb *= textureLod(ssaotex, texCoord, 0.0).r; | ||||||
| 	// #endif | 	// #endif | ||||||
|  | #else | ||||||
|  | #ifdef _SSGI | ||||||
|  | 	fragColor.rgb += textureLod(ssaotex, texCoord, 0.0).rgb; | ||||||
|  | #endif | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| #ifdef _EmissionShadeless | #ifdef _EmissionShadeless | ||||||
| @ -349,40 +381,70 @@ void main() { | |||||||
| 	#ifdef _ShadowMap | 	#ifdef _ShadowMap | ||||||
| 		#ifdef _CSM | 		#ifdef _CSM | ||||||
| 			svisibility = shadowTestCascade( | 			svisibility = shadowTestCascade( | ||||||
| 				#ifdef _ShadowMapAtlas | 											#ifdef _ShadowMapAtlas | ||||||
| 					#ifndef _SingleAtlas | 												#ifdef _ShadowMapTransparent | ||||||
| 					shadowMapAtlasSun, shadowMapAtlasSunTransparent | 												#ifndef _SingleAtlas | ||||||
| 					#else | 												shadowMapAtlasSun, shadowMapAtlasSunTransparent | ||||||
| 					shadowMapAtlas, shadowMapAtlasTransparent | 												#else | ||||||
| 					#endif | 												shadowMapAtlas, shadowMapAtlasTransparent | ||||||
| 				#else | 												#endif | ||||||
| 				shadowMap, shadowMapTransparent | 												#else | ||||||
| 				#endif | 												#ifndef _SingleAtlas | ||||||
| 				, eye, p + n * shadowsBias * 10, shadowsBias, false | 												shadowMapAtlasSun | ||||||
| 			); | 												#else | ||||||
|  | 												shadowMapAtlas | ||||||
|  | 												#endif | ||||||
|  | 												#endif | ||||||
|  | 											#else | ||||||
|  | 											#ifdef _ShadowMapTransparent | ||||||
|  | 											shadowMap, shadowMapTransparent | ||||||
|  | 											#else | ||||||
|  | 											shadowMap | ||||||
|  | 											#endif | ||||||
|  | 											#endif | ||||||
|  | 											, eye, p + n * shadowsBias * 10, shadowsBias | ||||||
|  | 											#ifdef _ShadowMapTransparent | ||||||
|  | 											, false | ||||||
|  | 											#endif | ||||||
|  | 											); | ||||||
| 		#else | 		#else | ||||||
| 			vec4 lPos = LWVP * vec4(p + n * shadowsBias * 100, 1.0); | 			vec4 lPos = LWVP * vec4(p + n * shadowsBias * 100, 1.0); | ||||||
| 			if (lPos.w > 0.0) { | 			if (lPos.w > 0.0) { | ||||||
| 				svisibility = shadowTest( | 				svisibility = shadowTest( | ||||||
| 					#ifdef _ShadowMapAtlas | 										#ifdef _ShadowMapAtlas | ||||||
| 						#ifndef _SingleAtlas | 											#ifdef _ShadowMapTransparent | ||||||
| 						shadowMapAtlasSun, shadowMapAtlasSunTransparent | 											#ifndef _SingleAtlas | ||||||
| 						#else | 											shadowMapAtlasSun, shadowMapAtlasSunTransparent | ||||||
| 						shadowMapAtlas, shadowMapAtlasTransparent | 											#else | ||||||
| 						#endif | 											shadowMapAtlas, shadowMapAtlasTransparent | ||||||
| 					#else | 											#endif | ||||||
| 					shadowMap, shadowMapTransparent | 											#else | ||||||
| 					#endif | 											#ifndef _SingleAtlas | ||||||
| 					, lPos.xyz / lPos.w, shadowsBias, false | 											shadowMapAtlasSun | ||||||
| 				); | 											#else | ||||||
|  | 											shadowMapAtlas | ||||||
|  | 											#endif | ||||||
|  | 											#endif | ||||||
|  | 										#else | ||||||
|  | 										#ifdef _ShadowMapTransparent | ||||||
|  | 										shadowMap, shadowMapTransparent | ||||||
|  | 										#else | ||||||
|  | 										shadowMap | ||||||
|  | 										#endif | ||||||
|  | 										#endif | ||||||
|  | 										, lPos.xyz / lPos.w, shadowsBias | ||||||
|  | 										#ifdef _ShadowMapTransparent | ||||||
|  | 										, false | ||||||
|  | 										#endif | ||||||
|  | 										); | ||||||
| 			} | 			} | ||||||
| 		#endif | 		#endif | ||||||
| 	#endif | 	#endif | ||||||
|  |  | ||||||
| 	#ifdef _VoxelShadow | 	#ifdef _VoxelShadow | ||||||
| 	svisibility *= (1.0 - traceShadow(p, n, voxels, voxelsSDF, sunDir, clipmaps, gl_FragCoord.xy, g2.rg).r) * voxelgiShad; | 	svisibility *= (1.0 - traceShadow(p, n, voxels, voxelsSDF, sunDir, clipmaps, gl_FragCoord.xy, -g2.rg).r) * voxelgiShad; | ||||||
| 	#endif | 	#endif | ||||||
| 	 |  | ||||||
| 	#ifdef _SSRS | 	#ifdef _SSRS | ||||||
| 	// vec2 coords = getProjectedCoord(hitCoord); | 	// vec2 coords = getProjectedCoord(hitCoord); | ||||||
| 	// vec2 deltaCoords = abs(vec2(0.5, 0.5) - coords.xy); | 	// vec2 deltaCoords = abs(vec2(0.5, 0.5) - coords.xy); | ||||||
| @ -439,13 +501,16 @@ void main() { | |||||||
| 	fragColor.rgb += sampleLight( | 	fragColor.rgb += sampleLight( | ||||||
| 		p, n, v, dotNV, pointPos, pointCol, albedo, roughness, occspec.y, f0 | 		p, n, v, dotNV, pointPos, pointCol, albedo, roughness, occspec.y, f0 | ||||||
| 		#ifdef _ShadowMap | 		#ifdef _ShadowMap | ||||||
| 			, 0, pointBias, true, false | 			, 0, pointBias, true | ||||||
|  | 		#ifdef _ShadowMapTransparent | ||||||
|  | 			, false | ||||||
|  | 		#endif | ||||||
| 		#endif | 		#endif | ||||||
| 		#ifdef _Spot | 		#ifdef _Spot | ||||||
| 		, true, spotData.x, spotData.y, spotDir, spotData.zw, spotRight | 		, true, spotData.x, spotData.y, spotDir, spotData.zw, spotRight | ||||||
| 		#endif | 		#endif | ||||||
| 		#ifdef _VoxelShadow | 		#ifdef _VoxelShadow | ||||||
| 			, voxels, voxelsSDF, clipmaps | 			, voxels, voxelsSDF, clipmaps, -g2.rg | ||||||
| 		#endif | 		#endif | ||||||
| 		#ifdef _MicroShadowing | 		#ifdef _MicroShadowing | ||||||
| 		, occspec.x | 		, occspec.x | ||||||
| @ -492,7 +557,10 @@ void main() { | |||||||
| 			f0 | 			f0 | ||||||
| 			#ifdef _ShadowMap | 			#ifdef _ShadowMap | ||||||
| 				// light index, shadow bias, cast_shadows | 				// light index, shadow bias, cast_shadows | ||||||
| 				, li, lightsArray[li * 3 + 2].x, lightsArray[li * 3 + 2].z != 0.0, false | 				, li, lightsArray[li * 3 + 2].x, lightsArray[li * 3 + 2].z != 0.0 | ||||||
|  | 			#ifdef _ShadowMapTransparent | ||||||
|  | 				, false | ||||||
|  | 			#endif | ||||||
| 			#endif | 			#endif | ||||||
| 			#ifdef _Spot | 			#ifdef _Spot | ||||||
| 			, lightsArray[li * 3 + 2].y != 0.0 | 			, lightsArray[li * 3 + 2].y != 0.0 | ||||||
| @ -503,7 +571,7 @@ void main() { | |||||||
| 			, lightsArraySpot[li * 2 + 1].xyz // right | 			, lightsArraySpot[li * 2 + 1].xyz // right | ||||||
| 			#endif | 			#endif | ||||||
| 			#ifdef _VoxelShadow | 			#ifdef _VoxelShadow | ||||||
| 			, voxels, voxelsSDF, clipmaps | 			, voxels, voxelsSDF, clipmaps, -g2.rg | ||||||
| 			#endif | 			#endif | ||||||
| 			#ifdef _MicroShadowing | 			#ifdef _MicroShadowing | ||||||
| 			, occspec.x | 			, occspec.x | ||||||
| @ -514,14 +582,5 @@ void main() { | |||||||
| 		); | 		); | ||||||
| 	} | 	} | ||||||
| #endif // _Clusters | #endif // _Clusters | ||||||
|  |  | ||||||
| /* |  | ||||||
| #ifdef _VoxelRefract |  | ||||||
| if(opac < 1.0) { |  | ||||||
| 	vec3 refraction = traceRefraction(p, n, voxels, v, ior, roughness, eye) * voxelgiRefr; |  | ||||||
| 	fragColor.rgb = mix(refraction, fragColor.rgb, opac); |  | ||||||
| } |  | ||||||
| #endif |  | ||||||
| */ |  | ||||||
| 	fragColor.a = 1.0; // Mark as opaque | 	fragColor.a = 1.0; // Mark as opaque | ||||||
| } | } | ||||||
|  | |||||||
| @ -2,13 +2,22 @@ | |||||||
|  |  | ||||||
| #include "compiled.inc" | #include "compiled.inc" | ||||||
|  |  | ||||||
|  | #ifdef _CPostprocess | ||||||
|  | uniform vec3 PPComp8; | ||||||
|  | #endif | ||||||
|  |  | ||||||
| uniform sampler2D tex; | uniform sampler2D tex; | ||||||
|  |  | ||||||
| in vec2 texCoord; | in vec2 texCoord; | ||||||
| out vec4 fragColor; | out vec4 fragColor; | ||||||
|  |  | ||||||
| void main() { | 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 + | 	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.2, 0.2), 0.0).rgb + | ||||||
| 					textureLod(tex, vec2(0.8, 0.2), 0.0).rgb + | 					textureLod(tex, vec2(0.8, 0.2), 0.0).rgb + | ||||||
|  | |||||||
| @ -8,7 +8,13 @@ | |||||||
| 			"blend_source": "source_alpha", | 			"blend_source": "source_alpha", | ||||||
| 			"blend_destination": "inverse_source_alpha", | 			"blend_destination": "inverse_source_alpha", | ||||||
| 			"blend_operation": "add", | 			"blend_operation": "add", | ||||||
| 			"links": [], | 			"links": [ | ||||||
|  | 				{ | ||||||
|  | 					"name": "PPComp8", | ||||||
|  | 					"link": "_PPComp8", | ||||||
|  | 					"ifdef": ["_CPostprocess"] | ||||||
|  | 				} | ||||||
|  | 			], | ||||||
| 			"texture_params": [], | 			"texture_params": [], | ||||||
| 			"vertex_shader": "../include/pass.vert.glsl", | 			"vertex_shader": "../include/pass.vert.glsl", | ||||||
| 			"fragment_shader": "histogram_pass.frag.glsl" | 			"fragment_shader": "histogram_pass.frag.glsl" | ||||||
|  | |||||||
| @ -1,107 +1,506 @@ | |||||||
| #version 450 | #version 450 | ||||||
|  |  | ||||||
| #include "compiled.inc" | #include "compiled.inc" | ||||||
| #include "std/math.glsl" |  | ||||||
| #include "std/gbuffer.glsl" | #include "std/gbuffer.glsl" | ||||||
|  | #include "std/brdf.glsl" | ||||||
|  | #include "std/math.glsl" | ||||||
|  | #ifdef _Clusters | ||||||
|  | #include "std/clusters.glsl" | ||||||
|  | #endif | ||||||
|  | #ifdef _ShadowMap | ||||||
|  | #include "std/shadows.glsl" | ||||||
|  | #endif | ||||||
|  | #ifdef _LTC | ||||||
|  | #include "std/ltc.glsl" | ||||||
|  | #endif | ||||||
|  | #ifdef _LightIES | ||||||
|  | #include "std/ies.glsl" | ||||||
|  | #endif | ||||||
|  | #ifdef _Spot | ||||||
|  | #include "std/light_common.glsl" | ||||||
|  | #endif | ||||||
|  | #include "std/constants.glsl" | ||||||
|  |  | ||||||
|  | uniform sampler2D gbuffer0; | ||||||
|  | uniform sampler2D gbuffer1; | ||||||
| uniform sampler2D gbufferD; | uniform sampler2D gbufferD; | ||||||
| uniform sampler2D gbuffer0; // Normal | #ifdef _EmissionShaded | ||||||
| // #ifdef _RTGI | uniform sampler2D gbufferEmission; | ||||||
| // uniform sampler2D gbuffer1; // Basecol | #endif | ||||||
| // #endif | uniform sampler2D sveloc; | ||||||
| uniform mat4 P; |  | ||||||
| uniform mat3 V3; |  | ||||||
|  |  | ||||||
| uniform vec2 cameraProj; | uniform vec2 cameraProj; | ||||||
|  | uniform vec3 eye; | ||||||
|  | uniform vec3 eyeLook; | ||||||
|  | uniform vec2 screenSize; | ||||||
|  | uniform mat4 invVP; | ||||||
|  |  | ||||||
| const float angleMix = 0.5f; | in vec2 texCoord; | ||||||
| #ifdef _SSGICone9 | in vec3 viewRay; | ||||||
| const float strength = 2.0 * (1.0 / ssgiStrength); | out vec3 fragColor; | ||||||
| #else |  | ||||||
| const float strength = 2.0 * (1.0 / ssgiStrength) * 1.8; | float metallic; | ||||||
|  | uint matid; | ||||||
|  |  | ||||||
|  | #ifdef _SMSizeUniform | ||||||
|  | //!uniform vec2 smSizeUniform; | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| in vec3 viewRay; | #ifdef _Clusters | ||||||
| in vec2 texCoord; | uniform vec4 lightsArray[maxLights * 3]; | ||||||
| out float fragColor; | 	#ifdef _Spot | ||||||
|  | 	uniform vec4 lightsArraySpot[maxLights * 2]; | ||||||
| vec3 hitCoord; |  | ||||||
| vec2 coord; |  | ||||||
| float depth; |  | ||||||
| // #ifdef _RTGI |  | ||||||
| // vec3 col = vec3(0.0); |  | ||||||
| // #endif |  | ||||||
| vec3 vpos; |  | ||||||
|  |  | ||||||
| vec2 getProjectedCoord(vec3 hitCoord) { |  | ||||||
| 	vec4 projectedCoord = P * vec4(hitCoord, 1.0); |  | ||||||
| 	projectedCoord.xy /= projectedCoord.w; |  | ||||||
| 	projectedCoord.xy = projectedCoord.xy * 0.5 + 0.5; |  | ||||||
| 	#ifdef _InvY |  | ||||||
| 	projectedCoord.y = 1.0 - projectedCoord.y; |  | ||||||
| 	#endif | 	#endif | ||||||
| 	return projectedCoord.xy; | uniform sampler2D clustersData; | ||||||
| } | uniform vec2 cameraPlane; | ||||||
|  | #endif | ||||||
|  |  | ||||||
| float getDeltaDepth(vec3 hitCoord) { | #ifdef _SinglePoint // Fast path for single light | ||||||
| 	coord = getProjectedCoord(hitCoord); | uniform vec3 pointPos; | ||||||
| 	depth = textureLod(gbufferD, coord, 0.0).r * 2.0 - 1.0; | uniform vec3 pointCol; | ||||||
| 	vec3 p = getPosView(viewRay, depth, cameraProj); | 	#ifdef _ShadowMap | ||||||
| 	return p.z - hitCoord.z; | 	uniform float pointBias; | ||||||
| } | 	#endif | ||||||
|  | 	#ifdef _Spot | ||||||
|  | 	uniform vec3 spotDir; | ||||||
|  | 	uniform vec3 spotRight; | ||||||
|  | 	uniform vec4 spotData; | ||||||
|  | 	#endif | ||||||
|  | #endif | ||||||
|  |  | ||||||
| void rayCast(vec3 dir) { | #ifdef _CPostprocess | ||||||
| 	hitCoord = vpos; |     uniform vec3 PPComp12; | ||||||
| 	dir *= ssgiRayStep * 2; | #endif | ||||||
| 	float dist = 0.15; |  | ||||||
| 	for (int i = 0; i < ssgiMaxSteps; i++) { | #ifdef _ShadowMap | ||||||
| 		hitCoord += dir; | 	#ifdef _SinglePoint | ||||||
| 		float delta = getDeltaDepth(hitCoord); | 		#ifdef _Spot | ||||||
| 		if (delta > 0.0 && delta < 0.2) { | 			#ifndef _LTC | ||||||
| 			dist = distance(vpos, hitCoord); | 				uniform sampler2DShadow shadowMapSpot[1]; | ||||||
| 			break; | 				uniform sampler2D shadowMapSpotTransparent[1]; | ||||||
|  | 				uniform mat4 LWVPSpot[1]; | ||||||
|  | 			#endif | ||||||
|  | 		#else | ||||||
|  | 			uniform samplerCubeShadow shadowMapPoint[1]; | ||||||
|  | 			uniform samplerCube shadowMapPointTransparent[1]; | ||||||
|  | 			uniform vec2 lightProj; | ||||||
|  | 		#endif | ||||||
|  | 	#endif | ||||||
|  | 	#ifdef _Clusters | ||||||
|  | 		#ifdef _SingleAtlas | ||||||
|  | 		uniform sampler2DShadow shadowMapAtlas; | ||||||
|  | 		uniform sampler2D shadowMapAtlasTransparent; | ||||||
|  | 		#endif | ||||||
|  | 		uniform vec2 lightProj; | ||||||
|  | 		#ifdef _ShadowMapAtlas | ||||||
|  | 		#ifndef _SingleAtlas | ||||||
|  | 		uniform sampler2DShadow shadowMapAtlasPoint; | ||||||
|  | 		uniform sampler2D shadowMapAtlasPointTransparent; | ||||||
|  | 		//!uniform vec4 pointLightDataArray[maxLightsCluster * 6]; | ||||||
|  | 		#else | ||||||
|  | 		uniform samplerCubeShadow shadowMapPoint[4]; | ||||||
|  | 		uniform samplerCube shadowMapPointTransparent[4]; | ||||||
|  | 		#endif | ||||||
|  | 		#endif | ||||||
|  | 		#ifdef _Spot | ||||||
|  | 			#ifdef _ShadowMapAtlas | ||||||
|  | 			#ifndef _SingleAtlas | ||||||
|  | 			uniform sampler2DShadow shadowMapAtlasSpot; | ||||||
|  | 			uniform sampler2D shadowMapAtlasSpotTransparent; | ||||||
|  | 			#endif | ||||||
|  | 			#else | ||||||
|  | 			uniform sampler2DShadow shadowMapSpot[4]; | ||||||
|  | 			uniform sampler2D shadowMapSpotTransparent[4]; | ||||||
|  | 			#endif | ||||||
|  | 			uniform mat4 LWVPSpotArray[maxLightsCluster]; | ||||||
|  | 		#endif | ||||||
|  | 	#endif | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | #ifdef _LTC | ||||||
|  | uniform vec3 lightArea0; | ||||||
|  | uniform vec3 lightArea1; | ||||||
|  | uniform vec3 lightArea2; | ||||||
|  | uniform vec3 lightArea3; | ||||||
|  | uniform sampler2D sltcMat; | ||||||
|  | uniform sampler2D sltcMag; | ||||||
|  | #ifdef _ShadowMap | ||||||
|  | #ifndef _Spot | ||||||
|  | 	#ifdef _SinglePoint | ||||||
|  | 		uniform sampler2DShadow shadowMapSpot[1]; | ||||||
|  | 		uniform sampler2D shadowMapSpotTransparent[1]; | ||||||
|  | 		uniform mat4 LWVPSpot[1]; | ||||||
|  | 	#endif | ||||||
|  | 	#ifdef _Clusters | ||||||
|  | 		uniform sampler2DShadow shadowMapSpot[maxLightsCluster]; | ||||||
|  | 		uniform mat4 LWVPSpotArray[maxLightsCluster]; | ||||||
|  | 	#endif | ||||||
|  | #endif | ||||||
|  | #endif | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | #ifdef _Sun | ||||||
|  | uniform vec3 sunDir; | ||||||
|  | uniform vec3 sunCol; | ||||||
|  | 	#ifdef _ShadowMap | ||||||
|  | 	#ifdef _ShadowMapAtlas | ||||||
|  | 	#ifndef _SingleAtlas | ||||||
|  | 	uniform sampler2DShadow shadowMapAtlasSun; | ||||||
|  | 	uniform sampler2D shadowMapAtlasSunTransparent; | ||||||
|  | 	#endif | ||||||
|  | 	#else | ||||||
|  | 	uniform sampler2DShadow shadowMap; | ||||||
|  | 	uniform sampler2D shadowMapTransparent; | ||||||
|  | 	#endif | ||||||
|  | 	uniform float shadowsBias; | ||||||
|  | 	#ifdef _CSM | ||||||
|  | 	//!uniform vec4 casData[shadowmapCascades * 4 + 4]; | ||||||
|  | 	#else | ||||||
|  | 	uniform mat4 LWVP; | ||||||
|  | 	#endif | ||||||
|  | 	#endif // _ShadowMap | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | vec3 sampleLight(const vec3 p, const vec3 n, const vec3 lp, const vec3 lightCol | ||||||
|  | 	#ifdef _ShadowMap | ||||||
|  | 		, int index, float bias, bool receiveShadow, bool transparent | ||||||
|  | 	#endif | ||||||
|  | 	#ifdef _Spot | ||||||
|  | 		, const bool isSpot, const float spotSize, float spotBlend, vec3 spotDir, vec2 scale, vec3 right | ||||||
|  | 	#endif | ||||||
|  | 	) { | ||||||
|  |  | ||||||
|  | 	vec3 ld = lp - p; | ||||||
|  | 	vec3 l = normalize(ld); | ||||||
|  |  | ||||||
|  | 	vec3 visibility = lightCol; | ||||||
|  | 	visibility *= attenuate(distance(p, lp)); | ||||||
|  |  | ||||||
|  | 	#ifdef _LTC | ||||||
|  | 	#ifdef _ShadowMap | ||||||
|  | 		if (receiveShadow) { | ||||||
|  | 			#ifdef _SinglePoint | ||||||
|  | 			vec4 lPos = LWVPSpotArray[0] * vec4(p + n * bias * 10, 1.0); | ||||||
|  | 			visibility *= shadowTest(shadowMapSpot[0], | ||||||
|  | 							shadowMapSpotTransparent[0], | ||||||
|  | 							lPos.xyz / lPos.w, bias, transparent); | ||||||
|  | 			#endif | ||||||
|  | 			#ifdef _Clusters | ||||||
|  | 			vec4 lPos = LWVPSpotArray[index] * vec4(p + n * bias * 10, 1.0); | ||||||
|  | 			if (index == 0) visibility *= shadowTest(shadowMapSpot[0], | ||||||
|  | 					shadowMapSpotTransparent[0], | ||||||
|  | 					lPos.xyz / lPos.w, bias, transparent); | ||||||
|  | 			else if (index == 1) visibility *= shadowTest(shadowMapSpot[1], | ||||||
|  | 					shadowMapSpotTransparent[1], | ||||||
|  | 					, lPos.xyz / lPos.w, bias, transparent); | ||||||
|  | 			else if (index == 2) visibility *= shadowTest(shadowMapSpot[2], | ||||||
|  | 					shadowMapSpotTransparent[2], | ||||||
|  | 					lPos.xyz / lPos.w, bias, transparent); | ||||||
|  | 			else if (index == 3) visibility *= shadowTest(shadowMapSpot[3], | ||||||
|  | 					shadowMapSpotTransparent[3], | ||||||
|  | 					lPos.xyz / lPos.w, bias, transparent); | ||||||
|  | 			#endif | ||||||
| 		} | 		} | ||||||
|  | 	#endif | ||||||
|  | 	return visibility; | ||||||
|  | 	#endif | ||||||
|  |  | ||||||
|  | 	#ifdef _Spot | ||||||
|  | 	if (isSpot) { | ||||||
|  | 		visibility *= spotlightMask(l, spotDir, right, scale, spotSize, spotBlend); | ||||||
|  |  | ||||||
|  | 		#ifdef _ShadowMap | ||||||
|  | 			if (receiveShadow) { | ||||||
|  | 				#ifdef _SinglePoint | ||||||
|  | 				vec4 lPos = LWVPSpot[0] * vec4(p + n * bias * 10, 1.0); | ||||||
|  | 				visibility *= shadowTest(shadowMapSpot[0], | ||||||
|  | 									 shadowMapSpotTransparent[0], | ||||||
|  | 									lPos.xyz / lPos.w, bias, transparent); | ||||||
|  | 				#endif | ||||||
|  | 				#ifdef _Clusters | ||||||
|  | 					vec4 lPos = LWVPSpotArray[index] * vec4(p + n * bias * 10, 1.0); | ||||||
|  | 					#ifdef _ShadowMapAtlas | ||||||
|  | 						visibility *= shadowTest( | ||||||
|  | 							#ifndef _SingleAtlas | ||||||
|  | 							shadowMapAtlasSpot, shadowMapAtlasSpotTransparent | ||||||
|  | 							#else | ||||||
|  | 							shadowMapAtlas, shadowMapAtlasTransparent | ||||||
|  | 							#endif | ||||||
|  | 							, lPos.xyz / lPos.w, bias, transparent | ||||||
|  | 						); | ||||||
|  | 					#else | ||||||
|  | 							 if (index == 0) visibility *= shadowTest(shadowMapSpot[0], | ||||||
|  | 									shadowMapSpotTransparent[0], | ||||||
|  | 									lPos.xyz / lPos.w, bias, transparent); | ||||||
|  | 						else if (index == 1) visibility *= shadowTest(shadowMapSpot[1], | ||||||
|  | 									shadowMapSpotTransparent[1], | ||||||
|  | 									lPos.xyz / lPos.w, bias, transparent); | ||||||
|  | 						else if (index == 2) visibility *= shadowTest(shadowMapSpot[2], | ||||||
|  | 									shadowMapSpotTransparent[2], | ||||||
|  | 									lPos.xyz / lPos.w, bias, transparent); | ||||||
|  | 						else if (index == 3) visibility *= shadowTest(shadowMapSpot[3], | ||||||
|  | 									shadowMapSpotTransparent[3], | ||||||
|  | 									lPos.xyz / lPos.w, bias, transparent); | ||||||
|  | 					#endif | ||||||
|  | 				#endif | ||||||
|  | 			} | ||||||
|  | 		#endif | ||||||
|  | 		return visibility; | ||||||
| 	} | 	} | ||||||
| 	fragColor += dist; | 	#endif | ||||||
| 	// #ifdef _RTGI |  | ||||||
| 	// col += textureLod(gbuffer1, coord, 0.0).rgb * ((ssgiRayStep * ssgiMaxSteps) - dist); | 	#ifdef _LightIES | ||||||
| 	// #endif | 	visibility *= iesAttenuation(-l); | ||||||
|  | 	#endif | ||||||
|  |  | ||||||
|  | 	#ifdef _ShadowMap | ||||||
|  | 		if (receiveShadow) { | ||||||
|  | 			#ifdef _SinglePoint | ||||||
|  | 			#ifndef _Spot | ||||||
|  | 			visibility *= PCFCube(shadowMapPoint[0], | ||||||
|  | 							shadowMapPointTransparent[0], | ||||||
|  | 							ld, -l, bias, lightProj, n, transparent); | ||||||
|  | 			#endif | ||||||
|  | 			#endif | ||||||
|  | 			#ifdef _Clusters | ||||||
|  | 				#ifdef _ShadowMapAtlas | ||||||
|  | 				visibility *= PCFFakeCube( | ||||||
|  | 					#ifndef _SingleAtlas | ||||||
|  | 					shadowMapAtlasPoint, shadowMapAtlasPointTransparent | ||||||
|  | 					#else | ||||||
|  | 					shadowMapAtlas, shadowMapAtlasTransparent | ||||||
|  | 					#endif | ||||||
|  | 					, ld, -l, bias, lightProj, n, index, transparent | ||||||
|  | 				); | ||||||
|  | 				#else | ||||||
|  | 					 if (index == 0) visibility *= PCFCube(shadowMapPoint[0], | ||||||
|  | 								shadowMapPointTransparent[0], | ||||||
|  | 								ld, -l, bias, lightProj, n, transparent); | ||||||
|  | 				else if (index == 1) visibility *= PCFCube(shadowMapPoint[1], | ||||||
|  | 								shadowMapPointTransparent[1], | ||||||
|  | 								ld, -l, bias, lightProj, n, transparent); | ||||||
|  | 				else if (index == 2) visibility *= PCFCube(shadowMapPoint[2], | ||||||
|  | 								shadowMapPointTransparent[2], | ||||||
|  | 								ld, -l, bias, lightProj, n, transparent); | ||||||
|  | 				else if (index == 3) visibility *= PCFCube(shadowMapPoint[3], | ||||||
|  | 								shadowMapPointTransparent[3], | ||||||
|  | 								ld, -l, bias, lightProj, n, transparent); | ||||||
|  | 				#endif | ||||||
|  | 			#endif | ||||||
|  | 		} | ||||||
|  | 	#endif | ||||||
|  |  | ||||||
|  | 	return visibility; | ||||||
| } | } | ||||||
|  |  | ||||||
| vec3 tangent(const vec3 n) { | vec3 getVisibility(vec3 p, vec3 n, float depth, vec2 uv) { | ||||||
| 	vec3 t1 = cross(n, vec3(0, 0, 1)); | 		vec3 visibility = vec3(0.0); | ||||||
| 	vec3 t2 = cross(n, vec3(0, 1, 0)); | #ifdef _Sun | ||||||
| 	if (length(t1) > length(t2)) return normalize(t1); | 	#ifdef _ShadowMap | ||||||
| 	else return normalize(t2); | 		#ifdef _CSM | ||||||
|  | 			visibility = shadowTestCascade( | ||||||
|  | 				#ifdef _ShadowMapAtlas | ||||||
|  | 					#ifndef _SingleAtlas | ||||||
|  | 					shadowMapAtlasSun, shadowMapAtlasSunTransparent | ||||||
|  | 					#else | ||||||
|  | 					shadowMapAtlas, shadowMapAtlasTransparent | ||||||
|  | 					#endif | ||||||
|  | 				#else | ||||||
|  | 				shadowMap, shadowMapTransparent | ||||||
|  | 				#endif | ||||||
|  | 				, eye, p + n * shadowsBias * 10, shadowsBias, false | ||||||
|  | 			); | ||||||
|  | 		#else | ||||||
|  | 			vec4 lPos = LWVP * vec4(p + n * shadowsBias * 100, 1.0); | ||||||
|  | 			if (lPos.w > 0.0) { | ||||||
|  | 				visibility = shadowTest( | ||||||
|  | 					#ifdef _ShadowMapAtlas | ||||||
|  | 						#ifndef _SingleAtlas | ||||||
|  | 						shadowMapAtlasSun, shadowMapAtlasSunTransparent | ||||||
|  | 						#else | ||||||
|  | 						shadowMapAtlas, shadowMapAtlasTransparent | ||||||
|  | 						#endif | ||||||
|  | 					#else | ||||||
|  | 					shadowMap, shadowMapTransparent | ||||||
|  | 					#endif | ||||||
|  | 					, lPos.xyz / lPos.w, shadowsBias, false | ||||||
|  | 				); | ||||||
|  | 			} | ||||||
|  | 		#endif | ||||||
|  | 	#endif | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | #ifdef _SinglePoint | ||||||
|  | 	visibility += sampleLight( | ||||||
|  | 		p, n, pointPos, pointCol | ||||||
|  | 		#ifdef _ShadowMap | ||||||
|  | 			, 0, pointBias, true, false | ||||||
|  | 		#endif | ||||||
|  | 		#ifdef _Spot | ||||||
|  | 		, true, spotData.x, spotData.y, spotDir, spotData.zw, spotRight | ||||||
|  | 		#endif | ||||||
|  | 	); | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | #ifdef _Clusters | ||||||
|  | 	float viewz = linearize(depth, cameraProj); | ||||||
|  | 	int clusterI = getClusterI(uv, viewz, cameraPlane); | ||||||
|  | 	int numLights = int(texelFetch(clustersData, ivec2(clusterI, 0), 0).r * 255); | ||||||
|  |  | ||||||
|  | 	#ifdef HLSL | ||||||
|  | 	viewz += textureLod(clustersData, vec2(0.0), 0.0).r * 1e-9; // TODO: krafix bug, needs to generate sampler | ||||||
|  | 	#endif | ||||||
|  |  | ||||||
|  | 	#ifdef _Spot | ||||||
|  | 	int numSpots = int(texelFetch(clustersData, ivec2(clusterI, 1 + maxLightsCluster), 0).r * 255); | ||||||
|  | 	int numPoints = numLights - numSpots; | ||||||
|  | 	#endif | ||||||
|  |  | ||||||
|  | 	for (int i = 0; i < min(numLights, maxLightsCluster); i++) { | ||||||
|  | 		int li = int(texelFetch(clustersData, ivec2(clusterI, i + 1), 0).r * 255); | ||||||
|  | 		visibility += sampleLight( | ||||||
|  | 			p, | ||||||
|  | 			n, | ||||||
|  | 			lightsArray[li * 3].xyz, // lp | ||||||
|  | 			lightsArray[li * 3 + 1].xyz // lightCol | ||||||
|  | 			#ifdef _ShadowMap | ||||||
|  | 				// light index, shadow bias, cast_shadows | ||||||
|  | 				, li, lightsArray[li * 3 + 2].x, lightsArray[li * 3 + 2].z != 0.0, false | ||||||
|  | 			#endif | ||||||
|  | 			#ifdef _Spot | ||||||
|  | 			, lightsArray[li * 3 + 2].y != 0.0 | ||||||
|  | 			, lightsArray[li * 3 + 2].y // spot size (cutoff) | ||||||
|  | 			, lightsArraySpot[li * 2].w // spot blend (exponent) | ||||||
|  | 			, lightsArraySpot[li * 2].xyz // spotDir | ||||||
|  | 			, vec2(lightsArray[li * 3].w, lightsArray[li * 3 + 1].w) // scale | ||||||
|  | 			, lightsArraySpot[li * 2 + 1].xyz // right | ||||||
|  | 			#endif | ||||||
|  | 		); | ||||||
|  | 	} | ||||||
|  | #endif // _Clusters | ||||||
|  | 	return visibility; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | vec3 getWorldPos(vec2 uv, float depth) { | ||||||
|  |     vec4 pos = invVP * vec4(uv * 2.0 - 1.0, depth * 2.0 - 1.0, 1.0); | ||||||
|  |     return pos.xyz / pos.w; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | vec3 getNormal(vec2 uv) { | ||||||
|  |     vec4 g0 = textureLod(gbuffer0, uv, 0.0); | ||||||
|  |     vec2 enc = g0.rg; | ||||||
|  |     vec3 n; | ||||||
|  |     n.z = 1.0 - abs(enc.x) - abs(enc.y); | ||||||
|  |     n.xy = n.z >= 0.0 ? enc.xy : octahedronWrap(enc.xy); | ||||||
|  |     return normalize(n); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | vec3 calculateIndirectLight(vec2 uv, vec3 pos, vec3 normal, float depth) { | ||||||
|  |     // Simplified visibility - replace with your full visibility function if needed | ||||||
|  |     vec3 sampleColor = textureLod(gbuffer1, uv, 0.0).rgb * getVisibility(pos, normal, depth, uv); | ||||||
|  |  | ||||||
|  | 	#ifdef _EmissionShadeless | ||||||
|  | 		if (matid == 1) { // pure emissive material, color stored in basecol | ||||||
|  | 			sampleColor += textureLod(gbuffer1, uv, 0.0).rgb; | ||||||
|  | 		} | ||||||
|  | 	#endif | ||||||
|  | 	#ifdef _EmissionShaded | ||||||
|  | 		#ifdef _EmissionShadeless | ||||||
|  | 		else { | ||||||
|  | 		#endif | ||||||
|  | 			vec3 sampleEmission = textureLod(gbufferEmission, uv, 0.0).rgb; | ||||||
|  | 			sampleColor += sampleEmission; // Emission should be added directly | ||||||
|  | 		#ifdef _EmissionShadeless | ||||||
|  | 		} | ||||||
|  | 		#endif | ||||||
|  | 	#endif | ||||||
|  |  | ||||||
|  | 	return sampleColor; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Improved sampling parameters | ||||||
|  | const float GOLDEN_ANGLE = 2.39996323; | ||||||
|  | const float MAX_DEPTH_DIFFERENCE = 0.9; // More conservative depth threshold | ||||||
|  | const float SAMPLE_BIAS = 0.01; // Small offset to avoid self-occlusion | ||||||
|  |  | ||||||
| void main() { | void main() { | ||||||
| 	fragColor = 0; |     float depth = textureLod(gbufferD, texCoord, 0.0).r; | ||||||
| 	vec4 g0 = textureLod(gbuffer0, texCoord, 0.0); |     if (depth >= 1.0) { | ||||||
| 	float d = textureLod(gbufferD, texCoord, 0.0).r * 2.0 - 1.0; |         fragColor = vec3(0.0); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | 	vec4 g0 = textureLod(gbuffer0, texCoord, 0.0); // Normal.xy, roughness, metallic/matid | ||||||
|  | 	unpackFloatInt16(g0.a, metallic, matid); | ||||||
|  |  | ||||||
|  | 	vec2 velocity = -textureLod(sveloc, texCoord, 0.0).rg; | ||||||
|  |  | ||||||
| 	vec2 enc = g0.rg; |  | ||||||
| 	vec3 n; | 	vec3 n; | ||||||
| 	n.z = 1.0 - abs(enc.x) - abs(enc.y); | 	n.z = 1.0 - abs(g0.x) - abs(g0.y); | ||||||
| 	n.xy = n.z >= 0.0 ? enc.xy : octahedronWrap(enc.xy); | 	n.xy = n.z >= 0.0 ? g0.xy : octahedronWrap(g0.xy); | ||||||
| 	n = normalize(V3 * n); | 	n = normalize(n); | ||||||
|  |  | ||||||
| 	vpos = getPosView(viewRay, d, cameraProj); |     vec3 pos = getWorldPos(texCoord, depth); | ||||||
|  |     vec3 normal = getNormal(texCoord); | ||||||
|  |     vec3 centerColor = textureLod(gbuffer1, texCoord, 0.0).rgb; | ||||||
|  |  | ||||||
| 	rayCast(n); |     float radius = ssaoRadius; | ||||||
| 	vec3 o1 = normalize(tangent(n)); |  | ||||||
| 	vec3 o2 = (cross(o1, n)); |  | ||||||
| 	vec3 c1 = 0.5f * (o1 + o2); |  | ||||||
| 	vec3 c2 = 0.5f * (o1 - o2); |  | ||||||
| 	rayCast(mix(n, o1, angleMix)); |  | ||||||
| 	rayCast(mix(n, o2, angleMix)); |  | ||||||
| 	rayCast(mix(n, -c1, angleMix)); |  | ||||||
| 	rayCast(mix(n, -c2, angleMix)); |  | ||||||
|  |  | ||||||
| 	#ifdef _SSGICone9 |     vec3 gi = vec3(0.0); | ||||||
| 	rayCast(mix(n, -o1, angleMix)); |     float totalWeight = 0.0; | ||||||
| 	rayCast(mix(n, -o2, angleMix)); |     float angle = fract(sin(dot(texCoord, vec2(12.9898, 78.233))) * 100.0); | ||||||
| 	rayCast(mix(n, c1, angleMix)); |  | ||||||
| 	rayCast(mix(n, c2, angleMix)); | 	for (int i = 0; i < ssgiSamples; i++) { | ||||||
|  | 		// Use quasi-random sequence for better coverage | ||||||
|  | 		float r = sqrt((float(i) + 0.5) / float(ssgiSamples)) * radius; | ||||||
|  | 		float a = (float(i) * GOLDEN_ANGLE) + angle; | ||||||
|  |  | ||||||
|  | 		vec2 offset = vec2(cos(a), sin(a)) * r * radius; | ||||||
|  | 		vec2 sampleUV = clamp(texCoord + offset * (BayerMatrix8[int(gl_FragCoord.x + velocity.x) % 8][int(gl_FragCoord.y + velocity.y) % 8] - 0.5) / screenSize, vec2(0.001), vec2(0.999)); | ||||||
|  |  | ||||||
|  | 		float sampleDepth = textureLod(gbufferD, sampleUV, 0.0).r; | ||||||
|  | 		if (sampleDepth >= 1.0) continue; | ||||||
|  |  | ||||||
|  | 		vec3 samplePos = getWorldPos(sampleUV, sampleDepth); | ||||||
|  | 		vec3 sampleNormal = getNormal(sampleUV); | ||||||
|  |  | ||||||
|  | 		// Apply small bias to sample position to avoid self-occlusion | ||||||
|  | 		samplePos += sampleNormal * SAMPLE_BIAS; | ||||||
|  |  | ||||||
|  | 		vec3 dir = pos - samplePos; | ||||||
|  | 		float dist = length(dir); | ||||||
|  |  | ||||||
|  | 		if (abs(pos.z - samplePos.z) > MAX_DEPTH_DIFFERENCE) continue;; | ||||||
|  |  | ||||||
|  | 		vec3 sampleColor = calculateIndirectLight(sampleUV, samplePos, sampleNormal, sampleDepth); | ||||||
|  | 		float weight = 1.0 / (1.0 + dist * dist * 2.0) * max(dot(sampleNormal, n), 0.0); | ||||||
|  |  | ||||||
|  | 		gi += sampleColor * weight; | ||||||
|  | 		totalWeight += weight; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  |     // Normalize and apply intensity | ||||||
|  |     if (totalWeight > 0.0) { | ||||||
|  |         gi /= totalWeight; | ||||||
|  |         #ifdef _CPostprocess | ||||||
|  |             gi *= PPComp12.x; | ||||||
|  |         #else | ||||||
|  |             gi *= ssaoStrength; | ||||||
|  |         #endif | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | 	#ifdef _EmissionShadeless | ||||||
|  | 		if (matid == 1) { // pure emissive material, color stored in basecol | ||||||
|  | 			gi += textureLod(gbuffer1, texCoord, 0.0).rgb; | ||||||
|  | 		} | ||||||
| 	#endif | 	#endif | ||||||
|  | 	#ifdef _EmissionShaded | ||||||
|  | 		#ifdef _EmissionShadeless | ||||||
|  | 		else { | ||||||
|  | 		#endif | ||||||
|  | 			gi += textureLod(gbufferEmission, texCoord, 0.0).rgb; | ||||||
|  | 		#ifdef _EmissionShadeless | ||||||
|  | 		} | ||||||
|  | 		#endif | ||||||
|  | 	#endif | ||||||
|  | 	fragColor = gi / (gi + vec3(1.0)); // Reinhard tone mapping | ||||||
| } | } | ||||||
|  | |||||||
| @ -6,6 +6,10 @@ | |||||||
| 			"compare_mode": "always", | 			"compare_mode": "always", | ||||||
| 			"cull_mode": "none", | 			"cull_mode": "none", | ||||||
| 			"links": [ | 			"links": [ | ||||||
|  | 				{ | ||||||
|  | 					"name": "invVP", | ||||||
|  | 					"link": "_inverseViewProjectionMatrix" | ||||||
|  | 				}, | ||||||
| 				{ | 				{ | ||||||
| 					"name": "P", | 					"name": "P", | ||||||
| 					"link": "_projectionMatrix" | 					"link": "_projectionMatrix" | ||||||
| @ -15,16 +19,180 @@ | |||||||
| 					"link": "_viewMatrix3" | 					"link": "_viewMatrix3" | ||||||
| 				}, | 				}, | ||||||
| 				{ | 				{ | ||||||
| 					"name": "invP", | 					"name": "eye", | ||||||
| 					"link": "_inverseProjectionMatrix" | 					"link": "_cameraPosition" | ||||||
|  | 				},				 | ||||||
|  | 				{ | ||||||
|  | 					"name": "eyeLook", | ||||||
|  | 					"link": "_cameraLook" | ||||||
| 				}, | 				}, | ||||||
| 				{ | 				{ | ||||||
| 					"name": "cameraProj", | 					"name": "cameraProj", | ||||||
| 					"link": "_cameraPlaneProj" | 					"link": "_cameraPlaneProj" | ||||||
|  |  | ||||||
|  | 				}, | ||||||
|  | 				{ | ||||||
|  | 					"name": "screenSize", | ||||||
|  | 					"link": "_screenSize" | ||||||
|  | 				}, | ||||||
|  | 				{ | ||||||
|  | 					"name": "PPComp12", | ||||||
|  | 					"link": "_PPComp12", | ||||||
|  | 					"ifdef": ["_CPostprocess"] | ||||||
|  | 				}, | ||||||
|  | 				{ | ||||||
|  | 					"name": "lightsArraySpot", | ||||||
|  | 					"link": "_lightsArraySpot", | ||||||
|  | 					"ifdef": ["_Clusters", "_Spot"] | ||||||
|  | 				}, | ||||||
|  | 				{ | ||||||
|  | 					"name": "lightsArray", | ||||||
|  | 					"link": "_lightsArray", | ||||||
|  | 					"ifdef": ["_Clusters"] | ||||||
|  | 				}, | ||||||
|  | 				{ | ||||||
|  | 					"name": "clustersData", | ||||||
|  | 					"link": "_clustersData", | ||||||
|  | 					"ifdef": ["_Clusters"] | ||||||
|  | 				}, | ||||||
|  | 				{ | ||||||
|  | 					"name": "cameraPlane", | ||||||
|  | 					"link": "_cameraPlane", | ||||||
|  | 					"ifdef": ["_Clusters"] | ||||||
|  | 				}, | ||||||
|  | 				{ | ||||||
|  | 					"name": "sunDir", | ||||||
|  | 					"link": "_sunDirection", | ||||||
|  | 					"ifdef": ["_Sun"] | ||||||
|  | 				}, | ||||||
|  | 				{ | ||||||
|  | 					"name": "sunCol", | ||||||
|  | 					"link": "_sunColor", | ||||||
|  | 					"ifdef": ["_Sun"] | ||||||
|  | 				}, | ||||||
|  | 				{ | ||||||
|  | 					"name": "shadowsBias", | ||||||
|  | 					"link": "_sunShadowsBias", | ||||||
|  | 					"ifdef": ["_Sun", "_ShadowMap"] | ||||||
|  | 				}, | ||||||
|  | 				{ | ||||||
|  | 					"name": "LWVP", | ||||||
|  | 					"link": "_biasLightWorldViewProjectionMatrixSun", | ||||||
|  | 					"ifndef": ["_CSM"], | ||||||
|  | 					"ifdef": ["_Sun", "_ShadowMap"] | ||||||
|  | 				}, | ||||||
|  | 				{ | ||||||
|  | 					"name": "casData", | ||||||
|  | 					"link": "_cascadeData", | ||||||
|  | 					"ifdef": ["_Sun", "_ShadowMap", "_CSM"] | ||||||
|  | 				}, | ||||||
|  | 				{ | ||||||
|  | 					"name": "lightArea0", | ||||||
|  | 					"link": "_lightArea0", | ||||||
|  | 					"ifdef": ["_LTC"] | ||||||
|  | 				}, | ||||||
|  | 				{ | ||||||
|  | 					"name": "lightArea1", | ||||||
|  | 					"link": "_lightArea1", | ||||||
|  | 					"ifdef": ["_LTC"] | ||||||
|  | 				}, | ||||||
|  | 				{ | ||||||
|  | 					"name": "lightArea2", | ||||||
|  | 					"link": "_lightArea2", | ||||||
|  | 					"ifdef": ["_LTC"] | ||||||
|  | 				}, | ||||||
|  | 				{ | ||||||
|  | 					"name": "lightArea3", | ||||||
|  | 					"link": "_lightArea3", | ||||||
|  | 					"ifdef": ["_LTC"] | ||||||
|  | 				}, | ||||||
|  | 				{ | ||||||
|  | 					"name": "sltcMat", | ||||||
|  | 					"link": "_ltcMat", | ||||||
|  | 					"ifdef": ["_LTC"] | ||||||
|  | 				}, | ||||||
|  | 				{ | ||||||
|  | 					"name": "sltcMag", | ||||||
|  | 					"link": "_ltcMag", | ||||||
|  | 					"ifdef": ["_LTC"] | ||||||
|  | 				}, | ||||||
|  | 				{ | ||||||
|  | 					"name": "smSizeUniform", | ||||||
|  | 					"link": "_shadowMapSize", | ||||||
|  | 					"ifdef": ["_SMSizeUniform"] | ||||||
|  | 				}, | ||||||
|  | 				{ | ||||||
|  | 					"name": "lightProj", | ||||||
|  | 					"link": "_lightPlaneProj", | ||||||
|  | 					"ifdef": ["_ShadowMap"] | ||||||
|  | 				}, | ||||||
|  | 				{ | ||||||
|  | 					"name": "pointPos", | ||||||
|  | 					"link": "_pointPosition", | ||||||
|  | 					"ifdef": ["_SinglePoint"] | ||||||
|  | 				}, | ||||||
|  | 				{ | ||||||
|  | 					"name": "pointCol", | ||||||
|  | 					"link": "_pointColor", | ||||||
|  | 					"ifdef": ["_SinglePoint"] | ||||||
|  | 				}, | ||||||
|  | 				{ | ||||||
|  | 					"name": "pointBias", | ||||||
|  | 					"link": "_pointShadowsBias", | ||||||
|  | 					"ifdef": ["_SinglePoint", "_ShadowMap"] | ||||||
|  | 				}, | ||||||
|  | 				{ | ||||||
|  | 					"name": "spotDir", | ||||||
|  | 					"link": "_spotDirection", | ||||||
|  | 					"ifdef": ["_SinglePoint", "_Spot"] | ||||||
|  | 				}, | ||||||
|  | 				{ | ||||||
|  | 					"name": "spotData", | ||||||
|  | 					"link": "_spotData", | ||||||
|  | 					"ifdef": ["_SinglePoint", "_Spot"] | ||||||
|  | 				}, | ||||||
|  | 				{ | ||||||
|  | 					"name": "spotRight", | ||||||
|  | 					"link": "_spotRight", | ||||||
|  | 					"ifdef": ["_SinglePoint", "_Spot"] | ||||||
|  | 				}, | ||||||
|  | 				{ | ||||||
|  | 					"name": "LWVPSpotArray", | ||||||
|  | 					"link": "_biasLightWorldViewProjectionMatrixSpotArray", | ||||||
|  | 					"ifdef": ["_Clusters", "_ShadowMap", "_Spot"] | ||||||
|  | 				}, | ||||||
|  | 				{ | ||||||
|  | 					"name": "pointLightDataArray", | ||||||
|  | 					"link": "_pointLightsAtlasArray", | ||||||
|  | 					"ifdef": ["_Clusters", "_ShadowMap", "_ShadowMapAtlas"] | ||||||
|  | 				}, | ||||||
|  | 				{ | ||||||
|  | 					"name": "LWVPSpot[0]", | ||||||
|  | 					"link": "_biasLightWorldViewProjectionMatrixSpot0", | ||||||
|  | 					"ifndef": ["_ShadowMapAtlas"], | ||||||
|  | 					"ifdef": ["_LTC", "_ShadowMap"] | ||||||
|  | 				}, | ||||||
|  | 				{ | ||||||
|  | 					"name": "LWVPSpot[1]", | ||||||
|  | 					"link": "_biasLightWorldViewProjectionMatrixSpot1", | ||||||
|  | 					"ifndef": ["_ShadowMapAtlas"], | ||||||
|  | 					"ifdef": ["_LTC", "_ShadowMap"] | ||||||
|  | 				}, | ||||||
|  | 				{ | ||||||
|  | 					"name": "LWVPSpot[2]", | ||||||
|  | 					"link": "_biasLightWorldViewProjectionMatrixSpot2", | ||||||
|  | 					"ifndef": ["_ShadowMapAtlas"], | ||||||
|  | 					"ifdef": ["_LTC", "_ShadowMap"] | ||||||
|  | 				}, | ||||||
|  | 				{ | ||||||
|  | 					"name": "LWVPSpot[3]", | ||||||
|  | 					"link": "_biasLightWorldViewProjectionMatrixSpot3", | ||||||
|  | 					"ifndef": ["_ShadowMapAtlas"], | ||||||
|  | 					"ifdef": ["_LTC", "_ShadowMap"] | ||||||
| 				} | 				} | ||||||
| 			], | 			], | ||||||
| 			"texture_params": [], | 			"texture_params": [], | ||||||
| 			"vertex_shader": "../include/pass_viewray2.vert.glsl", | 			"vertex_shader": "../include/pass_viewray.vert.glsl", | ||||||
| 			"fragment_shader": "ssgi_pass.frag.glsl" | 			"fragment_shader": "ssgi_pass.frag.glsl" | ||||||
| 		} | 		} | ||||||
| 	] | 	] | ||||||
|  | |||||||
| @ -92,7 +92,7 @@ void main() { | |||||||
|  |  | ||||||
| 	vec3 viewNormal = V3 * n; | 	vec3 viewNormal = V3 * n; | ||||||
| 	vec3 viewPos = getPosView(viewRay, d, cameraProj); | 	vec3 viewPos = getPosView(viewRay, d, cameraProj); | ||||||
| 	vec3 reflected = reflect(normalize(viewPos), viewNormal); | 	vec3 reflected = reflect(viewPos, viewNormal); | ||||||
| 	hitCoord = viewPos; | 	hitCoord = viewPos; | ||||||
|  |  | ||||||
| 	#ifdef _CPostprocess | 	#ifdef _CPostprocess | ||||||
|  | |||||||
| @ -57,17 +57,14 @@ vec4 binarySearch(vec3 dir) { | |||||||
| } | } | ||||||
|  |  | ||||||
| vec4 rayCast(vec3 dir) { | vec4 rayCast(vec3 dir) { | ||||||
|     float ddepth; | 	float ddepth; | ||||||
|     dir *= ss_refractionRayStep; | 	dir *= ss_refractionRayStep; | ||||||
|     for (int i = 0; i < maxSteps; i++) { | 	for (int i = 0; i < maxSteps; i++) { | ||||||
|         hitCoord += dir; | 		hitCoord += dir; | ||||||
|         ddepth = getDeltaDepth(hitCoord); | 		ddepth = getDeltaDepth(hitCoord); | ||||||
|         if (ddepth > 0.0) | 		if (ddepth > 0.0) return binarySearch(dir); | ||||||
|             return binarySearch(dir); | 	} | ||||||
|     } | 	return vec4(texCoord, 0.0, 1.0); | ||||||
|     // No hit — fallback to projecting the ray to UV space |  | ||||||
|     vec2 fallbackUV = getProjectedCoord(hitCoord); |  | ||||||
|     return vec4(fallbackUV, 0.0, 0.5); // We set .w lower to indicate fallback |  | ||||||
| } | } | ||||||
|  |  | ||||||
| void main() { | void main() { | ||||||
| @ -75,10 +72,11 @@ void main() { | |||||||
|     float roughness = g0.z; |     float roughness = g0.z; | ||||||
|     vec4 gr = textureLod(gbuffer_refraction, texCoord, 0.0); |     vec4 gr = textureLod(gbuffer_refraction, texCoord, 0.0); | ||||||
|     float ior = gr.x; |     float ior = gr.x; | ||||||
|     float opac = gr.y; |     float opac = 1.0 - gr.y; | ||||||
|     float d = textureLod(gbufferD, texCoord, 0.0).r * 2.0 - 1.0; |     float d = textureLod(gbufferD, texCoord, 0.0).r * 2.0 - 1.0; | ||||||
|     if (d == 0.0 || opac == 1.0 || ior == 1.0) { |     if (d == 0.0 || d == 1.0 || opac == 1.0 || ior == 1.0) { | ||||||
|         fragColor.rgb = textureLod(tex1, texCoord, 0.0).rgb; |         fragColor.rgb = textureLod(tex1, texCoord, 0.0).rgb; | ||||||
|  | 		fragColor.a = opac; | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
| 	vec2 enc = g0.rg; | 	vec2 enc = g0.rg; | ||||||
| @ -101,9 +99,12 @@ void main() { | |||||||
| 						clamp(-refracted.z, 0.0, 1.0) * clamp((length(viewPos - hitCoord)), 0.0, 1.0) * coords.w; | 						clamp(-refracted.z, 0.0, 1.0) * clamp((length(viewPos - hitCoord)), 0.0, 1.0) * coords.w; | ||||||
| 	intensity = clamp(intensity, 0.0, 1.0); | 	intensity = clamp(intensity, 0.0, 1.0); | ||||||
|  |  | ||||||
| 	vec3 refractionCol = textureLod(tex1, coords.xy, 0.0).rgb; | 	vec4 refractionCol = textureLod(tex1, coords.xy, 0.0).rgba; | ||||||
| 	refractionCol *= intensity; | 	refractionCol.a = opac; | ||||||
| 	vec3 color = textureLod(tex, texCoord.xy, 0.0).rgb; | 	//refractionCol *= intensity; | ||||||
|  | 	vec4 color = textureLod(tex, texCoord.xy, 0.0).rgba; | ||||||
|  | 	color.a = opac; | ||||||
|  |  | ||||||
| 	fragColor.rgb = mix(refractionCol, color, opac); | 	fragColor.rgba = mix(refractionCol, color, opac); | ||||||
|  | 	fragColor.a = opac; | ||||||
| } | } | ||||||
|  | |||||||
| @ -5,6 +5,12 @@ | |||||||
| 			"depth_write": false, | 			"depth_write": false, | ||||||
| 			"compare_mode": "always", | 			"compare_mode": "always", | ||||||
| 			"cull_mode": "none", | 			"cull_mode": "none", | ||||||
|  | 			"blend_source": "source_alpha", | ||||||
|  | 			"blend_destination": "inverse_source_alpha", | ||||||
|  | 			"blend_operation": "add", | ||||||
|  | 			"alpha_blend_source": "blend_one", | ||||||
|  | 			"alpha_blend_destination": "blend_one", | ||||||
|  | 			"alpha_blend_operation": "add", | ||||||
| 			"links": [ | 			"links": [ | ||||||
| 				{ | 				{ | ||||||
| 					"name": "P", | 					"name": "P", | ||||||
|  | |||||||
| @ -1,6 +1,7 @@ | |||||||
| // | // | ||||||
| // Copyright (C) 2012 Jorge Jimenez (jorge@iryoku.com) | // Copyright (C) 2012 Jorge Jimenez (jorge@iryoku.com) | ||||||
| // Copyright (C) 2012 Diego Gutierrez (diegog@unizar.es) | // Copyright (C) 2012 Diego Gutierrez (diegog@unizar.es) | ||||||
|  | // Copyright (C) 2025 Onek8 (info@leenkx.com) | ||||||
| // All rights reserved. | // All rights reserved. | ||||||
| // | // | ||||||
| // Redistribution and use in source and binary forms, with or without | // Redistribution and use in source and binary forms, with or without | ||||||
| @ -33,6 +34,14 @@ | |||||||
| // policies, either expressed or implied, of the copyright holders. | // policies, either expressed or implied, of the copyright holders. | ||||||
| // | // | ||||||
|  |  | ||||||
|  | // TODO: | ||||||
|  | // Add real sss radius | ||||||
|  | // Add real sss scale | ||||||
|  | // Move temp hash, reorganize shader utility functions | ||||||
|  | // Add compiler flag for quality presets or with samples parameter | ||||||
|  | // Clean up + Document comment | ||||||
|  |  | ||||||
|  |  | ||||||
| #version 450 | #version 450 | ||||||
|  |  | ||||||
| #include "compiled.inc" | #include "compiled.inc" | ||||||
| @ -49,67 +58,93 @@ out vec4 fragColor; | |||||||
|  |  | ||||||
| const float SSSS_FOVY = 108.0; | const float SSSS_FOVY = 108.0; | ||||||
|  |  | ||||||
| // Separable SSS Reflectance | // Temp hash func -  | ||||||
| // const float sssWidth = 0.005; | float hash13(vec3 p3) { | ||||||
|  | 	p3 = fract(p3 * vec3(0.1031, 0.1030, 0.0973)); | ||||||
|  | 	p3 += dot(p3, p3.yzx + 33.33); | ||||||
|  | 	return fract((p3.x + p3.y) * p3.z); | ||||||
|  | } | ||||||
|  |  | ||||||
| vec4 SSSSBlur() { | vec4 SSSSBlur() { | ||||||
| 	// Quality = 0 | 	const int SSSS_N_SAMPLES = 15; | ||||||
| 	const int SSSS_N_SAMPLES  = 11; | 	vec4 kernel[SSSS_N_SAMPLES]; | ||||||
| 	vec4 kernel[SSSS_N_SAMPLES];		 | 	 | ||||||
| 	kernel[0] = vec4(0.560479, 0.669086, 0.784728, 0); | 	// color neutral kernel weights to prevent color shifting | ||||||
| 	kernel[1] = vec4(0.00471691, 0.000184771, 5.07566e-005, -2); | 	kernel[0] = vec4(0.2, 0.2, 0.2, 0.0); | ||||||
| 	kernel[2] = vec4(0.0192831, 0.00282018, 0.00084214, -1.28); | 	kernel[1] = vec4(0.12, 0.12, 0.12, 0.2); | ||||||
| 	kernel[3] = vec4(0.03639, 0.0130999, 0.00643685, -0.72); | 	kernel[2] = vec4(0.09, 0.09, 0.09, 0.4); | ||||||
| 	kernel[4] = vec4(0.0821904, 0.0358608, 0.0209261, -0.32); | 	kernel[3] = vec4(0.06, 0.06, 0.06, 0.8); | ||||||
| 	kernel[5] = vec4(0.0771802, 0.113491, 0.0793803, -0.08); | 	kernel[4] = vec4(0.04, 0.04, 0.04, 1.2); | ||||||
| 	kernel[6] = vec4(0.0771802, 0.113491, 0.0793803, 0.08); | 	kernel[5] = vec4(0.025, 0.025, 0.025, 1.6); | ||||||
| 	kernel[7] = vec4(0.0821904, 0.0358608, 0.0209261, 0.32); | 	kernel[6] = vec4(0.015, 0.015, 0.015, 2.0); | ||||||
| 	kernel[8] = vec4(0.03639, 0.0130999, 0.00643685, 0.72); | 	kernel[7] = vec4(0.005, 0.005, 0.005, 2.5); | ||||||
| 	kernel[9] = vec4(0.0192831, 0.00282018, 0.00084214, 1.28); | 	kernel[8] = vec4(0.12, 0.12, 0.12, -0.2); | ||||||
| 	kernel[10] = vec4(0.00471691, 0.000184771, 5.07565e-005, 2); | 	kernel[9] = vec4(0.09, 0.09, 0.09, -0.4); | ||||||
|  | 	kernel[10] = vec4(0.06, 0.06, 0.06, -0.8); | ||||||
|  | 	kernel[11] = vec4(0.04, 0.04, 0.04, -1.2); | ||||||
|  | 	kernel[12] = vec4(0.025, 0.025, 0.025, -1.6); | ||||||
|  | 	kernel[13] = vec4(0.015, 0.015, 0.015, -2.0); | ||||||
|  | 	kernel[14] = vec4(0.005, 0.005, 0.005, -2.5); | ||||||
| 	 | 	 | ||||||
| 	vec4 colorM = textureLod(tex, texCoord, 0.0); | 	vec4 colorM = textureLod(tex, texCoord, 0.0); | ||||||
|  |  | ||||||
| 	// Fetch linear depth of current pixel |  | ||||||
| 	float depth = textureLod(gbufferD, texCoord, 0.0).r; | 	float depth = textureLod(gbufferD, texCoord, 0.0).r; | ||||||
| 	float depthM = cameraProj.y / (depth - cameraProj.x); | 	float depthM = cameraProj.y / (depth - cameraProj.x); | ||||||
|  |  | ||||||
| 	// Calculate the sssWidth scale (1.0 for a unit plane sitting on the projection window) |  | ||||||
| 	float distanceToProjectionWindow = 1.0 / tan(0.5 * radians(SSSS_FOVY)); | 	float distanceToProjectionWindow = 1.0 / tan(0.5 * radians(SSSS_FOVY)); | ||||||
| 	float scale = distanceToProjectionWindow / depthM; | 	float scale = distanceToProjectionWindow / depthM; | ||||||
|  |  | ||||||
| 	// Calculate the final step to fetch the surrounding pixels |  | ||||||
| 	vec2 finalStep = sssWidth * scale * dir; | 	vec2 finalStep = sssWidth * scale * dir; | ||||||
| 	finalStep *= 1.0;//SSSS_STREGTH_SOURCE; // Modulate it using the alpha channel. | 	 | ||||||
| 	finalStep *= 1.0 / 3.0; // Divide by 3 as the kernels range from -3 to 3. |  | ||||||
| 	finalStep *= 0.05; // |  | ||||||
|  |  | ||||||
| 	// Accumulate the center sample: | 	vec3 jitterSeed = vec3(texCoord.xy * 1000.0, fract(cameraProj.x * 0.0001)); | ||||||
| 	vec4 colorBlurred = colorM; | 	float jitterOffset = (hash13(jitterSeed) * 2.0 - 1.0) * 0.15; // 15% jitteR | ||||||
| 	colorBlurred.rgb *= kernel[0].rgb; | 	 | ||||||
|  | 	finalStep *= (1.0 + jitterOffset); | ||||||
| 	// Accumulate the other samples | 	finalStep *= 0.05;  | ||||||
|  | 	vec3 colorBlurred = vec3(0.0); | ||||||
|  | 	vec3 weightSum = vec3(0.0); | ||||||
|  | 	colorBlurred += colorM.rgb * kernel[0].rgb; | ||||||
|  | 	weightSum += kernel[0].rgb; | ||||||
|  | 	 | ||||||
|  | 	// Accumulate the other samples with per-pixel jittering to reduce banding | ||||||
| 	for (int i = 1; i < SSSS_N_SAMPLES; i++) { | 	for (int i = 1; i < SSSS_N_SAMPLES; i++) { | ||||||
| 		// Fetch color and depth for current sample | 		float sampleJitter = hash13(vec3(texCoord.xy * 720.0, float(i) * 37.45)) * 0.1 - 0.05; | ||||||
| 		vec2 offset = texCoord + kernel[i].a * finalStep; | 		 | ||||||
|  | 		vec2 offset = texCoord + (kernel[i].a + sampleJitter) * finalStep; | ||||||
| 		vec4 color = textureLod(tex, offset, 0.0); | 		vec4 color = textureLod(tex, offset, 0.0); | ||||||
| 		//#if SSSS_FOLLOW_SURFACE == 1 | 		 | ||||||
| 		// If the difference in depth is huge, we lerp color back to "colorM": | 		// ADJUST FOR SURFACE FOLLOWING | ||||||
| 		//float depth = textureLod(tex, offset, 0.0).r; | 		// 0.0 = disabled (maximum SSS but with bleeding), 1.0 = fully enabled (prevents bleeding but might reduce SSS effect) | ||||||
| 		//float s = clamp(300.0f * distanceToProjectionWindow * sssWidth * abs(depthM - depth),0.0,1.0); | 		const float SURFACE_FOLLOWING_STRENGTH = 0.15; // Reduced to preserve more SSS effect | ||||||
| 		//color.rgb = mix(color.rgb, colorM.rgb, s); | 		 | ||||||
| 		//#endif | 		if (SURFACE_FOLLOWING_STRENGTH > 0.0) { | ||||||
| 		// Accumulate | 			float sampleDepth = textureLod(gbufferD, offset, 0.0).r; | ||||||
| 		colorBlurred.rgb += kernel[i].rgb * color.rgb; | 			float depthScale = 5.0;  | ||||||
|  | 			float depthDiff = abs(depth - sampleDepth) * depthScale; | ||||||
|  | 			if (depthDiff > 0.3) {  | ||||||
|  | 				float blendFactor = clamp(depthDiff - 0.3, 0.0, 1.0) * SURFACE_FOLLOWING_STRENGTH; | ||||||
|  | 				color.rgb = mix(color.rgb, colorM.rgb, blendFactor); | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		 | ||||||
|  | 		colorBlurred += color.rgb * kernel[i].rgb; | ||||||
|  | 		weightSum += kernel[i].rgb; | ||||||
| 	} | 	} | ||||||
|  | 	vec3 normalizedColor = colorBlurred / max(weightSum, vec3(0.00001)); | ||||||
| 	return colorBlurred; | 	float dither = hash13(vec3(texCoord * 1333.0, 0.0)) * 0.003 - 0.0015; | ||||||
|  | 	return vec4(normalizedColor + vec3(dither), colorM.a); | ||||||
| } | } | ||||||
|  |  | ||||||
| void main() { | void main() { | ||||||
|  | 	 | ||||||
| 	if (textureLod(gbuffer0, texCoord, 0.0).a == 8192.0) { | 	if (textureLod(gbuffer0, texCoord, 0.0).a == 8192.0) { | ||||||
| 		fragColor = clamp(SSSSBlur(), 0.0, 1.0); | 		vec4 originalColor = textureLod(tex, texCoord, 0.0); | ||||||
| 	} | 		vec4 blurredColor = SSSSBlur(); | ||||||
| 	else { | 		vec4 finalColor = mix(blurredColor, originalColor, 0.15); | ||||||
|  | 		 | ||||||
|  | 		fragColor = clamp(finalColor, 0.0, 1.0); | ||||||
|  | 	} else { | ||||||
| 		fragColor = textureLod(tex, texCoord, 0.0); | 		fragColor = textureLod(tex, texCoord, 0.0); | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  | |||||||
							
								
								
									
										18
									
								
								leenkx/Shaders/std/aabb.glsl
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								leenkx/Shaders/std/aabb.glsl
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,18 @@ | |||||||
|  | #ifndef _AABB_GLSL | ||||||
|  | #define _AABB_GLSL | ||||||
|  |  | ||||||
|  | bool IntersectAABB(vec3[2] a, vec3[2] b) { | ||||||
|  |     const float EPSILON = 0.001; // Small tolerance to prevent false negatives | ||||||
|  |     if (abs(a[0].x - b[0].x) > (a[1].x + b[1].x + EPSILON)) return false; | ||||||
|  |     if (abs(a[0].y - b[0].y) > (a[1].y + b[1].y + EPSILON)) return false; | ||||||
|  |     if (abs(a[0].z - b[0].z) > (a[1].z + b[1].z + EPSILON)) return false; | ||||||
|  |     return true; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void AABBfromMinMax(inout vec3[2] aabb, vec3 _min, vec3 _max) | ||||||
|  | { | ||||||
|  | 	aabb[0] = (_min + _max) * 0.5f; | ||||||
|  | 	aabb[1] = abs(_max - aabb[0]); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #endif | ||||||
| @ -22,7 +22,7 @@ THE SOFTWARE. | |||||||
| #ifndef _CONETRACE_GLSL_ | #ifndef _CONETRACE_GLSL_ | ||||||
| #define _CONETRACE_GLSL_ | #define _CONETRACE_GLSL_ | ||||||
|  |  | ||||||
| #include "std/voxels_constants.glsl" | #include "std/constants.glsl" | ||||||
|  |  | ||||||
| // References | // References | ||||||
| // https://github.com/Friduric/voxel-cone-tracing | // https://github.com/Friduric/voxel-cone-tracing | ||||||
| @ -92,14 +92,14 @@ vec4 traceCone(const sampler3D voxels, const sampler3D voxelsSDF, const vec3 ori | |||||||
| 	float dist = voxelSize0; | 	float dist = voxelSize0; | ||||||
| 	float step_dist = dist; | 	float step_dist = dist; | ||||||
| 	vec3 samplePos; | 	vec3 samplePos; | ||||||
| 	vec3 start_pos = origin + n * voxelSize0 * voxelgiOffset; | 	vec3 start_pos = origin + n * voxelSize0; | ||||||
| 	int clipmap_index0 = 0; | 	int clipmap_index0 = 0; | ||||||
|  |  | ||||||
| 	vec3 aniso_direction = -dir; | 	vec3 aniso_direction = -dir; | ||||||
| 	vec3 face_offset = vec3( | 	vec3 face_offset = vec3( | ||||||
| 		aniso_direction.x > 0.0 ? 0 : 1, | 		aniso_direction.x > 0.0 ? 0.0 : 1.0, | ||||||
| 		aniso_direction.y > 0.0 ? 2 : 3, | 		aniso_direction.y > 0.0 ? 2.0 : 3.0, | ||||||
| 		aniso_direction.z > 0.0 ? 4 : 5 | 		aniso_direction.z > 0.0 ? 4.0 : 5.0 | ||||||
| 	) / (6 + DIFFUSE_CONE_COUNT); | 	) / (6 + DIFFUSE_CONE_COUNT); | ||||||
| 	vec3 direction_weight = abs(dir); | 	vec3 direction_weight = abs(dir); | ||||||
|  |  | ||||||
| @ -125,7 +125,7 @@ vec4 traceCone(const sampler3D voxels, const sampler3D voxelsSDF, const vec3 ori | |||||||
|  |  | ||||||
| 		if(clipmap_blend > 0.0 && clipmap_index < voxelgiClipmapCount - 1) { | 		if(clipmap_blend > 0.0 && clipmap_index < voxelgiClipmapCount - 1) { | ||||||
| 			vec4 mipSampleNext = sampleVoxel(voxels, p0, clipmaps, clipmap_index + 1.0, step_dist, precomputed_direction, face_offset, direction_weight); | 			vec4 mipSampleNext = sampleVoxel(voxels, p0, clipmaps, clipmap_index + 1.0, step_dist, precomputed_direction, face_offset, direction_weight); | ||||||
| 			mipSample = mix(mipSample, mipSampleNext, smoothstep(0.0, 1.0, clipmap_blend)); | 			mipSample = mix(mipSample, mipSampleNext, clipmap_blend); | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		sampleCol += (1.0 - sampleCol.a) * mipSample; | 		sampleCol += (1.0 - sampleCol.a) * mipSample; | ||||||
| @ -148,8 +148,9 @@ vec4 traceCone(const sampler3D voxels, const sampler3D voxelsSDF, const vec3 ori | |||||||
| vec4 traceDiffuse(const vec3 origin, const vec3 normal, const sampler3D voxels, const float clipmaps[voxelgiClipmapCount * 10]) { | vec4 traceDiffuse(const vec3 origin, const vec3 normal, const sampler3D voxels, const float clipmaps[voxelgiClipmapCount * 10]) { | ||||||
| 	float sum = 0.0; | 	float sum = 0.0; | ||||||
| 	vec4 amount = vec4(0.0); | 	vec4 amount = vec4(0.0); | ||||||
|  | 	mat3 TBN = makeTangentBasis(normal); | ||||||
| 	for (int i = 0; i < DIFFUSE_CONE_COUNT; ++i) { | 	for (int i = 0; i < DIFFUSE_CONE_COUNT; ++i) { | ||||||
| 		vec3 coneDir = DIFFUSE_CONE_DIRECTIONS[i]; | 		vec3 coneDir = TBN * DIFFUSE_CONE_DIRECTIONS[i]; | ||||||
| 		const float cosTheta = dot(normal, coneDir); | 		const float cosTheta = dot(normal, coneDir); | ||||||
| 		if (cosTheta <= 0) | 		if (cosTheta <= 0) | ||||||
| 			continue; | 			continue; | ||||||
| @ -196,14 +197,14 @@ float traceConeAO(const sampler3D voxels, const vec3 origin, const vec3 n, const | |||||||
| 	float dist = voxelSize0; | 	float dist = voxelSize0; | ||||||
| 	float step_dist = dist; | 	float step_dist = dist; | ||||||
| 	vec3 samplePos; | 	vec3 samplePos; | ||||||
| 	vec3 start_pos = origin + n * voxelSize0 * voxelgiOffset; | 	vec3 start_pos = origin + n * voxelSize0; | ||||||
| 	int clipmap_index0 = 0; | 	int clipmap_index0 = 0; | ||||||
|  |  | ||||||
| 	vec3 aniso_direction = -dir; | 	vec3 aniso_direction = -dir; | ||||||
| 	vec3 face_offset = vec3( | 	vec3 face_offset = vec3( | ||||||
| 		aniso_direction.x > 0.0 ? 0 : 1, | 		aniso_direction.x > 0.0 ? 0.0 : 1.0, | ||||||
| 		aniso_direction.y > 0.0 ? 2 : 3, | 		aniso_direction.y > 0.0 ? 2.0 : 3.0, | ||||||
| 		aniso_direction.z > 0.0 ? 4 : 5 | 		aniso_direction.z > 0.0 ? 4.0 : 5.0 | ||||||
| 	) / (6 + DIFFUSE_CONE_COUNT); | 	) / (6 + DIFFUSE_CONE_COUNT); | ||||||
| 	vec3 direction_weight = abs(dir); | 	vec3 direction_weight = abs(dir); | ||||||
|  |  | ||||||
| @ -259,7 +260,6 @@ float traceAO(const vec3 origin, const vec3 normal, const sampler3D voxels, cons | |||||||
| } | } | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
|  |  | ||||||
| #ifdef _VoxelShadow | #ifdef _VoxelShadow | ||||||
| float traceConeShadow(const sampler3D voxels, const sampler3D voxelsSDF, const vec3 origin, const vec3 n, const vec3 dir, const float aperture, const float step_size, const float clipmaps[voxelgiClipmapCount * 10]) { | float traceConeShadow(const sampler3D voxels, const sampler3D voxelsSDF, const vec3 origin, const vec3 n, const vec3 dir, const float aperture, const float step_size, const float clipmaps[voxelgiClipmapCount * 10]) { | ||||||
|     float sampleCol = 0.0; |     float sampleCol = 0.0; | ||||||
| @ -267,14 +267,14 @@ float traceConeShadow(const sampler3D voxels, const sampler3D voxelsSDF, const v | |||||||
| 	float dist = voxelSize0; | 	float dist = voxelSize0; | ||||||
| 	float step_dist = dist; | 	float step_dist = dist; | ||||||
| 	vec3 samplePos; | 	vec3 samplePos; | ||||||
| 	vec3 start_pos = origin + n * voxelSize0 * voxelgiOffset; | 	vec3 start_pos = origin + n * voxelSize0; | ||||||
| 	int clipmap_index0 = 0; | 	int clipmap_index0 = 0; | ||||||
|  |  | ||||||
| 	vec3 aniso_direction = -dir; | 	vec3 aniso_direction = -dir; | ||||||
| 	vec3 face_offset = vec3( | 	vec3 face_offset = vec3( | ||||||
| 		aniso_direction.x > 0.0 ? 0 : 1, | 		aniso_direction.x > 0.0 ? 0.0 : 1.0, | ||||||
| 		aniso_direction.y > 0.0 ? 2 : 3, | 		aniso_direction.y > 0.0 ? 2.0 : 3.0, | ||||||
| 		aniso_direction.z > 0.0 ? 4 : 5 | 		aniso_direction.z > 0.0 ? 4.0 : 5.0 | ||||||
| 	) / (6 + DIFFUSE_CONE_COUNT); | 	) / (6 + DIFFUSE_CONE_COUNT); | ||||||
| 	vec3 direction_weight = abs(dir); | 	vec3 direction_weight = abs(dir); | ||||||
| 	float coneCoefficient = 2.0 * tan(aperture * 0.5); | 	float coneCoefficient = 2.0 * tan(aperture * 0.5); | ||||||
| @ -287,7 +287,7 @@ float traceConeShadow(const sampler3D voxels, const sampler3D voxelsSDF, const v | |||||||
| 		float clipmap_blend = fract(lod); | 		float clipmap_blend = fract(lod); | ||||||
| 		vec3 p0 = start_pos + dir * dist; | 		vec3 p0 = start_pos + dir * dist; | ||||||
|  |  | ||||||
|         samplePos = (p0 - vec3(clipmaps[int(clipmap_index * 10 + 4)], clipmaps[int(clipmap_index * 10 + 5)], clipmaps[int(clipmap_index * 10 + 6)])) / (float(clipmaps[int(clipmap_index * 10)]) * voxelgiResolution.x); |         samplePos = (p0 - vec3(clipmaps[int(clipmap_index * 10 + 4)], clipmaps[int(clipmap_index * 10 + 5)], clipmaps[int(clipmap_index * 10 + 6)])) / (float(clipmaps[int(clipmap_index * 10)]) * voxelgiResolution); | ||||||
| 		samplePos = samplePos * 0.5 + 0.5; | 		samplePos = samplePos * 0.5 + 0.5; | ||||||
|  |  | ||||||
| 		if ((any(notEqual(samplePos, clamp(samplePos, 0.0, 1.0))))) { | 		if ((any(notEqual(samplePos, clamp(samplePos, 0.0, 1.0))))) { | ||||||
| @ -330,7 +330,7 @@ float traceConeShadow(const sampler3D voxels, const sampler3D voxelsSDF, const v | |||||||
|  |  | ||||||
| float traceShadow(const vec3 origin, const vec3 normal, const sampler3D voxels, const sampler3D voxelsSDF, const vec3 dir, const float clipmaps[voxelgiClipmapCount * 10], const vec2 pixel, const vec2 velocity) { | float traceShadow(const vec3 origin, const vec3 normal, const sampler3D voxels, const sampler3D voxelsSDF, const vec3 dir, const float clipmaps[voxelgiClipmapCount * 10], const vec2 pixel, const vec2 velocity) { | ||||||
|  	vec3 P = origin + dir * (BayerMatrix8[int(pixel.x + velocity.x) % 8][int(pixel.y + velocity.y) % 8] - 0.5) * voxelgiStep; |  	vec3 P = origin + dir * (BayerMatrix8[int(pixel.x + velocity.x) % 8][int(pixel.y + velocity.y) % 8] - 0.5) * voxelgiStep; | ||||||
| 	float amount = traceConeShadow(voxels, voxelsSDF, P, normal, dir, DIFFUSE_CONE_APERTURE, voxelgiStep, clipmaps); | 	float amount = traceConeShadow(voxels, voxelsSDF, P, normal, dir, SHADOW_CONE_APERTURE, voxelgiStep, clipmaps); | ||||||
| 	amount = clamp(amount, 0.0, 1.0); | 	amount = clamp(amount, 0.0, 1.0); | ||||||
| 	return amount * voxelgiOcc; | 	return amount * voxelgiOcc; | ||||||
| } | } | ||||||
|  | |||||||
| @ -21,29 +21,49 @@ THE SOFTWARE. | |||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| const int DIFFUSE_CONE_COUNT = 16; | const int DIFFUSE_CONE_COUNT = 16; | ||||||
| const float DIFFUSE_CONE_APERTURE = radians(45.0); |  | ||||||
| 
 | 
 | ||||||
| const vec3 DIFFUSE_CONE_DIRECTIONS[16] = { | const float SHADOW_CONE_APERTURE = radians(15.0); | ||||||
| 	vec3(0.0000, 0.0000, 1.0000),   // Central direction |  | ||||||
| 	vec3(0.3827, 0.0000, 0.9239),   // Ring 1 |  | ||||||
| 	vec3(-0.3827, 0.0000, 0.9239), |  | ||||||
| 	vec3(0.0000, 0.3827, 0.9239), |  | ||||||
| 	vec3(0.0000, -0.3827, 0.9239), |  | ||||||
| 	vec3(0.2706, 0.2706, 0.9239),   // Ring 2 |  | ||||||
| 	vec3(-0.2706, 0.2706, 0.9239), |  | ||||||
| 	vec3(0.2706, -0.2706, 0.9239), |  | ||||||
| 	vec3(-0.2706, -0.2706, 0.9239), |  | ||||||
| 	vec3(0.1802, 0.3604, 0.9239),   // Ring 3 |  | ||||||
| 	vec3(-0.1802, 0.3604, 0.9239), |  | ||||||
| 	vec3(0.1802, -0.3604, 0.9239), |  | ||||||
| 	vec3(-0.1802, -0.3604, 0.9239), |  | ||||||
| 	vec3(0.3604, 0.1802, 0.9239), |  | ||||||
| 	vec3(-0.3604, 0.1802, 0.9239), |  | ||||||
| 	vec3(0.3604, -0.1802, 0.9239) |  | ||||||
| }; |  | ||||||
| 
 | 
 | ||||||
|  | const float DIFFUSE_CONE_APERTURE = 0.872665; // 50 degrees in radians | ||||||
| 
 | 
 | ||||||
| const float BayerMatrix8[8][8] = | mat3 makeTangentBasis(const vec3 normal) { | ||||||
|  |     // Create a tangent basis from normal vector | ||||||
|  |     vec3 tangent; | ||||||
|  |     vec3 bitangent; | ||||||
|  | 
 | ||||||
|  |     // Compute tangent (Frisvad's method) | ||||||
|  |     if (abs(normal.z) < 0.999) { | ||||||
|  |         tangent = normalize(cross(vec3(0, 1, 0), normal)); | ||||||
|  |     } else { | ||||||
|  |         tangent = normalize(cross(normal, vec3(1, 0, 0))); | ||||||
|  |     } | ||||||
|  |     bitangent = cross(normal, tangent); | ||||||
|  | 
 | ||||||
|  |     return mat3(tangent, bitangent, normal); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // 16 optimized cone directions for hemisphere sampling (Z-up, normalized) | ||||||
|  | const vec3 DIFFUSE_CONE_DIRECTIONS[16] = vec3[]( | ||||||
|  |     vec3(0.707107, 0.000000, 0.707107),  // Front | ||||||
|  |     vec3(-0.707107, 0.000000, 0.707107), // Back | ||||||
|  |     vec3(0.000000, 0.707107, 0.707107),  // Right | ||||||
|  |     vec3(0.000000, -0.707107, 0.707107), // Left | ||||||
|  |     vec3(0.500000, 0.500000, 0.707107),  // Front-right | ||||||
|  |     vec3(-0.500000, 0.500000, 0.707107), // Back-right | ||||||
|  |     vec3(0.500000, -0.500000, 0.707107), // Front-left | ||||||
|  |     vec3(-0.500000, -0.500000, 0.707107),// Back-left | ||||||
|  |     vec3(0.353553, 0.000000, 0.935414),  // Narrow front | ||||||
|  |     vec3(-0.353553, 0.000000, 0.935414), // Narrow back | ||||||
|  |     vec3(0.000000, 0.353553, 0.935414),  // Narrow right | ||||||
|  |     vec3(0.000000, -0.353553, 0.935414), // Narrow left | ||||||
|  |     vec3(0.270598, 0.270598, 0.923880),  // Narrow front-right | ||||||
|  |     vec3(-0.270598, 0.270598, 0.923880), // Narrow back-right | ||||||
|  |     vec3(0.270598, -0.270598, 0.923880), // Narrow front-left | ||||||
|  |     vec3(-0.270598, -0.270598, 0.923880) // Narrow back-left | ||||||
|  | ); | ||||||
|  | 
 | ||||||
|  | // TO DO - Disabled momentarily instead of changing formulas | ||||||
|  | const float off_BayerMatrix8[8][8] = | ||||||
| { | { | ||||||
| 	{ 1.0 / 65.0, 49.0 / 65.0, 13.0 / 65.0, 61.0 / 65.0, 4.0 / 65.0, 52.0 / 65.0, 16.0 / 65.0, 64.0 / 65.0 }, | 	{ 1.0 / 65.0, 49.0 / 65.0, 13.0 / 65.0, 61.0 / 65.0, 4.0 / 65.0, 52.0 / 65.0, 16.0 / 65.0, 64.0 / 65.0 }, | ||||||
| 	{ 33.0 / 65.0, 17.0 / 65.0, 45.0 / 65.0, 29.0 / 65.0, 36.0 / 65.0, 20.0 / 65.0, 48.0 / 65.0, 32.0 / 65.0 }, | 	{ 33.0 / 65.0, 17.0 / 65.0, 45.0 / 65.0, 29.0 / 65.0, 36.0 / 65.0, 20.0 / 65.0, 48.0 / 65.0, 32.0 / 65.0 }, | ||||||
| @ -54,3 +74,15 @@ const float BayerMatrix8[8][8] = | |||||||
| 	{ 11.0 / 65.0, 59.0 / 65.0, 7.0 / 65.0, 55.0 / 65.0, 10.0 / 65.0, 58.0 / 65.0, 6.0 / 65.0, 54.0 / 65.0 }, | 	{ 11.0 / 65.0, 59.0 / 65.0, 7.0 / 65.0, 55.0 / 65.0, 10.0 / 65.0, 58.0 / 65.0, 6.0 / 65.0, 54.0 / 65.0 }, | ||||||
| 	{ 43.0 / 65.0, 27.0 / 65.0, 39.0 / 65.0, 23.0 / 65.0, 42.0 / 65.0, 26.0 / 65.0, 38.0 / 65.0, 22.0 / 65.0 } | 	{ 43.0 / 65.0, 27.0 / 65.0, 39.0 / 65.0, 23.0 / 65.0, 42.0 / 65.0, 26.0 / 65.0, 38.0 / 65.0, 22.0 / 65.0 } | ||||||
| }; | }; | ||||||
|  | const float BayerMatrix8[8][8] = | ||||||
|  | { | ||||||
|  | 	{ 1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0 }, | ||||||
|  | 	{ 1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0 }, | ||||||
|  | 	{ 1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0 }, | ||||||
|  | 	{ 1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0 }, | ||||||
|  | 	{ 1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0 }, | ||||||
|  | 	{ 1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0 }, | ||||||
|  | 	{ 1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0 }, | ||||||
|  | 	{ 1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0 } | ||||||
|  | }; | ||||||
|  | 
 | ||||||
| @ -10,9 +10,6 @@ | |||||||
| #ifdef _VoxelShadow | #ifdef _VoxelShadow | ||||||
| #include "std/conetrace.glsl" | #include "std/conetrace.glsl" | ||||||
| #endif | #endif | ||||||
| #ifdef _gbuffer2 |  | ||||||
| uniform sampler2D gbuffer2; |  | ||||||
| #endif |  | ||||||
| #ifdef _LTC | #ifdef _LTC | ||||||
| #include "std/ltc.glsl" | #include "std/ltc.glsl" | ||||||
| #endif | #endif | ||||||
| @ -25,46 +22,63 @@ uniform sampler2D gbuffer2; | |||||||
| #ifdef _Spot | #ifdef _Spot | ||||||
| #include "std/light_common.glsl" | #include "std/light_common.glsl" | ||||||
| #endif | #endif | ||||||
|  | #ifdef _VoxelShadow | ||||||
|  | #include "std/conetrace.glsl" | ||||||
|  | #endif | ||||||
|  |  | ||||||
| #ifdef _ShadowMap | #ifdef _ShadowMap | ||||||
| 	#ifdef _SinglePoint | 	#ifdef _SinglePoint | ||||||
| 		#ifdef _Spot | 		#ifdef _Spot | ||||||
| 			#ifndef _LTC | 			#ifndef _LTC | ||||||
| 				uniform sampler2DShadow shadowMapSpot[1]; | 				uniform sampler2DShadow shadowMapSpot[1]; | ||||||
|  | 				#ifdef _ShadowMapTransparent | ||||||
| 				uniform sampler2D shadowMapSpotTransparent[1]; | 				uniform sampler2D shadowMapSpotTransparent[1]; | ||||||
| 				uniform mat4 LWVPSpot[1]; | 				#endif | ||||||
|  | 				uniform mat4 LWVPSpotArray[1]; | ||||||
| 			#endif | 			#endif | ||||||
| 		#else | 		#else | ||||||
| 			uniform samplerCubeShadow shadowMapPoint[1]; | 			uniform samplerCubeShadow shadowMapPoint[1]; | ||||||
|  | 			#ifdef _ShadowMapTransparent | ||||||
| 			uniform samplerCube shadowMapPointTransparent[1]; | 			uniform samplerCube shadowMapPointTransparent[1]; | ||||||
|  | 			#endif | ||||||
| 			uniform vec2 lightProj; | 			uniform vec2 lightProj; | ||||||
| 		#endif | 		#endif | ||||||
| 	#endif | 	#endif | ||||||
| 	#ifdef _Clusters | 	#ifdef _Clusters | ||||||
| 		#ifdef _SingleAtlas | 		#ifdef _SingleAtlas | ||||||
| 		//!uniform sampler2DShadow shadowMapAtlas; | 		//!uniform sampler2DShadow shadowMapAtlas; | ||||||
|  | 		#ifdef _ShadowMapTransparent | ||||||
| 		//!uniform sampler2D shadowMapAtlasTransparent; | 		//!uniform sampler2D shadowMapAtlasTransparent; | ||||||
| 		#endif | 		#endif | ||||||
|  | 		#endif | ||||||
| 		uniform vec2 lightProj; | 		uniform vec2 lightProj; | ||||||
| 		#ifdef _ShadowMapAtlas | 		#ifdef _ShadowMapAtlas | ||||||
| 		#ifndef _SingleAtlas | 		#ifndef _SingleAtlas | ||||||
| 		uniform sampler2DShadow shadowMapAtlasPoint; | 		uniform sampler2DShadow shadowMapAtlasPoint; | ||||||
|  | 		#ifdef _ShadowMapTransparent | ||||||
| 		uniform sampler2D shadowMapAtlasPointTransparent; | 		uniform sampler2D shadowMapAtlasPointTransparent; | ||||||
| 		#endif | 		#endif | ||||||
|  | 		#endif | ||||||
| 		#else | 		#else | ||||||
| 		uniform samplerCubeShadow shadowMapPoint[4]; | 		uniform samplerCubeShadow shadowMapPoint[4]; | ||||||
|  | 		#ifdef _ShadowMapTransparent | ||||||
| 		uniform samplerCube shadowMapPointTransparent[4]; | 		uniform samplerCube shadowMapPointTransparent[4]; | ||||||
| 		#endif | 		#endif | ||||||
|  | 		#endif | ||||||
| 		#ifdef _Spot | 		#ifdef _Spot | ||||||
| 			#ifdef _ShadowMapAtlas | 			#ifdef _ShadowMapAtlas | ||||||
| 			#ifndef _SingleAtlas | 			#ifndef _SingleAtlas | ||||||
| 			uniform sampler2DShadow shadowMapAtlasSpot; | 			uniform sampler2DShadow shadowMapAtlasSpot; | ||||||
|  | 			#ifdef _ShadowMapTransparent | ||||||
| 			uniform sampler2D shadowMapAtlasSpotTransparent; | 			uniform sampler2D shadowMapAtlasSpotTransparent; | ||||||
| 			#endif | 			#endif | ||||||
|  | 			#endif | ||||||
| 			#else | 			#else | ||||||
| 			uniform sampler2DShadow shadowMapSpot[4]; | 			uniform sampler2DShadow shadowMapSpot[4]; | ||||||
|  | 			#ifdef _ShadowMapTransparent | ||||||
| 			uniform sampler2D shadowMapSpotTransparent[4]; | 			uniform sampler2D shadowMapSpotTransparent[4]; | ||||||
| 			#endif | 			#endif | ||||||
|  | 			#endif | ||||||
| 			uniform mat4 LWVPSpotArray[maxLightsCluster]; | 			uniform mat4 LWVPSpotArray[maxLightsCluster]; | ||||||
| 		#endif | 		#endif | ||||||
| 	#endif | 	#endif | ||||||
| @ -81,28 +95,35 @@ uniform sampler2D sltcMag; | |||||||
| #ifndef _Spot | #ifndef _Spot | ||||||
| 	#ifdef _SinglePoint | 	#ifdef _SinglePoint | ||||||
| 		uniform sampler2DShadow shadowMapSpot[1]; | 		uniform sampler2DShadow shadowMapSpot[1]; | ||||||
|  | 		#ifdef _ShadowMapTransparent | ||||||
| 		uniform sampler2D shadowMapSpotTransparent[1]; | 		uniform sampler2D shadowMapSpotTransparent[1]; | ||||||
| 		uniform mat4 LWVPSpot[1]; | 		#endif | ||||||
|  | 		uniform mat4 LWVPSpotArray[1]; | ||||||
| 	#endif | 	#endif | ||||||
| 	#ifdef _Clusters | 	#ifdef _Clusters | ||||||
| 		uniform sampler2DShadow shadowMapSpot[maxLightsCluster]; | 		uniform sampler2DShadow shadowMapSpot[maxLightsCluster]; | ||||||
|  | 		#ifdef _ShadowMapTransparent | ||||||
| 		uniform sampler2D shadowMapSpotTransparent[maxLightsCluster]; | 		uniform sampler2D shadowMapSpotTransparent[maxLightsCluster]; | ||||||
|  | 		#endif | ||||||
| 		uniform mat4 LWVPSpotArray[maxLightsCluster]; | 		uniform mat4 LWVPSpotArray[maxLightsCluster]; | ||||||
| 	#endif | 	#endif | ||||||
| 	#endif | #endif | ||||||
| #endif | #endif | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| vec3 sampleLight(const vec3 p, const vec3 n, const vec3 v, const float dotNV, const vec3 lp, const vec3 lightCol, | vec3 sampleLight(const vec3 p, const vec3 n, const vec3 v, const float dotNV, const vec3 lp, const vec3 lightCol, | ||||||
| 	const vec3 albedo, const float rough, const float spec, const vec3 f0 | 	const vec3 albedo, const float rough, const float spec, const vec3 f0 | ||||||
| 	#ifdef _ShadowMap | 	#ifdef _ShadowMap | ||||||
| 		, int index, float bias, bool receiveShadow, bool transparent | 		, int index, float bias, bool receiveShadow | ||||||
|  | 	#ifdef _ShadowMapTransparent | ||||||
|  | 		, bool transparent | ||||||
|  | 	#endif | ||||||
| 	#endif | 	#endif | ||||||
| 	#ifdef _Spot | 	#ifdef _Spot | ||||||
| 		, const bool isSpot, const float spotSize, float spotBlend, vec3 spotDir, vec2 scale, vec3 right | 		, const bool isSpot, const float spotSize, float spotBlend, vec3 spotDir, vec2 scale, vec3 right | ||||||
| 	#endif | 	#endif | ||||||
| 	#ifdef _VoxelShadow | 	#ifdef _VoxelShadow | ||||||
| 		, sampler3D voxels, sampler3D voxelsSDF, float clipmaps[10 * voxelgiClipmapCount] | 		, sampler3D voxels, sampler3D voxelsSDF, float clipmaps[10 * voxelgiClipmapCount], vec2 velocity | ||||||
| 	#endif | 	#endif | ||||||
| 	#ifdef _MicroShadowing | 	#ifdef _MicroShadowing | ||||||
| 		, float occ | 		, float occ | ||||||
| @ -148,23 +169,67 @@ vec3 sampleLight(const vec3 p, const vec3 n, const vec3 v, const float dotNV, co | |||||||
| 	#endif | 	#endif | ||||||
|  |  | ||||||
| 	#ifdef _VoxelShadow | 	#ifdef _VoxelShadow | ||||||
| 	vec4 g2 = textureLod(gbuffer2, gl_FragCoord.xy, 0.0); | 	vec3 lightDir = l; | ||||||
| 	direct *= (1.0 - traceShadow(p, n, voxels, voxelsSDF, l, clipmaps, gl_FragCoord.xy, g2.rg).r) * voxelgiShad; | 	#ifdef _Spot | ||||||
|  | 	if (isSpot) | ||||||
|  | 		lightDir = spotDir; | ||||||
|  | 	#endif | ||||||
|  | 	direct *= (1.0 - traceShadow(p, n, voxels, voxelsSDF, lightDir, clipmaps, gl_FragCoord.xy, velocity).r) * voxelgiShad; | ||||||
| 	#endif | 	#endif | ||||||
|  |  | ||||||
| 	#ifdef _LTC | 	#ifdef _LTC | ||||||
| 	#ifdef _ShadowMap | 	#ifdef _ShadowMap | ||||||
| 		if (receiveShadow) { | 		if (receiveShadow) { | ||||||
| 			#ifdef _SinglePoint | 			#ifdef _SinglePoint | ||||||
| 			vec4 lPos = LWVPSpotArray[0] * vec4(p + n * bias * 10, 1.0); | 			vec4 lPos = LWVPSpot[0] * vec4(p + n * bias * 10, 1.0); | ||||||
| 			direct *= shadowTest(shadowMapSpot[0], shadowMapSpotTransparent[0], lPos.xyz / lPos.w, bias, transparent); | 			direct *= shadowTest(shadowMapSpot[0], | ||||||
|  | 								#ifdef _ShadowMapTransparent | ||||||
|  | 								shadowMapSpotTransparent[0], | ||||||
|  | 								#endif | ||||||
|  | 								lPos.xyz / lPos.w, bias | ||||||
|  | 								#ifdef _ShadowMapTransparent | ||||||
|  | 								, transparent | ||||||
|  | 								#endif | ||||||
|  | 								); | ||||||
| 			#endif | 			#endif | ||||||
| 			#ifdef _Clusters | 			#ifdef _Clusters | ||||||
| 			vec4 lPos = LWVPSpotArray[index] * vec4(p + n * bias * 10, 1.0); | 			vec4 lPos = LWVPSpot[index] * vec4(p + n * bias * 10, 1.0); | ||||||
| 			if (index == 0) direct *= shadowTest(shadowMapSpot[0], shadowMapSpotTransparent[0], lPos.xyz / lPos.w, bias, transparent); | 			if (index == 0) direct *= shadowTest(shadowMapSpot[0], | ||||||
| 			else if (index == 1) direct *= shadowTest(shadowMapSpot[1], shadowMapSpotTransparent[1], lPos.xyz / lPos.w, bias, transparent); | 												#ifdef _ShadowMapTransparent | ||||||
| 			else if (index == 2) direct *= shadowTest(shadowMapSpot[2], shadowMapSpotTransparent[2], lPos.xyz / lPos.w, bias, transparent); | 												shadowMapSpotTransparent[0], | ||||||
| 			else if (index == 3) direct *= shadowTest(shadowMapSpot[3], shadowMapSpotTransparent[3], lPos.xyz / lPos.w, bias, transparent); | 												#endif | ||||||
|  | 												lPos.xyz / lPos.w, bias | ||||||
|  | 												#ifdef _ShadowMapTransparent | ||||||
|  | 												, transparent | ||||||
|  | 												#endif | ||||||
|  | 												); | ||||||
|  | 			else if (index == 1) direct *= shadowTest(shadowMapSpot[1], | ||||||
|  | 													#ifdef _ShadowMapTransparent | ||||||
|  | 													shadowMapSpotTransparent[1], | ||||||
|  | 													#endif | ||||||
|  | 													lPos.xyz / lPos.w, bias | ||||||
|  | 													#ifdef _ShadowMapTransparent | ||||||
|  | 													, transparent | ||||||
|  | 													#endif | ||||||
|  | 													); | ||||||
|  | 			else if (index == 2) direct *= shadowTest(shadowMapSpot[2], | ||||||
|  | 													#ifdef _ShadowMapTransparent | ||||||
|  | 													shadowMapSpotTransparent[2], | ||||||
|  | 													#endif | ||||||
|  | 													lPos.xyz / lPos.w, bias | ||||||
|  | 													#ifdef _ShadowMapTransparent | ||||||
|  | 													, transparent | ||||||
|  | 													#endif | ||||||
|  | 													); | ||||||
|  | 			else if (index == 3) direct *= shadowTest(shadowMapSpot[3], | ||||||
|  | 													#ifdef _ShadowMapTransparent | ||||||
|  | 													shadowMapSpotTransparent[3], | ||||||
|  | 													#endif | ||||||
|  | 													lPos.xyz / lPos.w, bias | ||||||
|  | 													#ifdef _ShadowMapTransparent | ||||||
|  | 													, transparent | ||||||
|  | 													#endif | ||||||
|  | 													); | ||||||
| 			#endif | 			#endif | ||||||
| 		} | 		} | ||||||
| 	#endif | 	#endif | ||||||
| @ -178,25 +243,76 @@ vec3 sampleLight(const vec3 p, const vec3 n, const vec3 v, const float dotNV, co | |||||||
| 		#ifdef _ShadowMap | 		#ifdef _ShadowMap | ||||||
| 			if (receiveShadow) { | 			if (receiveShadow) { | ||||||
| 				#ifdef _SinglePoint | 				#ifdef _SinglePoint | ||||||
| 				vec4 lPos = LWVPSpot[0] * vec4(p + n * bias * 10, 1.0); | 				vec4 lPos = LWVPSpotArray[0] * vec4(p + n * bias * 10, 1.0); | ||||||
| 				direct *= shadowTest(shadowMapSpot[0], shadowMapSpotTransparent[0], lPos.xyz / lPos.w, bias, transparent); | 				direct *= shadowTest(shadowMapSpot[0], | ||||||
|  | 									#ifdef _ShadowMapTransparent | ||||||
|  | 									shadowMapSpotTransparent[0], | ||||||
|  | 									#endif | ||||||
|  | 									lPos.xyz / lPos.w, bias | ||||||
|  | 									#ifdef _ShadowMapTransparent | ||||||
|  | 									, transparent | ||||||
|  | 									#endif | ||||||
|  | 									); | ||||||
| 				#endif | 				#endif | ||||||
| 				#ifdef _Clusters | 				#ifdef _Clusters | ||||||
| 					vec4 lPos = LWVPSpotArray[index] * vec4(p + n * bias * 10, 1.0); | 					vec4 lPos = LWVPSpotArray[index] * vec4(p + n * bias * 10, 1.0); | ||||||
| 					#ifdef _ShadowMapAtlas | 					#ifdef _ShadowMapAtlas | ||||||
| 						direct *= shadowTest( | 						direct *= shadowTest( | ||||||
| 							#ifndef _SingleAtlas | 											#ifdef _ShadowMapTransparent | ||||||
| 							shadowMapAtlasSpot, shadowMapAtlasSpotTransparent | 											#ifndef _SingleAtlas | ||||||
| 							#else | 											shadowMapAtlasSpot, shadowMapAtlasSpotTransparent | ||||||
| 							shadowMapAtlas, shadowMapAtlasTransparent | 											#else | ||||||
| 							#endif | 											shadowMapAtlas, shadowMapAtlasTransparent | ||||||
| 							, lPos.xyz / lPos.w, bias, transparent | 											#endif | ||||||
| 						); | 											#else | ||||||
|  | 											#ifndef _SingleAtlas | ||||||
|  | 											shadowMapAtlasSpot | ||||||
|  | 											#else | ||||||
|  | 											shadowMapAtlas | ||||||
|  | 											#endif | ||||||
|  | 											#endif | ||||||
|  | 											, lPos.xyz / lPos.w, bias | ||||||
|  | 											#ifdef _ShadowMapTransparent | ||||||
|  | 											, transparent | ||||||
|  | 											#endif | ||||||
|  | 											); | ||||||
| 					#else | 					#else | ||||||
| 							 if (index == 0) direct *= shadowTest(shadowMapSpot[0], shadowMapSpotTransparent[0], lPos.xyz / lPos.w, bias, transparent); | 							 if (index == 0) direct *= shadowTest(shadowMapSpot[0], | ||||||
| 						else if (index == 1) direct *= shadowTest(shadowMapSpot[1], shadowMapSpotTransparent[1], lPos.xyz / lPos.w, bias, transparent); | 																#ifdef _ShadowMapTransparent | ||||||
| 						else if (index == 2) direct *= shadowTest(shadowMapSpot[2], shadowMapSpotTransparent[2], lPos.xyz / lPos.w, bias, transparent); | 																shadowMapSpotTransparent[0], | ||||||
| 						else if (index == 3) direct *= shadowTest(shadowMapSpot[3], shadowMapSpotTransparent[3], lPos.xyz / lPos.w, bias, transparent); | 																#endif | ||||||
|  | 																lPos.xyz / lPos.w, bias | ||||||
|  | 																#ifdef _ShadowMapTransparent | ||||||
|  | 																, transparent | ||||||
|  | 																#endif | ||||||
|  | 																); | ||||||
|  | 						else if (index == 1) direct *= shadowTest(shadowMapSpot[1], | ||||||
|  | 																#ifdef _ShadowMapTransparent | ||||||
|  | 																shadowMapSpotTransparent[1], | ||||||
|  | 																#endif | ||||||
|  | 																lPos.xyz / lPos.w, bias | ||||||
|  | 																#ifdef _ShadowMapTransparent | ||||||
|  | 																, transparent | ||||||
|  | 																#endif | ||||||
|  | 																); | ||||||
|  | 						else if (index == 2) direct *= shadowTest(shadowMapSpot[2], | ||||||
|  | 																#ifdef _ShadowMapTransparent | ||||||
|  | 																shadowMapSpotTransparent[2], | ||||||
|  | 																#endif | ||||||
|  | 																lPos.xyz / lPos.w, bias | ||||||
|  | 																#ifdef _ShadowMapTransparent | ||||||
|  | 																, transparent | ||||||
|  | 																#endif | ||||||
|  | 																); | ||||||
|  | 						else if (index == 3) direct *= shadowTest(shadowMapSpot[3], | ||||||
|  | 																#ifdef _ShadowMapTransparent | ||||||
|  | 																shadowMapSpotTransparent[3], | ||||||
|  | 																#endif | ||||||
|  | 																lPos.xyz / lPos.w, bias | ||||||
|  | 																#ifdef _ShadowMapTransparent | ||||||
|  | 																, transparent | ||||||
|  | 																#endif | ||||||
|  | 																); | ||||||
| 					#endif | 					#endif | ||||||
| 				#endif | 				#endif | ||||||
| 			} | 			} | ||||||
| @ -213,24 +329,75 @@ vec3 sampleLight(const vec3 p, const vec3 n, const vec3 v, const float dotNV, co | |||||||
| 		if (receiveShadow) { | 		if (receiveShadow) { | ||||||
| 			#ifdef _SinglePoint | 			#ifdef _SinglePoint | ||||||
| 			#ifndef _Spot | 			#ifndef _Spot | ||||||
| 			direct *= PCFCube(shadowMapPoint[0], shadowMapPointTransparent[0], ld, -l, bias, lightProj, n, transparent); | 			direct *= PCFCube(shadowMapPoint[0], | ||||||
|  | 							#ifdef _ShadowMapTransparent | ||||||
|  | 							shadowMapPointTransparent[0], | ||||||
|  | 							#endif | ||||||
|  | 							ld, -l, bias, lightProj, n | ||||||
|  | 							#ifdef _ShadowMapTransparent | ||||||
|  | 							, transparent | ||||||
|  | 							#endif | ||||||
|  | 							); | ||||||
| 			#endif | 			#endif | ||||||
| 			#endif | 			#endif | ||||||
| 			#ifdef _Clusters | 			#ifdef _Clusters | ||||||
| 				#ifdef _ShadowMapAtlas | 				#ifdef _ShadowMapAtlas | ||||||
| 				direct *= PCFFakeCube( | 				direct *= PCFFakeCube( | ||||||
| 					#ifndef _SingleAtlas | 									#ifdef _ShadowMapTransparent | ||||||
| 					shadowMapAtlasPoint, shadowMapAtlasPointTransparent | 									#ifndef _SingleAtlas | ||||||
| 					#else | 									shadowMapAtlasPoint, shadowMapAtlasPointTransparent | ||||||
| 					shadowMapAtlas, shadowMapAtlasTransparent | 									#else | ||||||
| 					#endif | 									shadowMapAtlas, shadowMapAtlasTransparent | ||||||
| 					, ld, -l, bias, lightProj, n, index, transparent | 									#endif | ||||||
| 				); | 									#else | ||||||
|  | 									#ifndef _SingleAtlas | ||||||
|  | 									shadowMapAtlasPoint | ||||||
|  | 									#else | ||||||
|  | 									shadowMapAtlas | ||||||
|  | 									#endif | ||||||
|  | 									#endif | ||||||
|  | 									, ld, -l, bias, lightProj, n, index | ||||||
|  | 									#ifdef _ShadowMapTransparent | ||||||
|  | 									, transparent | ||||||
|  | 									#endif | ||||||
|  | 									); | ||||||
| 				#else | 				#else | ||||||
| 					 if (index == 0) direct *= PCFCube(shadowMapPoint[0], shadowMapPointTransparent[0], ld, -l, bias, lightProj, n, transparent); | 					 if (index == 0) direct *= PCFCube(shadowMapPoint[0], | ||||||
| 				else if (index == 1) direct *= PCFCube(shadowMapPoint[1], shadowMapPointTransparent[1], ld, -l, bias, lightProj, n, transparent); | 													#ifdef _ShadowMapTransparent | ||||||
| 				else if (index == 2) direct *= PCFCube(shadowMapPoint[2], shadowMapPointTransparent[2], ld, -l, bias, lightProj, n, transparent); | 													shadowMapPointTransparent[0], | ||||||
| 				else if (index == 3) direct *= PCFCube(shadowMapPoint[3], shadowMapPointTransparent[3], ld, -l, bias, lightProj, n, transparent); | 													#endif | ||||||
|  | 													ld, -l, bias, lightProj, n | ||||||
|  | 													#ifdef _ShadowMapTransparent | ||||||
|  | 													, transparent | ||||||
|  | 													#endif | ||||||
|  | 													); | ||||||
|  | 				else if (index == 1) direct *= PCFCube(shadowMapPoint[1], | ||||||
|  | 													#ifdef _ShadowMapTransparent | ||||||
|  | 													shadowMapPointTransparent[1], | ||||||
|  | 													#endif | ||||||
|  | 													ld, -l, bias, lightProj, n | ||||||
|  | 													#ifdef _ShadowMapTransparent | ||||||
|  | 													, transparent | ||||||
|  | 													#endif | ||||||
|  | 													); | ||||||
|  | 				else if (index == 2) direct *= PCFCube(shadowMapPoint[2], | ||||||
|  | 													#ifdef _ShadowMapTransparent | ||||||
|  | 													shadowMapPointTransparent[2], | ||||||
|  | 													#endif | ||||||
|  | 													ld, -l, bias, lightProj, n | ||||||
|  | 													#ifdef _ShadowMapTransparent | ||||||
|  | 													, transparent | ||||||
|  | 													#endif | ||||||
|  | 													); | ||||||
|  | 				else if (index == 3) direct *= PCFCube(shadowMapPoint[3], | ||||||
|  | 													#ifdef _ShadowMapTransparent | ||||||
|  | 													shadowMapPointTransparent[3], | ||||||
|  | 													#endif | ||||||
|  | 													ld, -l, bias, lightProj, n | ||||||
|  | 													#ifdef _ShadowMapTransparent | ||||||
|  | 													, transparent | ||||||
|  | 													#endif | ||||||
|  | 													); | ||||||
| 				#endif | 				#endif | ||||||
| 			#endif | 			#endif | ||||||
| 		} | 		} | ||||||
| @ -239,4 +406,274 @@ vec3 sampleLight(const vec3 p, const vec3 n, const vec3 v, const float dotNV, co | |||||||
| 	return direct; | 	return direct; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | #ifdef _VoxelGI | ||||||
|  | vec3 sampleLightVoxels(const vec3 p, const vec3 n, const vec3 v, const float dotNV, const vec3 lp, const vec3 lightCol, | ||||||
|  | 	const vec3 albedo, const float rough, const float spec, const vec3 f0 | ||||||
|  | 	#ifdef _ShadowMap | ||||||
|  | 		, int index, float bias, bool receiveShadow | ||||||
|  | 	#ifdef _ShadowMapTransparent | ||||||
|  | 		, bool transparent | ||||||
|  | 	#endif | ||||||
|  | 	#endif | ||||||
|  | 	#ifdef _Spot | ||||||
|  | 		, const bool isSpot, const float spotSize, float spotBlend, vec3 spotDir, vec2 scale, vec3 right | ||||||
|  | 	#endif | ||||||
|  | 	) { | ||||||
|  | 	vec3 ld = lp - p; | ||||||
|  | 	vec3 l = normalize(ld); | ||||||
|  | 	vec3 h = normalize(v + l); | ||||||
|  | 	float dotNH = max(0.0, dot(n, h)); | ||||||
|  | 	float dotVH = max(0.0, dot(v, h)); | ||||||
|  | 	float dotNL = max(0.0, dot(n, l)); | ||||||
|  |  | ||||||
|  | 	#ifdef _LTC | ||||||
|  | 	float theta = acos(dotNV); | ||||||
|  | 	vec2 tuv = vec2(rough, theta / (0.5 * PI)); | ||||||
|  | 	tuv = tuv * LUT_SCALE + LUT_BIAS; | ||||||
|  | 	vec4 t = textureLod(sltcMat, tuv, 0.0); | ||||||
|  | 	mat3 invM = mat3( | ||||||
|  | 		vec3(1.0, 0.0, t.y), | ||||||
|  | 		vec3(0.0, t.z, 0.0), | ||||||
|  | 		vec3(t.w, 0.0, t.x)); | ||||||
|  | 	float ltcspec = ltcEvaluate(n, v, dotNV, p, invM, lightArea0, lightArea1, lightArea2, lightArea3); | ||||||
|  | 	ltcspec *= textureLod(sltcMag, tuv, 0.0).a; | ||||||
|  | 	float ltcdiff = ltcEvaluate(n, v, dotNV, p, mat3(1.0), lightArea0, lightArea1, lightArea2, lightArea3); | ||||||
|  | 	vec3 direct = albedo * ltcdiff + ltcspec * spec * 0.05; | ||||||
|  | 	#else | ||||||
|  | 	vec3 direct = lambertDiffuseBRDF(albedo, dotNL) + | ||||||
|  | 				  specularBRDF(f0, rough, dotNL, dotNH, dotNV, dotVH) * spec; | ||||||
|  | 	#endif | ||||||
|  |  | ||||||
|  | 	direct *= attenuate(distance(p, lp)); | ||||||
|  | 	direct *= lightCol; | ||||||
|  |  | ||||||
|  | 	#ifdef _LTC | ||||||
|  | 	#ifdef _ShadowMap | ||||||
|  | 		if (receiveShadow) { | ||||||
|  | 			#ifdef _SinglePoint | ||||||
|  | 			vec4 lPos = LWVPSpot[0] * vec4(p + n * bias * 10, 1.0); | ||||||
|  | 			direct *= shadowTest(shadowMapSpot[0], | ||||||
|  | 								#ifdef _ShadowMapTransparent | ||||||
|  | 								shadowMapSpotTransparent[0], | ||||||
|  | 								#endif | ||||||
|  | 								lPos.xyz / lPos.w, bias | ||||||
|  | 								#ifdef _ShadowMapTransparent | ||||||
|  | 								, transparent | ||||||
|  | 								#endif | ||||||
|  | 								); | ||||||
|  | 			#endif | ||||||
|  | 			#ifdef _Clusters | ||||||
|  | 			vec4 lPos = LWVPSpot[index] * vec4(p + n * bias * 10, 1.0); | ||||||
|  | 			if (index == 0) direct *= shadowTest(shadowMapSpot[0], | ||||||
|  | 												#ifdef _ShadowMapTransparent | ||||||
|  | 												shadowMapSpotTransparent[0], | ||||||
|  | 												#endif | ||||||
|  | 												lPos.xyz / lPos.w, bias | ||||||
|  | 												#ifdef _ShadowMapTransparent | ||||||
|  | 												, transparent | ||||||
|  | 												#endif | ||||||
|  | 												); | ||||||
|  | 			else if (index == 1) direct *= shadowTest(shadowMapSpot[1], | ||||||
|  | 													#ifdef _ShadowMapTransparent | ||||||
|  | 													shadowMapSpotTransparent[1], | ||||||
|  | 													#endif | ||||||
|  | 													lPos.xyz / lPos.w, bias | ||||||
|  | 													#ifdef _ShadowMapTransparent | ||||||
|  | 													, transparent | ||||||
|  | 													#endif | ||||||
|  | 													); | ||||||
|  | 			else if (index == 2) direct *= shadowTest(shadowMapSpot[2], | ||||||
|  | 													#ifdef _ShadowMapTransparent | ||||||
|  | 													shadowMapSpotTransparent[2], | ||||||
|  | 													#endif | ||||||
|  | 													lPos.xyz / lPos.w, bias | ||||||
|  | 													#ifdef _ShadowMapTransparent | ||||||
|  | 													, transparent | ||||||
|  | 													#endif | ||||||
|  | 													); | ||||||
|  | 			else if (index == 3) direct *= shadowTest(shadowMapSpot[3], | ||||||
|  | 													#ifdef _ShadowMapTransparent | ||||||
|  | 													shadowMapSpotTransparent[3], | ||||||
|  | 													#endif | ||||||
|  | 													lPos.xyz / lPos.w, bias | ||||||
|  | 													#ifdef _ShadowMapTransparent | ||||||
|  | 													, transparent | ||||||
|  | 													#endif | ||||||
|  | 													); | ||||||
|  | 			#endif | ||||||
|  | 		} | ||||||
|  | 	#endif | ||||||
|  | 	return direct; | ||||||
|  | 	#endif | ||||||
|  |  | ||||||
|  | 	#ifdef _Spot | ||||||
|  | 	if (isSpot) { | ||||||
|  | 		direct *= spotlightMask(l, spotDir, right, scale, spotSize, spotBlend); | ||||||
|  |  | ||||||
|  | 		#ifdef _ShadowMap | ||||||
|  | 			if (receiveShadow) { | ||||||
|  | 				#ifdef _SinglePoint | ||||||
|  | 				vec4 lPos = LWVPSpotArray[0] * vec4(p + n * bias * 10, 1.0); | ||||||
|  | 				direct *= shadowTest(shadowMapSpot[0], | ||||||
|  | 									#ifdef _ShadowMapTransparent | ||||||
|  | 									shadowMapSpotTransparent[0], | ||||||
|  | 									#endif | ||||||
|  | 									lPos.xyz / lPos.w, bias | ||||||
|  | 									#ifdef _ShadowMapTransparent | ||||||
|  | 									, transparent | ||||||
|  | 									#endif | ||||||
|  | 									); | ||||||
|  | 				#endif | ||||||
|  | 				#ifdef _Clusters | ||||||
|  | 					vec4 lPos = LWVPSpotArray[index] * vec4(p + n * bias * 10, 1.0); | ||||||
|  | 					#ifdef _ShadowMapAtlas | ||||||
|  | 						direct *= shadowTest( | ||||||
|  | 											#ifdef _ShadowMapTransparent | ||||||
|  | 											#ifndef _SingleAtlas | ||||||
|  | 											shadowMapAtlasSpot, shadowMapAtlasSpotTransparent | ||||||
|  | 											#else | ||||||
|  | 											shadowMapAtlas, shadowMapAtlasTransparent | ||||||
|  | 											#endif | ||||||
|  | 											#else | ||||||
|  | 											#ifndef _SingleAtlas | ||||||
|  | 											shadowMapAtlasSpot | ||||||
|  | 											#else | ||||||
|  | 											shadowMapAtlas | ||||||
|  | 											#endif | ||||||
|  | 											#endif | ||||||
|  | 											, lPos.xyz / lPos.w, bias | ||||||
|  | 											#ifdef _ShadowMapTransparent | ||||||
|  | 											, transparent | ||||||
|  | 											#endif | ||||||
|  | 											); | ||||||
|  | 					#else | ||||||
|  | 							 if (index == 0) direct *= shadowTest(shadowMapSpot[0], | ||||||
|  | 																#ifdef _ShadowMapTransparent | ||||||
|  | 																shadowMapSpotTransparent[0], | ||||||
|  | 																#endif | ||||||
|  | 																lPos.xyz / lPos.w, bias | ||||||
|  | 																#ifdef _ShadowMapTransparent | ||||||
|  | 																, transparent | ||||||
|  | 																#endif | ||||||
|  | 																); | ||||||
|  | 						else if (index == 1) direct *= shadowTest(shadowMapSpot[1], | ||||||
|  | 																#ifdef _ShadowMapTransparent | ||||||
|  | 																shadowMapSpotTransparent[1], | ||||||
|  | 																#endif | ||||||
|  | 																lPos.xyz / lPos.w, bias | ||||||
|  | 																#ifdef _ShadowMapTransparent | ||||||
|  | 																, transparent | ||||||
|  | 																#endif | ||||||
|  | 																); | ||||||
|  | 						else if (index == 2) direct *= shadowTest(shadowMapSpot[2], | ||||||
|  | 																#ifdef _ShadowMapTransparent | ||||||
|  | 																shadowMapSpotTransparent[2], | ||||||
|  | 																#endif | ||||||
|  | 																lPos.xyz / lPos.w, bias | ||||||
|  | 																#ifdef _ShadowMapTransparent | ||||||
|  | 																, transparent | ||||||
|  | 																#endif | ||||||
|  | 																); | ||||||
|  | 						else if (index == 3) direct *= shadowTest(shadowMapSpot[3], | ||||||
|  | 																#ifdef _ShadowMapTransparent | ||||||
|  | 																shadowMapSpotTransparent[3], | ||||||
|  | 																#endif | ||||||
|  | 																lPos.xyz / lPos.w, bias | ||||||
|  | 																#ifdef _ShadowMapTransparent | ||||||
|  | 																, transparent | ||||||
|  | 																#endif | ||||||
|  | 																); | ||||||
|  | 					#endif | ||||||
|  | 				#endif | ||||||
|  | 			} | ||||||
|  | 		#endif | ||||||
|  | 		return direct; | ||||||
|  | 	} | ||||||
|  | 	#endif | ||||||
|  |  | ||||||
|  | 	#ifdef _LightIES | ||||||
|  | 	direct *= iesAttenuation(-l); | ||||||
|  | 	#endif | ||||||
|  |  | ||||||
|  | 	#ifdef _ShadowMap | ||||||
|  | 		if (receiveShadow) { | ||||||
|  | 			#ifdef _SinglePoint | ||||||
|  | 			#ifndef _Spot | ||||||
|  | 			direct *= PCFCube(shadowMapPoint[0], | ||||||
|  | 							#ifdef _ShadowMapTransparent | ||||||
|  | 							shadowMapPointTransparent[0], | ||||||
|  | 							#endif | ||||||
|  | 							ld, -l, bias, lightProj, n | ||||||
|  | 							#ifdef _ShadowMapTransparent | ||||||
|  | 							, transparent | ||||||
|  | 							#endif | ||||||
|  | 							); | ||||||
|  | 			#endif | ||||||
|  | 			#endif | ||||||
|  | 			#ifdef _Clusters | ||||||
|  | 				#ifdef _ShadowMapAtlas | ||||||
|  | 				direct *= PCFFakeCube( | ||||||
|  | 									#ifdef _ShadowMapTransparent | ||||||
|  | 									#ifndef _SingleAtlas | ||||||
|  | 									shadowMapAtlasPoint, shadowMapAtlasPointTransparent | ||||||
|  | 									#else | ||||||
|  | 									shadowMapAtlas, shadowMapAtlasTransparent | ||||||
|  | 									#endif | ||||||
|  | 									#else | ||||||
|  | 									#ifndef _SingleAtlas | ||||||
|  | 									shadowMapAtlasPoint | ||||||
|  | 									#else | ||||||
|  | 									shadowMapAtlas | ||||||
|  | 									#endif | ||||||
|  | 									#endif | ||||||
|  | 									, ld, -l, bias, lightProj, n, index | ||||||
|  | 									#ifdef _ShadowMapTransparent | ||||||
|  | 									, transparent | ||||||
|  | 									#endif | ||||||
|  | 									); | ||||||
|  | 				#else | ||||||
|  | 					 if (index == 0) direct *= PCFCube(shadowMapPoint[0], | ||||||
|  | 													#ifdef _ShadowMapTransparent | ||||||
|  | 													shadowMapPointTransparent[0], | ||||||
|  | 													#endif | ||||||
|  | 													ld, -l, bias, lightProj, n | ||||||
|  | 													#ifdef _ShadowMapTransparent | ||||||
|  | 													, transparent | ||||||
|  | 													#endif | ||||||
|  | 													); | ||||||
|  | 				else if (index == 1) direct *= PCFCube(shadowMapPoint[1], | ||||||
|  | 													#ifdef _ShadowMapTransparent | ||||||
|  | 													shadowMapPointTransparent[1], | ||||||
|  | 													#endif | ||||||
|  | 													ld, -l, bias, lightProj, n | ||||||
|  | 													#ifdef _ShadowMapTransparent | ||||||
|  | 													, transparent | ||||||
|  | 													#endif | ||||||
|  | 													); | ||||||
|  | 				else if (index == 2) direct *= PCFCube(shadowMapPoint[2], | ||||||
|  | 													#ifdef _ShadowMapTransparent | ||||||
|  | 													shadowMapPointTransparent[2], | ||||||
|  | 													#endif | ||||||
|  | 													ld, -l, bias, lightProj, n | ||||||
|  | 													#ifdef _ShadowMapTransparent | ||||||
|  | 													, transparent | ||||||
|  | 													#endif | ||||||
|  | 													); | ||||||
|  | 				else if (index == 3) direct *= PCFCube(shadowMapPoint[3], | ||||||
|  | 													#ifdef _ShadowMapTransparent | ||||||
|  | 													shadowMapPointTransparent[3], | ||||||
|  | 													#endif | ||||||
|  | 													ld, -l, bias, lightProj, n | ||||||
|  | 													#ifdef _ShadowMapTransparent | ||||||
|  | 													, transparent | ||||||
|  | 													#endif | ||||||
|  | 													); | ||||||
|  | 				#endif | ||||||
|  | 			#endif | ||||||
|  | 		} | ||||||
|  | 	#endif | ||||||
|  |  | ||||||
|  | 	return direct; | ||||||
|  | } | ||||||
|  | #endif | ||||||
| #endif | #endif | ||||||
|  | |||||||
| @ -58,7 +58,15 @@ vec2 sampleCube(vec3 dir, out int faceIndex) { | |||||||
| } | } | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| vec3 PCF(sampler2DShadow shadowMap, sampler2D shadowMapTransparent, const vec2 uv, const float compare, const vec2 smSize, const bool transparent) { | vec3 PCF(sampler2DShadow shadowMap, | ||||||
|  | 		#ifdef _ShadowMapTransparent | ||||||
|  | 		sampler2D shadowMapTransparent, | ||||||
|  | 		#endif | ||||||
|  | 		const vec2 uv, const float compare, const vec2 smSize | ||||||
|  | 		#ifdef _ShadowMapTransparent | ||||||
|  | 		, const bool transparent | ||||||
|  | 		#endif | ||||||
|  | 		) { | ||||||
| 	vec3 result = vec3(0.0); | 	vec3 result = vec3(0.0); | ||||||
| 	result.x = texture(shadowMap, vec3(uv + (vec2(-1.0, -1.0) / smSize), compare)); | 	result.x = texture(shadowMap, vec3(uv + (vec2(-1.0, -1.0) / smSize), compare)); | ||||||
| 	result.x += texture(shadowMap, vec3(uv + (vec2(-1.0, 0.0) / smSize), compare)); | 	result.x += texture(shadowMap, vec3(uv + (vec2(-1.0, 0.0) / smSize), compare)); | ||||||
| @ -71,11 +79,13 @@ vec3 PCF(sampler2DShadow shadowMap, sampler2D shadowMapTransparent, const vec2 u | |||||||
| 	result.x += texture(shadowMap, vec3(uv + (vec2(1.0, 1.0) / smSize), compare)); | 	result.x += texture(shadowMap, vec3(uv + (vec2(1.0, 1.0) / smSize), compare)); | ||||||
| 	result = result.xxx / 9.0; | 	result = result.xxx / 9.0; | ||||||
|  |  | ||||||
|  | 	#ifdef _ShadowMapTransparent | ||||||
| 	if (transparent == false) { | 	if (transparent == false) { | ||||||
| 		vec4 shadowmap_transparent = texture(shadowMapTransparent, uv); | 		vec4 shadowmap_transparent = texture(shadowMapTransparent, uv); | ||||||
| 		if (shadowmap_transparent.a < compare) | 		if (shadowmap_transparent.a < compare) | ||||||
| 			result *= shadowmap_transparent.rgb; | 			result *= shadowmap_transparent.rgb; | ||||||
| 	} | 	} | ||||||
|  | 	#endif | ||||||
|  |  | ||||||
| 	return result; | 	return result; | ||||||
| } | } | ||||||
| @ -87,7 +97,15 @@ float lpToDepth(vec3 lp, const vec2 lightProj) { | |||||||
| 	return zcomp * 0.5 + 0.5; | 	return zcomp * 0.5 + 0.5; | ||||||
| } | } | ||||||
|  |  | ||||||
| vec3 PCFCube(samplerCubeShadow shadowMapCube, samplerCube shadowMapCubeTransparent, const vec3 lp, vec3 ml, const float bias, const vec2 lightProj, const vec3 n, const bool transparent) { | vec3 PCFCube(samplerCubeShadow shadowMapCube, | ||||||
|  | 			#ifdef _ShadowMapTransparent | ||||||
|  | 			samplerCube shadowMapCubeTransparent, | ||||||
|  | 			#endif | ||||||
|  | 			const vec3 lp, vec3 ml, const float bias, const vec2 lightProj, const vec3 n | ||||||
|  | 			#ifdef _ShadowMapTransparent | ||||||
|  | 			, const bool transparent | ||||||
|  | 			#endif | ||||||
|  | 			) { | ||||||
| 	const float s = shadowmapCubePcfSize; // TODO: incorrect... | 	const float s = shadowmapCubePcfSize; // TODO: incorrect... | ||||||
| 	float compare = lpToDepth(lp, lightProj) - bias * 1.5; | 	float compare = lpToDepth(lp, lightProj) - bias * 1.5; | ||||||
| 	ml = ml + n * bias * 20; | 	ml = ml + n * bias * 20; | ||||||
| @ -106,11 +124,13 @@ vec3 PCFCube(samplerCubeShadow shadowMapCube, samplerCube shadowMapCubeTranspare | |||||||
| 	result.x += texture(shadowMapCube, vec4(ml + vec3(-s, -s, -s), compare)); | 	result.x += texture(shadowMapCube, vec4(ml + vec3(-s, -s, -s), compare)); | ||||||
| 	result = result.xxx / 9.0; | 	result = result.xxx / 9.0; | ||||||
|  |  | ||||||
|  | 	#ifdef _ShadowMapTransparent | ||||||
| 	if (transparent == false) { | 	if (transparent == false) { | ||||||
| 		vec4 shadowmap_transparent = texture(shadowMapCubeTransparent, ml); | 		vec4 shadowmap_transparent = texture(shadowMapCubeTransparent, ml); | ||||||
| 		if (shadowmap_transparent.a < compare) | 		if (shadowmap_transparent.a < compare) | ||||||
| 			result *= shadowmap_transparent.rgb; | 			result *= shadowmap_transparent.rgb; | ||||||
| 	} | 	} | ||||||
|  | 	#endif | ||||||
|  |  | ||||||
| 	return result; | 	return result; | ||||||
| } | } | ||||||
| @ -209,21 +229,31 @@ vec2 transformOffsetedUV(const int faceIndex, out int newFaceIndex, vec2 uv) { | |||||||
| 	return uv; | 	return uv; | ||||||
| } | } | ||||||
|  |  | ||||||
| vec3 PCFFakeCube(sampler2DShadow shadowMap, sampler2D shadowMapTransparent, const vec3 lp, vec3 ml, const float bias, const vec2 lightProj, const vec3 n, const int index, const bool transparent) { | vec3 PCFFakeCube(sampler2DShadow shadowMap, | ||||||
|  | 				#ifdef _ShadowMapTransparent | ||||||
|  | 				sampler2D shadowMapTransparent, | ||||||
|  | 				#endif | ||||||
|  | 				const vec3 lp, vec3 ml, const float bias, const vec2 lightProj, const vec3 n, const int index | ||||||
|  | 				#ifdef _ShadowMapTransparent | ||||||
|  | 				, const bool transparent | ||||||
|  | 				#endif | ||||||
|  | 				) { | ||||||
| 	const vec2 smSize = smSizeUniform; // TODO: incorrect... | 	const vec2 smSize = smSizeUniform; // TODO: incorrect... | ||||||
| 	const float compare = lpToDepth(lp, lightProj) - bias * 1.5; | 	const float compare = lpToDepth(lp, lightProj) - bias * 1.5; | ||||||
| 	ml = ml + n * bias * 20; | 	ml = ml + n * bias * 20; | ||||||
|  |  | ||||||
| 	int faceIndex = 0; | 	int faceIndex = 0; | ||||||
| 	const int lightIndex = index * 6; | 	const int lightIndex = index * 6; | ||||||
| 	const vec2 uv = sampleCube(ml, faceIndex); | 	const vec2 uv = sampleCube(ml, faceIndex); | ||||||
|  |  | ||||||
| 	vec4 pointLightTile = pointLightDataArray[lightIndex + faceIndex]; // x: tile X offset, y: tile Y offset, z: tile size relative to atlas | 	vec4 pointLightTile = pointLightDataArray[lightIndex + faceIndex]; // x: tile X offset, y: tile Y offset, z: tile size relative to atlas | ||||||
| 	vec2 uvtiled = pointLightTile.z * uv + pointLightTile.xy; | 	vec2 uvtiled = pointLightTile.z * uv + pointLightTile.xy; | ||||||
| 	#ifdef _FlipY | 	#ifdef _FlipY | ||||||
| 	uvtiled.y = 1.0 - uvtiled.y; // invert Y coordinates for direct3d coordinate system | 	uvtiled.y = 1.0 - uvtiled.y; // invert Y coordinates for direct3d coordinate system | ||||||
| 	#endif | 	#endif | ||||||
|  |  | ||||||
|  | 	if (any(lessThan(uvtiled, vec2(0.0))) || any(greaterThan(uvtiled, vec2(1.0)))) { | ||||||
|  | 		return vec3(1.0); // Handle edge cases by returning full light | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	vec3 result = vec3(0.0); | 	vec3 result = vec3(0.0); | ||||||
| 	result.x += texture(shadowMap, vec3(uvtiled, compare)); | 	result.x += texture(shadowMap, vec3(uvtiled, compare)); | ||||||
| 	// soft shadowing | 	// soft shadowing | ||||||
| @ -236,14 +266,6 @@ vec3 PCFFakeCube(sampler2DShadow shadowMap, sampler2D shadowMapTransparent, cons | |||||||
| 	#endif | 	#endif | ||||||
| 	result.x += texture(shadowMap, vec3(uvtiled, compare)); | 	result.x += texture(shadowMap, vec3(uvtiled, compare)); | ||||||
|  |  | ||||||
| 	uvtiled = transformOffsetedUV(faceIndex, newFaceIndex, vec2(uv + (vec2(-1.0, 1.0) / smSize))); |  | ||||||
| 	pointLightTile = pointLightDataArray[lightIndex + newFaceIndex]; |  | ||||||
| 	uvtiled = pointLightTile.z * uvtiled + pointLightTile.xy; |  | ||||||
| 	#ifdef _FlipY |  | ||||||
| 	uvtiled.y = 1.0 - uvtiled.y; // invert Y coordinates for direct3d coordinate system |  | ||||||
| 	#endif |  | ||||||
| 	result.x += texture(shadowMap, vec3(uvtiled, compare)); |  | ||||||
|  |  | ||||||
| 	uvtiled = transformOffsetedUV(faceIndex, newFaceIndex, vec2(uv + (vec2(0.0, -1.0) / smSize))); | 	uvtiled = transformOffsetedUV(faceIndex, newFaceIndex, vec2(uv + (vec2(0.0, -1.0) / smSize))); | ||||||
| 	pointLightTile = pointLightDataArray[lightIndex + newFaceIndex]; | 	pointLightTile = pointLightDataArray[lightIndex + newFaceIndex]; | ||||||
| 	uvtiled = pointLightTile.z * uvtiled + pointLightTile.xy; | 	uvtiled = pointLightTile.z * uvtiled + pointLightTile.xy; | ||||||
| @ -300,30 +322,47 @@ vec3 PCFFakeCube(sampler2DShadow shadowMap, sampler2D shadowMapTransparent, cons | |||||||
| 	uvtiled.y = 1.0 - uvtiled.y; // invert Y coordinates for direct3d coordinate system | 	uvtiled.y = 1.0 - uvtiled.y; // invert Y coordinates for direct3d coordinate system | ||||||
| 	#endif | 	#endif | ||||||
|  |  | ||||||
|  | 	#ifdef _ShadowMapTransparent | ||||||
| 	if (transparent == false) { | 	if (transparent == false) { | ||||||
| 		vec4 shadowmap_transparent = texture(shadowMapTransparent, uvtiled); | 		vec4 shadowmap_transparent = texture(shadowMapTransparent, uvtiled); | ||||||
| 		if (shadowmap_transparent.a < compare) | 		if (shadowmap_transparent.a < compare) | ||||||
| 			result *= shadowmap_transparent.rgb; | 			result *= shadowmap_transparent.rgb; | ||||||
| 	} | 	} | ||||||
|  | 	#endif | ||||||
|  |  | ||||||
| 	return result; | 	return result; | ||||||
| } | } | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| vec3 shadowTest(sampler2DShadow shadowMap, sampler2D shadowMapTransparent, const vec3 lPos, const float shadowsBias, const bool transparent) { | vec3 shadowTest(sampler2DShadow shadowMap, | ||||||
|  | 				#ifdef _ShadowMapTransparent | ||||||
|  | 				sampler2D shadowMapTransparent, | ||||||
|  | 				#endif | ||||||
|  | 				const vec3 lPos, const float shadowsBias | ||||||
|  | 				#ifdef _ShadowMapTransparent | ||||||
|  | 				, const bool transparent | ||||||
|  | 				#endif | ||||||
|  | 				) { | ||||||
| 	#ifdef _SMSizeUniform | 	#ifdef _SMSizeUniform | ||||||
| 	vec2 smSize = smSizeUniform; | 	vec2 smSize = smSizeUniform; | ||||||
| 	#else | 	#else | ||||||
| 	const vec2 smSize = shadowmapSize; | 	const vec2 smSize = shadowmapSize; | ||||||
| 	#endif | 	#endif | ||||||
| 	if (lPos.x < 0.0 || lPos.y < 0.0 || lPos.x > 1.0 || lPos.y > 1.0) return vec3(1.0); | 	if (lPos.x < 0.0 || lPos.y < 0.0 || lPos.x > 1.0 || lPos.y > 1.0) return vec3(1.0); | ||||||
| 	return PCF(shadowMap, shadowMapTransparent, lPos.xy, lPos.z - shadowsBias, smSize, transparent); | 	return PCF(shadowMap, | ||||||
|  | 				#ifdef _ShadowMapTransparent | ||||||
|  | 				shadowMapTransparent, | ||||||
|  | 				#endif | ||||||
|  | 				lPos.xy, lPos.z - shadowsBias, smSize | ||||||
|  | 				#ifdef _ShadowMapTransparent | ||||||
|  | 				, transparent | ||||||
|  | 				#endif | ||||||
|  | 				); | ||||||
| } | } | ||||||
|  |  | ||||||
| #ifdef _CSM | #ifdef _CSM | ||||||
| mat4 getCascadeMat(const float d, out int casi, out int casIndex) { | mat4 getCascadeMat(const float d, out int casi, out int casIndex) { | ||||||
| 	const int c = shadowmapCascades; | 	const int c = shadowmapCascades; | ||||||
|  |  | ||||||
| 	// Get cascade index | 	// Get cascade index | ||||||
| 	// TODO: use bounding box slice selection instead of sphere | 	// TODO: use bounding box slice selection instead of sphere | ||||||
| 	const vec4 ci = vec4(float(c > 0), float(c > 1), float(c > 2), float(c > 3)); | 	const vec4 ci = vec4(float(c > 0), float(c > 1), float(c > 2), float(c > 3)); | ||||||
| @ -339,21 +378,26 @@ mat4 getCascadeMat(const float d, out int casi, out int casIndex) { | |||||||
| 		float(d > casData[c * 4].z), | 		float(d > casData[c * 4].z), | ||||||
| 		float(d > casData[c * 4].w)); | 		float(d > casData[c * 4].w)); | ||||||
| 	casi = int(min(dot(ci, comp), c)); | 	casi = int(min(dot(ci, comp), c)); | ||||||
|  |  | ||||||
| 	// Get cascade mat | 	// Get cascade mat | ||||||
| 	casIndex = casi * 4; | 	casIndex = casi * 4; | ||||||
|  |  | ||||||
| 	return mat4( | 	return mat4( | ||||||
| 		casData[casIndex    ], | 		casData[casIndex    ], | ||||||
| 		casData[casIndex + 1], | 		casData[casIndex + 1], | ||||||
| 		casData[casIndex + 2], | 		casData[casIndex + 2], | ||||||
| 		casData[casIndex + 3]); | 		casData[casIndex + 3]); | ||||||
|  |  | ||||||
| 	// if (casIndex == 0) return mat4(casData[0], casData[1], casData[2], casData[3]); | 	// if (casIndex == 0) return mat4(casData[0], casData[1], casData[2], casData[3]); | ||||||
| 	// .. | 	// .. | ||||||
| } | } | ||||||
|  |  | ||||||
| vec3 shadowTestCascade(sampler2DShadow shadowMap, sampler2D shadowMapTransparent, const vec3 eye, const vec3 p, const float shadowsBias, const bool transparent) { | vec3 shadowTestCascade(sampler2DShadow shadowMap, | ||||||
|  | 					   #ifdef _ShadowMapTransparent | ||||||
|  | 					   sampler2D shadowMapTransparent, | ||||||
|  | 					   #endif | ||||||
|  | 					   const vec3 eye, const vec3 p, const float shadowsBias | ||||||
|  | 					   #ifdef _ShadowMapTransparent | ||||||
|  | 					   , const bool transparent | ||||||
|  | 					   #endif | ||||||
|  | 					   ) { | ||||||
| 	#ifdef _SMSizeUniform | 	#ifdef _SMSizeUniform | ||||||
| 	vec2 smSize = smSizeUniform; | 	vec2 smSize = smSizeUniform; | ||||||
| 	#else | 	#else | ||||||
| @ -361,16 +405,22 @@ vec3 shadowTestCascade(sampler2DShadow shadowMap, sampler2D shadowMapTransparent | |||||||
| 	#endif | 	#endif | ||||||
| 	const int c = shadowmapCascades; | 	const int c = shadowmapCascades; | ||||||
| 	float d = distance(eye, p); | 	float d = distance(eye, p); | ||||||
|  |  | ||||||
| 	int casi; | 	int casi; | ||||||
| 	int casIndex; | 	int casIndex; | ||||||
| 	mat4 LWVP = getCascadeMat(d, casi, casIndex); | 	mat4 LWVP = getCascadeMat(d, casi, casIndex); | ||||||
|  |  | ||||||
| 	vec4 lPos = LWVP * vec4(p, 1.0); | 	vec4 lPos = LWVP * vec4(p, 1.0); | ||||||
| 	lPos.xyz /= lPos.w; | 	lPos.xyz /= lPos.w; | ||||||
|  |  | ||||||
| 	vec3 visibility = vec3(1.0); | 	vec3 visibility = vec3(1.0); | ||||||
| 	if (lPos.w > 0.0) visibility = PCF(shadowMap, shadowMapTransparent, lPos.xy, lPos.z - shadowsBias, smSize, transparent); | 	if (lPos.w > 0.0) visibility = PCF(shadowMap, | ||||||
|  | 									#ifdef _ShadowMapTransparent | ||||||
|  | 									shadowMapTransparent, | ||||||
|  | 									#endif | ||||||
|  | 									lPos.xy, lPos.z - shadowsBias, smSize | ||||||
|  | 									#ifdef _ShadowMapTransparent | ||||||
|  | 									, transparent | ||||||
|  | 									#endif | ||||||
|  | 									); | ||||||
|  |  | ||||||
| 	// Blend cascade | 	// Blend cascade | ||||||
| 	// https://github.com/TheRealMJP/Shadows | 	// https://github.com/TheRealMJP/Shadows | ||||||
| @ -389,13 +439,20 @@ vec3 shadowTestCascade(sampler2DShadow shadowMap, sampler2D shadowMapTransparent | |||||||
| 		vec4 lPos2 = LWVP2 * vec4(p, 1.0); | 		vec4 lPos2 = LWVP2 * vec4(p, 1.0); | ||||||
| 		lPos2.xyz /= lPos2.w; | 		lPos2.xyz /= lPos2.w; | ||||||
| 		vec3 visibility2 = vec3(1.0); | 		vec3 visibility2 = vec3(1.0); | ||||||
| 		if (lPos2.w > 0.0) visibility2 = PCF(shadowMap, shadowMapTransparent, lPos2.xy, lPos2.z - shadowsBias, smSize, transparent); | 		if (lPos2.w > 0.0) visibility2 = PCF(shadowMap, | ||||||
|  | 											#ifdef _ShadowMapTransparent | ||||||
|  | 											shadowMapTransparent, | ||||||
|  | 											#endif | ||||||
|  | 											lPos.xy, lPos.z - shadowsBias, smSize | ||||||
|  | 											#ifdef _ShadowMapTransparent | ||||||
|  | 											, transparent | ||||||
|  | 											#endif | ||||||
|  | 											); | ||||||
|  |  | ||||||
| 		float lerpAmt = smoothstep(0.0, blendThres, splitDist); | 		float lerpAmt = smoothstep(0.0, blendThres, splitDist); | ||||||
| 		return mix(visibility2, visibility, lerpAmt); | 		return mix(visibility2, visibility, lerpAmt); | ||||||
| 	} | 	} | ||||||
| 	return visibility; | 	return visibility; | ||||||
|  |  | ||||||
| 	// Visualize cascades | 	// Visualize cascades | ||||||
| 	// if (ci == 0) albedo.rgb = vec3(1.0, 0.0, 0.0); | 	// if (ci == 0) albedo.rgb = vec3(1.0, 0.0, 0.0); | ||||||
| 	// if (ci == 4) albedo.rgb = vec3(0.0, 1.0, 0.0); | 	// if (ci == 4) albedo.rgb = vec3(0.0, 1.0, 0.0); | ||||||
| @ -403,4 +460,4 @@ vec3 shadowTestCascade(sampler2DShadow shadowMap, sampler2D shadowMapTransparent | |||||||
| 	// if (ci == 12) albedo.rgb = vec3(1.0, 1.0, 0.0); | 	// if (ci == 12) albedo.rgb = vec3(1.0, 1.0, 0.0); | ||||||
| } | } | ||||||
| #endif | #endif | ||||||
| #endif | #endif | ||||||
| @ -11,6 +11,8 @@ vec3 uncharted2Tonemap(const vec3 x) { | |||||||
| vec3 tonemapUncharted2(const vec3 color) { | vec3 tonemapUncharted2(const vec3 color) { | ||||||
| 	const float W = 11.2; | 	const float W = 11.2; | ||||||
| 	const float exposureBias = 2.0; | 	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 curr = uncharted2Tonemap(exposureBias * color); | ||||||
| 	vec3 whiteScale = 1.0 / uncharted2Tonemap(vec3(W)); | 	vec3 whiteScale = 1.0 / uncharted2Tonemap(vec3(W)); | ||||||
| 	return curr * whiteScale; | 	return curr * whiteScale; | ||||||
|  | |||||||
| @ -11,6 +11,11 @@ | |||||||
| #include "std/light_common.glsl" | #include "std/light_common.glsl" | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
|  | #ifdef _CPostprocess | ||||||
|  | uniform vec3 PPComp11; | ||||||
|  | uniform vec4 PPComp17; | ||||||
|  | #endif | ||||||
|  |  | ||||||
| uniform sampler2D gbufferD; | uniform sampler2D gbufferD; | ||||||
| uniform sampler2D snoise; | uniform sampler2D snoise; | ||||||
|  |  | ||||||
| @ -87,7 +92,13 @@ out float fragColor; | |||||||
| const float tScat = 0.08; | const float tScat = 0.08; | ||||||
| const float tAbs = 0.0; | const float tAbs = 0.0; | ||||||
| const float tExt = tScat + tAbs; | 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; | const float lighting = 0.4; | ||||||
|  |  | ||||||
| void rayStep(inout vec3 curPos, inout float curOpticalDepth, inout float scatteredLightAmount, float stepLenWorld, vec3 viewVecNorm) { | 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); | 		rayStep(curPos, curOpticalDepth, scatteredLightAmount, stepLenWorld, viewVecNorm); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	fragColor = scatteredLightAmount * volumAirTurbidity; | 	fragColor = scatteredLightAmount * AirTurbidity; | ||||||
| } | } | ||||||
|  | |||||||
| @ -140,6 +140,16 @@ | |||||||
| 					"link": "_biasLightWorldViewProjectionMatrixSpot3", | 					"link": "_biasLightWorldViewProjectionMatrixSpot3", | ||||||
| 					"ifndef": ["_ShadowMapAtlas"], | 					"ifndef": ["_ShadowMapAtlas"], | ||||||
| 					"ifdef": ["_Spot", "_ShadowMap"] | 					"ifdef": ["_Spot", "_ShadowMap"] | ||||||
|  | 				}, | ||||||
|  | 				{ | ||||||
|  | 					"name": "PPComp11", | ||||||
|  | 					"link": "_PPComp11", | ||||||
|  | 					"ifdef": ["_CPostprocess"] | ||||||
|  | 				}, | ||||||
|  | 				{ | ||||||
|  | 					"name": "PPComp17", | ||||||
|  | 					"link": "_PPComp17", | ||||||
|  | 					"ifdef": ["_CPostprocess"] | ||||||
| 				} | 				} | ||||||
| 			], | 			], | ||||||
| 			"texture_params": [], | 			"texture_params": [], | ||||||
|  | |||||||
| @ -87,51 +87,49 @@ float lpToDepth(vec3 lp, const vec2 lightProj) { | |||||||
|  |  | ||||||
| void main() { | void main() { | ||||||
| 	int res = voxelgiResolution.x; | 	int res = voxelgiResolution.x; | ||||||
| 	for (int i = 0; i < 6; i++) { | 	ivec3 dst = ivec3(gl_GlobalInvocationID.xyz); | ||||||
| 		ivec3 dst = ivec3(gl_GlobalInvocationID.xyz); |  | ||||||
| 		vec3 P = (gl_GlobalInvocationID.xyz + 0.5) / voxelgiResolution; |  | ||||||
| 		P = P * 2.0 - 1.0; |  | ||||||
|  |  | ||||||
| 		float visibility; | 	vec3 wposition = (gl_GlobalInvocationID.xyz + 0.5) / voxelgiResolution.x; | ||||||
| 		vec3 lp = lightPos - P; | 	wposition = wposition * 2.0 - 1.0; | ||||||
| 		vec3 l; | 	wposition *= float(clipmaps[int(clipmapLevel * 10)]); | ||||||
| 		if (lightType == 0) { l = lightDir; visibility = 1.0; } | 	wposition *= voxelgiResolution.x; | ||||||
| 		else { l = normalize(lp); visibility = attenuate(distance(P, lightPos)); } | 	wposition += vec3(clipmaps[clipmapLevel * 10 + 4], clipmaps[clipmapLevel * 10 + 5], clipmaps[clipmapLevel * 10 + 6]); | ||||||
|  |  | ||||||
|  | 	float visibility; | ||||||
|  | 	vec3 lp = lightPos -wposition; | ||||||
|  | 	vec3 l; | ||||||
|  | 	if (lightType == 0) { l = lightDir; visibility = 1.0; } | ||||||
|  | 	else { l = normalize(lp); visibility = attenuate(distance(wposition, lightPos)); } | ||||||
|  |  | ||||||
| #ifdef _ShadowMap | #ifdef _ShadowMap | ||||||
| 		if (lightShadow == 1) { | 	if (lightShadow == 1) { | ||||||
| 			vec4 lightPosition = LVP * vec4(P, 1.0); | 		vec4 lightPosition = LVP * vec4(wposition, 1.0); | ||||||
| 			vec3 lPos = lightPosition.xyz / lightPosition.w; | 		vec3 lPos = lightPosition.xyz / lightPosition.w; | ||||||
| 			visibility = texture(shadowMap, vec3(lPos.xy, lPos.z - shadowsBias)).r; | 		visibility = texture(shadowMap, vec3(lPos.xy, lPos.z - shadowsBias)).r; | ||||||
| 		} |  | ||||||
| 		else if (lightShadow == 2) { |  | ||||||
| 			vec4 lightPosition = LVP * vec4(P, 1.0); |  | ||||||
| 			vec3 lPos = lightPosition.xyz / lightPosition.w; |  | ||||||
| 			visibility *= texture(shadowMapSpot, vec3(lPos.xy, lPos.z - shadowsBias)).r; |  | ||||||
| 		} |  | ||||||
| 		else if (lightShadow == 3) { |  | ||||||
| 			#ifdef _ShadowMapAtlas |  | ||||||
| 			int faceIndex = 0; |  | ||||||
| 			const int lightIndex = index * 6; |  | ||||||
| 			const vec2 uv = sampleCube(-l, faceIndex); |  | ||||||
| 			vec4 pointLightTile = pointLightDataArray[lightIndex + faceIndex]; // x: tile X offset, y: tile Y offset, z: tile size relative to atlas |  | ||||||
| 			vec2 uvtiled = pointLightTile.z * uv + pointLightTile.xy; |  | ||||||
| 			#ifdef _FlipY |  | ||||||
| 			uvtiled.y = 1.0 - uvtiled.y; // invert Y coordinates for direct3d coordinate system |  | ||||||
| 			#endif |  | ||||||
| 			visibility *= texture(shadowMapPoint, vec3(uvtiled, lpToDepth(lp, lightProj) - shadowsBias)).r; |  | ||||||
| 			#else |  | ||||||
| 			visibility *= texture(shadowMapPoint, vec4(-l, lpToDepth(lp, lightProj) - shadowsBias)).r; |  | ||||||
| 			#endif |  | ||||||
| 		} |  | ||||||
| 	#endif |  | ||||||
| 		vec3 uvw_light = (P - vec3(clipmaps[int(clipmapLevel * 10 + 4)], clipmaps[int(clipmapLevel * 10 + 5)], clipmaps[int(clipmapLevel * 10 + 6)])) / (float(clipmaps[int(clipmapLevel * 10)]) * voxelgiResolution); |  | ||||||
| 		uvw_light = (uvw_light * 0.5 + 0.5); |  | ||||||
| 		if (any(notEqual(uvw_light, clamp(uvw_light, 0.0, 1.0)))) return; |  | ||||||
| 		vec3 writecoords_light = floor(uvw_light * voxelgiResolution); |  | ||||||
|  |  | ||||||
| 		imageAtomicMax(voxelsLight, ivec3(writecoords_light), uint(visibility * lightColor.r * 255)); |  | ||||||
| 		imageAtomicMax(voxelsLight, ivec3(writecoords_light) + ivec3(0, 0, voxelgiResolution.x), uint(visibility * lightColor.g * 255)); |  | ||||||
| 		imageAtomicMax(voxelsLight, ivec3(writecoords_light) + ivec3(0, 0, voxelgiResolution.x * 2), uint(visibility * lightColor.b * 255)); |  | ||||||
| 	} | 	} | ||||||
|  | 	else if (lightShadow == 2) { | ||||||
|  | 		vec4 lightPosition = LVP * vec4(wposition, 1.0); | ||||||
|  | 		vec3 lPos = lightPosition.xyz / lightPosition.w; | ||||||
|  | 		visibility *= texture(shadowMapSpot, vec3(lPos.xy, lPos.z - shadowsBias)).r; | ||||||
|  | 	} | ||||||
|  | 	else if (lightShadow == 3) { | ||||||
|  | 		#ifdef _ShadowMapAtlas | ||||||
|  | 		int faceIndex = 0; | ||||||
|  | 		const int lightIndex = index * 6; | ||||||
|  | 		const vec2 uv = sampleCube(-l, faceIndex); | ||||||
|  | 		vec4 pointLightTile = pointLightDataArray[lightIndex + faceIndex]; // x: tile X offset, y: tile Y offset, z: tile size relative to atlas | ||||||
|  | 		vec2 uvtiled = pointLightTile.z * uv + pointLightTile.xy; | ||||||
|  | 		#ifdef _FlipY | ||||||
|  | 		uvtiled.y = 1.0 - uvtiled.y; // invert Y coordinates for direct3d coordinate system | ||||||
|  | 		#endif | ||||||
|  | 		visibility *= texture(shadowMapPoint, vec3(uvtiled, lpToDepth(lp, lightProj) - shadowsBias)).r; | ||||||
|  | 		#else | ||||||
|  | 		visibility *= texture(shadowMapPoint, vec4(-l, lpToDepth(lp, lightProj) - shadowsBias)).r; | ||||||
|  | 		#endif | ||||||
|  | 	} | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | 	imageAtomicAdd(voxelsLight, dst, uint(visibility * lightColor.r * 255)); | ||||||
|  | 	imageAtomicAdd(voxelsLight, dst + ivec3(0, 0, voxelgiResolution.x), uint(visibility * lightColor.g * 255)); | ||||||
|  | 	imageAtomicAdd(voxelsLight, dst + ivec3(0, 0, voxelgiResolution.x * 2), uint(visibility * lightColor.b * 255)); | ||||||
| } | } | ||||||
|  | |||||||
| @ -27,14 +27,14 @@ layout (local_size_x = 8, local_size_y = 8, local_size_z = 8) in; | |||||||
| #include "std/math.glsl" | #include "std/math.glsl" | ||||||
| #include "std/gbuffer.glsl" | #include "std/gbuffer.glsl" | ||||||
| #include "std/imageatomic.glsl" | #include "std/imageatomic.glsl" | ||||||
| #include "std/voxels_constants.glsl" | #include "std/constants.glsl" | ||||||
|  |  | ||||||
| #ifdef _VoxelGI | #ifdef _VoxelGI | ||||||
| uniform layout(rgba8) image3D voxelsB; | uniform layout(rgba8) image3D voxelsB; | ||||||
| uniform layout(rgba8) image3D voxelsOut; | uniform layout(rgba8) image3D voxelsOut; | ||||||
| #else | #else | ||||||
| uniform layout(r16) image3D voxelsB; | uniform layout(r8) image3D voxelsB; | ||||||
| uniform layout(r16) image3D voxelsOut; | uniform layout(r8) image3D voxelsOut; | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| uniform int clipmapLevel; | uniform int clipmapLevel; | ||||||
|  | |||||||
| @ -29,19 +29,38 @@ layout (local_size_x = 8, local_size_y = 8, local_size_z = 1) in; | |||||||
| #include "std/gbuffer.glsl" | #include "std/gbuffer.glsl" | ||||||
| #include "std/imageatomic.glsl" | #include "std/imageatomic.glsl" | ||||||
| #include "std/conetrace.glsl" | #include "std/conetrace.glsl" | ||||||
|  | #include "std/brdf.glsl" | ||||||
|  | #include "std/shirr.glsl" | ||||||
|  |  | ||||||
| uniform sampler3D voxels; | uniform sampler3D voxels; | ||||||
| uniform sampler2D gbufferD; | uniform sampler2D gbufferD; | ||||||
| uniform sampler2D gbuffer0; | uniform sampler2D gbuffer0; | ||||||
| uniform layout(r16) image2D voxels_ao; | uniform layout(rgba8) image2D voxels_ao; | ||||||
|  |  | ||||||
| uniform float clipmaps[voxelgiClipmapCount * 10]; | uniform float clipmaps[voxelgiClipmapCount * 10]; | ||||||
| uniform mat4 InvVP; | uniform mat4 InvVP; | ||||||
| uniform vec2 cameraProj; |  | ||||||
| uniform vec3 eye; | uniform vec3 eye; | ||||||
| uniform vec3 eyeLook; |  | ||||||
| uniform vec2 postprocess_resolution; | uniform vec2 postprocess_resolution; | ||||||
|  |  | ||||||
|  | uniform sampler2D gbuffer1; | ||||||
|  | #ifdef _gbuffer2 | ||||||
|  | uniform sampler2D gbuffer2; | ||||||
|  | #endif | ||||||
|  | uniform float envmapStrength; | ||||||
|  | #ifdef _Irr | ||||||
|  | uniform float shirr[7 * 4]; | ||||||
|  | #endif | ||||||
|  | #ifdef _Brdf | ||||||
|  | uniform sampler2D senvmapBrdf; | ||||||
|  | #endif | ||||||
|  | #ifdef _Rad | ||||||
|  | uniform sampler2D senvmapRadiance; | ||||||
|  | uniform int envmapNumMipmaps; | ||||||
|  | #endif | ||||||
|  | #ifdef _EnvCol | ||||||
|  | uniform vec3 backgroundCol; | ||||||
|  | #endif | ||||||
|  |  | ||||||
| void main() { | void main() { | ||||||
| 	const vec2 pixel = gl_GlobalInvocationID.xy; | 	const vec2 pixel = gl_GlobalInvocationID.xy; | ||||||
| 	vec2 uv = (pixel + 0.5) / postprocess_resolution; | 	vec2 uv = (pixel + 0.5) / postprocess_resolution; | ||||||
| @ -54,12 +73,11 @@ void main() { | |||||||
|  |  | ||||||
| 	float x = uv.x * 2 - 1; | 	float x = uv.x * 2 - 1; | ||||||
| 	float y = uv.y * 2 - 1; | 	float y = uv.y * 2 - 1; | ||||||
| 	vec4 v = vec4(x, y, 1.0, 1.0); | 	vec4 clipPos = vec4(x, y, depth, 1.0); | ||||||
| 	v = vec4(InvVP * v); |     vec4 worldPos = InvVP * clipPos; | ||||||
| 	v.xyz /= v.w; |     vec3 P = worldPos.xyz / worldPos.w; | ||||||
| 	vec3 viewRay = v.xyz - eye; |  | ||||||
|  |  | ||||||
| 	vec3 P = getPos(eye, eyeLook, normalize(viewRay), depth, cameraProj); | 	vec3 v = normalize(eye - P); | ||||||
|  |  | ||||||
| 	vec4 g0 = textureLod(gbuffer0, uv, 0.0); | 	vec4 g0 = textureLod(gbuffer0, uv, 0.0); | ||||||
| 	vec3 n; | 	vec3 n; | ||||||
| @ -67,7 +85,89 @@ void main() { | |||||||
| 	n.xy = n.z >= 0.0 ? g0.xy : octahedronWrap(g0.xy); | 	n.xy = n.z >= 0.0 ? g0.xy : octahedronWrap(g0.xy); | ||||||
| 	n = normalize(n); | 	n = normalize(n); | ||||||
|  |  | ||||||
| 	float occ = 1.0 - traceAO(P, n, voxels, clipmaps); | 	float roughness = g0.b; | ||||||
|  | 	float metallic; | ||||||
|  | 	uint matid; | ||||||
|  | 	unpackFloatInt16(g0.a, metallic, matid); | ||||||
|  |  | ||||||
| 	imageStore(voxels_ao, ivec2(pixel), vec4(occ)); | 	vec4 g1 = textureLod(gbuffer1, uv, 0.0); // Basecolor.rgb, spec/occ | ||||||
|  | 	vec2 occspec = unpackFloat2(g1.a); | ||||||
|  | 	vec3 albedo = surfaceAlbedo(g1.rgb, metallic); // g1.rgb - basecolor | ||||||
|  | 	vec3 f0 = surfaceF0(g1.rgb, metallic); | ||||||
|  | 	float dotNV = max(dot(n, v), 0.0); | ||||||
|  |  | ||||||
|  | #ifdef _gbuffer2 | ||||||
|  | 	vec4 g2 = textureLod(gbuffer2, uv, 0.0); | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | #ifdef _MicroShadowing | ||||||
|  | 	occspec.x = mix(1.0, occspec.x, dotNV); // AO Fresnel | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | #ifdef _Brdf | ||||||
|  | 	vec2 envBRDF = texelFetch(senvmapBrdf, ivec2(vec2(dotNV, 1.0 - roughness) * 256.0), 0).xy; | ||||||
|  | 	vec3 F = f0 * envBRDF.x + envBRDF.y; | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | 	// Envmap | ||||||
|  | #ifdef _Irr | ||||||
|  | 	vec4 shPacked[7]; | ||||||
|  |     for (int i = 0; i < 7; i++) { | ||||||
|  |         int base = i * 4; | ||||||
|  |         shPacked[i] = vec4( | ||||||
|  |             shirr[base], | ||||||
|  |             shirr[base + 1], | ||||||
|  |             shirr[base + 2], | ||||||
|  |             shirr[base + 3] | ||||||
|  |         ); | ||||||
|  |     } | ||||||
|  | 	vec3 envl = shIrradiance(n, shPacked); | ||||||
|  |  | ||||||
|  | 	#ifdef _gbuffer2 | ||||||
|  | 		if (g2.b < 0.5) { | ||||||
|  | 			envl = envl; | ||||||
|  | 		} else { | ||||||
|  | 			envl = vec3(0.0); | ||||||
|  | 		} | ||||||
|  | 	#endif | ||||||
|  |  | ||||||
|  | 	#ifdef _EnvTex | ||||||
|  | 		envl /= PI; | ||||||
|  | 	#endif | ||||||
|  | #else | ||||||
|  | 	vec3 envl = vec3(0.0); | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | #ifdef _Rad | ||||||
|  | 	vec3 reflectionWorld = reflect(-v, n); | ||||||
|  | 	float lod = getMipFromRoughness(roughness, envmapNumMipmaps); | ||||||
|  | 	vec3 prefilteredColor = textureLod(senvmapRadiance, envMapEquirect(reflectionWorld), lod).rgb; | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | #ifdef _EnvLDR | ||||||
|  | 	envl.rgb = pow(envl.rgb, vec3(2.2)); | ||||||
|  | 	#ifdef _Rad | ||||||
|  | 		prefilteredColor = pow(prefilteredColor, vec3(2.2)); | ||||||
|  | 	#endif | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | 	envl.rgb *= albedo; | ||||||
|  |  | ||||||
|  | #ifdef _Brdf | ||||||
|  | 	envl.rgb *= 1.0 - F; //LV: We should take refracted light into account | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | #ifdef _Rad // Indirect specular | ||||||
|  | 	envl.rgb += prefilteredColor * F; //LV: Removed "1.5 * occspec.y". Specular should be weighted only by FV LUT | ||||||
|  | #else | ||||||
|  | 	#ifdef _EnvCol | ||||||
|  | 	envl.rgb += backgroundCol * F; //LV: Eh, what's the point of weighting it only by F0? | ||||||
|  | 	#endif | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | 	envl.rgb *= envmapStrength * occspec.x; | ||||||
|  |  | ||||||
|  | 	vec3 occ = envl * (1.0 - traceAO(P, n, voxels, clipmaps)); | ||||||
|  |  | ||||||
|  | 	imageStore(voxels_ao, ivec2(pixel), vec4(occ, 1.0)); | ||||||
| } | } | ||||||
|  | |||||||
| @ -29,19 +29,38 @@ layout (local_size_x = 8, local_size_y = 8, local_size_z = 1) in; | |||||||
| #include "std/gbuffer.glsl" | #include "std/gbuffer.glsl" | ||||||
| #include "std/imageatomic.glsl" | #include "std/imageatomic.glsl" | ||||||
| #include "std/conetrace.glsl" | #include "std/conetrace.glsl" | ||||||
|  | #include "std/brdf.glsl" | ||||||
|  | #include "std/shirr.glsl" | ||||||
|  |  | ||||||
| uniform sampler3D voxels; | uniform sampler3D voxels; | ||||||
| uniform sampler2D gbufferD; | uniform sampler2D gbufferD; | ||||||
| uniform sampler2D gbuffer0; | uniform sampler2D gbuffer0; | ||||||
| uniform layout(rgba16) image2D voxels_diffuse; | uniform layout(rgba8) image2D voxels_diffuse; | ||||||
|  |  | ||||||
| uniform float clipmaps[voxelgiClipmapCount * 10]; | uniform float clipmaps[voxelgiClipmapCount * 10]; | ||||||
| uniform mat4 InvVP; | uniform mat4 InvVP; | ||||||
| uniform vec2 cameraProj; |  | ||||||
| uniform vec3 eye; | uniform vec3 eye; | ||||||
| uniform vec3 eyeLook; |  | ||||||
| uniform vec2 postprocess_resolution; | uniform vec2 postprocess_resolution; | ||||||
|  |  | ||||||
|  | uniform sampler2D gbuffer1; | ||||||
|  | #ifdef _gbuffer2 | ||||||
|  | uniform sampler2D gbuffer2; | ||||||
|  | #endif | ||||||
|  | uniform float envmapStrength; | ||||||
|  | #ifdef _Irr | ||||||
|  | uniform float shirr[7 * 4]; | ||||||
|  | #endif | ||||||
|  | #ifdef _Brdf | ||||||
|  | uniform sampler2D senvmapBrdf; | ||||||
|  | #endif | ||||||
|  | #ifdef _Rad | ||||||
|  | uniform sampler2D senvmapRadiance; | ||||||
|  | uniform int envmapNumMipmaps; | ||||||
|  | #endif | ||||||
|  | #ifdef _EnvCol | ||||||
|  | uniform vec3 backgroundCol; | ||||||
|  | #endif | ||||||
|  |  | ||||||
| void main() { | void main() { | ||||||
| 	const vec2 pixel = gl_GlobalInvocationID.xy; | 	const vec2 pixel = gl_GlobalInvocationID.xy; | ||||||
| 	vec2 uv = (pixel + 0.5) / postprocess_resolution; | 	vec2 uv = (pixel + 0.5) / postprocess_resolution; | ||||||
| @ -50,16 +69,14 @@ void main() { | |||||||
| 	#endif | 	#endif | ||||||
|  |  | ||||||
| 	float depth = textureLod(gbufferD, uv, 0.0).r * 2.0 - 1.0; | 	float depth = textureLod(gbufferD, uv, 0.0).r * 2.0 - 1.0; | ||||||
| 	if (depth == 0) return; | 	if (depth == 0.0) return; | ||||||
|  |  | ||||||
| 	float x = uv.x * 2 - 1; | 	float x = uv.x * 2 - 1; | ||||||
| 	float y = uv.y * 2 - 1; | 	float y = uv.y * 2 - 1; | ||||||
| 	vec4 v = vec4(x, y, 1.0, 1.0); | 	vec4 clipPos = vec4(x, y, depth, 1.0); | ||||||
| 	v = vec4(InvVP * v); |     vec4 worldPos = InvVP * clipPos; | ||||||
| 	v.xyz /= v.w; |     vec3 P = worldPos.xyz / worldPos.w; | ||||||
| 	vec3 viewRay = v.xyz - eye; | 	vec3 v = normalize(eye - P); | ||||||
|  |  | ||||||
| 	vec3 P = getPos(eye, eyeLook, normalize(viewRay), depth, cameraProj); |  | ||||||
|  |  | ||||||
| 	vec4 g0 = textureLod(gbuffer0, uv, 0.0); | 	vec4 g0 = textureLod(gbuffer0, uv, 0.0); | ||||||
| 	vec3 n; | 	vec3 n; | ||||||
| @ -67,7 +84,94 @@ void main() { | |||||||
| 	n.xy = n.z >= 0.0 ? g0.xy : octahedronWrap(g0.xy); | 	n.xy = n.z >= 0.0 ? g0.xy : octahedronWrap(g0.xy); | ||||||
| 	n = normalize(n); | 	n = normalize(n); | ||||||
|  |  | ||||||
| 	vec4 color = traceDiffuse(P, n, voxels, clipmaps); | 	float roughness = g0.b; | ||||||
|  | 	float metallic; | ||||||
|  | 	uint matid; | ||||||
|  | 	unpackFloatInt16(g0.a, metallic, matid); | ||||||
|  |  | ||||||
| 	imageStore(voxels_diffuse, ivec2(pixel), color); | 	vec4 g1 = textureLod(gbuffer1, uv, 0.0); // Basecolor.rgb, spec/occ | ||||||
|  | 	vec2 occspec = unpackFloat2(g1.a); | ||||||
|  | 	vec3 albedo = surfaceAlbedo(g1.rgb, metallic); // g1.rgb - basecolor | ||||||
|  | 	vec3 f0 = surfaceF0(g1.rgb, metallic); | ||||||
|  | 	float dotNV = max(dot(n, v), 0.0); | ||||||
|  |  | ||||||
|  | #ifdef _gbuffer2 | ||||||
|  | 	vec4 g2 = textureLod(gbuffer2, uv, 0.0); | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | #ifdef _MicroShadowing | ||||||
|  | 	occspec.x = mix(1.0, occspec.x, dotNV); // AO Fresnel | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | #ifdef _Brdf | ||||||
|  | 	vec2 envBRDF = texelFetch(senvmapBrdf, ivec2(vec2(dotNV, 1.0 - roughness) * 256.0), 0).xy; | ||||||
|  | 	vec3 F = f0 * envBRDF.x + envBRDF.y; | ||||||
|  | #else | ||||||
|  | 	vec3 F = f0; | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  |  | ||||||
|  | 	// Envmap | ||||||
|  | #ifdef _Irr | ||||||
|  | 	vec4 shPacked[7]; | ||||||
|  |     for (int i = 0; i < 7; i++) { | ||||||
|  |         int base = i * 4; | ||||||
|  |         shPacked[i] = vec4( | ||||||
|  |             shirr[base], | ||||||
|  |             shirr[base + 1], | ||||||
|  |             shirr[base + 2], | ||||||
|  |             shirr[base + 3] | ||||||
|  |         ); | ||||||
|  |     } | ||||||
|  | 	vec3 envl = shIrradiance(n, shPacked); | ||||||
|  |  | ||||||
|  | 	#ifdef _gbuffer2 | ||||||
|  | 		if (g2.b < 0.5) { | ||||||
|  | 			envl = envl; | ||||||
|  | 		} else { | ||||||
|  | 			envl = vec3(0.0); | ||||||
|  | 		} | ||||||
|  | 	#endif | ||||||
|  |  | ||||||
|  | 	#ifdef _EnvTex | ||||||
|  | 		envl /= PI; | ||||||
|  | 	#endif | ||||||
|  | #else | ||||||
|  | 	vec3 envl = vec3(0.0); | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | #ifdef _Rad | ||||||
|  | 	vec3 reflectionWorld = reflect(-v, n); | ||||||
|  | 	float lod = getMipFromRoughness(roughness, envmapNumMipmaps); | ||||||
|  | 	vec3 prefilteredColor = textureLod(senvmapRadiance, envMapEquirect(reflectionWorld), lod).rgb; | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | #ifdef _EnvLDR | ||||||
|  | 	envl.rgb = pow(envl.rgb, vec3(2.2)); | ||||||
|  | 	#ifdef _Rad | ||||||
|  | 		prefilteredColor = pow(prefilteredColor, vec3(2.2)); | ||||||
|  | 	#endif | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | 	envl.rgb *= albedo; | ||||||
|  |  | ||||||
|  | #ifdef _Brdf | ||||||
|  | 	envl.rgb *= 1.0 - F; //LV: We should take refracted light into account | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | #ifdef _Rad // Indirect specular | ||||||
|  | 	envl.rgb += prefilteredColor * F; //LV: Removed "1.5 * occspec.y". Specular should be weighted only by FV LUT | ||||||
|  | #else | ||||||
|  | 	#ifdef _EnvCol | ||||||
|  | 	envl.rgb += backgroundCol * F; //LV: Eh, what's the point of weighting it only by F0? | ||||||
|  | 	#endif | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | 	envl.rgb *= envmapStrength * occspec.x; | ||||||
|  |  | ||||||
|  | 	vec4 trace = traceDiffuse(P, n, voxels, clipmaps); | ||||||
|  | 	vec3 color = trace.rgb * albedo * (1.0 - F); | ||||||
|  | 	color += envl * (1.0 - trace.a); | ||||||
|  |  | ||||||
|  | 	imageStore(voxels_diffuse, ivec2(pixel), vec4(color, 1.0)); | ||||||
| } | } | ||||||
|  | |||||||
| @ -1,79 +0,0 @@ | |||||||
| /* |  | ||||||
| Copyright (c) 2024 Turánszki János |  | ||||||
|  |  | ||||||
| Permission is hereby granted, free of charge, to any person obtaining a copy |  | ||||||
| of this software and associated documentation files (the "Software"), to deal |  | ||||||
| in the Software without restriction, including without limitation the rights |  | ||||||
| to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |  | ||||||
| copies of the Software, and to permit persons to whom the Software is |  | ||||||
| furnished to do so, subject to the following conditions: |  | ||||||
|  |  | ||||||
| The above copyright notice and this permission notice shall be included in |  | ||||||
| all copies or substantial portions of the Software. |  | ||||||
|  |  | ||||||
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |  | ||||||
| IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |  | ||||||
| FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE |  | ||||||
| AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |  | ||||||
| LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |  | ||||||
| OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |  | ||||||
| THE SOFTWARE. |  | ||||||
|  */ |  | ||||||
|  |  | ||||||
| #version 450 |  | ||||||
|  |  | ||||||
| layout (local_size_x = 8, local_size_y = 8, local_size_z = 1) in; |  | ||||||
|  |  | ||||||
| #include "compiled.inc" |  | ||||||
| #include "std/math.glsl" |  | ||||||
| #include "std/gbuffer.glsl" |  | ||||||
| #include "std/imageatomic.glsl" |  | ||||||
| #include "std/conetrace.glsl" |  | ||||||
|  |  | ||||||
| uniform sampler2D gbufferD; |  | ||||||
| uniform sampler2D gbuffer0; |  | ||||||
| uniform sampler3D voxels; |  | ||||||
| uniform sampler3D voxelsSDF; |  | ||||||
| uniform sampler2D gbuffer_refraction; |  | ||||||
| uniform layout(rgba8) image2D voxels_refraction; |  | ||||||
|  |  | ||||||
| uniform float clipmaps[voxelgiClipmapCount * 10]; |  | ||||||
| uniform mat4 InvVP; |  | ||||||
| uniform vec2 cameraProj; |  | ||||||
| uniform vec3 eye; |  | ||||||
| uniform vec3 eyeLook; |  | ||||||
| uniform vec2 postprocess_resolution; |  | ||||||
|  |  | ||||||
| void main() { |  | ||||||
| 	const vec2 pixel = gl_GlobalInvocationID.xy; |  | ||||||
| 	vec2 uv = (pixel + 0.5) / postprocess_resolution; |  | ||||||
| 	#ifdef _InvY |  | ||||||
| 	uv.y = 1.0 - uv.y |  | ||||||
| 	#endif |  | ||||||
|  |  | ||||||
| 	float depth = textureLod(gbufferD, uv, 0.0).r * 2.0 - 1.0; |  | ||||||
| 	if (depth == 0) return; |  | ||||||
|  |  | ||||||
| 	vec2 ior_opac = textureLod(gbuffer_refraction, uv, 0.0).xy; |  | ||||||
|  |  | ||||||
| 	float x = uv.x * 2 - 1; |  | ||||||
| 	float y = uv.y * 2 - 1; |  | ||||||
| 	vec4 v = vec4(x, y, 1.0, 1.0); |  | ||||||
| 	v = vec4(InvVP * v); |  | ||||||
| 	v.xyz /= v.w; |  | ||||||
| 	vec3 viewRay = v.xyz - eye; |  | ||||||
|  |  | ||||||
| 	vec3 P = getPos(eye, eyeLook, normalize(viewRay), depth, cameraProj); |  | ||||||
|  |  | ||||||
| 	vec4 g0 = textureLod(gbuffer0, uv, 0.0); |  | ||||||
| 	vec3 n; |  | ||||||
| 	n.z = 1.0 - abs(g0.x) - abs(g0.y); |  | ||||||
| 	n.xy = n.z >= 0.0 ? g0.xy : octahedronWrap(g0.xy); |  | ||||||
| 	n = normalize(n); |  | ||||||
|  |  | ||||||
| 	vec3 color = vec3(0.0); |  | ||||||
| 	if(ior_opac.y < 1.0) |  | ||||||
| 		color = traceRefraction(P, n, voxels, voxelsSDF, normalize(eye - P), ior_opac.x, g0.b, clipmaps, pixel).rgb; |  | ||||||
|  |  | ||||||
| 	imageStore(voxels_refraction, ivec2(pixel), vec4(color, 1.0)); |  | ||||||
| } |  | ||||||
| @ -1,75 +0,0 @@ | |||||||
| /* |  | ||||||
| Copyright (c) 2024 Turánszki János |  | ||||||
|  |  | ||||||
| Permission is hereby granted, free of charge, to any person obtaining a copy |  | ||||||
| of this software and associated documentation files (the "Software"), to deal |  | ||||||
| in the Software without restriction, including without limitation the rights |  | ||||||
| to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |  | ||||||
| copies of the Software, and to permit persons to whom the Software is |  | ||||||
| furnished to do so, subject to the following conditions: |  | ||||||
|  |  | ||||||
| The above copyright notice and this permission notice shall be included in |  | ||||||
| all copies or substantial portions of the Software. |  | ||||||
|  |  | ||||||
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |  | ||||||
| IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |  | ||||||
| FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE |  | ||||||
| AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |  | ||||||
| LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |  | ||||||
| OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |  | ||||||
| THE SOFTWARE. |  | ||||||
|  */ |  | ||||||
|  |  | ||||||
| #version 450 |  | ||||||
|  |  | ||||||
| layout (local_size_x = 8, local_size_y = 8, local_size_z = 1) in; |  | ||||||
|  |  | ||||||
| #include "compiled.inc" |  | ||||||
| #include "std/math.glsl" |  | ||||||
| #include "std/gbuffer.glsl" |  | ||||||
| #include "std/imageatomic.glsl" |  | ||||||
| #include "std/conetrace.glsl" |  | ||||||
|  |  | ||||||
| uniform sampler3D voxels; |  | ||||||
| uniform sampler3D voxelsSDF; |  | ||||||
| uniform sampler2D gbufferD; |  | ||||||
| uniform sampler2D gbuffer0; |  | ||||||
| uniform layout(r16) image2D voxels_shadows; |  | ||||||
|  |  | ||||||
| uniform float clipmaps[voxelgiClipmapCount * 10]; |  | ||||||
| uniform mat4 InvVP; |  | ||||||
| uniform vec2 cameraProj; |  | ||||||
| uniform vec3 eye; |  | ||||||
| uniform vec3 eyeLook; |  | ||||||
| uniform vec2 postprocess_resolution; |  | ||||||
| uniform vec3 lPos; |  | ||||||
|  |  | ||||||
| void main() { |  | ||||||
| 	const vec2 pixel = gl_GlobalInvocationID.xy; |  | ||||||
| 	vec2 uv = (pixel + 0.5) / postprocess_resolution; |  | ||||||
| 	#ifdef _InvY |  | ||||||
| 	uv.y = 1.0 - uv.y; |  | ||||||
| 	#endif |  | ||||||
|  |  | ||||||
| 	float depth = textureLod(gbufferD, uv, 0.0).r * 2.0 - 1.0; |  | ||||||
| 	if (depth == 0) return; |  | ||||||
|  |  | ||||||
| 	float x = uv.x * 2 - 1; |  | ||||||
| 	float y = uv.y * 2 - 1; |  | ||||||
| 	vec4 v = vec4(x, y, 1.0, 1.0); |  | ||||||
| 	v = vec4(InvVP * v); |  | ||||||
| 	v.xyz /= v.w; |  | ||||||
| 	vec3 viewRay = v.xyz - eye; |  | ||||||
|  |  | ||||||
| 	vec3 P = getPos(eye, eyeLook, normalize(viewRay), depth, cameraProj); |  | ||||||
|  |  | ||||||
| 	vec4 g0 = textureLod(gbuffer0, uv, 0.0); |  | ||||||
| 	vec3 n; |  | ||||||
| 	n.z = 1.0 - abs(g0.x) - abs(g0.y); |  | ||||||
| 	n.xy = n.z >= 0.0 ? g0.xy : octahedronWrap(g0.xy); |  | ||||||
| 	n = normalize(n); |  | ||||||
|  |  | ||||||
| 	float occ = 1.0 - traceShadow(P, n, voxels, voxelsSDF, normalize(lPos - P), clipmaps, pixel); |  | ||||||
|  |  | ||||||
| 	imageStore(voxels_shadows, ivec2(pixel), vec4(occ)); |  | ||||||
| } |  | ||||||
| @ -29,18 +29,17 @@ layout (local_size_x = 8, local_size_y = 8, local_size_z = 1) in; | |||||||
| #include "std/gbuffer.glsl" | #include "std/gbuffer.glsl" | ||||||
| #include "std/imageatomic.glsl" | #include "std/imageatomic.glsl" | ||||||
| #include "std/conetrace.glsl" | #include "std/conetrace.glsl" | ||||||
|  | #include "std/brdf.glsl" | ||||||
|  |  | ||||||
| uniform sampler2D gbufferD; | uniform sampler2D gbufferD; | ||||||
| uniform sampler2D gbuffer0; | uniform sampler2D gbuffer0; | ||||||
| uniform sampler3D voxels; | uniform sampler3D voxels; | ||||||
| uniform sampler3D voxelsSDF; | uniform sampler3D voxelsSDF; | ||||||
| uniform layout(rgba16) image2D voxels_specular; | uniform layout(rgba8) image2D voxels_specular; | ||||||
|  |  | ||||||
| uniform float clipmaps[voxelgiClipmapCount * 10]; | uniform float clipmaps[voxelgiClipmapCount * 10]; | ||||||
| uniform mat4 InvVP; | uniform mat4 InvVP; | ||||||
| uniform vec2 cameraProj; |  | ||||||
| uniform vec3 eye; | uniform vec3 eye; | ||||||
| uniform vec3 eyeLook; |  | ||||||
| uniform vec2 postprocess_resolution; | uniform vec2 postprocess_resolution; | ||||||
| uniform sampler2D sveloc; | uniform sampler2D sveloc; | ||||||
|  |  | ||||||
| @ -56,12 +55,10 @@ void main() { | |||||||
|  |  | ||||||
| 	float x = uv.x * 2 - 1; | 	float x = uv.x * 2 - 1; | ||||||
| 	float y = uv.y * 2 - 1; | 	float y = uv.y * 2 - 1; | ||||||
| 	vec4 v = vec4(x, y, 1.0, 1.0); | 	vec4 clipPos = vec4(x, y, depth, 1.0); | ||||||
| 	v = vec4(InvVP * v); |     vec4 worldPos = InvVP * clipPos; | ||||||
| 	v.xyz /= v.w; |     vec3 P = worldPos.xyz / worldPos.w; | ||||||
|  |  | ||||||
| 	vec3 viewRay = v.xyz - eye; |  | ||||||
| 	vec3 P = getPos(eye, eyeLook, normalize(viewRay), depth, cameraProj); |  | ||||||
| 	vec4 g0 = textureLod(gbuffer0, uv, 0.0); | 	vec4 g0 = textureLod(gbuffer0, uv, 0.0); | ||||||
|  |  | ||||||
| 	vec3 n; | 	vec3 n; | ||||||
|  | |||||||
| @ -74,13 +74,9 @@ void main() { | |||||||
| 	#endif | 	#endif | ||||||
| 	#endif | 	#endif | ||||||
|  |  | ||||||
| 	ivec3 src = ivec3(gl_GlobalInvocationID.xyz); | 	int nor_count = 0; | ||||||
| 	#ifdef _VoxelGI | 	vec3 avgNormal = vec3(0.0); | ||||||
| 	vec3 light = vec3(0.0); | 	mat3 TBN = mat3(0.0); | ||||||
| 	light.r = float(imageLoad(voxelsLight, src)) / 255; |  | ||||||
| 	light.g = float(imageLoad(voxelsLight, src + ivec3(0, 0, voxelgiResolution.x))) / 255; |  | ||||||
| 	light.b = float(imageLoad(voxelsLight, src + ivec3(0, 0, voxelgiResolution.x * 2))) / 255; |  | ||||||
| 	#endif |  | ||||||
|  |  | ||||||
| 	for (int i = 0; i < 6 + DIFFUSE_CONE_COUNT; i++) | 	for (int i = 0; i < 6 + DIFFUSE_CONE_COUNT; i++) | ||||||
| 	{ | 	{ | ||||||
| @ -90,7 +86,7 @@ void main() { | |||||||
| 		float aniso_colors[6]; | 		float aniso_colors[6]; | ||||||
| 		#endif | 		#endif | ||||||
|  |  | ||||||
| 		src = ivec3(gl_GlobalInvocationID.xyz); | 		ivec3 src = ivec3(gl_GlobalInvocationID.xyz); | ||||||
| 		src.x += i * res; | 		src.x += i * res; | ||||||
| 		ivec3 dst = src; | 		ivec3 dst = src; | ||||||
| 		dst.y += clipmapLevel * res; | 		dst.y += clipmapLevel * res; | ||||||
| @ -103,44 +99,67 @@ void main() { | |||||||
|  |  | ||||||
| 		if (i < 6) { | 		if (i < 6) { | ||||||
| 			#ifdef _VoxelGI | 			#ifdef _VoxelGI | ||||||
| 			vec4 basecol = vec4(0.0); | 			int count = int(imageLoad(voxels, src + ivec3(0, 0, voxelgiResolution.x * 15))); | ||||||
| 			basecol.r = float(imageLoad(voxels, src)) / 255; | 			if (count > 0) { | ||||||
| 			basecol.g = float(imageLoad(voxels, src + ivec3(0, 0, voxelgiResolution.x))) / 255; | 				vec4 basecol = vec4(0.0); | ||||||
| 			basecol.b = float(imageLoad(voxels, src + ivec3(0, 0, voxelgiResolution.x * 2))) / 255; | 				basecol.r = float(imageLoad(voxels, src)) / 255; | ||||||
| 			basecol.a = float(imageLoad(voxels, src + ivec3(0, 0, voxelgiResolution.x * 3))) / 255; | 				basecol.g = float(imageLoad(voxels, src + ivec3(0, 0, voxelgiResolution.x))) / 255; | ||||||
| 			basecol /= 4; | 				basecol.b = float(imageLoad(voxels, src + ivec3(0, 0, voxelgiResolution.x * 2))) / 255; | ||||||
| 			vec3 emission = vec3(0.0); | 				basecol.a = float(imageLoad(voxels, src + ivec3(0, 0, voxelgiResolution.x * 3))) / 255; | ||||||
| 			emission.r = float(imageLoad(voxels, src + ivec3(0, 0, voxelgiResolution.x * 4))) / 255; | 				basecol /= count; | ||||||
| 			emission.g = float(imageLoad(voxels, src + ivec3(0, 0, voxelgiResolution.x * 5))) / 255; | 				vec3 emission = vec3(0.0); | ||||||
| 			emission.b = float(imageLoad(voxels, src + ivec3(0, 0, voxelgiResolution.x * 6))) / 255; | 				emission.r = float(imageLoad(voxels, src + ivec3(0, 0, voxelgiResolution.x * 4))) / 255; | ||||||
| 			emission /= 3; | 				emission.g = float(imageLoad(voxels, src + ivec3(0, 0, voxelgiResolution.x * 5))) / 255; | ||||||
| 			vec3 N = vec3(0.0); | 				emission.b = float(imageLoad(voxels, src + ivec3(0, 0, voxelgiResolution.x * 6))) / 255; | ||||||
| 			N.r = float(imageLoad(voxels, src + ivec3(0, 0, voxelgiResolution.x * 7))) / 255; | 				emission /= count; | ||||||
| 			N.g = float(imageLoad(voxels, src + ivec3(0, 0, voxelgiResolution.x * 8))) / 255; | 				vec3 N = vec3(0.0); | ||||||
| 			N /= 2; | 				N.r = float(imageLoad(voxels, src + ivec3(0, 0, voxelgiResolution.x * 7))) / 255; | ||||||
| 			vec3 wnormal = decode_oct(N.rg * 2 - 1); | 				N.g = float(imageLoad(voxels, src + ivec3(0, 0, voxelgiResolution.x * 8))) / 255; | ||||||
| 			vec3 envl = vec3(0.0); | 				N /= count; | ||||||
| 			envl.r = float(imageLoad(voxels, src + ivec3(0, 0, voxelgiResolution.x * 9))) / 255; | 				N = decode_oct(N.rg * 2.0 - 1.0); | ||||||
| 			envl.g = float(imageLoad(voxels, src + ivec3(0, 0, voxelgiResolution.x * 10))) / 255; |  | ||||||
| 			envl.b = float(imageLoad(voxels, src + ivec3(0, 0, voxelgiResolution.x * 11))) / 255; |  | ||||||
| 			envl /= 3; |  | ||||||
|  |  | ||||||
|  | 				if (abs(N.x) > 0) | ||||||
|  | 					avgNormal.x += N.x; | ||||||
|  | 				if (abs(N.y) > 0) | ||||||
|  | 					avgNormal.y += N.y; | ||||||
|  | 				if (abs(N.z) > 0) | ||||||
|  | 					avgNormal.z += N.z; | ||||||
|  | 				if (i == 5) | ||||||
|  | 				{ | ||||||
|  | 					avgNormal = normalize(avgNormal); | ||||||
|  | 					TBN = makeTangentBasis(avgNormal); | ||||||
|  | 				} | ||||||
|  |  | ||||||
| 			//clipmap to world | 				vec3 envl = vec3(0.0); | ||||||
| 			vec3 wposition = (gl_GlobalInvocationID.xyz + 0.5) / voxelgiResolution.x; | 				envl.r = float(imageLoad(voxels, src + ivec3(0, 0, voxelgiResolution.x * 9))) / 255; | ||||||
| 			wposition = wposition * 2.0 - 1.0; | 				envl.g = float(imageLoad(voxels, src + ivec3(0, 0, voxelgiResolution.x * 10))) / 255; | ||||||
| 			wposition *= float(clipmaps[int(clipmapLevel * 10)]); | 				envl.b = float(imageLoad(voxels, src + ivec3(0, 0, voxelgiResolution.x * 11))) / 255; | ||||||
| 			wposition *= voxelgiResolution.x; | 				envl /= count; | ||||||
| 			wposition += vec3(clipmaps[clipmapLevel * 10 + 4], clipmaps[clipmapLevel * 10 + 5], clipmaps[clipmapLevel * 10 + 6]); | 				vec3 light = vec3(0.0); | ||||||
|  | 				light.r = float(imageLoad(voxels, src + ivec3(0, 0, voxelgiResolution.x * 12))) / 255; | ||||||
|  | 				light.g = float(imageLoad(voxels, src + ivec3(0, 0, voxelgiResolution.x * 13))) / 255; | ||||||
|  | 				light.b = float(imageLoad(voxels, src + ivec3(0, 0, voxelgiResolution.x * 14))) / 255; | ||||||
|  | 				light /= count; | ||||||
|  |  | ||||||
| 			radiance = basecol; | 				//clipmap to world | ||||||
| 			vec4 trace = traceDiffuse(wposition, wnormal, voxelsSampler, clipmaps); | 				vec3 wposition = (gl_GlobalInvocationID.xyz + 0.5) / voxelgiResolution.x; | ||||||
| 			vec3 indirect = trace.rgb + envl.rgb * (1.0 - trace.a); | 				wposition = wposition * 2.0 - 1.0; | ||||||
| 			radiance.rgb *= light / PI + indirect; | 				wposition *= float(clipmaps[int(clipmapLevel * 10)]); | ||||||
| 			radiance.rgb += emission.rgb; | 				wposition *= voxelgiResolution.x; | ||||||
|  | 				wposition += vec3(clipmaps[clipmapLevel * 10 + 4], clipmaps[clipmapLevel * 10 + 5], clipmaps[clipmapLevel * 10 + 6]); | ||||||
|  |  | ||||||
|  | 				radiance = basecol; | ||||||
|  | 				vec4 trace = traceDiffuse(wposition, N, voxelsSampler, clipmaps); | ||||||
|  | 				vec3 indirect = trace.rgb + envl.rgb * (1.0 - trace.a); | ||||||
|  | 				radiance.rgb *= light + indirect; | ||||||
|  | 				radiance.rgb += emission.rgb; | ||||||
|  | 			} | ||||||
| 			#else | 			#else | ||||||
| 			opac = float(imageLoad(voxels, src)) / 255; | 			int count = int(imageLoad(voxels, src + ivec3(0, 0, voxelgiResolution.x))); | ||||||
|  | 			if (count > 0) { | ||||||
|  | 				opac = float(imageLoad(voxels, src)) / 255; | ||||||
|  | 				opac /= count; | ||||||
|  | 			} | ||||||
| 			#endif | 			#endif | ||||||
|  |  | ||||||
| 			#ifdef _VoxelGI | 			#ifdef _VoxelGI | ||||||
| @ -194,7 +213,7 @@ void main() { | |||||||
| 		} | 		} | ||||||
| 		else { | 		else { | ||||||
| 			// precompute cone sampling: | 			// precompute cone sampling: | ||||||
| 			vec3 coneDirection = DIFFUSE_CONE_DIRECTIONS[i - 6]; | 			vec3 coneDirection = TBN * DIFFUSE_CONE_DIRECTIONS[i - 6]; | ||||||
| 			vec3 aniso_direction = -coneDirection; | 			vec3 aniso_direction = -coneDirection; | ||||||
| 			uvec3 face_offsets = uvec3( | 			uvec3 face_offsets = uvec3( | ||||||
| 				aniso_direction.x > 0 ? 0 : 1, | 				aniso_direction.x > 0 ? 0 : 1, | ||||||
| @ -235,4 +254,4 @@ void main() { | |||||||
| 	imageStore(SDF, dst_sdf, vec4(sdf)); | 	imageStore(SDF, dst_sdf, vec4(sdf)); | ||||||
| 	#endif | 	#endif | ||||||
| 	#endif | 	#endif | ||||||
| } | } | ||||||
| @ -12,6 +12,7 @@ class App { | |||||||
| 	static var traitInits: Array<Void->Void> = []; | 	static var traitInits: Array<Void->Void> = []; | ||||||
| 	static var traitUpdates: Array<Void->Void> = []; | 	static var traitUpdates: Array<Void->Void> = []; | ||||||
| 	static var traitLateUpdates: 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 traitRenders: Array<kha.graphics4.Graphics->Void> = []; | ||||||
| 	static var traitRenders2D: Array<kha.graphics2.Graphics->Void> = []; | 	static var traitRenders2D: Array<kha.graphics2.Graphics->Void> = []; | ||||||
| 	public static var framebuffer: kha.Framebuffer; | 	public static var framebuffer: kha.Framebuffer; | ||||||
| @ -23,6 +24,8 @@ class App { | |||||||
| 	public static var renderPathTime: Float; | 	public static var renderPathTime: Float; | ||||||
| 	public static var endFrameCallbacks: Array<Void->Void> = []; | 	public static var endFrameCallbacks: Array<Void->Void> = []; | ||||||
| 	#end | 	#end | ||||||
|  | 	static var last = 0.0; | ||||||
|  | 	static var time = 0.0; | ||||||
| 	static var lastw = -1; | 	static var lastw = -1; | ||||||
| 	static var lasth = -1; | 	static var lasth = -1; | ||||||
| 	public static var onResize: Void->Void = null; | 	public static var onResize: Void->Void = null; | ||||||
| @ -34,13 +37,14 @@ class App { | |||||||
| 	function new(done: Void->Void) { | 	function new(done: Void->Void) { | ||||||
| 		done(); | 		done(); | ||||||
| 		kha.System.notifyOnFrames(render); | 		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() { | 	public static function reset() { | ||||||
| 		traitInits = []; | 		traitInits = []; | ||||||
| 		traitUpdates = []; | 		traitUpdates = []; | ||||||
| 		traitLateUpdates = []; | 		traitLateUpdates = []; | ||||||
|  | 		traitFixedUpdates = []; | ||||||
| 		traitRenders = []; | 		traitRenders = []; | ||||||
| 		traitRenders2D = []; | 		traitRenders2D = []; | ||||||
| 		if (onResets != null) for (f in onResets) f(); | 		if (onResets != null) for (f in onResets) f(); | ||||||
| @ -48,6 +52,24 @@ class App { | |||||||
|  |  | ||||||
| 	static function update() { | 	static function update() { | ||||||
| 		if (Scene.active == null || !Scene.active.ready) return; | 		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 (pauseUpdates) return; | ||||||
|  |  | ||||||
| 		#if lnx_debug | 		#if lnx_debug | ||||||
| @ -56,6 +78,14 @@ class App { | |||||||
|  |  | ||||||
| 		Scene.active.updateFrame(); | 		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 i = 0; | ||||||
| 		var l = traitUpdates.length; | 		var l = traitUpdates.length; | ||||||
| 		while (i < l) { | 		while (i < l) { | ||||||
| @ -84,29 +114,13 @@ class App { | |||||||
| 		for (cb in endFrameCallbacks) cb(); | 		for (cb in endFrameCallbacks) cb(); | ||||||
| 		updateTime = kha.Scheduler.realTime() - startTime; | 		updateTime = kha.Scheduler.realTime() - startTime; | ||||||
| 		#end | 		#end | ||||||
|  |  | ||||||
| 		// Rebuild projection on window resize |  | ||||||
| 		if (lastw == -1) { |  | ||||||
| 			lastw = App.w(); |  | ||||||
| 			lasth = App.h(); |  | ||||||
| 		} |  | ||||||
| 		if (lastw != App.w() || lasth != App.h()) { |  | ||||||
| 			if (onResize != null) onResize(); |  | ||||||
| 			else { |  | ||||||
| 				if (Scene.active != null && Scene.active.camera != null) { |  | ||||||
| 					Scene.active.camera.buildProjection(); |  | ||||||
| 				} |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 		lastw = App.w(); |  | ||||||
| 		lasth = App.h(); |  | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	static function render(frames: Array<kha.Framebuffer>) { | 	static function render(frames: Array<kha.Framebuffer>) { | ||||||
| 		var frame = frames[0]; | 		var frame = frames[0]; | ||||||
| 		framebuffer = frame; | 		framebuffer = frame; | ||||||
|  |  | ||||||
| 		iron.system.Time.update(); | 		iron.system.Time.render(); | ||||||
|  |  | ||||||
| 		if (Scene.active == null || !Scene.active.ready) { | 		if (Scene.active == null || !Scene.active.ready) { | ||||||
| 			render2D(frame); | 			render2D(frame); | ||||||
| @ -172,6 +186,14 @@ class App { | |||||||
| 		traitLateUpdates.remove(f); | 		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) { | 	public static function notifyOnRender(f: kha.graphics4.Graphics->Void) { | ||||||
| 		traitRenders.push(f); | 		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 { | 		meshes.sort(function(a, b): Int { | ||||||
| 			#if rp_depth_texture | 			#if rp_depth_texture | ||||||
| 			var depthDiff = boolToInt(a.depthRead) - boolToInt(b.depthRead); | 			var depthDiff = boolToInt(a.depthRead) - boolToInt(b.depthRead); | ||||||
| 			if (depthDiff != 0) return depthDiff; | 			if (depthDiff != 0) return depthDiff; | ||||||
| 			#end | 			#end | ||||||
|  |  | ||||||
| 			return a.materials[0].name >= b.materials[0].name ? 1 : -1; | 			if (a.data.sortingIndex != b.data.sortingIndex) { | ||||||
| 		}); | 				return a.data.sortingIndex > b.data.sortingIndex ? 1 : -1; | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			return a.data.name >= b.data.name ? 1 : -1;		}); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	public function drawMeshes(context: String) { | 	public function drawMeshes(context: String) { | ||||||
| @ -399,7 +402,7 @@ class RenderPath { | |||||||
| 			#if lnx_batch | 			#if lnx_batch | ||||||
| 			sortMeshesDistance(Scene.active.meshBatch.nonBatched); | 			sortMeshesDistance(Scene.active.meshBatch.nonBatched); | ||||||
| 			#else | 			#else | ||||||
| 			drawOrder == DrawOrder.Shader ? sortMeshesShader(meshes) : sortMeshesDistance(meshes); | 			drawOrder == DrawOrder.Index ? sortMeshesIndex(meshes) : sortMeshesDistance(meshes); | ||||||
| 			#end | 			#end | ||||||
| 			meshesSorted = true; | 			meshesSorted = true; | ||||||
| 		} | 		} | ||||||
| @ -518,12 +521,44 @@ class RenderPath { | |||||||
| 		return Reflect.field(kha.Shaders, handle + "_comp"); | 		return Reflect.field(kha.Shaders, handle + "_comp"); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	#if (kha_krom && lnx_vr) | 	#if lnx_vr | ||||||
| 	public function drawStereo(drawMeshes: Int->Void) { | 	public function drawStereo(drawMeshes: Void->Void) { | ||||||
| 		for (eye in 0...2) { | 		var vr = kha.vr.VrInterface.instance; | ||||||
| 			Krom.vrBeginRender(eye); | 		var appw = iron.App.w(); | ||||||
| 			drawMeshes(eye); | 		var apph = iron.App.h(); | ||||||
| 			Krom.vrEndRender(eye); | 		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 | 	#end | ||||||
| @ -882,6 +917,6 @@ class CachedShaderContext { | |||||||
|  |  | ||||||
| @:enum abstract DrawOrder(Int) from Int { | @:enum abstract DrawOrder(Int) from Int { | ||||||
| 	var Distance = 0; // Early-z | 	var Distance = 0; // Early-z | ||||||
| 	var Shader = 1; // Less state changes | 	var Index = 1; // Less state changes | ||||||
| 	// var Mix = 2; // Distance buckets sorted by shader | 	// var Mix = 2; // Distance buckets sorted by shader | ||||||
| } | } | ||||||
|  | |||||||
| @ -775,6 +775,7 @@ class Scene { | |||||||
| 			// Attach particle systems | 			// Attach particle systems | ||||||
| 			#if lnx_particles | 			#if lnx_particles | ||||||
| 			if (o.particle_refs != null) { | 			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); | 				for (ref in o.particle_refs) cast(object, MeshObject).setupParticleSystem(sceneName, ref); | ||||||
| 			} | 			} | ||||||
| 			#end | 			#end | ||||||
| @ -782,6 +783,11 @@ class Scene { | |||||||
| 			if (o.tilesheet_ref != null) { | 			if (o.tilesheet_ref != null) { | ||||||
| 				cast(object, MeshObject).setupTilesheet(sceneName, o.tilesheet_ref, o.tilesheet_action_ref); | 				cast(object, MeshObject).setupTilesheet(sceneName, o.tilesheet_ref, o.tilesheet_action_ref); | ||||||
| 			} | 			} | ||||||
|  | 			 | ||||||
|  | 			if (o.camera_list != null){ | ||||||
|  | 				cast(object, MeshObject).cameraList = o.camera_list; | ||||||
|  | 			} | ||||||
|  |  | ||||||
| 			returnObject(object, o, done); | 			returnObject(object, o, done); | ||||||
| 		}); | 		}); | ||||||
| 	} | 	} | ||||||
| @ -881,8 +887,12 @@ class Scene { | |||||||
| 						var ptype: String = t.props[i * 3 + 1]; | 						var ptype: String = t.props[i * 3 + 1]; | ||||||
| 						var pval: Dynamic = t.props[i * 3 + 2]; | 						var pval: Dynamic = t.props[i * 3 + 2]; | ||||||
|  |  | ||||||
| 						if (StringTools.endsWith(ptype, "Object") && pval != "") { | 						if (StringTools.endsWith(ptype, "Object") && pval != "" && pval != null) { | ||||||
| 							Reflect.setProperty(traitInst, pname, Scene.active.getChild(pval)); | 							Reflect.setProperty(traitInst, pname, Scene.active.getChild(pval)); | ||||||
|  | 						} else if (ptype == "TSceneFormat" && pval != "") { | ||||||
|  | 							Data.getSceneRaw(pval, function (r: TSceneFormat) { | ||||||
|  | 								Reflect.setProperty(traitInst, pname, r); | ||||||
|  | 							}); | ||||||
| 						} | 						} | ||||||
| 						else { | 						else { | ||||||
| 							switch (ptype) { | 							switch (ptype) { | ||||||
|  | |||||||
| @ -16,6 +16,7 @@ class Trait { | |||||||
| 	var _remove: Array<Void->Void> = null; | 	var _remove: Array<Void->Void> = null; | ||||||
| 	var _update: Array<Void->Void> = null; | 	var _update: Array<Void->Void> = null; | ||||||
| 	var _lateUpdate: 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 _render: Array<kha.graphics4.Graphics->Void> = null; | ||||||
| 	var _render2D: Array<kha.graphics2.Graphics->Void> = null; | 	var _render2D: Array<kha.graphics2.Graphics->Void> = null; | ||||||
|  |  | ||||||
| @ -87,6 +88,23 @@ class Trait { | |||||||
| 		App.removeLateUpdate(f); | 		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. |       Add render handler. | ||||||
|     **/ |     **/ | ||||||
|  | |||||||
| @ -9,6 +9,7 @@ import iron.data.SceneFormat; | |||||||
| class MeshData { | class MeshData { | ||||||
|  |  | ||||||
| 	public var name: String; | 	public var name: String; | ||||||
|  | 	public var sortingIndex: Int; | ||||||
| 	public var raw: TMeshData; | 	public var raw: TMeshData; | ||||||
| 	public var format: TSceneFormat; | 	public var format: TSceneFormat; | ||||||
| 	public var geom: Geometry; | 	public var geom: Geometry; | ||||||
| @ -23,7 +24,8 @@ class MeshData { | |||||||
| 	public function new(raw: TMeshData, done: MeshData->Void) { | 	public function new(raw: TMeshData, done: MeshData->Void) { | ||||||
| 		this.raw = raw; | 		this.raw = raw; | ||||||
| 		this.name = raw.name; | 		this.name = raw.name; | ||||||
|  | 		this.sortingIndex = raw.sorting_index; | ||||||
|  | 		 | ||||||
| 		if (raw.scale_pos != null) scalePos = raw.scale_pos; | 		if (raw.scale_pos != null) scalePos = raw.scale_pos; | ||||||
| 		if (raw.scale_tex != null) scaleTex = raw.scale_tex; | 		if (raw.scale_tex != null) scaleTex = raw.scale_tex; | ||||||
|  |  | ||||||
|  | |||||||
| @ -49,6 +49,7 @@ typedef TMeshData = { | |||||||
| @:structInit class TMeshData { | @:structInit class TMeshData { | ||||||
| #end | #end | ||||||
| 	public var name: String; | 	public var name: String; | ||||||
|  | 	public var sorting_index: Int; | ||||||
| 	public var vertex_arrays: Array<TVertexArray>; | 	public var vertex_arrays: Array<TVertexArray>; | ||||||
| 	public var index_arrays: Array<TIndexArray>; | 	public var index_arrays: Array<TIndexArray>; | ||||||
| 	@:optional public var dynamic_usage: Null<Bool>; | 	@:optional public var dynamic_usage: Null<Bool>; | ||||||
| @ -222,6 +223,7 @@ typedef TShaderData = { | |||||||
| @:structInit class TShaderData { | @:structInit class TShaderData { | ||||||
| #end | #end | ||||||
| 	public var name: String; | 	public var name: String; | ||||||
|  | 	public var next_pass: String; | ||||||
| 	public var contexts: Array<TShaderContext>; | 	public var contexts: Array<TShaderContext>; | ||||||
| } | } | ||||||
|  |  | ||||||
| @ -392,6 +394,9 @@ typedef TParticleData = { | |||||||
| #end | #end | ||||||
| 	public var name: String; | 	public var name: String; | ||||||
| 	public var type: Int; // 0 - Emitter, Hair | 	public var type: Int; // 0 - Emitter, Hair | ||||||
|  | 	public var auto_start: Bool; | ||||||
|  | 	public var dynamic_emitter: Bool; | ||||||
|  | 	public var is_unique: Bool; | ||||||
| 	public var loop: Bool; | 	public var loop: Bool; | ||||||
| 	public var count: Int; | 	public var count: Int; | ||||||
| 	public var frame_start: FastFloat; | 	public var frame_start: FastFloat; | ||||||
| @ -439,6 +444,7 @@ typedef TObj = { | |||||||
| 	@:optional public var traits: Array<TTrait>; | 	@:optional public var traits: Array<TTrait>; | ||||||
| 	@:optional public var properties: Array<TProperty>; | 	@:optional public var properties: Array<TProperty>; | ||||||
| 	@:optional public var vertex_groups: Array<TVertex_groups>; | 	@:optional public var vertex_groups: Array<TVertex_groups>; | ||||||
|  | 	@:optional public var camera_list: Array<String>; | ||||||
| 	@:optional public var constraints: Array<TConstraint>; | 	@:optional public var constraints: Array<TConstraint>; | ||||||
| 	@:optional public var dimensions: Float32Array; // Geometry objects | 	@:optional public var dimensions: Float32Array; // Geometry objects | ||||||
| 	@:optional public var object_actions: Array<String>; | 	@:optional public var object_actions: Array<String>; | ||||||
|  | |||||||
| @ -22,6 +22,7 @@ using StringTools; | |||||||
| class ShaderData { | class ShaderData { | ||||||
|  |  | ||||||
| 	public var name: String; | 	public var name: String; | ||||||
|  | 	public var nextPass: String; | ||||||
| 	public var raw: TShaderData; | 	public var raw: TShaderData; | ||||||
| 	public var contexts: Array<ShaderContext> = []; | 	public var contexts: Array<ShaderContext> = []; | ||||||
|  |  | ||||||
| @ -33,6 +34,7 @@ class ShaderData { | |||||||
| 	public function new(raw: TShaderData, done: ShaderData->Void, overrideContext: TShaderOverride = null) { | 	public function new(raw: TShaderData, done: ShaderData->Void, overrideContext: TShaderOverride = null) { | ||||||
| 		this.raw = raw; | 		this.raw = raw; | ||||||
| 		this.name = raw.name; | 		this.name = raw.name; | ||||||
|  | 		this.nextPass = raw.next_pass; | ||||||
|  |  | ||||||
| 		for (c in raw.contexts) contexts.push(null); | 		for (c in raw.contexts) contexts.push(null); | ||||||
| 		var contextsLoaded = 0; | 		var contextsLoaded = 0; | ||||||
|  | |||||||
							
								
								
									
										50
									
								
								leenkx/Sources/iron/format/bmp/Data.hx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								leenkx/Sources/iron/format/bmp/Data.hx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,50 @@ | |||||||
|  | /* | ||||||
|  |  * format - Haxe File Formats | ||||||
|  |  * | ||||||
|  |  *  BMP File Format | ||||||
|  |  *  Copyright (C) 2007-2009 Trevor McCauley, Baluta Cristian (hx port) & Robert Sköld (format conversion) | ||||||
|  |  * | ||||||
|  |  * Copyright (c) 2009, The Haxe Project Contributors | ||||||
|  |  * All rights reserved. | ||||||
|  |  * Redistribution and use in source and binary forms, with or without | ||||||
|  |  * modification, are permitted provided that the following conditions are met: | ||||||
|  |  * | ||||||
|  |  *   - Redistributions of source code must retain the above copyright | ||||||
|  |  *     notice, this list of conditions and the following disclaimer. | ||||||
|  |  *   - Redistributions in binary form must reproduce the above copyright | ||||||
|  |  *     notice, this list of conditions and the following disclaimer in the | ||||||
|  |  *     documentation and/or other materials provided with the distribution. | ||||||
|  |  * | ||||||
|  |  * THIS SOFTWARE IS PROVIDED BY THE HAXE PROJECT CONTRIBUTORS "AS IS" AND ANY | ||||||
|  |  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||||||
|  |  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||||||
|  |  * DISCLAIMED. IN NO EVENT SHALL THE HAXE PROJECT CONTRIBUTORS BE LIABLE FOR | ||||||
|  |  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | ||||||
|  |  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | ||||||
|  |  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | ||||||
|  |  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | ||||||
|  |  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | ||||||
|  |  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH | ||||||
|  |  * DAMAGE. | ||||||
|  |  */ | ||||||
|  | package iron.format.bmp; | ||||||
|  |  | ||||||
|  | typedef Data = { | ||||||
|  |     var header : iron.format.bmp.Header; | ||||||
|  |     var pixels : haxe.io.Bytes; | ||||||
|  | #if (haxe_ver < 4) | ||||||
|  |     var colorTable : Null<haxe.io.Bytes>; | ||||||
|  | #else | ||||||
|  |     var ?colorTable : haxe.io.Bytes; | ||||||
|  | #end | ||||||
|  | } | ||||||
|  |  | ||||||
|  | typedef Header = { | ||||||
|  |     var width : Int;          // real width (in pixels) | ||||||
|  |     var height : Int;         // real height (in pixels) | ||||||
|  |     var paddedStride : Int;   // number of bytes in a stride (including padding) | ||||||
|  |     var topToBottom : Bool;   // whether the bitmap is stored top to bottom | ||||||
|  |     var bpp : Int;            // bits per pixel | ||||||
|  |     var dataLength : Int;     // equal to `paddedStride` * `height` | ||||||
|  |     var compression : Int;    // which compression is being used, 0 for no compression | ||||||
|  | } | ||||||
							
								
								
									
										122
									
								
								leenkx/Sources/iron/format/bmp/Reader.hx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										122
									
								
								leenkx/Sources/iron/format/bmp/Reader.hx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,122 @@ | |||||||
|  | /* | ||||||
|  |  * format - Haxe File Formats | ||||||
|  |  * | ||||||
|  |  *  BMP File Format | ||||||
|  |  *  Copyright (C) 2007-2009 Robert Sköld | ||||||
|  |  * | ||||||
|  |  * Copyright (c) 2009, The Haxe Project Contributors | ||||||
|  |  * All rights reserved. | ||||||
|  |  * Redistribution and use in source and binary forms, with or without | ||||||
|  |  * modification, are permitted provided that the following conditions are met: | ||||||
|  |  * | ||||||
|  |  *   - Redistributions of source code must retain the above copyright | ||||||
|  |  *     notice, this list of conditions and the following disclaimer. | ||||||
|  |  *   - Redistributions in binary form must reproduce the above copyright | ||||||
|  |  *     notice, this list of conditions and the following disclaimer in the | ||||||
|  |  *     documentation and/or other materials provided with the distribution. | ||||||
|  |  * | ||||||
|  |  * THIS SOFTWARE IS PROVIDED BY THE HAXE PROJECT CONTRIBUTORS "AS IS" AND ANY | ||||||
|  |  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||||||
|  |  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||||||
|  |  * DISCLAIMED. IN NO EVENT SHALL THE HAXE PROJECT CONTRIBUTORS BE LIABLE FOR | ||||||
|  |  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | ||||||
|  |  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | ||||||
|  |  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | ||||||
|  |  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | ||||||
|  |  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | ||||||
|  |  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH | ||||||
|  |  * DAMAGE. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | package iron.format.bmp; | ||||||
|  |  | ||||||
|  | import iron.format.bmp.Data; | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class Reader { | ||||||
|  |  | ||||||
|  | 	var input : haxe.io.Input; | ||||||
|  |  | ||||||
|  | 	public function new( i ) { | ||||||
|  | 		input = i; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	/**  | ||||||
|  | 	 * Only supports uncompressed 24bpp bitmaps (the most common format). | ||||||
|  | 	 *  | ||||||
|  | 	 * The returned bytes in `Data.pixels` will be in BGR order, and with padding (if present). | ||||||
|  | 	 *  | ||||||
|  | 	 * @see https://msdn.microsoft.com/en-us/library/windows/desktop/dd318229(v=vs.85).aspx | ||||||
|  | 	 * @see https://en.wikipedia.org/wiki/BMP_file_format#Bitmap_file_header | ||||||
|  | 	 */ | ||||||
|  | 	public function read() : format.bmp.Data { | ||||||
|  | 		// Read Header | ||||||
|  | 		for (b in ["B".code, "M".code]) { | ||||||
|  | 			if (input.readByte() != b) throw "Invalid header"; | ||||||
|  | 		} | ||||||
|  | 	 | ||||||
|  | 		var fileSize = input.readInt32(); | ||||||
|  | 		input.readInt32();							// Reserved | ||||||
|  | 		var offset = input.readInt32(); | ||||||
|  |  | ||||||
|  | 		// Read InfoHeader | ||||||
|  | 		var infoHeaderSize = input.readInt32();		// InfoHeader size | ||||||
|  | 		if (infoHeaderSize != 40) { | ||||||
|  | 			throw 'Info headers with size $infoHeaderSize not supported.'; | ||||||
|  | 		} | ||||||
|  | 		var width = input.readInt32();				// Image width (actual, not padded) | ||||||
|  | 		var height = input.readInt32();				// Image height | ||||||
|  | 		var numPlanes = input.readInt16();			// Number of planes | ||||||
|  | 		var bits = input.readInt16();				// Bits per pixel | ||||||
|  | 		var compression = input.readInt32();		// Compression type | ||||||
|  | 		var dataLength = input.readInt32();			// Image data size (includes padding!) | ||||||
|  | 		input.readInt32();							// Horizontal resolution | ||||||
|  | 		input.readInt32();							// Vertical resolution | ||||||
|  | 		var colorsUsed = input.readInt32();			// Colors used (0 when uncompressed) | ||||||
|  | 		input.readInt32();							// Important colors (0 when uncompressed) | ||||||
|  |  | ||||||
|  | 		// If there's no compression, the dataLength may be 0 | ||||||
|  | 		if ( compression == 0 && dataLength == 0 ) dataLength = fileSize - offset; | ||||||
|  |  | ||||||
|  | 		var bytesRead = 54; // total read above | ||||||
|  | 		 | ||||||
|  | 		var colorTable : haxe.io.Bytes = null; | ||||||
|  | 		if ( bits <= 8 ) { | ||||||
|  | 			if ( colorsUsed == 0 ) { | ||||||
|  | 				colorsUsed = Tools.getNumColorsForBitDepth(bits); | ||||||
|  | 			} | ||||||
|  | 			var colorTableLength = 4 * colorsUsed; | ||||||
|  | 			colorTable = haxe.io.Bytes.alloc( colorTableLength ); | ||||||
|  | 			input.readFullBytes( colorTable, 0, colorTableLength ); | ||||||
|  | 			bytesRead += colorTableLength; | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		input.read( offset - bytesRead ); | ||||||
|  | 		 | ||||||
|  | 		var p = haxe.io.Bytes.alloc( dataLength );	 | ||||||
|  | 		 | ||||||
|  | 		// Read Raster Data | ||||||
|  | 		var paddedStride = Tools.computePaddedStride(width, bits); | ||||||
|  | 		var topToBottom = false; | ||||||
|  | 		if ( height < 0 ) { // if bitmap is stored top to bottom | ||||||
|  | 			topToBottom = true; | ||||||
|  | 			height = -height; | ||||||
|  | 		} | ||||||
|  |      | ||||||
|  | 		input.readFullBytes(p, 0, dataLength); | ||||||
|  | 			 | ||||||
|  | 		return { | ||||||
|  | 			header: { | ||||||
|  | 				width: width, | ||||||
|  | 				height: height, | ||||||
|  | 				paddedStride: paddedStride, | ||||||
|  | 				topToBottom: topToBottom, | ||||||
|  | 				bpp: bits, | ||||||
|  | 				dataLength: dataLength, | ||||||
|  | 				compression: compression | ||||||
|  | 			}, | ||||||
|  | 			pixels: p, | ||||||
|  | 			colorTable: colorTable | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										256
									
								
								leenkx/Sources/iron/format/bmp/Tools.hx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										256
									
								
								leenkx/Sources/iron/format/bmp/Tools.hx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,256 @@ | |||||||
|  | /* | ||||||
|  |  * format - Haxe File Formats | ||||||
|  |  * | ||||||
|  |  *  BMP File Format | ||||||
|  |  *  Copyright (C) 2007-2009 Trevor McCauley, Baluta Cristian (hx port) & Robert Sköld (format conversion) | ||||||
|  |  * | ||||||
|  |  * Copyright (c) 2009, The Haxe Project Contributors | ||||||
|  |  * All rights reserved. | ||||||
|  |  * Redistribution and use in source and binary forms, with or without | ||||||
|  |  * modification, are permitted provided that the following conditions are met: | ||||||
|  |  * | ||||||
|  |  *   - Redistributions of source code must retain the above copyright | ||||||
|  |  *     notice, this list of conditions and the following disclaimer. | ||||||
|  |  *   - Redistributions in binary form must reproduce the above copyright | ||||||
|  |  *     notice, this list of conditions and the following disclaimer in the | ||||||
|  |  *     documentation and/or other materials provided with the distribution. | ||||||
|  |  * | ||||||
|  |  * THIS SOFTWARE IS PROVIDED BY THE HAXE PROJECT CONTRIBUTORS "AS IS" AND ANY | ||||||
|  |  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||||||
|  |  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||||||
|  |  * DISCLAIMED. IN NO EVENT SHALL THE HAXE PROJECT CONTRIBUTORS BE LIABLE FOR | ||||||
|  |  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | ||||||
|  |  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | ||||||
|  |  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | ||||||
|  |  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | ||||||
|  |  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | ||||||
|  |  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH | ||||||
|  |  * DAMAGE. | ||||||
|  |  */ | ||||||
|  | package iron.format.bmp; | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class Tools { | ||||||
|  |  | ||||||
|  | 	//												  a  r  g  b | ||||||
|  | 	static var ARGB_MAP(default, never):Array<Int> = [0, 1, 2, 3]; | ||||||
|  | 	static var BGRA_MAP(default, never):Array<Int> = [3, 2, 1, 0]; | ||||||
|  |  | ||||||
|  | 	static var COLOR_SIZE(default, never):Int = 4; | ||||||
|  | 	 | ||||||
|  | 	/** | ||||||
|  | 		Extract BMP pixel data (24bpp in BGR format) and expands it to BGRA, removing any padding in the process. | ||||||
|  | 	**/ | ||||||
|  | 	inline static public function extractBGRA( bmp : iron.format.bmp.Data ) : haxe.io.Bytes { | ||||||
|  | 		return _extract32(bmp, BGRA_MAP, 0xFF); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	/** | ||||||
|  | 		Extract BMP pixel data (24bpp in BGR format) and converts it to ARGB. | ||||||
|  | 	**/ | ||||||
|  | 	inline static public function extractARGB( bmp : iron.format.bmp.Data ) : haxe.io.Bytes { | ||||||
|  | 		return _extract32(bmp, ARGB_MAP, 0xFF); | ||||||
|  | 	} | ||||||
|  |    | ||||||
|  | 	/** | ||||||
|  | 		Creates BMP data from bytes in BGRA format for each pixel. | ||||||
|  | 	**/ | ||||||
|  | 	inline static public function buildFromBGRA( width : Int, height : Int, srcBytes : haxe.io.Bytes, topToBottom : Bool = false ) : Data { | ||||||
|  | 		return _buildFrom32(width, height, srcBytes, BGRA_MAP, topToBottom); | ||||||
|  | 	} | ||||||
|  |    | ||||||
|  | 	/** | ||||||
|  | 		Creates BMP data from bytes in ARGB format for each pixel. | ||||||
|  | 	**/ | ||||||
|  | 	inline static public function buildFromARGB( width : Int, height : Int, srcBytes : haxe.io.Bytes, topToBottom : Bool = false ) : Data { | ||||||
|  | 		return _buildFrom32(width, height, srcBytes, ARGB_MAP, topToBottom); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	inline static public function computePaddedStride(width:Int, bpp:Int):Int { | ||||||
|  | 		return ((((width * bpp) + 31) & ~31) >> 3); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	/** | ||||||
|  | 	 * Gets number of colors for indexed palettes | ||||||
|  | 	 */ | ||||||
|  | 	inline static public function getNumColorsForBitDepth(bpp:Int):Int { | ||||||
|  | 		return switch (bpp) { | ||||||
|  | 			case 1: 2; | ||||||
|  | 			case 4: 16; | ||||||
|  | 			case 8: 256; | ||||||
|  | 			case 16: 65536; | ||||||
|  | 			default: throw 'Unsupported bpp $bpp'; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	 | ||||||
|  | 	// `channelMap` contains indices to map into ARGB (f.e. the mapping for ARGB is [0,1,2,3], while for BGRA is [3,2,1,0]) | ||||||
|  | 	static function _extract32( bmp : iron.format.bmp.Data, channelMap : Array<Int>, alpha : Int = 0xFF) : haxe.io.Bytes { | ||||||
|  | 		var srcBytes = bmp.pixels; | ||||||
|  | 		var dstLen = bmp.header.width * bmp.header.height * 4; | ||||||
|  | 		var dstBytes = haxe.io.Bytes.alloc( dstLen ); | ||||||
|  | 		var srcPaddedStride = bmp.header.paddedStride; | ||||||
|  | 		 | ||||||
|  | 		var yDir = -1; | ||||||
|  | 		var dstPos = 0; | ||||||
|  | 		var srcPos = srcPaddedStride * (bmp.header.height - 1); | ||||||
|  |      | ||||||
|  | 		if ( bmp.header.topToBottom ) { | ||||||
|  | 			yDir = 1; | ||||||
|  | 			srcPos = 0; | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		if ( bmp.header.bpp < 8 || bmp.header.bpp == 16 ) { | ||||||
|  | 			throw 'bpp ${bmp.header.bpp} not supported'; | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		var colorTable:haxe.io.Bytes = null; | ||||||
|  | 		if ( bmp.header.bpp <= 8 ) { | ||||||
|  | 			var colorTableLength = getNumColorsForBitDepth(bmp.header.bpp); | ||||||
|  | 			colorTable = haxe.io.Bytes.alloc(colorTableLength * COLOR_SIZE); | ||||||
|  | 			var definedColorTableLength = Std.int( bmp.colorTable.length / COLOR_SIZE ); | ||||||
|  | 			for( i in 0...definedColorTableLength ) { | ||||||
|  | 				var b = bmp.colorTable.get( i * COLOR_SIZE); | ||||||
|  | 				var g = bmp.colorTable.get( i * COLOR_SIZE + 1); | ||||||
|  | 				var r = bmp.colorTable.get( i * COLOR_SIZE + 2); | ||||||
|  |  | ||||||
|  | 				colorTable.set(i * COLOR_SIZE + channelMap[0], alpha); | ||||||
|  | 				colorTable.set(i * COLOR_SIZE + channelMap[1], r); | ||||||
|  | 				colorTable.set(i * COLOR_SIZE + channelMap[2], g); | ||||||
|  | 				colorTable.set(i * COLOR_SIZE + channelMap[3], b); | ||||||
|  | 			} | ||||||
|  | 			// We want to have the table the full length in case indices outside the range are present | ||||||
|  | 			colorTable.fill(definedColorTableLength, colorTableLength - definedColorTableLength, 0); | ||||||
|  | 			for( i in definedColorTableLength...colorTableLength ) { | ||||||
|  | 				colorTable.set(i * COLOR_SIZE + channelMap[0], alpha); | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		switch bmp.header.compression { | ||||||
|  | 			case 0: | ||||||
|  | 				while( dstPos < dstLen ) { | ||||||
|  | 					for( i in 0...bmp.header.width ) { | ||||||
|  | 						if (bmp.header.bpp == 8) { | ||||||
|  |  | ||||||
|  | 							var currentSrcPos = srcPos + i; | ||||||
|  | 							var index = srcBytes.get(currentSrcPos); | ||||||
|  | 							dstBytes.blit( dstPos, colorTable, index * COLOR_SIZE, COLOR_SIZE ); | ||||||
|  |  | ||||||
|  | 						} else if (bmp.header.bpp == 24) { | ||||||
|  |  | ||||||
|  | 							var currentSrcPos = srcPos + i * 3; | ||||||
|  | 							var b = srcBytes.get(currentSrcPos); | ||||||
|  | 							var g = srcBytes.get(currentSrcPos + 1); | ||||||
|  | 							var r = srcBytes.get(currentSrcPos + 2); | ||||||
|  | 							 | ||||||
|  | 							dstBytes.set(dstPos + channelMap[0], alpha); | ||||||
|  | 							dstBytes.set(dstPos + channelMap[1], r); | ||||||
|  | 							dstBytes.set(dstPos + channelMap[2], g); | ||||||
|  | 							dstBytes.set(dstPos + channelMap[3], b); | ||||||
|  |  | ||||||
|  | 						} else if (bmp.header.bpp == 32) { | ||||||
|  |  | ||||||
|  | 							var currentSrcPos = srcPos + i * 4; | ||||||
|  | 							var b = srcBytes.get(currentSrcPos); | ||||||
|  | 							var g = srcBytes.get(currentSrcPos + 1); | ||||||
|  | 							var r = srcBytes.get(currentSrcPos + 2); | ||||||
|  | 							 | ||||||
|  | 							dstBytes.set(dstPos + channelMap[0], alpha); | ||||||
|  | 							dstBytes.set(dstPos + channelMap[1], r); | ||||||
|  | 							dstBytes.set(dstPos + channelMap[2], g); | ||||||
|  | 							dstBytes.set(dstPos + channelMap[3], b); | ||||||
|  |  | ||||||
|  | 						} | ||||||
|  | 						dstPos += 4; | ||||||
|  | 					} | ||||||
|  | 					srcPos += yDir * srcPaddedStride; | ||||||
|  | 				} | ||||||
|  | 			case 1: | ||||||
|  | 				srcPos = 0; | ||||||
|  | 				var x = 0; | ||||||
|  | 				var y = bmp.header.topToBottom ? 0 : bmp.header.height - 1; | ||||||
|  | 				while( srcPos < bmp.header.dataLength ) { | ||||||
|  | 					var count = srcBytes.get(srcPos++); | ||||||
|  | 					var index = srcBytes.get(srcPos++); | ||||||
|  | 					if ( count == 0 ) { | ||||||
|  | 						if ( index == 0 ) { | ||||||
|  | 							x = 0; | ||||||
|  | 							y += yDir; | ||||||
|  | 						} else if ( index == 1 ) { | ||||||
|  | 							break; | ||||||
|  | 						} else if ( index == 2 ) { | ||||||
|  | 							x += srcBytes.get(srcPos++); | ||||||
|  | 							y += srcBytes.get(srcPos++); | ||||||
|  | 						} else { | ||||||
|  | 							count = index; | ||||||
|  | 							for( i in 0...count ) { | ||||||
|  | 								index = srcBytes.get(srcPos++); | ||||||
|  | 								dstBytes.blit( COLOR_SIZE * ((x+i) + y * bmp.header.width), colorTable, index * COLOR_SIZE, COLOR_SIZE ); | ||||||
|  | 							} | ||||||
|  | 							if (srcPos % 2 != 0) srcPos++; | ||||||
|  | 							x += count; | ||||||
|  | 						} | ||||||
|  | 					} else { | ||||||
|  | 						for( i in 0...count ) { | ||||||
|  | 							dstBytes.blit( COLOR_SIZE * ((x+i) + y * bmp.header.width), colorTable, index * COLOR_SIZE, COLOR_SIZE ); | ||||||
|  | 						} | ||||||
|  | 						x += count; | ||||||
|  | 					} | ||||||
|  | 				} | ||||||
|  | 			default: | ||||||
|  | 				throw 'compression ${bmp.header.compression} not supported'; | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		return dstBytes; | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	// `channelMap` contains indices to map into ARGB (f.e. the mapping for ARGB is [0,1,2,3], while for BGRA is [3,2,1,0]) | ||||||
|  | 	static function _buildFrom32( width : Int, height : Int, srcBytes : haxe.io.Bytes, channelMap : Array<Int>, topToBottom : Bool = false ) : Data { | ||||||
|  | 		var bpp = 24; | ||||||
|  | 		var paddedStride = computePaddedStride(width, bpp); | ||||||
|  | 		var bytesBGR = haxe.io.Bytes.alloc(paddedStride * height); | ||||||
|  | 		var topToBottom = topToBottom; | ||||||
|  | 		var dataLength = bytesBGR.length; | ||||||
|  | 		 | ||||||
|  | 		var dstStride = width * 3; | ||||||
|  | 		var srcLen = width * height * 4; | ||||||
|  | 		var yDir = -1; | ||||||
|  | 		var dstPos = dataLength - paddedStride; | ||||||
|  | 		var srcPos = 0; | ||||||
|  | 		 | ||||||
|  | 		if ( topToBottom ) { | ||||||
|  | 			yDir = 1; | ||||||
|  | 			dstPos = 0; | ||||||
|  | 		} | ||||||
|  | 		 | ||||||
|  | 		while( srcPos < srcLen ) { | ||||||
|  | 			var i = dstPos; | ||||||
|  | 			while( i < dstPos + dstStride ) { | ||||||
|  | 				var r = srcBytes.get(srcPos + channelMap[1]); | ||||||
|  | 				var g = srcBytes.get(srcPos + channelMap[2]); | ||||||
|  | 				var b = srcBytes.get(srcPos + channelMap[3]); | ||||||
|  | 				 | ||||||
|  | 				bytesBGR.set(i++, b); | ||||||
|  | 				bytesBGR.set(i++, g); | ||||||
|  | 				bytesBGR.set(i++, r); | ||||||
|  | 				 | ||||||
|  | 				srcPos += 4; | ||||||
|  | 			} | ||||||
|  | 			dstPos += yDir * paddedStride; | ||||||
|  | 		} | ||||||
|  | 		 | ||||||
|  | 		return { | ||||||
|  | 			header: { | ||||||
|  | 				width: width, | ||||||
|  | 				height: height, | ||||||
|  | 				paddedStride: paddedStride, | ||||||
|  | 				topToBottom: topToBottom, | ||||||
|  | 				bpp: bpp, | ||||||
|  | 				dataLength: dataLength, | ||||||
|  | 				compression: 0 | ||||||
|  | 			}, | ||||||
|  | 			pixels: bytesBGR, | ||||||
|  | 			colorTable: null | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										74
									
								
								leenkx/Sources/iron/format/bmp/Writer.hx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										74
									
								
								leenkx/Sources/iron/format/bmp/Writer.hx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,74 @@ | |||||||
|  | /* | ||||||
|  |  * format - Haxe File Formats | ||||||
|  |  * | ||||||
|  |  *  BMP File Format | ||||||
|  |  *  Copyright (C) 2007-2009 Robert Sköld | ||||||
|  |  * | ||||||
|  |  * Copyright (c) 2009, The Haxe Project Contributors | ||||||
|  |  * All rights reserved. | ||||||
|  |  * Redistribution and use in source and binary forms, with or without | ||||||
|  |  * modification, are permitted provided that the following conditions are met: | ||||||
|  |  * | ||||||
|  |  *   - Redistributions of source code must retain the above copyright | ||||||
|  |  *     notice, this list of conditions and the following disclaimer. | ||||||
|  |  *   - Redistributions in binary form must reproduce the above copyright | ||||||
|  |  *     notice, this list of conditions and the following disclaimer in the | ||||||
|  |  *     documentation and/or other materials provided with the distribution. | ||||||
|  |  * | ||||||
|  |  * THIS SOFTWARE IS PROVIDED BY THE HAXE PROJECT CONTRIBUTORS "AS IS" AND ANY | ||||||
|  |  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||||||
|  |  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||||||
|  |  * DISCLAIMED. IN NO EVENT SHALL THE HAXE PROJECT CONTRIBUTORS BE LIABLE FOR | ||||||
|  |  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | ||||||
|  |  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | ||||||
|  |  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | ||||||
|  |  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | ||||||
|  |  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | ||||||
|  |  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH | ||||||
|  |  * DAMAGE. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | package iron.format.bmp; | ||||||
|  |  | ||||||
|  | import iron.format.bmp.Data; | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class Writer { | ||||||
|  |  | ||||||
|  | 	static var DATA_OFFSET : Int = 0x36; | ||||||
|  |  | ||||||
|  | 	var output : haxe.io.Output; | ||||||
|  |  | ||||||
|  | 	public function new(o) { | ||||||
|  | 		output = o; | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	/** | ||||||
|  | 	 * Specs: http://s223767089.online.de/en/file-format-bmp | ||||||
|  | 	 */ | ||||||
|  | 	public function write( bmp : Data ) { | ||||||
|  | 		// Write Header (14 bytes) | ||||||
|  | 		output.writeString( "BM" );								// Signature | ||||||
|  | 		output.writeInt32(bmp.pixels.length + DATA_OFFSET );	// FileSize | ||||||
|  | 		output.writeInt32( 0 );									// Reserved | ||||||
|  | 		output.writeInt32( DATA_OFFSET );						// Offset | ||||||
|  |  | ||||||
|  | 		// Write InfoHeader (40 bytes) | ||||||
|  | 		output.writeInt32( 40 );								// InfoHeader size | ||||||
|  | 		output.writeInt32( bmp.header.width );					// Image width | ||||||
|  | 		var height = bmp.header.height; | ||||||
|  | 		if (bmp.header.topToBottom) height = -height;  | ||||||
|  | 		output.writeInt32( height );							// Image height | ||||||
|  | 		output.writeInt16( 1 );									// Number of planes | ||||||
|  | 		output.writeInt16( 24 );								// Bits per pixel (24bit RGB) | ||||||
|  | 		output.writeInt32( 0 );									// Compression type (no compression) | ||||||
|  | 		output.writeInt32( bmp.header.dataLength );				// Image data size (0 when uncompressed) | ||||||
|  | 		output.writeInt32( 0x2e30 );							// Horizontal resolution | ||||||
|  | 		output.writeInt32( 0x2e30 );							// Vertical resolution | ||||||
|  | 		output.writeInt32( 0 );									// Colors used (0 when uncompressed) | ||||||
|  | 		output.writeInt32( 0 );									// Important colors (0 when uncompressed) | ||||||
|  |  | ||||||
|  | 		// Write Raster Data | ||||||
|  | 		output.write(bmp.pixels); | ||||||
|  |   } | ||||||
|  | } | ||||||
| @ -159,9 +159,17 @@ class Animation { | |||||||
| 			if(markerEvents.get(sampler) != null){ | 			if(markerEvents.get(sampler) != null){ | ||||||
| 				for (i in 0...anim.marker_frames.length) { | 				for (i in 0...anim.marker_frames.length) { | ||||||
| 					if (frameIndex == anim.marker_frames[i]) { | 					if (frameIndex == anim.marker_frames[i]) { | ||||||
| 						var marketAct = markerEvents.get(sampler); | 						var markerAct = markerEvents.get(sampler); | ||||||
| 						var ar = marketAct.get(anim.marker_names[i]); | 						var ar = markerAct.get(anim.marker_names[i]); | ||||||
| 						if (ar != null) for (f in ar) f(); | 						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; | 				lastFrameIndex = frameIndex; | ||||||
|  | |||||||
| @ -30,12 +30,22 @@ class CameraObject extends Object { | |||||||
| 	static var sphereCenter = new Vec4(); | 	static var sphereCenter = new Vec4(); | ||||||
| 	static var vcenter = new Vec4(); | 	static var vcenter = new Vec4(); | ||||||
| 	static var vup = new Vec4(); | 	static var vup = new Vec4(); | ||||||
|  | 	 | ||||||
|  | 	#if lnx_vr | ||||||
|  | 	var helpMat = Mat4.identity(); | ||||||
|  | 	public var leftV = Mat4.identity(); | ||||||
|  | 	public var rightV = Mat4.identity(); | ||||||
|  | 	#end | ||||||
|  | 	 | ||||||
| 	public function new(data: CameraData) { | 	public function new(data: CameraData) { | ||||||
| 		super(); | 		super(); | ||||||
|  |  | ||||||
| 		this.data = data; | 		this.data = data; | ||||||
|  |  | ||||||
|  | 		#if lnx_vr | ||||||
|  | 		iron.system.VR.initButton(); | ||||||
|  | 		#end | ||||||
|  | 		 | ||||||
| 		buildProjection(); | 		buildProjection(); | ||||||
|  |  | ||||||
| 		V = Mat4.identity(); | 		V = Mat4.identity(); | ||||||
| @ -117,6 +127,26 @@ class CameraObject extends Object { | |||||||
| 		V.getInverse(transform.world); | 		V.getInverse(transform.world); | ||||||
| 		VP.multmats(P, V); | 		VP.multmats(P, V); | ||||||
|  |  | ||||||
|  |  | ||||||
|  | 		#if lnx_vr | ||||||
|  | 		var vr = kha.vr.VrInterface.instance; | ||||||
|  | 		if (vr != null && vr.IsPresenting()) { | ||||||
|  | 			leftV.setFrom(V); | ||||||
|  | 			helpMat.self = vr.GetViewMatrix(0); | ||||||
|  | 			leftV.multmat(helpMat); | ||||||
|  |  | ||||||
|  | 			rightV.setFrom(V); | ||||||
|  | 			helpMat.self = vr.GetViewMatrix(1); | ||||||
|  | 			rightV.multmat(helpMat); | ||||||
|  | 		} | ||||||
|  | 		else { | ||||||
|  | 			leftV.setFrom(V); | ||||||
|  | 		} | ||||||
|  | 		VP.multmats(P, leftV); | ||||||
|  | 		#else | ||||||
|  | 		VP.multmats(P, V); | ||||||
|  | 		#end | ||||||
|  |  | ||||||
| 		if (data.raw.frustum_culling) { | 		if (data.raw.frustum_culling) { | ||||||
| 			buildViewFrustum(VP, frustumPlanes); | 			buildViewFrustum(VP, frustumPlanes); | ||||||
| 		} | 		} | ||||||
|  | |||||||
| @ -155,8 +155,13 @@ class LightObject extends Object { | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	public function setCascade(camera: CameraObject, cascade: Int) { | 	public function setCascade(camera: CameraObject, cascade: Int) { | ||||||
| 		m.setFrom(camera.V); |  | ||||||
|  |  | ||||||
|  | 		#if lnx_vr | ||||||
|  | 		m.setFrom(camera.leftV); | ||||||
|  | 		#else | ||||||
|  | 		m.setFrom(camera.V); | ||||||
|  | 		#end | ||||||
|  | 		 | ||||||
| 		#if lnx_csm | 		#if lnx_csm | ||||||
| 		if (camSlicedP == null) { | 		if (camSlicedP == null) { | ||||||
| 			camSlicedP = []; | 			camSlicedP = []; | ||||||
|  | |||||||
| @ -21,8 +21,10 @@ class MeshObject extends Object { | |||||||
| 	public var particleChildren: Array<MeshObject> = null; | 	public var particleChildren: Array<MeshObject> = null; | ||||||
| 	public var particleOwner: MeshObject = null; // Particle object | 	public var particleOwner: MeshObject = null; // Particle object | ||||||
| 	public var particleIndex = -1; | 	public var particleIndex = -1; | ||||||
|  | 	public var render_emitter = true; | ||||||
| 	#end | 	#end | ||||||
| 	public var cameraDistance: Float; | 	public var cameraDistance: Float; | ||||||
|  | 	public var cameraList: Array<String> = null; | ||||||
| 	public var screenSize = 0.0; | 	public var screenSize = 0.0; | ||||||
| 	public var frustumCulling = true; | 	public var frustumCulling = true; | ||||||
| 	public var activeTilesheet: Tilesheet = null; | 	public var activeTilesheet: Tilesheet = null; | ||||||
| @ -234,6 +236,8 @@ class MeshObject extends Object { | |||||||
| 		if (cullMesh(context, Scene.active.camera, RenderPath.active.light)) return; | 		if (cullMesh(context, Scene.active.camera, RenderPath.active.light)) return; | ||||||
| 		var meshContext = raw != null ? context == "mesh" : false; | 		var meshContext = raw != null ? context == "mesh" : false; | ||||||
|  |  | ||||||
|  | 		if (cameraList != null && cameraList.indexOf(Scene.active.camera.name) < 0) return; | ||||||
|  |  | ||||||
| 		#if lnx_particles | 		#if lnx_particles | ||||||
| 		if (raw != null && raw.is_particle && particleOwner == null) return; // Instancing not yet set-up by particle system owner | 		if (raw != null && raw.is_particle && particleOwner == null) return; // Instancing not yet set-up by particle system owner | ||||||
| 		if (particleSystems != null && meshContext) { | 		if (particleSystems != null && meshContext) { | ||||||
| @ -244,6 +248,7 @@ class MeshObject extends Object { | |||||||
| 					Scene.active.spawnObject(psys.data.raw.instance_object, null, function(o: Object) { | 					Scene.active.spawnObject(psys.data.raw.instance_object, null, function(o: Object) { | ||||||
| 						if (o != null) { | 						if (o != null) { | ||||||
| 							var c: MeshObject = cast o; | 							var c: MeshObject = cast o; | ||||||
|  |     						c.cameraList = this.cameraList; | ||||||
| 							particleChildren.push(c); | 							particleChildren.push(c); | ||||||
| 							c.particleOwner = this; | 							c.particleOwner = this; | ||||||
| 							c.particleIndex = particleChildren.length - 1; | 							c.particleIndex = particleChildren.length - 1; | ||||||
| @ -255,11 +260,11 @@ class MeshObject extends Object { | |||||||
| 				particleSystems[i].update(particleChildren[i], this); | 				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 | 		#end | ||||||
|  |  | ||||||
| 		if (cullMaterial(context)) return; |  | ||||||
|  |  | ||||||
| 		// Get lod | 		// Get lod | ||||||
| 		var mats = materials; | 		var mats = materials; | ||||||
| 		var lod = this; | 		var lod = this; | ||||||
| @ -297,6 +302,10 @@ class MeshObject extends Object { | |||||||
|  |  | ||||||
| 		// Render mesh | 		// Render mesh | ||||||
| 		var ldata = lod.data; | 		var ldata = lod.data; | ||||||
|  | 		 | ||||||
|  | 		// Next pass rendering first (inverse order) | ||||||
|  | 		renderNextPass(g, context, bindParams, lod); | ||||||
|  | 		 | ||||||
| 		for (i in 0...ldata.geom.indexBuffers.length) { | 		for (i in 0...ldata.geom.indexBuffers.length) { | ||||||
|  |  | ||||||
| 			var mi = ldata.geom.materialIndices[i]; | 			var mi = ldata.geom.materialIndices[i]; | ||||||
| @ -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); | 			for (f in t._init) App.removeInit(f); | ||||||
| 			t._init = null; | 			t._init = null; | ||||||
| 		} | 		} | ||||||
|  | 		if (t._fixedUpdate != null) { | ||||||
|  | 			for (f in t._fixedUpdate) App.removeFixedUpdate(f); | ||||||
|  | 			t._fixedUpdate = null; | ||||||
|  | 		} | ||||||
| 		if (t._update != null) { | 		if (t._update != null) { | ||||||
| 			for (f in t._update) App.removeUpdate(f); | 			for (f in t._update) App.removeUpdate(f); | ||||||
| 			t._update = null; | 			t._update = null; | ||||||
|  | |||||||
| @ -24,6 +24,9 @@ class ObjectAnimation extends Animation { | |||||||
|  |  | ||||||
| 	public var transformMap: Map<String, FastFloat>; | 	public var transformMap: Map<String, FastFloat>; | ||||||
|  |  | ||||||
|  | 	var defaultSampler: ActionSampler = null; | ||||||
|  | 	static inline var DEFAULT_SAMPLER_ID = "__object_default_action__"; | ||||||
|  |  | ||||||
| 	public static var trackNames: Array<String> = [	"xloc", "yloc", "zloc", | 	public static var trackNames: Array<String> = [	"xloc", "yloc", "zloc", | ||||||
| 											   		"xrot", "yrot", "zrot", | 											   		"xrot", "yrot", "zrot", | ||||||
| 											   		"qwrot", "qxrot", "qyrot", "qzrot", | 											   		"qwrot", "qxrot", "qyrot", "qzrot", | ||||||
| @ -39,7 +42,6 @@ class ObjectAnimation extends Animation { | |||||||
| 		isSkinned = false; | 		isSkinned = false; | ||||||
| 		super(); | 		super(); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	function getAction(action: String): TObj { | 	function getAction(action: String): TObj { | ||||||
| 		for (a in oactions) if (a != null && a.objects[0].name == action) return a.objects[0]; | 		for (a in oactions) if (a != null && a.objects[0].name == action) return a.objects[0]; | ||||||
| 		return null; | 		return null; | ||||||
| @ -47,10 +49,29 @@ class ObjectAnimation extends Animation { | |||||||
|  |  | ||||||
| 	override public function play(action = "", onComplete: Void->Void = null, blendTime = 0.0, speed = 1.0, loop = true) { | 	override public function play(action = "", onComplete: Void->Void = null, blendTime = 0.0, speed = 1.0, loop = true) { | ||||||
| 		super.play(action, onComplete, blendTime, speed, loop); | 		super.play(action, onComplete, blendTime, speed, loop); | ||||||
| 		if (this.action == "" && oactions[0] != null) this.action = oactions[0].objects[0].name; | 		if (this.action == "" && oactions != null && oactions[0] != null){ | ||||||
|  | 			this.action = oactions[0].objects[0].name; | ||||||
|  | 		} | ||||||
| 		oaction = getAction(this.action); | 		oaction = getAction(this.action); | ||||||
| 		if (oaction != null) { | 		if (oaction != null) { | ||||||
| 			isSampled = oaction.sampled != null && oaction.sampled; | 			isSampled = oaction.sampled != null && oaction.sampled; | ||||||
|  | 			if (defaultSampler != null) { | ||||||
|  | 				deRegisterAction(DEFAULT_SAMPLER_ID); | ||||||
|  | 			} | ||||||
|  | 			var callbacks = onComplete != null ? [onComplete] : null; | ||||||
|  | 			defaultSampler = new ActionSampler(this.action, speed, loop, false, callbacks); | ||||||
|  | 			registerAction(DEFAULT_SAMPLER_ID, defaultSampler); | ||||||
|  | 			if (paused) defaultSampler.paused = true; | ||||||
|  | 			updateAnimation = function(map: Map<String, FastFloat>) { | ||||||
|  | 				sampleAction(defaultSampler, map); | ||||||
|  | 			}; | ||||||
|  | 		} | ||||||
|  | 		else { | ||||||
|  | 			if (defaultSampler != null) { | ||||||
|  | 				deRegisterAction(DEFAULT_SAMPLER_ID); | ||||||
|  | 				defaultSampler = null; | ||||||
|  | 			} | ||||||
|  | 			updateAnimation = null; | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| @ -61,12 +82,13 @@ class ObjectAnimation extends Animation { | |||||||
| 		Animation.beginProfile(); | 		Animation.beginProfile(); | ||||||
| 		#end | 		#end | ||||||
|  |  | ||||||
| 		if(transformMap == null) transformMap = new Map(); | 		if (transformMap == null) transformMap = new Map(); | ||||||
| 		transformMap = initTransformMap(); | 		transformMap = initTransformMap(); | ||||||
|  |  | ||||||
| 		super.update(delta); | 		super.update(delta); | ||||||
|  | 		if (defaultSampler != null) defaultSampler.paused = paused; | ||||||
| 		if (paused) return; | 		if (paused) return; | ||||||
| 		if(updateAnimation == null) return; | 		if (updateAnimation == null) return; | ||||||
| 		if (!isSkinned) updateObjectAnimation(); | 		if (!isSkinned) updateObjectAnimation(); | ||||||
|  |  | ||||||
| 		#if lnx_debug | 		#if lnx_debug | ||||||
|  | |||||||
| @ -2,11 +2,14 @@ package iron.object; | |||||||
|  |  | ||||||
| #if lnx_particles | #if lnx_particles | ||||||
|  |  | ||||||
|  | import kha.FastFloat; | ||||||
| import kha.graphics4.Usage; | import kha.graphics4.Usage; | ||||||
| import kha.arrays.Float32Array; | import kha.arrays.Float32Array; | ||||||
| import iron.data.Data; | import iron.data.Data; | ||||||
| import iron.data.ParticleData; | import iron.data.ParticleData; | ||||||
| import iron.data.SceneFormat; | import iron.data.SceneFormat; | ||||||
|  | import iron.data.Geometry; | ||||||
|  | import iron.data.MeshData; | ||||||
| import iron.system.Time; | import iron.system.Time; | ||||||
| import iron.math.Mat4; | import iron.math.Mat4; | ||||||
| import iron.math.Quat; | import iron.math.Quat; | ||||||
| @ -16,10 +19,13 @@ import iron.math.Vec4; | |||||||
| class ParticleSystem { | class ParticleSystem { | ||||||
| 	public var data: ParticleData; | 	public var data: ParticleData; | ||||||
| 	public var speed = 1.0; | 	public var speed = 1.0; | ||||||
|  | 	public var dynamicEmitter: Bool = true; | ||||||
|  | 	var currentSpeed = 0.0; | ||||||
| 	var particles: Array<Particle>; | 	var particles: Array<Particle>; | ||||||
| 	var ready: Bool; | 	var ready: Bool; | ||||||
| 	var frameRate = 24; | 	var frameRate = 24; | ||||||
| 	var lifetime = 0.0; | 	var lifetime = 0.0; | ||||||
|  | 	var looptime = 0.0; | ||||||
| 	var animtime = 0.0; | 	var animtime = 0.0; | ||||||
| 	var time = 0.0; | 	var time = 0.0; | ||||||
| 	var spawnRate = 0.0; | 	var spawnRate = 0.0; | ||||||
| @ -46,15 +52,31 @@ class ParticleSystem { | |||||||
| 	var ownerLoc = new Vec4(); | 	var ownerLoc = new Vec4(); | ||||||
| 	var ownerRot = new Quat(); | 	var ownerRot = new Quat(); | ||||||
| 	var ownerScl = new Vec4(); | 	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) { | 	public function new(sceneName: String, pref: TParticleReference) { | ||||||
| 		seed = pref.seed; | 		seed = pref.seed; | ||||||
|  | 		currentSpeed = speed; | ||||||
|  | 		speed = 0; | ||||||
| 		particles = []; | 		particles = []; | ||||||
| 		ready = false; | 		ready = false; | ||||||
| 		 | 		 | ||||||
| 		Data.getParticle(sceneName, pref.particle, function(b: ParticleData) { | 		Data.getParticle(sceneName, pref.particle, function(b: ParticleData) { | ||||||
| 			data = b; | 			data = b; | ||||||
| 			r = data.raw; | 			r = data.raw; | ||||||
|  | 			var dyn: Null<Bool> = r.dynamic_emitter; | ||||||
|  | 			var dynValue: Bool = true; | ||||||
|  | 			if (dyn != null) { | ||||||
|  | 				dynValue = dyn; | ||||||
|  | 			} | ||||||
|  | 			dynamicEmitter = dynValue; | ||||||
| 			if (Scene.active.raw.gravity != null) { | 			if (Scene.active.raw.gravity != null) { | ||||||
| 				gx = Scene.active.raw.gravity[0] * r.weight_gravity; | 				gx = Scene.active.raw.gravity[0] * r.weight_gravity; | ||||||
| 				gy = Scene.active.raw.gravity[1] * r.weight_gravity; | 				gy = Scene.active.raw.gravity[1] * r.weight_gravity; | ||||||
| @ -65,38 +87,73 @@ class ParticleSystem { | |||||||
| 				gy = 0; | 				gy = 0; | ||||||
| 				gz = -9.81 * r.weight_gravity; | 				gz = -9.81 * r.weight_gravity; | ||||||
| 			} | 			} | ||||||
| 			alignx = r.object_align_factor[0] / 2; | 			alignx = r.object_align_factor[0]; | ||||||
| 			aligny = r.object_align_factor[1] / 2; | 			aligny = r.object_align_factor[1]; | ||||||
| 			alignz = r.object_align_factor[2] / 2; | 			alignz = r.object_align_factor[2]; | ||||||
|  | 			looptime = (r.frame_end - r.frame_start) / frameRate; | ||||||
| 			lifetime = r.lifetime / 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; | 			spawnRate = ((r.frame_end - r.frame_start) / r.count) / frameRate; | ||||||
|  |  | ||||||
| 			for (i in 0...r.count) { | 			for (i in 0...r.count) { | ||||||
| 				var particle = new Particle(i); | 				particles.push(new Particle(i)); | ||||||
| 				particle.sr = 1 - Math.random() * r.size_random; |  | ||||||
| 				particles.push(particle); |  | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
| 			ready = true; | 			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() { | 	public function pause() { | ||||||
| 		lifetime = 0; | 		speed = 0; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	public function resume() { | 	public function resume() { | ||||||
| 		lifetime = r.lifetime / frameRate; | 		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) { | 	public function update(object: MeshObject, owner: MeshObject) { | ||||||
| 		if (!ready || object == null || speed == 0.0) return; | 		if (!ready || object == null || speed == 0.0) return; | ||||||
|  | 		if (iron.App.pauseUpdates) return; | ||||||
|  | 		 | ||||||
|  | 		var prevLap = lap; | ||||||
|  |  | ||||||
| 		// Copy owner world transform but discard scale | 		// Copy owner world transform but discard scale | ||||||
| 		owner.transform.world.decompose(ownerLoc, ownerRot, ownerScl); | 		owner.transform.world.decompose(ownerLoc, ownerRot, ownerScl); | ||||||
| 		object.transform.loc = ownerLoc; | 		if (dynamicEmitter) { | ||||||
| 		object.transform.rot = ownerRot; | 			object.transform.loc.x = 0; object.transform.loc.y = 0; object.transform.loc.z = 0; | ||||||
|  | 			object.transform.rot = new Quat(); | ||||||
|  | 		} else { | ||||||
|  | 			object.transform.loc = ownerLoc; | ||||||
|  | 			object.transform.rot = ownerRot; | ||||||
|  | 		} | ||||||
|  |  | ||||||
| 		// Set particle size per particle system | 		// Set particle size per particle system | ||||||
| 		object.transform.scale = new Vec4(r.particle_size, r.particle_size, r.particle_size, 1); | 		object.transform.scale = new Vec4(r.particle_size, r.particle_size, r.particle_size, 1); | ||||||
| @ -115,16 +172,25 @@ class ParticleSystem { | |||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		// Animate | 		// Animate | ||||||
| 		time += Time.delta * speed; | 		time += Time.renderDelta * speed; // realDelta to renderDelta | ||||||
| 		lap = Std.int(time / animtime); | 		lap = Std.int(time / animtime); | ||||||
| 		lapTime = time - lap * animtime; | 		lapTime = time - lap * animtime; | ||||||
| 		count = Std.int(lapTime / spawnRate); | 		count = Std.int(lapTime / spawnRate); | ||||||
|  |  | ||||||
|  | 		if (lap > prevLap && !r.loop) { | ||||||
|  | 			end(); | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		if (lap > prevLap && r.loop) { | ||||||
|  | 			lastSpawnedCount = 0; | ||||||
|  | 		} | ||||||
|  | 		 | ||||||
| 		updateGpu(object, owner); | 		updateGpu(object, owner); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	public function getData(): Mat4 { | 	public function getData(): Mat4 { | ||||||
| 		var hair = r.type == 1; | 		var hair = r.type == 1; | ||||||
|  | 		// Store loop flag in the sign: positive -> loop, negative -> no loop | ||||||
| 		m._00 = r.loop ? animtime : -animtime; | 		m._00 = r.loop ? animtime : -animtime; | ||||||
| 		m._01 = hair ? 1 / particles.length : spawnRate; | 		m._01 = hair ? 1 / particles.length : spawnRate; | ||||||
| 		m._02 = hair ? 1 : lifetime; | 		m._02 = hair ? 1 : lifetime; | ||||||
| @ -133,9 +199,9 @@ class ParticleSystem { | |||||||
| 		m._11 = hair ? 0 : aligny; | 		m._11 = hair ? 0 : aligny; | ||||||
| 		m._12 = hair ? 0 : alignz; | 		m._12 = hair ? 0 : alignz; | ||||||
| 		m._13 = hair ? 0 : r.factor_random; | 		m._13 = hair ? 0 : r.factor_random; | ||||||
| 		m._20 = hair ? 0 : gx * r.mass; | 		m._20 = hair ? 0 : gx; | ||||||
| 		m._21 = hair ? 0 : gy * r.mass; | 		m._21 = hair ? 0 : gy; | ||||||
| 		m._22 = hair ? 0 : gz * r.mass; | 		m._22 = hair ? 0 : gz; | ||||||
| 		m._23 = hair ? 0 : r.lifetime_random; | 		m._23 = hair ? 0 : r.lifetime_random; | ||||||
| 		m._30 = tilesx; | 		m._30 = tilesx; | ||||||
| 		m._31 = tilesy; | 		m._31 = tilesy; | ||||||
| @ -144,13 +210,34 @@ class ParticleSystem { | |||||||
| 		return m; | 		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) { | 	function updateGpu(object: MeshObject, owner: MeshObject) { | ||||||
| 		if (!object.data.geom.instanced) setupGeomGpu(object, owner); | 		if (dynamicEmitter) { | ||||||
| 		// GPU particles transform is attached to owner object | 			if (!hasUniqueGeom) ensureUniqueGeom(object); | ||||||
|  | 			var needSetup = instancedData == null || object.data.geom.instancedVB == null; | ||||||
|  | 			if (needSetup) setupGeomGpuDynamic(object, owner); | ||||||
|  | 			updateSpawnedInstances(object, owner); | ||||||
|  | 		} | ||||||
|  | 		else { | ||||||
|  | 			if (!hasUniqueGeom) ensureUniqueGeom(object); | ||||||
|  | 			if (!object.data.geom.instanced) setupGeomGpu(object, owner); | ||||||
|  | 		} | ||||||
|  | 		// GPU particles transform is attached to owner object in static mode | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	function setupGeomGpu(object: MeshObject, owner: MeshObject) { | 	function setupGeomGpu(object: MeshObject, owner: MeshObject) { | ||||||
| 		var instancedData = new Float32Array(particles.length * 6); | 		var instancedData = new Float32Array(particles.length * 3); | ||||||
| 		var i = 0; | 		var i = 0; | ||||||
|  |  | ||||||
| 		var normFactor = 1 / 32767; // pa.values are not normalized | 		var normFactor = 1 / 32767; // pa.values are not normalized | ||||||
| @ -169,10 +256,6 @@ class ParticleSystem { | |||||||
| 					instancedData.set(i, pa.values[j * pa.size    ] * normFactor * scaleFactor.x); i++; | 					instancedData.set(i, pa.values[j * pa.size    ] * normFactor * scaleFactor.x); i++; | ||||||
| 					instancedData.set(i, pa.values[j * pa.size + 1] * normFactor * scaleFactor.y); i++; | 					instancedData.set(i, pa.values[j * pa.size + 1] * normFactor * scaleFactor.y); i++; | ||||||
| 					instancedData.set(i, pa.values[j * pa.size + 2] * normFactor * scaleFactor.z); i++; | 					instancedData.set(i, pa.values[j * pa.size + 2] * normFactor * scaleFactor.z); i++; | ||||||
|  |  | ||||||
| 					instancedData.set(i, p.sr); i++; |  | ||||||
| 					instancedData.set(i, p.sr); i++; |  | ||||||
| 					instancedData.set(i, p.sr); i++; |  | ||||||
| 				} | 				} | ||||||
|  |  | ||||||
| 			case 1: // Face | 			case 1: // Face | ||||||
| @ -196,10 +279,6 @@ class ParticleSystem { | |||||||
| 					instancedData.set(i, pos.x * normFactor * scaleFactor.x); i++; | 					instancedData.set(i, pos.x * normFactor * scaleFactor.x); i++; | ||||||
| 					instancedData.set(i, pos.y * normFactor * scaleFactor.y); i++; | 					instancedData.set(i, pos.y * normFactor * scaleFactor.y); i++; | ||||||
| 					instancedData.set(i, pos.z * normFactor * scaleFactor.z); i++; | 					instancedData.set(i, pos.z * normFactor * scaleFactor.z); i++; | ||||||
|  |  | ||||||
| 					instancedData.set(i, p.sr); i++; |  | ||||||
| 					instancedData.set(i, p.sr); i++; |  | ||||||
| 					instancedData.set(i, p.sr); i++; |  | ||||||
| 				} | 				} | ||||||
|  |  | ||||||
| 			case 2: // Volume | 			case 2: // Volume | ||||||
| @ -210,27 +289,139 @@ class ParticleSystem { | |||||||
| 					instancedData.set(i, (Math.random() * 2.0 - 1.0) * scaleFactorVolume.x); i++; | 					instancedData.set(i, (Math.random() * 2.0 - 1.0) * scaleFactorVolume.x); i++; | ||||||
| 					instancedData.set(i, (Math.random() * 2.0 - 1.0) * scaleFactorVolume.y); i++; | 					instancedData.set(i, (Math.random() * 2.0 - 1.0) * scaleFactorVolume.y); i++; | ||||||
| 					instancedData.set(i, (Math.random() * 2.0 - 1.0) * scaleFactorVolume.z); i++; | 					instancedData.set(i, (Math.random() * 2.0 - 1.0) * scaleFactorVolume.z); i++; | ||||||
|  |  | ||||||
| 					instancedData.set(i, p.sr); i++; |  | ||||||
| 					instancedData.set(i, p.sr); i++; |  | ||||||
| 					instancedData.set(i, p.sr); i++; |  | ||||||
| 				} | 				} | ||||||
| 		} | 		} | ||||||
| 		object.data.geom.setupInstanced(instancedData, 3, Usage.StaticUsage); | 		object.data.geom.setupInstanced(instancedData, 1, Usage.StaticUsage); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	function fhash(n: Int): Float { | 	// allocate instanced VB once for this object  | ||||||
| 		var s = n + 1.0; | 	function setupGeomGpuDynamic(object: MeshObject, owner: MeshObject) { | ||||||
| 		s *= 9301.0 % s; | 		if (instancedData == null) instancedData = new Float32Array(particles.length * 3); | ||||||
| 		s = (s * 9301.0 + 49297.0) % 233280.0; | 		lastSpawnedCount = 0;  | ||||||
| 		return s / 233280.0; | 		// Create instanced VB once if missing (seed with our instancedData) | ||||||
|  | 		if (object.data.geom.instancedVB == null) { | ||||||
|  | 			object.data.geom.setupInstanced(instancedData, 1, Usage.DynamicUsage); | ||||||
|  | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	function ensureUniqueGeom(object: MeshObject) { | ||||||
|  | 		if (hasUniqueGeom) return; | ||||||
|  | 		var newData: MeshData = null; | ||||||
|  | 		new MeshData(object.data.raw, function(dat: MeshData) { | ||||||
|  | 			dat.scalePos = object.data.scalePos; | ||||||
|  | 			dat.scaleTex = object.data.scaleTex; | ||||||
|  | 			dat.format = object.data.format; | ||||||
|  | 			newData = dat; | ||||||
|  | 		}); | ||||||
|  | 		if (newData != null) object.setData(newData); | ||||||
|  | 		hasUniqueGeom = true; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	function updateSpawnedInstances(object: MeshObject, owner: MeshObject) { | ||||||
|  | 		if (instancedData == null) return; | ||||||
|  | 		var targetCount = count; | ||||||
|  | 		if (targetCount > particles.length) targetCount = particles.length; | ||||||
|  | 		if (targetCount <= lastSpawnedCount) return; | ||||||
|  |  | ||||||
|  | 		var normFactor = 1 / 32767; | ||||||
|  | 		var scalePosOwner = owner.data.scalePos; | ||||||
|  | 		var scalePosParticle = object.data.scalePos; | ||||||
|  | 		var particleSize = r.particle_size; | ||||||
|  | 		var base = 1.0 / (particleSize * scalePosParticle); | ||||||
|  |  | ||||||
|  | 		switch (r.emit_from) { | ||||||
|  | 			case 0: // Vert | ||||||
|  | 				var pa = owner.data.geom.positions; | ||||||
|  | 				var osx = owner.transform.scale.x; | ||||||
|  | 				var osy = owner.transform.scale.y; | ||||||
|  | 				var osz = owner.transform.scale.z; | ||||||
|  | 				var pCount = Std.int(pa.values.length / pa.size); | ||||||
|  | 				for (idx in lastSpawnedCount...targetCount) { | ||||||
|  | 					var j = Std.int(fhash(idx) * pCount); | ||||||
|  | 					var lx = pa.values[j * pa.size    ] * normFactor * scalePosOwner * osx; | ||||||
|  | 					var ly = pa.values[j * pa.size + 1] * normFactor * scalePosOwner * osy; | ||||||
|  | 					var lz = pa.values[j * pa.size + 2] * normFactor * scalePosOwner * osz; | ||||||
|  | 					tmpV4.x = lx; tmpV4.y = ly; tmpV4.z = lz; tmpV4.w = 1; | ||||||
|  | 					tmpV4.applyQuat(ownerRot); | ||||||
|  | 					var o = idx * 3; | ||||||
|  | 					instancedData.set(o    , (tmpV4.x + ownerLoc.x) * base); | ||||||
|  | 					instancedData.set(o + 1, (tmpV4.y + ownerLoc.y) * base); | ||||||
|  | 					instancedData.set(o + 2, (tmpV4.z + ownerLoc.z) * base); | ||||||
|  | 				} | ||||||
|  |  | ||||||
|  | 			case 1: // Face | ||||||
|  | 				var positions = owner.data.geom.positions.values; | ||||||
|  | 				var osx1 = owner.transform.scale.x; | ||||||
|  | 				var osy1 = owner.transform.scale.y; | ||||||
|  | 				var osz1 = owner.transform.scale.z; | ||||||
|  | 				for (idx in lastSpawnedCount...targetCount) { | ||||||
|  | 					var ia = owner.data.geom.indices[Std.random(owner.data.geom.indices.length)]; | ||||||
|  | 					var faceIndex = Std.random(Std.int(ia.length / 3)); | ||||||
|  | 					var i0 = ia[faceIndex * 3 + 0]; | ||||||
|  | 					var i1 = ia[faceIndex * 3 + 1]; | ||||||
|  | 					var i2 = ia[faceIndex * 3 + 2]; | ||||||
|  | 					var v0x = positions[i0 * 4    ], v0y = positions[i0 * 4 + 1], v0z = positions[i0 * 4 + 2]; | ||||||
|  | 					var v1x = positions[i1 * 4    ], v1y = positions[i1 * 4 + 1], v1z = positions[i1 * 4 + 2]; | ||||||
|  | 					var v2x = positions[i2 * 4    ], v2y = positions[i2 * 4 + 1], v2z = positions[i2 * 4 + 2]; | ||||||
|  | 					var rx = Math.random(); var ry = Math.random(); if (rx + ry > 1) { rx = 1 - rx; ry = 1 - ry; } | ||||||
|  | 					var pxs = v0x + rx * (v1x - v0x) + ry * (v2x - v0x); | ||||||
|  | 					var pys = v0y + rx * (v1y - v0y) + ry * (v2y - v0y); | ||||||
|  | 					var pzs = v0z + rx * (v1z - v0z) + ry * (v2z - v0z); | ||||||
|  | 					var px = pxs * normFactor * scalePosOwner * osx1; | ||||||
|  | 					var py = pys * normFactor * scalePosOwner * osy1; | ||||||
|  | 					var pz = pzs * normFactor * scalePosOwner * osz1; | ||||||
|  | 					tmpV4.x = px; tmpV4.y = py; tmpV4.z = pz; tmpV4.w = 1; | ||||||
|  | 					tmpV4.applyQuat(ownerRot); | ||||||
|  | 					var o1 = idx * 3; | ||||||
|  | 					instancedData.set(o1    , (tmpV4.x + ownerLoc.x) * base); | ||||||
|  | 					instancedData.set(o1 + 1, (tmpV4.y + ownerLoc.y) * base); | ||||||
|  | 					instancedData.set(o1 + 2, (tmpV4.z + ownerLoc.z) * base); | ||||||
|  | 				} | ||||||
|  |  | ||||||
|  | 			case 2: // Volume | ||||||
|  | 				var dim = object.transform.dim; | ||||||
|  | 				for (idx in lastSpawnedCount...targetCount) { | ||||||
|  | 					tmpV4.x = (Math.random() * 2.0 - 1.0) * (dim.x * 0.5); | ||||||
|  | 					tmpV4.y = (Math.random() * 2.0 - 1.0) * (dim.y * 0.5); | ||||||
|  | 					tmpV4.z = (Math.random() * 2.0 - 1.0) * (dim.z * 0.5); | ||||||
|  | 					tmpV4.w = 1; | ||||||
|  | 					tmpV4.applyQuat(ownerRot); | ||||||
|  | 					var o2 = idx * 3; | ||||||
|  | 					instancedData.set(o2    , (tmpV4.x + ownerLoc.x) * base); | ||||||
|  | 					instancedData.set(o2 + 1, (tmpV4.y + ownerLoc.y) * base); | ||||||
|  | 					instancedData.set(o2 + 2, (tmpV4.z + ownerLoc.z) * base); | ||||||
|  | 				} | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		// Upload full active range [0..targetCount) to this object's instanced VB | ||||||
|  | 		var geom = object.data.geom; | ||||||
|  | 		if (geom.instancedVB == null) { | ||||||
|  | 			geom.setupInstanced(instancedData, 1, Usage.DynamicUsage); | ||||||
|  | 		} | ||||||
|  | 		var vb = geom.instancedVB.lock(); | ||||||
|  | 		var totalFloats = targetCount * 3; // xyz per instance | ||||||
|  | 		var i = 0; | ||||||
|  | 		while (i < totalFloats) { | ||||||
|  | 			vb.setFloat32(i * 4, instancedData[i]); | ||||||
|  | 			i++; | ||||||
|  | 		} | ||||||
|  | 		geom.instancedVB.unlock(); | ||||||
|  | 		geom.instanceCount = targetCount; | ||||||
|  | 		lastSpawnedCount = targetCount; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	inline function fhash(n: Int): Float { | ||||||
|  |     var s = n + 1.0; | ||||||
|  |     s *= 9301.0 % s; | ||||||
|  |     s = (s * 9301.0 + 49297.0) % 233280.0; | ||||||
|  |     return s / 233280.0; | ||||||
|  | } | ||||||
|  |  | ||||||
| 	public function remove() {} | 	public function remove() {} | ||||||
|  |  | ||||||
| 	/** | 	/** | ||||||
| 		Generates a random point in the triangle with vertex positions abc. | 		Generates a random point in the triangle with vertex positions abc. | ||||||
|  | 		 | ||||||
| 		Please note that the given position vectors are changed in-place by this | 		Please note that the given position vectors are changed in-place by this | ||||||
| 		function and can be considered garbage afterwards, so make sure to clone | 		function and can be considered garbage afterwards, so make sure to clone | ||||||
| 		them first if needed. | 		them first if needed. | ||||||
| @ -255,10 +446,11 @@ class ParticleSystem { | |||||||
|  |  | ||||||
| class Particle { | class Particle { | ||||||
| 	public var i: Int; | 	public var i: Int; | ||||||
| 	public var px = 0.0; | 	 | ||||||
| 	public var py = 0.0; | 	public var x = 0.0; | ||||||
| 	public var pz = 0.0; | 	public var y = 0.0; | ||||||
| 	public var sr = 1.0; // Size random | 	public var z = 0.0; | ||||||
|  |  | ||||||
| 	public var cameraDistance: Float; | 	public var cameraDistance: Float; | ||||||
|  |  | ||||||
| 	public function new(i: Int) { | 	public function new(i: Int) { | ||||||
|  | |||||||
| @ -80,7 +80,7 @@ class Tilesheet { | |||||||
| 	function update() { | 	function update() { | ||||||
| 		if (!ready || paused || action.start >= action.end) return; | 		if (!ready || paused || action.start >= action.end) return; | ||||||
|  |  | ||||||
| 		time += Time.realDelta; | 		time += Time.renderDelta; | ||||||
|  |  | ||||||
| 		var frameTime = 1 / raw.framerate; | 		var frameTime = 1 / raw.framerate; | ||||||
| 		var framesToAdvance = 0; | 		var framesToAdvance = 0; | ||||||
|  | |||||||
| @ -181,11 +181,15 @@ class Uniforms { | |||||||
| 						// Multiple voxel volumes, always set params | 						// Multiple voxel volumes, always set params | ||||||
| 						g.setImageTexture(context.textureUnits[j], rt.image); // image2D/3D | 						g.setImageTexture(context.textureUnits[j], rt.image); // image2D/3D | ||||||
| 						if (rt.raw.name.startsWith("voxels_")) { | 						if (rt.raw.name.startsWith("voxels_")) { | ||||||
| 							g.setTextureParameters(context.textureUnits[j], TextureAddressing.Clamp, TextureAddressing.Clamp, TextureFilter.LinearFilter, TextureFilter.LinearFilter, MipMapFilter.NoMipFilter); | 							g.setTextureParameters(context.textureUnits[j], TextureAddressing.Clamp, TextureAddressing.Clamp, TextureFilter.LinearFilter, TextureFilter.LinearFilter, MipMapFilter.LinearMipFilter); | ||||||
|  | 						} | ||||||
|  | 						else if (rt.raw.name.startsWith("voxelsSDF")) | ||||||
|  | 						{ | ||||||
|  | 							g.setTexture3DParameters(context.textureUnits[j], TextureAddressing.Clamp, TextureAddressing.Clamp, TextureAddressing.Clamp, TextureFilter.PointFilter, TextureFilter.PointFilter, MipMapFilter.NoMipFilter); | ||||||
| 						} | 						} | ||||||
| 						else if (rt.raw.name.startsWith("voxels")) | 						else if (rt.raw.name.startsWith("voxels")) | ||||||
| 						{ | 						{ | ||||||
| 							g.setTexture3DParameters(context.textureUnits[j], TextureAddressing.Clamp, TextureAddressing.Clamp, TextureAddressing.Clamp, TextureFilter.LinearFilter, TextureFilter.LinearFilter, MipMapFilter.NoMipFilter); | 							g.setTexture3DParameters(context.textureUnits[j], TextureAddressing.Clamp, TextureAddressing.Clamp, TextureAddressing.Clamp, TextureFilter.LinearFilter, TextureFilter.LinearFilter, MipMapFilter.PointMipFilter); | ||||||
| 						} | 						} | ||||||
| 						else | 						else | ||||||
| 						{ | 						{ | ||||||
| @ -1109,6 +1113,26 @@ class Uniforms { | |||||||
| 				case "_texUnpack": { | 				case "_texUnpack": { | ||||||
| 					f = texUnpack != null ? texUnpack : 1.0; | 					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) { | 			if (f == null && externalFloatLinks != null) { | ||||||
|  | |||||||
| @ -1,37 +1,58 @@ | |||||||
| package iron.system; | package iron.system; | ||||||
|  |  | ||||||
| class Time { | 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; | 	public static var step(get, never): Float; | ||||||
| 	static function get_step(): Float { | 	static function get_step(): Float { | ||||||
| 		if (frequency == null) initFrequency(); | 		if (frequency == null) initFrequency(); | ||||||
| 		return 1 / frequency; | 		return 1 / frequency; | ||||||
| 	} | 	} | ||||||
|  | 	 | ||||||
|  | 	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; | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	public static var scale = 1.0; | 	static var lastTime = 0.0; | ||||||
|  | 	static var _delta = 0.0; | ||||||
| 	public static var delta(get, never): Float; | 	public static var delta(get, never): Float; | ||||||
| 	static function get_delta(): Float { | 	static function get_delta(): Float { | ||||||
| 		if (frequency == null) initFrequency(); | 		return _delta; | ||||||
| 		return (1 / frequency) * scale; |  | ||||||
| 	} | 	} | ||||||
|  | 	 | ||||||
| 	static var last = 0.0; | 	static var lastRenderTime = 0.0; | ||||||
| 	public static var realDelta = 0.0; | 	static var _renderDelta = 0.0; | ||||||
|  | 	public static var renderDelta(get, never): Float; | ||||||
|  | 	static function get_renderDelta(): Float { | ||||||
|  | 		return _renderDelta; | ||||||
|  | 	} | ||||||
|  | 		 | ||||||
| 	public static inline function time(): Float { | 	public static inline function time(): Float { | ||||||
| 		return kha.Scheduler.time(); | 		return kha.Scheduler.time() * scale; | ||||||
| 	} | 	} | ||||||
|  | 	 | ||||||
| 	public static inline function realTime(): Float { | 	public static inline function realTime(): Float { | ||||||
| 		return kha.Scheduler.realTime(); | 		return kha.Scheduler.realTime() * scale; | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	static var frequency: Null<Int> = null; |  | ||||||
|  |  | ||||||
| 	static function initFrequency() { |  | ||||||
| 		frequency = kha.Display.primary != null ? kha.Display.primary.frequency : 60; |  | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	public static function update() { | 	public static function update() { | ||||||
| 		realDelta = realTime() - last; | 		_delta = realTime() - lastTime; | ||||||
| 		last = realTime(); | 		lastTime = realTime(); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	public static function render() { | ||||||
|  | 		_renderDelta = realTime() - lastRenderTime; | ||||||
|  | 		lastRenderTime = realTime(); | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  | |||||||
| @ -94,34 +94,34 @@ class Tween { | |||||||
|  |  | ||||||
| 				// Way too much Reflect trickery.. | 				// Way too much Reflect trickery.. | ||||||
| 				var ps = Reflect.fields(a.props); | 				var ps = Reflect.fields(a.props); | ||||||
| 				for (i in 0...ps.length) { | 				for (j in 0...ps.length) { | ||||||
| 					var p = ps[i]; | 					var p = ps[j]; | ||||||
| 					var k = a._time / a.duration; | 					var k = a._time / a.duration; | ||||||
| 					if (k > 1) k = 1; | 					if (k > 1) k = 1; | ||||||
|  |  | ||||||
| 					if (a._comps[i] == 1) { | 					if (a._comps[j] == 1) { | ||||||
| 						var fromVal: Float = a._x[i]; | 						var fromVal: Float = a._x[j]; | ||||||
| 						var toVal: Float = Reflect.getProperty(a.props, p); | 						var toVal: Float = Reflect.getProperty(a.props, p); | ||||||
| 						var val: Float = fromVal + (toVal - fromVal) * eases[a.ease](k); | 						var val: Float = fromVal + (toVal - fromVal) * eases[a.ease](k); | ||||||
| 						Reflect.setProperty(a.target, p, val); | 						Reflect.setProperty(a.target, p, val); | ||||||
| 					} | 					} | ||||||
| 					else { // _comps[i] == 4 | 					else { // _comps[j] == 4 | ||||||
| 						var obj = Reflect.getProperty(a.props, p); | 						var obj = Reflect.getProperty(a.props, p); | ||||||
| 						var toX: Float = Reflect.getProperty(obj, "x"); | 						var toX: Float = Reflect.getProperty(obj, "x"); | ||||||
| 						var toY: Float = Reflect.getProperty(obj, "y"); | 						var toY: Float = Reflect.getProperty(obj, "y"); | ||||||
| 						var toZ: Float = Reflect.getProperty(obj, "z"); | 						var toZ: Float = Reflect.getProperty(obj, "z"); | ||||||
| 						var toW: Float = Reflect.getProperty(obj, "w"); | 						var toW: Float = Reflect.getProperty(obj, "w"); | ||||||
| 						if (a._normalize[i]) { | 						if (a._normalize[j]) { | ||||||
| 							var qdot = (a._x[i] * toX) + (a._y[i] * toY) + (a._z[i] * toZ) + (a._w[i] * toW); | 							var qdot = (a._x[j] * toX) + (a._y[j] * toY) + (a._z[j] * toZ) + (a._w[j] * toW); | ||||||
| 							if (qdot < 0.0) { | 							if (qdot < 0.0) { | ||||||
| 								toX = -toX; toY = -toY; toZ = -toZ; toW = -toW; | 								toX = -toX; toY = -toY; toZ = -toZ; toW = -toW; | ||||||
| 							} | 							} | ||||||
| 						} | 						} | ||||||
| 						var x: Float = a._x[i] + (toX - a._x[i]) * eases[a.ease](k); | 						var x: Float = a._x[j] + (toX - a._x[j]) * eases[a.ease](k); | ||||||
| 						var y: Float = a._y[i] + (toY - a._y[i]) * eases[a.ease](k); | 						var y: Float = a._y[j] + (toY - a._y[j]) * eases[a.ease](k); | ||||||
| 						var z: Float = a._z[i] + (toZ - a._z[i]) * eases[a.ease](k); | 						var z: Float = a._z[j] + (toZ - a._z[j]) * eases[a.ease](k); | ||||||
| 						var w: Float = a._w[i] + (toW - a._w[i]) * eases[a.ease](k); | 						var w: Float = a._w[j] + (toW - a._w[j]) * eases[a.ease](k); | ||||||
| 						if (a._normalize[i]) { | 						if (a._normalize[j]) { | ||||||
| 							var l = Math.sqrt(x * x + y * y + z * z + w * w); | 							var l = Math.sqrt(x * x + y * y + z * z + w * w); | ||||||
| 							if (l > 0.0) { | 							if (l > 0.0) { | ||||||
| 								l = 1.0 / l; | 								l = 1.0 / l; | ||||||
|  | |||||||
							
								
								
									
										52
									
								
								leenkx/Sources/iron/system/VR.hx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								leenkx/Sources/iron/system/VR.hx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,52 @@ | |||||||
|  | package iron.system; | ||||||
|  |  | ||||||
|  | import iron.math.Mat4; | ||||||
|  |  | ||||||
|  | #if lnx_vr | ||||||
|  | class VR { | ||||||
|  |  | ||||||
|  | 	static var undistortionMatrix: Mat4 = null; | ||||||
|  |  | ||||||
|  | 	public function new() {} | ||||||
|  |  | ||||||
|  | 	public static function getUndistortionMatrix(): Mat4 { | ||||||
|  | 		if (undistortionMatrix == null) { | ||||||
|  | 			undistortionMatrix = Mat4.identity(); | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		return undistortionMatrix; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	public static function getMaxRadiusSq(): Float { | ||||||
|  | 		return 0.0; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	public static function initButton() { | ||||||
|  | 		function vrDownListener(index: Int, x: Float, y: Float) { | ||||||
|  | 			var vr = kha.vr.VrInterface.instance; | ||||||
|  | 			if (vr == null || !vr.IsVrEnabled() || vr.IsPresenting()) return; | ||||||
|  | 			var w: Float = iron.App.w(); | ||||||
|  | 			var h: Float = iron.App.h(); | ||||||
|  | 			if (x < w - 150 || y < h - 150) return; | ||||||
|  | 			vr.onVRRequestPresent(); | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		function vrRender2D(g: kha.graphics2.Graphics) { | ||||||
|  | 			var vr = kha.vr.VrInterface.instance; | ||||||
|  | 			if (vr == null || !vr.IsVrEnabled() || vr.IsPresenting()) return; | ||||||
|  | 			var w: Float = iron.App.w(); | ||||||
|  | 			var h: Float = iron.App.h(); | ||||||
|  | 			g.color = 0xffff0000; | ||||||
|  | 			g.fillRect(w - 150, h - 150, 140, 140); | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		kha.input.Mouse.get().notify(vrDownListener, null, null, null); | ||||||
|  | 		iron.App.notifyOnRender2D(vrRender2D); | ||||||
|  |  | ||||||
|  | 		var vr = kha.vr.VrInterface.instance; // Straight to VR (Oculus Carmel) | ||||||
|  | 		if (vr != null && vr.IsVrEnabled()) { | ||||||
|  | 			vr.onVRRequestPresent(); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | #end | ||||||
| @ -20,6 +20,7 @@ class Config { | |||||||
| 		var path = iron.data.Data.dataPath + "config.lnx"; | 		var path = iron.data.Data.dataPath + "config.lnx"; | ||||||
| 		var bytes = haxe.io.Bytes.ofString(haxe.Json.stringify(raw)); | 		var bytes = haxe.io.Bytes.ofString(haxe.Json.stringify(raw)); | ||||||
| 		#if kha_krom | 		#if kha_krom | ||||||
|  | 		if (iron.data.Data.dataPath == '') path = Krom.getFilesLocation() + "/config.lnx"; | ||||||
| 		Krom.fileSaveBytes(path, bytes.getData()); | 		Krom.fileSaveBytes(path, bytes.getData()); | ||||||
| 		#elseif kha_kore | 		#elseif kha_kore | ||||||
| 		sys.io.File.saveBytes(path, bytes); | 		sys.io.File.saveBytes(path, bytes); | ||||||
| @ -47,6 +48,7 @@ typedef TConfig = { | |||||||
| 	@:optional var rp_ssr: Null<Bool>; | 	@:optional var rp_ssr: Null<Bool>; | ||||||
| 	@:optional var rp_ssrefr: Null<Bool>; | 	@:optional var rp_ssrefr: Null<Bool>; | ||||||
| 	@:optional var rp_bloom: Null<Bool>; | 	@:optional var rp_bloom: Null<Bool>; | ||||||
|  | 	@:optional var rp_chromatic_aberration: Null<Bool>; | ||||||
| 	@:optional var rp_motionblur: Null<Bool>; | 	@:optional var rp_motionblur: Null<Bool>; | ||||||
| 	@:optional var rp_gi: Null<Bool>; // voxelao | 	@:optional var rp_gi: Null<Bool>; // voxelao | ||||||
| 	@:optional var rp_dynres: Null<Bool>; // dynamic resolution scaling | 	@:optional var rp_dynres: Null<Bool>; // dynamic resolution scaling | ||||||
|  | |||||||
							
								
								
									
										99
									
								
								leenkx/Sources/leenkx/logicnode/AddParticleToObjectNode.hx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										99
									
								
								leenkx/Sources/leenkx/logicnode/AddParticleToObjectNode.hx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,99 @@ | |||||||
|  | package leenkx.logicnode; | ||||||
|  |  | ||||||
|  | import iron.data.SceneFormat.TSceneFormat; | ||||||
|  | import iron.data.Data; | ||||||
|  | import iron.object.Object; | ||||||
|  |  | ||||||
|  | class AddParticleToObjectNode extends LogicNode { | ||||||
|  |  | ||||||
|  | 	public var property0: String; | ||||||
|  |  | ||||||
|  | 	public function new(tree: LogicTree) { | ||||||
|  | 		super(tree); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	override function run(from: Int) { | ||||||
|  | 		#if lnx_particles | ||||||
|  |  | ||||||
|  | 		if (property0 == 'Scene Active'){		 | ||||||
|  | 			var objFrom: Object = inputs[1].get(); | ||||||
|  | 			var slot: Int = inputs[2].get(); | ||||||
|  | 			var objTo: Object = inputs[3].get(); | ||||||
|  | 	 | ||||||
|  | 			if (objFrom == null || objTo == null) return; | ||||||
|  | 	 | ||||||
|  | 			var mobjFrom = cast(objFrom, iron.object.MeshObject); | ||||||
|  | 	 | ||||||
|  | 			var psys = mobjFrom.particleSystems != null ? mobjFrom.particleSystems[slot] :  | ||||||
|  | 				mobjFrom.particleOwner != null && mobjFrom.particleOwner.particleSystems != null ? mobjFrom.particleOwner.particleSystems[slot] : null; | ||||||
|  |  | ||||||
|  | 			if (psys == null) return; | ||||||
|  | 	 | ||||||
|  | 			var mobjTo = cast(objTo, iron.object.MeshObject); | ||||||
|  | 	 | ||||||
|  | 			mobjTo.setupParticleSystem(iron.Scene.active.raw.name, {name: 'LnxPS', seed: 0, particle: @:privateAccess psys.r.name}); | ||||||
|  | 			 | ||||||
|  | 			mobjTo.render_emitter = inputs[4].get(); | ||||||
|  |  | ||||||
|  | 			iron.Scene.active.spawnObject(psys.data.raw.instance_object, null, function(o: Object) { | ||||||
|  | 				if (o != null) { | ||||||
|  | 					var c: iron.object.MeshObject = cast o; | ||||||
|  | 					if (mobjTo.particleChildren == null) mobjTo.particleChildren = []; | ||||||
|  | 					mobjTo.particleChildren.push(c); | ||||||
|  | 					c.particleOwner = mobjTo; | ||||||
|  | 					c.particleIndex = mobjTo.particleChildren.length - 1; | ||||||
|  | 				} | ||||||
|  | 			}); | ||||||
|  | 	 | ||||||
|  | 			var oslot: Int = mobjTo.particleSystems.length-1; | ||||||
|  | 			var opsys = mobjTo.particleSystems[oslot]; | ||||||
|  | 			@:privateAccess opsys.setupGeomGpu(mobjTo.particleChildren[oslot], mobjTo); | ||||||
|  | 		 | ||||||
|  | 		} else { | ||||||
|  | 			var sceneName: String = inputs[1].get(); | ||||||
|  | 			var objectName: String = inputs[2].get(); | ||||||
|  | 			var slot: Int = inputs[3].get(); | ||||||
|  |  | ||||||
|  | 			var mobjTo: Object = inputs[4].get(); | ||||||
|  | 			var mobjTo = cast(mobjTo, iron.object.MeshObject); | ||||||
|  |  | ||||||
|  | 			#if lnx_json | ||||||
|  | 				sceneName += ".json"; | ||||||
|  | 			#elseif lnx_compress | ||||||
|  | 				sceneName += ".lz4"; | ||||||
|  | 			#end | ||||||
|  |  | ||||||
|  | 			Data.getSceneRaw(sceneName, (rawScene: TSceneFormat) -> { | ||||||
|  |  | ||||||
|  | 			for (obj in rawScene.objects) { | ||||||
|  | 				if (obj.name == objectName) { | ||||||
|  | 					mobjTo.setupParticleSystem(sceneName, obj.particle_refs[slot]); | ||||||
|  | 					mobjTo.render_emitter = inputs[5].get();					 | ||||||
|  |  | ||||||
|  | 					iron.Scene.active.spawnObject(rawScene.particle_datas[slot].instance_object, null, function(o: Object) { | ||||||
|  | 						if (o != null) { | ||||||
|  | 							var c: iron.object.MeshObject = cast o; | ||||||
|  | 							if (mobjTo.particleChildren == null) mobjTo.particleChildren = []; | ||||||
|  | 							mobjTo.particleChildren.push(c); | ||||||
|  | 							c.particleOwner = mobjTo; | ||||||
|  | 							c.particleIndex = mobjTo.particleChildren.length - 1; | ||||||
|  | 						} | ||||||
|  | 					}, true, rawScene); | ||||||
|  |  | ||||||
|  | 					var oslot: Int = mobjTo.particleSystems.length-1; | ||||||
|  | 					var opsys = mobjTo.particleSystems[oslot]; | ||||||
|  | 					@:privateAccess opsys.setupGeomGpu(mobjTo.particleChildren[oslot], mobjTo); | ||||||
|  |  | ||||||
|  | 					break; | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			}); | ||||||
|  |  | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		#end | ||||||
|  |  | ||||||
|  | 		runOutput(0); | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										48
									
								
								leenkx/Sources/leenkx/logicnode/AnyContactNode.hx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								leenkx/Sources/leenkx/logicnode/AnyContactNode.hx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,48 @@ | |||||||
|  | package leenkx.logicnode; | ||||||
|  | import iron.object.Object; | ||||||
|  |  | ||||||
|  | #if lnx_physics | ||||||
|  | import leenkx.trait.physics.PhysicsCache; | ||||||
|  | import leenkx.trait.physics.RigidBody; | ||||||
|  | #end | ||||||
|  |  | ||||||
|  | class AnyContactNode extends LogicNode { | ||||||
|  |  | ||||||
|  | 	public var property0: String;  | ||||||
|  | 	var lastContact = false; | ||||||
|  |  | ||||||
|  | 	public function new(tree: LogicTree) { | ||||||
|  | 		super(tree); | ||||||
|  | 		tree.notifyOnUpdate(update); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	function update() { | ||||||
|  | 		var object1: Object = inputs[0].get(); | ||||||
|  | 		if (object1 == null) object1 = tree.object; | ||||||
|  | 		if (object1 == null) return; | ||||||
|  |  | ||||||
|  | 		var contact = false; | ||||||
|  |  | ||||||
|  | 		#if lnx_physics | ||||||
|  | 		var rb1 = PhysicsCache.getCachedRigidBody(object1); | ||||||
|  | 		if (rb1 != null) { | ||||||
|  | 			var rbs = PhysicsCache.getCachedContacts(rb1); | ||||||
|  | 			contact = (rbs != null && rbs.length > 0); | ||||||
|  | 		} | ||||||
|  | 		#end | ||||||
|  |  | ||||||
|  | 		var shouldTrigger = false; | ||||||
|  | 		switch (property0) { | ||||||
|  | 		case "begin": | ||||||
|  | 			shouldTrigger = contact && !lastContact; | ||||||
|  | 		case "overlap": | ||||||
|  | 			shouldTrigger = contact; | ||||||
|  | 		case "end": | ||||||
|  | 			shouldTrigger = !contact && lastContact; | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		lastContact = contact; | ||||||
|  |  | ||||||
|  | 		if (shouldTrigger) runOutput(0); | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										16
									
								
								leenkx/Sources/leenkx/logicnode/AutoExposureGetNode.hx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								leenkx/Sources/leenkx/logicnode/AutoExposureGetNode.hx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,16 @@ | |||||||
|  | package leenkx.logicnode; | ||||||
|  |  | ||||||
|  | class AutoExposureGetNode extends LogicNode { | ||||||
|  |  | ||||||
|  | 	public function new(tree:LogicTree) { | ||||||
|  | 		super(tree); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	override function get(from:Int):Dynamic { | ||||||
|  | 		return switch (from) { | ||||||
|  | 			case 0: leenkx.renderpath.Postprocess.auto_exposure_uniforms[0]; | ||||||
|  | 			case 1: leenkx.renderpath.Postprocess.auto_exposure_uniforms[1]; | ||||||
|  | 			default: 0.0; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										15
									
								
								leenkx/Sources/leenkx/logicnode/AutoExposureSetNode.hx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								leenkx/Sources/leenkx/logicnode/AutoExposureSetNode.hx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,15 @@ | |||||||
|  | package leenkx.logicnode; | ||||||
|  |  | ||||||
|  | class AutoExposureSetNode extends LogicNode { | ||||||
|  |  | ||||||
|  | 	public function new(tree:LogicTree) { | ||||||
|  | 		super(tree); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	override function run(from:Int) { | ||||||
|  |         leenkx.renderpath.Postprocess.auto_exposure_uniforms[0] = inputs[1].get(); | ||||||
|  |         leenkx.renderpath.Postprocess.auto_exposure_uniforms[1] = inputs[2].get(); | ||||||
|  |  | ||||||
|  | 		runOutput(0); | ||||||
|  | 	} | ||||||
|  | } | ||||||
| @ -1,26 +1,49 @@ | |||||||
| package leenkx.logicnode; | package leenkx.logicnode; | ||||||
|  |  | ||||||
| class CameraSetNode extends LogicNode { | class CameraSetNode extends LogicNode { | ||||||
|  | 	 | ||||||
|  | 	public var property0: String; | ||||||
|  | 	 | ||||||
| 	public function new(tree:LogicTree) { | 	public function new(tree:LogicTree) { | ||||||
| 		super(tree); | 		super(tree); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	override function run(from:Int) { | 	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 | 		switch (property0) { | ||||||
| 		leenkx.renderpath.Postprocess.camera_uniforms[2] = inputs[3].get();//Camera: ISO | 			case 'F-stop': | ||||||
| 		leenkx.renderpath.Postprocess.camera_uniforms[3] = inputs[4].get();//Camera: Exposure Compensation | 				leenkx.renderpath.Postprocess.camera_uniforms[0] = inputs[1].get();//Camera: F-Number | ||||||
| 		leenkx.renderpath.Postprocess.camera_uniforms[4] = inputs[5].get();//Fisheye Distortion | 			case 'Shutter Time':	 | ||||||
| 		leenkx.renderpath.Postprocess.camera_uniforms[5] = inputs[6].get();//DoF AutoFocus §§ If true, it ignores the DoF Distance setting | 				leenkx.renderpath.Postprocess.camera_uniforms[1] = inputs[1].get();//Camera: Shutter time | ||||||
| 		leenkx.renderpath.Postprocess.camera_uniforms[6] = inputs[7].get();//DoF Distance | 			case 'ISO': | ||||||
| 		leenkx.renderpath.Postprocess.camera_uniforms[7] = inputs[8].get();//DoF Focal Length mm | 				leenkx.renderpath.Postprocess.camera_uniforms[2] = inputs[1].get();//Camera: ISO | ||||||
| 		leenkx.renderpath.Postprocess.camera_uniforms[8] = inputs[9].get();//DoF F-Stop | 			case 'Exposure Compensation': | ||||||
| 		leenkx.renderpath.Postprocess.camera_uniforms[9] = inputs[10].get();//Tonemapping Method | 				leenkx.renderpath.Postprocess.camera_uniforms[3] = inputs[1].get();//Camera: Exposure Compensation | ||||||
| 		leenkx.renderpath.Postprocess.camera_uniforms[10] = inputs[11].get();//Distort | 			case 'Fisheye Distortion': | ||||||
| 		leenkx.renderpath.Postprocess.camera_uniforms[11] = inputs[12].get();//Film Grain | 				leenkx.renderpath.Postprocess.camera_uniforms[4] = inputs[1].get();//Fisheye Distortion | ||||||
| 		leenkx.renderpath.Postprocess.camera_uniforms[12] = inputs[13].get();//Sharpen | 			case 'Auto Focus': | ||||||
| 		leenkx.renderpath.Postprocess.camera_uniforms[13] = inputs[14].get();//Vignette | 				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); | 		runOutput(0); | ||||||
| 	} | 	} | ||||||
|  | |||||||
| @ -10,6 +10,7 @@ class ChromaticAberrationGetNode extends LogicNode { | |||||||
| 		return switch (from) { | 		return switch (from) { | ||||||
| 			case 0: leenkx.renderpath.Postprocess.chromatic_aberration_uniforms[0]; | 			case 0: leenkx.renderpath.Postprocess.chromatic_aberration_uniforms[0]; | ||||||
| 			case 1: leenkx.renderpath.Postprocess.chromatic_aberration_uniforms[1]; | 			case 1: leenkx.renderpath.Postprocess.chromatic_aberration_uniforms[1]; | ||||||
|  | 			case 2: leenkx.renderpath.Postprocess.chromatic_aberration_uniforms[2]; | ||||||
| 			default: 0.0; | 			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[0] = inputs[1].get(); | ||||||
|         leenkx.renderpath.Postprocess.chromatic_aberration_uniforms[1] = inputs[2].get(); |         leenkx.renderpath.Postprocess.chromatic_aberration_uniforms[1] = inputs[2].get(); | ||||||
|  | 		leenkx.renderpath.Postprocess.chromatic_aberration_uniforms[2] = inputs[3].get(); | ||||||
|  |  | ||||||
| 		runOutput(0); | 		runOutput(0); | ||||||
| 	} | 	} | ||||||
|  | |||||||
| @ -2,6 +2,9 @@ package leenkx.logicnode; | |||||||
|  |  | ||||||
| import iron.Scene; | import iron.Scene; | ||||||
| import iron.object.CameraObject; | import iron.object.CameraObject; | ||||||
|  | import iron.math.Vec4; | ||||||
|  | import iron.math.Quat; | ||||||
|  | import leenkx.math.Helper; | ||||||
|  |  | ||||||
| import leenkx.renderpath.RenderPathCreator; | import leenkx.renderpath.RenderPathCreator; | ||||||
|  |  | ||||||
| @ -27,11 +30,19 @@ class DrawCameraTextureNode extends LogicNode { | |||||||
| 				final c = inputs[2].get(); | 				final c = inputs[2].get(); | ||||||
| 				assert(Error, Std.isOfType(c, CameraObject), "Camera must be a camera object!"); | 				assert(Error, Std.isOfType(c, CameraObject), "Camera must be a camera object!"); | ||||||
| 				cam = cast(c, CameraObject); | 				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'); | 				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); | 				tree.notifyOnRender(render); | ||||||
| 				runOutput(0); | 				runOutput(0); | ||||||
|  |  | ||||||
| @ -48,8 +59,20 @@ class DrawCameraTextureNode extends LogicNode { | |||||||
| 		iron.Scene.active.camera = cam; | 		iron.Scene.active.camera = cam; | ||||||
| 		cam.renderTarget = rt; | 		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); | 		cam.renderFrame(g); | ||||||
|  |  | ||||||
|  | 		#if kha_html5 | ||||||
|  | 		cam.transform.rot.mult(q); | ||||||
|  | 		cam.transform.buildMatrix(); | ||||||
|  | 		#end | ||||||
|  |  | ||||||
| 		cam.renderTarget = oldRT; | 		cam.renderTarget = oldRT; | ||||||
| 		iron.Scene.active.camera = sceneCam; | 		iron.Scene.active.camera = sceneCam; | ||||||
| 	} | 	} | ||||||
|  | |||||||
| @ -99,8 +99,6 @@ class DrawImageSequenceNode extends LogicNode { | |||||||
| 		final colorVec = inputs[4].get(); | 		final colorVec = inputs[4].get(); | ||||||
| 		g.color = Color.fromFloats(colorVec.x, colorVec.y, colorVec.z, colorVec.w); | 		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()); | 		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 { | 	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
									
								
							
							
						
						
									
										59
									
								
								leenkx/Sources/leenkx/logicnode/DrawSubImageNode.hx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,59 @@ | |||||||
|  | package leenkx.logicnode; | ||||||
|  |  | ||||||
|  | import iron.math.Vec4; | ||||||
|  | import kha.Image; | ||||||
|  | import kha.Color; | ||||||
|  | import leenkx.renderpath.RenderToTexture; | ||||||
|  |  | ||||||
|  | class DrawSubImageNode extends LogicNode { | ||||||
|  |     var img: Image; | ||||||
|  |     var lastImgName = ""; | ||||||
|  |  | ||||||
|  |     public function new(tree: LogicTree) { | ||||||
|  |         super(tree); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     override function run(from: Int) { | ||||||
|  |         RenderToTexture.ensure2DContext("DrawImageNode"); | ||||||
|  |  | ||||||
|  |         final imgName: String = inputs[1].get(); | ||||||
|  |         final colorVec: Vec4 = inputs[2].get(); | ||||||
|  |         final anchorH: Int = inputs[3].get(); | ||||||
|  |         final anchorV: Int = inputs[4].get(); | ||||||
|  |         final x: Float = inputs[5].get(); | ||||||
|  |         final y: Float = inputs[6].get(); | ||||||
|  |         final width: Float = inputs[7].get(); | ||||||
|  |         final height: Float = inputs[8].get(); | ||||||
|  |         final sx: Float = inputs[9].get(); | ||||||
|  |         final sy: Float = inputs[10].get(); | ||||||
|  |         final swidth: Float = inputs[11].get(); | ||||||
|  |         final sheight: Float = inputs[12].get(); | ||||||
|  |         final angle: Float = inputs[13].get(); | ||||||
|  |  | ||||||
|  |         final drawx = x - 0.5 * width * anchorH; | ||||||
|  |         final drawy = y - 0.5 * height * anchorV; | ||||||
|  |         final sdrawx = sx - 0.5 * swidth * anchorH; | ||||||
|  |         final sdrawy = sy - 0.5 * sheight * anchorV; | ||||||
|  |  | ||||||
|  |         RenderToTexture.g.rotate(angle, x, y); | ||||||
|  |  | ||||||
|  |         if (imgName != lastImgName) { | ||||||
|  |             // Load new image | ||||||
|  |             lastImgName = imgName; | ||||||
|  |             iron.data.Data.getImage(imgName, (image: Image) -> { | ||||||
|  |                 img = image; | ||||||
|  |             }); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (img == null) { | ||||||
|  |             runOutput(0); | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         RenderToTexture.g.color = Color.fromFloats(colorVec.x, colorVec.y, colorVec.z, colorVec.w); | ||||||
|  |         RenderToTexture.g.drawScaledSubImage(img, sdrawx, sdrawy, swidth, sheight, drawx, drawy, width, height); | ||||||
|  |         RenderToTexture.g.rotate(-angle, x, y); | ||||||
|  |  | ||||||
|  |         runOutput(0); | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										17
									
								
								leenkx/Sources/leenkx/logicnode/GetAudioPositionNode.hx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								leenkx/Sources/leenkx/logicnode/GetAudioPositionNode.hx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,17 @@ | |||||||
|  | package leenkx.logicnode; | ||||||
|  |  | ||||||
|  | import aura.Aura; | ||||||
|  | import aura.Types; | ||||||
|  |  | ||||||
|  | class GetAudioPositionNode extends LogicNode { | ||||||
|  |  | ||||||
|  | 	public function new(tree: LogicTree) { | ||||||
|  | 		super(tree); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	override function get(from: Int): Dynamic { | ||||||
|  | 		var audio = inputs[0].get(); | ||||||
|  | 		if (audio == null || audio.channel == null) return 0.0; | ||||||
|  | 		return audio.channel.floatPosition / audio.channel.sampleRate; | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										19
									
								
								leenkx/Sources/leenkx/logicnode/GetCameraRenderFilterNode.hx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								leenkx/Sources/leenkx/logicnode/GetCameraRenderFilterNode.hx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,19 @@ | |||||||
|  | package leenkx.logicnode; | ||||||
|  |  | ||||||
|  | import iron.object.MeshObject; | ||||||
|  | import iron.object.CameraObject; | ||||||
|  |  | ||||||
|  | class GetCameraRenderFilterNode extends LogicNode { | ||||||
|  |  | ||||||
|  | 	public function new(tree: LogicTree) { | ||||||
|  | 		super(tree); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	override function get(from: Int): Dynamic { | ||||||
|  | 		var mo: MeshObject = cast inputs[0].get(); | ||||||
|  |  | ||||||
|  | 		if (mo == null) return null; | ||||||
|  |  | ||||||
|  | 		return mo.cameraList; | ||||||
|  | 	} | ||||||
|  | } | ||||||
| @ -8,7 +8,7 @@ class GetFPSNode extends LogicNode { | |||||||
|  |  | ||||||
|     override function get(from: Int): Dynamic { |     override function get(from: Int): Dynamic { | ||||||
|         if (from == 0) { |         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))) { |             if ((fps == Math.POSITIVE_INFINITY) || (fps == Math.NEGATIVE_INFINITY) || (Math.isNaN(fps))) { | ||||||
|                 return 0; |                 return 0; | ||||||
|             } |             } | ||||||
|  | |||||||
							
								
								
									
										72
									
								
								leenkx/Sources/leenkx/logicnode/GetParticleDataNode.hx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								leenkx/Sources/leenkx/logicnode/GetParticleDataNode.hx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,72 @@ | |||||||
|  | package leenkx.logicnode; | ||||||
|  |  | ||||||
|  | import iron.object.Object; | ||||||
|  |  | ||||||
|  | class GetParticleDataNode extends LogicNode { | ||||||
|  |  | ||||||
|  | 	public function new(tree: LogicTree) { | ||||||
|  | 		super(tree); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	override function get(from: Int): Dynamic { | ||||||
|  | 		var object: Object = inputs[0].get(); | ||||||
|  | 		var slot: Int = inputs[1].get(); | ||||||
|  |  | ||||||
|  | 		if (object == null) return null; | ||||||
|  |  | ||||||
|  | 	#if lnx_particles | ||||||
|  |  | ||||||
|  | 		var mo = cast(object, iron.object.MeshObject); | ||||||
|  |  | ||||||
|  | 		var psys = mo.particleSystems != null ? mo.particleSystems[slot] :  | ||||||
|  | 			mo.particleOwner != null && mo.particleOwner.particleSystems != null ? mo.particleOwner.particleSystems[slot] : null; | ||||||
|  |  | ||||||
|  | 		if (psys == null) return null; | ||||||
|  |  | ||||||
|  | 		return switch (from) { | ||||||
|  | 			case 0: | ||||||
|  | 				@:privateAccess psys.r.name; | ||||||
|  | 			case 1: | ||||||
|  | 				@:privateAccess psys.r.particle_size; | ||||||
|  | 			case 2: | ||||||
|  | 				@:privateAccess psys.r.frame_start; | ||||||
|  | 			case 3: | ||||||
|  | 				@:privateAccess psys.r.frame_end; | ||||||
|  | 			case 4: | ||||||
|  | 				@:privateAccess psys.lifetime; | ||||||
|  | 			case 5: | ||||||
|  | 				@:privateAccess psys.r.lifetime; | ||||||
|  | 			case 6: | ||||||
|  | 				@:privateAccess psys.r.emit_from; | ||||||
|  | 			case 7: | ||||||
|  | 				@:privateAccess psys.r.auto_start; | ||||||
|  | 			case 8: | ||||||
|  | 				@:privateAccess psys.r.is_unique; | ||||||
|  | 			case 9: | ||||||
|  | 				@:privateAccess psys.r.loop; | ||||||
|  | 			case 10: | ||||||
|  | 				new iron.math.Vec3(@:privateAccess psys.alignx, @:privateAccess psys.aligny, @:privateAccess psys.alignz); | ||||||
|  | 			case 11: | ||||||
|  | 				@:privateAccess psys.r.factor_random; | ||||||
|  | 			case 12: | ||||||
|  | 				new iron.math.Vec3(@:privateAccess psys.gx, @:privateAccess psys.gy, @:privateAccess psys.gz); | ||||||
|  | 			case 13: | ||||||
|  | 				@:privateAccess psys.r.weight_gravity; | ||||||
|  | 			case 14: | ||||||
|  | 				psys.speed; | ||||||
|  | 			case 15: | ||||||
|  | 				@:privateAccess psys.time; | ||||||
|  | 			case 16: | ||||||
|  | 				@:privateAccess psys.lap; | ||||||
|  | 			case 17: | ||||||
|  | 				@:privateAccess psys.lapTime; | ||||||
|  | 			case 18: | ||||||
|  | 				@:privateAccess psys.count; | ||||||
|  | 			default:  | ||||||
|  | 				null; | ||||||
|  | 		} | ||||||
|  | 	#end | ||||||
|  |  | ||||||
|  | 		return null; | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										38
									
								
								leenkx/Sources/leenkx/logicnode/GetParticleNode.hx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								leenkx/Sources/leenkx/logicnode/GetParticleNode.hx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,38 @@ | |||||||
|  | package leenkx.logicnode; | ||||||
|  |  | ||||||
|  | import iron.object.Object; | ||||||
|  |  | ||||||
|  | class GetParticleNode extends LogicNode { | ||||||
|  |  | ||||||
|  | 	public function new(tree: LogicTree) { | ||||||
|  | 		super(tree); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	override function get(from: Int): Dynamic { | ||||||
|  | 		var object: Object = inputs[0].get(); | ||||||
|  |  | ||||||
|  | 		if (object == null) return null; | ||||||
|  |  | ||||||
|  | 	#if lnx_particles | ||||||
|  |  | ||||||
|  | 		var mo = cast(object, iron.object.MeshObject); | ||||||
|  | 	 | ||||||
|  | 		switch (from) { | ||||||
|  | 			case 0: | ||||||
|  | 				var names: Array<String> = []; | ||||||
|  | 				if (mo.particleSystems != null) | ||||||
|  | 					for (psys in mo.particleSystems) | ||||||
|  | 						names.push(@:privateAccess psys.r.name); | ||||||
|  | 				return names; | ||||||
|  | 			case 1: | ||||||
|  | 				return mo.particleSystems != null ? mo.particleSystems.length : 0; | ||||||
|  | 			case 2: | ||||||
|  | 				return mo.render_emitter; | ||||||
|  | 			default:  | ||||||
|  | 				null; | ||||||
|  | 		} | ||||||
|  | 	#end | ||||||
|  |  | ||||||
|  | 		return null; | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										33
									
								
								leenkx/Sources/leenkx/logicnode/GetPositionSpeakerNode.hx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								leenkx/Sources/leenkx/logicnode/GetPositionSpeakerNode.hx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,33 @@ | |||||||
|  | package leenkx.logicnode; | ||||||
|  |  | ||||||
|  | #if lnx_audio | ||||||
|  | import iron.object.SpeakerObject; | ||||||
|  | import kha.audio1.AudioChannel; | ||||||
|  | #end | ||||||
|  |  | ||||||
|  | class GetPositionSpeakerNode extends LogicNode { | ||||||
|  |  | ||||||
|  | 	public function new(tree: LogicTree) { | ||||||
|  | 		super(tree); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	override function get(from: Int): Dynamic { | ||||||
|  | 		#if lnx_audio | ||||||
|  | 		var object: SpeakerObject = cast(inputs[0].get(), SpeakerObject); | ||||||
|  | 		if (object == null || object.sound == null) return 0.0; | ||||||
|  | 		 | ||||||
|  | 		if (object.channels.length == 0) return 0.0; | ||||||
|  | 		 | ||||||
|  | 		var channel = object.channels[0]; | ||||||
|  | 		 | ||||||
|  | 		var position = 0.0; | ||||||
|  | 		if (channel != null) { | ||||||
|  | 			position = @:privateAccess channel.get_position(); | ||||||
|  | 		} | ||||||
|  | 		 | ||||||
|  | 		return position; | ||||||
|  | 		#else | ||||||
|  | 		return 0.0; | ||||||
|  | 		#end | ||||||
|  | 	} | ||||||
|  | } | ||||||
| @ -1,26 +1,12 @@ | |||||||
| package leenkx.logicnode; | package leenkx.logicnode; | ||||||
|  |  | ||||||
| import iron.object.Object; |  | ||||||
| import iron.math.Vec4; |  | ||||||
|  |  | ||||||
| class GetWorldNode extends LogicNode { | class GetWorldNode extends LogicNode { | ||||||
|  |  | ||||||
| 	public var property0: String; |  | ||||||
|  |  | ||||||
| 	public function new(tree: LogicTree) { | 	public function new(tree: LogicTree) { | ||||||
| 		super(tree); | 		super(tree); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	override function get(from: Int): Dynamic { | 	override function get(from: Int): Dynamic { | ||||||
| 		var object: Object = inputs[0].get(); | 		return iron.Scene.active.raw.world_ref; | ||||||
|  |  | ||||||
| 		if (object == null) return null; |  | ||||||
|  |  | ||||||
| 		return switch (property0) { |  | ||||||
| 			case "Right": object.transform.world.right(); |  | ||||||
| 			case "Look": object.transform.world.look(); |  | ||||||
| 			case "Up": object.transform.world.up(); |  | ||||||
| 			default: null; |  | ||||||
| 		} |  | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
							
								
								
									
										26
									
								
								leenkx/Sources/leenkx/logicnode/GetWorldOrientationNode.hx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								leenkx/Sources/leenkx/logicnode/GetWorldOrientationNode.hx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,26 @@ | |||||||
|  | package leenkx.logicnode; | ||||||
|  |  | ||||||
|  | import iron.object.Object; | ||||||
|  | import iron.math.Vec4; | ||||||
|  |  | ||||||
|  | class GetWorldOrientationNode extends LogicNode { | ||||||
|  |  | ||||||
|  | 	public var property0: String; | ||||||
|  |  | ||||||
|  | 	public function new(tree: LogicTree) { | ||||||
|  | 		super(tree); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	override function get(from: Int): Dynamic { | ||||||
|  | 		var object: Object = inputs[0].get(); | ||||||
|  |  | ||||||
|  | 		if (object == null) return null; | ||||||
|  |  | ||||||
|  | 		return switch (property0) { | ||||||
|  | 			case "Right": object.transform.world.right(); | ||||||
|  | 			case "Look": object.transform.world.look(); | ||||||
|  | 			case "Up": object.transform.world.up(); | ||||||
|  | 			default: null; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
| @ -1,7 +1,10 @@ | |||||||
| package leenkx.logicnode; | package leenkx.logicnode; | ||||||
|  |  | ||||||
| import iron.object.Object; | import iron.object.Object; | ||||||
|  |  | ||||||
|  | #if lnx_physics | ||||||
|  | import leenkx.trait.physics.PhysicsCache; | ||||||
| import leenkx.trait.physics.RigidBody; | import leenkx.trait.physics.RigidBody; | ||||||
|  | #end | ||||||
|  |  | ||||||
| class HasContactNode extends LogicNode { | class HasContactNode extends LogicNode { | ||||||
|  |  | ||||||
| @ -15,12 +18,15 @@ class HasContactNode extends LogicNode { | |||||||
|  |  | ||||||
| 		if (object1 == null || object2 == null) return false; | 		if (object1 == null || object2 == null) return false; | ||||||
|  |  | ||||||
| #if lnx_physics | 		#if lnx_physics | ||||||
| 		var physics = leenkx.trait.physics.PhysicsWorld.active; | 		var rb1 = PhysicsCache.getCachedRigidBody(object1); | ||||||
| 		var rb2 = object2.getTrait(RigidBody); | 		var rb2 = PhysicsCache.getCachedRigidBody(object2); | ||||||
| 		var rbs = physics.getContacts(object1.getTrait(RigidBody)); | 		 | ||||||
| 		if (rbs != null) for (rb in rbs) if (rb == rb2) return true; | 		if (rb1 != null && rb2 != null) { | ||||||
| #end | 			var rbs = PhysicsCache.getCachedContacts(rb1); | ||||||
|  | 			return PhysicsCache.hasContactWith(rbs, rb2); | ||||||
|  | 		} | ||||||
|  | 		#end | ||||||
| 		return false; | 		return false; | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  | |||||||
							
								
								
									
										233
									
								
								leenkx/Sources/leenkx/logicnode/MouseLookNode.hx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										233
									
								
								leenkx/Sources/leenkx/logicnode/MouseLookNode.hx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,233 @@ | |||||||
|  | package leenkx.logicnode; | ||||||
|  |  | ||||||
|  | import iron.math.Vec4; | ||||||
|  | import iron.system.Input; | ||||||
|  | import iron.object.Object; | ||||||
|  | import kha.System; | ||||||
|  | import kha.FastFloat; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * MouseLookNode - FPS-style mouse look camera controller | ||||||
|  |  *  | ||||||
|  |  * This node provides smooth, resolution-independent mouse look functionality for  | ||||||
|  |  * first-person perspective controls. It supports separate body and head objects, | ||||||
|  |  * allowing for realistic FPS camera movement where the body rotates horizontally | ||||||
|  |  * and the head/camera rotates vertically. | ||||||
|  |  *  | ||||||
|  |  * Key Features: | ||||||
|  |  * - Resolution-adaptive scaling for consistent feel across different screen sizes | ||||||
|  |  * - Configurable axis orientations (X, Y, Z as front) | ||||||
|  |  * - Optional mouse cursor locking and hiding | ||||||
|  |  * - Invertible X/Y axes | ||||||
|  |  * - Rotation capping/limiting for both horizontal and vertical movement | ||||||
|  |  * - Smoothing support for smoother camera movement | ||||||
|  |  * - Physics integration with automatic rigid body synchronization | ||||||
|  |  * - Support for both local and world space head rotation | ||||||
|  |  */ | ||||||
|  | class MouseLookNode extends LogicNode { | ||||||
|  | 	// Configuration properties (set from Blender node interface) | ||||||
|  | 	public var property0: String;  // Front axis: "X", "Y", or "Z" | ||||||
|  | 	public var property1: Bool;    // Hide Locked: auto-lock mouse cursor | ||||||
|  | 	public var property2: Bool;    // Invert X: invert horizontal mouse movement | ||||||
|  | 	public var property3: Bool;    // Invert Y: invert vertical mouse movement | ||||||
|  | 	public var property4: Bool;    // Cap Left/Right: limit horizontal rotation | ||||||
|  | 	public var property5: Bool;    // Cap Up/Down: limit vertical rotation | ||||||
|  | 	public var property6: Bool;    // Head Local Space: use local space for head rotation | ||||||
|  |  | ||||||
|  | 	// Smoothing state variables - maintain previous frame values for interpolation | ||||||
|  | 	var smoothX: Float = 0.0;      // Smoothed horizontal mouse delta | ||||||
|  | 	var smoothY: Float = 0.0;      // Smoothed vertical mouse delta | ||||||
|  | 	 | ||||||
|  | 	// Rotation limits (in radians) | ||||||
|  | 	var maxHorizontal: Float = Math.PI;      // Maximum horizontal rotation (180 degrees) | ||||||
|  | 	var maxVertical: Float = Math.PI / 2;    // Maximum vertical rotation (90 degrees) | ||||||
|  | 	 | ||||||
|  | 	// Current rotation tracking for capping calculations | ||||||
|  | 	var currentHorizontal: Float = 0.0;      // Accumulated horizontal rotation | ||||||
|  | 	var currentVertical: Float = 0.0;        // Accumulated vertical rotation | ||||||
|  | 	 | ||||||
|  | 	// Resolution scaling reference - base resolution for consistent sensitivity | ||||||
|  | 	var baseResolutionWidth: Float = 1920.0; | ||||||
|  | 	 | ||||||
|  | 	// Sensitivity scaling constants | ||||||
|  | 	static inline var BASE_SCALE: Float = 1500.0;              // Base sensitivity scale factor | ||||||
|  | 	static var RADIAN_SCALING_FACTOR: Float = Math.PI * 50.0 / 180.0;  // Degrees to radians conversion with sensitivity scaling | ||||||
|  |  | ||||||
|  | 	public function new(tree: LogicTree) { | ||||||
|  | 		super(tree); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	/** | ||||||
|  | 	 * Main execution function called every frame when the node is active | ||||||
|  | 	 *  | ||||||
|  | 	 * Input connections: | ||||||
|  | 	 * [0] - Action trigger (not used in current implementation) | ||||||
|  | 	 * [1] - Body Object: the main object that rotates horizontally | ||||||
|  | 	 * [2] - Head Object: optional object that rotates vertically (typically camera) | ||||||
|  | 	 * [3] - Sensitivity: mouse sensitivity multiplier | ||||||
|  | 	 * [4] - Smoothing: movement smoothing factor (0.0 = no smoothing, 0.99 = maximum smoothing) | ||||||
|  | 	 */ | ||||||
|  | 	override function run(from: Int) { | ||||||
|  | 		// Get input values from connected nodes | ||||||
|  | 		var bodyObject: Object = inputs[1].get(); | ||||||
|  | 		var headObject: Object = inputs[2].get(); | ||||||
|  | 		var sensitivity: FastFloat = inputs[3].get(); | ||||||
|  | 		var smoothing: FastFloat = inputs[4].get(); | ||||||
|  |  | ||||||
|  | 		// Early exit if no body object is provided | ||||||
|  | 		if (bodyObject == null) { | ||||||
|  | 			runOutput(0); | ||||||
|  | 			return; | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		// Get mouse input state | ||||||
|  | 		var mouse = Input.getMouse(); | ||||||
|  | 		 | ||||||
|  | 		// Handle automatic mouse cursor locking for FPS controls | ||||||
|  | 		if (property1) { | ||||||
|  | 			if (mouse.started() && !mouse.locked) { | ||||||
|  | 				mouse.lock();  // Center and hide cursor, enable unlimited movement | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		// Only process mouse look when cursor is locked or mouse button is held | ||||||
|  | 		// This prevents unwanted camera movement when UI elements are being used | ||||||
|  | 		if (!mouse.locked && !mouse.down()) { | ||||||
|  | 			runOutput(0); | ||||||
|  | 			return; | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		// Get raw mouse movement delta (pixels moved since last frame) | ||||||
|  | 		var deltaX: Float = mouse.movementX; | ||||||
|  | 		var deltaY: Float = mouse.movementY; | ||||||
|  |  | ||||||
|  | 		// Apply axis inversion if configured | ||||||
|  | 		if (property2) deltaX = -deltaX;  // Invert horizontal movement | ||||||
|  | 		if (property3) deltaY = -deltaY;  // Invert vertical movement | ||||||
|  |  | ||||||
|  | 		// Calculate resolution-adaptive scaling to maintain consistent sensitivity | ||||||
|  | 		// across different screen resolutions. Higher resolutions will have proportionally | ||||||
|  | 		// higher scaling to compensate for increased pixel density. | ||||||
|  | 		var resolutionMultiplier: Float = System.windowWidth() / baseResolutionWidth; | ||||||
|  |  | ||||||
|  | 		// Apply movement smoothing if enabled | ||||||
|  | 		// This creates a weighted average between current and previous movement values | ||||||
|  | 		// to reduce jittery camera movement, especially useful for low framerates | ||||||
|  | 		if (smoothing > 0.0) { | ||||||
|  | 			var smoothingFactor: Float = Math.min(smoothing, 0.99);  // Cap smoothing to prevent complete freeze | ||||||
|  | 			smoothX = smoothX * smoothingFactor + deltaX * (1.0 - smoothingFactor); | ||||||
|  | 			smoothY = smoothY * smoothingFactor + deltaY * (1.0 - smoothingFactor); | ||||||
|  | 			deltaX = smoothX; | ||||||
|  | 			deltaY = smoothY; | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		// Define rotation axes based on the configured front axis | ||||||
|  | 		// These determine which 3D axes are used for horizontal and vertical rotation | ||||||
|  | 		var horizontalAxis = new Vec4();  // Axis for left/right body rotation | ||||||
|  | 		var verticalAxis = new Vec4();    // Axis for up/down head rotation | ||||||
|  | 		 | ||||||
|  | 		switch (property0) { | ||||||
|  | 			case "X":  // X-axis forward (e.g., for side-scrolling or specific orientations) | ||||||
|  | 				horizontalAxis.set(0, 0, 1);  // Z-axis for horizontal rotation | ||||||
|  | 				verticalAxis.set(0, 1, 0);    // Y-axis for vertical rotation | ||||||
|  | 			case "Y":  // Y-axis forward (most common for 3D games) | ||||||
|  | 				#if lnx_yaxisup | ||||||
|  | 				// Y-up coordinate system (Blender default) | ||||||
|  | 				horizontalAxis.set(0, 0, 1);  // Z-axis for horizontal rotation | ||||||
|  | 				verticalAxis.set(1, 0, 0);    // X-axis for vertical rotation | ||||||
|  | 				#else | ||||||
|  | 				// Z-up coordinate system | ||||||
|  | 				horizontalAxis.set(0, 0, 1);  // Z-axis for horizontal rotation | ||||||
|  | 				verticalAxis.set(1, 0, 0);    // X-axis for vertical rotation | ||||||
|  | 				#end | ||||||
|  | 			case "Z":  // Z-axis forward (top-down or specific orientations) | ||||||
|  | 				horizontalAxis.set(0, 1, 0);  // Y-axis for horizontal rotation | ||||||
|  | 				verticalAxis.set(1, 0, 0);    // X-axis for vertical rotation | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		// Calculate final sensitivity scaling combining base scale and resolution adaptation | ||||||
|  | 		var finalScale: Float = BASE_SCALE * resolutionMultiplier; | ||||||
|  |  | ||||||
|  | 		// Apply user-defined sensitivity multiplier | ||||||
|  | 		deltaX *= sensitivity; | ||||||
|  | 		deltaY *= sensitivity; | ||||||
|  |  | ||||||
|  | 		// Convert pixel movement to rotation angles (radians) | ||||||
|  | 		// Negative values ensure natural movement direction (moving mouse right rotates right) | ||||||
|  | 		var horizontalRotation: Float = (-deltaX / finalScale) * RADIAN_SCALING_FACTOR; | ||||||
|  | 		var verticalRotation: Float = (-deltaY / finalScale) * RADIAN_SCALING_FACTOR; | ||||||
|  |  | ||||||
|  | 		// Apply horizontal rotation capping if enabled | ||||||
|  | 		// This prevents the character from rotating beyond specified limits | ||||||
|  | 		if (property4) { | ||||||
|  | 			currentHorizontal += horizontalRotation; | ||||||
|  | 			// Clamp rotation to maximum horizontal range and adjust current frame rotation | ||||||
|  | 			if (currentHorizontal > maxHorizontal) { | ||||||
|  | 				horizontalRotation -= (currentHorizontal - maxHorizontal); | ||||||
|  | 				currentHorizontal = maxHorizontal; | ||||||
|  | 			} else if (currentHorizontal < -maxHorizontal) { | ||||||
|  | 				horizontalRotation -= (currentHorizontal + maxHorizontal); | ||||||
|  | 				currentHorizontal = -maxHorizontal; | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		// Apply vertical rotation capping if enabled | ||||||
|  | 		// This prevents looking too far up or down (like human neck limitations) | ||||||
|  | 		if (property5) { | ||||||
|  | 			currentVertical += verticalRotation; | ||||||
|  | 			// Clamp rotation to maximum vertical range and adjust current frame rotation | ||||||
|  | 			if (currentVertical > maxVertical) { | ||||||
|  | 				verticalRotation -= (currentVertical - maxVertical); | ||||||
|  | 				currentVertical = maxVertical; | ||||||
|  | 			} else if (currentVertical < -maxVertical) { | ||||||
|  | 				verticalRotation -= (currentVertical + maxVertical); | ||||||
|  | 				currentVertical = -maxVertical; | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		// Apply horizontal rotation to body object (character turning left/right) | ||||||
|  | 		if (horizontalRotation != 0.0) { | ||||||
|  | 			bodyObject.transform.rotate(horizontalAxis, horizontalRotation); | ||||||
|  | 			 | ||||||
|  | 			// Synchronize physics rigid body if present | ||||||
|  | 			// This ensures physics simulation stays in sync with visual transform | ||||||
|  | 			#if lnx_physics | ||||||
|  | 			var rigidBody = bodyObject.getTrait(leenkx.trait.physics.RigidBody); | ||||||
|  | 			if (rigidBody != null) rigidBody.syncTransform(); | ||||||
|  | 			#end | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		// Apply vertical rotation to head object (camera looking up/down) | ||||||
|  | 		if (headObject != null && verticalRotation != 0.0) { | ||||||
|  | 			if (property6) { | ||||||
|  | 				// Local space rotation - recommended when head is a child of body | ||||||
|  | 				// This prevents gimbal lock and rotation inheritance issues | ||||||
|  | 				headObject.transform.rotate(verticalAxis, verticalRotation); | ||||||
|  | 			} else { | ||||||
|  | 				// World space rotation - uses head object's current right vector | ||||||
|  | 				// More accurate for independent head objects but can cause issues with parenting | ||||||
|  | 				var headVerticalAxis = headObject.transform.world.right(); | ||||||
|  | 				headObject.transform.rotate(headVerticalAxis, verticalRotation); | ||||||
|  | 			} | ||||||
|  | 			 | ||||||
|  | 			// Synchronize head physics rigid body if present | ||||||
|  | 			#if lnx_physics | ||||||
|  | 			var headRigidBody = headObject.getTrait(leenkx.trait.physics.RigidBody); | ||||||
|  | 			if (headRigidBody != null) headRigidBody.syncTransform(); | ||||||
|  | 			#end | ||||||
|  | 		} else if (headObject == null && verticalRotation != 0.0) { | ||||||
|  | 			// Fallback: if no separate head object, apply vertical rotation to body | ||||||
|  | 			// This creates a simpler single-object camera control | ||||||
|  | 			bodyObject.transform.rotate(verticalAxis, verticalRotation); | ||||||
|  | 			 | ||||||
|  | 			// Synchronize body physics rigid body | ||||||
|  | 			#if lnx_physics | ||||||
|  | 			var rigidBody = bodyObject.getTrait(leenkx.trait.physics.RigidBody); | ||||||
|  | 			if (rigidBody != null) rigidBody.syncTransform(); | ||||||
|  | 			#end | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		// Continue to next connected node in the logic tree | ||||||
|  | 		runOutput(0); | ||||||
|  | 	} | ||||||
|  | }  | ||||||
| @ -1,7 +1,11 @@ | |||||||
| package leenkx.logicnode; | package leenkx.logicnode; | ||||||
|  |  | ||||||
| import iron.object.Object; | import iron.object.Object; | ||||||
|  |  | ||||||
|  | #if lnx_physics | ||||||
|  | import leenkx.trait.physics.PhysicsCache; | ||||||
| import leenkx.trait.physics.RigidBody; | import leenkx.trait.physics.RigidBody; | ||||||
|  | #end | ||||||
|  |  | ||||||
|  |  | ||||||
| class OnContactNode extends LogicNode { | class OnContactNode extends LogicNode { | ||||||
|  |  | ||||||
| @ -23,22 +27,15 @@ class OnContactNode extends LogicNode { | |||||||
|  |  | ||||||
| 		var contact = false; | 		var contact = false; | ||||||
|  |  | ||||||
| #if lnx_physics | 		#if lnx_physics | ||||||
| 		var physics = leenkx.trait.physics.PhysicsWorld.active; | 			var rb1 = PhysicsCache.getCachedRigidBody(object1); | ||||||
| 		var rb1 = object1.getTrait(RigidBody); | 			var rb2 = PhysicsCache.getCachedRigidBody(object2); | ||||||
| 		if (rb1 != null) { | 			 | ||||||
| 			var rbs = physics.getContacts(rb1); | 			if (rb1 != null && rb2 != null) { | ||||||
| 			if (rbs != null) { | 				var rbs = PhysicsCache.getCachedContacts(rb1); | ||||||
| 				var rb2 = object2.getTrait(RigidBody); | 				contact = PhysicsCache.hasContactWith(rbs, rb2); | ||||||
| 				for (rb in rbs) { |  | ||||||
| 					if (rb == rb2) { |  | ||||||
| 						contact = true; |  | ||||||
| 						break; |  | ||||||
| 					} |  | ||||||
| 				} |  | ||||||
| 			} | 			} | ||||||
| 		} | 		#end | ||||||
| #end |  | ||||||
|  |  | ||||||
| 		var b = false; | 		var b = false; | ||||||
| 		switch (property0) { | 		switch (property0) { | ||||||
|  | |||||||
							
								
								
									
										23
									
								
								leenkx/Sources/leenkx/logicnode/OnceNode.hx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								leenkx/Sources/leenkx/logicnode/OnceNode.hx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,23 @@ | |||||||
|  | package leenkx.logicnode; | ||||||
|  |  | ||||||
|  | class OnceNode extends LogicNode { | ||||||
|  |  | ||||||
|  |     var triggered:Bool = false; | ||||||
|  |  | ||||||
|  |     public function new(tree: LogicTree) { | ||||||
|  |         super(tree); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     override function run(from: Int) { | ||||||
|  | 		if(from == 1){ | ||||||
|  | 			triggered = false; | ||||||
|  | 			return; | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  |         if (!triggered) { | ||||||
|  | 			triggered = true; | ||||||
|  |             runOutput(0); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | } | ||||||
| @ -9,19 +9,38 @@ import iron.Scene; | |||||||
|  |  | ||||||
| class PlayAnimationTreeNode extends LogicNode { | class PlayAnimationTreeNode extends LogicNode { | ||||||
|  |  | ||||||
|  | 	var object: Object; | ||||||
|  | 	var action: Dynamic; | ||||||
|  | 	var init: Bool = false; | ||||||
|  |  | ||||||
| 	public function new(tree: LogicTree) { | 	public function new(tree: LogicTree) { | ||||||
| 		super(tree); | 		super(tree); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	override function run(from: Int) { | 	override function run(from: Int) { | ||||||
| 		var object: Object = inputs[1].get(); | 		object = inputs[1].get(); | ||||||
| 		var action: Dynamic = inputs[2].get(); | 		action = inputs[2].get(); | ||||||
|  |  | ||||||
| 		assert(Error, object != null, "The object input not be null"); | 		assert(Error, object != null, "The object input not be null"); | ||||||
|  | 		init = true; | ||||||
|  | 		tree.notifyOnUpdate(playAnim); | ||||||
|  | 		// TO DO: Investigate AnimAction get and PlayAnimationTree notifiers | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	function playAnim() { | ||||||
|  | 		if (init = false) return; | ||||||
|  | 		 | ||||||
|  | 		init = false; | ||||||
|  | 		tree.removeUpdate(playAnim); | ||||||
|  | 		 | ||||||
| 		var animation = object.animation; | 		var animation = object.animation; | ||||||
| 		if(animation == null) { | 		if(animation == null) { | ||||||
| 			#if lnx_skin | 			#if lnx_skin | ||||||
| 			animation = object.getBoneAnimation(object.uid); | 			animation = object.getBoneAnimation(object.uid); | ||||||
|  | 			if (animation == null) { | ||||||
|  | 				tree.notifyOnUpdate(playAnim); | ||||||
|  | 				init = true; | ||||||
|  | 				return; | ||||||
|  | 			} | ||||||
| 			cast(animation, BoneAnimation).setAnimationLoop(function f(mats) { | 			cast(animation, BoneAnimation).setAnimationLoop(function f(mats) { | ||||||
| 				action(mats); | 				action(mats); | ||||||
| 			}); | 			}); | ||||||
| @ -32,7 +51,6 @@ class PlayAnimationTreeNode extends LogicNode { | |||||||
| 				action(mats); | 				action(mats); | ||||||
| 			}); | 			}); | ||||||
| 		} | 		} | ||||||
| 		 |  | ||||||
| 		runOutput(0); | 		runOutput(0); | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  | |||||||
							
								
								
									
										41
									
								
								leenkx/Sources/leenkx/logicnode/ProbabilisticIndexNode.hx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								leenkx/Sources/leenkx/logicnode/ProbabilisticIndexNode.hx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,41 @@ | |||||||
|  | package leenkx.logicnode; | ||||||
|  |  | ||||||
|  | class ProbabilisticIndexNode extends LogicNode { | ||||||
|  |  | ||||||
|  | 	public function new(tree: LogicTree) { | ||||||
|  | 		super(tree); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	override function get(from: Int): Dynamic { | ||||||
|  |  | ||||||
|  | 		var probs: Array<Float> = []; | ||||||
|  | 		var probs_acum: Array<Float> = []; | ||||||
|  | 		var sum: Float = 0; | ||||||
|  |  | ||||||
|  | 		for (p in 0...inputs.length){ | ||||||
|  | 			probs.push(inputs[p].get()); | ||||||
|  | 			sum += probs[p]; | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		if (sum > 1){ | ||||||
|  | 			for (p in 0...probs.length) | ||||||
|  | 				probs[p] /= sum; | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		sum = 0; | ||||||
|  | 		for (p in 0...probs.length){ | ||||||
|  | 			sum += probs[p]; | ||||||
|  | 			probs_acum.push(sum); | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		var rand: Float = Math.random(); | ||||||
|  |  | ||||||
|  | 		for (p in 0...probs.length){ | ||||||
|  | 			if (p == 0 && rand <= probs_acum[p]) return p; | ||||||
|  | 			else if (0 < p && p < probs.length-1 && probs_acum[p-1] < rand && rand <= probs_acum[p]) return p; | ||||||
|  | 			else if (p == probs.length-1 && probs_acum[p-1] < rand) return p; | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		return null; | ||||||
|  | 	} | ||||||
|  | } | ||||||
| @ -18,7 +18,6 @@ class ProbabilisticOutputNode extends LogicNode { | |||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		if (sum > 1){ | 		if (sum > 1){ | ||||||
| 			trace(sum); |  | ||||||
| 			for (p in 0...probs.length) | 			for (p in 0...probs.length) | ||||||
| 				probs[p] /= sum; | 				probs[p] /= sum; | ||||||
| 		} | 		} | ||||||
|  | |||||||
| @ -0,0 +1,64 @@ | |||||||
|  | package leenkx.logicnode; | ||||||
|  |  | ||||||
|  | import iron.object.Object; | ||||||
|  |  | ||||||
|  | class RemoveParticleFromObjectNode extends LogicNode { | ||||||
|  |  | ||||||
|  | 	public var property0: String; | ||||||
|  |  | ||||||
|  | 	public function new(tree: LogicTree) { | ||||||
|  | 		super(tree); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	override function run(from: Int) { | ||||||
|  | 		#if lnx_particles | ||||||
|  | 		var object: Object = inputs[1].get(); | ||||||
|  |  | ||||||
|  | 		if (object == null) return; | ||||||
|  |  | ||||||
|  | 		var mo = cast(object, iron.object.MeshObject); | ||||||
|  |  | ||||||
|  | 		if (mo.particleSystems == null) return; | ||||||
|  |  | ||||||
|  | 		if (property0 == 'All'){ | ||||||
|  | 			mo.particleSystems = null; | ||||||
|  | 			for (c in mo.particleChildren) c.remove(); | ||||||
|  | 			mo.particleChildren = null; | ||||||
|  | 			mo.particleOwner = null; | ||||||
|  | 			mo.render_emitter = true; | ||||||
|  | 		}  | ||||||
|  | 		else { | ||||||
|  | 			 | ||||||
|  | 			var slot: Int = -1; | ||||||
|  | 			if (property0 == 'Name'){ | ||||||
|  | 				var name: String = inputs[2].get(); | ||||||
|  | 				for (i => psys in mo.particleSystems){ | ||||||
|  | 					if (@:privateAccess psys.r.name == name){ slot = i; break; } | ||||||
|  | 				} | ||||||
|  | 			}  | ||||||
|  | 			else slot = inputs[2].get(); | ||||||
|  | 				 | ||||||
|  | 			if (mo.particleSystems.length > slot){ | ||||||
|  | 				for (i in slot+1...mo.particleSystems.length){ | ||||||
|  | 					var mi = cast(mo.particleChildren[i], iron.object.MeshObject); | ||||||
|  | 					mi.particleIndex = mi.particleIndex - 1; | ||||||
|  | 				} | ||||||
|  | 				mo.particleSystems.splice(slot, 1); | ||||||
|  | 				mo.particleChildren[slot].remove(); | ||||||
|  | 				mo.particleChildren.splice(slot, 1); | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			if (slot == 0){ | ||||||
|  | 				mo.particleSystems = null; | ||||||
|  | 				mo.particleChildren = null; | ||||||
|  | 				mo.particleOwner = null; | ||||||
|  | 				mo.render_emitter = true; | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		#end | ||||||
|  |  | ||||||
|  | 		runOutput(0); | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										16
									
								
								leenkx/Sources/leenkx/logicnode/ResolutionGetNode.hx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								leenkx/Sources/leenkx/logicnode/ResolutionGetNode.hx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,16 @@ | |||||||
|  | package leenkx.logicnode; | ||||||
|  |  | ||||||
|  | class ResolutionGetNode extends LogicNode { | ||||||
|  |  | ||||||
|  | 	public function new(tree:LogicTree) { | ||||||
|  | 		super(tree); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	override function get(from:Int):Dynamic { | ||||||
|  | 		return switch (from) { | ||||||
|  | 			case 0: leenkx.renderpath.Postprocess.resolution_uniforms[0]; | ||||||
|  | 			case 1: leenkx.renderpath.Postprocess.resolution_uniforms[1]; | ||||||
|  | 			default: 0; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										33
									
								
								leenkx/Sources/leenkx/logicnode/ResolutionSetNode.hx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								leenkx/Sources/leenkx/logicnode/ResolutionSetNode.hx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,33 @@ | |||||||
|  | package leenkx.logicnode; | ||||||
|  |  | ||||||
|  | import kha.graphics4.TextureFilter; | ||||||
|  |  | ||||||
|  | class ResolutionSetNode extends LogicNode { | ||||||
|  |  | ||||||
|  | 	public function new(tree:LogicTree) { | ||||||
|  | 		super(tree); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	override function run(from:Int) { | ||||||
|  |  | ||||||
|  | 		var size: Int = inputs[1].get(); | ||||||
|  | 		var filter: Int = inputs[2].get(); | ||||||
|  |  | ||||||
|  | 	#if rp_resolution_filter  | ||||||
|  | 		if (filter == 0) | ||||||
|  | 			iron.object.Uniforms.defaultFilter = TextureFilter.LinearFilter; | ||||||
|  | 		else | ||||||
|  | 			iron.object.Uniforms.defaultFilter = TextureFilter.PointFilter; | ||||||
|  |  | ||||||
|  | 		leenkx.renderpath.Postprocess.resolution_uniforms[0] = size; | ||||||
|  | 		leenkx.renderpath.Postprocess.resolution_uniforms[1] = filter; | ||||||
|  |  | ||||||
|  | 		var npath = leenkx.renderpath.RenderPathCreator.get(); | ||||||
|  | 		var world = iron.Scene.active.raw.world_ref; | ||||||
|  | 		npath.loadShader("shader_datas/World_" + world + "/World_" + world); | ||||||
|  | 		iron.RenderPath.setActive(npath); | ||||||
|  |     #end | ||||||
|  |  | ||||||
|  | 		runOutput(0); | ||||||
|  | 	} | ||||||
|  | } | ||||||
| @ -20,6 +20,8 @@ class RpConfigNode extends LogicNode { | |||||||
| 			on ? leenkx.data.Config.raw.rp_ssrefr = true : leenkx.data.Config.raw.rp_ssrefr = false; | 			on ? leenkx.data.Config.raw.rp_ssrefr = true : leenkx.data.Config.raw.rp_ssrefr = false; | ||||||
|         case "Bloom": |         case "Bloom": | ||||||
| 			on ? leenkx.data.Config.raw.rp_bloom = true : leenkx.data.Config.raw.rp_bloom = false; | 			on ? leenkx.data.Config.raw.rp_bloom = true : leenkx.data.Config.raw.rp_bloom = false; | ||||||
|  | 		case "CA": | ||||||
|  | 			on ? leenkx.data.Config.raw.rp_chromatic_aberration = true : leenkx.data.Config.raw.rp_chromatic_aberration = false; | ||||||
|         case "GI": |         case "GI": | ||||||
| 			on ? leenkx.data.Config.raw.rp_gi = true : leenkx.data.Config.raw.rp_gi = false; | 			on ? leenkx.data.Config.raw.rp_gi = true : leenkx.data.Config.raw.rp_gi = false; | ||||||
| 		case "Motion Blur": | 		case "Motion Blur": | ||||||
|  | |||||||
							
								
								
									
										23
									
								
								leenkx/Sources/leenkx/logicnode/SetAudioPositionNode.hx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								leenkx/Sources/leenkx/logicnode/SetAudioPositionNode.hx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,23 @@ | |||||||
|  | package leenkx.logicnode; | ||||||
|  |  | ||||||
|  | import aura.Aura; | ||||||
|  | import aura.Types; | ||||||
|  |  | ||||||
|  | class SetAudioPositionNode extends LogicNode { | ||||||
|  |  | ||||||
|  | 	public function new(tree: LogicTree) { | ||||||
|  | 		super(tree); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	override function run(from: Int) { | ||||||
|  | 		var audio = inputs[1].get(); | ||||||
|  | 		if (audio == null) return; | ||||||
|  | 		 | ||||||
|  | 		var positionInSeconds:Float = inputs[2].get(); | ||||||
|  | 		if (positionInSeconds < 0.0) positionInSeconds = 0.0; | ||||||
|  | 		 | ||||||
|  | 		audio.channel.floatPosition = positionInSeconds * audio.channel.sampleRate; | ||||||
|  |  | ||||||
|  | 		runOutput(0); | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										38
									
								
								leenkx/Sources/leenkx/logicnode/SetCameraRenderFilterNode.hx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								leenkx/Sources/leenkx/logicnode/SetCameraRenderFilterNode.hx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,38 @@ | |||||||
|  | package leenkx.logicnode; | ||||||
|  |  | ||||||
|  | import iron.object.MeshObject; | ||||||
|  | import iron.object.CameraObject; | ||||||
|  |  | ||||||
|  | class SetCameraRenderFilterNode extends LogicNode { | ||||||
|  |  | ||||||
|  | 	public var property0: String; | ||||||
|  |  | ||||||
|  | 	public function new(tree: LogicTree) { | ||||||
|  | 		super(tree); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	override function run(from: Int) { | ||||||
|  | 		var mo: MeshObject = cast inputs[1].get(); | ||||||
|  | 		var camera: CameraObject = inputs[2].get(); | ||||||
|  |  | ||||||
|  | 		assert(Error, Std.isOfType(camera, CameraObject), "Camera must be a camera object!"); | ||||||
|  |  | ||||||
|  | 		if (camera == null || mo == null) return; | ||||||
|  |  | ||||||
|  | 		if (property0 == 'Add'){ | ||||||
|  | 			if (mo.cameraList == null || mo.cameraList.indexOf(camera.name) == -1){ | ||||||
|  | 				if (mo.cameraList == null) mo.cameraList = []; | ||||||
|  | 				mo.cameraList.push(camera.name); | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		else{ | ||||||
|  | 			if (mo.cameraList != null){ | ||||||
|  | 				mo.cameraList.remove(camera.name); | ||||||
|  | 				if (mo.cameraList.length == 0) | ||||||
|  | 					mo.cameraList = null; | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		runOutput(0); | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										21
									
								
								leenkx/Sources/leenkx/logicnode/SetLightShadowNode.hx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								leenkx/Sources/leenkx/logicnode/SetLightShadowNode.hx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,21 @@ | |||||||
|  | package leenkx.logicnode; | ||||||
|  |  | ||||||
|  | import iron.object.LightObject; | ||||||
|  |  | ||||||
|  | class SetLightShadowNode extends LogicNode { | ||||||
|  |  | ||||||
|  | 	public function new(tree: LogicTree) { | ||||||
|  | 		super(tree); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	override function run(from: Int) { | ||||||
|  | 		var light: LightObject = inputs[1].get(); | ||||||
|  | 		var shadow: Bool = inputs[2].get(); | ||||||
|  |  | ||||||
|  | 		if (light == null) return; | ||||||
|  |  | ||||||
|  | 		light.data.raw.cast_shadow = shadow; | ||||||
|  |  | ||||||
|  | 		runOutput(0); | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										206
									
								
								leenkx/Sources/leenkx/logicnode/SetLookAtRotationNode.hx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										206
									
								
								leenkx/Sources/leenkx/logicnode/SetLookAtRotationNode.hx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,206 @@ | |||||||
|  | package leenkx.logicnode; | ||||||
|  |  | ||||||
|  | import iron.math.Vec4; | ||||||
|  | import iron.math.Quat; | ||||||
|  | import iron.math.Mat4; | ||||||
|  | import iron.object.Object; | ||||||
|  |  | ||||||
|  | class SetLookAtRotationNode extends LogicNode { | ||||||
|  |  | ||||||
|  | 	public var property0: String; // Axis to align | ||||||
|  | 	public var property1: String; // Use vector for target (true/false) | ||||||
|  | 	public var property2: String; // Use vector for source (true/false) | ||||||
|  | 	public var property3: String; // Damping value (backward compatibility, now input socket) | ||||||
|  | 	public var property4: String; // Disable rotation on aligning axis (true/false) | ||||||
|  | 	public var property5: String; // Use local space (true/false) | ||||||
|  | 	 | ||||||
|  | 	// Store the calculated rotation for output | ||||||
|  | 	var calculatedRotation: Quat = null; | ||||||
|  | 	// Store the previous rotation for smooth interpolation | ||||||
|  | 	var previousRotation: Quat = null; | ||||||
|  |  | ||||||
|  | 	public function new(tree: LogicTree) { | ||||||
|  | 		super(tree); | ||||||
|  | 		previousRotation = new Quat(); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	override function run(from: Int): Void { | ||||||
|  | 		// Determine if we're using a vector or an object as source | ||||||
|  | 		var useSourceVector: Bool = property2 == "true"; | ||||||
|  | 		var objectToUse: Object = null; | ||||||
|  | 		var objectLoc: Vec4 = null; | ||||||
|  |  | ||||||
|  | 		if (useSourceVector) { | ||||||
|  | 			// Use tree.object as the object to rotate | ||||||
|  | 			objectToUse = tree.object; | ||||||
|  | 			if (objectToUse == null) { | ||||||
|  | 				runOutput(0); | ||||||
|  | 				return; | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			// Get the source location directly | ||||||
|  | 			objectLoc = inputs[1].get(); | ||||||
|  | 			if (objectLoc == null) { | ||||||
|  | 				runOutput(0); | ||||||
|  | 				return; | ||||||
|  | 			} | ||||||
|  | 		} else { | ||||||
|  | 			// Get the source object (or fallback to tree.object) | ||||||
|  | 			objectToUse = (inputs.length > 1 && inputs[1] != null) ? inputs[1].get() : tree.object; | ||||||
|  | 			if (objectToUse == null) { | ||||||
|  | 				runOutput(0); | ||||||
|  | 				return; | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			// Get source object's WORLD position (important for child objects) | ||||||
|  | 			objectLoc = new Vec4(objectToUse.transform.worldx(), objectToUse.transform.worldy(), objectToUse.transform.worldz()); | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		// Determine if we're using a vector or an object as target | ||||||
|  | 		var useTargetVector: Bool = property1 == "true"; | ||||||
|  | 		var targetLoc: Vec4 = null; | ||||||
|  |  | ||||||
|  | 		if (useTargetVector) { | ||||||
|  | 			// Get the target location directly | ||||||
|  | 			targetLoc = inputs[2].get(); | ||||||
|  | 			if (targetLoc == null) { | ||||||
|  | 				runOutput(0); | ||||||
|  | 				return; | ||||||
|  | 			} | ||||||
|  | 		} else { | ||||||
|  | 			// Get the target object | ||||||
|  | 			var targetObject: Object = inputs[2].get(); | ||||||
|  | 			if (targetObject == null) { | ||||||
|  | 				runOutput(0); | ||||||
|  | 				return; | ||||||
|  | 			} | ||||||
|  | 			 | ||||||
|  | 			// Get target object's WORLD position (important for child objects) | ||||||
|  | 			targetLoc = new Vec4(targetObject.transform.worldx(), targetObject.transform.worldy(), targetObject.transform.worldz()); | ||||||
|  | 		} | ||||||
|  | 		 | ||||||
|  | 		// Calculate direction to target | ||||||
|  | 		var direction = new Vec4( | ||||||
|  | 			targetLoc.x - objectLoc.x, | ||||||
|  | 			targetLoc.y - objectLoc.y, | ||||||
|  | 			targetLoc.z - objectLoc.z | ||||||
|  | 		); | ||||||
|  | 		direction.normalize(); | ||||||
|  | 		 | ||||||
|  | 		// Calculate target rotation based on selected axis | ||||||
|  | 		calculatedRotation = new Quat(); | ||||||
|  | 		switch (property0) { | ||||||
|  | 			case "X": | ||||||
|  | 				calculatedRotation.fromTo(new Vec4(1, 0, 0), direction); | ||||||
|  | 			case "-X": | ||||||
|  | 				calculatedRotation.fromTo(new Vec4(-1, 0, 0), direction); | ||||||
|  | 			case "Y": | ||||||
|  | 				calculatedRotation.fromTo(new Vec4(0, 1, 0), direction); | ||||||
|  | 			case "-Y": | ||||||
|  | 				calculatedRotation.fromTo(new Vec4(0, -1, 0), direction); | ||||||
|  | 			case "Z": | ||||||
|  | 				calculatedRotation.fromTo(new Vec4(0, 0, 1), direction); | ||||||
|  | 			case "-Z": | ||||||
|  | 				calculatedRotation.fromTo(new Vec4(0, 0, -1), direction); | ||||||
|  | 		} | ||||||
|  | 		 | ||||||
|  | 		// If disable rotation on aligning axis is enabled, constrain the target rotation | ||||||
|  | 		if (property4 == "true") { | ||||||
|  | 			// Apply constraint to the target rotation BEFORE damping to avoid jiggling | ||||||
|  | 			var eulerAngles = calculatedRotation.toEulerOrdered("XYZ"); | ||||||
|  | 			 | ||||||
|  | 			// Set the rotation around the selected axis to 0 | ||||||
|  | 			switch (property0) { | ||||||
|  | 				case "X", "-X": | ||||||
|  | 					eulerAngles.x = 0.0; | ||||||
|  | 				case "Y", "-Y": | ||||||
|  | 					eulerAngles.y = 0.0; | ||||||
|  | 				case "Z", "-Z": | ||||||
|  | 					eulerAngles.z = 0.0; | ||||||
|  | 			} | ||||||
|  | 			 | ||||||
|  | 			// Convert back to quaternion | ||||||
|  | 			calculatedRotation.fromEulerOrdered(eulerAngles, "XYZ"); | ||||||
|  | 		} | ||||||
|  | 		 | ||||||
|  | 		// Convert world rotation to local rotation if local space is enabled and object has a parent | ||||||
|  | 		var targetRotation = new Quat(); | ||||||
|  | 		if (property5 == "true" && objectToUse.parent != null) { | ||||||
|  | 			// Get parent's world rotation | ||||||
|  | 			var parentWorldLoc = new Vec4(); | ||||||
|  | 			var parentWorldRot = new Quat(); | ||||||
|  | 			var parentWorldScale = new Vec4(); | ||||||
|  | 			objectToUse.parent.transform.world.decompose(parentWorldLoc, parentWorldRot, parentWorldScale); | ||||||
|  | 			 | ||||||
|  | 			// Convert world rotation to local space by removing parent's rotation influence | ||||||
|  | 			// local_rotation = inverse(parent_world_rotation) * world_rotation | ||||||
|  | 			var invParentRot = new Quat().setFrom(parentWorldRot); | ||||||
|  | 			invParentRot.x = -invParentRot.x; | ||||||
|  | 			invParentRot.y = -invParentRot.y; | ||||||
|  | 			invParentRot.z = -invParentRot.z; | ||||||
|  | 			 | ||||||
|  | 			targetRotation.multquats(invParentRot, calculatedRotation); | ||||||
|  | 		} else { | ||||||
|  | 			// No local space conversion needed, use world rotation directly | ||||||
|  | 			targetRotation.setFrom(calculatedRotation); | ||||||
|  | 		} | ||||||
|  | 		 | ||||||
|  | 		// Apply rotation with damping | ||||||
|  | 		var dampingValue: Float = 0.0; | ||||||
|  | 		 | ||||||
|  | 		// Try to get damping from input socket first (index 3), fallback to property | ||||||
|  | 		if (inputs.length > 3 && inputs[3] != null) { | ||||||
|  | 			var dampingInput: Dynamic = inputs[3].get(); | ||||||
|  | 			if (dampingInput != null) { | ||||||
|  | 				dampingValue = dampingInput; | ||||||
|  | 			} | ||||||
|  | 		} else { | ||||||
|  | 			// Fallback to property for backward compatibility | ||||||
|  | 			dampingValue = Std.parseFloat(property3); | ||||||
|  | 		} | ||||||
|  | 		 | ||||||
|  | 		if (dampingValue > 0.0) { | ||||||
|  | 			// Create a fixed interpolation rate that never reaches exactly 1.0 | ||||||
|  | 			// Higher damping = slower rotation (smaller step) | ||||||
|  | 			var step = Math.max(0.001, (1.0 - dampingValue) * 0.2); // 0.001 to 0.2 range | ||||||
|  | 			 | ||||||
|  | 			// Get current local rotation as quaternion | ||||||
|  | 			var currentLocalRot = new Quat().setFrom(objectToUse.transform.rot); | ||||||
|  | 			 | ||||||
|  | 			// Calculate the difference between current and target rotation | ||||||
|  | 			var diffQuat = new Quat(); | ||||||
|  | 			// q1 * inverse(q2) gives the rotation from q2 to q1 | ||||||
|  | 			var invCurrent = new Quat().setFrom(currentLocalRot); | ||||||
|  | 			invCurrent.x = -invCurrent.x; | ||||||
|  | 			invCurrent.y = -invCurrent.y; | ||||||
|  | 			invCurrent.z = -invCurrent.z; | ||||||
|  | 			diffQuat.multquats(targetRotation, invCurrent); | ||||||
|  | 			 | ||||||
|  | 			// Convert to axis-angle representation | ||||||
|  | 			var axis = new Vec4(); | ||||||
|  | 			var angle = diffQuat.toAxisAngle(axis); | ||||||
|  | 			 | ||||||
|  | 			// Apply only a portion of this rotation (step) | ||||||
|  | 			var partialAngle = angle * step; | ||||||
|  | 			 | ||||||
|  | 			// Create partial rotation quaternion | ||||||
|  | 			var partialRot = new Quat().fromAxisAngle(axis, partialAngle); | ||||||
|  | 			 | ||||||
|  | 			// Apply this partial rotation to current local rotation | ||||||
|  | 			var newLocalRot = new Quat(); | ||||||
|  | 			newLocalRot.multquats(partialRot, currentLocalRot); | ||||||
|  | 			 | ||||||
|  | 			// Apply the new local rotation | ||||||
|  | 			objectToUse.transform.rot.setFrom(newLocalRot); | ||||||
|  | 		} else { | ||||||
|  | 			// No damping, apply instant rotation | ||||||
|  | 			objectToUse.transform.rot.setFrom(targetRotation); | ||||||
|  | 		} | ||||||
|  | 		 | ||||||
|  | 		objectToUse.transform.buildMatrix(); | ||||||
|  | 		 | ||||||
|  | 		runOutput(0); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// No output sockets needed - this node only performs actions | ||||||
|  | } | ||||||
| @ -0,0 +1,55 @@ | |||||||
|  | package leenkx.logicnode; | ||||||
|  |  | ||||||
|  | import iron.object.MeshObject; | ||||||
|  | import iron.data.MaterialData; | ||||||
|  |  | ||||||
|  | class SetMaterialTextureFilterNode extends LogicNode { | ||||||
|  |  | ||||||
|  | 	public function new(tree: LogicTree) { | ||||||
|  | 		super(tree); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	override function run(from: Int) { | ||||||
|  | 		var object: MeshObject = inputs[1].get(); | ||||||
|  | 		var mat: MaterialData = inputs[2].get(); | ||||||
|  | 		var slot: Int = inputs[3].get(); | ||||||
|  | 		var name: String = inputs[4].get(); | ||||||
|  | 		var filter: Int = inputs[5].get(); | ||||||
|  |  | ||||||
|  | 		if (object == null) return; | ||||||
|  | 		if (slot >= object.materials.length) return; | ||||||
|  |  | ||||||
|  | 		var mo = cast(object, iron.object.MeshObject); | ||||||
|  |  | ||||||
|  | 		for (i => node in mo.materials[slot].contexts[0].raw.bind_textures) | ||||||
|  | 			if (node.name == name){ | ||||||
|  | 				var moImgt = mo.materials[slot].contexts[0].raw.bind_textures[i]; | ||||||
|  | 				switch(filter){ | ||||||
|  | 					case 0: //Linear | ||||||
|  | 						moImgt.min_filter = null; | ||||||
|  | 						moImgt.mag_filter = null; | ||||||
|  | 						moImgt.mipmap_filter = null; | ||||||
|  | 						moImgt.generate_mipmaps = null; | ||||||
|  | 					case 1: //Closest | ||||||
|  | 						moImgt.min_filter = 'point'; | ||||||
|  | 						moImgt.mag_filter = 'point'; | ||||||
|  | 						moImgt.mipmap_filter = null; | ||||||
|  | 						moImgt.generate_mipmaps = null; | ||||||
|  | 					case 2: //Cubic | ||||||
|  | 						moImgt.min_filter = null; | ||||||
|  | 						moImgt.mag_filter = null; | ||||||
|  | 						moImgt.mipmap_filter = 'linear'; | ||||||
|  | 						moImgt.generate_mipmaps = true; | ||||||
|  | 					case 3: //Smart | ||||||
|  | 						moImgt.min_filter = 'anisotropic'; | ||||||
|  | 						moImgt.mag_filter = null; | ||||||
|  | 						moImgt.mipmap_filter = 'linear'; | ||||||
|  | 						moImgt.generate_mipmaps = true; | ||||||
|  | 				} | ||||||
|  |  | ||||||
|  | 				break; | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 		runOutput(0); | ||||||
|  | 	} | ||||||
|  | } | ||||||
| @ -0,0 +1,74 @@ | |||||||
|  | package leenkx.logicnode; | ||||||
|  |  | ||||||
|  | import iron.object.Object; | ||||||
|  | import iron.math.Vec4; | ||||||
|  | import iron.math.Mat4; | ||||||
|  | import iron.system.Time; | ||||||
|  |  | ||||||
|  | class SetObjectDelayedLocationNode extends LogicNode { | ||||||
|  | 	public var use_local_space: Bool = false; | ||||||
|  |  | ||||||
|  | 	private var initialOffset: Vec4 = null; | ||||||
|  | 	private var targetPos: Vec4 = new Vec4(); | ||||||
|  | 	private var currentPos: Vec4 = new Vec4(); | ||||||
|  | 	private var deltaVec: Vec4 = new Vec4(); | ||||||
|  | 	private var tempVec: Vec4 = new Vec4(); | ||||||
|  | 	 | ||||||
|  | 	private var lastParent: Object = null; | ||||||
|  | 	private var invParentMatrix: Mat4 = null; | ||||||
|  | 	 | ||||||
|  | 	public function new(tree: LogicTree) { | ||||||
|  | 		super(tree); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	override function run(from: Int) { | ||||||
|  | 		var follower: Object = inputs[1].get(); | ||||||
|  | 		var target: Object = inputs[2].get(); | ||||||
|  | 		var delay: Float = inputs[3].get(); | ||||||
|  | 		 | ||||||
|  | 		if (follower == null || target == null) return runOutput(0); | ||||||
|  | 		 | ||||||
|  | 		if (initialOffset == null) { | ||||||
|  | 			initialOffset = new Vec4(); | ||||||
|  | 			var followerPos = follower.transform.world.getLoc(); | ||||||
|  | 			var targetPos = target.transform.world.getLoc(); | ||||||
|  | 			initialOffset.setFrom(followerPos); | ||||||
|  | 			initialOffset.sub(targetPos); | ||||||
|  | 		} | ||||||
|  | 		 | ||||||
|  | 		targetPos.setFrom(target.transform.world.getLoc()); | ||||||
|  | 		currentPos.setFrom(follower.transform.world.getLoc()); | ||||||
|  | 		 | ||||||
|  | 		tempVec.setFrom(targetPos).add(initialOffset); | ||||||
|  | 		 | ||||||
|  | 		deltaVec.setFrom(tempVec).sub(currentPos); | ||||||
|  |  | ||||||
|  | 		if (deltaVec.length() < 0.001 && delay < 0.01) { | ||||||
|  | 			runOutput(0); | ||||||
|  | 			return; | ||||||
|  | 		} | ||||||
|  | 		 | ||||||
|  | 		if (delay == 0.0) { | ||||||
|  | 			currentPos.setFrom(tempVec); | ||||||
|  | 		} else { | ||||||
|  | 			var smoothFactor = Math.exp(-Time.delta / Math.max(0.0001, delay)); | ||||||
|  | 			currentPos.x = tempVec.x + (currentPos.x - tempVec.x) * smoothFactor; | ||||||
|  | 			currentPos.y = tempVec.y + (currentPos.y - tempVec.y) * smoothFactor; | ||||||
|  | 			currentPos.z = tempVec.z + (currentPos.z - tempVec.z) * smoothFactor; | ||||||
|  | 		} | ||||||
|  | 		if (use_local_space && follower.parent != null) { | ||||||
|  | 			if (follower.parent != lastParent || invParentMatrix == null) { | ||||||
|  | 				lastParent = follower.parent; | ||||||
|  | 				invParentMatrix = Mat4.identity(); | ||||||
|  | 				invParentMatrix.getInverse(follower.parent.transform.world); | ||||||
|  | 			} | ||||||
|  | 			tempVec.setFrom(currentPos); | ||||||
|  | 			tempVec.applymat(invParentMatrix);  | ||||||
|  | 			follower.transform.loc.set(tempVec.x, tempVec.y, tempVec.z); | ||||||
|  | 		} else { | ||||||
|  | 			follower.transform.loc.set(currentPos.x, currentPos.y, currentPos.z); | ||||||
|  | 		} | ||||||
|  | 		follower.transform.buildMatrix(); | ||||||
|  | 		runOutput(0); | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										81
									
								
								leenkx/Sources/leenkx/logicnode/SetParticleDataNode.hx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										81
									
								
								leenkx/Sources/leenkx/logicnode/SetParticleDataNode.hx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,81 @@ | |||||||
|  | package leenkx.logicnode; | ||||||
|  |  | ||||||
|  | import iron.object.Object; | ||||||
|  |  | ||||||
|  | class SetParticleDataNode extends LogicNode { | ||||||
|  |  | ||||||
|  | 	public var property0: String; | ||||||
|  |  | ||||||
|  | 	public function new(tree: LogicTree) { | ||||||
|  | 		super(tree); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	override function run(from: Int) { | ||||||
|  | 		#if lnx_particles | ||||||
|  | 		var object: Object = inputs[1].get(); | ||||||
|  | 		var slot: Int = inputs[2].get(); | ||||||
|  |  | ||||||
|  | 		if (object == null) return; | ||||||
|  |  | ||||||
|  | 		var mo = cast(object, iron.object.MeshObject); | ||||||
|  |  | ||||||
|  | 		var psys = mo.particleSystems != null ? mo.particleSystems[slot] :  | ||||||
|  | 			mo.particleOwner != null && mo.particleOwner.particleSystems != null ? mo.particleOwner.particleSystems[slot] : null;		if (psys == null) return; | ||||||
|  |  | ||||||
|  | 		switch (property0) { | ||||||
|  | 			case 'Particle Size': | ||||||
|  | 				@:privateAccess psys.r.particle_size = inputs[3].get(); | ||||||
|  | 			case 'Frame Start': | ||||||
|  | 				@:privateAccess psys.r.frame_start = inputs[3].get(); | ||||||
|  | 				@:privateAccess psys.animtime = (@:privateAccess psys.r.frame_end - @:privateAccess psys.r.frame_start) / @:privateAccess psys.frameRate; | ||||||
|  | 				@:privateAccess psys.spawnRate = ((@:privateAccess psys.r.frame_end - @:privateAccess psys.r.frame_start) / @:privateAccess psys.count) / @:privateAccess psys.frameRate; | ||||||
|  | 			case 'Frame End': | ||||||
|  | 				@:privateAccess psys.r.frame_end = inputs[3].get(); | ||||||
|  | 				@:privateAccess psys.animtime = (@:privateAccess psys.r.frame_end - @:privateAccess psys.r.frame_start) / @:privateAccess psys.frameRate; | ||||||
|  | 				@:privateAccess psys.spawnRate = ((@:privateAccess psys.r.frame_end - @:privateAccess psys.r.frame_start) / @:privateAccess psys.count) / @:privateAccess psys.frameRate; | ||||||
|  | 			case 'Lifetime': | ||||||
|  | 				@:privateAccess psys.lifetime = inputs[3].get() / @:privateAccess psys.frameRate; | ||||||
|  | 			case 'Lifetime Random': | ||||||
|  | 				@:privateAccess psys.r.lifetime_random = inputs[3].get(); | ||||||
|  | 			case 'Emit From': | ||||||
|  | 				var emit_from: Int = inputs[3].get(); | ||||||
|  | 				if (emit_from == 0 || emit_from == 1 || emit_from == 2) { | ||||||
|  | 					@:privateAccess psys.r.emit_from = emit_from; | ||||||
|  | 					@:privateAccess psys.setupGeomGpu(mo.particleChildren != null ? mo.particleChildren[slot] : cast(iron.Scene.active.getChild(@:privateAccess psys.data.raw.instance_object), iron.object.MeshObject), mo); | ||||||
|  | 				} | ||||||
|  | 			case 'Auto Start': | ||||||
|  | 				@:privateAccess psys.r.auto_start = inputs[3].get();  | ||||||
|  | 			case 'Is Unique': | ||||||
|  | 				@:privateAccess psys.r.is_unique = inputs[3].get(); | ||||||
|  | 			case 'Loop': | ||||||
|  | 				@:privateAccess psys.r.loop = inputs[3].get(); | ||||||
|  | 			case 'Velocity': | ||||||
|  | 				var vel: iron.math.Vec3 = inputs[3].get(); | ||||||
|  | 				@:privateAccess psys.alignx = vel.x; | ||||||
|  | 				@:privateAccess psys.aligny = vel.y; | ||||||
|  | 				@:privateAccess psys.alignz = vel.z; | ||||||
|  | 			case 'Velocity Random': | ||||||
|  | 				@:privateAccess psys.r.factor_random = inputs[3].get(); | ||||||
|  | 			case 'Weight Gravity': | ||||||
|  | 				@:privateAccess psys.r.weight_gravity = inputs[3].get(); | ||||||
|  | 				if (iron.Scene.active.raw.gravity != null) { | ||||||
|  | 					@:privateAccess psys.gx = iron.Scene.active.raw.gravity[0] * @:privateAccess psys.r.weight_gravity; | ||||||
|  | 					@:privateAccess psys.gy = iron.Scene.active.raw.gravity[1] * @:privateAccess psys.r.weight_gravity; | ||||||
|  | 					@:privateAccess psys.gz = iron.Scene.active.raw.gravity[2] * @:privateAccess psys.r.weight_gravity; | ||||||
|  | 				} | ||||||
|  | 				else { | ||||||
|  | 					@:privateAccess psys.gx = 0; | ||||||
|  | 					@:privateAccess psys.gy = 0; | ||||||
|  | 					@:privateAccess psys.gz = -9.81 * @:privateAccess psys.r.weight_gravity; | ||||||
|  | 				} | ||||||
|  | 			case 'Speed': | ||||||
|  | 				psys.speed = inputs[3].get(); | ||||||
|  | 			default:  | ||||||
|  | 				null; | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		#end | ||||||
|  |  | ||||||
|  | 		runOutput(0); | ||||||
|  | 	} | ||||||
|  | } | ||||||
| @ -0,0 +1,23 @@ | |||||||
|  | package leenkx.logicnode; | ||||||
|  |  | ||||||
|  | import iron.object.Object; | ||||||
|  |  | ||||||
|  | class SetParticleRenderEmitterNode extends LogicNode { | ||||||
|  |  | ||||||
|  | 	public function new(tree: LogicTree) { | ||||||
|  | 		super(tree); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	override function run(from: Int) { | ||||||
|  | 		#if lnx_particles | ||||||
|  | 		var object: Object = inputs[1].get(); | ||||||
|  |  | ||||||
|  | 		if (object == null) return; | ||||||
|  |  | ||||||
|  | 		cast(object, iron.object.MeshObject).render_emitter = inputs[2].get(); | ||||||
|  |  | ||||||
|  | 		#end | ||||||
|  |  | ||||||
|  | 		runOutput(0); | ||||||
|  | 	} | ||||||
|  | } | ||||||
| @ -11,13 +11,16 @@ class SetParticleSpeedNode extends LogicNode { | |||||||
| 	override function run(from: Int) { | 	override function run(from: Int) { | ||||||
| 		#if lnx_particles | 		#if lnx_particles | ||||||
| 		var object: Object = inputs[1].get(); | 		var object: Object = inputs[1].get(); | ||||||
| 		var speed: Float = inputs[2].get(); | 		var slot: Int = inputs[2].get(); | ||||||
|  | 		var speed: Float = inputs[3].get(); | ||||||
|  |  | ||||||
| 		if (object == null) return; | 		if (object == null) return; | ||||||
|  |  | ||||||
| 		var mo = cast(object, iron.object.MeshObject); | 		var mo = cast(object, iron.object.MeshObject); | ||||||
| 		var psys = mo.particleSystems.length > 0 ? mo.particleSystems[0] : null; | 		var psys = mo.particleSystems != null ? mo.particleSystems[slot] :  | ||||||
| 		if (psys == null) mo.particleOwner.particleSystems[0]; | 			mo.particleOwner != null && mo.particleOwner.particleSystems != null ? mo.particleOwner.particleSystems[slot] : null; | ||||||
|  |  | ||||||
|  | 		if (psys == null) return; | ||||||
|  |  | ||||||
| 		psys.speed = speed; | 		psys.speed = speed; | ||||||
|  |  | ||||||
|  | |||||||
							
								
								
									
										39
									
								
								leenkx/Sources/leenkx/logicnode/SetPositionSpeakerNode.hx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								leenkx/Sources/leenkx/logicnode/SetPositionSpeakerNode.hx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,39 @@ | |||||||
|  | package leenkx.logicnode; | ||||||
|  |  | ||||||
|  | #if lnx_audio | ||||||
|  | import iron.object.SpeakerObject; | ||||||
|  | import kha.audio1.AudioChannel; | ||||||
|  | import iron.system.Audio; | ||||||
|  | #end | ||||||
|  |  | ||||||
|  | class SetPositionSpeakerNode extends LogicNode { | ||||||
|  |  | ||||||
|  | 	public function new(tree: LogicTree) { | ||||||
|  | 		super(tree); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	override function run(from: Int) { | ||||||
|  | 		#if lnx_audio | ||||||
|  | 		var object: SpeakerObject = cast(inputs[1].get(), SpeakerObject); | ||||||
|  | 		if (object == null || object.sound == null) return; | ||||||
|  | 		 | ||||||
|  | 		var positionInSeconds:Float = inputs[2].get(); | ||||||
|  | 		if (positionInSeconds < 0) positionInSeconds = 0;  | ||||||
|  |  | ||||||
|  | 		var volume = object.data.volume; | ||||||
|  | 		var loop = object.data.loop; | ||||||
|  | 		var stream = object.data.stream; | ||||||
|  | 		 | ||||||
|  | 		object.stop(); | ||||||
|  |  | ||||||
|  | 		var channel = Audio.play(object.sound, loop, stream); | ||||||
|  | 		if (channel != null) { | ||||||
|  | 			object.channels.push(channel); | ||||||
|  | 			channel.volume = volume; | ||||||
|  | 			@:privateAccess channel.set_position(positionInSeconds); | ||||||
|  | 		} | ||||||
|  | 		 | ||||||
|  | 		#end | ||||||
|  | 		runOutput(0); | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										23
									
								
								leenkx/Sources/leenkx/logicnode/SetWorldNode.hx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								leenkx/Sources/leenkx/logicnode/SetWorldNode.hx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,23 @@ | |||||||
|  | package leenkx.logicnode; | ||||||
|  |  | ||||||
|  | import iron.data.SceneFormat; | ||||||
|  |  | ||||||
|  | class SetWorldNode extends LogicNode { | ||||||
|  |  | ||||||
|  | 	public function new(tree: LogicTree) { | ||||||
|  | 		super(tree); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	override function run(from: Int) { | ||||||
|  | 		var world: String = inputs[1].get(); | ||||||
|  |  | ||||||
|  | 		if (world != null){ | ||||||
|  | 			iron.Scene.active.raw.world_ref = world; | ||||||
|  | 			var npath = leenkx.renderpath.RenderPathCreator.get(); | ||||||
|  | 			npath.loadShader("shader_datas/World_" + world + "/World_" + world); | ||||||
|  | 			iron.RenderPath.setActive(npath); | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		runOutput(0); | ||||||
|  | 	} | ||||||
|  | } | ||||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user