forked from LeenkxTeam/LNXSDK
		
	Compare commits
	
		
			377 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 48f5575e4e | |||
| f2c4be6336 | |||
| 2ddc938db8 | |||
| 5eb735ada2 | |||
| 9894cc20f2 | |||
| dbe6d0829a | |||
| 6f383e2ab2 | |||
| 5c2d29d7ce | |||
| 28579e14d7 | |||
| 2ec6f43cc5 | |||
| 027021815a | |||
| b9b387803f | |||
| e05d9d0237 | |||
| c908e6cad2 | |||
| 506a0a0245 | |||
| 5cf33724e4 | |||
| ac5aa3d19c | |||
| 0c534ee632 | |||
| e3e7855d26 | |||
| f7917974f8 | |||
| fa2d8f05d5 | |||
| 73fcb55acc | |||
| c24baa3364 | |||
| 4517c4863f | |||
| 1299306e09 | |||
| f97d8fd846 | |||
| 8f8d4b1376 | |||
| a926fa8dbb | |||
| 6c3efa6c83 | |||
| 21afad6d09 | |||
| 04c6983a09 | |||
| 45966ef0bb | |||
| a72edc6203 | |||
| 6af1ef2df1 | |||
| 46e3047877 | |||
| de74af215a | |||
| b6e96553c2 | |||
| 58e009f709 | |||
| e88f101ca6 | |||
| d28d59b9e6 | |||
| a4398c7279 | |||
| abedfd799e | |||
| 4520422f6b | |||
| 88418c06c3 | |||
| aedc2783ab | |||
| 1505414c4c | |||
| fa818602c4 | |||
| 79dc458671 | |||
| 8e635fb1e9 | |||
| 4c2e6ab26a | |||
| 2371e3777e | |||
| b458b77e5c | |||
| 9b76f8cca9 | |||
| 5f2acb209e | |||
| 6fc446e7a9 | |||
| 71e57026e1 | |||
| 5288a98440 | |||
| 35e346be39 | |||
| 843ef0b058 | |||
| 177890bf39 | |||
| 9ac37e6dc7 | |||
| e697437778 | |||
| c94fc0fd97 | |||
| cd0a6f6788 | |||
| 4400e0e9c8 | |||
| 20cf07cfc3 | |||
| 1939f19c05 | |||
| 0d2b152ccb | |||
| 7f58e0fc85 | |||
| 0e4a6575c7 | |||
| 024676f43a | |||
| 8fe758862c | |||
| 1f3d1b47ae | |||
| f659a3c2be | |||
| 6eeb9017d4 | |||
| afe89c3834 | |||
| 8b695f72bb | |||
| 3d99fa60c0 | |||
| 43be7729ba | |||
| de0b1075c2 | |||
| c7aba23fa4 | |||
| 881f3267cc | |||
| 19b79d61c7 | |||
| fcbab54a0c | |||
| 8fd05d5514 | |||
| ad4013ed75 | |||
| 8ac567b57b | |||
| 43b7ae7060 | |||
| 29e9e71a6a | |||
| bfb85b0a3b | |||
| ef99b800e0 | |||
| 7cca955fc5 | |||
| 7e7bbd5eae | |||
| c31b2a18ad | |||
| fb47bf2564 | |||
| 7ae6750620 | |||
| 5b87010f76 | |||
| 97e952fc15 | |||
| b440539d65 | |||
| 60a9db6459 | |||
| 3b5a93c92a | |||
| 4af990796e | |||
| 9fb4916c3c | |||
| f61d5833bb | |||
| 40b52be713 | |||
| 07d8422f22 | |||
| 7179d42b27 | |||
| 99a5d7d445 | |||
| 5e0acd3d5d | |||
| f4077e461b | |||
| 28943f1522 | |||
| df4feac132 | |||
| 82412dbf81 | |||
| 6afc209db7 | |||
| e9aae53be9 | |||
| a65675ef75 | |||
| 8f073c5ae1 | |||
| 08d08e42d9 | |||
| a1ee335c68 | |||
| de6bf8a08a | |||
| b9848cd2dc | |||
| e922cc38e6 | |||
| 57f0e937d0 | |||
| e234c8615c | |||
| e594518e57 | |||
| a41be0f436 | |||
| 1306033b36 | |||
| eee0011cdd | |||
| 315ac0bd16 | |||
| f289e6f89c | |||
| 700d236bf1 | |||
| f228eab8d3 | |||
| 863d884b76 | |||
| 34e0f5a282 | |||
| 45e2e52008 | |||
| 444a215e63 | |||
| fb2d2a1a7c | |||
| f88c04abea | |||
| 6fdd4b3f70 | |||
| a389c27d75 | |||
| 1909c3da9f | |||
| 5824bd91aa | |||
| 43fe559eef | |||
| 12c09545ce | |||
| 0e60951ec9 | |||
| ccb8b358d3 | |||
| 1a8586777b | |||
| 3721c774a1 | |||
| a58fba408d | |||
| 268fba6cd5 | |||
| 4ab14ce6c8 | |||
| 9023e8d1da | |||
| b58c7a9632 | |||
| 99b70622f5 | |||
| 647b73b746 | |||
| 935c30ec08 | |||
| 0b0d597f89 | |||
| d5878afb30 | |||
| 96b55a1a56 | |||
| 91b3072305 | |||
| 1d0b338d92 | |||
| 8e83c0d0d0 | |||
| 927baec4df | |||
| f5c9e70d1a | |||
| 0423a735fc | |||
| bd413917fc | |||
| 852377f60d | |||
| e17e9a8e35 | |||
| 4055c979a1 | |||
| 06b003ecdb | |||
| fd7f215bb2 | |||
| 6a1df9ec46 | |||
| deccac3c46 | |||
| 432b0210b2 | |||
| 14cf5cebed | |||
| 2307e1504f | |||
| 1ebfecb644 | |||
| 175b575b23 | |||
| 63943a9cbf | |||
| 224d9be76f | |||
| 62d3c8757b | |||
| 3785f93573 | |||
| ffb276745f | |||
| d1c9258da5 | |||
| 3e0cd2be35 | |||
| 1fd1973470 | |||
| a01c72ef76 | |||
| 4b01a562c9 | |||
| 6972d9abc4 | |||
| 63c6b9d98b | |||
| 313d24bbc8 | |||
| 6d2812306d | |||
| e84d6ada84 | |||
| 5057f2b946 | |||
| 2715fe3398 | |||
| 7cb8b8a2d2 | |||
| cd606009e0 | |||
| 965162b101 | |||
| c61a57bfb3 | |||
| cdc425fbcb | |||
| 846bb28c86 | |||
| 7fabd77ef8 | |||
| fb1a5c88bf | |||
| e05c83a8bb | |||
| ee4f62e881 | |||
| 59f8dff22f | |||
| 5572226ac5 | |||
| d04874e0b3 | |||
| ef8b3a99ab | |||
| 1d3254a237 | |||
| 188af4a50f | |||
| c45baaf396 | |||
| 4b1da08819 | |||
| aeb353fb20 | |||
| 65961b1593 | |||
| 1c472155e2 | |||
| 4238f0b2a0 | |||
| b40aadf76c | |||
| 7277987335 | |||
| a48ec4d034 | |||
| 7f0153f816 | |||
| 40d893e139 | |||
| 9b9289d27d | |||
| 8786798edd | |||
| fa425a98a5 | |||
| 5165769088 | |||
| feabf446db | |||
| e770120f7d | |||
| 7c1b1f2dd9 | |||
| 88a4f0e76a | |||
| d9e613b3eb | |||
| a0c84dc807 | |||
| 5a92920b1f | |||
| 30a624c857 | |||
| 8153d67eac | |||
| 9963a42c76 | |||
| 885385d7cb | |||
| c433a7f09e | |||
| c7e2c7d452 | |||
| 2a1235b3d8 | |||
| 074962d158 | |||
| 3ea0d7da9d | |||
| 00b580a4fa | |||
| f779462f36 | |||
| 2c7343aa31 | |||
| 85912d84fb | |||
| ad0d750dd0 | |||
| 9914d53045 | |||
| 9daf6c15ab | |||
| 9ac3de67aa | |||
| fd02f6bca3 | |||
| 2cc0d3db3b | |||
| fe730a65ee | |||
| 18ec9712fd | |||
| 0d80f3fb6d | |||
| be06b222cb | |||
| 3f0984e227 | |||
| 15a10ea3aa | |||
| 012abfeaf6 | |||
| dd6cd16661 | |||
| d8b37efe1b | |||
| a40a035c03 | |||
| a318758cbf | |||
| 7c13a25caf | |||
| 141567467f | |||
| 047983a280 | |||
| 48ad4322cf | |||
| eaa34308d0 | |||
| 741a12de78 | |||
| 9749467cd7 | |||
| 91ae053346 | |||
| b7b7edb5e2 | |||
| dfa99fcb14 | |||
| bc0bf41b91 | |||
| 535e69dcd0 | |||
| 2eaf83d89c | |||
| 6e62917819 | |||
| 03106eff02 | |||
| 0c5d71ecd2 | |||
| d6a7b7e305 | |||
| 3d91f2f1e7 | |||
| d76c295786 | |||
| 79422337ae | |||
| b0e624ef75 | |||
| 9d78aabf35 | |||
| a3930d7761 | |||
| c958113c94 | |||
| 385c683fe3 | |||
| 1050337751 | |||
| 114bf7544a | |||
| 0199ee9877 | |||
| 6e02aeee53 | |||
| 6c3d71c4c9 | |||
| 78592b245f | |||
| 80d4422c90 | |||
| 32df55d636 | |||
| eede86e278 | |||
| 1b855f953f | |||
| 59df400b0d | |||
| 4af244e3e2 | |||
| ae91f8801f | |||
| 7f5786d47c | |||
| ea12d5b951 | |||
| d37468a6ab | |||
| d0b3dc2ff8 | |||
| 85bbc10d06 | |||
| 2fca73aebd | |||
| 8b187940ee | |||
| 00369aa90b | |||
| 26a10020ac | |||
| 421463a642 | |||
| 8d9f248d2f | |||
| ed72206a26 | |||
| 6b7460dd4c | |||
| 447af740be | |||
| 3ff6c32ac9 | |||
| b752d70109 | |||
| a6f83e2d37 | |||
| 016e223fb8 | |||
| 502601e684 | |||
| 29a4bb6803 | |||
| cfbe7c83cb | |||
| a6d9cb9201 | |||
| 32cdbd8c54 | |||
| 1a17b646e4 | |||
| cdf79de36b | |||
| 2aa6be6496 | |||
| 25c391d244 | |||
| a3c2be4e79 | |||
| 3413e10134 | |||
| fa91348428 | |||
| d40d3eb96e | |||
| 16e019be26 | |||
| 2e77f67683 | |||
| 9824dc5a44 | |||
| 1c20e03e0c | |||
| 98f334c883 | |||
| 8ac8a780e1 | |||
| 25e5700084 | |||
| 28d60a652b | |||
| 778be03472 | |||
| fff8b5c29e | |||
| a70d0bd601 | |||
| 8fc14ac793 | |||
| 13add8f60b | |||
| 2c1605c855 | |||
| 3524676fcc | |||
| a577d57263 | |||
| 58440bb347 | |||
| f3808c4251 | |||
| 95c08e3424 | |||
| ce762b3cfa | |||
| 15bcf4a374 | |||
| 634aaa0b6a | |||
| 69fc090f55 | |||
| f42041ccb6 | |||
| a8787bd315 | |||
| b5e77aeef8 | |||
| f379685fdd | |||
| 32ff286691 | |||
| 38ab682978 | |||
| 8efe115698 | |||
| 38f72101eb | |||
| f6d03b060c | |||
| f450c00ff1 | |||
| 751f960b82 | |||
| 9558ded5c4 | |||
| 8066756605 | |||
| ac2507e0ae | |||
| 205d4ccc41 | |||
| 7565818d0e | |||
| fc093eca3e | |||
| 61b8f21037 | |||
| d988ce8c99 | |||
| 727d82f268 | |||
| c3c89c320b | |||
| 0b5bb877fb | 
							
								
								
									
										2
									
								
								.gitattributes
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								.gitattributes
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,2 @@ | ||||
| *.hdr binary | ||||
| blender/lnx/props.py ident | ||||
							
								
								
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,3 @@ | ||||
| __pycache__/ | ||||
| *.pyc | ||||
| *.DS_Store | ||||
| @ -2,10 +2,12 @@ | ||||
| -cp ../Kha/Backends/Krom | ||||
| -cp ../leenkx/Sources | ||||
| -cp ../iron/Sources | ||||
| -cp ../lib/aura/Sources | ||||
| -cp ../lib/haxebullet/Sources | ||||
| -cp ../lib/haxerecast/Sources | ||||
| -cp ../lib/zui/Sources | ||||
| --macro include('iron', true, null, ['../iron/Sources']) | ||||
| --macro include('aura', true, null, ['../lib/aura/Sources']) | ||||
| --macro include('haxebullet', true, null, ['../lib/haxebullet/Sources']) | ||||
| --macro include('haxerecast', true, null, ['../lib/haxerecast/Sources']) | ||||
| --macro include('leenkx', true, ['leenkx.network'], ['../leenkx/Sources','../iron/Sources']) | ||||
|  | ||||
							
								
								
									
										26
									
								
								leenkx.py
									
									
									
									
									
								
							
							
						
						
									
										26
									
								
								leenkx.py
									
									
									
									
									
								
							| @ -24,7 +24,7 @@ import textwrap | ||||
| import threading | ||||
| import traceback | ||||
| import typing | ||||
| from typing import Callable, Optional | ||||
| from typing import Callable, Optional, List | ||||
| import webbrowser | ||||
|  | ||||
| import bpy | ||||
| @ -33,6 +33,12 @@ from bpy.props import * | ||||
| from bpy.types import Operator, AddonPreferences | ||||
|  | ||||
|  | ||||
| if bpy.app.version < (2, 90, 0):   | ||||
|     ListType = List | ||||
| else: | ||||
|     ListType = list | ||||
|  | ||||
|  | ||||
| class SDKSource(IntEnum): | ||||
|     PREFS = 0 | ||||
|     LOCAL = 1 | ||||
| @ -73,9 +79,10 @@ def detect_sdk_path(): | ||||
|     area = win.screen.areas[0] | ||||
|     area_type = area.type | ||||
|     area.type = "INFO" | ||||
|     with bpy.context.temp_override(window=win, screen=win.screen, area=area): | ||||
|         bpy.ops.info.select_all(action='SELECT') | ||||
|         bpy.ops.info.report_copy() | ||||
|     if bpy.app.version >= (2, 92, 0): | ||||
|         with bpy.context.temp_override(window=win, screen=win.screen, area=area): | ||||
|             bpy.ops.info.select_all(action='SELECT') | ||||
|             bpy.ops.info.report_copy() | ||||
|     area.type = area_type | ||||
|     clipboard = bpy.context.window_manager.clipboard | ||||
|  | ||||
| @ -85,6 +92,7 @@ def detect_sdk_path(): | ||||
|     if match: | ||||
|         addon_prefs.sdk_path = os.path.dirname(match[-1]) | ||||
|  | ||||
|  | ||||
| def get_link_web_server(self): | ||||
|     return self.get('link_web_server', 'http://localhost/') | ||||
|  | ||||
| @ -558,7 +566,7 @@ def remove_readonly(func, path, excinfo): | ||||
|     func(path) | ||||
|  | ||||
|  | ||||
| def run_proc(cmd: list[str], done: Optional[Callable[[bool], None]] = None): | ||||
| def run_proc(cmd: ListType[str], done: Optional[Callable[[bool], None]] = None): | ||||
|     def fn(p, done): | ||||
|         p.wait() | ||||
|         if done is not None: | ||||
| @ -840,7 +848,13 @@ def update_leenkx_py(sdk_path: str, force_relink=False): | ||||
|                 else: | ||||
|                     raise err | ||||
|     else: | ||||
|         lnx_module_file.unlink(missing_ok=True) | ||||
|         if bpy.app.version < (2, 92, 0): | ||||
|             try: | ||||
|                 lnx_module_file.unlink() | ||||
|             except FileNotFoundError: | ||||
|                 pass  | ||||
|         else: | ||||
|             lnx_module_file.unlink(missing_ok=True) | ||||
|         shutil.copy(Path(sdk_path) / 'leenkx.py', lnx_module_file) | ||||
|  | ||||
|  | ||||
|  | ||||
| @ -3,6 +3,10 @@ | ||||
|  | ||||
| #include "compiled.inc" | ||||
|  | ||||
| #ifdef _CPostprocess | ||||
| uniform vec4 PPComp17; | ||||
| #endif | ||||
|  | ||||
| uniform sampler2D tex; | ||||
| uniform vec2 dir; | ||||
| uniform vec2 screenSize; | ||||
| @ -45,6 +49,12 @@ void main() { | ||||
| 		res += factor * col; | ||||
| 	} | ||||
|  | ||||
| 	#ifdef _CPostprocess | ||||
| 		vec3 AirColor = vec3(PPComp17.x, PPComp17.y, PPComp17.z); | ||||
| 	#else | ||||
| 		vec3 AirColor = volumAirColor; | ||||
| 	#endif | ||||
| 	 | ||||
| 	res /= sumfactor; | ||||
| 	fragColor = vec4(volumAirColor * res, 1.0); | ||||
| 	fragColor = vec4(AirColor * res, 1.0); | ||||
| } | ||||
|  | ||||
| @ -19,6 +19,11 @@ | ||||
| 				{ | ||||
| 					"name": "screenSize", | ||||
| 					"link": "_screenSize" | ||||
| 				}, | ||||
| 				{ | ||||
| 					"name": "PPComp17", | ||||
| 					"link": "_PPComp17", | ||||
| 					"ifdef": ["_CPostprocess"] | ||||
| 				} | ||||
| 			], | ||||
| 			"texture_params": [], | ||||
|  | ||||
| @ -5,7 +5,7 @@ | ||||
| uniform sampler2D tex; | ||||
|  | ||||
| #ifdef _CPostprocess | ||||
| uniform vec3 PPComp13; | ||||
| uniform vec4 PPComp13; | ||||
| #endif | ||||
|  | ||||
| in vec2 texCoord; | ||||
| @ -43,13 +43,17 @@ void main() { | ||||
| 	#ifdef _CPostprocess | ||||
| 		float max_distort = PPComp13.x; | ||||
| 		int num_iter = int(PPComp13.y); | ||||
| 		int CAType = int(PPComp13.z); | ||||
| 		int on = int(PPComp13.w); | ||||
| 	#else | ||||
| 		float max_distort = compoChromaticStrength; | ||||
| 		int num_iter = compoChromaticSamples; | ||||
| 		int CAType = compoChromaticType; | ||||
| 		int on = 1; | ||||
| 	#endif | ||||
|  | ||||
| 	// Spectral | ||||
| 	if (compoChromaticType == 1) { | ||||
| 	if (CAType == 1) { | ||||
| 		float reci_num_iter_f = 1.0 / float(num_iter); | ||||
|  | ||||
| 		vec2 resolution = vec2(1,1); | ||||
| @ -64,7 +68,7 @@ void main() { | ||||
| 			sumcol += w * texture(tex, barrelDistortion(uv, 0.6 * max_distort * t)); | ||||
| 		} | ||||
|  | ||||
| 		fragColor = sumcol / sumw; | ||||
| 		if (on == 1) fragColor = sumcol / sumw; else fragColor = texture(tex, texCoord); | ||||
| 	} | ||||
|  | ||||
| 	// Simple | ||||
| @ -73,6 +77,7 @@ void main() { | ||||
| 		col.x = texture(tex, texCoord + ((vec2(0.0, 1.0) * max_distort) / vec2(1000.0))).x; | ||||
| 		col.y = texture(tex, texCoord + ((vec2(-0.85, -0.5) * max_distort) / vec2(1000.0))).y; | ||||
| 		col.z = texture(tex, texCoord + ((vec2(0.85, -0.5) * max_distort) / vec2(1000.0))).z; | ||||
| 		fragColor = vec4(col.x, col.y, col.z, fragColor.w); | ||||
| 		if (on == 1) fragColor = vec4(col.x, col.y, col.z, fragColor.w);  | ||||
| 		else fragColor = texture(tex, texCoord); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @ -62,8 +62,11 @@ uniform vec3 PPComp5; | ||||
| uniform vec3 PPComp6; | ||||
| uniform vec3 PPComp7; | ||||
| uniform vec3 PPComp8; | ||||
| uniform vec3 PPComp11; | ||||
| uniform vec3 PPComp14; | ||||
| uniform vec4 PPComp15; | ||||
| uniform vec4 PPComp16; | ||||
| uniform vec4 PPComp18; | ||||
| #endif | ||||
|  | ||||
| // #ifdef _CPos | ||||
| @ -106,6 +109,16 @@ in vec2 texCoord; | ||||
| out vec4 fragColor; | ||||
|  | ||||
| #ifdef _CFog | ||||
| 	#ifdef _CPostprocess | ||||
| 		vec3 FogColor = vec3(PPComp18.x, PPComp18.y, PPComp18.z); | ||||
| 		float FogAmountA = PPComp18.w; | ||||
| 		float FogAmountB = PPComp11.z; | ||||
| 	#else | ||||
| 		vec3 FogColor = compoFogColor; | ||||
| 		float FogAmountA = compoFogAmountA; | ||||
| 		float FogAmountB = compoFogAmountB; | ||||
| 	#endif | ||||
| 	 | ||||
| // const vec3 compoFogColor = vec3(0.5, 0.6, 0.7); | ||||
| // const float compoFogAmountA = 1.0; // b = 0.01 | ||||
| // const float compoFogAmountB = 1.0; // c = 0.1 | ||||
| @ -118,8 +131,8 @@ out vec4 fragColor; | ||||
| // } | ||||
| vec3 applyFog(vec3 rgb, float distance) { | ||||
| 	// float fogAmount = 1.0 - exp(-distance * compoFogAmountA); | ||||
| 	float fogAmount = 1.0 - exp(-distance * (compoFogAmountA / 100)); | ||||
| 	return mix(rgb, compoFogColor, fogAmount); | ||||
| 	float fogAmount = 1.0 - exp(-distance * (FogAmountA / 100)); | ||||
| 	return mix(rgb, FogColor, fogAmount); | ||||
| } | ||||
| #endif | ||||
|  | ||||
| @ -131,7 +144,7 @@ float ConvertEV100ToExposure(float EV100) { | ||||
|     return 1/0.8 * exp2(-EV100); | ||||
| } | ||||
| float ComputeEV(float avgLuminance) { | ||||
|     const float sqAperture = PPComp1[0].x * PPComp1.x; | ||||
|     const float sqAperture = PPComp1.x * PPComp1.x; | ||||
|     const float shutterTime = 1.0 / PPComp1.y; | ||||
|     const float ISO = PPComp1.z; | ||||
|     const float EC = PPComp2.x; | ||||
| @ -349,16 +362,22 @@ void main() { | ||||
|  | ||||
| #ifdef _CSharpen | ||||
| 	#ifdef _CPostprocess | ||||
| 		float strengthSharpen = PPComp14.y; | ||||
| 		float strengthSharpen = PPComp14.y;	 | ||||
| 		vec3 SharpenColor = vec3(PPComp16.x, PPComp16.y, PPComp16.z);  | ||||
| 		float SharpenSize = PPComp16.w; | ||||
| 	#else | ||||
| 		float strengthSharpen = compoSharpenStrength; | ||||
| 		vec3 SharpenColor = compoSharpenColor; | ||||
| 		float SharpenSize = compoSharpenSize; | ||||
| 	#endif | ||||
| 	vec3 col1 = textureLod(tex, texCo + vec2(-texStep.x, -texStep.y) * 1.5, 0.0).rgb; | ||||
| 	vec3 col2 = textureLod(tex, texCo + vec2(texStep.x, -texStep.y) * 1.5, 0.0).rgb; | ||||
| 	vec3 col3 = textureLod(tex, texCo + vec2(-texStep.x, texStep.y) * 1.5, 0.0).rgb; | ||||
| 	vec3 col4 = textureLod(tex, texCo + vec2(texStep.x, texStep.y) * 1.5, 0.0).rgb; | ||||
| 	vec3 col1 = textureLod(tex, texCo + vec2(-texStep.x, -texStep.y) * SharpenSize, 0.0).rgb; | ||||
| 	vec3 col2 = textureLod(tex, texCo + vec2(texStep.x, -texStep.y) * SharpenSize, 0.0).rgb; | ||||
| 	vec3 col3 = textureLod(tex, texCo + vec2(-texStep.x, texStep.y) * SharpenSize, 0.0).rgb; | ||||
| 	vec3 col4 = textureLod(tex, texCo + vec2(texStep.x, texStep.y) * SharpenSize, 0.0).rgb; | ||||
| 	vec3 colavg = (col1 + col2 + col3 + col4) * 0.25; | ||||
| 	fragColor.rgb += (fragColor.rgb - colavg) * strengthSharpen; | ||||
| 	 | ||||
| 	float edgeMagnitude = length(fragColor.rgb - colavg);  | ||||
| 	fragColor.rgb = mix(fragColor.rgb, SharpenColor, min(edgeMagnitude * strengthSharpen * 2.0, 1.0)); | ||||
| #endif | ||||
|  | ||||
| #ifdef _CFog | ||||
| @ -407,7 +426,11 @@ void main() { | ||||
| #endif | ||||
|  | ||||
| #ifdef _CExposure | ||||
| 	fragColor.rgb += fragColor.rgb * compoExposureStrength; | ||||
| 	#ifdef _CPostprocess | ||||
| 		fragColor.rgb+=fragColor.rgb*PPComp8.x; | ||||
| 	#else | ||||
| 		fragColor.rgb+= fragColor.rgb*compoExposureStrength; | ||||
| 	#endif | ||||
| #endif | ||||
|  | ||||
| #ifdef _CPostprocess | ||||
| @ -415,8 +438,13 @@ void main() { | ||||
| #endif | ||||
|  | ||||
| #ifdef _AutoExposure | ||||
| 	#ifdef _CPostprocess | ||||
| 		float AEStrength = PPComp8.y; | ||||
| 	#else | ||||
| 		float AEStrength = autoExposureStrength; | ||||
| 	#endif | ||||
| 	float expo = 2.0 - clamp(length(textureLod(histogram, vec2(0.5, 0.5), 0).rgb), 0.0, 1.0); | ||||
| 	fragColor.rgb *= pow(expo, autoExposureStrength * 2.0); | ||||
| 	fragColor.rgb *= pow(expo, AEStrength * 2.0); | ||||
| #endif | ||||
|  | ||||
| // Clamp color to get rid of INF values that don't work for the tone mapping below | ||||
| @ -480,9 +508,7 @@ fragColor.rgb = min(fragColor.rgb, 65504 * 0.5); | ||||
| 			fragColor.rgb = pow(fragColor.rgb, vec3(1.0 / 2.2)); // To gamma | ||||
| 		} else if (PPComp4.x == 10){ | ||||
| 			fragColor.rgb = tonemapAgXFull(fragColor.rgb); | ||||
| 		} else { | ||||
| 			fragColor.rgb = vec3(0,1,0); //ERROR | ||||
| 		} | ||||
| 		} //else { fragColor.rgb = vec3(0,1,0); //ERROR} | ||||
| 	#endif | ||||
|  | ||||
| #else | ||||
|  | ||||
| @ -235,6 +235,16 @@ | ||||
| 					"name": "PPComp15", | ||||
| 					"link": "_PPComp15", | ||||
| 					"ifdef": ["_CPostprocess"] | ||||
| 				}, | ||||
| 				{ | ||||
| 					"name": "PPComp16", | ||||
| 					"link": "_PPComp16", | ||||
| 					"ifdef": ["_CPostprocess"] | ||||
| 				}, | ||||
| 				{ | ||||
| 					"name": "PPComp18", | ||||
| 					"link": "_PPComp18", | ||||
| 					"ifdef": ["_CPostprocess"] | ||||
| 				} | ||||
| 			], | ||||
| 			"texture_params": [], | ||||
|  | ||||
| @ -20,7 +20,7 @@ uniform sampler2D gbuffer0; | ||||
| uniform sampler2D gbuffer1; | ||||
|  | ||||
| #ifdef _gbuffer2 | ||||
| 	//!uniform sampler2D gbuffer2; | ||||
| 	uniform sampler2D gbuffer2; | ||||
| #endif | ||||
| #ifdef _EmissionShaded | ||||
| 	uniform sampler2D gbufferEmission; | ||||
| @ -286,7 +286,7 @@ void main() { | ||||
|  | ||||
| #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; | ||||
| 	fragColor.rgb = (indirect_diffuse.rgb * albedo + envl.rgb * (1.0 - indirect_diffuse.a)) * voxelgiDiff; | ||||
| 	if(roughness < 1.0 && occspec.y > 0.0) | ||||
| 		fragColor.rgb += textureLod(voxels_specular, texCoord, 0.0).rgb * occspec.y * voxelgiRefl; | ||||
| #endif | ||||
| @ -380,7 +380,7 @@ void main() { | ||||
| 	#endif | ||||
|  | ||||
| 	#ifdef _VoxelShadow | ||||
| 	svisibility *= (1.0 - traceShadow(p, n, voxels, voxelsSDF, sunDir, clipmaps, gl_FragCoord.xy, g2.rg).r) * voxelgiShad; | ||||
| 	svisibility *= (1.0 - traceShadow(p, n, voxels, voxelsSDF, sunDir, clipmaps, gl_FragCoord.xy).r) * voxelgiShad; | ||||
| 	#endif | ||||
| 	 | ||||
| 	#ifdef _SSRS | ||||
|  | ||||
| @ -2,13 +2,22 @@ | ||||
|  | ||||
| #include "compiled.inc" | ||||
|  | ||||
| #ifdef _CPostprocess | ||||
| uniform vec3 PPComp8; | ||||
| #endif | ||||
|  | ||||
| uniform sampler2D tex; | ||||
|  | ||||
| in vec2 texCoord; | ||||
| out vec4 fragColor; | ||||
|  | ||||
| void main() { | ||||
| 	fragColor.a = 0.01 * autoExposureSpeed; | ||||
| 	#ifdef _CPostprocess | ||||
| 		fragColor.a = 0.01 * PPComp8.z; | ||||
| 	#else | ||||
| 		fragColor.a = 0.01 * autoExposureSpeed; | ||||
| 	#endif | ||||
|  | ||||
| 	fragColor.rgb = textureLod(tex, vec2(0.5, 0.5), 0.0).rgb + | ||||
| 					textureLod(tex, vec2(0.2, 0.2), 0.0).rgb + | ||||
| 					textureLod(tex, vec2(0.8, 0.2), 0.0).rgb + | ||||
|  | ||||
| @ -8,7 +8,13 @@ | ||||
| 			"blend_source": "source_alpha", | ||||
| 			"blend_destination": "inverse_source_alpha", | ||||
| 			"blend_operation": "add", | ||||
| 			"links": [], | ||||
| 			"links": [ | ||||
| 				{ | ||||
| 					"name": "PPComp8", | ||||
| 					"link": "_PPComp8", | ||||
| 					"ifdef": ["_CPostprocess"] | ||||
| 				} | ||||
| 			], | ||||
| 			"texture_params": [], | ||||
| 			"vertex_shader": "../include/pass.vert.glsl", | ||||
| 			"fragment_shader": "histogram_pass.frag.glsl" | ||||
|  | ||||
| @ -92,7 +92,7 @@ void main() { | ||||
|  | ||||
| 	vec3 viewNormal = V3 * n; | ||||
| 	vec3 viewPos = getPosView(viewRay, d, cameraProj); | ||||
| 	vec3 reflected = reflect(normalize(viewPos), viewNormal); | ||||
| 	vec3 reflected = reflect(viewPos, viewNormal); | ||||
| 	hitCoord = viewPos; | ||||
|  | ||||
| 	#ifdef _CPostprocess | ||||
|  | ||||
| @ -57,17 +57,14 @@ vec4 binarySearch(vec3 dir) { | ||||
| } | ||||
|  | ||||
| vec4 rayCast(vec3 dir) { | ||||
|     float ddepth; | ||||
|     dir *= ss_refractionRayStep; | ||||
|     for (int i = 0; i < maxSteps; i++) { | ||||
|         hitCoord += dir; | ||||
|         ddepth = getDeltaDepth(hitCoord); | ||||
|         if (ddepth > 0.0) | ||||
|             return binarySearch(dir); | ||||
|     } | ||||
|     // No hit — fallback to projecting the ray to UV space | ||||
|     vec2 fallbackUV = getProjectedCoord(hitCoord); | ||||
|     return vec4(fallbackUV, 0.0, 0.5); // We set .w lower to indicate fallback | ||||
| 	float ddepth; | ||||
| 	dir *= ss_refractionRayStep; | ||||
| 	for (int i = 0; i < maxSteps; i++) { | ||||
| 		hitCoord += dir; | ||||
| 		ddepth = getDeltaDepth(hitCoord); | ||||
| 		if (ddepth > 0.0) return binarySearch(dir); | ||||
| 	} | ||||
| 	return vec4(texCoord, 0.0, 1.0); | ||||
| } | ||||
|  | ||||
| void main() { | ||||
| @ -77,7 +74,7 @@ void main() { | ||||
|     float ior = gr.x; | ||||
|     float opac = gr.y; | ||||
|     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; | ||||
|         return; | ||||
|     } | ||||
|  | ||||
| @ -97,9 +97,9 @@ vec4 traceCone(const sampler3D voxels, const sampler3D voxelsSDF, const vec3 ori | ||||
|  | ||||
| 	vec3 aniso_direction = -dir; | ||||
| 	vec3 face_offset = vec3( | ||||
| 		aniso_direction.x > 0.0 ? 0 : 1, | ||||
| 		aniso_direction.y > 0.0 ? 2 : 3, | ||||
| 		aniso_direction.z > 0.0 ? 4 : 5 | ||||
| 		aniso_direction.x > 0.0 ? 0.0 : 1.0, | ||||
| 		aniso_direction.y > 0.0 ? 2.0 : 3.0, | ||||
| 		aniso_direction.z > 0.0 ? 4.0 : 5.0 | ||||
| 	) / (6 + DIFFUSE_CONE_COUNT); | ||||
| 	vec3 direction_weight = abs(dir); | ||||
|  | ||||
| @ -166,7 +166,7 @@ vec4 traceDiffuse(const vec3 origin, const vec3 normal, const sampler3D voxels, | ||||
| } | ||||
|  | ||||
| vec4 traceSpecular(const vec3 origin, const vec3 normal, const sampler3D voxels, const sampler3D voxelsSDF, const vec3 viewDir, const float roughness, const float clipmaps[voxelgiClipmapCount * 10], const vec2 pixel, const vec2 velocity) { | ||||
| 	vec3 specularDir = reflect(normalize(-viewDir), normal); | ||||
| 	vec3 specularDir = reflect(-viewDir, normal); | ||||
| 	vec3 P = origin + specularDir * ((BayerMatrix8[int(pixel.x + velocity.x) % 8][int(pixel.y + velocity.y) % 8] - 0.5)) * voxelgiStep; | ||||
| 	vec4 amount = traceCone(voxels, voxelsSDF, P, normal, specularDir, 0, true, roughness, voxelgiStep, clipmaps); | ||||
|  | ||||
| @ -176,9 +176,9 @@ vec4 traceSpecular(const vec3 origin, const vec3 normal, const sampler3D voxels, | ||||
| 	return amount * voxelgiOcc; | ||||
| } | ||||
|  | ||||
| vec4 traceRefraction(const vec3 origin, const vec3 normal, sampler3D voxels, sampler3D voxelsSDF, const vec3 viewDir, const float ior, const float roughness, const float clipmaps[voxelgiClipmapCount * 10], const vec2 pixel, const vec2 velocity, const float opacity) { | ||||
|  	const float transmittance = 1.0 - opacity; | ||||
|  	vec3 refractionDir = refract(normalize(-viewDir), normal, 1.0 / ior); | ||||
| vec4 traceRefraction(const vec3 origin, const vec3 normal, sampler3D voxels, sampler3D voxelsSDF, const vec3 viewDir, const float ior, const float roughness, const float clipmaps[voxelgiClipmapCount * 10], const vec2 pixel, const vec2 velocity) { | ||||
|  	const float transmittance = 1.0; | ||||
|  	vec3 refractionDir = refract(-viewDir, normal, 1.0 / ior); | ||||
|  	vec3 P = origin + refractionDir * (BayerMatrix8[int(pixel.x + velocity.x) % 8][int(pixel.y + velocity.y) % 8] - 0.5) * voxelgiStep; | ||||
| 	vec4 amount =  transmittance * traceCone(voxels, voxelsSDF, P, normal, refractionDir, 0, true, roughness, voxelgiStep, clipmaps); | ||||
|  | ||||
| @ -201,9 +201,9 @@ float traceConeAO(const sampler3D voxels, const vec3 origin, const vec3 n, const | ||||
|  | ||||
| 	vec3 aniso_direction = -dir; | ||||
| 	vec3 face_offset = vec3( | ||||
| 		aniso_direction.x > 0.0 ? 0 : 1, | ||||
| 		aniso_direction.y > 0.0 ? 2 : 3, | ||||
| 		aniso_direction.z > 0.0 ? 4 : 5 | ||||
| 		aniso_direction.x > 0.0 ? 0.0 : 1.0, | ||||
| 		aniso_direction.y > 0.0 ? 2.0 : 3.0, | ||||
| 		aniso_direction.z > 0.0 ? 4.0 : 5.0 | ||||
| 	) / (6 + DIFFUSE_CONE_COUNT); | ||||
| 	vec3 direction_weight = abs(dir); | ||||
|  | ||||
| @ -272,9 +272,9 @@ float traceConeShadow(const sampler3D voxels, const sampler3D voxelsSDF, const v | ||||
|  | ||||
| 	vec3 aniso_direction = -dir; | ||||
| 	vec3 face_offset = vec3( | ||||
| 		aniso_direction.x > 0.0 ? 0 : 1, | ||||
| 		aniso_direction.y > 0.0 ? 2 : 3, | ||||
| 		aniso_direction.z > 0.0 ? 4 : 5 | ||||
| 		aniso_direction.x > 0.0 ? 0.0 : 1.0, | ||||
| 		aniso_direction.y > 0.0 ? 2.0 : 3.0, | ||||
| 		aniso_direction.z > 0.0 ? 4.0 : 5.0 | ||||
| 	) / (6 + DIFFUSE_CONE_COUNT); | ||||
| 	vec3 direction_weight = abs(dir); | ||||
| 	float coneCoefficient = 2.0 * tan(aperture * 0.5); | ||||
| @ -328,8 +328,8 @@ float traceConeShadow(const sampler3D voxels, const sampler3D voxelsSDF, const v | ||||
| } | ||||
|  | ||||
|  | ||||
| float traceShadow(const vec3 origin, const vec3 normal, const sampler3D voxels, const sampler3D voxelsSDF, const vec3 dir, const float clipmaps[voxelgiClipmapCount * 10], const vec2 pixel, const vec2 velocity) { | ||||
|  	vec3 P = origin + dir * (BayerMatrix8[int(pixel.x + velocity.x) % 8][int(pixel.y + velocity.y) % 8] - 0.5) * voxelgiStep; | ||||
| float traceShadow(const vec3 origin, const vec3 normal, const sampler3D voxels, const sampler3D voxelsSDF, const vec3 dir, const float clipmaps[voxelgiClipmapCount * 10], const vec2 pixel) { | ||||
|  	vec3 P = origin + dir * (BayerMatrix8[int(pixel.x) % 8][int(pixel.y) % 8] - 0.5) * voxelgiStep; | ||||
| 	float amount = traceConeShadow(voxels, voxelsSDF, P, normal, dir, DIFFUSE_CONE_APERTURE, voxelgiStep, clipmaps); | ||||
| 	amount = clamp(amount, 0.0, 1.0); | ||||
| 	return amount * voxelgiOcc; | ||||
|  | ||||
| @ -1,242 +1,239 @@ | ||||
| #ifndef _LIGHT_GLSL_ | ||||
| #define _LIGHT_GLSL_ | ||||
|  | ||||
| #include "compiled.inc" | ||||
| #include "std/brdf.glsl" | ||||
| #include "std/math.glsl" | ||||
| #ifdef _ShadowMap | ||||
| #include "std/shadows.glsl" | ||||
| #endif | ||||
| #ifdef _VoxelShadow | ||||
| #include "std/conetrace.glsl" | ||||
| #endif | ||||
| #ifdef _gbuffer2 | ||||
| uniform sampler2D gbuffer2; | ||||
| #endif | ||||
| #ifdef _LTC | ||||
| #include "std/ltc.glsl" | ||||
| #endif | ||||
| #ifdef _LightIES | ||||
| #include "std/ies.glsl" | ||||
| #endif | ||||
| #ifdef _SSRS | ||||
| #include "std/ssrs.glsl" | ||||
| #endif | ||||
| #ifdef _Spot | ||||
| #include "std/light_common.glsl" | ||||
| #endif | ||||
|  | ||||
| #ifdef _ShadowMap | ||||
| 	#ifdef _SinglePoint | ||||
| 		#ifdef _Spot | ||||
| 			#ifndef _LTC | ||||
| 				uniform sampler2DShadow shadowMapSpot[1]; | ||||
| 				uniform sampler2D shadowMapSpotTransparent[1]; | ||||
| 				uniform mat4 LWVPSpot[1]; | ||||
| 			#endif | ||||
| 		#else | ||||
| 			uniform samplerCubeShadow shadowMapPoint[1]; | ||||
| 			uniform samplerCube shadowMapPointTransparent[1]; | ||||
| 			uniform vec2 lightProj; | ||||
| 		#endif | ||||
| 	#endif | ||||
| 	#ifdef _Clusters | ||||
| 		#ifdef _SingleAtlas | ||||
| 		//!uniform sampler2DShadow shadowMapAtlas; | ||||
| 		//!uniform sampler2D shadowMapAtlasTransparent; | ||||
| 		#endif | ||||
| 		uniform vec2 lightProj; | ||||
| 		#ifdef _ShadowMapAtlas | ||||
| 		#ifndef _SingleAtlas | ||||
| 		uniform sampler2DShadow shadowMapAtlasPoint; | ||||
| 		uniform sampler2D shadowMapAtlasPointTransparent; | ||||
| 		#endif | ||||
| 		#else | ||||
| 		uniform samplerCubeShadow shadowMapPoint[4]; | ||||
| 		uniform samplerCube shadowMapPointTransparent[4]; | ||||
| 		#endif | ||||
| 		#ifdef _Spot | ||||
| 			#ifdef _ShadowMapAtlas | ||||
| 			#ifndef _SingleAtlas | ||||
| 			uniform sampler2DShadow shadowMapAtlasSpot; | ||||
| 			uniform sampler2D shadowMapAtlasSpotTransparent; | ||||
| 			#endif | ||||
| 			#else | ||||
| 			uniform sampler2DShadow shadowMapSpot[4]; | ||||
| 			uniform sampler2D shadowMapSpotTransparent[4]; | ||||
| 			#endif | ||||
| 			uniform mat4 LWVPSpotArray[maxLightsCluster]; | ||||
| 		#endif | ||||
| 	#endif | ||||
| #endif | ||||
|  | ||||
| #ifdef _LTC | ||||
| uniform vec3 lightArea0; | ||||
| uniform vec3 lightArea1; | ||||
| uniform vec3 lightArea2; | ||||
| uniform vec3 lightArea3; | ||||
| uniform sampler2D sltcMat; | ||||
| uniform sampler2D sltcMag; | ||||
| #ifdef _ShadowMap | ||||
| #ifndef _Spot | ||||
| 	#ifdef _SinglePoint | ||||
| 		uniform sampler2DShadow shadowMapSpot[1]; | ||||
| 		uniform sampler2D shadowMapSpotTransparent[1]; | ||||
| 		uniform mat4 LWVPSpot[1]; | ||||
| 	#endif | ||||
| 	#ifdef _Clusters | ||||
| 		uniform sampler2DShadow shadowMapSpot[maxLightsCluster]; | ||||
| 		uniform sampler2D shadowMapSpotTransparent[maxLightsCluster]; | ||||
| 		uniform mat4 LWVPSpotArray[maxLightsCluster]; | ||||
| 	#endif | ||||
| 	#endif | ||||
| #endif | ||||
| #endif | ||||
|  | ||||
| vec3 sampleLight(const vec3 p, const vec3 n, const vec3 v, const float dotNV, const vec3 lp, const vec3 lightCol, | ||||
| 	const vec3 albedo, const float rough, const float spec, const vec3 f0 | ||||
| 	#ifdef _ShadowMap | ||||
| 		, int index, float bias, bool receiveShadow, bool transparent | ||||
| 	#endif | ||||
| 	#ifdef _Spot | ||||
| 		, const bool isSpot, const float spotSize, float spotBlend, vec3 spotDir, vec2 scale, vec3 right | ||||
| 	#endif | ||||
| 	#ifdef _VoxelShadow | ||||
| 		, sampler3D voxels, sampler3D voxelsSDF, float clipmaps[10 * voxelgiClipmapCount] | ||||
| 	#endif | ||||
| 	#ifdef _MicroShadowing | ||||
| 		, float occ | ||||
| 	#endif | ||||
| 	#ifdef _SSRS | ||||
| 		, sampler2D gbufferD, mat4 invVP, vec3 eye | ||||
| 	#endif | ||||
| 	) { | ||||
| 	vec3 ld = lp - p; | ||||
| 	vec3 l = normalize(ld); | ||||
| 	vec3 h = normalize(v + l); | ||||
| 	float dotNH = max(0.0, dot(n, h)); | ||||
| 	float dotVH = max(0.0, dot(v, h)); | ||||
| 	float dotNL = max(0.0, dot(n, l)); | ||||
|  | ||||
| 	#ifdef _LTC | ||||
| 	float theta = acos(dotNV); | ||||
| 	vec2 tuv = vec2(rough, theta / (0.5 * PI)); | ||||
| 	tuv = tuv * LUT_SCALE + LUT_BIAS; | ||||
| 	vec4 t = textureLod(sltcMat, tuv, 0.0); | ||||
| 	mat3 invM = mat3( | ||||
| 		vec3(1.0, 0.0, t.y), | ||||
| 		vec3(0.0, t.z, 0.0), | ||||
| 		vec3(t.w, 0.0, t.x)); | ||||
| 	float ltcspec = ltcEvaluate(n, v, dotNV, p, invM, lightArea0, lightArea1, lightArea2, lightArea3); | ||||
| 	ltcspec *= textureLod(sltcMag, tuv, 0.0).a; | ||||
| 	float ltcdiff = ltcEvaluate(n, v, dotNV, p, mat3(1.0), lightArea0, lightArea1, lightArea2, lightArea3); | ||||
| 	vec3 direct = albedo * ltcdiff + ltcspec * spec * 0.05; | ||||
| 	#else | ||||
| 	vec3 direct = lambertDiffuseBRDF(albedo, dotNL) + | ||||
| 				  specularBRDF(f0, rough, dotNL, dotNH, dotNV, dotVH) * spec; | ||||
| 	#endif | ||||
|  | ||||
| 	direct *= attenuate(distance(p, lp)); | ||||
| 	direct *= lightCol; | ||||
|  | ||||
| 	#ifdef _MicroShadowing | ||||
| 	direct *= clamp(dotNL + 2.0 * occ * occ - 1.0, 0.0, 1.0); | ||||
| 	#endif | ||||
|  | ||||
| 	#ifdef _SSRS | ||||
| 	direct *= traceShadowSS(l, p, gbufferD, invVP, eye); | ||||
| 	#endif | ||||
|  | ||||
| 	#ifdef _VoxelShadow | ||||
| 	vec4 g2 = textureLod(gbuffer2, gl_FragCoord.xy, 0.0); | ||||
| 	direct *= (1.0 - traceShadow(p, n, voxels, voxelsSDF, l, clipmaps, gl_FragCoord.xy, g2.rg).r) * voxelgiShad; | ||||
| 	#endif | ||||
|  | ||||
| 	#ifdef _LTC | ||||
| 	#ifdef _ShadowMap | ||||
| 		if (receiveShadow) { | ||||
| 			#ifdef _SinglePoint | ||||
| 			vec4 lPos = LWVPSpotArray[0] * vec4(p + n * bias * 10, 1.0); | ||||
| 			direct *= shadowTest(shadowMapSpot[0], shadowMapSpotTransparent[0], lPos.xyz / lPos.w, bias, transparent); | ||||
| 			#endif | ||||
| 			#ifdef _Clusters | ||||
| 			vec4 lPos = LWVPSpotArray[index] * vec4(p + n * bias * 10, 1.0); | ||||
| 			if (index == 0) direct *= shadowTest(shadowMapSpot[0], shadowMapSpotTransparent[0], lPos.xyz / lPos.w, bias, transparent); | ||||
| 			else if (index == 1) direct *= shadowTest(shadowMapSpot[1], shadowMapSpotTransparent[1], lPos.xyz / lPos.w, bias, transparent); | ||||
| 			else if (index == 2) direct *= shadowTest(shadowMapSpot[2], shadowMapSpotTransparent[2], lPos.xyz / lPos.w, bias, transparent); | ||||
| 			else if (index == 3) direct *= shadowTest(shadowMapSpot[3], shadowMapSpotTransparent[3], lPos.xyz / lPos.w, bias, transparent); | ||||
| 			#endif | ||||
| 		} | ||||
| 	#endif | ||||
| 	return direct; | ||||
| 	#endif | ||||
|  | ||||
| 	#ifdef _Spot | ||||
| 	if (isSpot) { | ||||
| 		direct *= spotlightMask(l, spotDir, right, scale, spotSize, spotBlend); | ||||
|  | ||||
| 		#ifdef _ShadowMap | ||||
| 			if (receiveShadow) { | ||||
| 				#ifdef _SinglePoint | ||||
| 				vec4 lPos = LWVPSpot[0] * vec4(p + n * bias * 10, 1.0); | ||||
| 				direct *= shadowTest(shadowMapSpot[0], shadowMapSpotTransparent[0], lPos.xyz / lPos.w, bias, transparent); | ||||
| 				#endif | ||||
| 				#ifdef _Clusters | ||||
| 					vec4 lPos = LWVPSpotArray[index] * vec4(p + n * bias * 10, 1.0); | ||||
| 					#ifdef _ShadowMapAtlas | ||||
| 						direct *= shadowTest( | ||||
| 							#ifndef _SingleAtlas | ||||
| 							shadowMapAtlasSpot, shadowMapAtlasSpotTransparent | ||||
| 							#else | ||||
| 							shadowMapAtlas, shadowMapAtlasTransparent | ||||
| 							#endif | ||||
| 							, lPos.xyz / lPos.w, bias, transparent | ||||
| 						); | ||||
| 					#else | ||||
| 							 if (index == 0) direct *= shadowTest(shadowMapSpot[0], shadowMapSpotTransparent[0], lPos.xyz / lPos.w, bias, transparent); | ||||
| 						else if (index == 1) direct *= shadowTest(shadowMapSpot[1], shadowMapSpotTransparent[1], lPos.xyz / lPos.w, bias, transparent); | ||||
| 						else if (index == 2) direct *= shadowTest(shadowMapSpot[2], shadowMapSpotTransparent[2], lPos.xyz / lPos.w, bias, transparent); | ||||
| 						else if (index == 3) direct *= shadowTest(shadowMapSpot[3], shadowMapSpotTransparent[3], lPos.xyz / lPos.w, bias, transparent); | ||||
| 					#endif | ||||
| 				#endif | ||||
| 			} | ||||
| 		#endif | ||||
| 		return direct; | ||||
| 	} | ||||
| 	#endif | ||||
|  | ||||
| 	#ifdef _LightIES | ||||
| 	direct *= iesAttenuation(-l); | ||||
| 	#endif | ||||
|  | ||||
| 	#ifdef _ShadowMap | ||||
| 		if (receiveShadow) { | ||||
| 			#ifdef _SinglePoint | ||||
| 			#ifndef _Spot | ||||
| 			direct *= PCFCube(shadowMapPoint[0], shadowMapPointTransparent[0], ld, -l, bias, lightProj, n, transparent); | ||||
| 			#endif | ||||
| 			#endif | ||||
| 			#ifdef _Clusters | ||||
| 				#ifdef _ShadowMapAtlas | ||||
| 				direct *= PCFFakeCube( | ||||
| 					#ifndef _SingleAtlas | ||||
| 					shadowMapAtlasPoint, shadowMapAtlasPointTransparent | ||||
| 					#else | ||||
| 					shadowMapAtlas, shadowMapAtlasTransparent | ||||
| 					#endif | ||||
| 					, ld, -l, bias, lightProj, n, index, transparent | ||||
| 				); | ||||
| 				#else | ||||
| 					 if (index == 0) direct *= PCFCube(shadowMapPoint[0], shadowMapPointTransparent[0], ld, -l, bias, lightProj, n, transparent); | ||||
| 				else if (index == 1) direct *= PCFCube(shadowMapPoint[1], shadowMapPointTransparent[1], ld, -l, bias, lightProj, n, transparent); | ||||
| 				else if (index == 2) direct *= PCFCube(shadowMapPoint[2], shadowMapPointTransparent[2], ld, -l, bias, lightProj, n, transparent); | ||||
| 				else if (index == 3) direct *= PCFCube(shadowMapPoint[3], shadowMapPointTransparent[3], ld, -l, bias, lightProj, n, transparent); | ||||
| 				#endif | ||||
| 			#endif | ||||
| 		} | ||||
| 	#endif | ||||
|  | ||||
| 	return direct; | ||||
| } | ||||
|  | ||||
| #endif | ||||
| #ifndef _LIGHT_GLSL_ | ||||
| #define _LIGHT_GLSL_ | ||||
|  | ||||
| #include "compiled.inc" | ||||
| #include "std/brdf.glsl" | ||||
| #include "std/math.glsl" | ||||
| #ifdef _ShadowMap | ||||
| #include "std/shadows.glsl" | ||||
| #endif | ||||
| #ifdef _VoxelShadow | ||||
| #include "std/conetrace.glsl" | ||||
| //!uniform sampler2D voxels_shadows; | ||||
| #endif | ||||
| #ifdef _LTC | ||||
| #include "std/ltc.glsl" | ||||
| #endif | ||||
| #ifdef _LightIES | ||||
| #include "std/ies.glsl" | ||||
| #endif | ||||
| #ifdef _SSRS | ||||
| #include "std/ssrs.glsl" | ||||
| #endif | ||||
| #ifdef _Spot | ||||
| #include "std/light_common.glsl" | ||||
| #endif | ||||
|  | ||||
| #ifdef _ShadowMap | ||||
| 	#ifdef _SinglePoint | ||||
| 		#ifdef _Spot | ||||
| 			#ifndef _LTC | ||||
| 				uniform sampler2DShadow shadowMapSpot[1]; | ||||
| 				uniform sampler2D shadowMapSpotTransparent[1]; | ||||
| 				uniform mat4 LWVPSpot[1]; | ||||
| 			#endif | ||||
| 		#else | ||||
| 			uniform samplerCubeShadow shadowMapPoint[1]; | ||||
| 			uniform samplerCube shadowMapPointTransparent[1]; | ||||
| 			uniform vec2 lightProj; | ||||
| 		#endif | ||||
| 	#endif | ||||
| 	#ifdef _Clusters | ||||
| 		#ifdef _SingleAtlas | ||||
| 		//!uniform sampler2DShadow shadowMapAtlas; | ||||
| 		//!uniform sampler2D shadowMapAtlasTransparent; | ||||
| 		#endif | ||||
| 		uniform vec2 lightProj; | ||||
| 		#ifdef _ShadowMapAtlas | ||||
| 		#ifndef _SingleAtlas | ||||
| 		uniform sampler2DShadow shadowMapAtlasPoint; | ||||
| 		uniform sampler2D shadowMapAtlasPointTransparent; | ||||
| 		#endif | ||||
| 		#else | ||||
| 		uniform samplerCubeShadow shadowMapPoint[4]; | ||||
| 		uniform samplerCube shadowMapPointTransparent[4]; | ||||
| 		#endif | ||||
| 		#ifdef _Spot | ||||
| 			#ifdef _ShadowMapAtlas | ||||
| 			#ifndef _SingleAtlas | ||||
| 			uniform sampler2DShadow shadowMapAtlasSpot; | ||||
| 			uniform sampler2D shadowMapAtlasSpotTransparent; | ||||
| 			#endif | ||||
| 			#else | ||||
| 			uniform sampler2DShadow shadowMapSpot[4]; | ||||
| 			uniform sampler2D shadowMapSpotTransparent[4]; | ||||
| 			#endif | ||||
| 			uniform mat4 LWVPSpotArray[maxLightsCluster]; | ||||
| 		#endif | ||||
| 	#endif | ||||
| #endif | ||||
|  | ||||
| #ifdef _LTC | ||||
| uniform vec3 lightArea0; | ||||
| uniform vec3 lightArea1; | ||||
| uniform vec3 lightArea2; | ||||
| uniform vec3 lightArea3; | ||||
| uniform sampler2D sltcMat; | ||||
| uniform sampler2D sltcMag; | ||||
| #ifdef _ShadowMap | ||||
| #ifndef _Spot | ||||
| 	#ifdef _SinglePoint | ||||
| 		uniform sampler2DShadow shadowMapSpot[1]; | ||||
| 		uniform sampler2D shadowMapSpotTransparent[1]; | ||||
| 		uniform mat4 LWVPSpot[1]; | ||||
| 	#endif | ||||
| 	#ifdef _Clusters | ||||
| 		uniform sampler2DShadow shadowMapSpot[maxLightsCluster]; | ||||
| 		uniform sampler2D shadowMapSpotTransparent[maxLightsCluster]; | ||||
| 		uniform mat4 LWVPSpotArray[maxLightsCluster]; | ||||
| 	#endif | ||||
| 	#endif | ||||
| #endif | ||||
| #endif | ||||
|  | ||||
| vec3 sampleLight(const vec3 p, const vec3 n, const vec3 v, const float dotNV, const vec3 lp, const vec3 lightCol, | ||||
| 	const vec3 albedo, const float rough, const float spec, const vec3 f0 | ||||
| 	#ifdef _ShadowMap | ||||
| 		, int index, float bias, bool receiveShadow, bool transparent | ||||
| 	#endif | ||||
| 	#ifdef _Spot | ||||
| 		, const bool isSpot, const float spotSize, float spotBlend, vec3 spotDir, vec2 scale, vec3 right | ||||
| 	#endif | ||||
| 	#ifdef _VoxelShadow | ||||
| 		, sampler3D voxels, sampler3D voxelsSDF, float clipmaps[10 * voxelgiClipmapCount] | ||||
| 	#endif | ||||
| 	#ifdef _MicroShadowing | ||||
| 		, float occ | ||||
| 	#endif | ||||
| 	#ifdef _SSRS | ||||
| 		, sampler2D gbufferD, mat4 invVP, vec3 eye | ||||
| 	#endif | ||||
| 	) { | ||||
| 	vec3 ld = lp - p; | ||||
| 	vec3 l = normalize(ld); | ||||
| 	vec3 h = normalize(v + l); | ||||
| 	float dotNH = max(0.0, dot(n, h)); | ||||
| 	float dotVH = max(0.0, dot(v, h)); | ||||
| 	float dotNL = max(0.0, dot(n, l)); | ||||
|  | ||||
| 	#ifdef _LTC | ||||
| 	float theta = acos(dotNV); | ||||
| 	vec2 tuv = vec2(rough, theta / (0.5 * PI)); | ||||
| 	tuv = tuv * LUT_SCALE + LUT_BIAS; | ||||
| 	vec4 t = textureLod(sltcMat, tuv, 0.0); | ||||
| 	mat3 invM = mat3( | ||||
| 		vec3(1.0, 0.0, t.y), | ||||
| 		vec3(0.0, t.z, 0.0), | ||||
| 		vec3(t.w, 0.0, t.x)); | ||||
| 	float ltcspec = ltcEvaluate(n, v, dotNV, p, invM, lightArea0, lightArea1, lightArea2, lightArea3); | ||||
| 	ltcspec *= textureLod(sltcMag, tuv, 0.0).a; | ||||
| 	float ltcdiff = ltcEvaluate(n, v, dotNV, p, mat3(1.0), lightArea0, lightArea1, lightArea2, lightArea3); | ||||
| 	vec3 direct = albedo * ltcdiff + ltcspec * spec * 0.05; | ||||
| 	#else | ||||
| 	vec3 direct = lambertDiffuseBRDF(albedo, dotNL) + | ||||
| 				  specularBRDF(f0, rough, dotNL, dotNH, dotNV, dotVH) * spec; | ||||
| 	#endif | ||||
|  | ||||
| 	direct *= attenuate(distance(p, lp)); | ||||
| 	direct *= lightCol; | ||||
|  | ||||
| 	#ifdef _MicroShadowing | ||||
| 	direct *= clamp(dotNL + 2.0 * occ * occ - 1.0, 0.0, 1.0); | ||||
| 	#endif | ||||
|  | ||||
| 	#ifdef _SSRS | ||||
| 	direct *= traceShadowSS(l, p, gbufferD, invVP, eye); | ||||
| 	#endif | ||||
|  | ||||
| 	#ifdef _VoxelShadow | ||||
| 	direct *= (1.0 - traceShadow(p, n, voxels, voxelsSDF, l, clipmaps, gl_FragCoord.xy).r) * voxelgiShad; | ||||
| 	#endif | ||||
|  | ||||
| 	#ifdef _LTC | ||||
| 	#ifdef _ShadowMap | ||||
| 		if (receiveShadow) { | ||||
| 			#ifdef _SinglePoint | ||||
| 			vec4 lPos = LWVPSpotArray[0] * vec4(p + n * bias * 10, 1.0); | ||||
| 			direct *= shadowTest(shadowMapSpot[0], shadowMapSpotTransparent[0], lPos.xyz / lPos.w, bias, transparent); | ||||
| 			#endif | ||||
| 			#ifdef _Clusters | ||||
| 			vec4 lPos = LWVPSpotArray[index] * vec4(p + n * bias * 10, 1.0); | ||||
| 			if (index == 0) direct *= shadowTest(shadowMapSpot[0], shadowMapSpotTransparent[0], lPos.xyz / lPos.w, bias, transparent); | ||||
| 			else if (index == 1) direct *= shadowTest(shadowMapSpot[1], shadowMapSpotTransparent[1], lPos.xyz / lPos.w, bias, transparent); | ||||
| 			else if (index == 2) direct *= shadowTest(shadowMapSpot[2], shadowMapSpotTransparent[2], lPos.xyz / lPos.w, bias, transparent); | ||||
| 			else if (index == 3) direct *= shadowTest(shadowMapSpot[3], shadowMapSpotTransparent[3], lPos.xyz / lPos.w, bias, transparent); | ||||
| 			#endif | ||||
| 		} | ||||
| 	#endif | ||||
| 	return direct; | ||||
| 	#endif | ||||
|  | ||||
| 	#ifdef _Spot | ||||
| 	if (isSpot) { | ||||
| 		direct *= spotlightMask(l, spotDir, right, scale, spotSize, spotBlend); | ||||
|  | ||||
| 		#ifdef _ShadowMap | ||||
| 			if (receiveShadow) { | ||||
| 				#ifdef _SinglePoint | ||||
| 				vec4 lPos = LWVPSpot[0] * vec4(p + n * bias * 10, 1.0); | ||||
| 				direct *= shadowTest(shadowMapSpot[0], shadowMapSpotTransparent[0], lPos.xyz / lPos.w, bias, transparent); | ||||
| 				#endif | ||||
| 				#ifdef _Clusters | ||||
| 					vec4 lPos = LWVPSpotArray[index] * vec4(p + n * bias * 10, 1.0); | ||||
| 					#ifdef _ShadowMapAtlas | ||||
| 						direct *= shadowTest( | ||||
| 							#ifndef _SingleAtlas | ||||
| 							shadowMapAtlasSpot, shadowMapAtlasSpotTransparent | ||||
| 							#else | ||||
| 							shadowMapAtlas, shadowMapAtlasTransparent | ||||
| 							#endif | ||||
| 							, lPos.xyz / lPos.w, bias, transparent | ||||
| 						); | ||||
| 					#else | ||||
| 							 if (index == 0) direct *= shadowTest(shadowMapSpot[0], shadowMapSpotTransparent[0], lPos.xyz / lPos.w, bias, transparent); | ||||
| 						else if (index == 1) direct *= shadowTest(shadowMapSpot[1], shadowMapSpotTransparent[1], lPos.xyz / lPos.w, bias, transparent); | ||||
| 						else if (index == 2) direct *= shadowTest(shadowMapSpot[2], shadowMapSpotTransparent[2], lPos.xyz / lPos.w, bias, transparent); | ||||
| 						else if (index == 3) direct *= shadowTest(shadowMapSpot[3], shadowMapSpotTransparent[3], lPos.xyz / lPos.w, bias, transparent); | ||||
| 					#endif | ||||
| 				#endif | ||||
| 			} | ||||
| 		#endif | ||||
| 		return direct; | ||||
| 	} | ||||
| 	#endif | ||||
|  | ||||
| 	#ifdef _LightIES | ||||
| 	direct *= iesAttenuation(-l); | ||||
| 	#endif | ||||
|  | ||||
| 	#ifdef _ShadowMap | ||||
| 		if (receiveShadow) { | ||||
| 			#ifdef _SinglePoint | ||||
| 			#ifndef _Spot | ||||
| 			direct *= PCFCube(shadowMapPoint[0], shadowMapPointTransparent[0], ld, -l, bias, lightProj, n, transparent); | ||||
| 			#endif | ||||
| 			#endif | ||||
| 			#ifdef _Clusters | ||||
| 				#ifdef _ShadowMapAtlas | ||||
| 				direct *= PCFFakeCube( | ||||
| 					#ifndef _SingleAtlas | ||||
| 					shadowMapAtlasPoint, shadowMapAtlasPointTransparent | ||||
| 					#else | ||||
| 					shadowMapAtlas, shadowMapAtlasTransparent | ||||
| 					#endif | ||||
| 					, ld, -l, bias, lightProj, n, index, transparent | ||||
| 				); | ||||
| 				#else | ||||
| 					 if (index == 0) direct *= PCFCube(shadowMapPoint[0], shadowMapPointTransparent[0], ld, -l, bias, lightProj, n, transparent); | ||||
| 				else if (index == 1) direct *= PCFCube(shadowMapPoint[1], shadowMapPointTransparent[1], ld, -l, bias, lightProj, n, transparent); | ||||
| 				else if (index == 2) direct *= PCFCube(shadowMapPoint[2], shadowMapPointTransparent[2], ld, -l, bias, lightProj, n, transparent); | ||||
| 				else if (index == 3) direct *= PCFCube(shadowMapPoint[3], shadowMapPointTransparent[3], ld, -l, bias, lightProj, n, transparent); | ||||
| 				#endif | ||||
| 			#endif | ||||
| 		} | ||||
| 	#endif | ||||
|  | ||||
| 	return direct; | ||||
| } | ||||
|  | ||||
| #endif | ||||
|  | ||||
| @ -87,6 +87,40 @@ float lpToDepth(vec3 lp, const vec2 lightProj) { | ||||
| 	return zcomp * 0.5 + 0.5; | ||||
| } | ||||
|  | ||||
| #ifndef _ShadowMapAtlas | ||||
| vec3 PCFCube(samplerCubeShadow shadowMapCube, samplerCube shadowMapCubeTransparent, vec3 lp, vec3 ml, float bias, vec2 lightProj, vec3 n, const bool transparent) { | ||||
|     const float s = shadowmapCubePcfSize; | ||||
|     float compare = lpToDepth(lp, lightProj) - bias * 1.5; | ||||
|     ml = ml + n * bias * 20; | ||||
|     #ifdef _InvY | ||||
|     ml.y = -ml.y; | ||||
|     #endif | ||||
|      | ||||
|     float shadowFactor = 0.0; | ||||
|     shadowFactor = texture(shadowMapCube, vec4(ml, compare)); | ||||
|     shadowFactor += texture(shadowMapCube, vec4(ml + vec3(s, s, s), compare)); | ||||
|     shadowFactor += texture(shadowMapCube, vec4(ml + vec3(-s, s, s), compare)); | ||||
|     shadowFactor += texture(shadowMapCube, vec4(ml + vec3(s, -s, s), compare)); | ||||
|     shadowFactor += texture(shadowMapCube, vec4(ml + vec3(s, s, -s), compare)); | ||||
|     shadowFactor += texture(shadowMapCube, vec4(ml + vec3(-s, -s, s), compare)); | ||||
|     shadowFactor += texture(shadowMapCube, vec4(ml + vec3(s, -s, -s), compare)); | ||||
|     shadowFactor += texture(shadowMapCube, vec4(ml + vec3(-s, s, -s), compare)); | ||||
|     shadowFactor += texture(shadowMapCube, vec4(ml + vec3(-s, -s, -s), compare)); | ||||
|     shadowFactor /= 9.0; | ||||
|      | ||||
|     vec3 result = vec3(shadowFactor); | ||||
|      | ||||
|     if (transparent == false) { | ||||
|         vec4 shadowmap_transparent = texture(shadowMapCubeTransparent, ml); | ||||
|         if (shadowmap_transparent.a < compare) | ||||
|             result *= shadowmap_transparent.rgb; | ||||
|     } | ||||
|  | ||||
|     return result; | ||||
| } | ||||
| #endif | ||||
|  | ||||
| #ifdef _ShadowMapAtlas | ||||
| vec3 PCFCube(samplerCubeShadow shadowMapCube, samplerCube shadowMapCubeTransparent, const vec3 lp, vec3 ml, const float bias, const vec2 lightProj, const vec3 n, const bool transparent) { | ||||
| 	const float s = shadowmapCubePcfSize; // TODO: incorrect... | ||||
| 	float compare = lpToDepth(lp, lightProj) - bias * 1.5; | ||||
| @ -115,7 +149,7 @@ vec3 PCFCube(samplerCubeShadow shadowMapCube, samplerCube shadowMapCubeTranspare | ||||
| 	return result; | ||||
| } | ||||
|  | ||||
| #ifdef _ShadowMapAtlas | ||||
|  | ||||
| // transform "out-of-bounds" coordinates to the correct face/coordinate system | ||||
| // https://www.khronos.org/opengl/wiki/File:CubeMapAxes.png | ||||
| vec2 transformOffsetedUV(const int faceIndex, out int newFaceIndex, vec2 uv) { | ||||
|  | ||||
| @ -11,6 +11,8 @@ vec3 uncharted2Tonemap(const vec3 x) { | ||||
| vec3 tonemapUncharted2(const vec3 color) { | ||||
| 	const float W = 11.2; | ||||
| 	const float exposureBias = 2.0; | ||||
|     // TODO - Find out why black world value of 0.0,0.0,0.0 turns to white pixels | ||||
| 	if (dot(color, color) < 0.001) return vec3(0.001); | ||||
| 	vec3 curr = uncharted2Tonemap(exposureBias * color); | ||||
| 	vec3 whiteScale = 1.0 / uncharted2Tonemap(vec3(W)); | ||||
| 	return curr * whiteScale; | ||||
|  | ||||
| @ -11,6 +11,11 @@ | ||||
| #include "std/light_common.glsl" | ||||
| #endif | ||||
|  | ||||
| #ifdef _CPostprocess | ||||
| uniform vec3 PPComp11; | ||||
| uniform vec4 PPComp17; | ||||
| #endif | ||||
|  | ||||
| uniform sampler2D gbufferD; | ||||
| uniform sampler2D snoise; | ||||
|  | ||||
| @ -87,7 +92,13 @@ out float fragColor; | ||||
| const float tScat = 0.08; | ||||
| const float tAbs = 0.0; | ||||
| const float tExt = tScat + tAbs; | ||||
| const float stepLen = 1.0 / volumSteps; | ||||
| #ifdef _CPostprocess | ||||
| 	float stepLen = 1.0 / int(PPComp11.y); | ||||
| 	float AirTurbidity = PPComp17.w; | ||||
| #else | ||||
| 	const float stepLen = 1.0 / volumSteps; | ||||
| 	float AirTurbidity = volumAirTurbidity; | ||||
| #endif | ||||
| const float lighting = 0.4; | ||||
|  | ||||
| void rayStep(inout vec3 curPos, inout float curOpticalDepth, inout float scatteredLightAmount, float stepLenWorld, vec3 viewVecNorm) { | ||||
| @ -162,5 +173,5 @@ void main() { | ||||
| 		rayStep(curPos, curOpticalDepth, scatteredLightAmount, stepLenWorld, viewVecNorm); | ||||
| 	} | ||||
|  | ||||
| 	fragColor = scatteredLightAmount * volumAirTurbidity; | ||||
| 	fragColor = scatteredLightAmount * AirTurbidity; | ||||
| } | ||||
|  | ||||
| @ -140,6 +140,16 @@ | ||||
| 					"link": "_biasLightWorldViewProjectionMatrixSpot3", | ||||
| 					"ifndef": ["_ShadowMapAtlas"], | ||||
| 					"ifdef": ["_Spot", "_ShadowMap"] | ||||
| 				}, | ||||
| 				{ | ||||
| 					"name": "PPComp11", | ||||
| 					"link": "_PPComp11", | ||||
| 					"ifdef": ["_CPostprocess"] | ||||
| 				}, | ||||
| 				{ | ||||
| 					"name": "PPComp17", | ||||
| 					"link": "_PPComp17", | ||||
| 					"ifdef": ["_CPostprocess"] | ||||
| 				} | ||||
| 			], | ||||
| 			"texture_params": [], | ||||
|  | ||||
| @ -33,7 +33,6 @@ uniform layout(r32ui) uimage3D voxelsLight; | ||||
|  | ||||
| #ifdef _ShadowMap | ||||
| uniform sampler2DShadow shadowMap; | ||||
| uniform sampler2D shadowMapTransparent; | ||||
| uniform sampler2DShadow shadowMapSpot; | ||||
| #ifdef _ShadowMapAtlas | ||||
| uniform sampler2DShadow shadowMapPoint; | ||||
| @ -87,51 +86,53 @@ float lpToDepth(vec3 lp, const vec2 lightProj) { | ||||
|  | ||||
| void main() { | ||||
| 	int res = voxelgiResolution.x; | ||||
| 	for (int i = 0; i < 6; i++) { | ||||
| 		ivec3 dst = ivec3(gl_GlobalInvocationID.xyz); | ||||
| 		vec3 P = (gl_GlobalInvocationID.xyz + 0.5) / voxelgiResolution; | ||||
| 		P = P * 2.0 - 1.0; | ||||
|  | ||||
| 		float visibility; | ||||
| 		vec3 lp = lightPos - P; | ||||
| 		vec3 l; | ||||
| 		if (lightType == 0) { l = lightDir; visibility = 1.0; } | ||||
| 		else { l = normalize(lp); visibility = attenuate(distance(P, lightPos)); } | ||||
| 	ivec3 dst = ivec3(gl_GlobalInvocationID.xyz); | ||||
| 	dst.y += clipmapLevel * res; | ||||
|  | ||||
| 	vec3 P = (gl_GlobalInvocationID.xyz + 0.5) / voxelgiResolution; | ||||
| 	P = P * 2.0 - 1.0; | ||||
| 	P *= clipmaps[int(clipmapLevel * 10)]; | ||||
| 	P *= voxelgiResolution; | ||||
| 	P += vec3(clipmaps[int(clipmapLevel * 10 + 4)], clipmaps[int(clipmapLevel * 10 + 5)], clipmaps[int(clipmapLevel * 10 + 6)]); | ||||
|  | ||||
| 	vec3 visibility; | ||||
| 	vec3 lp = lightPos - P; | ||||
| 	vec3 l; | ||||
| 	if (lightType == 0) { l = lightDir; visibility = vec3(1.0); } | ||||
| 	else { l = normalize(lp); visibility = vec3(attenuate(distance(P, lightPos))); } | ||||
|  | ||||
| #ifdef _ShadowMap | ||||
| 		if (lightShadow == 1) { | ||||
| 			vec4 lightPosition = LVP * vec4(P, 1.0); | ||||
| 			vec3 lPos = lightPosition.xyz / lightPosition.w; | ||||
| 			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)); | ||||
| 	if (lightShadow == 1) { | ||||
| 		vec4 lightPosition = LVP * vec4(P, 1.0); | ||||
| 		vec3 lPos = lightPosition.xyz / lightPosition.w; | ||||
| 		visibility = texture(shadowMap, vec3(lPos.xy, lPos.z - shadowsBias)).rrr; | ||||
| 	} | ||||
| 	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 light = visibility * lightColor; | ||||
|  | ||||
| 	imageAtomicAdd(voxelsLight, dst + ivec3(0, 0, 0), uint(light.r * 255)); | ||||
| 	imageAtomicAdd(voxelsLight, dst + ivec3(0, 0, voxelgiResolution.x), uint(light.g * 255)); | ||||
| 	imageAtomicAdd(voxelsLight, dst + ivec3(0, 0, voxelgiResolution.x * 2), uint(light.b * 255)); | ||||
| } | ||||
|  | ||||
| @ -33,7 +33,7 @@ layout (local_size_x = 8, local_size_y = 8, local_size_z = 1) in; | ||||
| uniform sampler3D voxels; | ||||
| uniform sampler2D gbufferD; | ||||
| uniform sampler2D gbuffer0; | ||||
| uniform layout(r16) image2D voxels_ao; | ||||
| uniform layout(r8) image2D voxels_ao; | ||||
|  | ||||
| uniform float clipmaps[voxelgiClipmapCount * 10]; | ||||
| uniform mat4 InvVP; | ||||
|  | ||||
| @ -33,7 +33,7 @@ layout (local_size_x = 8, local_size_y = 8, local_size_z = 1) in; | ||||
| uniform sampler3D voxels; | ||||
| uniform sampler2D gbufferD; | ||||
| uniform sampler2D gbuffer0; | ||||
| uniform layout(rgba16) image2D voxels_diffuse; | ||||
| uniform layout(rgba8) image2D voxels_diffuse; | ||||
|  | ||||
| uniform float clipmaps[voxelgiClipmapCount * 10]; | ||||
| uniform mat4 InvVP; | ||||
| @ -46,7 +46,7 @@ void main() { | ||||
| 	const vec2 pixel = gl_GlobalInvocationID.xy; | ||||
| 	vec2 uv = (pixel + 0.5) / postprocess_resolution; | ||||
| 	#ifdef _InvY | ||||
| 	uv.y = 1.0 - uv.y; | ||||
| 	uv.y = 1.0 - uv.y | ||||
| 	#endif | ||||
|  | ||||
| 	float depth = textureLod(gbufferD, uv, 0.0).r * 2.0 - 1.0; | ||||
|  | ||||
| @ -34,7 +34,7 @@ uniform sampler2D gbufferD; | ||||
| uniform sampler2D gbuffer0; | ||||
| uniform sampler3D voxels; | ||||
| uniform sampler3D voxelsSDF; | ||||
| uniform layout(rgba16) image2D voxels_specular; | ||||
| uniform layout(rgba8) image2D voxels_specular; | ||||
|  | ||||
| uniform float clipmaps[voxelgiClipmapCount * 10]; | ||||
| uniform mat4 InvVP; | ||||
| @ -71,7 +71,7 @@ void main() { | ||||
|  | ||||
| 	vec2 velocity = -textureLod(sveloc, uv, 0.0).rg; | ||||
|  | ||||
| 	vec3 color = traceSpecular(P, n, voxels, voxelsSDF, normalize(eye - P), g0.z * g0.z, clipmaps, pixel, velocity).rgb; | ||||
| 	vec3 color = traceSpecular(P, n, voxels, voxelsSDF, normalize(eye - P), g0.z, clipmaps, pixel, velocity).rgb; | ||||
|  | ||||
| 	imageStore(voxels_specular, ivec2(pixel), vec4(color, 1.0)); | ||||
| } | ||||
|  | ||||
| @ -23,8 +23,8 @@ THE SOFTWARE. | ||||
|  | ||||
| #include "compiled.inc" | ||||
|  | ||||
| uniform layout(r8) image3D input_sdf; | ||||
| uniform layout(r8) image3D output_sdf; | ||||
| uniform layout(r16) image3D input_sdf; | ||||
| uniform layout(r16) image3D output_sdf; | ||||
|  | ||||
| uniform float jump_size; | ||||
| uniform int clipmapLevel; | ||||
|  | ||||
| @ -46,15 +46,15 @@ uniform layout(r32ui) uimage3D voxels; | ||||
| uniform layout(r32ui) uimage3D voxelsLight; | ||||
| uniform layout(rgba8) image3D voxelsB; | ||||
| uniform layout(rgba8) image3D voxelsOut; | ||||
| uniform layout(r8) image3D SDF; | ||||
| uniform layout(r16) image3D SDF; | ||||
| #else | ||||
| #ifdef _VoxelAOvar | ||||
| #ifdef _VoxelShadow | ||||
| uniform layout(r8) image3D SDF; | ||||
| uniform layout(r16) image3D SDF; | ||||
| #endif | ||||
| uniform layout(r32ui) uimage3D voxels; | ||||
| uniform layout(r8) image3D voxelsB; | ||||
| uniform layout(r8) image3D voxelsOut; | ||||
| uniform layout(r16) image3D voxelsB; | ||||
| uniform layout(r16) image3D voxelsOut; | ||||
| #endif | ||||
| #endif | ||||
|  | ||||
| @ -80,6 +80,7 @@ void main() { | ||||
| 	light.r = float(imageLoad(voxelsLight, src)) / 255; | ||||
| 	light.g = float(imageLoad(voxelsLight, src + ivec3(0, 0, voxelgiResolution.x))) / 255; | ||||
| 	light.b = float(imageLoad(voxelsLight, src + ivec3(0, 0, voxelgiResolution.x * 2))) / 255; | ||||
| 	light /= 3; | ||||
| 	#endif | ||||
|  | ||||
| 	for (int i = 0; i < 6 + DIFFUSE_CONE_COUNT; i++) | ||||
| @ -124,7 +125,7 @@ void main() { | ||||
| 			envl.g = float(imageLoad(voxels, src + ivec3(0, 0, voxelgiResolution.x * 10))) / 255; | ||||
| 			envl.b = float(imageLoad(voxels, src + ivec3(0, 0, voxelgiResolution.x * 11))) / 255; | ||||
| 			envl /= 3; | ||||
|  | ||||
| 			envl *= 100; | ||||
|  | ||||
| 			//clipmap to world | ||||
| 			vec3 wposition = (gl_GlobalInvocationID.xyz + 0.5) / voxelgiResolution.x; | ||||
| @ -136,7 +137,7 @@ void main() { | ||||
| 			radiance = basecol; | ||||
| 			vec4 trace = traceDiffuse(wposition, wnormal, voxelsSampler, clipmaps); | ||||
| 			vec3 indirect = trace.rgb + envl.rgb * (1.0 - trace.a); | ||||
| 			radiance.rgb *= light / PI + indirect; | ||||
| 			radiance.rgb *= light + indirect; | ||||
| 			radiance.rgb += emission.rgb; | ||||
|  | ||||
| 			#else | ||||
|  | ||||
| @ -75,17 +75,16 @@ vec4 binarySearch(vec3 dir) { | ||||
| } | ||||
|  | ||||
| vec4 rayCast(vec3 dir) { | ||||
|     float ddepth; | ||||
|     dir *= ss_refractionRayStep; | ||||
|     for (int i = 0; i < maxSteps; i++) { | ||||
|         hitCoord += dir; | ||||
|         ddepth = getDeltaDepth(hitCoord); | ||||
|         if (ddepth > 0.0) | ||||
|             return binarySearch(dir); | ||||
|     } | ||||
|     // No hit — fallback to projecting the ray to UV space | ||||
|     vec2 fallbackUV = getProjectedCoord(hitCoord); | ||||
|     return vec4(fallbackUV, 0.0, 0.5); // We set .w lower to indicate fallback | ||||
| 	#ifdef _CPostprocess | ||||
| 		dir *= PPComp9.x; | ||||
| 	#else | ||||
| 		dir *= ssrRayStep; | ||||
| 	#endif | ||||
| 	for (int i = 0; i < maxSteps; i++) { | ||||
| 		hitCoord += dir; | ||||
| 		if (getDeltaDepth(hitCoord) > 0.0) return binarySearch(dir); | ||||
| 	} | ||||
| 	return vec4(0.0); | ||||
| } | ||||
| #endif //SSR | ||||
|  | ||||
|  | ||||
| @ -12,6 +12,7 @@ class App { | ||||
| 	static var traitInits: Array<Void->Void> = []; | ||||
| 	static var traitUpdates: Array<Void->Void> = []; | ||||
| 	static var traitLateUpdates: Array<Void->Void> = []; | ||||
| 	static var traitFixedUpdates: Array<Void->Void> = []; | ||||
| 	static var traitRenders: Array<kha.graphics4.Graphics->Void> = []; | ||||
| 	static var traitRenders2D: Array<kha.graphics2.Graphics->Void> = []; | ||||
| 	public static var framebuffer: kha.Framebuffer; | ||||
| @ -23,6 +24,8 @@ class App { | ||||
| 	public static var renderPathTime: Float; | ||||
| 	public static var endFrameCallbacks: Array<Void->Void> = []; | ||||
| 	#end | ||||
| 	static var last = 0.0; | ||||
| 	static var time = 0.0; | ||||
| 	static var lastw = -1; | ||||
| 	static var lasth = -1; | ||||
| 	public static var onResize: Void->Void = null; | ||||
| @ -34,13 +37,14 @@ class App { | ||||
| 	function new(done: Void->Void) { | ||||
| 		done(); | ||||
| 		kha.System.notifyOnFrames(render); | ||||
| 		kha.Scheduler.addTimeTask(update, 0, iron.system.Time.delta); | ||||
| 		kha.Scheduler.addTimeTask(update, 0, iron.system.Time.step); | ||||
| 	} | ||||
|  | ||||
| 	public static function reset() { | ||||
| 		traitInits = []; | ||||
| 		traitUpdates = []; | ||||
| 		traitLateUpdates = []; | ||||
| 		traitFixedUpdates = []; | ||||
| 		traitRenders = []; | ||||
| 		traitRenders2D = []; | ||||
| 		if (onResets != null) for (f in onResets) f(); | ||||
| @ -48,6 +52,24 @@ class App { | ||||
|  | ||||
| 	static function update() { | ||||
| 		if (Scene.active == null || !Scene.active.ready) return; | ||||
| 		 | ||||
| 		iron.system.Time.update(); | ||||
| 		 | ||||
| 		if (lastw == -1) { | ||||
| 			lastw = App.w(); | ||||
| 			lasth = App.h(); | ||||
| 		} | ||||
| 		if (lastw != App.w() || lasth != App.h()) { | ||||
| 			if (onResize != null) onResize(); | ||||
| 			else { | ||||
| 				if (Scene.active != null && Scene.active.camera != null) { | ||||
| 					Scene.active.camera.buildProjection(); | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		lastw = App.w(); | ||||
| 		lasth = App.h(); | ||||
| 		 | ||||
| 		if (pauseUpdates) return; | ||||
|  | ||||
| 		#if lnx_debug | ||||
| @ -56,6 +78,14 @@ class App { | ||||
|  | ||||
| 		Scene.active.updateFrame(); | ||||
|  | ||||
|  | ||||
| 		time += iron.system.Time.delta; | ||||
|  | ||||
| 		while (time >= iron.system.Time.fixedStep) { | ||||
| 			for (f in traitFixedUpdates) f(); | ||||
| 			time -= iron.system.Time.fixedStep; | ||||
| 		} | ||||
|  | ||||
| 		var i = 0; | ||||
| 		var l = traitUpdates.length; | ||||
| 		while (i < l) { | ||||
| @ -84,29 +114,13 @@ class App { | ||||
| 		for (cb in endFrameCallbacks) cb(); | ||||
| 		updateTime = kha.Scheduler.realTime() - startTime; | ||||
| 		#end | ||||
|  | ||||
| 		// Rebuild projection on window resize | ||||
| 		if (lastw == -1) { | ||||
| 			lastw = App.w(); | ||||
| 			lasth = App.h(); | ||||
| 		} | ||||
| 		if (lastw != App.w() || lasth != App.h()) { | ||||
| 			if (onResize != null) onResize(); | ||||
| 			else { | ||||
| 				if (Scene.active != null && Scene.active.camera != null) { | ||||
| 					Scene.active.camera.buildProjection(); | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		lastw = App.w(); | ||||
| 		lasth = App.h(); | ||||
| 	} | ||||
|  | ||||
| 	static function render(frames: Array<kha.Framebuffer>) { | ||||
| 		var frame = frames[0]; | ||||
| 		framebuffer = frame; | ||||
|  | ||||
| 		iron.system.Time.update(); | ||||
| 		iron.system.Time.render(); | ||||
|  | ||||
| 		if (Scene.active == null || !Scene.active.ready) { | ||||
| 			render2D(frame); | ||||
| @ -172,6 +186,14 @@ class App { | ||||
| 		traitLateUpdates.remove(f); | ||||
| 	} | ||||
|  | ||||
| 	public static function notifyOnFixedUpdate(f: Void->Void) { | ||||
| 		traitFixedUpdates.push(f); | ||||
| 	} | ||||
|  | ||||
| 	public static function removeFixedUpdate(f: Void->Void) { | ||||
| 		traitFixedUpdates.remove(f); | ||||
| 	} | ||||
|  | ||||
| 	public static function notifyOnRender(f: kha.graphics4.Graphics->Void) { | ||||
| 		traitRenders.push(f); | ||||
| 	} | ||||
|  | ||||
| @ -331,15 +331,18 @@ class RenderPath { | ||||
| 		}); | ||||
| 	} | ||||
|  | ||||
| 	public static function sortMeshesShader(meshes: Array<MeshObject>) { | ||||
| 	public static function sortMeshesIndex(meshes: Array<MeshObject>) { | ||||
| 		meshes.sort(function(a, b): Int { | ||||
| 			#if rp_depth_texture | ||||
| 			var depthDiff = boolToInt(a.depthRead) - boolToInt(b.depthRead); | ||||
| 			if (depthDiff != 0) return depthDiff; | ||||
| 			#end | ||||
|  | ||||
| 			return a.materials[0].name >= b.materials[0].name ? 1 : -1; | ||||
| 		}); | ||||
| 			if (a.data.sortingIndex != b.data.sortingIndex) { | ||||
| 				return a.data.sortingIndex > b.data.sortingIndex ? 1 : -1; | ||||
| 			} | ||||
|  | ||||
| 			return a.data.name >= b.data.name ? 1 : -1;		}); | ||||
| 	} | ||||
|  | ||||
| 	public function drawMeshes(context: String) { | ||||
| @ -399,7 +402,7 @@ class RenderPath { | ||||
| 			#if lnx_batch | ||||
| 			sortMeshesDistance(Scene.active.meshBatch.nonBatched); | ||||
| 			#else | ||||
| 			drawOrder == DrawOrder.Shader ? sortMeshesShader(meshes) : sortMeshesDistance(meshes); | ||||
| 			drawOrder == DrawOrder.Index ? sortMeshesIndex(meshes) : sortMeshesDistance(meshes); | ||||
| 			#end | ||||
| 			meshesSorted = true; | ||||
| 		} | ||||
| @ -518,12 +521,44 @@ class RenderPath { | ||||
| 		return Reflect.field(kha.Shaders, handle + "_comp"); | ||||
| 	} | ||||
|  | ||||
| 	#if (kha_krom && lnx_vr) | ||||
| 	public function drawStereo(drawMeshes: Int->Void) { | ||||
| 		for (eye in 0...2) { | ||||
| 			Krom.vrBeginRender(eye); | ||||
| 			drawMeshes(eye); | ||||
| 			Krom.vrEndRender(eye); | ||||
| 	#if lnx_vr | ||||
| 	public function drawStereo(drawMeshes: Void->Void) { | ||||
| 		var vr = kha.vr.VrInterface.instance; | ||||
| 		var appw = iron.App.w(); | ||||
| 		var apph = iron.App.h(); | ||||
| 		var halfw = Std.int(appw / 2); | ||||
| 		var g = currentG; | ||||
|  | ||||
| 		if (vr != null && vr.IsPresenting()) { | ||||
| 			// Left eye | ||||
| 			Scene.active.camera.V.setFrom(Scene.active.camera.leftV); | ||||
| 			Scene.active.camera.P.self = vr.GetProjectionMatrix(0); | ||||
| 			g.viewport(0, 0, halfw, apph); | ||||
| 			drawMeshes(); | ||||
|  | ||||
| 			// Right eye | ||||
| 			begin(g, additionalTargets); | ||||
| 			Scene.active.camera.V.setFrom(Scene.active.camera.rightV); | ||||
| 			Scene.active.camera.P.self = vr.GetProjectionMatrix(1); | ||||
| 			g.viewport(halfw, 0, halfw, apph); | ||||
| 			drawMeshes(); | ||||
| 		} | ||||
| 		else { // Simulate | ||||
| 			Scene.active.camera.buildProjection(halfw / apph); | ||||
|  | ||||
| 			// Left eye | ||||
| 			g.viewport(0, 0, halfw, apph); | ||||
| 			drawMeshes(); | ||||
|  | ||||
| 			// Right eye | ||||
| 			begin(g, additionalTargets); | ||||
| 			Scene.active.camera.transform.move(Scene.active.camera.right(), 0.032); | ||||
| 			Scene.active.camera.buildMatrix(); | ||||
| 			g.viewport(halfw, 0, halfw, apph); | ||||
| 			drawMeshes(); | ||||
|  | ||||
| 			Scene.active.camera.transform.move(Scene.active.camera.right(), -0.032); | ||||
| 			Scene.active.camera.buildMatrix(); | ||||
| 		} | ||||
| 	} | ||||
| 	#end | ||||
| @ -882,6 +917,6 @@ class CachedShaderContext { | ||||
|  | ||||
| @:enum abstract DrawOrder(Int) from Int { | ||||
| 	var Distance = 0; // Early-z | ||||
| 	var Shader = 1; // Less state changes | ||||
| 	var Index = 1; // Less state changes | ||||
| 	// var Mix = 2; // Distance buckets sorted by shader | ||||
| } | ||||
|  | ||||
| @ -775,6 +775,7 @@ class Scene { | ||||
| 			// Attach particle systems | ||||
| 			#if lnx_particles | ||||
| 			if (o.particle_refs != null) { | ||||
| 				cast(object, MeshObject).render_emitter = o.render_emitter; | ||||
| 				for (ref in o.particle_refs) cast(object, MeshObject).setupParticleSystem(sceneName, ref); | ||||
| 			} | ||||
| 			#end | ||||
| @ -782,6 +783,11 @@ class Scene { | ||||
| 			if (o.tilesheet_ref != null) { | ||||
| 				cast(object, MeshObject).setupTilesheet(sceneName, o.tilesheet_ref, o.tilesheet_action_ref); | ||||
| 			} | ||||
| 			 | ||||
| 			if (o.camera_list != null){ | ||||
| 				cast(object, MeshObject).cameraList = o.camera_list; | ||||
| 			} | ||||
|  | ||||
| 			returnObject(object, o, done); | ||||
| 		}); | ||||
| 	} | ||||
| @ -881,8 +887,12 @@ class Scene { | ||||
| 						var ptype: String = t.props[i * 3 + 1]; | ||||
| 						var pval: Dynamic = t.props[i * 3 + 2]; | ||||
|  | ||||
| 						if (StringTools.endsWith(ptype, "Object") && pval != "") { | ||||
| 						if (StringTools.endsWith(ptype, "Object") && pval != "" && pval != null) { | ||||
| 							Reflect.setProperty(traitInst, pname, Scene.active.getChild(pval)); | ||||
| 						} else if (ptype == "TSceneFormat" && pval != "") { | ||||
| 							Data.getSceneRaw(pval, function (r: TSceneFormat) { | ||||
| 								Reflect.setProperty(traitInst, pname, r); | ||||
| 							}); | ||||
| 						} | ||||
| 						else { | ||||
| 							switch (ptype) { | ||||
|  | ||||
| @ -16,6 +16,7 @@ class Trait { | ||||
| 	var _remove: Array<Void->Void> = null; | ||||
| 	var _update: Array<Void->Void> = null; | ||||
| 	var _lateUpdate: Array<Void->Void> = null; | ||||
| 	var _fixedUpdate: Array<Void->Void> = null; | ||||
| 	var _render: Array<kha.graphics4.Graphics->Void> = null; | ||||
| 	var _render2D: Array<kha.graphics2.Graphics->Void> = null; | ||||
|  | ||||
| @ -87,6 +88,23 @@ class Trait { | ||||
| 		App.removeLateUpdate(f); | ||||
| 	} | ||||
|  | ||||
|     /** | ||||
|       Add fixed game logic handler. | ||||
|     **/ | ||||
| 	public function notifyOnFixedUpdate(f: Void->Void) { | ||||
| 		if (_fixedUpdate == null) _fixedUpdate = []; | ||||
| 		_fixedUpdate.push(f); | ||||
| 		App.notifyOnFixedUpdate(f); | ||||
| 	} | ||||
|  | ||||
|     /** | ||||
|       Remove fixed game logic handler. | ||||
|     **/ | ||||
| 	public function removeFixedUpdate(f: Void->Void) { | ||||
| 		_fixedUpdate.remove(f); | ||||
| 		App.removeFixedUpdate(f); | ||||
| 	} | ||||
|  | ||||
|     /** | ||||
|       Add render handler. | ||||
|     **/ | ||||
|  | ||||
| @ -9,6 +9,7 @@ import iron.data.SceneFormat; | ||||
| class MeshData { | ||||
|  | ||||
| 	public var name: String; | ||||
| 	public var sortingIndex: Int; | ||||
| 	public var raw: TMeshData; | ||||
| 	public var format: TSceneFormat; | ||||
| 	public var geom: Geometry; | ||||
| @ -23,7 +24,8 @@ class MeshData { | ||||
| 	public function new(raw: TMeshData, done: MeshData->Void) { | ||||
| 		this.raw = raw; | ||||
| 		this.name = raw.name; | ||||
|  | ||||
| 		this.sortingIndex = raw.sorting_index; | ||||
| 		 | ||||
| 		if (raw.scale_pos != null) scalePos = raw.scale_pos; | ||||
| 		if (raw.scale_tex != null) scaleTex = raw.scale_tex; | ||||
|  | ||||
|  | ||||
| @ -49,6 +49,7 @@ typedef TMeshData = { | ||||
| @:structInit class TMeshData { | ||||
| #end | ||||
| 	public var name: String; | ||||
| 	public var sorting_index: Int; | ||||
| 	public var vertex_arrays: Array<TVertexArray>; | ||||
| 	public var index_arrays: Array<TIndexArray>; | ||||
| 	@:optional public var dynamic_usage: Null<Bool>; | ||||
| @ -222,6 +223,7 @@ typedef TShaderData = { | ||||
| @:structInit class TShaderData { | ||||
| #end | ||||
| 	public var name: String; | ||||
| 	public var next_pass: String; | ||||
| 	public var contexts: Array<TShaderContext>; | ||||
| } | ||||
|  | ||||
| @ -392,6 +394,9 @@ typedef TParticleData = { | ||||
| #end | ||||
| 	public var name: String; | ||||
| 	public var type: Int; // 0 - Emitter, Hair | ||||
| 	public var auto_start: Bool; | ||||
| 	public var dynamic_emitter: Bool; | ||||
| 	public var is_unique: Bool; | ||||
| 	public var loop: Bool; | ||||
| 	public var count: Int; | ||||
| 	public var frame_start: FastFloat; | ||||
| @ -439,6 +444,7 @@ typedef TObj = { | ||||
| 	@:optional public var traits: Array<TTrait>; | ||||
| 	@:optional public var properties: Array<TProperty>; | ||||
| 	@:optional public var vertex_groups: Array<TVertex_groups>; | ||||
| 	@:optional public var camera_list: Array<String>; | ||||
| 	@:optional public var constraints: Array<TConstraint>; | ||||
| 	@:optional public var dimensions: Float32Array; // Geometry objects | ||||
| 	@:optional public var object_actions: Array<String>; | ||||
|  | ||||
| @ -22,6 +22,7 @@ using StringTools; | ||||
| class ShaderData { | ||||
|  | ||||
| 	public var name: String; | ||||
| 	public var nextPass: String; | ||||
| 	public var raw: TShaderData; | ||||
| 	public var contexts: Array<ShaderContext> = []; | ||||
|  | ||||
| @ -33,6 +34,7 @@ class ShaderData { | ||||
| 	public function new(raw: TShaderData, done: ShaderData->Void, overrideContext: TShaderOverride = null) { | ||||
| 		this.raw = raw; | ||||
| 		this.name = raw.name; | ||||
| 		this.nextPass = raw.next_pass; | ||||
|  | ||||
| 		for (c in raw.contexts) contexts.push(null); | ||||
| 		var contextsLoaded = 0; | ||||
|  | ||||
							
								
								
									
										50
									
								
								leenkx/Sources/iron/format/bmp/Data.hx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								leenkx/Sources/iron/format/bmp/Data.hx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,50 @@ | ||||
| /* | ||||
|  * format - Haxe File Formats | ||||
|  * | ||||
|  *  BMP File Format | ||||
|  *  Copyright (C) 2007-2009 Trevor McCauley, Baluta Cristian (hx port) & Robert Sköld (format conversion) | ||||
|  * | ||||
|  * Copyright (c) 2009, The Haxe Project Contributors | ||||
|  * All rights reserved. | ||||
|  * Redistribution and use in source and binary forms, with or without | ||||
|  * modification, are permitted provided that the following conditions are met: | ||||
|  * | ||||
|  *   - Redistributions of source code must retain the above copyright | ||||
|  *     notice, this list of conditions and the following disclaimer. | ||||
|  *   - Redistributions in binary form must reproduce the above copyright | ||||
|  *     notice, this list of conditions and the following disclaimer in the | ||||
|  *     documentation and/or other materials provided with the distribution. | ||||
|  * | ||||
|  * THIS SOFTWARE IS PROVIDED BY THE HAXE PROJECT CONTRIBUTORS "AS IS" AND ANY | ||||
|  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||||
|  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||||
|  * DISCLAIMED. IN NO EVENT SHALL THE HAXE PROJECT CONTRIBUTORS BE LIABLE FOR | ||||
|  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | ||||
|  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | ||||
|  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | ||||
|  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | ||||
|  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | ||||
|  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH | ||||
|  * DAMAGE. | ||||
|  */ | ||||
| package iron.format.bmp; | ||||
|  | ||||
| typedef Data = { | ||||
|     var header : iron.format.bmp.Header; | ||||
|     var pixels : haxe.io.Bytes; | ||||
| #if (haxe_ver < 4) | ||||
|     var colorTable : Null<haxe.io.Bytes>; | ||||
| #else | ||||
|     var ?colorTable : haxe.io.Bytes; | ||||
| #end | ||||
| } | ||||
|  | ||||
| typedef Header = { | ||||
|     var width : Int;          // real width (in pixels) | ||||
|     var height : Int;         // real height (in pixels) | ||||
|     var paddedStride : Int;   // number of bytes in a stride (including padding) | ||||
|     var topToBottom : Bool;   // whether the bitmap is stored top to bottom | ||||
|     var bpp : Int;            // bits per pixel | ||||
|     var dataLength : Int;     // equal to `paddedStride` * `height` | ||||
|     var compression : Int;    // which compression is being used, 0 for no compression | ||||
| } | ||||
							
								
								
									
										122
									
								
								leenkx/Sources/iron/format/bmp/Reader.hx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										122
									
								
								leenkx/Sources/iron/format/bmp/Reader.hx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,122 @@ | ||||
| /* | ||||
|  * format - Haxe File Formats | ||||
|  * | ||||
|  *  BMP File Format | ||||
|  *  Copyright (C) 2007-2009 Robert Sköld | ||||
|  * | ||||
|  * Copyright (c) 2009, The Haxe Project Contributors | ||||
|  * All rights reserved. | ||||
|  * Redistribution and use in source and binary forms, with or without | ||||
|  * modification, are permitted provided that the following conditions are met: | ||||
|  * | ||||
|  *   - Redistributions of source code must retain the above copyright | ||||
|  *     notice, this list of conditions and the following disclaimer. | ||||
|  *   - Redistributions in binary form must reproduce the above copyright | ||||
|  *     notice, this list of conditions and the following disclaimer in the | ||||
|  *     documentation and/or other materials provided with the distribution. | ||||
|  * | ||||
|  * THIS SOFTWARE IS PROVIDED BY THE HAXE PROJECT CONTRIBUTORS "AS IS" AND ANY | ||||
|  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||||
|  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||||
|  * DISCLAIMED. IN NO EVENT SHALL THE HAXE PROJECT CONTRIBUTORS BE LIABLE FOR | ||||
|  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | ||||
|  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | ||||
|  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | ||||
|  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | ||||
|  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | ||||
|  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH | ||||
|  * DAMAGE. | ||||
|  */ | ||||
|  | ||||
| package iron.format.bmp; | ||||
|  | ||||
| import iron.format.bmp.Data; | ||||
|  | ||||
|  | ||||
| class Reader { | ||||
|  | ||||
| 	var input : haxe.io.Input; | ||||
|  | ||||
| 	public function new( i ) { | ||||
| 		input = i; | ||||
| 	} | ||||
|  | ||||
| 	/**  | ||||
| 	 * Only supports uncompressed 24bpp bitmaps (the most common format). | ||||
| 	 *  | ||||
| 	 * The returned bytes in `Data.pixels` will be in BGR order, and with padding (if present). | ||||
| 	 *  | ||||
| 	 * @see https://msdn.microsoft.com/en-us/library/windows/desktop/dd318229(v=vs.85).aspx | ||||
| 	 * @see https://en.wikipedia.org/wiki/BMP_file_format#Bitmap_file_header | ||||
| 	 */ | ||||
| 	public function read() : format.bmp.Data { | ||||
| 		// Read Header | ||||
| 		for (b in ["B".code, "M".code]) { | ||||
| 			if (input.readByte() != b) throw "Invalid header"; | ||||
| 		} | ||||
| 	 | ||||
| 		var fileSize = input.readInt32(); | ||||
| 		input.readInt32();							// Reserved | ||||
| 		var offset = input.readInt32(); | ||||
|  | ||||
| 		// Read InfoHeader | ||||
| 		var infoHeaderSize = input.readInt32();		// InfoHeader size | ||||
| 		if (infoHeaderSize != 40) { | ||||
| 			throw 'Info headers with size $infoHeaderSize not supported.'; | ||||
| 		} | ||||
| 		var width = input.readInt32();				// Image width (actual, not padded) | ||||
| 		var height = input.readInt32();				// Image height | ||||
| 		var numPlanes = input.readInt16();			// Number of planes | ||||
| 		var bits = input.readInt16();				// Bits per pixel | ||||
| 		var compression = input.readInt32();		// Compression type | ||||
| 		var dataLength = input.readInt32();			// Image data size (includes padding!) | ||||
| 		input.readInt32();							// Horizontal resolution | ||||
| 		input.readInt32();							// Vertical resolution | ||||
| 		var colorsUsed = input.readInt32();			// Colors used (0 when uncompressed) | ||||
| 		input.readInt32();							// Important colors (0 when uncompressed) | ||||
|  | ||||
| 		// If there's no compression, the dataLength may be 0 | ||||
| 		if ( compression == 0 && dataLength == 0 ) dataLength = fileSize - offset; | ||||
|  | ||||
| 		var bytesRead = 54; // total read above | ||||
| 		 | ||||
| 		var colorTable : haxe.io.Bytes = null; | ||||
| 		if ( bits <= 8 ) { | ||||
| 			if ( colorsUsed == 0 ) { | ||||
| 				colorsUsed = Tools.getNumColorsForBitDepth(bits); | ||||
| 			} | ||||
| 			var colorTableLength = 4 * colorsUsed; | ||||
| 			colorTable = haxe.io.Bytes.alloc( colorTableLength ); | ||||
| 			input.readFullBytes( colorTable, 0, colorTableLength ); | ||||
| 			bytesRead += colorTableLength; | ||||
| 		} | ||||
|  | ||||
| 		input.read( offset - bytesRead ); | ||||
| 		 | ||||
| 		var p = haxe.io.Bytes.alloc( dataLength );	 | ||||
| 		 | ||||
| 		// Read Raster Data | ||||
| 		var paddedStride = Tools.computePaddedStride(width, bits); | ||||
| 		var topToBottom = false; | ||||
| 		if ( height < 0 ) { // if bitmap is stored top to bottom | ||||
| 			topToBottom = true; | ||||
| 			height = -height; | ||||
| 		} | ||||
|      | ||||
| 		input.readFullBytes(p, 0, dataLength); | ||||
| 			 | ||||
| 		return { | ||||
| 			header: { | ||||
| 				width: width, | ||||
| 				height: height, | ||||
| 				paddedStride: paddedStride, | ||||
| 				topToBottom: topToBottom, | ||||
| 				bpp: bits, | ||||
| 				dataLength: dataLength, | ||||
| 				compression: compression | ||||
| 			}, | ||||
| 			pixels: p, | ||||
| 			colorTable: colorTable | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										256
									
								
								leenkx/Sources/iron/format/bmp/Tools.hx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										256
									
								
								leenkx/Sources/iron/format/bmp/Tools.hx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,256 @@ | ||||
| /* | ||||
|  * format - Haxe File Formats | ||||
|  * | ||||
|  *  BMP File Format | ||||
|  *  Copyright (C) 2007-2009 Trevor McCauley, Baluta Cristian (hx port) & Robert Sköld (format conversion) | ||||
|  * | ||||
|  * Copyright (c) 2009, The Haxe Project Contributors | ||||
|  * All rights reserved. | ||||
|  * Redistribution and use in source and binary forms, with or without | ||||
|  * modification, are permitted provided that the following conditions are met: | ||||
|  * | ||||
|  *   - Redistributions of source code must retain the above copyright | ||||
|  *     notice, this list of conditions and the following disclaimer. | ||||
|  *   - Redistributions in binary form must reproduce the above copyright | ||||
|  *     notice, this list of conditions and the following disclaimer in the | ||||
|  *     documentation and/or other materials provided with the distribution. | ||||
|  * | ||||
|  * THIS SOFTWARE IS PROVIDED BY THE HAXE PROJECT CONTRIBUTORS "AS IS" AND ANY | ||||
|  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||||
|  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||||
|  * DISCLAIMED. IN NO EVENT SHALL THE HAXE PROJECT CONTRIBUTORS BE LIABLE FOR | ||||
|  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | ||||
|  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | ||||
|  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | ||||
|  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | ||||
|  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | ||||
|  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH | ||||
|  * DAMAGE. | ||||
|  */ | ||||
| package iron.format.bmp; | ||||
|  | ||||
|  | ||||
| class Tools { | ||||
|  | ||||
| 	//												  a  r  g  b | ||||
| 	static var ARGB_MAP(default, never):Array<Int> = [0, 1, 2, 3]; | ||||
| 	static var BGRA_MAP(default, never):Array<Int> = [3, 2, 1, 0]; | ||||
|  | ||||
| 	static var COLOR_SIZE(default, never):Int = 4; | ||||
| 	 | ||||
| 	/** | ||||
| 		Extract BMP pixel data (24bpp in BGR format) and expands it to BGRA, removing any padding in the process. | ||||
| 	**/ | ||||
| 	inline static public function extractBGRA( bmp : iron.format.bmp.Data ) : haxe.io.Bytes { | ||||
| 		return _extract32(bmp, BGRA_MAP, 0xFF); | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 		Extract BMP pixel data (24bpp in BGR format) and converts it to ARGB. | ||||
| 	**/ | ||||
| 	inline static public function extractARGB( bmp : iron.format.bmp.Data ) : haxe.io.Bytes { | ||||
| 		return _extract32(bmp, ARGB_MAP, 0xFF); | ||||
| 	} | ||||
|    | ||||
| 	/** | ||||
| 		Creates BMP data from bytes in BGRA format for each pixel. | ||||
| 	**/ | ||||
| 	inline static public function buildFromBGRA( width : Int, height : Int, srcBytes : haxe.io.Bytes, topToBottom : Bool = false ) : Data { | ||||
| 		return _buildFrom32(width, height, srcBytes, BGRA_MAP, topToBottom); | ||||
| 	} | ||||
|    | ||||
| 	/** | ||||
| 		Creates BMP data from bytes in ARGB format for each pixel. | ||||
| 	**/ | ||||
| 	inline static public function buildFromARGB( width : Int, height : Int, srcBytes : haxe.io.Bytes, topToBottom : Bool = false ) : Data { | ||||
| 		return _buildFrom32(width, height, srcBytes, ARGB_MAP, topToBottom); | ||||
| 	} | ||||
|  | ||||
| 	inline static public function computePaddedStride(width:Int, bpp:Int):Int { | ||||
| 		return ((((width * bpp) + 31) & ~31) >> 3); | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * Gets number of colors for indexed palettes | ||||
| 	 */ | ||||
| 	inline static public function getNumColorsForBitDepth(bpp:Int):Int { | ||||
| 		return switch (bpp) { | ||||
| 			case 1: 2; | ||||
| 			case 4: 16; | ||||
| 			case 8: 256; | ||||
| 			case 16: 65536; | ||||
| 			default: throw 'Unsupported bpp $bpp'; | ||||
| 		} | ||||
| 	} | ||||
| 	 | ||||
| 	 | ||||
| 	// `channelMap` contains indices to map into ARGB (f.e. the mapping for ARGB is [0,1,2,3], while for BGRA is [3,2,1,0]) | ||||
| 	static function _extract32( bmp : iron.format.bmp.Data, channelMap : Array<Int>, alpha : Int = 0xFF) : haxe.io.Bytes { | ||||
| 		var srcBytes = bmp.pixels; | ||||
| 		var dstLen = bmp.header.width * bmp.header.height * 4; | ||||
| 		var dstBytes = haxe.io.Bytes.alloc( dstLen ); | ||||
| 		var srcPaddedStride = bmp.header.paddedStride; | ||||
| 		 | ||||
| 		var yDir = -1; | ||||
| 		var dstPos = 0; | ||||
| 		var srcPos = srcPaddedStride * (bmp.header.height - 1); | ||||
|      | ||||
| 		if ( bmp.header.topToBottom ) { | ||||
| 			yDir = 1; | ||||
| 			srcPos = 0; | ||||
| 		} | ||||
|  | ||||
| 		if ( bmp.header.bpp < 8 || bmp.header.bpp == 16 ) { | ||||
| 			throw 'bpp ${bmp.header.bpp} not supported'; | ||||
| 		} | ||||
|  | ||||
| 		var colorTable:haxe.io.Bytes = null; | ||||
| 		if ( bmp.header.bpp <= 8 ) { | ||||
| 			var colorTableLength = getNumColorsForBitDepth(bmp.header.bpp); | ||||
| 			colorTable = haxe.io.Bytes.alloc(colorTableLength * COLOR_SIZE); | ||||
| 			var definedColorTableLength = Std.int( bmp.colorTable.length / COLOR_SIZE ); | ||||
| 			for( i in 0...definedColorTableLength ) { | ||||
| 				var b = bmp.colorTable.get( i * COLOR_SIZE); | ||||
| 				var g = bmp.colorTable.get( i * COLOR_SIZE + 1); | ||||
| 				var r = bmp.colorTable.get( i * COLOR_SIZE + 2); | ||||
|  | ||||
| 				colorTable.set(i * COLOR_SIZE + channelMap[0], alpha); | ||||
| 				colorTable.set(i * COLOR_SIZE + channelMap[1], r); | ||||
| 				colorTable.set(i * COLOR_SIZE + channelMap[2], g); | ||||
| 				colorTable.set(i * COLOR_SIZE + channelMap[3], b); | ||||
| 			} | ||||
| 			// We want to have the table the full length in case indices outside the range are present | ||||
| 			colorTable.fill(definedColorTableLength, colorTableLength - definedColorTableLength, 0); | ||||
| 			for( i in definedColorTableLength...colorTableLength ) { | ||||
| 				colorTable.set(i * COLOR_SIZE + channelMap[0], alpha); | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		switch bmp.header.compression { | ||||
| 			case 0: | ||||
| 				while( dstPos < dstLen ) { | ||||
| 					for( i in 0...bmp.header.width ) { | ||||
| 						if (bmp.header.bpp == 8) { | ||||
|  | ||||
| 							var currentSrcPos = srcPos + i; | ||||
| 							var index = srcBytes.get(currentSrcPos); | ||||
| 							dstBytes.blit( dstPos, colorTable, index * COLOR_SIZE, COLOR_SIZE ); | ||||
|  | ||||
| 						} else if (bmp.header.bpp == 24) { | ||||
|  | ||||
| 							var currentSrcPos = srcPos + i * 3; | ||||
| 							var b = srcBytes.get(currentSrcPos); | ||||
| 							var g = srcBytes.get(currentSrcPos + 1); | ||||
| 							var r = srcBytes.get(currentSrcPos + 2); | ||||
| 							 | ||||
| 							dstBytes.set(dstPos + channelMap[0], alpha); | ||||
| 							dstBytes.set(dstPos + channelMap[1], r); | ||||
| 							dstBytes.set(dstPos + channelMap[2], g); | ||||
| 							dstBytes.set(dstPos + channelMap[3], b); | ||||
|  | ||||
| 						} else if (bmp.header.bpp == 32) { | ||||
|  | ||||
| 							var currentSrcPos = srcPos + i * 4; | ||||
| 							var b = srcBytes.get(currentSrcPos); | ||||
| 							var g = srcBytes.get(currentSrcPos + 1); | ||||
| 							var r = srcBytes.get(currentSrcPos + 2); | ||||
| 							 | ||||
| 							dstBytes.set(dstPos + channelMap[0], alpha); | ||||
| 							dstBytes.set(dstPos + channelMap[1], r); | ||||
| 							dstBytes.set(dstPos + channelMap[2], g); | ||||
| 							dstBytes.set(dstPos + channelMap[3], b); | ||||
|  | ||||
| 						} | ||||
| 						dstPos += 4; | ||||
| 					} | ||||
| 					srcPos += yDir * srcPaddedStride; | ||||
| 				} | ||||
| 			case 1: | ||||
| 				srcPos = 0; | ||||
| 				var x = 0; | ||||
| 				var y = bmp.header.topToBottom ? 0 : bmp.header.height - 1; | ||||
| 				while( srcPos < bmp.header.dataLength ) { | ||||
| 					var count = srcBytes.get(srcPos++); | ||||
| 					var index = srcBytes.get(srcPos++); | ||||
| 					if ( count == 0 ) { | ||||
| 						if ( index == 0 ) { | ||||
| 							x = 0; | ||||
| 							y += yDir; | ||||
| 						} else if ( index == 1 ) { | ||||
| 							break; | ||||
| 						} else if ( index == 2 ) { | ||||
| 							x += srcBytes.get(srcPos++); | ||||
| 							y += srcBytes.get(srcPos++); | ||||
| 						} else { | ||||
| 							count = index; | ||||
| 							for( i in 0...count ) { | ||||
| 								index = srcBytes.get(srcPos++); | ||||
| 								dstBytes.blit( COLOR_SIZE * ((x+i) + y * bmp.header.width), colorTable, index * COLOR_SIZE, COLOR_SIZE ); | ||||
| 							} | ||||
| 							if (srcPos % 2 != 0) srcPos++; | ||||
| 							x += count; | ||||
| 						} | ||||
| 					} else { | ||||
| 						for( i in 0...count ) { | ||||
| 							dstBytes.blit( COLOR_SIZE * ((x+i) + y * bmp.header.width), colorTable, index * COLOR_SIZE, COLOR_SIZE ); | ||||
| 						} | ||||
| 						x += count; | ||||
| 					} | ||||
| 				} | ||||
| 			default: | ||||
| 				throw 'compression ${bmp.header.compression} not supported'; | ||||
| 		} | ||||
|  | ||||
| 		return dstBytes; | ||||
| 	} | ||||
| 	 | ||||
| 	// `channelMap` contains indices to map into ARGB (f.e. the mapping for ARGB is [0,1,2,3], while for BGRA is [3,2,1,0]) | ||||
| 	static function _buildFrom32( width : Int, height : Int, srcBytes : haxe.io.Bytes, channelMap : Array<Int>, topToBottom : Bool = false ) : Data { | ||||
| 		var bpp = 24; | ||||
| 		var paddedStride = computePaddedStride(width, bpp); | ||||
| 		var bytesBGR = haxe.io.Bytes.alloc(paddedStride * height); | ||||
| 		var topToBottom = topToBottom; | ||||
| 		var dataLength = bytesBGR.length; | ||||
| 		 | ||||
| 		var dstStride = width * 3; | ||||
| 		var srcLen = width * height * 4; | ||||
| 		var yDir = -1; | ||||
| 		var dstPos = dataLength - paddedStride; | ||||
| 		var srcPos = 0; | ||||
| 		 | ||||
| 		if ( topToBottom ) { | ||||
| 			yDir = 1; | ||||
| 			dstPos = 0; | ||||
| 		} | ||||
| 		 | ||||
| 		while( srcPos < srcLen ) { | ||||
| 			var i = dstPos; | ||||
| 			while( i < dstPos + dstStride ) { | ||||
| 				var r = srcBytes.get(srcPos + channelMap[1]); | ||||
| 				var g = srcBytes.get(srcPos + channelMap[2]); | ||||
| 				var b = srcBytes.get(srcPos + channelMap[3]); | ||||
| 				 | ||||
| 				bytesBGR.set(i++, b); | ||||
| 				bytesBGR.set(i++, g); | ||||
| 				bytesBGR.set(i++, r); | ||||
| 				 | ||||
| 				srcPos += 4; | ||||
| 			} | ||||
| 			dstPos += yDir * paddedStride; | ||||
| 		} | ||||
| 		 | ||||
| 		return { | ||||
| 			header: { | ||||
| 				width: width, | ||||
| 				height: height, | ||||
| 				paddedStride: paddedStride, | ||||
| 				topToBottom: topToBottom, | ||||
| 				bpp: bpp, | ||||
| 				dataLength: dataLength, | ||||
| 				compression: 0 | ||||
| 			}, | ||||
| 			pixels: bytesBGR, | ||||
| 			colorTable: null | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										74
									
								
								leenkx/Sources/iron/format/bmp/Writer.hx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										74
									
								
								leenkx/Sources/iron/format/bmp/Writer.hx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,74 @@ | ||||
| /* | ||||
|  * format - Haxe File Formats | ||||
|  * | ||||
|  *  BMP File Format | ||||
|  *  Copyright (C) 2007-2009 Robert Sköld | ||||
|  * | ||||
|  * Copyright (c) 2009, The Haxe Project Contributors | ||||
|  * All rights reserved. | ||||
|  * Redistribution and use in source and binary forms, with or without | ||||
|  * modification, are permitted provided that the following conditions are met: | ||||
|  * | ||||
|  *   - Redistributions of source code must retain the above copyright | ||||
|  *     notice, this list of conditions and the following disclaimer. | ||||
|  *   - Redistributions in binary form must reproduce the above copyright | ||||
|  *     notice, this list of conditions and the following disclaimer in the | ||||
|  *     documentation and/or other materials provided with the distribution. | ||||
|  * | ||||
|  * THIS SOFTWARE IS PROVIDED BY THE HAXE PROJECT CONTRIBUTORS "AS IS" AND ANY | ||||
|  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||||
|  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||||
|  * DISCLAIMED. IN NO EVENT SHALL THE HAXE PROJECT CONTRIBUTORS BE LIABLE FOR | ||||
|  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | ||||
|  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | ||||
|  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | ||||
|  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | ||||
|  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | ||||
|  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH | ||||
|  * DAMAGE. | ||||
|  */ | ||||
|  | ||||
| package iron.format.bmp; | ||||
|  | ||||
| import iron.format.bmp.Data; | ||||
|  | ||||
|  | ||||
| class Writer { | ||||
|  | ||||
| 	static var DATA_OFFSET : Int = 0x36; | ||||
|  | ||||
| 	var output : haxe.io.Output; | ||||
|  | ||||
| 	public function new(o) { | ||||
| 		output = o; | ||||
| 	} | ||||
| 	 | ||||
| 	/** | ||||
| 	 * Specs: http://s223767089.online.de/en/file-format-bmp | ||||
| 	 */ | ||||
| 	public function write( bmp : Data ) { | ||||
| 		// Write Header (14 bytes) | ||||
| 		output.writeString( "BM" );								// Signature | ||||
| 		output.writeInt32(bmp.pixels.length + DATA_OFFSET );	// FileSize | ||||
| 		output.writeInt32( 0 );									// Reserved | ||||
| 		output.writeInt32( DATA_OFFSET );						// Offset | ||||
|  | ||||
| 		// Write InfoHeader (40 bytes) | ||||
| 		output.writeInt32( 40 );								// InfoHeader size | ||||
| 		output.writeInt32( bmp.header.width );					// Image width | ||||
| 		var height = bmp.header.height; | ||||
| 		if (bmp.header.topToBottom) height = -height;  | ||||
| 		output.writeInt32( height );							// Image height | ||||
| 		output.writeInt16( 1 );									// Number of planes | ||||
| 		output.writeInt16( 24 );								// Bits per pixel (24bit RGB) | ||||
| 		output.writeInt32( 0 );									// Compression type (no compression) | ||||
| 		output.writeInt32( bmp.header.dataLength );				// Image data size (0 when uncompressed) | ||||
| 		output.writeInt32( 0x2e30 );							// Horizontal resolution | ||||
| 		output.writeInt32( 0x2e30 );							// Vertical resolution | ||||
| 		output.writeInt32( 0 );									// Colors used (0 when uncompressed) | ||||
| 		output.writeInt32( 0 );									// Important colors (0 when uncompressed) | ||||
|  | ||||
| 		// Write Raster Data | ||||
| 		output.write(bmp.pixels); | ||||
|   } | ||||
| } | ||||
| @ -159,9 +159,17 @@ class Animation { | ||||
| 			if(markerEvents.get(sampler) != null){ | ||||
| 				for (i in 0...anim.marker_frames.length) { | ||||
| 					if (frameIndex == anim.marker_frames[i]) { | ||||
| 						var marketAct = markerEvents.get(sampler); | ||||
| 						var ar = marketAct.get(anim.marker_names[i]); | ||||
| 						var markerAct = markerEvents.get(sampler); | ||||
| 						var ar = markerAct.get(anim.marker_names[i]); | ||||
| 						if (ar != null) for (f in ar) f(); | ||||
| 					} else { | ||||
| 						for (j in 0...(frameIndex - lastFrameIndex)) { | ||||
| 							if (lastFrameIndex + j + 1 == anim.marker_frames[i]) { | ||||
| 								var markerAct = markerEvents.get(sampler); | ||||
| 								var ar = markerAct.get(anim.marker_names[i]); | ||||
| 								if (ar != null) for (f in ar) f(); | ||||
| 							} | ||||
| 						} | ||||
| 					} | ||||
| 				} | ||||
| 				lastFrameIndex = frameIndex; | ||||
|  | ||||
| @ -30,12 +30,22 @@ class CameraObject extends Object { | ||||
| 	static var sphereCenter = new Vec4(); | ||||
| 	static var vcenter = new Vec4(); | ||||
| 	static var vup = new Vec4(); | ||||
|  | ||||
| 	 | ||||
| 	#if lnx_vr | ||||
| 	var helpMat = Mat4.identity(); | ||||
| 	public var leftV = Mat4.identity(); | ||||
| 	public var rightV = Mat4.identity(); | ||||
| 	#end | ||||
| 	 | ||||
| 	public function new(data: CameraData) { | ||||
| 		super(); | ||||
|  | ||||
| 		this.data = data; | ||||
|  | ||||
| 		#if lnx_vr | ||||
| 		iron.system.VR.initButton(); | ||||
| 		#end | ||||
| 		 | ||||
| 		buildProjection(); | ||||
|  | ||||
| 		V = Mat4.identity(); | ||||
| @ -117,6 +127,26 @@ class CameraObject extends Object { | ||||
| 		V.getInverse(transform.world); | ||||
| 		VP.multmats(P, V); | ||||
|  | ||||
|  | ||||
| 		#if lnx_vr | ||||
| 		var vr = kha.vr.VrInterface.instance; | ||||
| 		if (vr != null && vr.IsPresenting()) { | ||||
| 			leftV.setFrom(V); | ||||
| 			helpMat.self = vr.GetViewMatrix(0); | ||||
| 			leftV.multmat(helpMat); | ||||
|  | ||||
| 			rightV.setFrom(V); | ||||
| 			helpMat.self = vr.GetViewMatrix(1); | ||||
| 			rightV.multmat(helpMat); | ||||
| 		} | ||||
| 		else { | ||||
| 			leftV.setFrom(V); | ||||
| 		} | ||||
| 		VP.multmats(P, leftV); | ||||
| 		#else | ||||
| 		VP.multmats(P, V); | ||||
| 		#end | ||||
|  | ||||
| 		if (data.raw.frustum_culling) { | ||||
| 			buildViewFrustum(VP, frustumPlanes); | ||||
| 		} | ||||
|  | ||||
| @ -155,8 +155,13 @@ class LightObject extends Object { | ||||
| 	} | ||||
|  | ||||
| 	public function setCascade(camera: CameraObject, cascade: Int) { | ||||
| 		m.setFrom(camera.V); | ||||
|  | ||||
| 		#if lnx_vr | ||||
| 		m.setFrom(camera.leftV); | ||||
| 		#else | ||||
| 		m.setFrom(camera.V); | ||||
| 		#end | ||||
| 		 | ||||
| 		#if lnx_csm | ||||
| 		if (camSlicedP == null) { | ||||
| 			camSlicedP = []; | ||||
|  | ||||
| @ -21,8 +21,10 @@ class MeshObject extends Object { | ||||
| 	public var particleChildren: Array<MeshObject> = null; | ||||
| 	public var particleOwner: MeshObject = null; // Particle object | ||||
| 	public var particleIndex = -1; | ||||
| 	public var render_emitter = true; | ||||
| 	#end | ||||
| 	public var cameraDistance: Float; | ||||
| 	public var cameraList: Array<String> = null; | ||||
| 	public var screenSize = 0.0; | ||||
| 	public var frustumCulling = true; | ||||
| 	public var activeTilesheet: Tilesheet = null; | ||||
| @ -234,6 +236,8 @@ class MeshObject extends Object { | ||||
| 		if (cullMesh(context, Scene.active.camera, RenderPath.active.light)) return; | ||||
| 		var meshContext = raw != null ? context == "mesh" : false; | ||||
|  | ||||
| 		if (cameraList != null && cameraList.indexOf(Scene.active.camera.name) < 0) return; | ||||
|  | ||||
| 		#if lnx_particles | ||||
| 		if (raw != null && raw.is_particle && particleOwner == null) return; // Instancing not yet set-up by particle system owner | ||||
| 		if (particleSystems != null && meshContext) { | ||||
| @ -244,6 +248,7 @@ class MeshObject extends Object { | ||||
| 					Scene.active.spawnObject(psys.data.raw.instance_object, null, function(o: Object) { | ||||
| 						if (o != null) { | ||||
| 							var c: MeshObject = cast o; | ||||
|     						c.cameraList = this.cameraList; | ||||
| 							particleChildren.push(c); | ||||
| 							c.particleOwner = this; | ||||
| 							c.particleIndex = particleChildren.length - 1; | ||||
| @ -255,11 +260,11 @@ class MeshObject extends Object { | ||||
| 				particleSystems[i].update(particleChildren[i], this); | ||||
| 			} | ||||
| 		} | ||||
| 		if (particleSystems != null && particleSystems.length > 0 && !raw.render_emitter) return; | ||||
| 		if (particleSystems != null && particleSystems.length > 0 && !render_emitter) return; | ||||
|         if (particleSystems == null && cullMaterial(context)) return; | ||||
|         #else | ||||
|         if (cullMaterial(context)) return; | ||||
| 		#end | ||||
|  | ||||
| 		if (cullMaterial(context)) return; | ||||
|  | ||||
| 		// Get lod | ||||
| 		var mats = materials; | ||||
| 		var lod = this; | ||||
| @ -297,6 +302,10 @@ class MeshObject extends Object { | ||||
|  | ||||
| 		// Render mesh | ||||
| 		var ldata = lod.data; | ||||
| 		 | ||||
| 		// Next pass rendering first (inverse order) | ||||
| 		renderNextPass(g, context, bindParams, lod); | ||||
| 		 | ||||
| 		for (i in 0...ldata.geom.indexBuffers.length) { | ||||
|  | ||||
| 			var mi = ldata.geom.materialIndices[i]; | ||||
| @ -400,4 +409,85 @@ class MeshObject extends Object { | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	 | ||||
| 	 | ||||
| 	function renderNextPass(g: Graphics, context: String, bindParams: Array<String>, lod: MeshObject) { | ||||
| 		var ldata = lod.data; | ||||
| 		for (i in 0...ldata.geom.indexBuffers.length) { | ||||
| 			var mi = ldata.geom.materialIndices[i]; | ||||
| 			if (mi >= materials.length) continue; | ||||
|  | ||||
| 			var currentMaterial: MaterialData = materials[mi]; | ||||
| 			if (currentMaterial == null || currentMaterial.shader == null) continue; | ||||
|  | ||||
| 			var nextPassName: String = currentMaterial.shader.nextPass; | ||||
| 			if (nextPassName == null || nextPassName == "") continue; | ||||
|  | ||||
| 			var nextMaterial: MaterialData = null; | ||||
| 			for (mat in materials) { | ||||
| 				// First try exact match | ||||
| 				if (mat.name == nextPassName) { | ||||
| 					nextMaterial = mat; | ||||
| 					break; | ||||
| 				} | ||||
| 				// If no exact match, try to match base name for linked materials | ||||
| 				if (mat.name.indexOf("_") > 0 && mat.name.substr(mat.name.length - 6) == ".blend") { | ||||
| 					var baseName = mat.name.substring(0, mat.name.indexOf("_")); | ||||
| 					if (baseName == nextPassName) { | ||||
| 						nextMaterial = mat; | ||||
| 						break; | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| 			if (nextMaterial == null) continue; | ||||
|  | ||||
| 			var nextMaterialContext: MaterialContext = null; | ||||
| 			var nextShaderContext: ShaderContext = null; | ||||
|  | ||||
| 			for (j in 0...nextMaterial.raw.contexts.length) { | ||||
| 				if (nextMaterial.raw.contexts[j].name.substr(0, context.length) == context) { | ||||
| 					nextMaterialContext = nextMaterial.contexts[j]; | ||||
| 					nextShaderContext = nextMaterial.shader.getContext(context); | ||||
| 					break; | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| 			if (nextShaderContext == null) continue; | ||||
| 			if (skipContext(context, nextMaterial)) continue; | ||||
|  | ||||
| 			var elems = nextShaderContext.raw.vertex_elements; | ||||
|  | ||||
| 			// Uniforms | ||||
| 			if (nextShaderContext.pipeState != lastPipeline) { | ||||
| 				g.setPipeline(nextShaderContext.pipeState); | ||||
| 				lastPipeline = nextShaderContext.pipeState; | ||||
| 			} | ||||
| 			Uniforms.setContextConstants(g, nextShaderContext, bindParams); | ||||
| 			Uniforms.setObjectConstants(g, nextShaderContext, this); | ||||
| 			Uniforms.setMaterialConstants(g, nextShaderContext, nextMaterialContext); | ||||
|  | ||||
| 			// VB / IB | ||||
| 			#if lnx_deinterleaved | ||||
| 			g.setVertexBuffers(ldata.geom.get(elems)); | ||||
| 			#else | ||||
| 			if (ldata.geom.instancedVB != null) { | ||||
| 				g.setVertexBuffers([ldata.geom.get(elems), ldata.geom.instancedVB]); | ||||
| 			} | ||||
| 			else { | ||||
| 				g.setVertexBuffer(ldata.geom.get(elems)); | ||||
| 			} | ||||
| 			#end | ||||
|  | ||||
| 			g.setIndexBuffer(ldata.geom.indexBuffers[i]); | ||||
|  | ||||
| 			// Draw next pass for this specific geometry section | ||||
| 			if (ldata.geom.instanced) { | ||||
| 				g.drawIndexedVerticesInstanced(ldata.geom.instanceCount, ldata.geom.start, ldata.geom.count); | ||||
| 			} | ||||
| 			else { | ||||
| 				g.drawIndexedVertices(ldata.geom.start, ldata.geom.count); | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @ -172,6 +172,10 @@ class Object { | ||||
| 			for (f in t._init) App.removeInit(f); | ||||
| 			t._init = null; | ||||
| 		} | ||||
| 		if (t._fixedUpdate != null) { | ||||
| 			for (f in t._fixedUpdate) App.removeFixedUpdate(f); | ||||
| 			t._fixedUpdate = null; | ||||
| 		} | ||||
| 		if (t._update != null) { | ||||
| 			for (f in t._update) App.removeUpdate(f); | ||||
| 			t._update = null; | ||||
|  | ||||
| @ -24,6 +24,9 @@ class ObjectAnimation extends Animation { | ||||
|  | ||||
| 	public var transformMap: Map<String, FastFloat>; | ||||
|  | ||||
| 	var defaultSampler: ActionSampler = null; | ||||
| 	static inline var DEFAULT_SAMPLER_ID = "__object_default_action__"; | ||||
|  | ||||
| 	public static var trackNames: Array<String> = [	"xloc", "yloc", "zloc", | ||||
| 											   		"xrot", "yrot", "zrot", | ||||
| 											   		"qwrot", "qxrot", "qyrot", "qzrot", | ||||
| @ -39,7 +42,6 @@ class ObjectAnimation extends Animation { | ||||
| 		isSkinned = false; | ||||
| 		super(); | ||||
| 	} | ||||
|  | ||||
| 	function getAction(action: String): TObj { | ||||
| 		for (a in oactions) if (a != null && a.objects[0].name == action) return a.objects[0]; | ||||
| 		return null; | ||||
| @ -47,10 +49,29 @@ class ObjectAnimation extends Animation { | ||||
|  | ||||
| 	override public function play(action = "", onComplete: Void->Void = null, blendTime = 0.0, speed = 1.0, loop = true) { | ||||
| 		super.play(action, onComplete, blendTime, speed, loop); | ||||
| 		if (this.action == "" && oactions[0] != null) this.action = oactions[0].objects[0].name; | ||||
| 		if (this.action == "" && oactions != null && oactions[0] != null){ | ||||
| 			this.action = oactions[0].objects[0].name; | ||||
| 		} | ||||
| 		oaction = getAction(this.action); | ||||
| 		if (oaction != null) { | ||||
| 			isSampled = oaction.sampled != null && oaction.sampled; | ||||
| 			if (defaultSampler != null) { | ||||
| 				deRegisterAction(DEFAULT_SAMPLER_ID); | ||||
| 			} | ||||
| 			var callbacks = onComplete != null ? [onComplete] : null; | ||||
| 			defaultSampler = new ActionSampler(this.action, speed, loop, false, callbacks); | ||||
| 			registerAction(DEFAULT_SAMPLER_ID, defaultSampler); | ||||
| 			if (paused) defaultSampler.paused = true; | ||||
| 			updateAnimation = function(map: Map<String, FastFloat>) { | ||||
| 				sampleAction(defaultSampler, map); | ||||
| 			}; | ||||
| 		} | ||||
| 		else { | ||||
| 			if (defaultSampler != null) { | ||||
| 				deRegisterAction(DEFAULT_SAMPLER_ID); | ||||
| 				defaultSampler = null; | ||||
| 			} | ||||
| 			updateAnimation = null; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| @ -61,12 +82,13 @@ class ObjectAnimation extends Animation { | ||||
| 		Animation.beginProfile(); | ||||
| 		#end | ||||
|  | ||||
| 		if(transformMap == null) transformMap = new Map(); | ||||
| 		if (transformMap == null) transformMap = new Map(); | ||||
| 		transformMap = initTransformMap(); | ||||
|  | ||||
| 		super.update(delta); | ||||
| 		if (defaultSampler != null) defaultSampler.paused = paused; | ||||
| 		if (paused) return; | ||||
| 		if(updateAnimation == null) return; | ||||
| 		if (updateAnimation == null) return; | ||||
| 		if (!isSkinned) updateObjectAnimation(); | ||||
|  | ||||
| 		#if lnx_debug | ||||
|  | ||||
| @ -2,11 +2,14 @@ package iron.object; | ||||
|  | ||||
| #if lnx_particles | ||||
|  | ||||
| import kha.FastFloat; | ||||
| import kha.graphics4.Usage; | ||||
| import kha.arrays.Float32Array; | ||||
| import iron.data.Data; | ||||
| import iron.data.ParticleData; | ||||
| import iron.data.SceneFormat; | ||||
| import iron.data.Geometry; | ||||
| import iron.data.MeshData; | ||||
| import iron.system.Time; | ||||
| import iron.math.Mat4; | ||||
| import iron.math.Quat; | ||||
| @ -16,10 +19,13 @@ import iron.math.Vec4; | ||||
| class ParticleSystem { | ||||
| 	public var data: ParticleData; | ||||
| 	public var speed = 1.0; | ||||
| 	public var dynamicEmitter: Bool = true; | ||||
| 	var currentSpeed = 0.0; | ||||
| 	var particles: Array<Particle>; | ||||
| 	var ready: Bool; | ||||
| 	var frameRate = 24; | ||||
| 	var lifetime = 0.0; | ||||
| 	var looptime = 0.0; | ||||
| 	var animtime = 0.0; | ||||
| 	var time = 0.0; | ||||
| 	var spawnRate = 0.0; | ||||
| @ -46,15 +52,31 @@ class ParticleSystem { | ||||
| 	var ownerLoc = new Vec4(); | ||||
| 	var ownerRot = new Quat(); | ||||
| 	var ownerScl = new Vec4(); | ||||
| 	 | ||||
| 	var random = 0.0; | ||||
|  | ||||
| 	var tmpV4 = new Vec4(); | ||||
|  | ||||
| 	var instancedData: Float32Array = null; | ||||
| 	var lastSpawnedCount: Int = 0; | ||||
| 	var hasUniqueGeom: Bool = false;  | ||||
|  | ||||
| 	public function new(sceneName: String, pref: TParticleReference) { | ||||
| 		seed = pref.seed; | ||||
| 		currentSpeed = speed; | ||||
| 		speed = 0; | ||||
| 		particles = []; | ||||
| 		ready = false; | ||||
| 		 | ||||
| 		Data.getParticle(sceneName, pref.particle, function(b: ParticleData) { | ||||
| 			data = b; | ||||
| 			r = data.raw; | ||||
| 			var dyn: Null<Bool> = r.dynamic_emitter; | ||||
| 			var dynValue: Bool = true; | ||||
| 			if (dyn != null) { | ||||
| 				dynValue = dyn; | ||||
| 			} | ||||
| 			dynamicEmitter = dynValue; | ||||
| 			if (Scene.active.raw.gravity != null) { | ||||
| 				gx = Scene.active.raw.gravity[0] * r.weight_gravity; | ||||
| 				gy = Scene.active.raw.gravity[1] * r.weight_gravity; | ||||
| @ -65,38 +87,73 @@ class ParticleSystem { | ||||
| 				gy = 0; | ||||
| 				gz = -9.81 * r.weight_gravity; | ||||
| 			} | ||||
| 			alignx = r.object_align_factor[0] / 2; | ||||
| 			aligny = r.object_align_factor[1] / 2; | ||||
| 			alignz = r.object_align_factor[2] / 2; | ||||
| 			alignx = r.object_align_factor[0]; | ||||
| 			aligny = r.object_align_factor[1]; | ||||
| 			alignz = r.object_align_factor[2]; | ||||
| 			looptime = (r.frame_end - r.frame_start) / frameRate; | ||||
| 			lifetime = r.lifetime / frameRate; | ||||
| 			animtime = (r.frame_end - r.frame_start) / frameRate; | ||||
| 			animtime = r.loop ? looptime : looptime + lifetime; | ||||
| 			spawnRate = ((r.frame_end - r.frame_start) / r.count) / frameRate; | ||||
|  | ||||
| 			for (i in 0...r.count) { | ||||
| 				var particle = new Particle(i); | ||||
| 				particle.sr = 1 - Math.random() * r.size_random; | ||||
| 				particles.push(particle); | ||||
| 				particles.push(new Particle(i)); | ||||
| 			} | ||||
|  | ||||
| 			ready = true; | ||||
| 			if (r.auto_start){ | ||||
| 				start(); | ||||
| 			} | ||||
| 		}); | ||||
| 	} | ||||
|  | ||||
| 	public function start() { | ||||
| 		if (r.is_unique) random = Math.random(); | ||||
| 		lifetime = r.lifetime / frameRate; | ||||
| 		time = 0; | ||||
| 		lap = 0; | ||||
| 		lapTime = 0; | ||||
| 		speed = currentSpeed; | ||||
| 		lastSpawnedCount = 0; | ||||
| 		instancedData = null; | ||||
| 	} | ||||
|  | ||||
| 	public function pause() { | ||||
| 		lifetime = 0; | ||||
| 		speed = 0; | ||||
| 	} | ||||
|  | ||||
| 	public function resume() { | ||||
| 		lifetime = r.lifetime / frameRate; | ||||
| 		speed = currentSpeed; | ||||
| 	} | ||||
|  | ||||
|  | ||||
|  | ||||
| 	// TODO: interrupt smoothly | ||||
| 	public function stop() { | ||||
| 		end(); | ||||
| 	} | ||||
|  | ||||
| 	function end() { | ||||
| 		lifetime = 0; | ||||
| 		speed = 0; | ||||
| 		lap = 0; | ||||
| 	} | ||||
| 	 | ||||
| 	public function update(object: MeshObject, owner: MeshObject) { | ||||
| 		if (!ready || object == null || speed == 0.0) return; | ||||
| 		if (iron.App.pauseUpdates) return; | ||||
| 		 | ||||
| 		var prevLap = lap; | ||||
|  | ||||
| 		// Copy owner world transform but discard scale | ||||
| 		owner.transform.world.decompose(ownerLoc, ownerRot, ownerScl); | ||||
| 		object.transform.loc = ownerLoc; | ||||
| 		object.transform.rot = ownerRot; | ||||
| 		if (dynamicEmitter) { | ||||
| 			object.transform.loc.x = 0; object.transform.loc.y = 0; object.transform.loc.z = 0; | ||||
| 			object.transform.rot = new Quat(); | ||||
| 		} else { | ||||
| 			object.transform.loc = ownerLoc; | ||||
| 			object.transform.rot = ownerRot; | ||||
| 		} | ||||
|  | ||||
| 		// Set particle size per particle system | ||||
| 		object.transform.scale = new Vec4(r.particle_size, r.particle_size, r.particle_size, 1); | ||||
| @ -115,16 +172,25 @@ class ParticleSystem { | ||||
| 		} | ||||
|  | ||||
| 		// Animate | ||||
| 		time += Time.delta * speed; | ||||
| 		time += Time.renderDelta * speed; // realDelta to renderDelta | ||||
| 		lap = Std.int(time / animtime); | ||||
| 		lapTime = time - lap * animtime; | ||||
| 		count = Std.int(lapTime / spawnRate); | ||||
|  | ||||
| 		if (lap > prevLap && !r.loop) { | ||||
| 			end(); | ||||
| 		} | ||||
|  | ||||
| 		if (lap > prevLap && r.loop) { | ||||
| 			lastSpawnedCount = 0; | ||||
| 		} | ||||
| 		 | ||||
| 		updateGpu(object, owner); | ||||
| 	} | ||||
|  | ||||
| 	public function getData(): Mat4 { | ||||
| 		var hair = r.type == 1; | ||||
| 		// Store loop flag in the sign: positive -> loop, negative -> no loop | ||||
| 		m._00 = r.loop ? animtime : -animtime; | ||||
| 		m._01 = hair ? 1 / particles.length : spawnRate; | ||||
| 		m._02 = hair ? 1 : lifetime; | ||||
| @ -133,9 +199,9 @@ class ParticleSystem { | ||||
| 		m._11 = hair ? 0 : aligny; | ||||
| 		m._12 = hair ? 0 : alignz; | ||||
| 		m._13 = hair ? 0 : r.factor_random; | ||||
| 		m._20 = hair ? 0 : gx * r.mass; | ||||
| 		m._21 = hair ? 0 : gy * r.mass; | ||||
| 		m._22 = hair ? 0 : gz * r.mass; | ||||
| 		m._20 = hair ? 0 : gx; | ||||
| 		m._21 = hair ? 0 : gy; | ||||
| 		m._22 = hair ? 0 : gz; | ||||
| 		m._23 = hair ? 0 : r.lifetime_random; | ||||
| 		m._30 = tilesx; | ||||
| 		m._31 = tilesy; | ||||
| @ -144,13 +210,34 @@ class ParticleSystem { | ||||
| 		return m; | ||||
| 	} | ||||
|  | ||||
| 	public function getSizeRandom(): FastFloat { | ||||
| 		return r.size_random; | ||||
| 	} | ||||
|  | ||||
| 	public inline function getRandom(): FastFloat { | ||||
| 		return random; | ||||
| 	} | ||||
|  | ||||
| 	public inline function getSize(): FastFloat { | ||||
| 		return r.particle_size; | ||||
| 	} | ||||
|  | ||||
| 	function updateGpu(object: MeshObject, owner: MeshObject) { | ||||
| 		if (!object.data.geom.instanced) setupGeomGpu(object, owner); | ||||
| 		// GPU particles transform is attached to owner object | ||||
| 		if (dynamicEmitter) { | ||||
| 			if (!hasUniqueGeom) ensureUniqueGeom(object); | ||||
| 			var needSetup = instancedData == null || object.data.geom.instancedVB == null; | ||||
| 			if (needSetup) setupGeomGpuDynamic(object, owner); | ||||
| 			updateSpawnedInstances(object, owner); | ||||
| 		} | ||||
| 		else { | ||||
| 			if (!hasUniqueGeom) ensureUniqueGeom(object); | ||||
| 			if (!object.data.geom.instanced) setupGeomGpu(object, owner); | ||||
| 		} | ||||
| 		// GPU particles transform is attached to owner object in static mode | ||||
| 	} | ||||
|  | ||||
| 	function setupGeomGpu(object: MeshObject, owner: MeshObject) { | ||||
| 		var instancedData = new Float32Array(particles.length * 6); | ||||
| 		var instancedData = new Float32Array(particles.length * 3); | ||||
| 		var i = 0; | ||||
|  | ||||
| 		var normFactor = 1 / 32767; // pa.values are not normalized | ||||
| @ -169,10 +256,6 @@ class ParticleSystem { | ||||
| 					instancedData.set(i, pa.values[j * pa.size    ] * normFactor * scaleFactor.x); i++; | ||||
| 					instancedData.set(i, pa.values[j * pa.size + 1] * normFactor * scaleFactor.y); i++; | ||||
| 					instancedData.set(i, pa.values[j * pa.size + 2] * normFactor * scaleFactor.z); i++; | ||||
|  | ||||
| 					instancedData.set(i, p.sr); i++; | ||||
| 					instancedData.set(i, p.sr); i++; | ||||
| 					instancedData.set(i, p.sr); i++; | ||||
| 				} | ||||
|  | ||||
| 			case 1: // Face | ||||
| @ -196,10 +279,6 @@ class ParticleSystem { | ||||
| 					instancedData.set(i, pos.x * normFactor * scaleFactor.x); i++; | ||||
| 					instancedData.set(i, pos.y * normFactor * scaleFactor.y); i++; | ||||
| 					instancedData.set(i, pos.z * normFactor * scaleFactor.z); i++; | ||||
|  | ||||
| 					instancedData.set(i, p.sr); i++; | ||||
| 					instancedData.set(i, p.sr); i++; | ||||
| 					instancedData.set(i, p.sr); i++; | ||||
| 				} | ||||
|  | ||||
| 			case 2: // Volume | ||||
| @ -210,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.y); i++; | ||||
| 					instancedData.set(i, (Math.random() * 2.0 - 1.0) * scaleFactorVolume.z); i++; | ||||
|  | ||||
| 					instancedData.set(i, p.sr); i++; | ||||
| 					instancedData.set(i, p.sr); i++; | ||||
| 					instancedData.set(i, p.sr); i++; | ||||
| 				} | ||||
| 		} | ||||
| 		object.data.geom.setupInstanced(instancedData, 3, Usage.StaticUsage); | ||||
| 		object.data.geom.setupInstanced(instancedData, 1, Usage.StaticUsage); | ||||
| 	} | ||||
|  | ||||
| 	function fhash(n: Int): Float { | ||||
| 		var s = n + 1.0; | ||||
| 		s *= 9301.0 % s; | ||||
| 		s = (s * 9301.0 + 49297.0) % 233280.0; | ||||
| 		return s / 233280.0; | ||||
| 	// allocate instanced VB once for this object  | ||||
| 	function setupGeomGpuDynamic(object: MeshObject, owner: MeshObject) { | ||||
| 		if (instancedData == null) instancedData = new Float32Array(particles.length * 3); | ||||
| 		lastSpawnedCount = 0;  | ||||
| 		// Create instanced VB once if missing (seed with our instancedData) | ||||
| 		if (object.data.geom.instancedVB == null) { | ||||
| 			object.data.geom.setupInstanced(instancedData, 1, Usage.DynamicUsage); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	function ensureUniqueGeom(object: MeshObject) { | ||||
| 		if (hasUniqueGeom) return; | ||||
| 		var newData: MeshData = null; | ||||
| 		new MeshData(object.data.raw, function(dat: MeshData) { | ||||
| 			dat.scalePos = object.data.scalePos; | ||||
| 			dat.scaleTex = object.data.scaleTex; | ||||
| 			dat.format = object.data.format; | ||||
| 			newData = dat; | ||||
| 		}); | ||||
| 		if (newData != null) object.setData(newData); | ||||
| 		hasUniqueGeom = true; | ||||
| 	} | ||||
|  | ||||
| 	function updateSpawnedInstances(object: MeshObject, owner: MeshObject) { | ||||
| 		if (instancedData == null) return; | ||||
| 		var targetCount = count; | ||||
| 		if (targetCount > particles.length) targetCount = particles.length; | ||||
| 		if (targetCount <= lastSpawnedCount) return; | ||||
|  | ||||
| 		var normFactor = 1 / 32767; | ||||
| 		var scalePosOwner = owner.data.scalePos; | ||||
| 		var scalePosParticle = object.data.scalePos; | ||||
| 		var particleSize = r.particle_size; | ||||
| 		var base = 1.0 / (particleSize * scalePosParticle); | ||||
|  | ||||
| 		switch (r.emit_from) { | ||||
| 			case 0: // Vert | ||||
| 				var pa = owner.data.geom.positions; | ||||
| 				var osx = owner.transform.scale.x; | ||||
| 				var osy = owner.transform.scale.y; | ||||
| 				var osz = owner.transform.scale.z; | ||||
| 				var pCount = Std.int(pa.values.length / pa.size); | ||||
| 				for (idx in lastSpawnedCount...targetCount) { | ||||
| 					var j = Std.int(fhash(idx) * pCount); | ||||
| 					var lx = pa.values[j * pa.size    ] * normFactor * scalePosOwner * osx; | ||||
| 					var ly = pa.values[j * pa.size + 1] * normFactor * scalePosOwner * osy; | ||||
| 					var lz = pa.values[j * pa.size + 2] * normFactor * scalePosOwner * osz; | ||||
| 					tmpV4.x = lx; tmpV4.y = ly; tmpV4.z = lz; tmpV4.w = 1; | ||||
| 					tmpV4.applyQuat(ownerRot); | ||||
| 					var o = idx * 3; | ||||
| 					instancedData.set(o    , (tmpV4.x + ownerLoc.x) * base); | ||||
| 					instancedData.set(o + 1, (tmpV4.y + ownerLoc.y) * base); | ||||
| 					instancedData.set(o + 2, (tmpV4.z + ownerLoc.z) * base); | ||||
| 				} | ||||
|  | ||||
| 			case 1: // Face | ||||
| 				var positions = owner.data.geom.positions.values; | ||||
| 				var osx1 = owner.transform.scale.x; | ||||
| 				var osy1 = owner.transform.scale.y; | ||||
| 				var osz1 = owner.transform.scale.z; | ||||
| 				for (idx in lastSpawnedCount...targetCount) { | ||||
| 					var ia = owner.data.geom.indices[Std.random(owner.data.geom.indices.length)]; | ||||
| 					var faceIndex = Std.random(Std.int(ia.length / 3)); | ||||
| 					var i0 = ia[faceIndex * 3 + 0]; | ||||
| 					var i1 = ia[faceIndex * 3 + 1]; | ||||
| 					var i2 = ia[faceIndex * 3 + 2]; | ||||
| 					var v0x = positions[i0 * 4    ], v0y = positions[i0 * 4 + 1], v0z = positions[i0 * 4 + 2]; | ||||
| 					var v1x = positions[i1 * 4    ], v1y = positions[i1 * 4 + 1], v1z = positions[i1 * 4 + 2]; | ||||
| 					var v2x = positions[i2 * 4    ], v2y = positions[i2 * 4 + 1], v2z = positions[i2 * 4 + 2]; | ||||
| 					var rx = Math.random(); var ry = Math.random(); if (rx + ry > 1) { rx = 1 - rx; ry = 1 - ry; } | ||||
| 					var pxs = v0x + rx * (v1x - v0x) + ry * (v2x - v0x); | ||||
| 					var pys = v0y + rx * (v1y - v0y) + ry * (v2y - v0y); | ||||
| 					var pzs = v0z + rx * (v1z - v0z) + ry * (v2z - v0z); | ||||
| 					var px = pxs * normFactor * scalePosOwner * osx1; | ||||
| 					var py = pys * normFactor * scalePosOwner * osy1; | ||||
| 					var pz = pzs * normFactor * scalePosOwner * osz1; | ||||
| 					tmpV4.x = px; tmpV4.y = py; tmpV4.z = pz; tmpV4.w = 1; | ||||
| 					tmpV4.applyQuat(ownerRot); | ||||
| 					var o1 = idx * 3; | ||||
| 					instancedData.set(o1    , (tmpV4.x + ownerLoc.x) * base); | ||||
| 					instancedData.set(o1 + 1, (tmpV4.y + ownerLoc.y) * base); | ||||
| 					instancedData.set(o1 + 2, (tmpV4.z + ownerLoc.z) * base); | ||||
| 				} | ||||
|  | ||||
| 			case 2: // Volume | ||||
| 				var dim = object.transform.dim; | ||||
| 				for (idx in lastSpawnedCount...targetCount) { | ||||
| 					tmpV4.x = (Math.random() * 2.0 - 1.0) * (dim.x * 0.5); | ||||
| 					tmpV4.y = (Math.random() * 2.0 - 1.0) * (dim.y * 0.5); | ||||
| 					tmpV4.z = (Math.random() * 2.0 - 1.0) * (dim.z * 0.5); | ||||
| 					tmpV4.w = 1; | ||||
| 					tmpV4.applyQuat(ownerRot); | ||||
| 					var o2 = idx * 3; | ||||
| 					instancedData.set(o2    , (tmpV4.x + ownerLoc.x) * base); | ||||
| 					instancedData.set(o2 + 1, (tmpV4.y + ownerLoc.y) * base); | ||||
| 					instancedData.set(o2 + 2, (tmpV4.z + ownerLoc.z) * base); | ||||
| 				} | ||||
| 		} | ||||
|  | ||||
| 		// Upload full active range [0..targetCount) to this object's instanced VB | ||||
| 		var geom = object.data.geom; | ||||
| 		if (geom.instancedVB == null) { | ||||
| 			geom.setupInstanced(instancedData, 1, Usage.DynamicUsage); | ||||
| 		} | ||||
| 		var vb = geom.instancedVB.lock(); | ||||
| 		var totalFloats = targetCount * 3; // xyz per instance | ||||
| 		var i = 0; | ||||
| 		while (i < totalFloats) { | ||||
| 			vb.setFloat32(i * 4, instancedData[i]); | ||||
| 			i++; | ||||
| 		} | ||||
| 		geom.instancedVB.unlock(); | ||||
| 		geom.instanceCount = targetCount; | ||||
| 		lastSpawnedCount = targetCount; | ||||
| 	} | ||||
|  | ||||
| 	inline function fhash(n: Int): Float { | ||||
|     var s = n + 1.0; | ||||
|     s *= 9301.0 % s; | ||||
|     s = (s * 9301.0 + 49297.0) % 233280.0; | ||||
|     return s / 233280.0; | ||||
| } | ||||
|  | ||||
| 	public function remove() {} | ||||
|  | ||||
| 	/** | ||||
| 		Generates a random point in the triangle with vertex positions abc. | ||||
|  | ||||
| 		 | ||||
| 		Please note that the given position vectors are changed in-place by this | ||||
| 		function and can be considered garbage afterwards, so make sure to clone | ||||
| 		them first if needed. | ||||
| @ -255,10 +446,11 @@ class ParticleSystem { | ||||
|  | ||||
| class Particle { | ||||
| 	public var i: Int; | ||||
| 	public var px = 0.0; | ||||
| 	public var py = 0.0; | ||||
| 	public var pz = 0.0; | ||||
| 	public var sr = 1.0; // Size random | ||||
| 	 | ||||
| 	public var x = 0.0; | ||||
| 	public var y = 0.0; | ||||
| 	public var z = 0.0; | ||||
|  | ||||
| 	public var cameraDistance: Float; | ||||
|  | ||||
| 	public function new(i: Int) { | ||||
|  | ||||
| @ -80,7 +80,7 @@ class Tilesheet { | ||||
| 	function update() { | ||||
| 		if (!ready || paused || action.start >= action.end) return; | ||||
|  | ||||
| 		time += Time.realDelta; | ||||
| 		time += Time.renderDelta; | ||||
|  | ||||
| 		var frameTime = 1 / raw.framerate; | ||||
| 		var framesToAdvance = 0; | ||||
|  | ||||
| @ -1109,6 +1109,26 @@ class Uniforms { | ||||
| 				case "_texUnpack": { | ||||
| 					f = texUnpack != null ? texUnpack : 1.0; | ||||
| 				} | ||||
| 				#if lnx_particles | ||||
| 				case "_particleSizeRandom": { | ||||
| 					var mo = cast(object, MeshObject); | ||||
| 					if (mo.particleOwner != null && mo.particleOwner.particleSystems != null) { | ||||
| 						f = mo.particleOwner.particleSystems[mo.particleIndex].getSizeRandom(); | ||||
| 					} | ||||
| 				} | ||||
| 				case "_particleRandom": { | ||||
| 					var mo = cast(object, MeshObject); | ||||
| 					if (mo.particleOwner != null && mo.particleOwner.particleSystems != null) { | ||||
| 						f = mo.particleOwner.particleSystems[mo.particleIndex].getRandom(); | ||||
| 					} | ||||
| 				} | ||||
| 				case "_particleSize": { | ||||
| 					var mo = cast(object, MeshObject); | ||||
| 					if (mo.particleOwner != null && mo.particleOwner.particleSystems != null) { | ||||
| 						f = mo.particleOwner.particleSystems[mo.particleIndex].getSize(); | ||||
| 					} | ||||
| 				} | ||||
| 				#end | ||||
| 			} | ||||
|  | ||||
| 			if (f == null && externalFloatLinks != null) { | ||||
|  | ||||
| @ -1,37 +1,58 @@ | ||||
| package iron.system; | ||||
|  | ||||
| class Time { | ||||
|  | ||||
| 	public static var scale = 1.0; | ||||
| 	 | ||||
| 	static var frequency: Null<Int> = null; | ||||
| 	static function initFrequency() { | ||||
| 		frequency = kha.Display.primary != null ? kha.Display.primary.frequency : 60; | ||||
| 	} | ||||
| 	 | ||||
| 	public static var step(get, never): Float; | ||||
| 	static function get_step(): Float { | ||||
| 		if (frequency == null) initFrequency(); | ||||
| 		return 1 / frequency; | ||||
| 	} | ||||
| 	 | ||||
| 	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; | ||||
| 	static function get_delta(): Float { | ||||
| 		if (frequency == null) initFrequency(); | ||||
| 		return (1 / frequency) * scale; | ||||
| 		return _delta; | ||||
| 	} | ||||
|  | ||||
| 	static var last = 0.0; | ||||
| 	public static var realDelta = 0.0; | ||||
| 	 | ||||
| 	static var lastRenderTime = 0.0; | ||||
| 	static var _renderDelta = 0.0; | ||||
| 	public static var renderDelta(get, never): Float; | ||||
| 	static function get_renderDelta(): Float { | ||||
| 		return _renderDelta; | ||||
| 	} | ||||
| 		 | ||||
| 	public static inline function time(): Float { | ||||
| 		return kha.Scheduler.time(); | ||||
| 		return kha.Scheduler.time() * scale; | ||||
| 	} | ||||
| 	 | ||||
| 	public static inline function realTime(): Float { | ||||
| 		return kha.Scheduler.realTime(); | ||||
| 	} | ||||
|  | ||||
| 	static var frequency: Null<Int> = null; | ||||
|  | ||||
| 	static function initFrequency() { | ||||
| 		frequency = kha.Display.primary != null ? kha.Display.primary.frequency : 60; | ||||
| 		return kha.Scheduler.realTime() * scale; | ||||
| 	} | ||||
|  | ||||
| 	public static function update() { | ||||
| 		realDelta = realTime() - last; | ||||
| 		last = realTime(); | ||||
| 		_delta = realTime() - lastTime; | ||||
| 		lastTime = realTime(); | ||||
| 	} | ||||
|  | ||||
| 	public static function render() { | ||||
| 		_renderDelta = realTime() - lastRenderTime; | ||||
| 		lastRenderTime = realTime(); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @ -94,34 +94,34 @@ class Tween { | ||||
|  | ||||
| 				// Way too much Reflect trickery.. | ||||
| 				var ps = Reflect.fields(a.props); | ||||
| 				for (i in 0...ps.length) { | ||||
| 					var p = ps[i]; | ||||
| 				for (j in 0...ps.length) { | ||||
| 					var p = ps[j]; | ||||
| 					var k = a._time / a.duration; | ||||
| 					if (k > 1) k = 1; | ||||
|  | ||||
| 					if (a._comps[i] == 1) { | ||||
| 						var fromVal: Float = a._x[i]; | ||||
| 					if (a._comps[j] == 1) { | ||||
| 						var fromVal: Float = a._x[j]; | ||||
| 						var toVal: Float = Reflect.getProperty(a.props, p); | ||||
| 						var val: Float = fromVal + (toVal - fromVal) * eases[a.ease](k); | ||||
| 						Reflect.setProperty(a.target, p, val); | ||||
| 					} | ||||
| 					else { // _comps[i] == 4 | ||||
| 					else { // _comps[j] == 4 | ||||
| 						var obj = Reflect.getProperty(a.props, p); | ||||
| 						var toX: Float = Reflect.getProperty(obj, "x"); | ||||
| 						var toY: Float = Reflect.getProperty(obj, "y"); | ||||
| 						var toZ: Float = Reflect.getProperty(obj, "z"); | ||||
| 						var toW: Float = Reflect.getProperty(obj, "w"); | ||||
| 						if (a._normalize[i]) { | ||||
| 							var qdot = (a._x[i] * toX) + (a._y[i] * toY) + (a._z[i] * toZ) + (a._w[i] * toW); | ||||
| 						if (a._normalize[j]) { | ||||
| 							var qdot = (a._x[j] * toX) + (a._y[j] * toY) + (a._z[j] * toZ) + (a._w[j] * toW); | ||||
| 							if (qdot < 0.0) { | ||||
| 								toX = -toX; toY = -toY; toZ = -toZ; toW = -toW; | ||||
| 							} | ||||
| 						} | ||||
| 						var x: Float = a._x[i] + (toX - a._x[i]) * eases[a.ease](k); | ||||
| 						var y: Float = a._y[i] + (toY - a._y[i]) * eases[a.ease](k); | ||||
| 						var z: Float = a._z[i] + (toZ - a._z[i]) * eases[a.ease](k); | ||||
| 						var w: Float = a._w[i] + (toW - a._w[i]) * eases[a.ease](k); | ||||
| 						if (a._normalize[i]) { | ||||
| 						var x: Float = a._x[j] + (toX - a._x[j]) * eases[a.ease](k); | ||||
| 						var y: Float = a._y[j] + (toY - a._y[j]) * eases[a.ease](k); | ||||
| 						var z: Float = a._z[j] + (toZ - a._z[j]) * eases[a.ease](k); | ||||
| 						var w: Float = a._w[j] + (toW - a._w[j]) * eases[a.ease](k); | ||||
| 						if (a._normalize[j]) { | ||||
| 							var l = Math.sqrt(x * x + y * y + z * z + w * w); | ||||
| 							if (l > 0.0) { | ||||
| 								l = 1.0 / l; | ||||
|  | ||||
							
								
								
									
										52
									
								
								leenkx/Sources/iron/system/VR.hx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								leenkx/Sources/iron/system/VR.hx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,52 @@ | ||||
| package iron.system; | ||||
|  | ||||
| import iron.math.Mat4; | ||||
|  | ||||
| #if lnx_vr | ||||
| class VR { | ||||
|  | ||||
| 	static var undistortionMatrix: Mat4 = null; | ||||
|  | ||||
| 	public function new() {} | ||||
|  | ||||
| 	public static function getUndistortionMatrix(): Mat4 { | ||||
| 		if (undistortionMatrix == null) { | ||||
| 			undistortionMatrix = Mat4.identity(); | ||||
| 		} | ||||
|  | ||||
| 		return undistortionMatrix; | ||||
| 	} | ||||
|  | ||||
| 	public static function getMaxRadiusSq(): Float { | ||||
| 		return 0.0; | ||||
| 	} | ||||
|  | ||||
| 	public static function initButton() { | ||||
| 		function vrDownListener(index: Int, x: Float, y: Float) { | ||||
| 			var vr = kha.vr.VrInterface.instance; | ||||
| 			if (vr == null || !vr.IsVrEnabled() || vr.IsPresenting()) return; | ||||
| 			var w: Float = iron.App.w(); | ||||
| 			var h: Float = iron.App.h(); | ||||
| 			if (x < w - 150 || y < h - 150) return; | ||||
| 			vr.onVRRequestPresent(); | ||||
| 		} | ||||
|  | ||||
| 		function vrRender2D(g: kha.graphics2.Graphics) { | ||||
| 			var vr = kha.vr.VrInterface.instance; | ||||
| 			if (vr == null || !vr.IsVrEnabled() || vr.IsPresenting()) return; | ||||
| 			var w: Float = iron.App.w(); | ||||
| 			var h: Float = iron.App.h(); | ||||
| 			g.color = 0xffff0000; | ||||
| 			g.fillRect(w - 150, h - 150, 140, 140); | ||||
| 		} | ||||
|  | ||||
| 		kha.input.Mouse.get().notify(vrDownListener, null, null, null); | ||||
| 		iron.App.notifyOnRender2D(vrRender2D); | ||||
|  | ||||
| 		var vr = kha.vr.VrInterface.instance; // Straight to VR (Oculus Carmel) | ||||
| 		if (vr != null && vr.IsVrEnabled()) { | ||||
| 			vr.onVRRequestPresent(); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| #end | ||||
| @ -20,6 +20,7 @@ class Config { | ||||
| 		var path = iron.data.Data.dataPath + "config.lnx"; | ||||
| 		var bytes = haxe.io.Bytes.ofString(haxe.Json.stringify(raw)); | ||||
| 		#if kha_krom | ||||
| 		if (iron.data.Data.dataPath == '') path = Krom.getFilesLocation() + "/config.lnx"; | ||||
| 		Krom.fileSaveBytes(path, bytes.getData()); | ||||
| 		#elseif kha_kore | ||||
| 		sys.io.File.saveBytes(path, bytes); | ||||
| @ -47,6 +48,7 @@ typedef TConfig = { | ||||
| 	@:optional var rp_ssr: Null<Bool>; | ||||
| 	@:optional var rp_ssrefr: Null<Bool>; | ||||
| 	@:optional var rp_bloom: Null<Bool>; | ||||
| 	@:optional var rp_chromatic_aberration: Null<Bool>; | ||||
| 	@:optional var rp_motionblur: Null<Bool>; | ||||
| 	@:optional var rp_gi: Null<Bool>; // voxelao | ||||
| 	@:optional var rp_dynres: Null<Bool>; // dynamic resolution scaling | ||||
|  | ||||
							
								
								
									
										99
									
								
								leenkx/Sources/leenkx/logicnode/AddParticleToObjectNode.hx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										99
									
								
								leenkx/Sources/leenkx/logicnode/AddParticleToObjectNode.hx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,99 @@ | ||||
| package leenkx.logicnode; | ||||
|  | ||||
| import iron.data.SceneFormat.TSceneFormat; | ||||
| import iron.data.Data; | ||||
| import iron.object.Object; | ||||
|  | ||||
| class AddParticleToObjectNode extends LogicNode { | ||||
|  | ||||
| 	public var property0: String; | ||||
|  | ||||
| 	public function new(tree: LogicTree) { | ||||
| 		super(tree); | ||||
| 	} | ||||
|  | ||||
| 	override function run(from: Int) { | ||||
| 		#if lnx_particles | ||||
|  | ||||
| 		if (property0 == 'Scene Active'){		 | ||||
| 			var objFrom: Object = inputs[1].get(); | ||||
| 			var slot: Int = inputs[2].get(); | ||||
| 			var objTo: Object = inputs[3].get(); | ||||
| 	 | ||||
| 			if (objFrom == null || objTo == null) return; | ||||
| 	 | ||||
| 			var mobjFrom = cast(objFrom, iron.object.MeshObject); | ||||
| 	 | ||||
| 			var psys = mobjFrom.particleSystems != null ? mobjFrom.particleSystems[slot] :  | ||||
| 				mobjFrom.particleOwner != null && mobjFrom.particleOwner.particleSystems != null ? mobjFrom.particleOwner.particleSystems[slot] : null; | ||||
|  | ||||
| 			if (psys == null) return; | ||||
| 	 | ||||
| 			var mobjTo = cast(objTo, iron.object.MeshObject); | ||||
| 	 | ||||
| 			mobjTo.setupParticleSystem(iron.Scene.active.raw.name, {name: 'LnxPS', seed: 0, particle: @:privateAccess psys.r.name}); | ||||
| 			 | ||||
| 			mobjTo.render_emitter = inputs[4].get(); | ||||
|  | ||||
| 			iron.Scene.active.spawnObject(psys.data.raw.instance_object, null, function(o: Object) { | ||||
| 				if (o != null) { | ||||
| 					var c: iron.object.MeshObject = cast o; | ||||
| 					if (mobjTo.particleChildren == null) mobjTo.particleChildren = []; | ||||
| 					mobjTo.particleChildren.push(c); | ||||
| 					c.particleOwner = mobjTo; | ||||
| 					c.particleIndex = mobjTo.particleChildren.length - 1; | ||||
| 				} | ||||
| 			}); | ||||
| 	 | ||||
| 			var oslot: Int = mobjTo.particleSystems.length-1; | ||||
| 			var opsys = mobjTo.particleSystems[oslot]; | ||||
| 			@:privateAccess opsys.setupGeomGpu(mobjTo.particleChildren[oslot], mobjTo); | ||||
| 		 | ||||
| 		} else { | ||||
| 			var sceneName: String = inputs[1].get(); | ||||
| 			var objectName: String = inputs[2].get(); | ||||
| 			var slot: Int = inputs[3].get(); | ||||
|  | ||||
| 			var mobjTo: Object = inputs[4].get(); | ||||
| 			var mobjTo = cast(mobjTo, iron.object.MeshObject); | ||||
|  | ||||
| 			#if lnx_json | ||||
| 				sceneName += ".json"; | ||||
| 			#elseif lnx_compress | ||||
| 				sceneName += ".lz4"; | ||||
| 			#end | ||||
|  | ||||
| 			Data.getSceneRaw(sceneName, (rawScene: TSceneFormat) -> { | ||||
|  | ||||
| 			for (obj in rawScene.objects) { | ||||
| 				if (obj.name == objectName) { | ||||
| 					mobjTo.setupParticleSystem(sceneName, obj.particle_refs[slot]); | ||||
| 					mobjTo.render_emitter = inputs[5].get();					 | ||||
|  | ||||
| 					iron.Scene.active.spawnObject(rawScene.particle_datas[slot].instance_object, null, function(o: Object) { | ||||
| 						if (o != null) { | ||||
| 							var c: iron.object.MeshObject = cast o; | ||||
| 							if (mobjTo.particleChildren == null) mobjTo.particleChildren = []; | ||||
| 							mobjTo.particleChildren.push(c); | ||||
| 							c.particleOwner = mobjTo; | ||||
| 							c.particleIndex = mobjTo.particleChildren.length - 1; | ||||
| 						} | ||||
| 					}, true, rawScene); | ||||
|  | ||||
| 					var oslot: Int = mobjTo.particleSystems.length-1; | ||||
| 					var opsys = mobjTo.particleSystems[oslot]; | ||||
| 					@:privateAccess opsys.setupGeomGpu(mobjTo.particleChildren[oslot], mobjTo); | ||||
|  | ||||
| 					break; | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| 			}); | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		#end | ||||
|  | ||||
| 		runOutput(0); | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										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; | ||||
|  | ||||
| class CameraSetNode extends LogicNode { | ||||
|  | ||||
| 	 | ||||
| 	public var property0: String; | ||||
| 	 | ||||
| 	public function new(tree:LogicTree) { | ||||
| 		super(tree); | ||||
| 	} | ||||
|  | ||||
| 	override function run(from:Int) { | ||||
| 		leenkx.renderpath.Postprocess.camera_uniforms[0] = inputs[1].get();//Camera: F-Number | ||||
| 		leenkx.renderpath.Postprocess.camera_uniforms[1] = inputs[2].get();//Camera: Shutter time | ||||
| 		leenkx.renderpath.Postprocess.camera_uniforms[2] = inputs[3].get();//Camera: ISO | ||||
| 		leenkx.renderpath.Postprocess.camera_uniforms[3] = inputs[4].get();//Camera: Exposure Compensation | ||||
| 		leenkx.renderpath.Postprocess.camera_uniforms[4] = inputs[5].get();//Fisheye Distortion | ||||
| 		leenkx.renderpath.Postprocess.camera_uniforms[5] = inputs[6].get();//DoF AutoFocus §§ If true, it ignores the DoF Distance setting | ||||
| 		leenkx.renderpath.Postprocess.camera_uniforms[6] = inputs[7].get();//DoF Distance | ||||
| 		leenkx.renderpath.Postprocess.camera_uniforms[7] = inputs[8].get();//DoF Focal Length mm | ||||
| 		leenkx.renderpath.Postprocess.camera_uniforms[8] = inputs[9].get();//DoF F-Stop | ||||
| 		leenkx.renderpath.Postprocess.camera_uniforms[9] = inputs[10].get();//Tonemapping Method | ||||
| 		leenkx.renderpath.Postprocess.camera_uniforms[10] = inputs[11].get();//Distort | ||||
| 		leenkx.renderpath.Postprocess.camera_uniforms[11] = inputs[12].get();//Film Grain | ||||
| 		leenkx.renderpath.Postprocess.camera_uniforms[12] = inputs[13].get();//Sharpen | ||||
| 		leenkx.renderpath.Postprocess.camera_uniforms[13] = inputs[14].get();//Vignette | ||||
|  | ||||
| 		switch (property0) { | ||||
| 			case 'F-stop': | ||||
| 				leenkx.renderpath.Postprocess.camera_uniforms[0] = inputs[1].get();//Camera: F-Number | ||||
| 			case 'Shutter Time':	 | ||||
| 				leenkx.renderpath.Postprocess.camera_uniforms[1] = inputs[1].get();//Camera: Shutter time | ||||
| 			case 'ISO': | ||||
| 				leenkx.renderpath.Postprocess.camera_uniforms[2] = inputs[1].get();//Camera: ISO | ||||
| 			case 'Exposure Compensation': | ||||
| 				leenkx.renderpath.Postprocess.camera_uniforms[3] = inputs[1].get();//Camera: Exposure Compensation | ||||
| 			case 'Fisheye Distortion': | ||||
| 				leenkx.renderpath.Postprocess.camera_uniforms[4] = inputs[1].get();//Fisheye Distortion | ||||
| 			case 'Auto Focus': | ||||
| 				leenkx.renderpath.Postprocess.camera_uniforms[5] = inputs[1].get();//DoF AutoFocus §§ If true, it ignores the DoF Distance setting | ||||
| 			case 'DoF Distance': | ||||
| 				leenkx.renderpath.Postprocess.camera_uniforms[6] = inputs[1].get();//DoF Distance | ||||
| 			case 'DoF Length': | ||||
| 				leenkx.renderpath.Postprocess.camera_uniforms[7] = inputs[1].get();//DoF Focal Length mm | ||||
| 			case 'DoF F-Stop': | ||||
| 				leenkx.renderpath.Postprocess.camera_uniforms[8] = inputs[1].get();//DoF F-Stop | ||||
| 			case 'Tonemapping': | ||||
| 				leenkx.renderpath.Postprocess.camera_uniforms[9] = inputs[1].get();//Tonemapping Method | ||||
| 			case 'Distort': | ||||
| 				leenkx.renderpath.Postprocess.camera_uniforms[10] = inputs[1].get();//Distort | ||||
| 			case 'Film Grain': | ||||
| 				leenkx.renderpath.Postprocess.camera_uniforms[11] = inputs[1].get();//Film Grain | ||||
| 			case 'Sharpen': | ||||
| 				leenkx.renderpath.Postprocess.camera_uniforms[12] = inputs[1].get();//Sharpen | ||||
| 			case 'Vignette': | ||||
| 				leenkx.renderpath.Postprocess.camera_uniforms[13] = inputs[1].get();//Vignette | ||||
| 			case 'Exposure': | ||||
| 				leenkx.renderpath.Postprocess.exposure_uniforms[0] = inputs[1].get();//Exposure | ||||
| 			default:  | ||||
| 				null; | ||||
| 			} | ||||
|  | ||||
| 		runOutput(0); | ||||
| 	} | ||||
|  | ||||
| @ -10,6 +10,7 @@ class ChromaticAberrationGetNode extends LogicNode { | ||||
| 		return switch (from) { | ||||
| 			case 0: leenkx.renderpath.Postprocess.chromatic_aberration_uniforms[0]; | ||||
| 			case 1: leenkx.renderpath.Postprocess.chromatic_aberration_uniforms[1]; | ||||
| 			case 2: leenkx.renderpath.Postprocess.chromatic_aberration_uniforms[2]; | ||||
| 			default: 0.0; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| @ -10,6 +10,7 @@ class ChromaticAberrationSetNode extends LogicNode { | ||||
|  | ||||
|         leenkx.renderpath.Postprocess.chromatic_aberration_uniforms[0] = inputs[1].get(); | ||||
|         leenkx.renderpath.Postprocess.chromatic_aberration_uniforms[1] = inputs[2].get(); | ||||
| 		leenkx.renderpath.Postprocess.chromatic_aberration_uniforms[2] = inputs[3].get(); | ||||
|  | ||||
| 		runOutput(0); | ||||
| 	} | ||||
|  | ||||
| @ -2,6 +2,9 @@ package leenkx.logicnode; | ||||
|  | ||||
| import iron.Scene; | ||||
| import iron.object.CameraObject; | ||||
| import iron.math.Vec4; | ||||
| import iron.math.Quat; | ||||
| import leenkx.math.Helper; | ||||
|  | ||||
| import leenkx.renderpath.RenderPathCreator; | ||||
|  | ||||
| @ -27,11 +30,19 @@ class DrawCameraTextureNode extends LogicNode { | ||||
| 				final c = inputs[2].get(); | ||||
| 				assert(Error, Std.isOfType(c, CameraObject), "Camera must be a camera object!"); | ||||
| 				cam = cast(c, CameraObject); | ||||
| 				rt = kha.Image.createRenderTarget(iron.App.w(), iron.App.h()); | ||||
| 				rt = kha.Image.createRenderTarget(iron.App.w(), iron.App.h(), | ||||
| 					kha.graphics4.TextureFormat.RGBA32, | ||||
| 					kha.graphics4.DepthStencilFormat.NoDepthAndStencil); | ||||
|  | ||||
| 				assert(Error, mo.materials[matSlot].contexts[0].textures != null, 'Object "${mo.name}" has no diffuse texture to render to'); | ||||
| 				mo.materials[matSlot].contexts[0].textures[0] = rt; // Override diffuse texture | ||||
|  | ||||
|                 final n = inputs[5].get(); | ||||
| 				for (i => node in mo.materials[matSlot].contexts[0].raw.bind_textures){ | ||||
| 					if (node.name == n){ | ||||
| 						mo.materials[matSlot].contexts[0].textures[i] = rt; // Override diffuse texture | ||||
| 						break; | ||||
| 					} | ||||
| 				} | ||||
| 				tree.notifyOnRender(render); | ||||
| 				runOutput(0); | ||||
|  | ||||
| @ -48,8 +59,20 @@ class DrawCameraTextureNode extends LogicNode { | ||||
| 		iron.Scene.active.camera = cam; | ||||
| 		cam.renderTarget = rt; | ||||
|  | ||||
| 		#if kha_html5 | ||||
| 		var q: Quat = new Quat(); | ||||
| 		q.fromAxisAngle(new Vec4(0, 0, 1, 1), Helper.degToRad(180)); | ||||
| 		cam.transform.rot.mult(q); | ||||
| 		cam.transform.buildMatrix(); | ||||
| 		#end | ||||
|  | ||||
| 		cam.renderFrame(g); | ||||
|  | ||||
| 		#if kha_html5 | ||||
| 		cam.transform.rot.mult(q); | ||||
| 		cam.transform.buildMatrix(); | ||||
| 		#end | ||||
|  | ||||
| 		cam.renderTarget = oldRT; | ||||
| 		iron.Scene.active.camera = sceneCam; | ||||
| 	} | ||||
|  | ||||
| @ -99,8 +99,6 @@ class DrawImageSequenceNode extends LogicNode { | ||||
| 		final colorVec = inputs[4].get(); | ||||
| 		g.color = Color.fromFloats(colorVec.x, colorVec.y, colorVec.z, colorVec.w); | ||||
|  | ||||
| 		trace(currentImgIdx); | ||||
|  | ||||
| 		g.drawScaledImage(images[currentImgIdx], inputs[5].get(), inputs[6].get(), inputs[7].get(), inputs[8].get()); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @ -62,7 +62,7 @@ class DrawStringNode extends LogicNode { | ||||
|  | ||||
| 	override function get(from: Int): Dynamic { | ||||
|  | ||||
| 		return from == 1 ? RenderToTexture.g.font.height(RenderToTexture.g.fontSize) : RenderToTexture.g.font.width(RenderToTexture.g.fontSize, string); | ||||
| 	 | ||||
| 		return from == 1 ? RenderToTexture.g.font.width(RenderToTexture.g.fontSize, string) : RenderToTexture.g.font.height(RenderToTexture.g.fontSize); | ||||
| 			 | ||||
| 	} | ||||
| } | ||||
|  | ||||
							
								
								
									
										59
									
								
								leenkx/Sources/leenkx/logicnode/DrawSubImageNode.hx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								leenkx/Sources/leenkx/logicnode/DrawSubImageNode.hx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,59 @@ | ||||
| package leenkx.logicnode; | ||||
|  | ||||
| import iron.math.Vec4; | ||||
| import kha.Image; | ||||
| import kha.Color; | ||||
| import leenkx.renderpath.RenderToTexture; | ||||
|  | ||||
| class DrawSubImageNode extends LogicNode { | ||||
|     var img: Image; | ||||
|     var lastImgName = ""; | ||||
|  | ||||
|     public function new(tree: LogicTree) { | ||||
|         super(tree); | ||||
|     } | ||||
|  | ||||
|     override function run(from: Int) { | ||||
|         RenderToTexture.ensure2DContext("DrawImageNode"); | ||||
|  | ||||
|         final imgName: String = inputs[1].get(); | ||||
|         final colorVec: Vec4 = inputs[2].get(); | ||||
|         final anchorH: Int = inputs[3].get(); | ||||
|         final anchorV: Int = inputs[4].get(); | ||||
|         final x: Float = inputs[5].get(); | ||||
|         final y: Float = inputs[6].get(); | ||||
|         final width: Float = inputs[7].get(); | ||||
|         final height: Float = inputs[8].get(); | ||||
|         final sx: Float = inputs[9].get(); | ||||
|         final sy: Float = inputs[10].get(); | ||||
|         final swidth: Float = inputs[11].get(); | ||||
|         final sheight: Float = inputs[12].get(); | ||||
|         final angle: Float = inputs[13].get(); | ||||
|  | ||||
|         final drawx = x - 0.5 * width * anchorH; | ||||
|         final drawy = y - 0.5 * height * anchorV; | ||||
|         final sdrawx = sx - 0.5 * swidth * anchorH; | ||||
|         final sdrawy = sy - 0.5 * sheight * anchorV; | ||||
|  | ||||
|         RenderToTexture.g.rotate(angle, x, y); | ||||
|  | ||||
|         if (imgName != lastImgName) { | ||||
|             // Load new image | ||||
|             lastImgName = imgName; | ||||
|             iron.data.Data.getImage(imgName, (image: Image) -> { | ||||
|                 img = image; | ||||
|             }); | ||||
|         } | ||||
|  | ||||
|         if (img == null) { | ||||
|             runOutput(0); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         RenderToTexture.g.color = Color.fromFloats(colorVec.x, colorVec.y, colorVec.z, colorVec.w); | ||||
|         RenderToTexture.g.drawScaledSubImage(img, sdrawx, sdrawy, swidth, sheight, drawx, drawy, width, height); | ||||
|         RenderToTexture.g.rotate(-angle, x, y); | ||||
|  | ||||
|         runOutput(0); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										17
									
								
								leenkx/Sources/leenkx/logicnode/GetAudioPositionNode.hx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								leenkx/Sources/leenkx/logicnode/GetAudioPositionNode.hx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,17 @@ | ||||
| package leenkx.logicnode; | ||||
|  | ||||
| import aura.Aura; | ||||
| import aura.Types; | ||||
|  | ||||
| class GetAudioPositionNode extends LogicNode { | ||||
|  | ||||
| 	public function new(tree: LogicTree) { | ||||
| 		super(tree); | ||||
| 	} | ||||
|  | ||||
| 	override function get(from: Int): Dynamic { | ||||
| 		var audio = inputs[0].get(); | ||||
| 		if (audio == null || audio.channel == null) return 0.0; | ||||
| 		return audio.channel.floatPosition / audio.channel.sampleRate; | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										19
									
								
								leenkx/Sources/leenkx/logicnode/GetCameraRenderFilterNode.hx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								leenkx/Sources/leenkx/logicnode/GetCameraRenderFilterNode.hx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,19 @@ | ||||
| package leenkx.logicnode; | ||||
|  | ||||
| import iron.object.MeshObject; | ||||
| import iron.object.CameraObject; | ||||
|  | ||||
| class GetCameraRenderFilterNode extends LogicNode { | ||||
|  | ||||
| 	public function new(tree: LogicTree) { | ||||
| 		super(tree); | ||||
| 	} | ||||
|  | ||||
| 	override function get(from: Int): Dynamic { | ||||
| 		var mo: MeshObject = cast inputs[0].get(); | ||||
|  | ||||
| 		if (mo == null) return null; | ||||
|  | ||||
| 		return mo.cameraList; | ||||
| 	} | ||||
| } | ||||
| @ -8,7 +8,7 @@ class GetFPSNode extends LogicNode { | ||||
|  | ||||
|     override function get(from: Int): Dynamic { | ||||
|         if (from == 0) { | ||||
|             var fps = Math.round(1 / iron.system.Time.realDelta); | ||||
|             var fps = Math.round(1 / iron.system.Time.renderDelta); | ||||
|             if ((fps == Math.POSITIVE_INFINITY) || (fps == Math.NEGATIVE_INFINITY) || (Math.isNaN(fps))) { | ||||
|                 return 0; | ||||
|             } | ||||
|  | ||||
							
								
								
									
										72
									
								
								leenkx/Sources/leenkx/logicnode/GetParticleDataNode.hx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								leenkx/Sources/leenkx/logicnode/GetParticleDataNode.hx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,72 @@ | ||||
| package leenkx.logicnode; | ||||
|  | ||||
| import iron.object.Object; | ||||
|  | ||||
| class GetParticleDataNode extends LogicNode { | ||||
|  | ||||
| 	public function new(tree: LogicTree) { | ||||
| 		super(tree); | ||||
| 	} | ||||
|  | ||||
| 	override function get(from: Int): Dynamic { | ||||
| 		var object: Object = inputs[0].get(); | ||||
| 		var slot: Int = inputs[1].get(); | ||||
|  | ||||
| 		if (object == null) return null; | ||||
|  | ||||
| 	#if lnx_particles | ||||
|  | ||||
| 		var mo = cast(object, iron.object.MeshObject); | ||||
|  | ||||
| 		var psys = mo.particleSystems != null ? mo.particleSystems[slot] :  | ||||
| 			mo.particleOwner != null && mo.particleOwner.particleSystems != null ? mo.particleOwner.particleSystems[slot] : null; | ||||
|  | ||||
| 		if (psys == null) return null; | ||||
|  | ||||
| 		return switch (from) { | ||||
| 			case 0: | ||||
| 				@:privateAccess psys.r.name; | ||||
| 			case 1: | ||||
| 				@:privateAccess psys.r.particle_size; | ||||
| 			case 2: | ||||
| 				@:privateAccess psys.r.frame_start; | ||||
| 			case 3: | ||||
| 				@:privateAccess psys.r.frame_end; | ||||
| 			case 4: | ||||
| 				@:privateAccess psys.lifetime; | ||||
| 			case 5: | ||||
| 				@:privateAccess psys.r.lifetime; | ||||
| 			case 6: | ||||
| 				@:privateAccess psys.r.emit_from; | ||||
| 			case 7: | ||||
| 				@:privateAccess psys.r.auto_start; | ||||
| 			case 8: | ||||
| 				@:privateAccess psys.r.is_unique; | ||||
| 			case 9: | ||||
| 				@:privateAccess psys.r.loop; | ||||
| 			case 10: | ||||
| 				new iron.math.Vec3(@:privateAccess psys.alignx, @:privateAccess psys.aligny, @:privateAccess psys.alignz); | ||||
| 			case 11: | ||||
| 				@:privateAccess psys.r.factor_random; | ||||
| 			case 12: | ||||
| 				new iron.math.Vec3(@:privateAccess psys.gx, @:privateAccess psys.gy, @:privateAccess psys.gz); | ||||
| 			case 13: | ||||
| 				@:privateAccess psys.r.weight_gravity; | ||||
| 			case 14: | ||||
| 				psys.speed; | ||||
| 			case 15: | ||||
| 				@:privateAccess psys.time; | ||||
| 			case 16: | ||||
| 				@:privateAccess psys.lap; | ||||
| 			case 17: | ||||
| 				@:privateAccess psys.lapTime; | ||||
| 			case 18: | ||||
| 				@:privateAccess psys.count; | ||||
| 			default:  | ||||
| 				null; | ||||
| 		} | ||||
| 	#end | ||||
|  | ||||
| 		return null; | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										38
									
								
								leenkx/Sources/leenkx/logicnode/GetParticleNode.hx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								leenkx/Sources/leenkx/logicnode/GetParticleNode.hx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,38 @@ | ||||
| package leenkx.logicnode; | ||||
|  | ||||
| import iron.object.Object; | ||||
|  | ||||
| class GetParticleNode extends LogicNode { | ||||
|  | ||||
| 	public function new(tree: LogicTree) { | ||||
| 		super(tree); | ||||
| 	} | ||||
|  | ||||
| 	override function get(from: Int): Dynamic { | ||||
| 		var object: Object = inputs[0].get(); | ||||
|  | ||||
| 		if (object == null) return null; | ||||
|  | ||||
| 	#if lnx_particles | ||||
|  | ||||
| 		var mo = cast(object, iron.object.MeshObject); | ||||
| 	 | ||||
| 		switch (from) { | ||||
| 			case 0: | ||||
| 				var names: Array<String> = []; | ||||
| 				if (mo.particleSystems != null) | ||||
| 					for (psys in mo.particleSystems) | ||||
| 						names.push(@:privateAccess psys.r.name); | ||||
| 				return names; | ||||
| 			case 1: | ||||
| 				return mo.particleSystems != null ? mo.particleSystems.length : 0; | ||||
| 			case 2: | ||||
| 				return mo.render_emitter; | ||||
| 			default:  | ||||
| 				null; | ||||
| 		} | ||||
| 	#end | ||||
|  | ||||
| 		return null; | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										33
									
								
								leenkx/Sources/leenkx/logicnode/GetPositionSpeakerNode.hx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								leenkx/Sources/leenkx/logicnode/GetPositionSpeakerNode.hx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,33 @@ | ||||
| package leenkx.logicnode; | ||||
|  | ||||
| #if lnx_audio | ||||
| import iron.object.SpeakerObject; | ||||
| import kha.audio1.AudioChannel; | ||||
| #end | ||||
|  | ||||
| class GetPositionSpeakerNode extends LogicNode { | ||||
|  | ||||
| 	public function new(tree: LogicTree) { | ||||
| 		super(tree); | ||||
| 	} | ||||
|  | ||||
| 	override function get(from: Int): Dynamic { | ||||
| 		#if lnx_audio | ||||
| 		var object: SpeakerObject = cast(inputs[0].get(), SpeakerObject); | ||||
| 		if (object == null || object.sound == null) return 0.0; | ||||
| 		 | ||||
| 		if (object.channels.length == 0) return 0.0; | ||||
| 		 | ||||
| 		var channel = object.channels[0]; | ||||
| 		 | ||||
| 		var position = 0.0; | ||||
| 		if (channel != null) { | ||||
| 			position = @:privateAccess channel.get_position(); | ||||
| 		} | ||||
| 		 | ||||
| 		return position; | ||||
| 		#else | ||||
| 		return 0.0; | ||||
| 		#end | ||||
| 	} | ||||
| } | ||||
| @ -1,26 +1,12 @@ | ||||
| package leenkx.logicnode; | ||||
|  | ||||
| import iron.object.Object; | ||||
| import iron.math.Vec4; | ||||
|  | ||||
| class GetWorldNode extends LogicNode { | ||||
|  | ||||
| 	public var property0: String; | ||||
|  | ||||
| 	public function new(tree: LogicTree) { | ||||
| 		super(tree); | ||||
| 	} | ||||
|  | ||||
| 	override function get(from: Int): Dynamic { | ||||
| 		var object: Object = inputs[0].get(); | ||||
|  | ||||
| 		if (object == null) return null; | ||||
|  | ||||
| 		return switch (property0) { | ||||
| 			case "Right": object.transform.world.right(); | ||||
| 			case "Look": object.transform.world.look(); | ||||
| 			case "Up": object.transform.world.up(); | ||||
| 			default: null; | ||||
| 		} | ||||
| 		return iron.Scene.active.raw.world_ref; | ||||
| 	} | ||||
| } | ||||
| } | ||||
							
								
								
									
										26
									
								
								leenkx/Sources/leenkx/logicnode/GetWorldOrientationNode.hx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								leenkx/Sources/leenkx/logicnode/GetWorldOrientationNode.hx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,26 @@ | ||||
| package leenkx.logicnode; | ||||
|  | ||||
| import iron.object.Object; | ||||
| import iron.math.Vec4; | ||||
|  | ||||
| class GetWorldOrientationNode extends LogicNode { | ||||
|  | ||||
| 	public var property0: String; | ||||
|  | ||||
| 	public function new(tree: LogicTree) { | ||||
| 		super(tree); | ||||
| 	} | ||||
|  | ||||
| 	override function get(from: Int): Dynamic { | ||||
| 		var object: Object = inputs[0].get(); | ||||
|  | ||||
| 		if (object == null) return null; | ||||
|  | ||||
| 		return switch (property0) { | ||||
| 			case "Right": object.transform.world.right(); | ||||
| 			case "Look": object.transform.world.look(); | ||||
| 			case "Up": object.transform.world.up(); | ||||
| 			default: null; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| @ -1,7 +1,10 @@ | ||||
| package leenkx.logicnode; | ||||
|  | ||||
| import iron.object.Object; | ||||
|  | ||||
| #if lnx_physics | ||||
| import leenkx.trait.physics.PhysicsCache; | ||||
| import leenkx.trait.physics.RigidBody; | ||||
| #end | ||||
|  | ||||
| class HasContactNode extends LogicNode { | ||||
|  | ||||
| @ -15,12 +18,15 @@ class HasContactNode extends LogicNode { | ||||
|  | ||||
| 		if (object1 == null || object2 == null) return false; | ||||
|  | ||||
| #if lnx_physics | ||||
| 		var physics = leenkx.trait.physics.PhysicsWorld.active; | ||||
| 		var rb2 = object2.getTrait(RigidBody); | ||||
| 		var rbs = physics.getContacts(object1.getTrait(RigidBody)); | ||||
| 		if (rbs != null) for (rb in rbs) if (rb == rb2) return true; | ||||
| #end | ||||
| 		#if lnx_physics | ||||
| 		var rb1 = PhysicsCache.getCachedRigidBody(object1); | ||||
| 		var rb2 = PhysicsCache.getCachedRigidBody(object2); | ||||
| 		 | ||||
| 		if (rb1 != null && rb2 != null) { | ||||
| 			var rbs = PhysicsCache.getCachedContacts(rb1); | ||||
| 			return PhysicsCache.hasContactWith(rbs, rb2); | ||||
| 		} | ||||
| 		#end | ||||
| 		return false; | ||||
| 	} | ||||
| } | ||||
|  | ||||
							
								
								
									
										233
									
								
								leenkx/Sources/leenkx/logicnode/MouseLookNode.hx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										233
									
								
								leenkx/Sources/leenkx/logicnode/MouseLookNode.hx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,233 @@ | ||||
| package leenkx.logicnode; | ||||
|  | ||||
| import iron.math.Vec4; | ||||
| import iron.system.Input; | ||||
| import iron.object.Object; | ||||
| import kha.System; | ||||
| import kha.FastFloat; | ||||
|  | ||||
| /** | ||||
|  * MouseLookNode - FPS-style mouse look camera controller | ||||
|  *  | ||||
|  * This node provides smooth, resolution-independent mouse look functionality for  | ||||
|  * first-person perspective controls. It supports separate body and head objects, | ||||
|  * allowing for realistic FPS camera movement where the body rotates horizontally | ||||
|  * and the head/camera rotates vertically. | ||||
|  *  | ||||
|  * Key Features: | ||||
|  * - Resolution-adaptive scaling for consistent feel across different screen sizes | ||||
|  * - Configurable axis orientations (X, Y, Z as front) | ||||
|  * - Optional mouse cursor locking and hiding | ||||
|  * - Invertible X/Y axes | ||||
|  * - Rotation capping/limiting for both horizontal and vertical movement | ||||
|  * - Smoothing support for smoother camera movement | ||||
|  * - Physics integration with automatic rigid body synchronization | ||||
|  * - Support for both local and world space head rotation | ||||
|  */ | ||||
| class MouseLookNode extends LogicNode { | ||||
| 	// Configuration properties (set from Blender node interface) | ||||
| 	public var property0: String;  // Front axis: "X", "Y", or "Z" | ||||
| 	public var property1: Bool;    // Hide Locked: auto-lock mouse cursor | ||||
| 	public var property2: Bool;    // Invert X: invert horizontal mouse movement | ||||
| 	public var property3: Bool;    // Invert Y: invert vertical mouse movement | ||||
| 	public var property4: Bool;    // Cap Left/Right: limit horizontal rotation | ||||
| 	public var property5: Bool;    // Cap Up/Down: limit vertical rotation | ||||
| 	public var property6: Bool;    // Head Local Space: use local space for head rotation | ||||
|  | ||||
| 	// Smoothing state variables - maintain previous frame values for interpolation | ||||
| 	var smoothX: Float = 0.0;      // Smoothed horizontal mouse delta | ||||
| 	var smoothY: Float = 0.0;      // Smoothed vertical mouse delta | ||||
| 	 | ||||
| 	// Rotation limits (in radians) | ||||
| 	var maxHorizontal: Float = Math.PI;      // Maximum horizontal rotation (180 degrees) | ||||
| 	var maxVertical: Float = Math.PI / 2;    // Maximum vertical rotation (90 degrees) | ||||
| 	 | ||||
| 	// Current rotation tracking for capping calculations | ||||
| 	var currentHorizontal: Float = 0.0;      // Accumulated horizontal rotation | ||||
| 	var currentVertical: Float = 0.0;        // Accumulated vertical rotation | ||||
| 	 | ||||
| 	// Resolution scaling reference - base resolution for consistent sensitivity | ||||
| 	var baseResolutionWidth: Float = 1920.0; | ||||
| 	 | ||||
| 	// Sensitivity scaling constants | ||||
| 	static inline var BASE_SCALE: Float = 1500.0;              // Base sensitivity scale factor | ||||
| 	static var RADIAN_SCALING_FACTOR: Float = Math.PI * 50.0 / 180.0;  // Degrees to radians conversion with sensitivity scaling | ||||
|  | ||||
| 	public function new(tree: LogicTree) { | ||||
| 		super(tree); | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * Main execution function called every frame when the node is active | ||||
| 	 *  | ||||
| 	 * Input connections: | ||||
| 	 * [0] - Action trigger (not used in current implementation) | ||||
| 	 * [1] - Body Object: the main object that rotates horizontally | ||||
| 	 * [2] - Head Object: optional object that rotates vertically (typically camera) | ||||
| 	 * [3] - Sensitivity: mouse sensitivity multiplier | ||||
| 	 * [4] - Smoothing: movement smoothing factor (0.0 = no smoothing, 0.99 = maximum smoothing) | ||||
| 	 */ | ||||
| 	override function run(from: Int) { | ||||
| 		// Get input values from connected nodes | ||||
| 		var bodyObject: Object = inputs[1].get(); | ||||
| 		var headObject: Object = inputs[2].get(); | ||||
| 		var sensitivity: FastFloat = inputs[3].get(); | ||||
| 		var smoothing: FastFloat = inputs[4].get(); | ||||
|  | ||||
| 		// Early exit if no body object is provided | ||||
| 		if (bodyObject == null) { | ||||
| 			runOutput(0); | ||||
| 			return; | ||||
| 		} | ||||
|  | ||||
| 		// Get mouse input state | ||||
| 		var mouse = Input.getMouse(); | ||||
| 		 | ||||
| 		// Handle automatic mouse cursor locking for FPS controls | ||||
| 		if (property1) { | ||||
| 			if (mouse.started() && !mouse.locked) { | ||||
| 				mouse.lock();  // Center and hide cursor, enable unlimited movement | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		// Only process mouse look when cursor is locked or mouse button is held | ||||
| 		// This prevents unwanted camera movement when UI elements are being used | ||||
| 		if (!mouse.locked && !mouse.down()) { | ||||
| 			runOutput(0); | ||||
| 			return; | ||||
| 		} | ||||
|  | ||||
| 		// Get raw mouse movement delta (pixels moved since last frame) | ||||
| 		var deltaX: Float = mouse.movementX; | ||||
| 		var deltaY: Float = mouse.movementY; | ||||
|  | ||||
| 		// Apply axis inversion if configured | ||||
| 		if (property2) deltaX = -deltaX;  // Invert horizontal movement | ||||
| 		if (property3) deltaY = -deltaY;  // Invert vertical movement | ||||
|  | ||||
| 		// Calculate resolution-adaptive scaling to maintain consistent sensitivity | ||||
| 		// across different screen resolutions. Higher resolutions will have proportionally | ||||
| 		// higher scaling to compensate for increased pixel density. | ||||
| 		var resolutionMultiplier: Float = System.windowWidth() / baseResolutionWidth; | ||||
|  | ||||
| 		// Apply movement smoothing if enabled | ||||
| 		// This creates a weighted average between current and previous movement values | ||||
| 		// to reduce jittery camera movement, especially useful for low framerates | ||||
| 		if (smoothing > 0.0) { | ||||
| 			var smoothingFactor: Float = Math.min(smoothing, 0.99);  // Cap smoothing to prevent complete freeze | ||||
| 			smoothX = smoothX * smoothingFactor + deltaX * (1.0 - smoothingFactor); | ||||
| 			smoothY = smoothY * smoothingFactor + deltaY * (1.0 - smoothingFactor); | ||||
| 			deltaX = smoothX; | ||||
| 			deltaY = smoothY; | ||||
| 		} | ||||
|  | ||||
| 		// Define rotation axes based on the configured front axis | ||||
| 		// These determine which 3D axes are used for horizontal and vertical rotation | ||||
| 		var horizontalAxis = new Vec4();  // Axis for left/right body rotation | ||||
| 		var verticalAxis = new Vec4();    // Axis for up/down head rotation | ||||
| 		 | ||||
| 		switch (property0) { | ||||
| 			case "X":  // X-axis forward (e.g., for side-scrolling or specific orientations) | ||||
| 				horizontalAxis.set(0, 0, 1);  // Z-axis for horizontal rotation | ||||
| 				verticalAxis.set(0, 1, 0);    // Y-axis for vertical rotation | ||||
| 			case "Y":  // Y-axis forward (most common for 3D games) | ||||
| 				#if lnx_yaxisup | ||||
| 				// Y-up coordinate system (Blender default) | ||||
| 				horizontalAxis.set(0, 0, 1);  // Z-axis for horizontal rotation | ||||
| 				verticalAxis.set(1, 0, 0);    // X-axis for vertical rotation | ||||
| 				#else | ||||
| 				// Z-up coordinate system | ||||
| 				horizontalAxis.set(0, 0, 1);  // Z-axis for horizontal rotation | ||||
| 				verticalAxis.set(1, 0, 0);    // X-axis for vertical rotation | ||||
| 				#end | ||||
| 			case "Z":  // Z-axis forward (top-down or specific orientations) | ||||
| 				horizontalAxis.set(0, 1, 0);  // Y-axis for horizontal rotation | ||||
| 				verticalAxis.set(1, 0, 0);    // X-axis for vertical rotation | ||||
| 		} | ||||
|  | ||||
| 		// Calculate final sensitivity scaling combining base scale and resolution adaptation | ||||
| 		var finalScale: Float = BASE_SCALE * resolutionMultiplier; | ||||
|  | ||||
| 		// Apply user-defined sensitivity multiplier | ||||
| 		deltaX *= sensitivity; | ||||
| 		deltaY *= sensitivity; | ||||
|  | ||||
| 		// Convert pixel movement to rotation angles (radians) | ||||
| 		// Negative values ensure natural movement direction (moving mouse right rotates right) | ||||
| 		var horizontalRotation: Float = (-deltaX / finalScale) * RADIAN_SCALING_FACTOR; | ||||
| 		var verticalRotation: Float = (-deltaY / finalScale) * RADIAN_SCALING_FACTOR; | ||||
|  | ||||
| 		// Apply horizontal rotation capping if enabled | ||||
| 		// This prevents the character from rotating beyond specified limits | ||||
| 		if (property4) { | ||||
| 			currentHorizontal += horizontalRotation; | ||||
| 			// Clamp rotation to maximum horizontal range and adjust current frame rotation | ||||
| 			if (currentHorizontal > maxHorizontal) { | ||||
| 				horizontalRotation -= (currentHorizontal - maxHorizontal); | ||||
| 				currentHorizontal = maxHorizontal; | ||||
| 			} else if (currentHorizontal < -maxHorizontal) { | ||||
| 				horizontalRotation -= (currentHorizontal + maxHorizontal); | ||||
| 				currentHorizontal = -maxHorizontal; | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		// Apply vertical rotation capping if enabled | ||||
| 		// This prevents looking too far up or down (like human neck limitations) | ||||
| 		if (property5) { | ||||
| 			currentVertical += verticalRotation; | ||||
| 			// Clamp rotation to maximum vertical range and adjust current frame rotation | ||||
| 			if (currentVertical > maxVertical) { | ||||
| 				verticalRotation -= (currentVertical - maxVertical); | ||||
| 				currentVertical = maxVertical; | ||||
| 			} else if (currentVertical < -maxVertical) { | ||||
| 				verticalRotation -= (currentVertical + maxVertical); | ||||
| 				currentVertical = -maxVertical; | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		// Apply horizontal rotation to body object (character turning left/right) | ||||
| 		if (horizontalRotation != 0.0) { | ||||
| 			bodyObject.transform.rotate(horizontalAxis, horizontalRotation); | ||||
| 			 | ||||
| 			// Synchronize physics rigid body if present | ||||
| 			// This ensures physics simulation stays in sync with visual transform | ||||
| 			#if lnx_physics | ||||
| 			var rigidBody = bodyObject.getTrait(leenkx.trait.physics.RigidBody); | ||||
| 			if (rigidBody != null) rigidBody.syncTransform(); | ||||
| 			#end | ||||
| 		} | ||||
|  | ||||
| 		// Apply vertical rotation to head object (camera looking up/down) | ||||
| 		if (headObject != null && verticalRotation != 0.0) { | ||||
| 			if (property6) { | ||||
| 				// Local space rotation - recommended when head is a child of body | ||||
| 				// This prevents gimbal lock and rotation inheritance issues | ||||
| 				headObject.transform.rotate(verticalAxis, verticalRotation); | ||||
| 			} else { | ||||
| 				// World space rotation - uses head object's current right vector | ||||
| 				// More accurate for independent head objects but can cause issues with parenting | ||||
| 				var headVerticalAxis = headObject.transform.world.right(); | ||||
| 				headObject.transform.rotate(headVerticalAxis, verticalRotation); | ||||
| 			} | ||||
| 			 | ||||
| 			// Synchronize head physics rigid body if present | ||||
| 			#if lnx_physics | ||||
| 			var headRigidBody = headObject.getTrait(leenkx.trait.physics.RigidBody); | ||||
| 			if (headRigidBody != null) headRigidBody.syncTransform(); | ||||
| 			#end | ||||
| 		} else if (headObject == null && verticalRotation != 0.0) { | ||||
| 			// Fallback: if no separate head object, apply vertical rotation to body | ||||
| 			// This creates a simpler single-object camera control | ||||
| 			bodyObject.transform.rotate(verticalAxis, verticalRotation); | ||||
| 			 | ||||
| 			// Synchronize body physics rigid body | ||||
| 			#if lnx_physics | ||||
| 			var rigidBody = bodyObject.getTrait(leenkx.trait.physics.RigidBody); | ||||
| 			if (rigidBody != null) rigidBody.syncTransform(); | ||||
| 			#end | ||||
| 		} | ||||
|  | ||||
| 		// Continue to next connected node in the logic tree | ||||
| 		runOutput(0); | ||||
| 	} | ||||
| }  | ||||
| @ -1,7 +1,11 @@ | ||||
| package leenkx.logicnode; | ||||
|  | ||||
| import iron.object.Object; | ||||
|  | ||||
| #if lnx_physics | ||||
| import leenkx.trait.physics.PhysicsCache; | ||||
| import leenkx.trait.physics.RigidBody; | ||||
| #end | ||||
|  | ||||
|  | ||||
| class OnContactNode extends LogicNode { | ||||
|  | ||||
| @ -23,22 +27,15 @@ class OnContactNode extends LogicNode { | ||||
|  | ||||
| 		var contact = false; | ||||
|  | ||||
| #if lnx_physics | ||||
| 		var physics = leenkx.trait.physics.PhysicsWorld.active; | ||||
| 		var rb1 = object1.getTrait(RigidBody); | ||||
| 		if (rb1 != null) { | ||||
| 			var rbs = physics.getContacts(rb1); | ||||
| 			if (rbs != null) { | ||||
| 				var rb2 = object2.getTrait(RigidBody); | ||||
| 				for (rb in rbs) { | ||||
| 					if (rb == rb2) { | ||||
| 						contact = true; | ||||
| 						break; | ||||
| 					} | ||||
| 				} | ||||
| 		#if lnx_physics | ||||
| 			var rb1 = PhysicsCache.getCachedRigidBody(object1); | ||||
| 			var rb2 = PhysicsCache.getCachedRigidBody(object2); | ||||
| 			 | ||||
| 			if (rb1 != null && rb2 != null) { | ||||
| 				var rbs = PhysicsCache.getCachedContacts(rb1); | ||||
| 				contact = PhysicsCache.hasContactWith(rbs, rb2); | ||||
| 			} | ||||
| 		} | ||||
| #end | ||||
| 		#end | ||||
|  | ||||
| 		var b = false; | ||||
| 		switch (property0) { | ||||
|  | ||||
							
								
								
									
										23
									
								
								leenkx/Sources/leenkx/logicnode/OnceNode.hx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								leenkx/Sources/leenkx/logicnode/OnceNode.hx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,23 @@ | ||||
| package leenkx.logicnode; | ||||
|  | ||||
| class OnceNode extends LogicNode { | ||||
|  | ||||
|     var triggered:Bool = false; | ||||
|  | ||||
|     public function new(tree: LogicTree) { | ||||
|         super(tree); | ||||
|     } | ||||
|  | ||||
|     override function run(from: Int) { | ||||
| 		if(from == 1){ | ||||
| 			triggered = false; | ||||
| 			return; | ||||
| 		} | ||||
|  | ||||
|         if (!triggered) { | ||||
| 			triggered = true; | ||||
|             runOutput(0); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| } | ||||
| @ -9,19 +9,38 @@ import iron.Scene; | ||||
|  | ||||
| class PlayAnimationTreeNode extends LogicNode { | ||||
|  | ||||
| 	var object: Object; | ||||
| 	var action: Dynamic; | ||||
| 	var init: Bool = false; | ||||
|  | ||||
| 	public function new(tree: LogicTree) { | ||||
| 		super(tree); | ||||
| 	} | ||||
|  | ||||
| 	override function run(from: Int) { | ||||
| 		var object: Object = inputs[1].get(); | ||||
| 		var action: Dynamic = inputs[2].get(); | ||||
|  | ||||
| 		object = inputs[1].get(); | ||||
| 		action = inputs[2].get(); | ||||
| 		assert(Error, object != null, "The object input not be null"); | ||||
| 		init = true; | ||||
| 		tree.notifyOnUpdate(playAnim); | ||||
| 		// TO DO: Investigate AnimAction get and PlayAnimationTree notifiers | ||||
| 	} | ||||
| 	 | ||||
| 	function playAnim() { | ||||
| 		if (init = false) return; | ||||
| 		 | ||||
| 		init = false; | ||||
| 		tree.removeUpdate(playAnim); | ||||
| 		 | ||||
| 		var animation = object.animation; | ||||
| 		if(animation == null) { | ||||
| 			#if lnx_skin | ||||
| 			animation = object.getBoneAnimation(object.uid); | ||||
| 			if (animation == null) { | ||||
| 				tree.notifyOnUpdate(playAnim); | ||||
| 				init = true; | ||||
| 				return; | ||||
| 			} | ||||
| 			cast(animation, BoneAnimation).setAnimationLoop(function f(mats) { | ||||
| 				action(mats); | ||||
| 			}); | ||||
| @ -32,7 +51,6 @@ class PlayAnimationTreeNode extends LogicNode { | ||||
| 				action(mats); | ||||
| 			}); | ||||
| 		} | ||||
| 		 | ||||
| 		runOutput(0); | ||||
| 	} | ||||
| } | ||||
|  | ||||
							
								
								
									
										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){ | ||||
| 			trace(sum); | ||||
| 			for (p in 0...probs.length) | ||||
| 				probs[p] /= sum; | ||||
| 		} | ||||
|  | ||||
| @ -0,0 +1,64 @@ | ||||
| package leenkx.logicnode; | ||||
|  | ||||
| import iron.object.Object; | ||||
|  | ||||
| class RemoveParticleFromObjectNode extends LogicNode { | ||||
|  | ||||
| 	public var property0: String; | ||||
|  | ||||
| 	public function new(tree: LogicTree) { | ||||
| 		super(tree); | ||||
| 	} | ||||
|  | ||||
| 	override function run(from: Int) { | ||||
| 		#if lnx_particles | ||||
| 		var object: Object = inputs[1].get(); | ||||
|  | ||||
| 		if (object == null) return; | ||||
|  | ||||
| 		var mo = cast(object, iron.object.MeshObject); | ||||
|  | ||||
| 		if (mo.particleSystems == null) return; | ||||
|  | ||||
| 		if (property0 == 'All'){ | ||||
| 			mo.particleSystems = null; | ||||
| 			for (c in mo.particleChildren) c.remove(); | ||||
| 			mo.particleChildren = null; | ||||
| 			mo.particleOwner = null; | ||||
| 			mo.render_emitter = true; | ||||
| 		}  | ||||
| 		else { | ||||
| 			 | ||||
| 			var slot: Int = -1; | ||||
| 			if (property0 == 'Name'){ | ||||
| 				var name: String = inputs[2].get(); | ||||
| 				for (i => psys in mo.particleSystems){ | ||||
| 					if (@:privateAccess psys.r.name == name){ slot = i; break; } | ||||
| 				} | ||||
| 			}  | ||||
| 			else slot = inputs[2].get(); | ||||
| 				 | ||||
| 			if (mo.particleSystems.length > slot){ | ||||
| 				for (i in slot+1...mo.particleSystems.length){ | ||||
| 					var mi = cast(mo.particleChildren[i], iron.object.MeshObject); | ||||
| 					mi.particleIndex = mi.particleIndex - 1; | ||||
| 				} | ||||
| 				mo.particleSystems.splice(slot, 1); | ||||
| 				mo.particleChildren[slot].remove(); | ||||
| 				mo.particleChildren.splice(slot, 1); | ||||
| 			} | ||||
|  | ||||
| 			if (slot == 0){ | ||||
| 				mo.particleSystems = null; | ||||
| 				mo.particleChildren = null; | ||||
| 				mo.particleOwner = null; | ||||
| 				mo.render_emitter = true; | ||||
| 			} | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		#end | ||||
|  | ||||
| 		runOutput(0); | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										16
									
								
								leenkx/Sources/leenkx/logicnode/ResolutionGetNode.hx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								leenkx/Sources/leenkx/logicnode/ResolutionGetNode.hx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,16 @@ | ||||
| package leenkx.logicnode; | ||||
|  | ||||
| class ResolutionGetNode extends LogicNode { | ||||
|  | ||||
| 	public function new(tree:LogicTree) { | ||||
| 		super(tree); | ||||
| 	} | ||||
|  | ||||
| 	override function get(from:Int):Dynamic { | ||||
| 		return switch (from) { | ||||
| 			case 0: leenkx.renderpath.Postprocess.resolution_uniforms[0]; | ||||
| 			case 1: leenkx.renderpath.Postprocess.resolution_uniforms[1]; | ||||
| 			default: 0; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										33
									
								
								leenkx/Sources/leenkx/logicnode/ResolutionSetNode.hx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								leenkx/Sources/leenkx/logicnode/ResolutionSetNode.hx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,33 @@ | ||||
| package leenkx.logicnode; | ||||
|  | ||||
| import kha.graphics4.TextureFilter; | ||||
|  | ||||
| class ResolutionSetNode extends LogicNode { | ||||
|  | ||||
| 	public function new(tree:LogicTree) { | ||||
| 		super(tree); | ||||
| 	} | ||||
|  | ||||
| 	override function run(from:Int) { | ||||
|  | ||||
| 		var size: Int = inputs[1].get(); | ||||
| 		var filter: Int = inputs[2].get(); | ||||
|  | ||||
| 	#if rp_resolution_filter  | ||||
| 		if (filter == 0) | ||||
| 			iron.object.Uniforms.defaultFilter = TextureFilter.LinearFilter; | ||||
| 		else | ||||
| 			iron.object.Uniforms.defaultFilter = TextureFilter.PointFilter; | ||||
|  | ||||
| 		leenkx.renderpath.Postprocess.resolution_uniforms[0] = size; | ||||
| 		leenkx.renderpath.Postprocess.resolution_uniforms[1] = filter; | ||||
|  | ||||
| 		var npath = leenkx.renderpath.RenderPathCreator.get(); | ||||
| 		var world = iron.Scene.active.raw.world_ref; | ||||
| 		npath.loadShader("shader_datas/World_" + world + "/World_" + world); | ||||
| 		iron.RenderPath.setActive(npath); | ||||
|     #end | ||||
|  | ||||
| 		runOutput(0); | ||||
| 	} | ||||
| } | ||||
| @ -20,6 +20,8 @@ class RpConfigNode extends LogicNode { | ||||
| 			on ? leenkx.data.Config.raw.rp_ssrefr = true : leenkx.data.Config.raw.rp_ssrefr = false; | ||||
|         case "Bloom": | ||||
| 			on ? leenkx.data.Config.raw.rp_bloom = true : leenkx.data.Config.raw.rp_bloom = false; | ||||
| 		case "CA": | ||||
| 			on ? leenkx.data.Config.raw.rp_chromatic_aberration = true : leenkx.data.Config.raw.rp_chromatic_aberration = false; | ||||
|         case "GI": | ||||
| 			on ? leenkx.data.Config.raw.rp_gi = true : leenkx.data.Config.raw.rp_gi = false; | ||||
| 		case "Motion Blur": | ||||
|  | ||||
							
								
								
									
										23
									
								
								leenkx/Sources/leenkx/logicnode/SetAudioPositionNode.hx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								leenkx/Sources/leenkx/logicnode/SetAudioPositionNode.hx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,23 @@ | ||||
| package leenkx.logicnode; | ||||
|  | ||||
| import aura.Aura; | ||||
| import aura.Types; | ||||
|  | ||||
| class SetAudioPositionNode extends LogicNode { | ||||
|  | ||||
| 	public function new(tree: LogicTree) { | ||||
| 		super(tree); | ||||
| 	} | ||||
|  | ||||
| 	override function run(from: Int) { | ||||
| 		var audio = inputs[1].get(); | ||||
| 		if (audio == null) return; | ||||
| 		 | ||||
| 		var positionInSeconds:Float = inputs[2].get(); | ||||
| 		if (positionInSeconds < 0.0) positionInSeconds = 0.0; | ||||
| 		 | ||||
| 		audio.channel.floatPosition = positionInSeconds * audio.channel.sampleRate; | ||||
|  | ||||
| 		runOutput(0); | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										38
									
								
								leenkx/Sources/leenkx/logicnode/SetCameraRenderFilterNode.hx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								leenkx/Sources/leenkx/logicnode/SetCameraRenderFilterNode.hx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,38 @@ | ||||
| package leenkx.logicnode; | ||||
|  | ||||
| import iron.object.MeshObject; | ||||
| import iron.object.CameraObject; | ||||
|  | ||||
| class SetCameraRenderFilterNode extends LogicNode { | ||||
|  | ||||
| 	public var property0: String; | ||||
|  | ||||
| 	public function new(tree: LogicTree) { | ||||
| 		super(tree); | ||||
| 	} | ||||
|  | ||||
| 	override function run(from: Int) { | ||||
| 		var mo: MeshObject = cast inputs[1].get(); | ||||
| 		var camera: CameraObject = inputs[2].get(); | ||||
|  | ||||
| 		assert(Error, Std.isOfType(camera, CameraObject), "Camera must be a camera object!"); | ||||
|  | ||||
| 		if (camera == null || mo == null) return; | ||||
|  | ||||
| 		if (property0 == 'Add'){ | ||||
| 			if (mo.cameraList == null || mo.cameraList.indexOf(camera.name) == -1){ | ||||
| 				if (mo.cameraList == null) mo.cameraList = []; | ||||
| 				mo.cameraList.push(camera.name); | ||||
| 			} | ||||
| 		} | ||||
| 		else{ | ||||
| 			if (mo.cameraList != null){ | ||||
| 				mo.cameraList.remove(camera.name); | ||||
| 				if (mo.cameraList.length == 0) | ||||
| 					mo.cameraList = null; | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		runOutput(0); | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										21
									
								
								leenkx/Sources/leenkx/logicnode/SetLightShadowNode.hx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								leenkx/Sources/leenkx/logicnode/SetLightShadowNode.hx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,21 @@ | ||||
| package leenkx.logicnode; | ||||
|  | ||||
| import iron.object.LightObject; | ||||
|  | ||||
| class SetLightShadowNode extends LogicNode { | ||||
|  | ||||
| 	public function new(tree: LogicTree) { | ||||
| 		super(tree); | ||||
| 	} | ||||
|  | ||||
| 	override function run(from: Int) { | ||||
| 		var light: LightObject = inputs[1].get(); | ||||
| 		var shadow: Bool = inputs[2].get(); | ||||
|  | ||||
| 		if (light == null) return; | ||||
|  | ||||
| 		light.data.raw.cast_shadow = shadow; | ||||
|  | ||||
| 		runOutput(0); | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										206
									
								
								leenkx/Sources/leenkx/logicnode/SetLookAtRotationNode.hx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										206
									
								
								leenkx/Sources/leenkx/logicnode/SetLookAtRotationNode.hx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,206 @@ | ||||
| package leenkx.logicnode; | ||||
|  | ||||
| import iron.math.Vec4; | ||||
| import iron.math.Quat; | ||||
| import iron.math.Mat4; | ||||
| import iron.object.Object; | ||||
|  | ||||
| class SetLookAtRotationNode extends LogicNode { | ||||
|  | ||||
| 	public var property0: String; // Axis to align | ||||
| 	public var property1: String; // Use vector for target (true/false) | ||||
| 	public var property2: String; // Use vector for source (true/false) | ||||
| 	public var property3: String; // Damping value (backward compatibility, now input socket) | ||||
| 	public var property4: String; // Disable rotation on aligning axis (true/false) | ||||
| 	public var property5: String; // Use local space (true/false) | ||||
| 	 | ||||
| 	// Store the calculated rotation for output | ||||
| 	var calculatedRotation: Quat = null; | ||||
| 	// Store the previous rotation for smooth interpolation | ||||
| 	var previousRotation: Quat = null; | ||||
|  | ||||
| 	public function new(tree: LogicTree) { | ||||
| 		super(tree); | ||||
| 		previousRotation = new Quat(); | ||||
| 	} | ||||
|  | ||||
| 	override function run(from: Int): Void { | ||||
| 		// Determine if we're using a vector or an object as source | ||||
| 		var useSourceVector: Bool = property2 == "true"; | ||||
| 		var objectToUse: Object = null; | ||||
| 		var objectLoc: Vec4 = null; | ||||
|  | ||||
| 		if (useSourceVector) { | ||||
| 			// Use tree.object as the object to rotate | ||||
| 			objectToUse = tree.object; | ||||
| 			if (objectToUse == null) { | ||||
| 				runOutput(0); | ||||
| 				return; | ||||
| 			} | ||||
|  | ||||
| 			// Get the source location directly | ||||
| 			objectLoc = inputs[1].get(); | ||||
| 			if (objectLoc == null) { | ||||
| 				runOutput(0); | ||||
| 				return; | ||||
| 			} | ||||
| 		} else { | ||||
| 			// Get the source object (or fallback to tree.object) | ||||
| 			objectToUse = (inputs.length > 1 && inputs[1] != null) ? inputs[1].get() : tree.object; | ||||
| 			if (objectToUse == null) { | ||||
| 				runOutput(0); | ||||
| 				return; | ||||
| 			} | ||||
|  | ||||
| 			// Get source object's WORLD position (important for child objects) | ||||
| 			objectLoc = new Vec4(objectToUse.transform.worldx(), objectToUse.transform.worldy(), objectToUse.transform.worldz()); | ||||
| 		} | ||||
|  | ||||
| 		// Determine if we're using a vector or an object as target | ||||
| 		var useTargetVector: Bool = property1 == "true"; | ||||
| 		var targetLoc: Vec4 = null; | ||||
|  | ||||
| 		if (useTargetVector) { | ||||
| 			// Get the target location directly | ||||
| 			targetLoc = inputs[2].get(); | ||||
| 			if (targetLoc == null) { | ||||
| 				runOutput(0); | ||||
| 				return; | ||||
| 			} | ||||
| 		} else { | ||||
| 			// Get the target object | ||||
| 			var targetObject: Object = inputs[2].get(); | ||||
| 			if (targetObject == null) { | ||||
| 				runOutput(0); | ||||
| 				return; | ||||
| 			} | ||||
| 			 | ||||
| 			// Get target object's WORLD position (important for child objects) | ||||
| 			targetLoc = new Vec4(targetObject.transform.worldx(), targetObject.transform.worldy(), targetObject.transform.worldz()); | ||||
| 		} | ||||
| 		 | ||||
| 		// Calculate direction to target | ||||
| 		var direction = new Vec4( | ||||
| 			targetLoc.x - objectLoc.x, | ||||
| 			targetLoc.y - objectLoc.y, | ||||
| 			targetLoc.z - objectLoc.z | ||||
| 		); | ||||
| 		direction.normalize(); | ||||
| 		 | ||||
| 		// Calculate target rotation based on selected axis | ||||
| 		calculatedRotation = new Quat(); | ||||
| 		switch (property0) { | ||||
| 			case "X": | ||||
| 				calculatedRotation.fromTo(new Vec4(1, 0, 0), direction); | ||||
| 			case "-X": | ||||
| 				calculatedRotation.fromTo(new Vec4(-1, 0, 0), direction); | ||||
| 			case "Y": | ||||
| 				calculatedRotation.fromTo(new Vec4(0, 1, 0), direction); | ||||
| 			case "-Y": | ||||
| 				calculatedRotation.fromTo(new Vec4(0, -1, 0), direction); | ||||
| 			case "Z": | ||||
| 				calculatedRotation.fromTo(new Vec4(0, 0, 1), direction); | ||||
| 			case "-Z": | ||||
| 				calculatedRotation.fromTo(new Vec4(0, 0, -1), direction); | ||||
| 		} | ||||
| 		 | ||||
| 		// If disable rotation on aligning axis is enabled, constrain the target rotation | ||||
| 		if (property4 == "true") { | ||||
| 			// Apply constraint to the target rotation BEFORE damping to avoid jiggling | ||||
| 			var eulerAngles = calculatedRotation.toEulerOrdered("XYZ"); | ||||
| 			 | ||||
| 			// Set the rotation around the selected axis to 0 | ||||
| 			switch (property0) { | ||||
| 				case "X", "-X": | ||||
| 					eulerAngles.x = 0.0; | ||||
| 				case "Y", "-Y": | ||||
| 					eulerAngles.y = 0.0; | ||||
| 				case "Z", "-Z": | ||||
| 					eulerAngles.z = 0.0; | ||||
| 			} | ||||
| 			 | ||||
| 			// Convert back to quaternion | ||||
| 			calculatedRotation.fromEulerOrdered(eulerAngles, "XYZ"); | ||||
| 		} | ||||
| 		 | ||||
| 		// Convert world rotation to local rotation if local space is enabled and object has a parent | ||||
| 		var targetRotation = new Quat(); | ||||
| 		if (property5 == "true" && objectToUse.parent != null) { | ||||
| 			// Get parent's world rotation | ||||
| 			var parentWorldLoc = new Vec4(); | ||||
| 			var parentWorldRot = new Quat(); | ||||
| 			var parentWorldScale = new Vec4(); | ||||
| 			objectToUse.parent.transform.world.decompose(parentWorldLoc, parentWorldRot, parentWorldScale); | ||||
| 			 | ||||
| 			// Convert world rotation to local space by removing parent's rotation influence | ||||
| 			// local_rotation = inverse(parent_world_rotation) * world_rotation | ||||
| 			var invParentRot = new Quat().setFrom(parentWorldRot); | ||||
| 			invParentRot.x = -invParentRot.x; | ||||
| 			invParentRot.y = -invParentRot.y; | ||||
| 			invParentRot.z = -invParentRot.z; | ||||
| 			 | ||||
| 			targetRotation.multquats(invParentRot, calculatedRotation); | ||||
| 		} else { | ||||
| 			// No local space conversion needed, use world rotation directly | ||||
| 			targetRotation.setFrom(calculatedRotation); | ||||
| 		} | ||||
| 		 | ||||
| 		// Apply rotation with damping | ||||
| 		var dampingValue: Float = 0.0; | ||||
| 		 | ||||
| 		// Try to get damping from input socket first (index 3), fallback to property | ||||
| 		if (inputs.length > 3 && inputs[3] != null) { | ||||
| 			var dampingInput: Dynamic = inputs[3].get(); | ||||
| 			if (dampingInput != null) { | ||||
| 				dampingValue = dampingInput; | ||||
| 			} | ||||
| 		} else { | ||||
| 			// Fallback to property for backward compatibility | ||||
| 			dampingValue = Std.parseFloat(property3); | ||||
| 		} | ||||
| 		 | ||||
| 		if (dampingValue > 0.0) { | ||||
| 			// Create a fixed interpolation rate that never reaches exactly 1.0 | ||||
| 			// Higher damping = slower rotation (smaller step) | ||||
| 			var step = Math.max(0.001, (1.0 - dampingValue) * 0.2); // 0.001 to 0.2 range | ||||
| 			 | ||||
| 			// Get current local rotation as quaternion | ||||
| 			var currentLocalRot = new Quat().setFrom(objectToUse.transform.rot); | ||||
| 			 | ||||
| 			// Calculate the difference between current and target rotation | ||||
| 			var diffQuat = new Quat(); | ||||
| 			// q1 * inverse(q2) gives the rotation from q2 to q1 | ||||
| 			var invCurrent = new Quat().setFrom(currentLocalRot); | ||||
| 			invCurrent.x = -invCurrent.x; | ||||
| 			invCurrent.y = -invCurrent.y; | ||||
| 			invCurrent.z = -invCurrent.z; | ||||
| 			diffQuat.multquats(targetRotation, invCurrent); | ||||
| 			 | ||||
| 			// Convert to axis-angle representation | ||||
| 			var axis = new Vec4(); | ||||
| 			var angle = diffQuat.toAxisAngle(axis); | ||||
| 			 | ||||
| 			// Apply only a portion of this rotation (step) | ||||
| 			var partialAngle = angle * step; | ||||
| 			 | ||||
| 			// Create partial rotation quaternion | ||||
| 			var partialRot = new Quat().fromAxisAngle(axis, partialAngle); | ||||
| 			 | ||||
| 			// Apply this partial rotation to current local rotation | ||||
| 			var newLocalRot = new Quat(); | ||||
| 			newLocalRot.multquats(partialRot, currentLocalRot); | ||||
| 			 | ||||
| 			// Apply the new local rotation | ||||
| 			objectToUse.transform.rot.setFrom(newLocalRot); | ||||
| 		} else { | ||||
| 			// No damping, apply instant rotation | ||||
| 			objectToUse.transform.rot.setFrom(targetRotation); | ||||
| 		} | ||||
| 		 | ||||
| 		objectToUse.transform.buildMatrix(); | ||||
| 		 | ||||
| 		runOutput(0); | ||||
| 	} | ||||
|  | ||||
| 	// No output sockets needed - this node only performs actions | ||||
| } | ||||
| @ -0,0 +1,55 @@ | ||||
| package leenkx.logicnode; | ||||
|  | ||||
| import iron.object.MeshObject; | ||||
| import iron.data.MaterialData; | ||||
|  | ||||
| class SetMaterialTextureFilterNode extends LogicNode { | ||||
|  | ||||
| 	public function new(tree: LogicTree) { | ||||
| 		super(tree); | ||||
| 	} | ||||
|  | ||||
| 	override function run(from: Int) { | ||||
| 		var object: MeshObject = inputs[1].get(); | ||||
| 		var mat: MaterialData = inputs[2].get(); | ||||
| 		var slot: Int = inputs[3].get(); | ||||
| 		var name: String = inputs[4].get(); | ||||
| 		var filter: Int = inputs[5].get(); | ||||
|  | ||||
| 		if (object == null) return; | ||||
| 		if (slot >= object.materials.length) return; | ||||
|  | ||||
| 		var mo = cast(object, iron.object.MeshObject); | ||||
|  | ||||
| 		for (i => node in mo.materials[slot].contexts[0].raw.bind_textures) | ||||
| 			if (node.name == name){ | ||||
| 				var moImgt = mo.materials[slot].contexts[0].raw.bind_textures[i]; | ||||
| 				switch(filter){ | ||||
| 					case 0: //Linear | ||||
| 						moImgt.min_filter = null; | ||||
| 						moImgt.mag_filter = null; | ||||
| 						moImgt.mipmap_filter = null; | ||||
| 						moImgt.generate_mipmaps = null; | ||||
| 					case 1: //Closest | ||||
| 						moImgt.min_filter = 'point'; | ||||
| 						moImgt.mag_filter = 'point'; | ||||
| 						moImgt.mipmap_filter = null; | ||||
| 						moImgt.generate_mipmaps = null; | ||||
| 					case 2: //Cubic | ||||
| 						moImgt.min_filter = null; | ||||
| 						moImgt.mag_filter = null; | ||||
| 						moImgt.mipmap_filter = 'linear'; | ||||
| 						moImgt.generate_mipmaps = true; | ||||
| 					case 3: //Smart | ||||
| 						moImgt.min_filter = 'anisotropic'; | ||||
| 						moImgt.mag_filter = null; | ||||
| 						moImgt.mipmap_filter = 'linear'; | ||||
| 						moImgt.generate_mipmaps = true; | ||||
| 				} | ||||
|  | ||||
| 				break; | ||||
| 			} | ||||
|  | ||||
| 		runOutput(0); | ||||
| 	} | ||||
| } | ||||
| @ -0,0 +1,74 @@ | ||||
| package leenkx.logicnode; | ||||
|  | ||||
| import iron.object.Object; | ||||
| import iron.math.Vec4; | ||||
| import iron.math.Mat4; | ||||
| import iron.system.Time; | ||||
|  | ||||
| class SetObjectDelayedLocationNode extends LogicNode { | ||||
| 	public var use_local_space: Bool = false; | ||||
|  | ||||
| 	private var initialOffset: Vec4 = null; | ||||
| 	private var targetPos: Vec4 = new Vec4(); | ||||
| 	private var currentPos: Vec4 = new Vec4(); | ||||
| 	private var deltaVec: Vec4 = new Vec4(); | ||||
| 	private var tempVec: Vec4 = new Vec4(); | ||||
| 	 | ||||
| 	private var lastParent: Object = null; | ||||
| 	private var invParentMatrix: Mat4 = null; | ||||
| 	 | ||||
| 	public function new(tree: LogicTree) { | ||||
| 		super(tree); | ||||
| 	} | ||||
|  | ||||
| 	override function run(from: Int) { | ||||
| 		var follower: Object = inputs[1].get(); | ||||
| 		var target: Object = inputs[2].get(); | ||||
| 		var delay: Float = inputs[3].get(); | ||||
| 		 | ||||
| 		if (follower == null || target == null) return runOutput(0); | ||||
| 		 | ||||
| 		if (initialOffset == null) { | ||||
| 			initialOffset = new Vec4(); | ||||
| 			var followerPos = follower.transform.world.getLoc(); | ||||
| 			var targetPos = target.transform.world.getLoc(); | ||||
| 			initialOffset.setFrom(followerPos); | ||||
| 			initialOffset.sub(targetPos); | ||||
| 		} | ||||
| 		 | ||||
| 		targetPos.setFrom(target.transform.world.getLoc()); | ||||
| 		currentPos.setFrom(follower.transform.world.getLoc()); | ||||
| 		 | ||||
| 		tempVec.setFrom(targetPos).add(initialOffset); | ||||
| 		 | ||||
| 		deltaVec.setFrom(tempVec).sub(currentPos); | ||||
|  | ||||
| 		if (deltaVec.length() < 0.001 && delay < 0.01) { | ||||
| 			runOutput(0); | ||||
| 			return; | ||||
| 		} | ||||
| 		 | ||||
| 		if (delay == 0.0) { | ||||
| 			currentPos.setFrom(tempVec); | ||||
| 		} else { | ||||
| 			var smoothFactor = Math.exp(-Time.delta / Math.max(0.0001, delay)); | ||||
| 			currentPos.x = tempVec.x + (currentPos.x - tempVec.x) * smoothFactor; | ||||
| 			currentPos.y = tempVec.y + (currentPos.y - tempVec.y) * smoothFactor; | ||||
| 			currentPos.z = tempVec.z + (currentPos.z - tempVec.z) * smoothFactor; | ||||
| 		} | ||||
| 		if (use_local_space && follower.parent != null) { | ||||
| 			if (follower.parent != lastParent || invParentMatrix == null) { | ||||
| 				lastParent = follower.parent; | ||||
| 				invParentMatrix = Mat4.identity(); | ||||
| 				invParentMatrix.getInverse(follower.parent.transform.world); | ||||
| 			} | ||||
| 			tempVec.setFrom(currentPos); | ||||
| 			tempVec.applymat(invParentMatrix);  | ||||
| 			follower.transform.loc.set(tempVec.x, tempVec.y, tempVec.z); | ||||
| 		} else { | ||||
| 			follower.transform.loc.set(currentPos.x, currentPos.y, currentPos.z); | ||||
| 		} | ||||
| 		follower.transform.buildMatrix(); | ||||
| 		runOutput(0); | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										81
									
								
								leenkx/Sources/leenkx/logicnode/SetParticleDataNode.hx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										81
									
								
								leenkx/Sources/leenkx/logicnode/SetParticleDataNode.hx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,81 @@ | ||||
| package leenkx.logicnode; | ||||
|  | ||||
| import iron.object.Object; | ||||
|  | ||||
| class SetParticleDataNode extends LogicNode { | ||||
|  | ||||
| 	public var property0: String; | ||||
|  | ||||
| 	public function new(tree: LogicTree) { | ||||
| 		super(tree); | ||||
| 	} | ||||
|  | ||||
| 	override function run(from: Int) { | ||||
| 		#if lnx_particles | ||||
| 		var object: Object = inputs[1].get(); | ||||
| 		var slot: Int = inputs[2].get(); | ||||
|  | ||||
| 		if (object == null) return; | ||||
|  | ||||
| 		var mo = cast(object, iron.object.MeshObject); | ||||
|  | ||||
| 		var psys = mo.particleSystems != null ? mo.particleSystems[slot] :  | ||||
| 			mo.particleOwner != null && mo.particleOwner.particleSystems != null ? mo.particleOwner.particleSystems[slot] : null;		if (psys == null) return; | ||||
|  | ||||
| 		switch (property0) { | ||||
| 			case 'Particle Size': | ||||
| 				@:privateAccess psys.r.particle_size = inputs[3].get(); | ||||
| 			case 'Frame Start': | ||||
| 				@:privateAccess psys.r.frame_start = inputs[3].get(); | ||||
| 				@:privateAccess psys.animtime = (@:privateAccess psys.r.frame_end - @:privateAccess psys.r.frame_start) / @:privateAccess psys.frameRate; | ||||
| 				@:privateAccess psys.spawnRate = ((@:privateAccess psys.r.frame_end - @:privateAccess psys.r.frame_start) / @:privateAccess psys.count) / @:privateAccess psys.frameRate; | ||||
| 			case 'Frame End': | ||||
| 				@:privateAccess psys.r.frame_end = inputs[3].get(); | ||||
| 				@:privateAccess psys.animtime = (@:privateAccess psys.r.frame_end - @:privateAccess psys.r.frame_start) / @:privateAccess psys.frameRate; | ||||
| 				@:privateAccess psys.spawnRate = ((@:privateAccess psys.r.frame_end - @:privateAccess psys.r.frame_start) / @:privateAccess psys.count) / @:privateAccess psys.frameRate; | ||||
| 			case 'Lifetime': | ||||
| 				@:privateAccess psys.lifetime = inputs[3].get() / @:privateAccess psys.frameRate; | ||||
| 			case 'Lifetime Random': | ||||
| 				@:privateAccess psys.r.lifetime_random = inputs[3].get(); | ||||
| 			case 'Emit From': | ||||
| 				var emit_from: Int = inputs[3].get(); | ||||
| 				if (emit_from == 0 || emit_from == 1 || emit_from == 2) { | ||||
| 					@:privateAccess psys.r.emit_from = emit_from; | ||||
| 					@:privateAccess psys.setupGeomGpu(mo.particleChildren != null ? mo.particleChildren[slot] : cast(iron.Scene.active.getChild(@:privateAccess psys.data.raw.instance_object), iron.object.MeshObject), mo); | ||||
| 				} | ||||
| 			case 'Auto Start': | ||||
| 				@:privateAccess psys.r.auto_start = inputs[3].get();  | ||||
| 			case 'Is Unique': | ||||
| 				@:privateAccess psys.r.is_unique = inputs[3].get(); | ||||
| 			case 'Loop': | ||||
| 				@:privateAccess psys.r.loop = inputs[3].get(); | ||||
| 			case 'Velocity': | ||||
| 				var vel: iron.math.Vec3 = inputs[3].get(); | ||||
| 				@:privateAccess psys.alignx = vel.x; | ||||
| 				@:privateAccess psys.aligny = vel.y; | ||||
| 				@:privateAccess psys.alignz = vel.z; | ||||
| 			case 'Velocity Random': | ||||
| 				@:privateAccess psys.r.factor_random = inputs[3].get(); | ||||
| 			case 'Weight Gravity': | ||||
| 				@:privateAccess psys.r.weight_gravity = inputs[3].get(); | ||||
| 				if (iron.Scene.active.raw.gravity != null) { | ||||
| 					@:privateAccess psys.gx = iron.Scene.active.raw.gravity[0] * @:privateAccess psys.r.weight_gravity; | ||||
| 					@:privateAccess psys.gy = iron.Scene.active.raw.gravity[1] * @:privateAccess psys.r.weight_gravity; | ||||
| 					@:privateAccess psys.gz = iron.Scene.active.raw.gravity[2] * @:privateAccess psys.r.weight_gravity; | ||||
| 				} | ||||
| 				else { | ||||
| 					@:privateAccess psys.gx = 0; | ||||
| 					@:privateAccess psys.gy = 0; | ||||
| 					@:privateAccess psys.gz = -9.81 * @:privateAccess psys.r.weight_gravity; | ||||
| 				} | ||||
| 			case 'Speed': | ||||
| 				psys.speed = inputs[3].get(); | ||||
| 			default:  | ||||
| 				null; | ||||
| 		} | ||||
|  | ||||
| 		#end | ||||
|  | ||||
| 		runOutput(0); | ||||
| 	} | ||||
| } | ||||
| @ -0,0 +1,23 @@ | ||||
| package leenkx.logicnode; | ||||
|  | ||||
| import iron.object.Object; | ||||
|  | ||||
| class SetParticleRenderEmitterNode extends LogicNode { | ||||
|  | ||||
| 	public function new(tree: LogicTree) { | ||||
| 		super(tree); | ||||
| 	} | ||||
|  | ||||
| 	override function run(from: Int) { | ||||
| 		#if lnx_particles | ||||
| 		var object: Object = inputs[1].get(); | ||||
|  | ||||
| 		if (object == null) return; | ||||
|  | ||||
| 		cast(object, iron.object.MeshObject).render_emitter = inputs[2].get(); | ||||
|  | ||||
| 		#end | ||||
|  | ||||
| 		runOutput(0); | ||||
| 	} | ||||
| } | ||||
| @ -11,13 +11,16 @@ class SetParticleSpeedNode extends LogicNode { | ||||
| 	override function run(from: Int) { | ||||
| 		#if lnx_particles | ||||
| 		var object: Object = inputs[1].get(); | ||||
| 		var speed: Float = inputs[2].get(); | ||||
| 		var slot: Int = inputs[2].get(); | ||||
| 		var speed: Float = inputs[3].get(); | ||||
|  | ||||
| 		if (object == null) return; | ||||
|  | ||||
| 		var mo = cast(object, iron.object.MeshObject); | ||||
| 		var psys = mo.particleSystems.length > 0 ? mo.particleSystems[0] : null; | ||||
| 		if (psys == null) mo.particleOwner.particleSystems[0]; | ||||
| 		var psys = mo.particleSystems != null ? mo.particleSystems[slot] :  | ||||
| 			mo.particleOwner != null && mo.particleOwner.particleSystems != null ? mo.particleOwner.particleSystems[slot] : null; | ||||
|  | ||||
| 		if (psys == null) return; | ||||
|  | ||||
| 		psys.speed = speed; | ||||
|  | ||||
|  | ||||
							
								
								
									
										39
									
								
								leenkx/Sources/leenkx/logicnode/SetPositionSpeakerNode.hx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								leenkx/Sources/leenkx/logicnode/SetPositionSpeakerNode.hx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,39 @@ | ||||
| package leenkx.logicnode; | ||||
|  | ||||
| #if lnx_audio | ||||
| import iron.object.SpeakerObject; | ||||
| import kha.audio1.AudioChannel; | ||||
| import iron.system.Audio; | ||||
| #end | ||||
|  | ||||
| class SetPositionSpeakerNode extends LogicNode { | ||||
|  | ||||
| 	public function new(tree: LogicTree) { | ||||
| 		super(tree); | ||||
| 	} | ||||
|  | ||||
| 	override function run(from: Int) { | ||||
| 		#if lnx_audio | ||||
| 		var object: SpeakerObject = cast(inputs[1].get(), SpeakerObject); | ||||
| 		if (object == null || object.sound == null) return; | ||||
| 		 | ||||
| 		var positionInSeconds:Float = inputs[2].get(); | ||||
| 		if (positionInSeconds < 0) positionInSeconds = 0;  | ||||
|  | ||||
| 		var volume = object.data.volume; | ||||
| 		var loop = object.data.loop; | ||||
| 		var stream = object.data.stream; | ||||
| 		 | ||||
| 		object.stop(); | ||||
|  | ||||
| 		var channel = Audio.play(object.sound, loop, stream); | ||||
| 		if (channel != null) { | ||||
| 			object.channels.push(channel); | ||||
| 			channel.volume = volume; | ||||
| 			@:privateAccess channel.set_position(positionInSeconds); | ||||
| 		} | ||||
| 		 | ||||
| 		#end | ||||
| 		runOutput(0); | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										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); | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										17
									
								
								leenkx/Sources/leenkx/logicnode/SharpenGetNode.hx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								leenkx/Sources/leenkx/logicnode/SharpenGetNode.hx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,17 @@ | ||||
| package leenkx.logicnode; | ||||
|  | ||||
| class SharpenGetNode extends LogicNode { | ||||
|  | ||||
| 	public function new(tree:LogicTree) { | ||||
| 		super(tree); | ||||
| 	} | ||||
|  | ||||
| 	override function get(from:Int):Dynamic { | ||||
| 		return switch (from) { | ||||
| 			case 0: leenkx.renderpath.Postprocess.sharpen_uniforms[0]; | ||||
| 			case 1: leenkx.renderpath.Postprocess.sharpen_uniforms[1][0]; | ||||
| 			case 2: leenkx.renderpath.Postprocess.camera_uniforms[12]; | ||||
| 			default: 0.0; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										18
									
								
								leenkx/Sources/leenkx/logicnode/SharpenSetNode.hx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								leenkx/Sources/leenkx/logicnode/SharpenSetNode.hx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,18 @@ | ||||
| package leenkx.logicnode; | ||||
|  | ||||
| class SharpenSetNode extends LogicNode { | ||||
|  | ||||
| 	public function new(tree:LogicTree) { | ||||
| 		super(tree); | ||||
| 	} | ||||
|  | ||||
| 	override function run(from:Int) { | ||||
|         leenkx.renderpath.Postprocess.sharpen_uniforms[0][0] = inputs[1].get().x; | ||||
|         leenkx.renderpath.Postprocess.sharpen_uniforms[0][1] = inputs[1].get().y; | ||||
|         leenkx.renderpath.Postprocess.sharpen_uniforms[0][2] = inputs[1].get().z; | ||||
|         leenkx.renderpath.Postprocess.sharpen_uniforms[1][0] = inputs[2].get(); | ||||
|         leenkx.renderpath.Postprocess.camera_uniforms[12] = inputs[3].get(); | ||||
|  | ||||
| 		runOutput(0); | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										17
									
								
								leenkx/Sources/leenkx/logicnode/VolumetricFogGetNode.hx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								leenkx/Sources/leenkx/logicnode/VolumetricFogGetNode.hx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,17 @@ | ||||
| package leenkx.logicnode; | ||||
|  | ||||
| class VolumetricFogGetNode extends LogicNode { | ||||
|  | ||||
| 	public function new(tree:LogicTree) { | ||||
| 		super(tree); | ||||
| 	} | ||||
|  | ||||
| 	override function get(from:Int):Dynamic { | ||||
| 		return switch (from) { | ||||
| 			case 0: leenkx.renderpath.Postprocess.volumetric_fog_uniforms[0]; | ||||
| 			case 1: leenkx.renderpath.Postprocess.volumetric_fog_uniforms[1][0]; | ||||
| 			case 2: leenkx.renderpath.Postprocess.volumetric_fog_uniforms[2][0]; | ||||
| 			default: 0.0; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										18
									
								
								leenkx/Sources/leenkx/logicnode/VolumetricFogSetNode.hx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								leenkx/Sources/leenkx/logicnode/VolumetricFogSetNode.hx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,18 @@ | ||||
| package leenkx.logicnode; | ||||
|  | ||||
| class VolumetricFogSetNode extends LogicNode { | ||||
|  | ||||
| 	public function new(tree:LogicTree) { | ||||
| 		super(tree); | ||||
| 	} | ||||
|  | ||||
| 	override function run(from:Int) { | ||||
|         leenkx.renderpath.Postprocess.volumetric_fog_uniforms[0][0] = inputs[1].get().x; | ||||
|         leenkx.renderpath.Postprocess.volumetric_fog_uniforms[0][1] = inputs[1].get().y; | ||||
|         leenkx.renderpath.Postprocess.volumetric_fog_uniforms[0][2] = inputs[1].get().z; | ||||
|         leenkx.renderpath.Postprocess.volumetric_fog_uniforms[1][0] = inputs[2].get(); | ||||
|         leenkx.renderpath.Postprocess.volumetric_fog_uniforms[2][0] = inputs[3].get(); | ||||
|  | ||||
| 		runOutput(0); | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										17
									
								
								leenkx/Sources/leenkx/logicnode/VolumetricLightGetNode.hx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								leenkx/Sources/leenkx/logicnode/VolumetricLightGetNode.hx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,17 @@ | ||||
| package leenkx.logicnode; | ||||
|  | ||||
| class VolumetricLightGetNode extends LogicNode { | ||||
|  | ||||
| 	public function new(tree:LogicTree) { | ||||
| 		super(tree); | ||||
| 	} | ||||
|  | ||||
| 	override function get(from:Int):Dynamic { | ||||
| 		return switch (from) { | ||||
| 			case 0: leenkx.renderpath.Postprocess.volumetric_light_uniforms[0]; | ||||
| 			case 1: leenkx.renderpath.Postprocess.volumetric_light_uniforms[1][0]; | ||||
| 			case 2: leenkx.renderpath.Postprocess.volumetric_light_uniforms[2][0]; | ||||
| 			default: 0.0; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										18
									
								
								leenkx/Sources/leenkx/logicnode/VolumetricLightSetNode.hx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								leenkx/Sources/leenkx/logicnode/VolumetricLightSetNode.hx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,18 @@ | ||||
| package leenkx.logicnode; | ||||
|  | ||||
| class VolumetricLightSetNode extends LogicNode { | ||||
|  | ||||
| 	public function new(tree:LogicTree) { | ||||
| 		super(tree); | ||||
| 	} | ||||
|  | ||||
| 	override function run(from:Int) { | ||||
|         leenkx.renderpath.Postprocess.volumetric_light_uniforms[0][0] = inputs[1].get().x; | ||||
|         leenkx.renderpath.Postprocess.volumetric_light_uniforms[0][1] = inputs[1].get().y; | ||||
|         leenkx.renderpath.Postprocess.volumetric_light_uniforms[0][2] = inputs[1].get().z; | ||||
|         leenkx.renderpath.Postprocess.volumetric_light_uniforms[1][0] = inputs[2].get(); | ||||
|         leenkx.renderpath.Postprocess.volumetric_light_uniforms[2][0] = inputs[3].get(); | ||||
|  | ||||
| 		runOutput(0); | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										105
									
								
								leenkx/Sources/leenkx/logicnode/WriteImageNode.hx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										105
									
								
								leenkx/Sources/leenkx/logicnode/WriteImageNode.hx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,105 @@ | ||||
| package leenkx.logicnode; | ||||
|  | ||||
| import iron.object.CameraObject; | ||||
|  | ||||
| class WriteImageNode extends LogicNode { | ||||
|  | ||||
|     var file: String; | ||||
|     var camera: CameraObject; | ||||
|     var renderTarget: kha.Image; | ||||
|  | ||||
|     public function new(tree: LogicTree) { | ||||
|         super(tree); | ||||
|     } | ||||
|  | ||||
|     override function run(from: Int) { | ||||
|         // Relative or absolute path to file | ||||
|         file = inputs[1].get(); | ||||
|  | ||||
|         assert(Error, iron.App.w() % inputs[3].get() == 0 && iron.App.h() % inputs[4].get() == 0, "Aspect ratio must match display resolution ratio"); | ||||
|  | ||||
|         camera = inputs[2].get(); | ||||
|         renderTarget = kha.Image.createRenderTarget(inputs[3].get(), inputs[4].get(), | ||||
|             kha.graphics4.TextureFormat.RGBA32, | ||||
|             kha.graphics4.DepthStencilFormat.NoDepthAndStencil); | ||||
|  | ||||
|         tree.notifyOnRender(render); | ||||
|          | ||||
|     } | ||||
|  | ||||
|     function render(g: kha.graphics4.Graphics) { | ||||
|  | ||||
|         var ready = false; | ||||
|         final sceneCam = iron.Scene.active.camera; | ||||
|         final oldRT = camera.renderTarget; | ||||
|  | ||||
|         iron.Scene.active.camera = camera; | ||||
|         camera.renderTarget = renderTarget; | ||||
|  | ||||
|         camera.renderFrame(g); | ||||
|  | ||||
|         var tex = camera.renderTarget; | ||||
|  | ||||
|         camera.renderTarget = oldRT; | ||||
|         iron.Scene.active.camera = sceneCam; | ||||
|  | ||||
|         var pixels = tex.getPixels(); | ||||
|  | ||||
|         for (i in 0...pixels.length){ | ||||
|             if (pixels.get(i) != 0){ ready = true; break; } | ||||
|         } | ||||
|  | ||||
|         //wait for getPixels ready | ||||
|         if (ready) {  | ||||
|  | ||||
|             var tx = inputs[5].get(); | ||||
|             var ty = inputs[6].get(); | ||||
|             var tw = inputs[7].get(); | ||||
|             var th = inputs[8].get(); | ||||
|  | ||||
|             var bo = new haxe.io.BytesOutput(); | ||||
|             var rgb = haxe.io.Bytes.alloc(tw * th * 4); | ||||
|             for (j in ty...ty + th) { | ||||
|                 for (i in tx...tx + tw) { | ||||
|                     var k = j * tex.width + i; | ||||
|                     var m =  (j - ty) * tw + i - tx; | ||||
|                      | ||||
|                     #if kha_krom | ||||
|                     var l = k; | ||||
|                     #elseif kha_html5 | ||||
|                     var l = (tex.height - j) * tex.width + i; | ||||
|                     #end | ||||
|  | ||||
|                     //ARGB 0xff | ||||
|                     rgb.set(m * 4 + 0, pixels.get(l * 4 + 3)); | ||||
|                     rgb.set(m * 4 + 1, pixels.get(l * 4 + 0));  | ||||
|                     rgb.set(m * 4 + 2, pixels.get(l * 4 + 1)); | ||||
|                     rgb.set(m * 4 + 3, pixels.get(l * 4 + 2)); | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             var imgwriter = new iron.format.bmp.Writer(bo); | ||||
|             imgwriter.write(iron.format.bmp.Tools.buildFromARGB(tw, th, rgb)); | ||||
|  | ||||
|             #if kha_krom | ||||
|             Krom.fileSaveBytes(Krom.getFilesLocation() +  "/" + file, bo.getBytes().getData()); | ||||
|      | ||||
|             #elseif kha_html5 | ||||
|             var blob = new js.html.Blob([bo.getBytes().getData()], {type: "application"}); | ||||
|             var url = js.html.URL.createObjectURL(blob); | ||||
|             var a = cast(js.Browser.document.createElement("a"), js.html.AnchorElement); | ||||
|             a.href = url; | ||||
|             a.download = file; | ||||
|             a.click(); | ||||
|             js.html.URL.revokeObjectURL(url); | ||||
|             #end | ||||
|  | ||||
|             runOutput(0); | ||||
|  | ||||
|             tree.removeRender(render); | ||||
|  | ||||
|         } | ||||
|  | ||||
|     } | ||||
|  | ||||
| } | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user