forked from LeenkxTeam/LNXSDK
		
	Compare commits
	
		
			38 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 316441b954 | |||
| 9bf83bc49f | |||
| d88e1f0f42 | |||
| 96f4e29778 | |||
| 1d705d2ca2 | |||
| 0979cd976f | |||
| db6d786ee4 | |||
| 106e36e30d | |||
| 2bb296028f | |||
| 25d7ba3e72 | |||
| bf7b4416ec | |||
| a2d03cfe6e | |||
| 95f0ecfc54 | |||
| 07f59224fc | |||
| 02259985be | |||
| 6b8585c81a | |||
| 5d78eabf94 | |||
| 41c1459c4e | |||
| 304a497565 | |||
| 9fa399371a | |||
| 4625fdb6b2 | |||
| 79553927aa | |||
| 86661c1012 | |||
| 03967c7a2b | |||
| 61fd48a12f | |||
| 519039b8b6 | |||
| 5244b1b3e8 | |||
| 7ae3bbe496 | |||
| 001be2f8da | |||
| 6a25b3c5d7 | |||
| 8d4ac7251a | |||
| ae63b252c6 | |||
| ee73823206 | |||
| af2850e20c | |||
| bc4a31d415 | |||
| 5303ad3ac6 | |||
| 5153cff790 | |||
| abe17870ce | 
							
								
								
									
										2
									
								
								.gitattributes
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.gitattributes
									
									
									
									
										vendored
									
									
								
							| @ -1,2 +0,0 @@ | |||||||
| *.hdr binary |  | ||||||
| blender/lnx/props.py ident |  | ||||||
							
								
								
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @ -1,3 +0,0 @@ | |||||||
| __pycache__/ |  | ||||||
| *.pyc |  | ||||||
| *.DS_Store |  | ||||||
| @ -2,12 +2,10 @@ | |||||||
| -cp ../Kha/Backends/Krom | -cp ../Kha/Backends/Krom | ||||||
| -cp ../leenkx/Sources | -cp ../leenkx/Sources | ||||||
| -cp ../iron/Sources | -cp ../iron/Sources | ||||||
| -cp ../lib/aura/Sources |  | ||||||
| -cp ../lib/haxebullet/Sources | -cp ../lib/haxebullet/Sources | ||||||
| -cp ../lib/haxerecast/Sources | -cp ../lib/haxerecast/Sources | ||||||
| -cp ../lib/zui/Sources | -cp ../lib/zui/Sources | ||||||
| --macro include('iron', true, null, ['../iron/Sources']) | --macro include('iron', true, null, ['../iron/Sources']) | ||||||
| --macro include('aura', true, null, ['../lib/aura/Sources']) |  | ||||||
| --macro include('haxebullet', true, null, ['../lib/haxebullet/Sources']) | --macro include('haxebullet', true, null, ['../lib/haxebullet/Sources']) | ||||||
| --macro include('haxerecast', true, null, ['../lib/haxerecast/Sources']) | --macro include('haxerecast', true, null, ['../lib/haxerecast/Sources']) | ||||||
| --macro include('leenkx', true, ['leenkx.network'], ['../leenkx/Sources','../iron/Sources']) | --macro include('leenkx', true, ['leenkx.network'], ['../leenkx/Sources','../iron/Sources']) | ||||||
|  | |||||||
							
								
								
									
										26
									
								
								leenkx.py
									
									
									
									
									
								
							
							
						
						
									
										26
									
								
								leenkx.py
									
									
									
									
									
								
							| @ -24,7 +24,7 @@ import textwrap | |||||||
| import threading | import threading | ||||||
| import traceback | import traceback | ||||||
| import typing | import typing | ||||||
| from typing import Callable, Optional, List | from typing import Callable, Optional | ||||||
| import webbrowser | import webbrowser | ||||||
|  |  | ||||||
| import bpy | import bpy | ||||||
| @ -33,12 +33,6 @@ from bpy.props import * | |||||||
| from bpy.types import Operator, AddonPreferences | from bpy.types import Operator, AddonPreferences | ||||||
|  |  | ||||||
|  |  | ||||||
| if bpy.app.version < (2, 90, 0):   |  | ||||||
|     ListType = List |  | ||||||
| else: |  | ||||||
|     ListType = list |  | ||||||
|  |  | ||||||
|  |  | ||||||
| class SDKSource(IntEnum): | class SDKSource(IntEnum): | ||||||
|     PREFS = 0 |     PREFS = 0 | ||||||
|     LOCAL = 1 |     LOCAL = 1 | ||||||
| @ -79,10 +73,9 @@ def detect_sdk_path(): | |||||||
|     area = win.screen.areas[0] |     area = win.screen.areas[0] | ||||||
|     area_type = area.type |     area_type = area.type | ||||||
|     area.type = "INFO" |     area.type = "INFO" | ||||||
|     if bpy.app.version >= (2, 92, 0): |     with bpy.context.temp_override(window=win, screen=win.screen, area=area): | ||||||
|         with bpy.context.temp_override(window=win, screen=win.screen, area=area): |         bpy.ops.info.select_all(action='SELECT') | ||||||
|             bpy.ops.info.select_all(action='SELECT') |         bpy.ops.info.report_copy() | ||||||
|             bpy.ops.info.report_copy() |  | ||||||
|     area.type = area_type |     area.type = area_type | ||||||
|     clipboard = bpy.context.window_manager.clipboard |     clipboard = bpy.context.window_manager.clipboard | ||||||
|  |  | ||||||
| @ -92,7 +85,6 @@ def detect_sdk_path(): | |||||||
|     if match: |     if match: | ||||||
|         addon_prefs.sdk_path = os.path.dirname(match[-1]) |         addon_prefs.sdk_path = os.path.dirname(match[-1]) | ||||||
|  |  | ||||||
|  |  | ||||||
| def get_link_web_server(self): | def get_link_web_server(self): | ||||||
|     return self.get('link_web_server', 'http://localhost/') |     return self.get('link_web_server', 'http://localhost/') | ||||||
|  |  | ||||||
| @ -566,7 +558,7 @@ def remove_readonly(func, path, excinfo): | |||||||
|     func(path) |     func(path) | ||||||
|  |  | ||||||
|  |  | ||||||
| def run_proc(cmd: ListType[str], done: Optional[Callable[[bool], None]] = None): | def run_proc(cmd: list[str], done: Optional[Callable[[bool], None]] = None): | ||||||
|     def fn(p, done): |     def fn(p, done): | ||||||
|         p.wait() |         p.wait() | ||||||
|         if done is not None: |         if done is not None: | ||||||
| @ -848,13 +840,7 @@ def update_leenkx_py(sdk_path: str, force_relink=False): | |||||||
|                 else: |                 else: | ||||||
|                     raise err |                     raise err | ||||||
|     else: |     else: | ||||||
|         if bpy.app.version < (2, 92, 0): |         lnx_module_file.unlink(missing_ok=True) | ||||||
|             try: |  | ||||||
|                 lnx_module_file.unlink() |  | ||||||
|             except FileNotFoundError: |  | ||||||
|                 pass  |  | ||||||
|         else: |  | ||||||
|             lnx_module_file.unlink(missing_ok=True) |  | ||||||
|         shutil.copy(Path(sdk_path) / 'leenkx.py', lnx_module_file) |         shutil.copy(Path(sdk_path) / 'leenkx.py', lnx_module_file) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | |||||||
| @ -3,10 +3,6 @@ | |||||||
|  |  | ||||||
| #include "compiled.inc" | #include "compiled.inc" | ||||||
|  |  | ||||||
| #ifdef _CPostprocess |  | ||||||
| uniform vec4 PPComp17; |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
| uniform sampler2D tex; | uniform sampler2D tex; | ||||||
| uniform vec2 dir; | uniform vec2 dir; | ||||||
| uniform vec2 screenSize; | uniform vec2 screenSize; | ||||||
| @ -49,12 +45,6 @@ void main() { | |||||||
| 		res += factor * col; | 		res += factor * col; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	#ifdef _CPostprocess |  | ||||||
| 		vec3 AirColor = vec3(PPComp17.x, PPComp17.y, PPComp17.z); |  | ||||||
| 	#else |  | ||||||
| 		vec3 AirColor = volumAirColor; |  | ||||||
| 	#endif |  | ||||||
| 	 |  | ||||||
| 	res /= sumfactor; | 	res /= sumfactor; | ||||||
| 	fragColor = vec4(AirColor * res, 1.0); | 	fragColor = vec4(volumAirColor * res, 1.0); | ||||||
| } | } | ||||||
|  | |||||||
| @ -19,11 +19,6 @@ | |||||||
| 				{ | 				{ | ||||||
| 					"name": "screenSize", | 					"name": "screenSize", | ||||||
| 					"link": "_screenSize" | 					"link": "_screenSize" | ||||||
| 				}, |  | ||||||
| 				{ |  | ||||||
| 					"name": "PPComp17", |  | ||||||
| 					"link": "_PPComp17", |  | ||||||
| 					"ifdef": ["_CPostprocess"] |  | ||||||
| 				} | 				} | ||||||
| 			], | 			], | ||||||
| 			"texture_params": [], | 			"texture_params": [], | ||||||
|  | |||||||
| @ -5,7 +5,7 @@ | |||||||
| uniform sampler2D tex; | uniform sampler2D tex; | ||||||
|  |  | ||||||
| #ifdef _CPostprocess | #ifdef _CPostprocess | ||||||
| uniform vec4 PPComp13; | uniform vec3 PPComp13; | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| in vec2 texCoord; | in vec2 texCoord; | ||||||
| @ -43,17 +43,13 @@ void main() { | |||||||
| 	#ifdef _CPostprocess | 	#ifdef _CPostprocess | ||||||
| 		float max_distort = PPComp13.x; | 		float max_distort = PPComp13.x; | ||||||
| 		int num_iter = int(PPComp13.y); | 		int num_iter = int(PPComp13.y); | ||||||
| 		int CAType = int(PPComp13.z); |  | ||||||
| 		int on = int(PPComp13.w); |  | ||||||
| 	#else | 	#else | ||||||
| 		float max_distort = compoChromaticStrength; | 		float max_distort = compoChromaticStrength; | ||||||
| 		int num_iter = compoChromaticSamples; | 		int num_iter = compoChromaticSamples; | ||||||
| 		int CAType = compoChromaticType; |  | ||||||
| 		int on = 1; |  | ||||||
| 	#endif | 	#endif | ||||||
|  |  | ||||||
| 	// Spectral | 	// Spectral | ||||||
| 	if (CAType == 1) { | 	if (compoChromaticType == 1) { | ||||||
| 		float reci_num_iter_f = 1.0 / float(num_iter); | 		float reci_num_iter_f = 1.0 / float(num_iter); | ||||||
|  |  | ||||||
| 		vec2 resolution = vec2(1,1); | 		vec2 resolution = vec2(1,1); | ||||||
| @ -68,7 +64,7 @@ void main() { | |||||||
| 			sumcol += w * texture(tex, barrelDistortion(uv, 0.6 * max_distort * t)); | 			sumcol += w * texture(tex, barrelDistortion(uv, 0.6 * max_distort * t)); | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		if (on == 1) fragColor = sumcol / sumw; else fragColor = texture(tex, texCoord); | 		fragColor = sumcol / sumw; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	// Simple | 	// Simple | ||||||
| @ -77,7 +73,6 @@ void main() { | |||||||
| 		col.x = texture(tex, texCoord + ((vec2(0.0, 1.0) * max_distort) / vec2(1000.0))).x; | 		col.x = texture(tex, texCoord + ((vec2(0.0, 1.0) * max_distort) / vec2(1000.0))).x; | ||||||
| 		col.y = texture(tex, texCoord + ((vec2(-0.85, -0.5) * max_distort) / vec2(1000.0))).y; | 		col.y = texture(tex, texCoord + ((vec2(-0.85, -0.5) * max_distort) / vec2(1000.0))).y; | ||||||
| 		col.z = texture(tex, texCoord + ((vec2(0.85, -0.5) * max_distort) / vec2(1000.0))).z; | 		col.z = texture(tex, texCoord + ((vec2(0.85, -0.5) * max_distort) / vec2(1000.0))).z; | ||||||
| 		if (on == 1) fragColor = vec4(col.x, col.y, col.z, fragColor.w);  | 		fragColor = vec4(col.x, col.y, col.z, fragColor.w); | ||||||
| 		else fragColor = texture(tex, texCoord); |  | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  | |||||||
| @ -62,11 +62,8 @@ uniform vec3 PPComp5; | |||||||
| uniform vec3 PPComp6; | uniform vec3 PPComp6; | ||||||
| uniform vec3 PPComp7; | uniform vec3 PPComp7; | ||||||
| uniform vec3 PPComp8; | uniform vec3 PPComp8; | ||||||
| uniform vec3 PPComp11; |  | ||||||
| uniform vec3 PPComp14; | uniform vec3 PPComp14; | ||||||
| uniform vec4 PPComp15; | uniform vec4 PPComp15; | ||||||
| uniform vec4 PPComp16; |  | ||||||
| uniform vec4 PPComp18; |  | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| // #ifdef _CPos | // #ifdef _CPos | ||||||
| @ -109,16 +106,6 @@ in vec2 texCoord; | |||||||
| out vec4 fragColor; | out vec4 fragColor; | ||||||
|  |  | ||||||
| #ifdef _CFog | #ifdef _CFog | ||||||
| 	#ifdef _CPostprocess |  | ||||||
| 		vec3 FogColor = vec3(PPComp18.x, PPComp18.y, PPComp18.z); |  | ||||||
| 		float FogAmountA = PPComp18.w; |  | ||||||
| 		float FogAmountB = PPComp11.z; |  | ||||||
| 	#else |  | ||||||
| 		vec3 FogColor = compoFogColor; |  | ||||||
| 		float FogAmountA = compoFogAmountA; |  | ||||||
| 		float FogAmountB = compoFogAmountB; |  | ||||||
| 	#endif |  | ||||||
| 	 |  | ||||||
| // const vec3 compoFogColor = vec3(0.5, 0.6, 0.7); | // const vec3 compoFogColor = vec3(0.5, 0.6, 0.7); | ||||||
| // const float compoFogAmountA = 1.0; // b = 0.01 | // const float compoFogAmountA = 1.0; // b = 0.01 | ||||||
| // const float compoFogAmountB = 1.0; // c = 0.1 | // const float compoFogAmountB = 1.0; // c = 0.1 | ||||||
| @ -131,8 +118,8 @@ out vec4 fragColor; | |||||||
| // } | // } | ||||||
| vec3 applyFog(vec3 rgb, float distance) { | vec3 applyFog(vec3 rgb, float distance) { | ||||||
| 	// float fogAmount = 1.0 - exp(-distance * compoFogAmountA); | 	// float fogAmount = 1.0 - exp(-distance * compoFogAmountA); | ||||||
| 	float fogAmount = 1.0 - exp(-distance * (FogAmountA / 100)); | 	float fogAmount = 1.0 - exp(-distance * (compoFogAmountA / 100)); | ||||||
| 	return mix(rgb, FogColor, fogAmount); | 	return mix(rgb, compoFogColor, fogAmount); | ||||||
| } | } | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| @ -144,7 +131,7 @@ float ConvertEV100ToExposure(float EV100) { | |||||||
|     return 1/0.8 * exp2(-EV100); |     return 1/0.8 * exp2(-EV100); | ||||||
| } | } | ||||||
| float ComputeEV(float avgLuminance) { | float ComputeEV(float avgLuminance) { | ||||||
|     const float sqAperture = PPComp1.x * PPComp1.x; |     const float sqAperture = PPComp1[0].x * PPComp1.x; | ||||||
|     const float shutterTime = 1.0 / PPComp1.y; |     const float shutterTime = 1.0 / PPComp1.y; | ||||||
|     const float ISO = PPComp1.z; |     const float ISO = PPComp1.z; | ||||||
|     const float EC = PPComp2.x; |     const float EC = PPComp2.x; | ||||||
| @ -362,22 +349,16 @@ void main() { | |||||||
|  |  | ||||||
| #ifdef _CSharpen | #ifdef _CSharpen | ||||||
| 	#ifdef _CPostprocess | 	#ifdef _CPostprocess | ||||||
| 		float strengthSharpen = PPComp14.y;	 | 		float strengthSharpen = PPComp14.y; | ||||||
| 		vec3 SharpenColor = vec3(PPComp16.x, PPComp16.y, PPComp16.z);  |  | ||||||
| 		float SharpenSize = PPComp16.w; |  | ||||||
| 	#else | 	#else | ||||||
| 		float strengthSharpen = compoSharpenStrength; | 		float strengthSharpen = compoSharpenStrength; | ||||||
| 		vec3 SharpenColor = compoSharpenColor; |  | ||||||
| 		float SharpenSize = compoSharpenSize; |  | ||||||
| 	#endif | 	#endif | ||||||
| 	vec3 col1 = textureLod(tex, texCo + vec2(-texStep.x, -texStep.y) * SharpenSize, 0.0).rgb; | 	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) * SharpenSize, 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) * SharpenSize, 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) * SharpenSize, 0.0).rgb; | 	vec3 col4 = textureLod(tex, texCo + vec2(texStep.x, texStep.y) * 1.5, 0.0).rgb; | ||||||
| 	vec3 colavg = (col1 + col2 + col3 + col4) * 0.25; | 	vec3 colavg = (col1 + col2 + col3 + col4) * 0.25; | ||||||
| 	 | 	fragColor.rgb += (fragColor.rgb - colavg) * strengthSharpen; | ||||||
| 	float edgeMagnitude = length(fragColor.rgb - colavg);  |  | ||||||
| 	fragColor.rgb = mix(fragColor.rgb, SharpenColor, min(edgeMagnitude * strengthSharpen * 2.0, 1.0)); |  | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| #ifdef _CFog | #ifdef _CFog | ||||||
| @ -426,11 +407,7 @@ void main() { | |||||||
| #endif | #endif | ||||||
|  |  | ||||||
| #ifdef _CExposure | #ifdef _CExposure | ||||||
| 	#ifdef _CPostprocess | 	fragColor.rgb += fragColor.rgb * compoExposureStrength; | ||||||
| 		fragColor.rgb+=fragColor.rgb*PPComp8.x; |  | ||||||
| 	#else |  | ||||||
| 		fragColor.rgb+= fragColor.rgb*compoExposureStrength; |  | ||||||
| 	#endif |  | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| #ifdef _CPostprocess | #ifdef _CPostprocess | ||||||
| @ -438,13 +415,8 @@ void main() { | |||||||
| #endif | #endif | ||||||
|  |  | ||||||
| #ifdef _AutoExposure | #ifdef _AutoExposure | ||||||
| 	#ifdef _CPostprocess |  | ||||||
| 		float AEStrength = PPComp8.y; |  | ||||||
| 	#else |  | ||||||
| 		float AEStrength = autoExposureStrength; |  | ||||||
| 	#endif |  | ||||||
| 	float expo = 2.0 - clamp(length(textureLod(histogram, vec2(0.5, 0.5), 0).rgb), 0.0, 1.0); | 	float expo = 2.0 - clamp(length(textureLod(histogram, vec2(0.5, 0.5), 0).rgb), 0.0, 1.0); | ||||||
| 	fragColor.rgb *= pow(expo, AEStrength * 2.0); | 	fragColor.rgb *= pow(expo, autoExposureStrength * 2.0); | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| // Clamp color to get rid of INF values that don't work for the tone mapping below | // Clamp color to get rid of INF values that don't work for the tone mapping below | ||||||
| @ -508,7 +480,9 @@ fragColor.rgb = min(fragColor.rgb, 65504 * 0.5); | |||||||
| 			fragColor.rgb = pow(fragColor.rgb, vec3(1.0 / 2.2)); // To gamma | 			fragColor.rgb = pow(fragColor.rgb, vec3(1.0 / 2.2)); // To gamma | ||||||
| 		} else if (PPComp4.x == 10){ | 		} else if (PPComp4.x == 10){ | ||||||
| 			fragColor.rgb = tonemapAgXFull(fragColor.rgb); | 			fragColor.rgb = tonemapAgXFull(fragColor.rgb); | ||||||
| 		} //else { fragColor.rgb = vec3(0,1,0); //ERROR} | 		} else { | ||||||
|  | 			fragColor.rgb = vec3(0,1,0); //ERROR | ||||||
|  | 		} | ||||||
| 	#endif | 	#endif | ||||||
|  |  | ||||||
| #else | #else | ||||||
|  | |||||||
| @ -235,16 +235,6 @@ | |||||||
| 					"name": "PPComp15", | 					"name": "PPComp15", | ||||||
| 					"link": "_PPComp15", | 					"link": "_PPComp15", | ||||||
| 					"ifdef": ["_CPostprocess"] | 					"ifdef": ["_CPostprocess"] | ||||||
| 				}, |  | ||||||
| 				{ |  | ||||||
| 					"name": "PPComp16", |  | ||||||
| 					"link": "_PPComp16", |  | ||||||
| 					"ifdef": ["_CPostprocess"] |  | ||||||
| 				}, |  | ||||||
| 				{ |  | ||||||
| 					"name": "PPComp18", |  | ||||||
| 					"link": "_PPComp18", |  | ||||||
| 					"ifdef": ["_CPostprocess"] |  | ||||||
| 				} | 				} | ||||||
| 			], | 			], | ||||||
| 			"texture_params": [], | 			"texture_params": [], | ||||||
|  | |||||||
| @ -20,7 +20,7 @@ uniform sampler2D gbuffer0; | |||||||
| uniform sampler2D gbuffer1; | uniform sampler2D gbuffer1; | ||||||
|  |  | ||||||
| #ifdef _gbuffer2 | #ifdef _gbuffer2 | ||||||
| 	uniform sampler2D gbuffer2; | 	//!uniform sampler2D gbuffer2; | ||||||
| #endif | #endif | ||||||
| #ifdef _EmissionShaded | #ifdef _EmissionShaded | ||||||
| 	uniform sampler2D gbufferEmission; | 	uniform sampler2D gbufferEmission; | ||||||
| @ -286,7 +286,7 @@ void main() { | |||||||
|  |  | ||||||
| #ifdef _VoxelGI | #ifdef _VoxelGI | ||||||
| 	vec4 indirect_diffuse = textureLod(voxels_diffuse, texCoord, 0.0); | 	vec4 indirect_diffuse = textureLod(voxels_diffuse, texCoord, 0.0); | ||||||
| 	fragColor.rgb = (indirect_diffuse.rgb * albedo + envl.rgb * (1.0 - indirect_diffuse.a)) * voxelgiDiff; | 	fragColor.rgb = (indirect_diffuse.rgb + envl.rgb * (1.0 - indirect_diffuse.a)) * albedo * voxelgiDiff; | ||||||
| 	if(roughness < 1.0 && occspec.y > 0.0) | 	if(roughness < 1.0 && occspec.y > 0.0) | ||||||
| 		fragColor.rgb += textureLod(voxels_specular, texCoord, 0.0).rgb * occspec.y * voxelgiRefl; | 		fragColor.rgb += textureLod(voxels_specular, texCoord, 0.0).rgb * occspec.y * voxelgiRefl; | ||||||
| #endif | #endif | ||||||
| @ -380,7 +380,7 @@ void main() { | |||||||
| 	#endif | 	#endif | ||||||
|  |  | ||||||
| 	#ifdef _VoxelShadow | 	#ifdef _VoxelShadow | ||||||
| 	svisibility *= (1.0 - traceShadow(p, n, voxels, voxelsSDF, sunDir, clipmaps, gl_FragCoord.xy).r) * voxelgiShad; | 	svisibility *= (1.0 - traceShadow(p, n, voxels, voxelsSDF, sunDir, clipmaps, gl_FragCoord.xy, g2.rg).r) * voxelgiShad; | ||||||
| 	#endif | 	#endif | ||||||
| 	 | 	 | ||||||
| 	#ifdef _SSRS | 	#ifdef _SSRS | ||||||
|  | |||||||
| @ -2,22 +2,13 @@ | |||||||
|  |  | ||||||
| #include "compiled.inc" | #include "compiled.inc" | ||||||
|  |  | ||||||
| #ifdef _CPostprocess |  | ||||||
| uniform vec3 PPComp8; |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
| uniform sampler2D tex; | uniform sampler2D tex; | ||||||
|  |  | ||||||
| in vec2 texCoord; | in vec2 texCoord; | ||||||
| out vec4 fragColor; | out vec4 fragColor; | ||||||
|  |  | ||||||
| void main() { | void main() { | ||||||
| 	#ifdef _CPostprocess | 	fragColor.a = 0.01 * autoExposureSpeed; | ||||||
| 		fragColor.a = 0.01 * PPComp8.z; |  | ||||||
| 	#else |  | ||||||
| 		fragColor.a = 0.01 * autoExposureSpeed; |  | ||||||
| 	#endif |  | ||||||
|  |  | ||||||
| 	fragColor.rgb = textureLod(tex, vec2(0.5, 0.5), 0.0).rgb + | 	fragColor.rgb = textureLod(tex, vec2(0.5, 0.5), 0.0).rgb + | ||||||
| 					textureLod(tex, vec2(0.2, 0.2), 0.0).rgb + | 					textureLod(tex, vec2(0.2, 0.2), 0.0).rgb + | ||||||
| 					textureLod(tex, vec2(0.8, 0.2), 0.0).rgb + | 					textureLod(tex, vec2(0.8, 0.2), 0.0).rgb + | ||||||
|  | |||||||
| @ -8,13 +8,7 @@ | |||||||
| 			"blend_source": "source_alpha", | 			"blend_source": "source_alpha", | ||||||
| 			"blend_destination": "inverse_source_alpha", | 			"blend_destination": "inverse_source_alpha", | ||||||
| 			"blend_operation": "add", | 			"blend_operation": "add", | ||||||
| 			"links": [ | 			"links": [], | ||||||
| 				{ |  | ||||||
| 					"name": "PPComp8", |  | ||||||
| 					"link": "_PPComp8", |  | ||||||
| 					"ifdef": ["_CPostprocess"] |  | ||||||
| 				} |  | ||||||
| 			], |  | ||||||
| 			"texture_params": [], | 			"texture_params": [], | ||||||
| 			"vertex_shader": "../include/pass.vert.glsl", | 			"vertex_shader": "../include/pass.vert.glsl", | ||||||
| 			"fragment_shader": "histogram_pass.frag.glsl" | 			"fragment_shader": "histogram_pass.frag.glsl" | ||||||
|  | |||||||
| @ -92,7 +92,7 @@ void main() { | |||||||
|  |  | ||||||
| 	vec3 viewNormal = V3 * n; | 	vec3 viewNormal = V3 * n; | ||||||
| 	vec3 viewPos = getPosView(viewRay, d, cameraProj); | 	vec3 viewPos = getPosView(viewRay, d, cameraProj); | ||||||
| 	vec3 reflected = reflect(viewPos, viewNormal); | 	vec3 reflected = reflect(normalize(viewPos), viewNormal); | ||||||
| 	hitCoord = viewPos; | 	hitCoord = viewPos; | ||||||
|  |  | ||||||
| 	#ifdef _CPostprocess | 	#ifdef _CPostprocess | ||||||
|  | |||||||
| @ -57,14 +57,17 @@ vec4 binarySearch(vec3 dir) { | |||||||
| } | } | ||||||
|  |  | ||||||
| vec4 rayCast(vec3 dir) { | vec4 rayCast(vec3 dir) { | ||||||
| 	float ddepth; |     float ddepth; | ||||||
| 	dir *= ss_refractionRayStep; |     dir *= ss_refractionRayStep; | ||||||
| 	for (int i = 0; i < maxSteps; i++) { |     for (int i = 0; i < maxSteps; i++) { | ||||||
| 		hitCoord += dir; |         hitCoord += dir; | ||||||
| 		ddepth = getDeltaDepth(hitCoord); |         ddepth = getDeltaDepth(hitCoord); | ||||||
| 		if (ddepth > 0.0) return binarySearch(dir); |         if (ddepth > 0.0) | ||||||
| 	} |             return binarySearch(dir); | ||||||
| 	return vec4(texCoord, 0.0, 1.0); |     } | ||||||
|  |     // No hit — fallback to projecting the ray to UV space | ||||||
|  |     vec2 fallbackUV = getProjectedCoord(hitCoord); | ||||||
|  |     return vec4(fallbackUV, 0.0, 0.5); // We set .w lower to indicate fallback | ||||||
| } | } | ||||||
|  |  | ||||||
| void main() { | void main() { | ||||||
| @ -74,7 +77,7 @@ void main() { | |||||||
|     float ior = gr.x; |     float ior = gr.x; | ||||||
|     float opac = gr.y; |     float opac = gr.y; | ||||||
|     float d = textureLod(gbufferD, texCoord, 0.0).r * 2.0 - 1.0; |     float d = textureLod(gbufferD, texCoord, 0.0).r * 2.0 - 1.0; | ||||||
|     if (d == 0.0 || d == 1.0 || opac == 1.0 || ior == 1.0) { |     if (d == 0.0 || opac == 1.0 || ior == 1.0) { | ||||||
|         fragColor.rgb = textureLod(tex1, texCoord, 0.0).rgb; |         fragColor.rgb = textureLod(tex1, texCoord, 0.0).rgb; | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  | |||||||
| @ -97,9 +97,9 @@ vec4 traceCone(const sampler3D voxels, const sampler3D voxelsSDF, const vec3 ori | |||||||
|  |  | ||||||
| 	vec3 aniso_direction = -dir; | 	vec3 aniso_direction = -dir; | ||||||
| 	vec3 face_offset = vec3( | 	vec3 face_offset = vec3( | ||||||
| 		aniso_direction.x > 0.0 ? 0.0 : 1.0, | 		aniso_direction.x > 0.0 ? 0 : 1, | ||||||
| 		aniso_direction.y > 0.0 ? 2.0 : 3.0, | 		aniso_direction.y > 0.0 ? 2 : 3, | ||||||
| 		aniso_direction.z > 0.0 ? 4.0 : 5.0 | 		aniso_direction.z > 0.0 ? 4 : 5 | ||||||
| 	) / (6 + DIFFUSE_CONE_COUNT); | 	) / (6 + DIFFUSE_CONE_COUNT); | ||||||
| 	vec3 direction_weight = abs(dir); | 	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) { | 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(-viewDir, normal); | 	vec3 specularDir = reflect(normalize(-viewDir), normal); | ||||||
| 	vec3 P = origin + specularDir * ((BayerMatrix8[int(pixel.x + velocity.x) % 8][int(pixel.y + velocity.y) % 8] - 0.5)) * voxelgiStep; | 	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); | 	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; | 	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) { | 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; |  	const float transmittance = 1.0 - opacity; | ||||||
|  	vec3 refractionDir = refract(-viewDir, normal, 1.0 / ior); |  	vec3 refractionDir = refract(normalize(-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; |  	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); | 	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 aniso_direction = -dir; | ||||||
| 	vec3 face_offset = vec3( | 	vec3 face_offset = vec3( | ||||||
| 		aniso_direction.x > 0.0 ? 0.0 : 1.0, | 		aniso_direction.x > 0.0 ? 0 : 1, | ||||||
| 		aniso_direction.y > 0.0 ? 2.0 : 3.0, | 		aniso_direction.y > 0.0 ? 2 : 3, | ||||||
| 		aniso_direction.z > 0.0 ? 4.0 : 5.0 | 		aniso_direction.z > 0.0 ? 4 : 5 | ||||||
| 	) / (6 + DIFFUSE_CONE_COUNT); | 	) / (6 + DIFFUSE_CONE_COUNT); | ||||||
| 	vec3 direction_weight = abs(dir); | 	vec3 direction_weight = abs(dir); | ||||||
|  |  | ||||||
| @ -272,9 +272,9 @@ float traceConeShadow(const sampler3D voxels, const sampler3D voxelsSDF, const v | |||||||
|  |  | ||||||
| 	vec3 aniso_direction = -dir; | 	vec3 aniso_direction = -dir; | ||||||
| 	vec3 face_offset = vec3( | 	vec3 face_offset = vec3( | ||||||
| 		aniso_direction.x > 0.0 ? 0.0 : 1.0, | 		aniso_direction.x > 0.0 ? 0 : 1, | ||||||
| 		aniso_direction.y > 0.0 ? 2.0 : 3.0, | 		aniso_direction.y > 0.0 ? 2 : 3, | ||||||
| 		aniso_direction.z > 0.0 ? 4.0 : 5.0 | 		aniso_direction.z > 0.0 ? 4 : 5 | ||||||
| 	) / (6 + DIFFUSE_CONE_COUNT); | 	) / (6 + DIFFUSE_CONE_COUNT); | ||||||
| 	vec3 direction_weight = abs(dir); | 	vec3 direction_weight = abs(dir); | ||||||
| 	float coneCoefficient = 2.0 * tan(aperture * 0.5); | 	float coneCoefficient = 2.0 * tan(aperture * 0.5); | ||||||
| @ -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) { | 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) % 8][int(pixel.y) % 8] - 0.5) * voxelgiStep; |  	vec3 P = origin + dir * (BayerMatrix8[int(pixel.x + velocity.x) % 8][int(pixel.y + velocity.y) % 8] - 0.5) * voxelgiStep; | ||||||
| 	float amount = traceConeShadow(voxels, voxelsSDF, P, normal, dir, DIFFUSE_CONE_APERTURE, voxelgiStep, clipmaps); | 	float amount = traceConeShadow(voxels, voxelsSDF, P, normal, dir, DIFFUSE_CONE_APERTURE, voxelgiStep, clipmaps); | ||||||
| 	amount = clamp(amount, 0.0, 1.0); | 	amount = clamp(amount, 0.0, 1.0); | ||||||
| 	return amount * voxelgiOcc; | 	return amount * voxelgiOcc; | ||||||
|  | |||||||
| @ -1,239 +1,242 @@ | |||||||
| #ifndef _LIGHT_GLSL_ | #ifndef _LIGHT_GLSL_ | ||||||
| #define _LIGHT_GLSL_ | #define _LIGHT_GLSL_ | ||||||
|  |  | ||||||
| #include "compiled.inc" | #include "compiled.inc" | ||||||
| #include "std/brdf.glsl" | #include "std/brdf.glsl" | ||||||
| #include "std/math.glsl" | #include "std/math.glsl" | ||||||
| #ifdef _ShadowMap | #ifdef _ShadowMap | ||||||
| #include "std/shadows.glsl" | #include "std/shadows.glsl" | ||||||
| #endif | #endif | ||||||
| #ifdef _VoxelShadow | #ifdef _VoxelShadow | ||||||
| #include "std/conetrace.glsl" | #include "std/conetrace.glsl" | ||||||
| //!uniform sampler2D voxels_shadows; | #endif | ||||||
| #endif | #ifdef _gbuffer2 | ||||||
| #ifdef _LTC | uniform sampler2D gbuffer2; | ||||||
| #include "std/ltc.glsl" | #endif | ||||||
| #endif | #ifdef _LTC | ||||||
| #ifdef _LightIES | #include "std/ltc.glsl" | ||||||
| #include "std/ies.glsl" | #endif | ||||||
| #endif | #ifdef _LightIES | ||||||
| #ifdef _SSRS | #include "std/ies.glsl" | ||||||
| #include "std/ssrs.glsl" | #endif | ||||||
| #endif | #ifdef _SSRS | ||||||
| #ifdef _Spot | #include "std/ssrs.glsl" | ||||||
| #include "std/light_common.glsl" | #endif | ||||||
| #endif | #ifdef _Spot | ||||||
|  | #include "std/light_common.glsl" | ||||||
| #ifdef _ShadowMap | #endif | ||||||
| 	#ifdef _SinglePoint |  | ||||||
| 		#ifdef _Spot | #ifdef _ShadowMap | ||||||
| 			#ifndef _LTC | 	#ifdef _SinglePoint | ||||||
| 				uniform sampler2DShadow shadowMapSpot[1]; | 		#ifdef _Spot | ||||||
| 				uniform sampler2D shadowMapSpotTransparent[1]; | 			#ifndef _LTC | ||||||
| 				uniform mat4 LWVPSpot[1]; | 				uniform sampler2DShadow shadowMapSpot[1]; | ||||||
| 			#endif | 				uniform sampler2D shadowMapSpotTransparent[1]; | ||||||
| 		#else | 				uniform mat4 LWVPSpot[1]; | ||||||
| 			uniform samplerCubeShadow shadowMapPoint[1]; | 			#endif | ||||||
| 			uniform samplerCube shadowMapPointTransparent[1]; | 		#else | ||||||
| 			uniform vec2 lightProj; | 			uniform samplerCubeShadow shadowMapPoint[1]; | ||||||
| 		#endif | 			uniform samplerCube shadowMapPointTransparent[1]; | ||||||
| 	#endif | 			uniform vec2 lightProj; | ||||||
| 	#ifdef _Clusters | 		#endif | ||||||
| 		#ifdef _SingleAtlas | 	#endif | ||||||
| 		//!uniform sampler2DShadow shadowMapAtlas; | 	#ifdef _Clusters | ||||||
| 		//!uniform sampler2D shadowMapAtlasTransparent; | 		#ifdef _SingleAtlas | ||||||
| 		#endif | 		//!uniform sampler2DShadow shadowMapAtlas; | ||||||
| 		uniform vec2 lightProj; | 		//!uniform sampler2D shadowMapAtlasTransparent; | ||||||
| 		#ifdef _ShadowMapAtlas | 		#endif | ||||||
| 		#ifndef _SingleAtlas | 		uniform vec2 lightProj; | ||||||
| 		uniform sampler2DShadow shadowMapAtlasPoint; | 		#ifdef _ShadowMapAtlas | ||||||
| 		uniform sampler2D shadowMapAtlasPointTransparent; | 		#ifndef _SingleAtlas | ||||||
| 		#endif | 		uniform sampler2DShadow shadowMapAtlasPoint; | ||||||
| 		#else | 		uniform sampler2D shadowMapAtlasPointTransparent; | ||||||
| 		uniform samplerCubeShadow shadowMapPoint[4]; | 		#endif | ||||||
| 		uniform samplerCube shadowMapPointTransparent[4]; | 		#else | ||||||
| 		#endif | 		uniform samplerCubeShadow shadowMapPoint[4]; | ||||||
| 		#ifdef _Spot | 		uniform samplerCube shadowMapPointTransparent[4]; | ||||||
| 			#ifdef _ShadowMapAtlas | 		#endif | ||||||
| 			#ifndef _SingleAtlas | 		#ifdef _Spot | ||||||
| 			uniform sampler2DShadow shadowMapAtlasSpot; | 			#ifdef _ShadowMapAtlas | ||||||
| 			uniform sampler2D shadowMapAtlasSpotTransparent; | 			#ifndef _SingleAtlas | ||||||
| 			#endif | 			uniform sampler2DShadow shadowMapAtlasSpot; | ||||||
| 			#else | 			uniform sampler2D shadowMapAtlasSpotTransparent; | ||||||
| 			uniform sampler2DShadow shadowMapSpot[4]; | 			#endif | ||||||
| 			uniform sampler2D shadowMapSpotTransparent[4]; | 			#else | ||||||
| 			#endif | 			uniform sampler2DShadow shadowMapSpot[4]; | ||||||
| 			uniform mat4 LWVPSpotArray[maxLightsCluster]; | 			uniform sampler2D shadowMapSpotTransparent[4]; | ||||||
| 		#endif | 			#endif | ||||||
| 	#endif | 			uniform mat4 LWVPSpotArray[maxLightsCluster]; | ||||||
| #endif | 		#endif | ||||||
|  | 	#endif | ||||||
| #ifdef _LTC | #endif | ||||||
| uniform vec3 lightArea0; |  | ||||||
| uniform vec3 lightArea1; | #ifdef _LTC | ||||||
| uniform vec3 lightArea2; | uniform vec3 lightArea0; | ||||||
| uniform vec3 lightArea3; | uniform vec3 lightArea1; | ||||||
| uniform sampler2D sltcMat; | uniform vec3 lightArea2; | ||||||
| uniform sampler2D sltcMag; | uniform vec3 lightArea3; | ||||||
| #ifdef _ShadowMap | uniform sampler2D sltcMat; | ||||||
| #ifndef _Spot | uniform sampler2D sltcMag; | ||||||
| 	#ifdef _SinglePoint | #ifdef _ShadowMap | ||||||
| 		uniform sampler2DShadow shadowMapSpot[1]; | #ifndef _Spot | ||||||
| 		uniform sampler2D shadowMapSpotTransparent[1]; | 	#ifdef _SinglePoint | ||||||
| 		uniform mat4 LWVPSpot[1]; | 		uniform sampler2DShadow shadowMapSpot[1]; | ||||||
| 	#endif | 		uniform sampler2D shadowMapSpotTransparent[1]; | ||||||
| 	#ifdef _Clusters | 		uniform mat4 LWVPSpot[1]; | ||||||
| 		uniform sampler2DShadow shadowMapSpot[maxLightsCluster]; | 	#endif | ||||||
| 		uniform sampler2D shadowMapSpotTransparent[maxLightsCluster]; | 	#ifdef _Clusters | ||||||
| 		uniform mat4 LWVPSpotArray[maxLightsCluster]; | 		uniform sampler2DShadow shadowMapSpot[maxLightsCluster]; | ||||||
| 	#endif | 		uniform sampler2D shadowMapSpotTransparent[maxLightsCluster]; | ||||||
| 	#endif | 		uniform mat4 LWVPSpotArray[maxLightsCluster]; | ||||||
| #endif | 	#endif | ||||||
| #endif | 	#endif | ||||||
|  | #endif | ||||||
| vec3 sampleLight(const vec3 p, const vec3 n, const vec3 v, const float dotNV, const vec3 lp, const vec3 lightCol, | #endif | ||||||
| 	const vec3 albedo, const float rough, const float spec, const vec3 f0 |  | ||||||
| 	#ifdef _ShadowMap | vec3 sampleLight(const vec3 p, const vec3 n, const vec3 v, const float dotNV, const vec3 lp, const vec3 lightCol, | ||||||
| 		, int index, float bias, bool receiveShadow, bool transparent | 	const vec3 albedo, const float rough, const float spec, const vec3 f0 | ||||||
| 	#endif | 	#ifdef _ShadowMap | ||||||
| 	#ifdef _Spot | 		, int index, float bias, bool receiveShadow, bool transparent | ||||||
| 		, const bool isSpot, const float spotSize, float spotBlend, vec3 spotDir, vec2 scale, vec3 right | 	#endif | ||||||
| 	#endif | 	#ifdef _Spot | ||||||
| 	#ifdef _VoxelShadow | 		, const bool isSpot, const float spotSize, float spotBlend, vec3 spotDir, vec2 scale, vec3 right | ||||||
| 		, sampler3D voxels, sampler3D voxelsSDF, float clipmaps[10 * voxelgiClipmapCount] | 	#endif | ||||||
| 	#endif | 	#ifdef _VoxelShadow | ||||||
| 	#ifdef _MicroShadowing | 		, sampler3D voxels, sampler3D voxelsSDF, float clipmaps[10 * voxelgiClipmapCount] | ||||||
| 		, float occ | 	#endif | ||||||
| 	#endif | 	#ifdef _MicroShadowing | ||||||
| 	#ifdef _SSRS | 		, float occ | ||||||
| 		, sampler2D gbufferD, mat4 invVP, vec3 eye | 	#endif | ||||||
| 	#endif | 	#ifdef _SSRS | ||||||
| 	) { | 		, sampler2D gbufferD, mat4 invVP, vec3 eye | ||||||
| 	vec3 ld = lp - p; | 	#endif | ||||||
| 	vec3 l = normalize(ld); | 	) { | ||||||
| 	vec3 h = normalize(v + l); | 	vec3 ld = lp - p; | ||||||
| 	float dotNH = max(0.0, dot(n, h)); | 	vec3 l = normalize(ld); | ||||||
| 	float dotVH = max(0.0, dot(v, h)); | 	vec3 h = normalize(v + l); | ||||||
| 	float dotNL = max(0.0, dot(n, l)); | 	float dotNH = max(0.0, dot(n, h)); | ||||||
|  | 	float dotVH = max(0.0, dot(v, h)); | ||||||
| 	#ifdef _LTC | 	float dotNL = max(0.0, dot(n, l)); | ||||||
| 	float theta = acos(dotNV); |  | ||||||
| 	vec2 tuv = vec2(rough, theta / (0.5 * PI)); | 	#ifdef _LTC | ||||||
| 	tuv = tuv * LUT_SCALE + LUT_BIAS; | 	float theta = acos(dotNV); | ||||||
| 	vec4 t = textureLod(sltcMat, tuv, 0.0); | 	vec2 tuv = vec2(rough, theta / (0.5 * PI)); | ||||||
| 	mat3 invM = mat3( | 	tuv = tuv * LUT_SCALE + LUT_BIAS; | ||||||
| 		vec3(1.0, 0.0, t.y), | 	vec4 t = textureLod(sltcMat, tuv, 0.0); | ||||||
| 		vec3(0.0, t.z, 0.0), | 	mat3 invM = mat3( | ||||||
| 		vec3(t.w, 0.0, t.x)); | 		vec3(1.0, 0.0, t.y), | ||||||
| 	float ltcspec = ltcEvaluate(n, v, dotNV, p, invM, lightArea0, lightArea1, lightArea2, lightArea3); | 		vec3(0.0, t.z, 0.0), | ||||||
| 	ltcspec *= textureLod(sltcMag, tuv, 0.0).a; | 		vec3(t.w, 0.0, t.x)); | ||||||
| 	float ltcdiff = ltcEvaluate(n, v, dotNV, p, mat3(1.0), lightArea0, lightArea1, lightArea2, lightArea3); | 	float ltcspec = ltcEvaluate(n, v, dotNV, p, invM, lightArea0, lightArea1, lightArea2, lightArea3); | ||||||
| 	vec3 direct = albedo * ltcdiff + ltcspec * spec * 0.05; | 	ltcspec *= textureLod(sltcMag, tuv, 0.0).a; | ||||||
| 	#else | 	float ltcdiff = ltcEvaluate(n, v, dotNV, p, mat3(1.0), lightArea0, lightArea1, lightArea2, lightArea3); | ||||||
| 	vec3 direct = lambertDiffuseBRDF(albedo, dotNL) + | 	vec3 direct = albedo * ltcdiff + ltcspec * spec * 0.05; | ||||||
| 				  specularBRDF(f0, rough, dotNL, dotNH, dotNV, dotVH) * spec; | 	#else | ||||||
| 	#endif | 	vec3 direct = lambertDiffuseBRDF(albedo, dotNL) + | ||||||
|  | 				  specularBRDF(f0, rough, dotNL, dotNH, dotNV, dotVH) * spec; | ||||||
| 	direct *= attenuate(distance(p, lp)); | 	#endif | ||||||
| 	direct *= lightCol; |  | ||||||
|  | 	direct *= attenuate(distance(p, lp)); | ||||||
| 	#ifdef _MicroShadowing | 	direct *= lightCol; | ||||||
| 	direct *= clamp(dotNL + 2.0 * occ * occ - 1.0, 0.0, 1.0); |  | ||||||
| 	#endif | 	#ifdef _MicroShadowing | ||||||
|  | 	direct *= clamp(dotNL + 2.0 * occ * occ - 1.0, 0.0, 1.0); | ||||||
| 	#ifdef _SSRS | 	#endif | ||||||
| 	direct *= traceShadowSS(l, p, gbufferD, invVP, eye); |  | ||||||
| 	#endif | 	#ifdef _SSRS | ||||||
|  | 	direct *= traceShadowSS(l, p, gbufferD, invVP, eye); | ||||||
| 	#ifdef _VoxelShadow | 	#endif | ||||||
| 	direct *= (1.0 - traceShadow(p, n, voxels, voxelsSDF, l, clipmaps, gl_FragCoord.xy).r) * voxelgiShad; |  | ||||||
| 	#endif | 	#ifdef _VoxelShadow | ||||||
|  | 	vec4 g2 = textureLod(gbuffer2, gl_FragCoord.xy, 0.0); | ||||||
| 	#ifdef _LTC | 	direct *= (1.0 - traceShadow(p, n, voxels, voxelsSDF, l, clipmaps, gl_FragCoord.xy, g2.rg).r) * voxelgiShad; | ||||||
| 	#ifdef _ShadowMap | 	#endif | ||||||
| 		if (receiveShadow) { |  | ||||||
| 			#ifdef _SinglePoint | 	#ifdef _LTC | ||||||
| 			vec4 lPos = LWVPSpotArray[0] * vec4(p + n * bias * 10, 1.0); | 	#ifdef _ShadowMap | ||||||
| 			direct *= shadowTest(shadowMapSpot[0], shadowMapSpotTransparent[0], lPos.xyz / lPos.w, bias, transparent); | 		if (receiveShadow) { | ||||||
| 			#endif | 			#ifdef _SinglePoint | ||||||
| 			#ifdef _Clusters | 			vec4 lPos = LWVPSpotArray[0] * vec4(p + n * bias * 10, 1.0); | ||||||
| 			vec4 lPos = LWVPSpotArray[index] * vec4(p + n * bias * 10, 1.0); | 			direct *= shadowTest(shadowMapSpot[0], shadowMapSpotTransparent[0], lPos.xyz / lPos.w, bias, transparent); | ||||||
| 			if (index == 0) direct *= shadowTest(shadowMapSpot[0], shadowMapSpotTransparent[0], lPos.xyz / lPos.w, bias, transparent); | 			#endif | ||||||
| 			else if (index == 1) direct *= shadowTest(shadowMapSpot[1], shadowMapSpotTransparent[1], lPos.xyz / lPos.w, bias, transparent); | 			#ifdef _Clusters | ||||||
| 			else if (index == 2) direct *= shadowTest(shadowMapSpot[2], shadowMapSpotTransparent[2], lPos.xyz / lPos.w, bias, transparent); | 			vec4 lPos = LWVPSpotArray[index] * vec4(p + n * bias * 10, 1.0); | ||||||
| 			else if (index == 3) direct *= shadowTest(shadowMapSpot[3], shadowMapSpotTransparent[3], lPos.xyz / lPos.w, bias, transparent); | 			if (index == 0) direct *= shadowTest(shadowMapSpot[0], shadowMapSpotTransparent[0], lPos.xyz / lPos.w, bias, transparent); | ||||||
| 			#endif | 			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); | ||||||
| 	#endif | 			else if (index == 3) direct *= shadowTest(shadowMapSpot[3], shadowMapSpotTransparent[3], lPos.xyz / lPos.w, bias, transparent); | ||||||
| 	return direct; | 			#endif | ||||||
| 	#endif | 		} | ||||||
|  | 	#endif | ||||||
| 	#ifdef _Spot | 	return direct; | ||||||
| 	if (isSpot) { | 	#endif | ||||||
| 		direct *= spotlightMask(l, spotDir, right, scale, spotSize, spotBlend); |  | ||||||
|  | 	#ifdef _Spot | ||||||
| 		#ifdef _ShadowMap | 	if (isSpot) { | ||||||
| 			if (receiveShadow) { | 		direct *= spotlightMask(l, spotDir, right, scale, spotSize, spotBlend); | ||||||
| 				#ifdef _SinglePoint |  | ||||||
| 				vec4 lPos = LWVPSpot[0] * vec4(p + n * bias * 10, 1.0); | 		#ifdef _ShadowMap | ||||||
| 				direct *= shadowTest(shadowMapSpot[0], shadowMapSpotTransparent[0], lPos.xyz / lPos.w, bias, transparent); | 			if (receiveShadow) { | ||||||
| 				#endif | 				#ifdef _SinglePoint | ||||||
| 				#ifdef _Clusters | 				vec4 lPos = LWVPSpot[0] * vec4(p + n * bias * 10, 1.0); | ||||||
| 					vec4 lPos = LWVPSpotArray[index] * vec4(p + n * bias * 10, 1.0); | 				direct *= shadowTest(shadowMapSpot[0], shadowMapSpotTransparent[0], lPos.xyz / lPos.w, bias, transparent); | ||||||
| 					#ifdef _ShadowMapAtlas | 				#endif | ||||||
| 						direct *= shadowTest( | 				#ifdef _Clusters | ||||||
| 							#ifndef _SingleAtlas | 					vec4 lPos = LWVPSpotArray[index] * vec4(p + n * bias * 10, 1.0); | ||||||
| 							shadowMapAtlasSpot, shadowMapAtlasSpotTransparent | 					#ifdef _ShadowMapAtlas | ||||||
| 							#else | 						direct *= shadowTest( | ||||||
| 							shadowMapAtlas, shadowMapAtlasTransparent | 							#ifndef _SingleAtlas | ||||||
| 							#endif | 							shadowMapAtlasSpot, shadowMapAtlasSpotTransparent | ||||||
| 							, lPos.xyz / lPos.w, bias, transparent | 							#else | ||||||
| 						); | 							shadowMapAtlas, shadowMapAtlasTransparent | ||||||
| 					#else | 							#endif | ||||||
| 							 if (index == 0) direct *= shadowTest(shadowMapSpot[0], shadowMapSpotTransparent[0], lPos.xyz / lPos.w, bias, transparent); | 							, 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 | ||||||
| 						else if (index == 3) direct *= shadowTest(shadowMapSpot[3], shadowMapSpotTransparent[3], lPos.xyz / lPos.w, bias, transparent); | 							 if (index == 0) direct *= shadowTest(shadowMapSpot[0], shadowMapSpotTransparent[0], lPos.xyz / lPos.w, bias, transparent); | ||||||
| 					#endif | 						else if (index == 1) direct *= shadowTest(shadowMapSpot[1], shadowMapSpotTransparent[1], lPos.xyz / lPos.w, bias, transparent); | ||||||
| 				#endif | 						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 | ||||||
| 	} | 			} | ||||||
| 	#endif | 		#endif | ||||||
|  | 		return direct; | ||||||
| 	#ifdef _LightIES | 	} | ||||||
| 	direct *= iesAttenuation(-l); | 	#endif | ||||||
| 	#endif |  | ||||||
|  | 	#ifdef _LightIES | ||||||
| 	#ifdef _ShadowMap | 	direct *= iesAttenuation(-l); | ||||||
| 		if (receiveShadow) { | 	#endif | ||||||
| 			#ifdef _SinglePoint |  | ||||||
| 			#ifndef _Spot | 	#ifdef _ShadowMap | ||||||
| 			direct *= PCFCube(shadowMapPoint[0], shadowMapPointTransparent[0], ld, -l, bias, lightProj, n, transparent); | 		if (receiveShadow) { | ||||||
| 			#endif | 			#ifdef _SinglePoint | ||||||
| 			#endif | 			#ifndef _Spot | ||||||
| 			#ifdef _Clusters | 			direct *= PCFCube(shadowMapPoint[0], shadowMapPointTransparent[0], ld, -l, bias, lightProj, n, transparent); | ||||||
| 				#ifdef _ShadowMapAtlas | 			#endif | ||||||
| 				direct *= PCFFakeCube( | 			#endif | ||||||
| 					#ifndef _SingleAtlas | 			#ifdef _Clusters | ||||||
| 					shadowMapAtlasPoint, shadowMapAtlasPointTransparent | 				#ifdef _ShadowMapAtlas | ||||||
| 					#else | 				direct *= PCFFakeCube( | ||||||
| 					shadowMapAtlas, shadowMapAtlasTransparent | 					#ifndef _SingleAtlas | ||||||
| 					#endif | 					shadowMapAtlasPoint, shadowMapAtlasPointTransparent | ||||||
| 					, ld, -l, bias, lightProj, n, index, transparent | 					#else | ||||||
| 				); | 					shadowMapAtlas, shadowMapAtlasTransparent | ||||||
| 				#else | 					#endif | ||||||
| 					 if (index == 0) direct *= PCFCube(shadowMapPoint[0], shadowMapPointTransparent[0], ld, -l, bias, lightProj, n, transparent); | 					, ld, -l, bias, lightProj, n, index, 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 | ||||||
| 				else if (index == 3) direct *= PCFCube(shadowMapPoint[3], shadowMapPointTransparent[3], ld, -l, bias, lightProj, n, transparent); | 					 if (index == 0) direct *= PCFCube(shadowMapPoint[0], shadowMapPointTransparent[0], ld, -l, bias, lightProj, n, transparent); | ||||||
| 				#endif | 				else if (index == 1) direct *= PCFCube(shadowMapPoint[1], shadowMapPointTransparent[1], ld, -l, bias, lightProj, n, transparent); | ||||||
| 			#endif | 				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 | ||||||
|  |  | ||||||
| #endif | 	return direct; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #endif | ||||||
|  | |||||||
| @ -87,40 +87,6 @@ float lpToDepth(vec3 lp, const vec2 lightProj) { | |||||||
| 	return zcomp * 0.5 + 0.5; | 	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) { | 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... | 	const float s = shadowmapCubePcfSize; // TODO: incorrect... | ||||||
| 	float compare = lpToDepth(lp, lightProj) - bias * 1.5; | 	float compare = lpToDepth(lp, lightProj) - bias * 1.5; | ||||||
| @ -149,7 +115,7 @@ vec3 PCFCube(samplerCubeShadow shadowMapCube, samplerCube shadowMapCubeTranspare | |||||||
| 	return result; | 	return result; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | #ifdef _ShadowMapAtlas | ||||||
| // transform "out-of-bounds" coordinates to the correct face/coordinate system | // transform "out-of-bounds" coordinates to the correct face/coordinate system | ||||||
| // https://www.khronos.org/opengl/wiki/File:CubeMapAxes.png | // https://www.khronos.org/opengl/wiki/File:CubeMapAxes.png | ||||||
| vec2 transformOffsetedUV(const int faceIndex, out int newFaceIndex, vec2 uv) { | vec2 transformOffsetedUV(const int faceIndex, out int newFaceIndex, vec2 uv) { | ||||||
|  | |||||||
| @ -11,8 +11,6 @@ vec3 uncharted2Tonemap(const vec3 x) { | |||||||
| vec3 tonemapUncharted2(const vec3 color) { | vec3 tonemapUncharted2(const vec3 color) { | ||||||
| 	const float W = 11.2; | 	const float W = 11.2; | ||||||
| 	const float exposureBias = 2.0; | 	const float exposureBias = 2.0; | ||||||
|     // TODO - Find out why black world value of 0.0,0.0,0.0 turns to white pixels |  | ||||||
| 	if (dot(color, color) < 0.001) return vec3(0.001); |  | ||||||
| 	vec3 curr = uncharted2Tonemap(exposureBias * color); | 	vec3 curr = uncharted2Tonemap(exposureBias * color); | ||||||
| 	vec3 whiteScale = 1.0 / uncharted2Tonemap(vec3(W)); | 	vec3 whiteScale = 1.0 / uncharted2Tonemap(vec3(W)); | ||||||
| 	return curr * whiteScale; | 	return curr * whiteScale; | ||||||
|  | |||||||
| @ -11,11 +11,6 @@ | |||||||
| #include "std/light_common.glsl" | #include "std/light_common.glsl" | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| #ifdef _CPostprocess |  | ||||||
| uniform vec3 PPComp11; |  | ||||||
| uniform vec4 PPComp17; |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
| uniform sampler2D gbufferD; | uniform sampler2D gbufferD; | ||||||
| uniform sampler2D snoise; | uniform sampler2D snoise; | ||||||
|  |  | ||||||
| @ -92,13 +87,7 @@ out float fragColor; | |||||||
| const float tScat = 0.08; | const float tScat = 0.08; | ||||||
| const float tAbs = 0.0; | const float tAbs = 0.0; | ||||||
| const float tExt = tScat + tAbs; | const float tExt = tScat + tAbs; | ||||||
| #ifdef _CPostprocess | const float stepLen = 1.0 / volumSteps; | ||||||
| 	float stepLen = 1.0 / int(PPComp11.y); |  | ||||||
| 	float AirTurbidity = PPComp17.w; |  | ||||||
| #else |  | ||||||
| 	const float stepLen = 1.0 / volumSteps; |  | ||||||
| 	float AirTurbidity = volumAirTurbidity; |  | ||||||
| #endif |  | ||||||
| const float lighting = 0.4; | const float lighting = 0.4; | ||||||
|  |  | ||||||
| void rayStep(inout vec3 curPos, inout float curOpticalDepth, inout float scatteredLightAmount, float stepLenWorld, vec3 viewVecNorm) { | void rayStep(inout vec3 curPos, inout float curOpticalDepth, inout float scatteredLightAmount, float stepLenWorld, vec3 viewVecNorm) { | ||||||
| @ -173,5 +162,5 @@ void main() { | |||||||
| 		rayStep(curPos, curOpticalDepth, scatteredLightAmount, stepLenWorld, viewVecNorm); | 		rayStep(curPos, curOpticalDepth, scatteredLightAmount, stepLenWorld, viewVecNorm); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	fragColor = scatteredLightAmount * AirTurbidity; | 	fragColor = scatteredLightAmount * volumAirTurbidity; | ||||||
| } | } | ||||||
|  | |||||||
| @ -140,16 +140,6 @@ | |||||||
| 					"link": "_biasLightWorldViewProjectionMatrixSpot3", | 					"link": "_biasLightWorldViewProjectionMatrixSpot3", | ||||||
| 					"ifndef": ["_ShadowMapAtlas"], | 					"ifndef": ["_ShadowMapAtlas"], | ||||||
| 					"ifdef": ["_Spot", "_ShadowMap"] | 					"ifdef": ["_Spot", "_ShadowMap"] | ||||||
| 				}, |  | ||||||
| 				{ |  | ||||||
| 					"name": "PPComp11", |  | ||||||
| 					"link": "_PPComp11", |  | ||||||
| 					"ifdef": ["_CPostprocess"] |  | ||||||
| 				}, |  | ||||||
| 				{ |  | ||||||
| 					"name": "PPComp17", |  | ||||||
| 					"link": "_PPComp17", |  | ||||||
| 					"ifdef": ["_CPostprocess"] |  | ||||||
| 				} | 				} | ||||||
| 			], | 			], | ||||||
| 			"texture_params": [], | 			"texture_params": [], | ||||||
|  | |||||||
| @ -33,6 +33,7 @@ uniform layout(r32ui) uimage3D voxelsLight; | |||||||
|  |  | ||||||
| #ifdef _ShadowMap | #ifdef _ShadowMap | ||||||
| uniform sampler2DShadow shadowMap; | uniform sampler2DShadow shadowMap; | ||||||
|  | uniform sampler2D shadowMapTransparent; | ||||||
| uniform sampler2DShadow shadowMapSpot; | uniform sampler2DShadow shadowMapSpot; | ||||||
| #ifdef _ShadowMapAtlas | #ifdef _ShadowMapAtlas | ||||||
| uniform sampler2DShadow shadowMapPoint; | uniform sampler2DShadow shadowMapPoint; | ||||||
| @ -86,53 +87,51 @@ float lpToDepth(vec3 lp, const vec2 lightProj) { | |||||||
|  |  | ||||||
| void main() { | void main() { | ||||||
| 	int res = voxelgiResolution.x; | 	int res = voxelgiResolution.x; | ||||||
|  | 	for (int i = 0; i < 6; i++) { | ||||||
|  | 		ivec3 dst = ivec3(gl_GlobalInvocationID.xyz); | ||||||
|  | 		vec3 P = (gl_GlobalInvocationID.xyz + 0.5) / voxelgiResolution; | ||||||
|  | 		P = P * 2.0 - 1.0; | ||||||
|  |  | ||||||
| 	ivec3 dst = ivec3(gl_GlobalInvocationID.xyz); | 		float visibility; | ||||||
| 	dst.y += clipmapLevel * res; | 		vec3 lp = lightPos - P; | ||||||
|  | 		vec3 l; | ||||||
| 	vec3 P = (gl_GlobalInvocationID.xyz + 0.5) / voxelgiResolution; | 		if (lightType == 0) { l = lightDir; visibility = 1.0; } | ||||||
| 	P = P * 2.0 - 1.0; | 		else { l = normalize(lp); visibility = attenuate(distance(P, lightPos)); } | ||||||
| 	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 | #ifdef _ShadowMap | ||||||
| 	if (lightShadow == 1) { | 		if (lightShadow == 1) { | ||||||
| 		vec4 lightPosition = LVP * vec4(P, 1.0); | 			vec4 lightPosition = LVP * vec4(P, 1.0); | ||||||
| 		vec3 lPos = lightPosition.xyz / lightPosition.w; | 			vec3 lPos = lightPosition.xyz / lightPosition.w; | ||||||
| 		visibility = texture(shadowMap, vec3(lPos.xy, lPos.z - shadowsBias)).rrr; | 			visibility = texture(shadowMap, vec3(lPos.xy, lPos.z - shadowsBias)).r; | ||||||
| 	} | 		} | ||||||
| 	else if (lightShadow == 2) { | 		else if (lightShadow == 2) { | ||||||
| 		vec4 lightPosition = LVP * vec4(P, 1.0); | 			vec4 lightPosition = LVP * vec4(P, 1.0); | ||||||
| 		vec3 lPos = lightPosition.xyz / lightPosition.w; | 			vec3 lPos = lightPosition.xyz / lightPosition.w; | ||||||
| 		visibility *= texture(shadowMapSpot, vec3(lPos.xy, lPos.z - shadowsBias)).r; | 			visibility *= texture(shadowMapSpot, vec3(lPos.xy, lPos.z - shadowsBias)).r; | ||||||
| 	} | 		} | ||||||
| 	else if (lightShadow == 3) { | 		else if (lightShadow == 3) { | ||||||
| 		#ifdef _ShadowMapAtlas | 			#ifdef _ShadowMapAtlas | ||||||
| 		int faceIndex = 0; | 			int faceIndex = 0; | ||||||
| 		const int lightIndex = index * 6; | 			const int lightIndex = index * 6; | ||||||
| 		const vec2 uv = sampleCube(-l, faceIndex); | 			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 | 			vec4 pointLightTile = pointLightDataArray[lightIndex + faceIndex]; // x: tile X offset, y: tile Y offset, z: tile size relative to atlas | ||||||
| 		vec2 uvtiled = pointLightTile.z * uv + pointLightTile.xy; | 			vec2 uvtiled = pointLightTile.z * uv + pointLightTile.xy; | ||||||
| 		#ifdef _FlipY | 			#ifdef _FlipY | ||||||
| 		uvtiled.y = 1.0 - uvtiled.y; // invert Y coordinates for direct3d coordinate system | 			uvtiled.y = 1.0 - uvtiled.y; // invert Y coordinates for direct3d coordinate system | ||||||
| 		#endif | 			#endif | ||||||
| 		visibility *= texture(shadowMapPoint, vec3(uvtiled, lpToDepth(lp, lightProj) - shadowsBias)).r; | 			visibility *= texture(shadowMapPoint, vec3(uvtiled, lpToDepth(lp, lightProj) - shadowsBias)).r; | ||||||
| 		#else | 			#else | ||||||
| 		visibility *= texture(shadowMapPoint, vec4(-l, lpToDepth(lp, lightProj) - shadowsBias)).r; | 			visibility *= texture(shadowMapPoint, vec4(-l, lpToDepth(lp, lightProj) - shadowsBias)).r; | ||||||
| 		#endif | 			#endif | ||||||
| 	} | 		} | ||||||
| #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); | ||||||
|  |  | ||||||
| 	vec3 light = visibility * lightColor; | 		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)); | ||||||
| 	imageAtomicAdd(voxelsLight, dst + ivec3(0, 0, 0), uint(light.r * 255)); | 		imageAtomicMax(voxelsLight, ivec3(writecoords_light) + ivec3(0, 0, voxelgiResolution.x * 2), uint(visibility * lightColor.b * 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 sampler3D voxels; | ||||||
| uniform sampler2D gbufferD; | uniform sampler2D gbufferD; | ||||||
| uniform sampler2D gbuffer0; | uniform sampler2D gbuffer0; | ||||||
| uniform layout(r8) image2D voxels_ao; | uniform layout(r16) image2D voxels_ao; | ||||||
|  |  | ||||||
| uniform float clipmaps[voxelgiClipmapCount * 10]; | uniform float clipmaps[voxelgiClipmapCount * 10]; | ||||||
| uniform mat4 InvVP; | 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 sampler3D voxels; | ||||||
| uniform sampler2D gbufferD; | uniform sampler2D gbufferD; | ||||||
| uniform sampler2D gbuffer0; | uniform sampler2D gbuffer0; | ||||||
| uniform layout(rgba8) image2D voxels_diffuse; | uniform layout(rgba16) image2D voxels_diffuse; | ||||||
|  |  | ||||||
| uniform float clipmaps[voxelgiClipmapCount * 10]; | uniform float clipmaps[voxelgiClipmapCount * 10]; | ||||||
| uniform mat4 InvVP; | uniform mat4 InvVP; | ||||||
| @ -46,7 +46,7 @@ void main() { | |||||||
| 	const vec2 pixel = gl_GlobalInvocationID.xy; | 	const vec2 pixel = gl_GlobalInvocationID.xy; | ||||||
| 	vec2 uv = (pixel + 0.5) / postprocess_resolution; | 	vec2 uv = (pixel + 0.5) / postprocess_resolution; | ||||||
| 	#ifdef _InvY | 	#ifdef _InvY | ||||||
| 	uv.y = 1.0 - uv.y | 	uv.y = 1.0 - uv.y; | ||||||
| 	#endif | 	#endif | ||||||
|  |  | ||||||
| 	float depth = textureLod(gbufferD, uv, 0.0).r * 2.0 - 1.0; | 	float depth = textureLod(gbufferD, uv, 0.0).r * 2.0 - 1.0; | ||||||
|  | |||||||
| @ -34,7 +34,7 @@ uniform sampler2D gbufferD; | |||||||
| uniform sampler2D gbuffer0; | uniform sampler2D gbuffer0; | ||||||
| uniform sampler3D voxels; | uniform sampler3D voxels; | ||||||
| uniform sampler3D voxelsSDF; | uniform sampler3D voxelsSDF; | ||||||
| uniform layout(rgba8) image2D voxels_specular; | uniform layout(rgba16) image2D voxels_specular; | ||||||
|  |  | ||||||
| uniform float clipmaps[voxelgiClipmapCount * 10]; | uniform float clipmaps[voxelgiClipmapCount * 10]; | ||||||
| uniform mat4 InvVP; | uniform mat4 InvVP; | ||||||
| @ -71,7 +71,7 @@ void main() { | |||||||
|  |  | ||||||
| 	vec2 velocity = -textureLod(sveloc, uv, 0.0).rg; | 	vec2 velocity = -textureLod(sveloc, uv, 0.0).rg; | ||||||
|  |  | ||||||
| 	vec3 color = traceSpecular(P, n, voxels, voxelsSDF, normalize(eye - P), g0.z, clipmaps, pixel, velocity).rgb; | 	vec3 color = traceSpecular(P, n, voxels, voxelsSDF, normalize(eye - P), g0.z * g0.z, clipmaps, pixel, velocity).rgb; | ||||||
|  |  | ||||||
| 	imageStore(voxels_specular, ivec2(pixel), vec4(color, 1.0)); | 	imageStore(voxels_specular, ivec2(pixel), vec4(color, 1.0)); | ||||||
| } | } | ||||||
|  | |||||||
| @ -23,8 +23,8 @@ THE SOFTWARE. | |||||||
|  |  | ||||||
| #include "compiled.inc" | #include "compiled.inc" | ||||||
|  |  | ||||||
| uniform layout(r16) image3D input_sdf; | uniform layout(r8) image3D input_sdf; | ||||||
| uniform layout(r16) image3D output_sdf; | uniform layout(r8) image3D output_sdf; | ||||||
|  |  | ||||||
| uniform float jump_size; | uniform float jump_size; | ||||||
| uniform int clipmapLevel; | uniform int clipmapLevel; | ||||||
|  | |||||||
| @ -46,15 +46,15 @@ uniform layout(r32ui) uimage3D voxels; | |||||||
| uniform layout(r32ui) uimage3D voxelsLight; | uniform layout(r32ui) uimage3D voxelsLight; | ||||||
| uniform layout(rgba8) image3D voxelsB; | uniform layout(rgba8) image3D voxelsB; | ||||||
| uniform layout(rgba8) image3D voxelsOut; | uniform layout(rgba8) image3D voxelsOut; | ||||||
| uniform layout(r16) image3D SDF; | uniform layout(r8) image3D SDF; | ||||||
| #else | #else | ||||||
| #ifdef _VoxelAOvar | #ifdef _VoxelAOvar | ||||||
| #ifdef _VoxelShadow | #ifdef _VoxelShadow | ||||||
| uniform layout(r16) image3D SDF; | uniform layout(r8) image3D SDF; | ||||||
| #endif | #endif | ||||||
| uniform layout(r32ui) uimage3D voxels; | uniform layout(r32ui) uimage3D voxels; | ||||||
| uniform layout(r16) image3D voxelsB; | uniform layout(r8) image3D voxelsB; | ||||||
| uniform layout(r16) image3D voxelsOut; | uniform layout(r8) image3D voxelsOut; | ||||||
| #endif | #endif | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| @ -80,7 +80,6 @@ void main() { | |||||||
| 	light.r = float(imageLoad(voxelsLight, src)) / 255; | 	light.r = float(imageLoad(voxelsLight, src)) / 255; | ||||||
| 	light.g = float(imageLoad(voxelsLight, src + ivec3(0, 0, voxelgiResolution.x))) / 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.b = float(imageLoad(voxelsLight, src + ivec3(0, 0, voxelgiResolution.x * 2))) / 255; | ||||||
| 	light /= 3; |  | ||||||
| 	#endif | 	#endif | ||||||
|  |  | ||||||
| 	for (int i = 0; i < 6 + DIFFUSE_CONE_COUNT; i++) | 	for (int i = 0; i < 6 + DIFFUSE_CONE_COUNT; i++) | ||||||
| @ -125,7 +124,7 @@ void main() { | |||||||
| 			envl.g = float(imageLoad(voxels, src + ivec3(0, 0, voxelgiResolution.x * 10))) / 255; | 			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.b = float(imageLoad(voxels, src + ivec3(0, 0, voxelgiResolution.x * 11))) / 255; | ||||||
| 			envl /= 3; | 			envl /= 3; | ||||||
| 			envl *= 100; |  | ||||||
|  |  | ||||||
| 			//clipmap to world | 			//clipmap to world | ||||||
| 			vec3 wposition = (gl_GlobalInvocationID.xyz + 0.5) / voxelgiResolution.x; | 			vec3 wposition = (gl_GlobalInvocationID.xyz + 0.5) / voxelgiResolution.x; | ||||||
| @ -137,7 +136,7 @@ void main() { | |||||||
| 			radiance = basecol; | 			radiance = basecol; | ||||||
| 			vec4 trace = traceDiffuse(wposition, wnormal, voxelsSampler, clipmaps); | 			vec4 trace = traceDiffuse(wposition, wnormal, voxelsSampler, clipmaps); | ||||||
| 			vec3 indirect = trace.rgb + envl.rgb * (1.0 - trace.a); | 			vec3 indirect = trace.rgb + envl.rgb * (1.0 - trace.a); | ||||||
| 			radiance.rgb *= light + indirect; | 			radiance.rgb *= light / PI + indirect; | ||||||
| 			radiance.rgb += emission.rgb; | 			radiance.rgb += emission.rgb; | ||||||
|  |  | ||||||
| 			#else | 			#else | ||||||
|  | |||||||
| @ -75,16 +75,17 @@ vec4 binarySearch(vec3 dir) { | |||||||
| } | } | ||||||
|  |  | ||||||
| vec4 rayCast(vec3 dir) { | vec4 rayCast(vec3 dir) { | ||||||
| 	#ifdef _CPostprocess |     float ddepth; | ||||||
| 		dir *= PPComp9.x; |     dir *= ss_refractionRayStep; | ||||||
| 	#else |     for (int i = 0; i < maxSteps; i++) { | ||||||
| 		dir *= ssrRayStep; |         hitCoord += dir; | ||||||
| 	#endif |         ddepth = getDeltaDepth(hitCoord); | ||||||
| 	for (int i = 0; i < maxSteps; i++) { |         if (ddepth > 0.0) | ||||||
| 		hitCoord += dir; |             return binarySearch(dir); | ||||||
| 		if (getDeltaDepth(hitCoord) > 0.0) return binarySearch(dir); |     } | ||||||
| 	} |     // No hit — fallback to projecting the ray to UV space | ||||||
| 	return vec4(0.0); |     vec2 fallbackUV = getProjectedCoord(hitCoord); | ||||||
|  |     return vec4(fallbackUV, 0.0, 0.5); // We set .w lower to indicate fallback | ||||||
| } | } | ||||||
| #endif //SSR | #endif //SSR | ||||||
|  |  | ||||||
|  | |||||||
| @ -12,7 +12,6 @@ class App { | |||||||
| 	static var traitInits: Array<Void->Void> = []; | 	static var traitInits: Array<Void->Void> = []; | ||||||
| 	static var traitUpdates: Array<Void->Void> = []; | 	static var traitUpdates: Array<Void->Void> = []; | ||||||
| 	static var traitLateUpdates: Array<Void->Void> = []; | 	static var traitLateUpdates: Array<Void->Void> = []; | ||||||
| 	static var traitFixedUpdates: Array<Void->Void> = []; |  | ||||||
| 	static var traitRenders: Array<kha.graphics4.Graphics->Void> = []; | 	static var traitRenders: Array<kha.graphics4.Graphics->Void> = []; | ||||||
| 	static var traitRenders2D: Array<kha.graphics2.Graphics->Void> = []; | 	static var traitRenders2D: Array<kha.graphics2.Graphics->Void> = []; | ||||||
| 	public static var framebuffer: kha.Framebuffer; | 	public static var framebuffer: kha.Framebuffer; | ||||||
| @ -24,8 +23,6 @@ class App { | |||||||
| 	public static var renderPathTime: Float; | 	public static var renderPathTime: Float; | ||||||
| 	public static var endFrameCallbacks: Array<Void->Void> = []; | 	public static var endFrameCallbacks: Array<Void->Void> = []; | ||||||
| 	#end | 	#end | ||||||
| 	static var last = 0.0; |  | ||||||
| 	static var time = 0.0; |  | ||||||
| 	static var lastw = -1; | 	static var lastw = -1; | ||||||
| 	static var lasth = -1; | 	static var lasth = -1; | ||||||
| 	public static var onResize: Void->Void = null; | 	public static var onResize: Void->Void = null; | ||||||
| @ -37,14 +34,13 @@ class App { | |||||||
| 	function new(done: Void->Void) { | 	function new(done: Void->Void) { | ||||||
| 		done(); | 		done(); | ||||||
| 		kha.System.notifyOnFrames(render); | 		kha.System.notifyOnFrames(render); | ||||||
| 		kha.Scheduler.addTimeTask(update, 0, iron.system.Time.step); | 		kha.Scheduler.addTimeTask(update, 0, iron.system.Time.delta); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	public static function reset() { | 	public static function reset() { | ||||||
| 		traitInits = []; | 		traitInits = []; | ||||||
| 		traitUpdates = []; | 		traitUpdates = []; | ||||||
| 		traitLateUpdates = []; | 		traitLateUpdates = []; | ||||||
| 		traitFixedUpdates = []; |  | ||||||
| 		traitRenders = []; | 		traitRenders = []; | ||||||
| 		traitRenders2D = []; | 		traitRenders2D = []; | ||||||
| 		if (onResets != null) for (f in onResets) f(); | 		if (onResets != null) for (f in onResets) f(); | ||||||
| @ -52,24 +48,6 @@ class App { | |||||||
|  |  | ||||||
| 	static function update() { | 	static function update() { | ||||||
| 		if (Scene.active == null || !Scene.active.ready) return; | 		if (Scene.active == null || !Scene.active.ready) return; | ||||||
| 		 |  | ||||||
| 		iron.system.Time.update(); |  | ||||||
| 		 |  | ||||||
| 		if (lastw == -1) { |  | ||||||
| 			lastw = App.w(); |  | ||||||
| 			lasth = App.h(); |  | ||||||
| 		} |  | ||||||
| 		if (lastw != App.w() || lasth != App.h()) { |  | ||||||
| 			if (onResize != null) onResize(); |  | ||||||
| 			else { |  | ||||||
| 				if (Scene.active != null && Scene.active.camera != null) { |  | ||||||
| 					Scene.active.camera.buildProjection(); |  | ||||||
| 				} |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 		lastw = App.w(); |  | ||||||
| 		lasth = App.h(); |  | ||||||
| 		 |  | ||||||
| 		if (pauseUpdates) return; | 		if (pauseUpdates) return; | ||||||
|  |  | ||||||
| 		#if lnx_debug | 		#if lnx_debug | ||||||
| @ -78,14 +56,6 @@ class App { | |||||||
|  |  | ||||||
| 		Scene.active.updateFrame(); | 		Scene.active.updateFrame(); | ||||||
|  |  | ||||||
|  |  | ||||||
| 		time += iron.system.Time.delta; |  | ||||||
|  |  | ||||||
| 		while (time >= iron.system.Time.fixedStep) { |  | ||||||
| 			for (f in traitFixedUpdates) f(); |  | ||||||
| 			time -= iron.system.Time.fixedStep; |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		var i = 0; | 		var i = 0; | ||||||
| 		var l = traitUpdates.length; | 		var l = traitUpdates.length; | ||||||
| 		while (i < l) { | 		while (i < l) { | ||||||
| @ -114,13 +84,29 @@ class App { | |||||||
| 		for (cb in endFrameCallbacks) cb(); | 		for (cb in endFrameCallbacks) cb(); | ||||||
| 		updateTime = kha.Scheduler.realTime() - startTime; | 		updateTime = kha.Scheduler.realTime() - startTime; | ||||||
| 		#end | 		#end | ||||||
|  |  | ||||||
|  | 		// Rebuild projection on window resize | ||||||
|  | 		if (lastw == -1) { | ||||||
|  | 			lastw = App.w(); | ||||||
|  | 			lasth = App.h(); | ||||||
|  | 		} | ||||||
|  | 		if (lastw != App.w() || lasth != App.h()) { | ||||||
|  | 			if (onResize != null) onResize(); | ||||||
|  | 			else { | ||||||
|  | 				if (Scene.active != null && Scene.active.camera != null) { | ||||||
|  | 					Scene.active.camera.buildProjection(); | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		lastw = App.w(); | ||||||
|  | 		lasth = App.h(); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	static function render(frames: Array<kha.Framebuffer>) { | 	static function render(frames: Array<kha.Framebuffer>) { | ||||||
| 		var frame = frames[0]; | 		var frame = frames[0]; | ||||||
| 		framebuffer = frame; | 		framebuffer = frame; | ||||||
|  |  | ||||||
| 		iron.system.Time.render(); | 		iron.system.Time.update(); | ||||||
|  |  | ||||||
| 		if (Scene.active == null || !Scene.active.ready) { | 		if (Scene.active == null || !Scene.active.ready) { | ||||||
| 			render2D(frame); | 			render2D(frame); | ||||||
| @ -186,14 +172,6 @@ class App { | |||||||
| 		traitLateUpdates.remove(f); | 		traitLateUpdates.remove(f); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	public static function notifyOnFixedUpdate(f: Void->Void) { |  | ||||||
| 		traitFixedUpdates.push(f); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	public static function removeFixedUpdate(f: Void->Void) { |  | ||||||
| 		traitFixedUpdates.remove(f); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	public static function notifyOnRender(f: kha.graphics4.Graphics->Void) { | 	public static function notifyOnRender(f: kha.graphics4.Graphics->Void) { | ||||||
| 		traitRenders.push(f); | 		traitRenders.push(f); | ||||||
| 	} | 	} | ||||||
|  | |||||||
| @ -331,18 +331,15 @@ class RenderPath { | |||||||
| 		}); | 		}); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	public static function sortMeshesIndex(meshes: Array<MeshObject>) { | 	public static function sortMeshesShader(meshes: Array<MeshObject>) { | ||||||
| 		meshes.sort(function(a, b): Int { | 		meshes.sort(function(a, b): Int { | ||||||
| 			#if rp_depth_texture | 			#if rp_depth_texture | ||||||
| 			var depthDiff = boolToInt(a.depthRead) - boolToInt(b.depthRead); | 			var depthDiff = boolToInt(a.depthRead) - boolToInt(b.depthRead); | ||||||
| 			if (depthDiff != 0) return depthDiff; | 			if (depthDiff != 0) return depthDiff; | ||||||
| 			#end | 			#end | ||||||
|  |  | ||||||
| 			if (a.data.sortingIndex != b.data.sortingIndex) { | 			return a.materials[0].name >= b.materials[0].name ? 1 : -1; | ||||||
| 				return a.data.sortingIndex > b.data.sortingIndex ? 1 : -1; | 		}); | ||||||
| 			} |  | ||||||
|  |  | ||||||
| 			return a.data.name >= b.data.name ? 1 : -1;		}); |  | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	public function drawMeshes(context: String) { | 	public function drawMeshes(context: String) { | ||||||
| @ -402,7 +399,7 @@ class RenderPath { | |||||||
| 			#if lnx_batch | 			#if lnx_batch | ||||||
| 			sortMeshesDistance(Scene.active.meshBatch.nonBatched); | 			sortMeshesDistance(Scene.active.meshBatch.nonBatched); | ||||||
| 			#else | 			#else | ||||||
| 			drawOrder == DrawOrder.Index ? sortMeshesIndex(meshes) : sortMeshesDistance(meshes); | 			drawOrder == DrawOrder.Shader ? sortMeshesShader(meshes) : sortMeshesDistance(meshes); | ||||||
| 			#end | 			#end | ||||||
| 			meshesSorted = true; | 			meshesSorted = true; | ||||||
| 		} | 		} | ||||||
| @ -521,44 +518,12 @@ class RenderPath { | |||||||
| 		return Reflect.field(kha.Shaders, handle + "_comp"); | 		return Reflect.field(kha.Shaders, handle + "_comp"); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	#if lnx_vr | 	#if (kha_krom && lnx_vr) | ||||||
| 	public function drawStereo(drawMeshes: Void->Void) { | 	public function drawStereo(drawMeshes: Int->Void) { | ||||||
| 		var vr = kha.vr.VrInterface.instance; | 		for (eye in 0...2) { | ||||||
| 		var appw = iron.App.w(); | 			Krom.vrBeginRender(eye); | ||||||
| 		var apph = iron.App.h(); | 			drawMeshes(eye); | ||||||
| 		var halfw = Std.int(appw / 2); | 			Krom.vrEndRender(eye); | ||||||
| 		var g = currentG; |  | ||||||
|  |  | ||||||
| 		if (vr != null && vr.IsPresenting()) { |  | ||||||
| 			// Left eye |  | ||||||
| 			Scene.active.camera.V.setFrom(Scene.active.camera.leftV); |  | ||||||
| 			Scene.active.camera.P.self = vr.GetProjectionMatrix(0); |  | ||||||
| 			g.viewport(0, 0, halfw, apph); |  | ||||||
| 			drawMeshes(); |  | ||||||
|  |  | ||||||
| 			// Right eye |  | ||||||
| 			begin(g, additionalTargets); |  | ||||||
| 			Scene.active.camera.V.setFrom(Scene.active.camera.rightV); |  | ||||||
| 			Scene.active.camera.P.self = vr.GetProjectionMatrix(1); |  | ||||||
| 			g.viewport(halfw, 0, halfw, apph); |  | ||||||
| 			drawMeshes(); |  | ||||||
| 		} |  | ||||||
| 		else { // Simulate |  | ||||||
| 			Scene.active.camera.buildProjection(halfw / apph); |  | ||||||
|  |  | ||||||
| 			// Left eye |  | ||||||
| 			g.viewport(0, 0, halfw, apph); |  | ||||||
| 			drawMeshes(); |  | ||||||
|  |  | ||||||
| 			// Right eye |  | ||||||
| 			begin(g, additionalTargets); |  | ||||||
| 			Scene.active.camera.transform.move(Scene.active.camera.right(), 0.032); |  | ||||||
| 			Scene.active.camera.buildMatrix(); |  | ||||||
| 			g.viewport(halfw, 0, halfw, apph); |  | ||||||
| 			drawMeshes(); |  | ||||||
|  |  | ||||||
| 			Scene.active.camera.transform.move(Scene.active.camera.right(), -0.032); |  | ||||||
| 			Scene.active.camera.buildMatrix(); |  | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	#end | 	#end | ||||||
| @ -917,6 +882,6 @@ class CachedShaderContext { | |||||||
|  |  | ||||||
| @:enum abstract DrawOrder(Int) from Int { | @:enum abstract DrawOrder(Int) from Int { | ||||||
| 	var Distance = 0; // Early-z | 	var Distance = 0; // Early-z | ||||||
| 	var Index = 1; // Less state changes | 	var Shader = 1; // Less state changes | ||||||
| 	// var Mix = 2; // Distance buckets sorted by shader | 	// var Mix = 2; // Distance buckets sorted by shader | ||||||
| } | } | ||||||
|  | |||||||
| @ -775,7 +775,6 @@ class Scene { | |||||||
| 			// Attach particle systems | 			// Attach particle systems | ||||||
| 			#if lnx_particles | 			#if lnx_particles | ||||||
| 			if (o.particle_refs != null) { | 			if (o.particle_refs != null) { | ||||||
| 				cast(object, MeshObject).render_emitter = o.render_emitter; |  | ||||||
| 				for (ref in o.particle_refs) cast(object, MeshObject).setupParticleSystem(sceneName, ref); | 				for (ref in o.particle_refs) cast(object, MeshObject).setupParticleSystem(sceneName, ref); | ||||||
| 			} | 			} | ||||||
| 			#end | 			#end | ||||||
| @ -783,11 +782,6 @@ class Scene { | |||||||
| 			if (o.tilesheet_ref != null) { | 			if (o.tilesheet_ref != null) { | ||||||
| 				cast(object, MeshObject).setupTilesheet(sceneName, o.tilesheet_ref, o.tilesheet_action_ref); | 				cast(object, MeshObject).setupTilesheet(sceneName, o.tilesheet_ref, o.tilesheet_action_ref); | ||||||
| 			} | 			} | ||||||
| 			 |  | ||||||
| 			if (o.camera_list != null){ |  | ||||||
| 				cast(object, MeshObject).cameraList = o.camera_list; |  | ||||||
| 			} |  | ||||||
|  |  | ||||||
| 			returnObject(object, o, done); | 			returnObject(object, o, done); | ||||||
| 		}); | 		}); | ||||||
| 	} | 	} | ||||||
| @ -887,12 +881,8 @@ class Scene { | |||||||
| 						var ptype: String = t.props[i * 3 + 1]; | 						var ptype: String = t.props[i * 3 + 1]; | ||||||
| 						var pval: Dynamic = t.props[i * 3 + 2]; | 						var pval: Dynamic = t.props[i * 3 + 2]; | ||||||
|  |  | ||||||
| 						if (StringTools.endsWith(ptype, "Object") && pval != "" && pval != null) { | 						if (StringTools.endsWith(ptype, "Object") && pval != "") { | ||||||
| 							Reflect.setProperty(traitInst, pname, Scene.active.getChild(pval)); | 							Reflect.setProperty(traitInst, pname, Scene.active.getChild(pval)); | ||||||
| 						} else if (ptype == "TSceneFormat" && pval != "") { |  | ||||||
| 							Data.getSceneRaw(pval, function (r: TSceneFormat) { |  | ||||||
| 								Reflect.setProperty(traitInst, pname, r); |  | ||||||
| 							}); |  | ||||||
| 						} | 						} | ||||||
| 						else { | 						else { | ||||||
| 							switch (ptype) { | 							switch (ptype) { | ||||||
|  | |||||||
| @ -16,7 +16,6 @@ class Trait { | |||||||
| 	var _remove: Array<Void->Void> = null; | 	var _remove: Array<Void->Void> = null; | ||||||
| 	var _update: Array<Void->Void> = null; | 	var _update: Array<Void->Void> = null; | ||||||
| 	var _lateUpdate: Array<Void->Void> = null; | 	var _lateUpdate: Array<Void->Void> = null; | ||||||
| 	var _fixedUpdate: Array<Void->Void> = null; |  | ||||||
| 	var _render: Array<kha.graphics4.Graphics->Void> = null; | 	var _render: Array<kha.graphics4.Graphics->Void> = null; | ||||||
| 	var _render2D: Array<kha.graphics2.Graphics->Void> = null; | 	var _render2D: Array<kha.graphics2.Graphics->Void> = null; | ||||||
|  |  | ||||||
| @ -88,23 +87,6 @@ class Trait { | |||||||
| 		App.removeLateUpdate(f); | 		App.removeLateUpdate(f); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|     /** |  | ||||||
|       Add fixed game logic handler. |  | ||||||
|     **/ |  | ||||||
| 	public function notifyOnFixedUpdate(f: Void->Void) { |  | ||||||
| 		if (_fixedUpdate == null) _fixedUpdate = []; |  | ||||||
| 		_fixedUpdate.push(f); |  | ||||||
| 		App.notifyOnFixedUpdate(f); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
|     /** |  | ||||||
|       Remove fixed game logic handler. |  | ||||||
|     **/ |  | ||||||
| 	public function removeFixedUpdate(f: Void->Void) { |  | ||||||
| 		_fixedUpdate.remove(f); |  | ||||||
| 		App.removeFixedUpdate(f); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|       Add render handler. |       Add render handler. | ||||||
|     **/ |     **/ | ||||||
|  | |||||||
| @ -9,7 +9,6 @@ import iron.data.SceneFormat; | |||||||
| class MeshData { | class MeshData { | ||||||
|  |  | ||||||
| 	public var name: String; | 	public var name: String; | ||||||
| 	public var sortingIndex: Int; |  | ||||||
| 	public var raw: TMeshData; | 	public var raw: TMeshData; | ||||||
| 	public var format: TSceneFormat; | 	public var format: TSceneFormat; | ||||||
| 	public var geom: Geometry; | 	public var geom: Geometry; | ||||||
| @ -24,8 +23,7 @@ class MeshData { | |||||||
| 	public function new(raw: TMeshData, done: MeshData->Void) { | 	public function new(raw: TMeshData, done: MeshData->Void) { | ||||||
| 		this.raw = raw; | 		this.raw = raw; | ||||||
| 		this.name = raw.name; | 		this.name = raw.name; | ||||||
| 		this.sortingIndex = raw.sorting_index; |  | ||||||
| 		 |  | ||||||
| 		if (raw.scale_pos != null) scalePos = raw.scale_pos; | 		if (raw.scale_pos != null) scalePos = raw.scale_pos; | ||||||
| 		if (raw.scale_tex != null) scaleTex = raw.scale_tex; | 		if (raw.scale_tex != null) scaleTex = raw.scale_tex; | ||||||
|  |  | ||||||
|  | |||||||
| @ -49,7 +49,6 @@ typedef TMeshData = { | |||||||
| @:structInit class TMeshData { | @:structInit class TMeshData { | ||||||
| #end | #end | ||||||
| 	public var name: String; | 	public var name: String; | ||||||
| 	public var sorting_index: Int; |  | ||||||
| 	public var vertex_arrays: Array<TVertexArray>; | 	public var vertex_arrays: Array<TVertexArray>; | ||||||
| 	public var index_arrays: Array<TIndexArray>; | 	public var index_arrays: Array<TIndexArray>; | ||||||
| 	@:optional public var dynamic_usage: Null<Bool>; | 	@:optional public var dynamic_usage: Null<Bool>; | ||||||
| @ -223,7 +222,6 @@ typedef TShaderData = { | |||||||
| @:structInit class TShaderData { | @:structInit class TShaderData { | ||||||
| #end | #end | ||||||
| 	public var name: String; | 	public var name: String; | ||||||
| 	public var next_pass: String; |  | ||||||
| 	public var contexts: Array<TShaderContext>; | 	public var contexts: Array<TShaderContext>; | ||||||
| } | } | ||||||
|  |  | ||||||
| @ -394,9 +392,6 @@ typedef TParticleData = { | |||||||
| #end | #end | ||||||
| 	public var name: String; | 	public var name: String; | ||||||
| 	public var type: Int; // 0 - Emitter, Hair | 	public var type: Int; // 0 - Emitter, Hair | ||||||
| 	public var auto_start: Bool; |  | ||||||
| 	public var dynamic_emitter: Bool; |  | ||||||
| 	public var is_unique: Bool; |  | ||||||
| 	public var loop: Bool; | 	public var loop: Bool; | ||||||
| 	public var count: Int; | 	public var count: Int; | ||||||
| 	public var frame_start: FastFloat; | 	public var frame_start: FastFloat; | ||||||
| @ -444,7 +439,6 @@ typedef TObj = { | |||||||
| 	@:optional public var traits: Array<TTrait>; | 	@:optional public var traits: Array<TTrait>; | ||||||
| 	@:optional public var properties: Array<TProperty>; | 	@:optional public var properties: Array<TProperty>; | ||||||
| 	@:optional public var vertex_groups: Array<TVertex_groups>; | 	@:optional public var vertex_groups: Array<TVertex_groups>; | ||||||
| 	@:optional public var camera_list: Array<String>; |  | ||||||
| 	@:optional public var constraints: Array<TConstraint>; | 	@:optional public var constraints: Array<TConstraint>; | ||||||
| 	@:optional public var dimensions: Float32Array; // Geometry objects | 	@:optional public var dimensions: Float32Array; // Geometry objects | ||||||
| 	@:optional public var object_actions: Array<String>; | 	@:optional public var object_actions: Array<String>; | ||||||
|  | |||||||
| @ -22,7 +22,6 @@ using StringTools; | |||||||
| class ShaderData { | class ShaderData { | ||||||
|  |  | ||||||
| 	public var name: String; | 	public var name: String; | ||||||
| 	public var nextPass: String; |  | ||||||
| 	public var raw: TShaderData; | 	public var raw: TShaderData; | ||||||
| 	public var contexts: Array<ShaderContext> = []; | 	public var contexts: Array<ShaderContext> = []; | ||||||
|  |  | ||||||
| @ -34,7 +33,6 @@ class ShaderData { | |||||||
| 	public function new(raw: TShaderData, done: ShaderData->Void, overrideContext: TShaderOverride = null) { | 	public function new(raw: TShaderData, done: ShaderData->Void, overrideContext: TShaderOverride = null) { | ||||||
| 		this.raw = raw; | 		this.raw = raw; | ||||||
| 		this.name = raw.name; | 		this.name = raw.name; | ||||||
| 		this.nextPass = raw.next_pass; |  | ||||||
|  |  | ||||||
| 		for (c in raw.contexts) contexts.push(null); | 		for (c in raw.contexts) contexts.push(null); | ||||||
| 		var contextsLoaded = 0; | 		var contextsLoaded = 0; | ||||||
|  | |||||||
| @ -1,50 +0,0 @@ | |||||||
| /* |  | ||||||
|  * 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 |  | ||||||
| } |  | ||||||
| @ -1,122 +0,0 @@ | |||||||
| /* |  | ||||||
|  * 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 |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| @ -1,256 +0,0 @@ | |||||||
| /* |  | ||||||
|  * 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 |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| @ -1,74 +0,0 @@ | |||||||
| /* |  | ||||||
|  * 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,17 +159,9 @@ class Animation { | |||||||
| 			if(markerEvents.get(sampler) != null){ | 			if(markerEvents.get(sampler) != null){ | ||||||
| 				for (i in 0...anim.marker_frames.length) { | 				for (i in 0...anim.marker_frames.length) { | ||||||
| 					if (frameIndex == anim.marker_frames[i]) { | 					if (frameIndex == anim.marker_frames[i]) { | ||||||
| 						var markerAct = markerEvents.get(sampler); | 						var marketAct = markerEvents.get(sampler); | ||||||
| 						var ar = markerAct.get(anim.marker_names[i]); | 						var ar = marketAct.get(anim.marker_names[i]); | ||||||
| 						if (ar != null) for (f in ar) f(); | 						if (ar != null) for (f in ar) f(); | ||||||
| 					} else { |  | ||||||
| 						for (j in 0...(frameIndex - lastFrameIndex)) { |  | ||||||
| 							if (lastFrameIndex + j + 1 == anim.marker_frames[i]) { |  | ||||||
| 								var markerAct = markerEvents.get(sampler); |  | ||||||
| 								var ar = markerAct.get(anim.marker_names[i]); |  | ||||||
| 								if (ar != null) for (f in ar) f(); |  | ||||||
| 							} |  | ||||||
| 						} |  | ||||||
| 					} | 					} | ||||||
| 				} | 				} | ||||||
| 				lastFrameIndex = frameIndex; | 				lastFrameIndex = frameIndex; | ||||||
|  | |||||||
| @ -30,22 +30,12 @@ class CameraObject extends Object { | |||||||
| 	static var sphereCenter = new Vec4(); | 	static var sphereCenter = new Vec4(); | ||||||
| 	static var vcenter = new Vec4(); | 	static var vcenter = new Vec4(); | ||||||
| 	static var vup = new Vec4(); | 	static var vup = new Vec4(); | ||||||
| 	 |  | ||||||
| 	#if lnx_vr |  | ||||||
| 	var helpMat = Mat4.identity(); |  | ||||||
| 	public var leftV = Mat4.identity(); |  | ||||||
| 	public var rightV = Mat4.identity(); |  | ||||||
| 	#end |  | ||||||
| 	 |  | ||||||
| 	public function new(data: CameraData) { | 	public function new(data: CameraData) { | ||||||
| 		super(); | 		super(); | ||||||
|  |  | ||||||
| 		this.data = data; | 		this.data = data; | ||||||
|  |  | ||||||
| 		#if lnx_vr |  | ||||||
| 		iron.system.VR.initButton(); |  | ||||||
| 		#end |  | ||||||
| 		 |  | ||||||
| 		buildProjection(); | 		buildProjection(); | ||||||
|  |  | ||||||
| 		V = Mat4.identity(); | 		V = Mat4.identity(); | ||||||
| @ -127,26 +117,6 @@ class CameraObject extends Object { | |||||||
| 		V.getInverse(transform.world); | 		V.getInverse(transform.world); | ||||||
| 		VP.multmats(P, V); | 		VP.multmats(P, V); | ||||||
|  |  | ||||||
|  |  | ||||||
| 		#if lnx_vr |  | ||||||
| 		var vr = kha.vr.VrInterface.instance; |  | ||||||
| 		if (vr != null && vr.IsPresenting()) { |  | ||||||
| 			leftV.setFrom(V); |  | ||||||
| 			helpMat.self = vr.GetViewMatrix(0); |  | ||||||
| 			leftV.multmat(helpMat); |  | ||||||
|  |  | ||||||
| 			rightV.setFrom(V); |  | ||||||
| 			helpMat.self = vr.GetViewMatrix(1); |  | ||||||
| 			rightV.multmat(helpMat); |  | ||||||
| 		} |  | ||||||
| 		else { |  | ||||||
| 			leftV.setFrom(V); |  | ||||||
| 		} |  | ||||||
| 		VP.multmats(P, leftV); |  | ||||||
| 		#else |  | ||||||
| 		VP.multmats(P, V); |  | ||||||
| 		#end |  | ||||||
|  |  | ||||||
| 		if (data.raw.frustum_culling) { | 		if (data.raw.frustum_culling) { | ||||||
| 			buildViewFrustum(VP, frustumPlanes); | 			buildViewFrustum(VP, frustumPlanes); | ||||||
| 		} | 		} | ||||||
|  | |||||||
| @ -155,13 +155,8 @@ class LightObject extends Object { | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	public function setCascade(camera: CameraObject, cascade: Int) { | 	public function setCascade(camera: CameraObject, cascade: Int) { | ||||||
|  |  | ||||||
| 		#if lnx_vr |  | ||||||
| 		m.setFrom(camera.leftV); |  | ||||||
| 		#else |  | ||||||
| 		m.setFrom(camera.V); | 		m.setFrom(camera.V); | ||||||
| 		#end |  | ||||||
| 		 |  | ||||||
| 		#if lnx_csm | 		#if lnx_csm | ||||||
| 		if (camSlicedP == null) { | 		if (camSlicedP == null) { | ||||||
| 			camSlicedP = []; | 			camSlicedP = []; | ||||||
|  | |||||||
| @ -21,10 +21,8 @@ class MeshObject extends Object { | |||||||
| 	public var particleChildren: Array<MeshObject> = null; | 	public var particleChildren: Array<MeshObject> = null; | ||||||
| 	public var particleOwner: MeshObject = null; // Particle object | 	public var particleOwner: MeshObject = null; // Particle object | ||||||
| 	public var particleIndex = -1; | 	public var particleIndex = -1; | ||||||
| 	public var render_emitter = true; |  | ||||||
| 	#end | 	#end | ||||||
| 	public var cameraDistance: Float; | 	public var cameraDistance: Float; | ||||||
| 	public var cameraList: Array<String> = null; |  | ||||||
| 	public var screenSize = 0.0; | 	public var screenSize = 0.0; | ||||||
| 	public var frustumCulling = true; | 	public var frustumCulling = true; | ||||||
| 	public var activeTilesheet: Tilesheet = null; | 	public var activeTilesheet: Tilesheet = null; | ||||||
| @ -236,8 +234,6 @@ class MeshObject extends Object { | |||||||
| 		if (cullMesh(context, Scene.active.camera, RenderPath.active.light)) return; | 		if (cullMesh(context, Scene.active.camera, RenderPath.active.light)) return; | ||||||
| 		var meshContext = raw != null ? context == "mesh" : false; | 		var meshContext = raw != null ? context == "mesh" : false; | ||||||
|  |  | ||||||
| 		if (cameraList != null && cameraList.indexOf(Scene.active.camera.name) < 0) return; |  | ||||||
|  |  | ||||||
| 		#if lnx_particles | 		#if lnx_particles | ||||||
| 		if (raw != null && raw.is_particle && particleOwner == null) return; // Instancing not yet set-up by particle system owner | 		if (raw != null && raw.is_particle && particleOwner == null) return; // Instancing not yet set-up by particle system owner | ||||||
| 		if (particleSystems != null && meshContext) { | 		if (particleSystems != null && meshContext) { | ||||||
| @ -248,7 +244,6 @@ class MeshObject extends Object { | |||||||
| 					Scene.active.spawnObject(psys.data.raw.instance_object, null, function(o: Object) { | 					Scene.active.spawnObject(psys.data.raw.instance_object, null, function(o: Object) { | ||||||
| 						if (o != null) { | 						if (o != null) { | ||||||
| 							var c: MeshObject = cast o; | 							var c: MeshObject = cast o; | ||||||
|     						c.cameraList = this.cameraList; |  | ||||||
| 							particleChildren.push(c); | 							particleChildren.push(c); | ||||||
| 							c.particleOwner = this; | 							c.particleOwner = this; | ||||||
| 							c.particleIndex = particleChildren.length - 1; | 							c.particleIndex = particleChildren.length - 1; | ||||||
| @ -260,11 +255,11 @@ class MeshObject extends Object { | |||||||
| 				particleSystems[i].update(particleChildren[i], this); | 				particleSystems[i].update(particleChildren[i], this); | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 		if (particleSystems != null && particleSystems.length > 0 && !render_emitter) return; | 		if (particleSystems != null && particleSystems.length > 0 && !raw.render_emitter) return; | ||||||
|         if (particleSystems == null && cullMaterial(context)) return; |  | ||||||
|         #else |  | ||||||
|         if (cullMaterial(context)) return; |  | ||||||
| 		#end | 		#end | ||||||
|  |  | ||||||
|  | 		if (cullMaterial(context)) return; | ||||||
|  |  | ||||||
| 		// Get lod | 		// Get lod | ||||||
| 		var mats = materials; | 		var mats = materials; | ||||||
| 		var lod = this; | 		var lod = this; | ||||||
| @ -302,10 +297,6 @@ class MeshObject extends Object { | |||||||
|  |  | ||||||
| 		// Render mesh | 		// Render mesh | ||||||
| 		var ldata = lod.data; | 		var ldata = lod.data; | ||||||
| 		 |  | ||||||
| 		// Next pass rendering first (inverse order) |  | ||||||
| 		renderNextPass(g, context, bindParams, lod); |  | ||||||
| 		 |  | ||||||
| 		for (i in 0...ldata.geom.indexBuffers.length) { | 		for (i in 0...ldata.geom.indexBuffers.length) { | ||||||
|  |  | ||||||
| 			var mi = ldata.geom.materialIndices[i]; | 			var mi = ldata.geom.materialIndices[i]; | ||||||
| @ -409,85 +400,4 @@ 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,10 +172,6 @@ class Object { | |||||||
| 			for (f in t._init) App.removeInit(f); | 			for (f in t._init) App.removeInit(f); | ||||||
| 			t._init = null; | 			t._init = null; | ||||||
| 		} | 		} | ||||||
| 		if (t._fixedUpdate != null) { |  | ||||||
| 			for (f in t._fixedUpdate) App.removeFixedUpdate(f); |  | ||||||
| 			t._fixedUpdate = null; |  | ||||||
| 		} |  | ||||||
| 		if (t._update != null) { | 		if (t._update != null) { | ||||||
| 			for (f in t._update) App.removeUpdate(f); | 			for (f in t._update) App.removeUpdate(f); | ||||||
| 			t._update = null; | 			t._update = null; | ||||||
|  | |||||||
| @ -24,9 +24,6 @@ class ObjectAnimation extends Animation { | |||||||
|  |  | ||||||
| 	public var transformMap: Map<String, FastFloat>; | 	public var transformMap: Map<String, FastFloat>; | ||||||
|  |  | ||||||
| 	var defaultSampler: ActionSampler = null; |  | ||||||
| 	static inline var DEFAULT_SAMPLER_ID = "__object_default_action__"; |  | ||||||
|  |  | ||||||
| 	public static var trackNames: Array<String> = [	"xloc", "yloc", "zloc", | 	public static var trackNames: Array<String> = [	"xloc", "yloc", "zloc", | ||||||
| 											   		"xrot", "yrot", "zrot", | 											   		"xrot", "yrot", "zrot", | ||||||
| 											   		"qwrot", "qxrot", "qyrot", "qzrot", | 											   		"qwrot", "qxrot", "qyrot", "qzrot", | ||||||
| @ -42,6 +39,7 @@ class ObjectAnimation extends Animation { | |||||||
| 		isSkinned = false; | 		isSkinned = false; | ||||||
| 		super(); | 		super(); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	function getAction(action: String): TObj { | 	function getAction(action: String): TObj { | ||||||
| 		for (a in oactions) if (a != null && a.objects[0].name == action) return a.objects[0]; | 		for (a in oactions) if (a != null && a.objects[0].name == action) return a.objects[0]; | ||||||
| 		return null; | 		return null; | ||||||
| @ -49,29 +47,10 @@ class ObjectAnimation extends Animation { | |||||||
|  |  | ||||||
| 	override public function play(action = "", onComplete: Void->Void = null, blendTime = 0.0, speed = 1.0, loop = true) { | 	override public function play(action = "", onComplete: Void->Void = null, blendTime = 0.0, speed = 1.0, loop = true) { | ||||||
| 		super.play(action, onComplete, blendTime, speed, loop); | 		super.play(action, onComplete, blendTime, speed, loop); | ||||||
| 		if (this.action == "" && oactions != null && oactions[0] != null){ | 		if (this.action == "" && oactions[0] != null) this.action = oactions[0].objects[0].name; | ||||||
| 			this.action = oactions[0].objects[0].name; |  | ||||||
| 		} |  | ||||||
| 		oaction = getAction(this.action); | 		oaction = getAction(this.action); | ||||||
| 		if (oaction != null) { | 		if (oaction != null) { | ||||||
| 			isSampled = oaction.sampled != null && oaction.sampled; | 			isSampled = oaction.sampled != null && oaction.sampled; | ||||||
| 			if (defaultSampler != null) { |  | ||||||
| 				deRegisterAction(DEFAULT_SAMPLER_ID); |  | ||||||
| 			} |  | ||||||
| 			var callbacks = onComplete != null ? [onComplete] : null; |  | ||||||
| 			defaultSampler = new ActionSampler(this.action, speed, loop, false, callbacks); |  | ||||||
| 			registerAction(DEFAULT_SAMPLER_ID, defaultSampler); |  | ||||||
| 			if (paused) defaultSampler.paused = true; |  | ||||||
| 			updateAnimation = function(map: Map<String, FastFloat>) { |  | ||||||
| 				sampleAction(defaultSampler, map); |  | ||||||
| 			}; |  | ||||||
| 		} |  | ||||||
| 		else { |  | ||||||
| 			if (defaultSampler != null) { |  | ||||||
| 				deRegisterAction(DEFAULT_SAMPLER_ID); |  | ||||||
| 				defaultSampler = null; |  | ||||||
| 			} |  | ||||||
| 			updateAnimation = null; |  | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| @ -82,13 +61,12 @@ class ObjectAnimation extends Animation { | |||||||
| 		Animation.beginProfile(); | 		Animation.beginProfile(); | ||||||
| 		#end | 		#end | ||||||
|  |  | ||||||
| 		if (transformMap == null) transformMap = new Map(); | 		if(transformMap == null) transformMap = new Map(); | ||||||
| 		transformMap = initTransformMap(); | 		transformMap = initTransformMap(); | ||||||
|  |  | ||||||
| 		super.update(delta); | 		super.update(delta); | ||||||
| 		if (defaultSampler != null) defaultSampler.paused = paused; |  | ||||||
| 		if (paused) return; | 		if (paused) return; | ||||||
| 		if (updateAnimation == null) return; | 		if(updateAnimation == null) return; | ||||||
| 		if (!isSkinned) updateObjectAnimation(); | 		if (!isSkinned) updateObjectAnimation(); | ||||||
|  |  | ||||||
| 		#if lnx_debug | 		#if lnx_debug | ||||||
|  | |||||||
| @ -2,14 +2,11 @@ package iron.object; | |||||||
|  |  | ||||||
| #if lnx_particles | #if lnx_particles | ||||||
|  |  | ||||||
| import kha.FastFloat; |  | ||||||
| import kha.graphics4.Usage; | import kha.graphics4.Usage; | ||||||
| import kha.arrays.Float32Array; | import kha.arrays.Float32Array; | ||||||
| import iron.data.Data; | import iron.data.Data; | ||||||
| import iron.data.ParticleData; | import iron.data.ParticleData; | ||||||
| import iron.data.SceneFormat; | import iron.data.SceneFormat; | ||||||
| import iron.data.Geometry; |  | ||||||
| import iron.data.MeshData; |  | ||||||
| import iron.system.Time; | import iron.system.Time; | ||||||
| import iron.math.Mat4; | import iron.math.Mat4; | ||||||
| import iron.math.Quat; | import iron.math.Quat; | ||||||
| @ -19,13 +16,10 @@ import iron.math.Vec4; | |||||||
| class ParticleSystem { | class ParticleSystem { | ||||||
| 	public var data: ParticleData; | 	public var data: ParticleData; | ||||||
| 	public var speed = 1.0; | 	public var speed = 1.0; | ||||||
| 	public var dynamicEmitter: Bool = true; |  | ||||||
| 	var currentSpeed = 0.0; |  | ||||||
| 	var particles: Array<Particle>; | 	var particles: Array<Particle>; | ||||||
| 	var ready: Bool; | 	var ready: Bool; | ||||||
| 	var frameRate = 24; | 	var frameRate = 24; | ||||||
| 	var lifetime = 0.0; | 	var lifetime = 0.0; | ||||||
| 	var looptime = 0.0; |  | ||||||
| 	var animtime = 0.0; | 	var animtime = 0.0; | ||||||
| 	var time = 0.0; | 	var time = 0.0; | ||||||
| 	var spawnRate = 0.0; | 	var spawnRate = 0.0; | ||||||
| @ -52,31 +46,15 @@ class ParticleSystem { | |||||||
| 	var ownerLoc = new Vec4(); | 	var ownerLoc = new Vec4(); | ||||||
| 	var ownerRot = new Quat(); | 	var ownerRot = new Quat(); | ||||||
| 	var ownerScl = new Vec4(); | 	var ownerScl = new Vec4(); | ||||||
| 	 |  | ||||||
| 	var random = 0.0; |  | ||||||
|  |  | ||||||
| 	var tmpV4 = new Vec4(); |  | ||||||
|  |  | ||||||
| 	var instancedData: Float32Array = null; |  | ||||||
| 	var lastSpawnedCount: Int = 0; |  | ||||||
| 	var hasUniqueGeom: Bool = false;  |  | ||||||
|  |  | ||||||
| 	public function new(sceneName: String, pref: TParticleReference) { | 	public function new(sceneName: String, pref: TParticleReference) { | ||||||
| 		seed = pref.seed; | 		seed = pref.seed; | ||||||
| 		currentSpeed = speed; |  | ||||||
| 		speed = 0; |  | ||||||
| 		particles = []; | 		particles = []; | ||||||
| 		ready = false; | 		ready = false; | ||||||
| 		 | 		 | ||||||
| 		Data.getParticle(sceneName, pref.particle, function(b: ParticleData) { | 		Data.getParticle(sceneName, pref.particle, function(b: ParticleData) { | ||||||
| 			data = b; | 			data = b; | ||||||
| 			r = data.raw; | 			r = data.raw; | ||||||
| 			var dyn: Null<Bool> = r.dynamic_emitter; |  | ||||||
| 			var dynValue: Bool = true; |  | ||||||
| 			if (dyn != null) { |  | ||||||
| 				dynValue = dyn; |  | ||||||
| 			} |  | ||||||
| 			dynamicEmitter = dynValue; |  | ||||||
| 			if (Scene.active.raw.gravity != null) { | 			if (Scene.active.raw.gravity != null) { | ||||||
| 				gx = Scene.active.raw.gravity[0] * r.weight_gravity; | 				gx = Scene.active.raw.gravity[0] * r.weight_gravity; | ||||||
| 				gy = Scene.active.raw.gravity[1] * r.weight_gravity; | 				gy = Scene.active.raw.gravity[1] * r.weight_gravity; | ||||||
| @ -87,73 +65,38 @@ class ParticleSystem { | |||||||
| 				gy = 0; | 				gy = 0; | ||||||
| 				gz = -9.81 * r.weight_gravity; | 				gz = -9.81 * r.weight_gravity; | ||||||
| 			} | 			} | ||||||
| 			alignx = r.object_align_factor[0]; | 			alignx = r.object_align_factor[0] / 2; | ||||||
| 			aligny = r.object_align_factor[1]; | 			aligny = r.object_align_factor[1] / 2; | ||||||
| 			alignz = r.object_align_factor[2]; | 			alignz = r.object_align_factor[2] / 2; | ||||||
| 			looptime = (r.frame_end - r.frame_start) / frameRate; |  | ||||||
| 			lifetime = r.lifetime / frameRate; | 			lifetime = r.lifetime / frameRate; | ||||||
| 			animtime = r.loop ? looptime : looptime + lifetime; | 			animtime = (r.frame_end - r.frame_start) / frameRate; | ||||||
| 			spawnRate = ((r.frame_end - r.frame_start) / r.count) / frameRate; | 			spawnRate = ((r.frame_end - r.frame_start) / r.count) / frameRate; | ||||||
|  |  | ||||||
| 			for (i in 0...r.count) { | 			for (i in 0...r.count) { | ||||||
| 				particles.push(new Particle(i)); | 				var particle = new Particle(i); | ||||||
|  | 				particle.sr = 1 - Math.random() * r.size_random; | ||||||
|  | 				particles.push(particle); | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
| 			ready = true; | 			ready = true; | ||||||
| 			if (r.auto_start){ |  | ||||||
| 				start(); |  | ||||||
| 			} |  | ||||||
| 		}); | 		}); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	public function start() { |  | ||||||
| 		if (r.is_unique) random = Math.random(); |  | ||||||
| 		lifetime = r.lifetime / frameRate; |  | ||||||
| 		time = 0; |  | ||||||
| 		lap = 0; |  | ||||||
| 		lapTime = 0; |  | ||||||
| 		speed = currentSpeed; |  | ||||||
| 		lastSpawnedCount = 0; |  | ||||||
| 		instancedData = null; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	public function pause() { | 	public function pause() { | ||||||
| 		speed = 0; | 		lifetime = 0; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	public function resume() { | 	public function resume() { | ||||||
| 		lifetime = r.lifetime / frameRate; | 		lifetime = r.lifetime / frameRate; | ||||||
| 		speed = currentSpeed; |  | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
| 	// TODO: interrupt smoothly |  | ||||||
| 	public function stop() { |  | ||||||
| 		end(); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	function end() { |  | ||||||
| 		lifetime = 0; |  | ||||||
| 		speed = 0; |  | ||||||
| 		lap = 0; |  | ||||||
| 	} |  | ||||||
| 	 |  | ||||||
| 	public function update(object: MeshObject, owner: MeshObject) { | 	public function update(object: MeshObject, owner: MeshObject) { | ||||||
| 		if (!ready || object == null || speed == 0.0) return; | 		if (!ready || object == null || speed == 0.0) return; | ||||||
| 		if (iron.App.pauseUpdates) return; |  | ||||||
| 		 |  | ||||||
| 		var prevLap = lap; |  | ||||||
|  |  | ||||||
| 		// Copy owner world transform but discard scale | 		// Copy owner world transform but discard scale | ||||||
| 		owner.transform.world.decompose(ownerLoc, ownerRot, ownerScl); | 		owner.transform.world.decompose(ownerLoc, ownerRot, ownerScl); | ||||||
| 		if (dynamicEmitter) { | 		object.transform.loc = ownerLoc; | ||||||
| 			object.transform.loc.x = 0; object.transform.loc.y = 0; object.transform.loc.z = 0; | 		object.transform.rot = ownerRot; | ||||||
| 			object.transform.rot = new Quat(); |  | ||||||
| 		} else { |  | ||||||
| 			object.transform.loc = ownerLoc; |  | ||||||
| 			object.transform.rot = ownerRot; |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		// Set particle size per particle system | 		// Set particle size per particle system | ||||||
| 		object.transform.scale = new Vec4(r.particle_size, r.particle_size, r.particle_size, 1); | 		object.transform.scale = new Vec4(r.particle_size, r.particle_size, r.particle_size, 1); | ||||||
| @ -172,25 +115,16 @@ class ParticleSystem { | |||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		// Animate | 		// Animate | ||||||
| 		time += Time.renderDelta * speed; // realDelta to renderDelta | 		time += Time.delta * speed; | ||||||
| 		lap = Std.int(time / animtime); | 		lap = Std.int(time / animtime); | ||||||
| 		lapTime = time - lap * animtime; | 		lapTime = time - lap * animtime; | ||||||
| 		count = Std.int(lapTime / spawnRate); | 		count = Std.int(lapTime / spawnRate); | ||||||
|  |  | ||||||
| 		if (lap > prevLap && !r.loop) { |  | ||||||
| 			end(); |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		if (lap > prevLap && r.loop) { |  | ||||||
| 			lastSpawnedCount = 0; |  | ||||||
| 		} |  | ||||||
| 		 |  | ||||||
| 		updateGpu(object, owner); | 		updateGpu(object, owner); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	public function getData(): Mat4 { | 	public function getData(): Mat4 { | ||||||
| 		var hair = r.type == 1; | 		var hair = r.type == 1; | ||||||
| 		// Store loop flag in the sign: positive -> loop, negative -> no loop |  | ||||||
| 		m._00 = r.loop ? animtime : -animtime; | 		m._00 = r.loop ? animtime : -animtime; | ||||||
| 		m._01 = hair ? 1 / particles.length : spawnRate; | 		m._01 = hair ? 1 / particles.length : spawnRate; | ||||||
| 		m._02 = hair ? 1 : lifetime; | 		m._02 = hair ? 1 : lifetime; | ||||||
| @ -199,9 +133,9 @@ class ParticleSystem { | |||||||
| 		m._11 = hair ? 0 : aligny; | 		m._11 = hair ? 0 : aligny; | ||||||
| 		m._12 = hair ? 0 : alignz; | 		m._12 = hair ? 0 : alignz; | ||||||
| 		m._13 = hair ? 0 : r.factor_random; | 		m._13 = hair ? 0 : r.factor_random; | ||||||
| 		m._20 = hair ? 0 : gx; | 		m._20 = hair ? 0 : gx * r.mass; | ||||||
| 		m._21 = hair ? 0 : gy; | 		m._21 = hair ? 0 : gy * r.mass; | ||||||
| 		m._22 = hair ? 0 : gz; | 		m._22 = hair ? 0 : gz * r.mass; | ||||||
| 		m._23 = hair ? 0 : r.lifetime_random; | 		m._23 = hair ? 0 : r.lifetime_random; | ||||||
| 		m._30 = tilesx; | 		m._30 = tilesx; | ||||||
| 		m._31 = tilesy; | 		m._31 = tilesy; | ||||||
| @ -210,34 +144,13 @@ class ParticleSystem { | |||||||
| 		return m; | 		return m; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	public function getSizeRandom(): FastFloat { |  | ||||||
| 		return r.size_random; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	public inline function getRandom(): FastFloat { |  | ||||||
| 		return random; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	public inline function getSize(): FastFloat { |  | ||||||
| 		return r.particle_size; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	function updateGpu(object: MeshObject, owner: MeshObject) { | 	function updateGpu(object: MeshObject, owner: MeshObject) { | ||||||
| 		if (dynamicEmitter) { | 		if (!object.data.geom.instanced) setupGeomGpu(object, owner); | ||||||
| 			if (!hasUniqueGeom) ensureUniqueGeom(object); | 		// GPU particles transform is attached to owner object | ||||||
| 			var needSetup = instancedData == null || object.data.geom.instancedVB == null; |  | ||||||
| 			if (needSetup) setupGeomGpuDynamic(object, owner); |  | ||||||
| 			updateSpawnedInstances(object, owner); |  | ||||||
| 		} |  | ||||||
| 		else { |  | ||||||
| 			if (!hasUniqueGeom) ensureUniqueGeom(object); |  | ||||||
| 			if (!object.data.geom.instanced) setupGeomGpu(object, owner); |  | ||||||
| 		} |  | ||||||
| 		// GPU particles transform is attached to owner object in static mode |  | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	function setupGeomGpu(object: MeshObject, owner: MeshObject) { | 	function setupGeomGpu(object: MeshObject, owner: MeshObject) { | ||||||
| 		var instancedData = new Float32Array(particles.length * 3); | 		var instancedData = new Float32Array(particles.length * 6); | ||||||
| 		var i = 0; | 		var i = 0; | ||||||
|  |  | ||||||
| 		var normFactor = 1 / 32767; // pa.values are not normalized | 		var normFactor = 1 / 32767; // pa.values are not normalized | ||||||
| @ -256,6 +169,10 @@ class ParticleSystem { | |||||||
| 					instancedData.set(i, pa.values[j * pa.size    ] * normFactor * scaleFactor.x); i++; | 					instancedData.set(i, pa.values[j * pa.size    ] * normFactor * scaleFactor.x); i++; | ||||||
| 					instancedData.set(i, pa.values[j * pa.size + 1] * normFactor * scaleFactor.y); i++; | 					instancedData.set(i, pa.values[j * pa.size + 1] * normFactor * scaleFactor.y); i++; | ||||||
| 					instancedData.set(i, pa.values[j * pa.size + 2] * normFactor * scaleFactor.z); i++; | 					instancedData.set(i, pa.values[j * pa.size + 2] * normFactor * scaleFactor.z); i++; | ||||||
|  |  | ||||||
|  | 					instancedData.set(i, p.sr); i++; | ||||||
|  | 					instancedData.set(i, p.sr); i++; | ||||||
|  | 					instancedData.set(i, p.sr); i++; | ||||||
| 				} | 				} | ||||||
|  |  | ||||||
| 			case 1: // Face | 			case 1: // Face | ||||||
| @ -279,6 +196,10 @@ class ParticleSystem { | |||||||
| 					instancedData.set(i, pos.x * normFactor * scaleFactor.x); i++; | 					instancedData.set(i, pos.x * normFactor * scaleFactor.x); i++; | ||||||
| 					instancedData.set(i, pos.y * normFactor * scaleFactor.y); i++; | 					instancedData.set(i, pos.y * normFactor * scaleFactor.y); i++; | ||||||
| 					instancedData.set(i, pos.z * normFactor * scaleFactor.z); i++; | 					instancedData.set(i, pos.z * normFactor * scaleFactor.z); i++; | ||||||
|  |  | ||||||
|  | 					instancedData.set(i, p.sr); i++; | ||||||
|  | 					instancedData.set(i, p.sr); i++; | ||||||
|  | 					instancedData.set(i, p.sr); i++; | ||||||
| 				} | 				} | ||||||
|  |  | ||||||
| 			case 2: // Volume | 			case 2: // Volume | ||||||
| @ -289,139 +210,27 @@ class ParticleSystem { | |||||||
| 					instancedData.set(i, (Math.random() * 2.0 - 1.0) * scaleFactorVolume.x); i++; | 					instancedData.set(i, (Math.random() * 2.0 - 1.0) * scaleFactorVolume.x); i++; | ||||||
| 					instancedData.set(i, (Math.random() * 2.0 - 1.0) * scaleFactorVolume.y); i++; | 					instancedData.set(i, (Math.random() * 2.0 - 1.0) * scaleFactorVolume.y); i++; | ||||||
| 					instancedData.set(i, (Math.random() * 2.0 - 1.0) * scaleFactorVolume.z); i++; | 					instancedData.set(i, (Math.random() * 2.0 - 1.0) * scaleFactorVolume.z); i++; | ||||||
|  |  | ||||||
|  | 					instancedData.set(i, p.sr); i++; | ||||||
|  | 					instancedData.set(i, p.sr); i++; | ||||||
|  | 					instancedData.set(i, p.sr); i++; | ||||||
| 				} | 				} | ||||||
| 		} | 		} | ||||||
| 		object.data.geom.setupInstanced(instancedData, 1, Usage.StaticUsage); | 		object.data.geom.setupInstanced(instancedData, 3, Usage.StaticUsage); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	// allocate instanced VB once for this object  | 	function fhash(n: Int): Float { | ||||||
| 	function setupGeomGpuDynamic(object: MeshObject, owner: MeshObject) { | 		var s = n + 1.0; | ||||||
| 		if (instancedData == null) instancedData = new Float32Array(particles.length * 3); | 		s *= 9301.0 % s; | ||||||
| 		lastSpawnedCount = 0;  | 		s = (s * 9301.0 + 49297.0) % 233280.0; | ||||||
| 		// Create instanced VB once if missing (seed with our instancedData) | 		return s / 233280.0; | ||||||
| 		if (object.data.geom.instancedVB == null) { |  | ||||||
| 			object.data.geom.setupInstanced(instancedData, 1, Usage.DynamicUsage); |  | ||||||
| 		} |  | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	function ensureUniqueGeom(object: MeshObject) { |  | ||||||
| 		if (hasUniqueGeom) return; |  | ||||||
| 		var newData: MeshData = null; |  | ||||||
| 		new MeshData(object.data.raw, function(dat: MeshData) { |  | ||||||
| 			dat.scalePos = object.data.scalePos; |  | ||||||
| 			dat.scaleTex = object.data.scaleTex; |  | ||||||
| 			dat.format = object.data.format; |  | ||||||
| 			newData = dat; |  | ||||||
| 		}); |  | ||||||
| 		if (newData != null) object.setData(newData); |  | ||||||
| 		hasUniqueGeom = true; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	function updateSpawnedInstances(object: MeshObject, owner: MeshObject) { |  | ||||||
| 		if (instancedData == null) return; |  | ||||||
| 		var targetCount = count; |  | ||||||
| 		if (targetCount > particles.length) targetCount = particles.length; |  | ||||||
| 		if (targetCount <= lastSpawnedCount) return; |  | ||||||
|  |  | ||||||
| 		var normFactor = 1 / 32767; |  | ||||||
| 		var scalePosOwner = owner.data.scalePos; |  | ||||||
| 		var scalePosParticle = object.data.scalePos; |  | ||||||
| 		var particleSize = r.particle_size; |  | ||||||
| 		var base = 1.0 / (particleSize * scalePosParticle); |  | ||||||
|  |  | ||||||
| 		switch (r.emit_from) { |  | ||||||
| 			case 0: // Vert |  | ||||||
| 				var pa = owner.data.geom.positions; |  | ||||||
| 				var osx = owner.transform.scale.x; |  | ||||||
| 				var osy = owner.transform.scale.y; |  | ||||||
| 				var osz = owner.transform.scale.z; |  | ||||||
| 				var pCount = Std.int(pa.values.length / pa.size); |  | ||||||
| 				for (idx in lastSpawnedCount...targetCount) { |  | ||||||
| 					var j = Std.int(fhash(idx) * pCount); |  | ||||||
| 					var lx = pa.values[j * pa.size    ] * normFactor * scalePosOwner * osx; |  | ||||||
| 					var ly = pa.values[j * pa.size + 1] * normFactor * scalePosOwner * osy; |  | ||||||
| 					var lz = pa.values[j * pa.size + 2] * normFactor * scalePosOwner * osz; |  | ||||||
| 					tmpV4.x = lx; tmpV4.y = ly; tmpV4.z = lz; tmpV4.w = 1; |  | ||||||
| 					tmpV4.applyQuat(ownerRot); |  | ||||||
| 					var o = idx * 3; |  | ||||||
| 					instancedData.set(o    , (tmpV4.x + ownerLoc.x) * base); |  | ||||||
| 					instancedData.set(o + 1, (tmpV4.y + ownerLoc.y) * base); |  | ||||||
| 					instancedData.set(o + 2, (tmpV4.z + ownerLoc.z) * base); |  | ||||||
| 				} |  | ||||||
|  |  | ||||||
| 			case 1: // Face |  | ||||||
| 				var positions = owner.data.geom.positions.values; |  | ||||||
| 				var osx1 = owner.transform.scale.x; |  | ||||||
| 				var osy1 = owner.transform.scale.y; |  | ||||||
| 				var osz1 = owner.transform.scale.z; |  | ||||||
| 				for (idx in lastSpawnedCount...targetCount) { |  | ||||||
| 					var ia = owner.data.geom.indices[Std.random(owner.data.geom.indices.length)]; |  | ||||||
| 					var faceIndex = Std.random(Std.int(ia.length / 3)); |  | ||||||
| 					var i0 = ia[faceIndex * 3 + 0]; |  | ||||||
| 					var i1 = ia[faceIndex * 3 + 1]; |  | ||||||
| 					var i2 = ia[faceIndex * 3 + 2]; |  | ||||||
| 					var v0x = positions[i0 * 4    ], v0y = positions[i0 * 4 + 1], v0z = positions[i0 * 4 + 2]; |  | ||||||
| 					var v1x = positions[i1 * 4    ], v1y = positions[i1 * 4 + 1], v1z = positions[i1 * 4 + 2]; |  | ||||||
| 					var v2x = positions[i2 * 4    ], v2y = positions[i2 * 4 + 1], v2z = positions[i2 * 4 + 2]; |  | ||||||
| 					var rx = Math.random(); var ry = Math.random(); if (rx + ry > 1) { rx = 1 - rx; ry = 1 - ry; } |  | ||||||
| 					var pxs = v0x + rx * (v1x - v0x) + ry * (v2x - v0x); |  | ||||||
| 					var pys = v0y + rx * (v1y - v0y) + ry * (v2y - v0y); |  | ||||||
| 					var pzs = v0z + rx * (v1z - v0z) + ry * (v2z - v0z); |  | ||||||
| 					var px = pxs * normFactor * scalePosOwner * osx1; |  | ||||||
| 					var py = pys * normFactor * scalePosOwner * osy1; |  | ||||||
| 					var pz = pzs * normFactor * scalePosOwner * osz1; |  | ||||||
| 					tmpV4.x = px; tmpV4.y = py; tmpV4.z = pz; tmpV4.w = 1; |  | ||||||
| 					tmpV4.applyQuat(ownerRot); |  | ||||||
| 					var o1 = idx * 3; |  | ||||||
| 					instancedData.set(o1    , (tmpV4.x + ownerLoc.x) * base); |  | ||||||
| 					instancedData.set(o1 + 1, (tmpV4.y + ownerLoc.y) * base); |  | ||||||
| 					instancedData.set(o1 + 2, (tmpV4.z + ownerLoc.z) * base); |  | ||||||
| 				} |  | ||||||
|  |  | ||||||
| 			case 2: // Volume |  | ||||||
| 				var dim = object.transform.dim; |  | ||||||
| 				for (idx in lastSpawnedCount...targetCount) { |  | ||||||
| 					tmpV4.x = (Math.random() * 2.0 - 1.0) * (dim.x * 0.5); |  | ||||||
| 					tmpV4.y = (Math.random() * 2.0 - 1.0) * (dim.y * 0.5); |  | ||||||
| 					tmpV4.z = (Math.random() * 2.0 - 1.0) * (dim.z * 0.5); |  | ||||||
| 					tmpV4.w = 1; |  | ||||||
| 					tmpV4.applyQuat(ownerRot); |  | ||||||
| 					var o2 = idx * 3; |  | ||||||
| 					instancedData.set(o2    , (tmpV4.x + ownerLoc.x) * base); |  | ||||||
| 					instancedData.set(o2 + 1, (tmpV4.y + ownerLoc.y) * base); |  | ||||||
| 					instancedData.set(o2 + 2, (tmpV4.z + ownerLoc.z) * base); |  | ||||||
| 				} |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		// Upload full active range [0..targetCount) to this object's instanced VB |  | ||||||
| 		var geom = object.data.geom; |  | ||||||
| 		if (geom.instancedVB == null) { |  | ||||||
| 			geom.setupInstanced(instancedData, 1, Usage.DynamicUsage); |  | ||||||
| 		} |  | ||||||
| 		var vb = geom.instancedVB.lock(); |  | ||||||
| 		var totalFloats = targetCount * 3; // xyz per instance |  | ||||||
| 		var i = 0; |  | ||||||
| 		while (i < totalFloats) { |  | ||||||
| 			vb.setFloat32(i * 4, instancedData[i]); |  | ||||||
| 			i++; |  | ||||||
| 		} |  | ||||||
| 		geom.instancedVB.unlock(); |  | ||||||
| 		geom.instanceCount = targetCount; |  | ||||||
| 		lastSpawnedCount = targetCount; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	inline function fhash(n: Int): Float { |  | ||||||
|     var s = n + 1.0; |  | ||||||
|     s *= 9301.0 % s; |  | ||||||
|     s = (s * 9301.0 + 49297.0) % 233280.0; |  | ||||||
|     return s / 233280.0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| 	public function remove() {} | 	public function remove() {} | ||||||
|  |  | ||||||
| 	/** | 	/** | ||||||
| 		Generates a random point in the triangle with vertex positions abc. | 		Generates a random point in the triangle with vertex positions abc. | ||||||
| 		 |  | ||||||
| 		Please note that the given position vectors are changed in-place by this | 		Please note that the given position vectors are changed in-place by this | ||||||
| 		function and can be considered garbage afterwards, so make sure to clone | 		function and can be considered garbage afterwards, so make sure to clone | ||||||
| 		them first if needed. | 		them first if needed. | ||||||
| @ -446,11 +255,10 @@ class ParticleSystem { | |||||||
|  |  | ||||||
| class Particle { | class Particle { | ||||||
| 	public var i: Int; | 	public var i: Int; | ||||||
| 	 | 	public var px = 0.0; | ||||||
| 	public var x = 0.0; | 	public var py = 0.0; | ||||||
| 	public var y = 0.0; | 	public var pz = 0.0; | ||||||
| 	public var z = 0.0; | 	public var sr = 1.0; // Size random | ||||||
|  |  | ||||||
| 	public var cameraDistance: Float; | 	public var cameraDistance: Float; | ||||||
|  |  | ||||||
| 	public function new(i: Int) { | 	public function new(i: Int) { | ||||||
|  | |||||||
| @ -80,7 +80,7 @@ class Tilesheet { | |||||||
| 	function update() { | 	function update() { | ||||||
| 		if (!ready || paused || action.start >= action.end) return; | 		if (!ready || paused || action.start >= action.end) return; | ||||||
|  |  | ||||||
| 		time += Time.renderDelta; | 		time += Time.realDelta; | ||||||
|  |  | ||||||
| 		var frameTime = 1 / raw.framerate; | 		var frameTime = 1 / raw.framerate; | ||||||
| 		var framesToAdvance = 0; | 		var framesToAdvance = 0; | ||||||
|  | |||||||
| @ -1109,26 +1109,6 @@ class Uniforms { | |||||||
| 				case "_texUnpack": { | 				case "_texUnpack": { | ||||||
| 					f = texUnpack != null ? texUnpack : 1.0; | 					f = texUnpack != null ? texUnpack : 1.0; | ||||||
| 				} | 				} | ||||||
| 				#if lnx_particles |  | ||||||
| 				case "_particleSizeRandom": { |  | ||||||
| 					var mo = cast(object, MeshObject); |  | ||||||
| 					if (mo.particleOwner != null && mo.particleOwner.particleSystems != null) { |  | ||||||
| 						f = mo.particleOwner.particleSystems[mo.particleIndex].getSizeRandom(); |  | ||||||
| 					} |  | ||||||
| 				} |  | ||||||
| 				case "_particleRandom": { |  | ||||||
| 					var mo = cast(object, MeshObject); |  | ||||||
| 					if (mo.particleOwner != null && mo.particleOwner.particleSystems != null) { |  | ||||||
| 						f = mo.particleOwner.particleSystems[mo.particleIndex].getRandom(); |  | ||||||
| 					} |  | ||||||
| 				} |  | ||||||
| 				case "_particleSize": { |  | ||||||
| 					var mo = cast(object, MeshObject); |  | ||||||
| 					if (mo.particleOwner != null && mo.particleOwner.particleSystems != null) { |  | ||||||
| 						f = mo.particleOwner.particleSystems[mo.particleIndex].getSize(); |  | ||||||
| 					} |  | ||||||
| 				} |  | ||||||
| 				#end |  | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
| 			if (f == null && externalFloatLinks != null) { | 			if (f == null && externalFloatLinks != null) { | ||||||
|  | |||||||
| @ -1,58 +1,37 @@ | |||||||
| package iron.system; | package iron.system; | ||||||
|  |  | ||||||
| class Time { | class Time { | ||||||
| 	public static var scale = 1.0; |  | ||||||
| 	 |  | ||||||
| 	static var frequency: Null<Int> = null; |  | ||||||
| 	static function initFrequency() { |  | ||||||
| 		frequency = kha.Display.primary != null ? kha.Display.primary.frequency : 60; |  | ||||||
| 	} |  | ||||||
| 	 |  | ||||||
| 	public static var step(get, never): Float; | 	public static var step(get, never): Float; | ||||||
| 	static function get_step(): Float { | 	static function get_step(): Float { | ||||||
| 		if (frequency == null) initFrequency(); | 		if (frequency == null) initFrequency(); | ||||||
| 		return 1 / frequency; | 		return 1 / frequency; | ||||||
| 	} | 	} | ||||||
| 	 |  | ||||||
| 	static var _fixedStep: Null<Float> = 1/60; |  | ||||||
| 	public static var fixedStep(get, never): Float; |  | ||||||
| 	static function get_fixedStep(): Float { |  | ||||||
| 		return _fixedStep; |  | ||||||
| 	} |  | ||||||
| 	 |  | ||||||
| 	public static function initFixedStep(value: Float = 1 / 60) { |  | ||||||
| 		_fixedStep = value; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	static var lastTime = 0.0; | 	public static var scale = 1.0; | ||||||
| 	static var _delta = 0.0; |  | ||||||
| 	public static var delta(get, never): Float; | 	public static var delta(get, never): Float; | ||||||
| 	static function get_delta(): Float { | 	static function get_delta(): Float { | ||||||
| 		return _delta; | 		if (frequency == null) initFrequency(); | ||||||
|  | 		return (1 / frequency) * scale; | ||||||
| 	} | 	} | ||||||
| 	 |  | ||||||
| 	static var lastRenderTime = 0.0; | 	static var last = 0.0; | ||||||
| 	static var _renderDelta = 0.0; | 	public static var realDelta = 0.0; | ||||||
| 	public static var renderDelta(get, never): Float; |  | ||||||
| 	static function get_renderDelta(): Float { |  | ||||||
| 		return _renderDelta; |  | ||||||
| 	} |  | ||||||
| 		 |  | ||||||
| 	public static inline function time(): Float { | 	public static inline function time(): Float { | ||||||
| 		return kha.Scheduler.time() * scale; | 		return kha.Scheduler.time(); | ||||||
| 	} | 	} | ||||||
| 	 |  | ||||||
| 	public static inline function realTime(): Float { | 	public static inline function realTime(): Float { | ||||||
| 		return kha.Scheduler.realTime() * scale; | 		return kha.Scheduler.realTime(); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	static var frequency: Null<Int> = null; | ||||||
|  |  | ||||||
|  | 	static function initFrequency() { | ||||||
|  | 		frequency = kha.Display.primary != null ? kha.Display.primary.frequency : 60; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	public static function update() { | 	public static function update() { | ||||||
| 		_delta = realTime() - lastTime; | 		realDelta = realTime() - last; | ||||||
| 		lastTime = realTime(); | 		last = realTime(); | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	public static function render() { |  | ||||||
| 		_renderDelta = realTime() - lastRenderTime; |  | ||||||
| 		lastRenderTime = realTime(); |  | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  | |||||||
| @ -94,34 +94,34 @@ class Tween { | |||||||
|  |  | ||||||
| 				// Way too much Reflect trickery.. | 				// Way too much Reflect trickery.. | ||||||
| 				var ps = Reflect.fields(a.props); | 				var ps = Reflect.fields(a.props); | ||||||
| 				for (j in 0...ps.length) { | 				for (i in 0...ps.length) { | ||||||
| 					var p = ps[j]; | 					var p = ps[i]; | ||||||
| 					var k = a._time / a.duration; | 					var k = a._time / a.duration; | ||||||
| 					if (k > 1) k = 1; | 					if (k > 1) k = 1; | ||||||
|  |  | ||||||
| 					if (a._comps[j] == 1) { | 					if (a._comps[i] == 1) { | ||||||
| 						var fromVal: Float = a._x[j]; | 						var fromVal: Float = a._x[i]; | ||||||
| 						var toVal: Float = Reflect.getProperty(a.props, p); | 						var toVal: Float = Reflect.getProperty(a.props, p); | ||||||
| 						var val: Float = fromVal + (toVal - fromVal) * eases[a.ease](k); | 						var val: Float = fromVal + (toVal - fromVal) * eases[a.ease](k); | ||||||
| 						Reflect.setProperty(a.target, p, val); | 						Reflect.setProperty(a.target, p, val); | ||||||
| 					} | 					} | ||||||
| 					else { // _comps[j] == 4 | 					else { // _comps[i] == 4 | ||||||
| 						var obj = Reflect.getProperty(a.props, p); | 						var obj = Reflect.getProperty(a.props, p); | ||||||
| 						var toX: Float = Reflect.getProperty(obj, "x"); | 						var toX: Float = Reflect.getProperty(obj, "x"); | ||||||
| 						var toY: Float = Reflect.getProperty(obj, "y"); | 						var toY: Float = Reflect.getProperty(obj, "y"); | ||||||
| 						var toZ: Float = Reflect.getProperty(obj, "z"); | 						var toZ: Float = Reflect.getProperty(obj, "z"); | ||||||
| 						var toW: Float = Reflect.getProperty(obj, "w"); | 						var toW: Float = Reflect.getProperty(obj, "w"); | ||||||
| 						if (a._normalize[j]) { | 						if (a._normalize[i]) { | ||||||
| 							var qdot = (a._x[j] * toX) + (a._y[j] * toY) + (a._z[j] * toZ) + (a._w[j] * toW); | 							var qdot = (a._x[i] * toX) + (a._y[i] * toY) + (a._z[i] * toZ) + (a._w[i] * toW); | ||||||
| 							if (qdot < 0.0) { | 							if (qdot < 0.0) { | ||||||
| 								toX = -toX; toY = -toY; toZ = -toZ; toW = -toW; | 								toX = -toX; toY = -toY; toZ = -toZ; toW = -toW; | ||||||
| 							} | 							} | ||||||
| 						} | 						} | ||||||
| 						var x: Float = a._x[j] + (toX - a._x[j]) * eases[a.ease](k); | 						var x: Float = a._x[i] + (toX - a._x[i]) * eases[a.ease](k); | ||||||
| 						var y: Float = a._y[j] + (toY - a._y[j]) * eases[a.ease](k); | 						var y: Float = a._y[i] + (toY - a._y[i]) * eases[a.ease](k); | ||||||
| 						var z: Float = a._z[j] + (toZ - a._z[j]) * eases[a.ease](k); | 						var z: Float = a._z[i] + (toZ - a._z[i]) * eases[a.ease](k); | ||||||
| 						var w: Float = a._w[j] + (toW - a._w[j]) * eases[a.ease](k); | 						var w: Float = a._w[i] + (toW - a._w[i]) * eases[a.ease](k); | ||||||
| 						if (a._normalize[j]) { | 						if (a._normalize[i]) { | ||||||
| 							var l = Math.sqrt(x * x + y * y + z * z + w * w); | 							var l = Math.sqrt(x * x + y * y + z * z + w * w); | ||||||
| 							if (l > 0.0) { | 							if (l > 0.0) { | ||||||
| 								l = 1.0 / l; | 								l = 1.0 / l; | ||||||
|  | |||||||
| @ -1,52 +0,0 @@ | |||||||
| 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,7 +20,6 @@ class Config { | |||||||
| 		var path = iron.data.Data.dataPath + "config.lnx"; | 		var path = iron.data.Data.dataPath + "config.lnx"; | ||||||
| 		var bytes = haxe.io.Bytes.ofString(haxe.Json.stringify(raw)); | 		var bytes = haxe.io.Bytes.ofString(haxe.Json.stringify(raw)); | ||||||
| 		#if kha_krom | 		#if kha_krom | ||||||
| 		if (iron.data.Data.dataPath == '') path = Krom.getFilesLocation() + "/config.lnx"; |  | ||||||
| 		Krom.fileSaveBytes(path, bytes.getData()); | 		Krom.fileSaveBytes(path, bytes.getData()); | ||||||
| 		#elseif kha_kore | 		#elseif kha_kore | ||||||
| 		sys.io.File.saveBytes(path, bytes); | 		sys.io.File.saveBytes(path, bytes); | ||||||
| @ -48,7 +47,6 @@ typedef TConfig = { | |||||||
| 	@:optional var rp_ssr: Null<Bool>; | 	@:optional var rp_ssr: Null<Bool>; | ||||||
| 	@:optional var rp_ssrefr: Null<Bool>; | 	@:optional var rp_ssrefr: Null<Bool>; | ||||||
| 	@:optional var rp_bloom: Null<Bool>; | 	@:optional var rp_bloom: Null<Bool>; | ||||||
| 	@:optional var rp_chromatic_aberration: Null<Bool>; |  | ||||||
| 	@:optional var rp_motionblur: Null<Bool>; | 	@:optional var rp_motionblur: Null<Bool>; | ||||||
| 	@:optional var rp_gi: Null<Bool>; // voxelao | 	@:optional var rp_gi: Null<Bool>; // voxelao | ||||||
| 	@:optional var rp_dynres: Null<Bool>; // dynamic resolution scaling | 	@:optional var rp_dynres: Null<Bool>; // dynamic resolution scaling | ||||||
|  | |||||||
| @ -1,99 +0,0 @@ | |||||||
| 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); |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| @ -1,48 +0,0 @@ | |||||||
| 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); |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| @ -1,16 +0,0 @@ | |||||||
| 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; |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| @ -1,15 +0,0 @@ | |||||||
| 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,49 +1,26 @@ | |||||||
| package leenkx.logicnode; | package leenkx.logicnode; | ||||||
|  |  | ||||||
| class CameraSetNode extends LogicNode { | class CameraSetNode extends LogicNode { | ||||||
| 	 |  | ||||||
| 	public var property0: String; |  | ||||||
| 	 |  | ||||||
| 	public function new(tree:LogicTree) { | 	public function new(tree:LogicTree) { | ||||||
| 		super(tree); | 		super(tree); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	override function run(from:Int) { | 	override function run(from:Int) { | ||||||
|  | 		leenkx.renderpath.Postprocess.camera_uniforms[0] = inputs[1].get();//Camera: F-Number | ||||||
| 		switch (property0) { | 		leenkx.renderpath.Postprocess.camera_uniforms[1] = inputs[2].get();//Camera: Shutter time | ||||||
| 			case 'F-stop': | 		leenkx.renderpath.Postprocess.camera_uniforms[2] = inputs[3].get();//Camera: ISO | ||||||
| 				leenkx.renderpath.Postprocess.camera_uniforms[0] = inputs[1].get();//Camera: F-Number | 		leenkx.renderpath.Postprocess.camera_uniforms[3] = inputs[4].get();//Camera: Exposure Compensation | ||||||
| 			case 'Shutter Time':	 | 		leenkx.renderpath.Postprocess.camera_uniforms[4] = inputs[5].get();//Fisheye Distortion | ||||||
| 				leenkx.renderpath.Postprocess.camera_uniforms[1] = inputs[1].get();//Camera: Shutter time | 		leenkx.renderpath.Postprocess.camera_uniforms[5] = inputs[6].get();//DoF AutoFocus §§ If true, it ignores the DoF Distance setting | ||||||
| 			case 'ISO': | 		leenkx.renderpath.Postprocess.camera_uniforms[6] = inputs[7].get();//DoF Distance | ||||||
| 				leenkx.renderpath.Postprocess.camera_uniforms[2] = inputs[1].get();//Camera: ISO | 		leenkx.renderpath.Postprocess.camera_uniforms[7] = inputs[8].get();//DoF Focal Length mm | ||||||
| 			case 'Exposure Compensation': | 		leenkx.renderpath.Postprocess.camera_uniforms[8] = inputs[9].get();//DoF F-Stop | ||||||
| 				leenkx.renderpath.Postprocess.camera_uniforms[3] = inputs[1].get();//Camera: Exposure Compensation | 		leenkx.renderpath.Postprocess.camera_uniforms[9] = inputs[10].get();//Tonemapping Method | ||||||
| 			case 'Fisheye Distortion': | 		leenkx.renderpath.Postprocess.camera_uniforms[10] = inputs[11].get();//Distort | ||||||
| 				leenkx.renderpath.Postprocess.camera_uniforms[4] = inputs[1].get();//Fisheye Distortion | 		leenkx.renderpath.Postprocess.camera_uniforms[11] = inputs[12].get();//Film Grain | ||||||
| 			case 'Auto Focus': | 		leenkx.renderpath.Postprocess.camera_uniforms[12] = inputs[13].get();//Sharpen | ||||||
| 				leenkx.renderpath.Postprocess.camera_uniforms[5] = inputs[1].get();//DoF AutoFocus §§ If true, it ignores the DoF Distance setting | 		leenkx.renderpath.Postprocess.camera_uniforms[13] = inputs[14].get();//Vignette | ||||||
| 			case 'DoF Distance': |  | ||||||
| 				leenkx.renderpath.Postprocess.camera_uniforms[6] = inputs[1].get();//DoF Distance |  | ||||||
| 			case 'DoF Length': |  | ||||||
| 				leenkx.renderpath.Postprocess.camera_uniforms[7] = inputs[1].get();//DoF Focal Length mm |  | ||||||
| 			case 'DoF F-Stop': |  | ||||||
| 				leenkx.renderpath.Postprocess.camera_uniforms[8] = inputs[1].get();//DoF F-Stop |  | ||||||
| 			case 'Tonemapping': |  | ||||||
| 				leenkx.renderpath.Postprocess.camera_uniforms[9] = inputs[1].get();//Tonemapping Method |  | ||||||
| 			case 'Distort': |  | ||||||
| 				leenkx.renderpath.Postprocess.camera_uniforms[10] = inputs[1].get();//Distort |  | ||||||
| 			case 'Film Grain': |  | ||||||
| 				leenkx.renderpath.Postprocess.camera_uniforms[11] = inputs[1].get();//Film Grain |  | ||||||
| 			case 'Sharpen': |  | ||||||
| 				leenkx.renderpath.Postprocess.camera_uniforms[12] = inputs[1].get();//Sharpen |  | ||||||
| 			case 'Vignette': |  | ||||||
| 				leenkx.renderpath.Postprocess.camera_uniforms[13] = inputs[1].get();//Vignette |  | ||||||
| 			case 'Exposure': |  | ||||||
| 				leenkx.renderpath.Postprocess.exposure_uniforms[0] = inputs[1].get();//Exposure |  | ||||||
| 			default:  |  | ||||||
| 				null; |  | ||||||
| 			} |  | ||||||
|  |  | ||||||
| 		runOutput(0); | 		runOutput(0); | ||||||
| 	} | 	} | ||||||
|  | |||||||
| @ -10,7 +10,6 @@ class ChromaticAberrationGetNode extends LogicNode { | |||||||
| 		return switch (from) { | 		return switch (from) { | ||||||
| 			case 0: leenkx.renderpath.Postprocess.chromatic_aberration_uniforms[0]; | 			case 0: leenkx.renderpath.Postprocess.chromatic_aberration_uniforms[0]; | ||||||
| 			case 1: leenkx.renderpath.Postprocess.chromatic_aberration_uniforms[1]; | 			case 1: leenkx.renderpath.Postprocess.chromatic_aberration_uniforms[1]; | ||||||
| 			case 2: leenkx.renderpath.Postprocess.chromatic_aberration_uniforms[2]; |  | ||||||
| 			default: 0.0; | 			default: 0.0; | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  | |||||||
| @ -10,7 +10,6 @@ class ChromaticAberrationSetNode extends LogicNode { | |||||||
|  |  | ||||||
|         leenkx.renderpath.Postprocess.chromatic_aberration_uniforms[0] = inputs[1].get(); |         leenkx.renderpath.Postprocess.chromatic_aberration_uniforms[0] = inputs[1].get(); | ||||||
|         leenkx.renderpath.Postprocess.chromatic_aberration_uniforms[1] = inputs[2].get(); |         leenkx.renderpath.Postprocess.chromatic_aberration_uniforms[1] = inputs[2].get(); | ||||||
| 		leenkx.renderpath.Postprocess.chromatic_aberration_uniforms[2] = inputs[3].get(); |  | ||||||
|  |  | ||||||
| 		runOutput(0); | 		runOutput(0); | ||||||
| 	} | 	} | ||||||
|  | |||||||
| @ -2,9 +2,6 @@ package leenkx.logicnode; | |||||||
|  |  | ||||||
| import iron.Scene; | import iron.Scene; | ||||||
| import iron.object.CameraObject; | import iron.object.CameraObject; | ||||||
| import iron.math.Vec4; |  | ||||||
| import iron.math.Quat; |  | ||||||
| import leenkx.math.Helper; |  | ||||||
|  |  | ||||||
| import leenkx.renderpath.RenderPathCreator; | import leenkx.renderpath.RenderPathCreator; | ||||||
|  |  | ||||||
| @ -30,19 +27,11 @@ class DrawCameraTextureNode extends LogicNode { | |||||||
| 				final c = inputs[2].get(); | 				final c = inputs[2].get(); | ||||||
| 				assert(Error, Std.isOfType(c, CameraObject), "Camera must be a camera object!"); | 				assert(Error, Std.isOfType(c, CameraObject), "Camera must be a camera object!"); | ||||||
| 				cam = cast(c, CameraObject); | 				cam = cast(c, CameraObject); | ||||||
| 				rt = kha.Image.createRenderTarget(iron.App.w(), iron.App.h(), | 				rt = kha.Image.createRenderTarget(iron.App.w(), iron.App.h()); | ||||||
| 					kha.graphics4.TextureFormat.RGBA32, |  | ||||||
| 					kha.graphics4.DepthStencilFormat.NoDepthAndStencil); |  | ||||||
|  |  | ||||||
| 				assert(Error, mo.materials[matSlot].contexts[0].textures != null, 'Object "${mo.name}" has no diffuse texture to render to'); | 				assert(Error, mo.materials[matSlot].contexts[0].textures != null, 'Object "${mo.name}" has no diffuse texture to render to'); | ||||||
|  | 				mo.materials[matSlot].contexts[0].textures[0] = rt; // Override diffuse texture | ||||||
|  |  | ||||||
|                 final n = inputs[5].get(); |  | ||||||
| 				for (i => node in mo.materials[matSlot].contexts[0].raw.bind_textures){ |  | ||||||
| 					if (node.name == n){ |  | ||||||
| 						mo.materials[matSlot].contexts[0].textures[i] = rt; // Override diffuse texture |  | ||||||
| 						break; |  | ||||||
| 					} |  | ||||||
| 				} |  | ||||||
| 				tree.notifyOnRender(render); | 				tree.notifyOnRender(render); | ||||||
| 				runOutput(0); | 				runOutput(0); | ||||||
|  |  | ||||||
| @ -59,20 +48,8 @@ class DrawCameraTextureNode extends LogicNode { | |||||||
| 		iron.Scene.active.camera = cam; | 		iron.Scene.active.camera = cam; | ||||||
| 		cam.renderTarget = rt; | 		cam.renderTarget = rt; | ||||||
|  |  | ||||||
| 		#if kha_html5 |  | ||||||
| 		var q: Quat = new Quat(); |  | ||||||
| 		q.fromAxisAngle(new Vec4(0, 0, 1, 1), Helper.degToRad(180)); |  | ||||||
| 		cam.transform.rot.mult(q); |  | ||||||
| 		cam.transform.buildMatrix(); |  | ||||||
| 		#end |  | ||||||
|  |  | ||||||
| 		cam.renderFrame(g); | 		cam.renderFrame(g); | ||||||
|  |  | ||||||
| 		#if kha_html5 |  | ||||||
| 		cam.transform.rot.mult(q); |  | ||||||
| 		cam.transform.buildMatrix(); |  | ||||||
| 		#end |  | ||||||
|  |  | ||||||
| 		cam.renderTarget = oldRT; | 		cam.renderTarget = oldRT; | ||||||
| 		iron.Scene.active.camera = sceneCam; | 		iron.Scene.active.camera = sceneCam; | ||||||
| 	} | 	} | ||||||
|  | |||||||
| @ -99,6 +99,8 @@ class DrawImageSequenceNode extends LogicNode { | |||||||
| 		final colorVec = inputs[4].get(); | 		final colorVec = inputs[4].get(); | ||||||
| 		g.color = Color.fromFloats(colorVec.x, colorVec.y, colorVec.z, colorVec.w); | 		g.color = Color.fromFloats(colorVec.x, colorVec.y, colorVec.z, colorVec.w); | ||||||
|  |  | ||||||
|  | 		trace(currentImgIdx); | ||||||
|  |  | ||||||
| 		g.drawScaledImage(images[currentImgIdx], inputs[5].get(), inputs[6].get(), inputs[7].get(), inputs[8].get()); | 		g.drawScaledImage(images[currentImgIdx], inputs[5].get(), inputs[6].get(), inputs[7].get(), inputs[8].get()); | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  | |||||||
| @ -62,7 +62,7 @@ class DrawStringNode extends LogicNode { | |||||||
|  |  | ||||||
| 	override function get(from: Int): Dynamic { | 	override function get(from: Int): Dynamic { | ||||||
|  |  | ||||||
| 		return from == 1 ? RenderToTexture.g.font.width(RenderToTexture.g.fontSize, string) : RenderToTexture.g.font.height(RenderToTexture.g.fontSize); | 		return from == 1 ? RenderToTexture.g.font.height(RenderToTexture.g.fontSize) : RenderToTexture.g.font.width(RenderToTexture.g.fontSize, string); | ||||||
| 			 | 	 | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  | |||||||
| @ -1,59 +0,0 @@ | |||||||
| 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); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @ -1,17 +0,0 @@ | |||||||
| 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; |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| @ -1,19 +0,0 @@ | |||||||
| package leenkx.logicnode; |  | ||||||
|  |  | ||||||
| import iron.object.MeshObject; |  | ||||||
| import iron.object.CameraObject; |  | ||||||
|  |  | ||||||
| class GetCameraRenderFilterNode extends LogicNode { |  | ||||||
|  |  | ||||||
| 	public function new(tree: LogicTree) { |  | ||||||
| 		super(tree); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	override function get(from: Int): Dynamic { |  | ||||||
| 		var mo: MeshObject = cast inputs[0].get(); |  | ||||||
|  |  | ||||||
| 		if (mo == null) return null; |  | ||||||
|  |  | ||||||
| 		return mo.cameraList; |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| @ -8,7 +8,7 @@ class GetFPSNode extends LogicNode { | |||||||
|  |  | ||||||
|     override function get(from: Int): Dynamic { |     override function get(from: Int): Dynamic { | ||||||
|         if (from == 0) { |         if (from == 0) { | ||||||
|             var fps = Math.round(1 / iron.system.Time.renderDelta); |             var fps = Math.round(1 / iron.system.Time.realDelta); | ||||||
|             if ((fps == Math.POSITIVE_INFINITY) || (fps == Math.NEGATIVE_INFINITY) || (Math.isNaN(fps))) { |             if ((fps == Math.POSITIVE_INFINITY) || (fps == Math.NEGATIVE_INFINITY) || (Math.isNaN(fps))) { | ||||||
|                 return 0; |                 return 0; | ||||||
|             } |             } | ||||||
|  | |||||||
| @ -1,72 +0,0 @@ | |||||||
| 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; |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| @ -1,38 +0,0 @@ | |||||||
| 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; |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| @ -1,33 +0,0 @@ | |||||||
| 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,12 +1,26 @@ | |||||||
| package leenkx.logicnode; | package leenkx.logicnode; | ||||||
|  |  | ||||||
|  | import iron.object.Object; | ||||||
|  | import iron.math.Vec4; | ||||||
|  |  | ||||||
| class GetWorldNode extends LogicNode { | class GetWorldNode extends LogicNode { | ||||||
|  |  | ||||||
|  | 	public var property0: String; | ||||||
|  |  | ||||||
| 	public function new(tree: LogicTree) { | 	public function new(tree: LogicTree) { | ||||||
| 		super(tree); | 		super(tree); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	override function get(from: Int): Dynamic { | 	override function get(from: Int): Dynamic { | ||||||
| 		return iron.Scene.active.raw.world_ref; | 		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,26 +0,0 @@ | |||||||
| 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,10 +1,7 @@ | |||||||
| package leenkx.logicnode; | package leenkx.logicnode; | ||||||
| import iron.object.Object; |  | ||||||
|  |  | ||||||
| #if lnx_physics | import iron.object.Object; | ||||||
| import leenkx.trait.physics.PhysicsCache; |  | ||||||
| import leenkx.trait.physics.RigidBody; | import leenkx.trait.physics.RigidBody; | ||||||
| #end |  | ||||||
|  |  | ||||||
| class HasContactNode extends LogicNode { | class HasContactNode extends LogicNode { | ||||||
|  |  | ||||||
| @ -18,15 +15,12 @@ class HasContactNode extends LogicNode { | |||||||
|  |  | ||||||
| 		if (object1 == null || object2 == null) return false; | 		if (object1 == null || object2 == null) return false; | ||||||
|  |  | ||||||
| 		#if lnx_physics | #if lnx_physics | ||||||
| 		var rb1 = PhysicsCache.getCachedRigidBody(object1); | 		var physics = leenkx.trait.physics.PhysicsWorld.active; | ||||||
| 		var rb2 = PhysicsCache.getCachedRigidBody(object2); | 		var rb2 = object2.getTrait(RigidBody); | ||||||
| 		 | 		var rbs = physics.getContacts(object1.getTrait(RigidBody)); | ||||||
| 		if (rb1 != null && rb2 != null) { | 		if (rbs != null) for (rb in rbs) if (rb == rb2) return true; | ||||||
| 			var rbs = PhysicsCache.getCachedContacts(rb1); | #end | ||||||
| 			return PhysicsCache.hasContactWith(rbs, rb2); |  | ||||||
| 		} |  | ||||||
| 		#end |  | ||||||
| 		return false; | 		return false; | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  | |||||||
| @ -1,233 +0,0 @@ | |||||||
| 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,11 +1,7 @@ | |||||||
| package leenkx.logicnode; | package leenkx.logicnode; | ||||||
|  |  | ||||||
| import iron.object.Object; | import iron.object.Object; | ||||||
|  |  | ||||||
| #if lnx_physics |  | ||||||
| import leenkx.trait.physics.PhysicsCache; |  | ||||||
| import leenkx.trait.physics.RigidBody; | import leenkx.trait.physics.RigidBody; | ||||||
| #end |  | ||||||
|  |  | ||||||
|  |  | ||||||
| class OnContactNode extends LogicNode { | class OnContactNode extends LogicNode { | ||||||
|  |  | ||||||
| @ -27,15 +23,22 @@ class OnContactNode extends LogicNode { | |||||||
|  |  | ||||||
| 		var contact = false; | 		var contact = false; | ||||||
|  |  | ||||||
| 		#if lnx_physics | #if lnx_physics | ||||||
| 			var rb1 = PhysicsCache.getCachedRigidBody(object1); | 		var physics = leenkx.trait.physics.PhysicsWorld.active; | ||||||
| 			var rb2 = PhysicsCache.getCachedRigidBody(object2); | 		var rb1 = object1.getTrait(RigidBody); | ||||||
| 			 | 		if (rb1 != null) { | ||||||
| 			if (rb1 != null && rb2 != null) { | 			var rbs = physics.getContacts(rb1); | ||||||
| 				var rbs = PhysicsCache.getCachedContacts(rb1); | 			if (rbs != null) { | ||||||
| 				contact = PhysicsCache.hasContactWith(rbs, rb2); | 				var rb2 = object2.getTrait(RigidBody); | ||||||
|  | 				for (rb in rbs) { | ||||||
|  | 					if (rb == rb2) { | ||||||
|  | 						contact = true; | ||||||
|  | 						break; | ||||||
|  | 					} | ||||||
|  | 				} | ||||||
| 			} | 			} | ||||||
| 		#end | 		} | ||||||
|  | #end | ||||||
|  |  | ||||||
| 		var b = false; | 		var b = false; | ||||||
| 		switch (property0) { | 		switch (property0) { | ||||||
|  | |||||||
| @ -1,23 +0,0 @@ | |||||||
| 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,38 +9,19 @@ import iron.Scene; | |||||||
|  |  | ||||||
| class PlayAnimationTreeNode extends LogicNode { | class PlayAnimationTreeNode extends LogicNode { | ||||||
|  |  | ||||||
| 	var object: Object; |  | ||||||
| 	var action: Dynamic; |  | ||||||
| 	var init: Bool = false; |  | ||||||
|  |  | ||||||
| 	public function new(tree: LogicTree) { | 	public function new(tree: LogicTree) { | ||||||
| 		super(tree); | 		super(tree); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	override function run(from: Int) { | 	override function run(from: Int) { | ||||||
| 		object = inputs[1].get(); | 		var object: Object = inputs[1].get(); | ||||||
| 		action = inputs[2].get(); | 		var action: Dynamic = inputs[2].get(); | ||||||
|  |  | ||||||
| 		assert(Error, object != null, "The object input not be null"); | 		assert(Error, object != null, "The object input not be null"); | ||||||
| 		init = true; |  | ||||||
| 		tree.notifyOnUpdate(playAnim); |  | ||||||
| 		// TO DO: Investigate AnimAction get and PlayAnimationTree notifiers |  | ||||||
| 	} |  | ||||||
| 	 |  | ||||||
| 	function playAnim() { |  | ||||||
| 		if (init = false) return; |  | ||||||
| 		 |  | ||||||
| 		init = false; |  | ||||||
| 		tree.removeUpdate(playAnim); |  | ||||||
| 		 |  | ||||||
| 		var animation = object.animation; | 		var animation = object.animation; | ||||||
| 		if(animation == null) { | 		if(animation == null) { | ||||||
| 			#if lnx_skin | 			#if lnx_skin | ||||||
| 			animation = object.getBoneAnimation(object.uid); | 			animation = object.getBoneAnimation(object.uid); | ||||||
| 			if (animation == null) { |  | ||||||
| 				tree.notifyOnUpdate(playAnim); |  | ||||||
| 				init = true; |  | ||||||
| 				return; |  | ||||||
| 			} |  | ||||||
| 			cast(animation, BoneAnimation).setAnimationLoop(function f(mats) { | 			cast(animation, BoneAnimation).setAnimationLoop(function f(mats) { | ||||||
| 				action(mats); | 				action(mats); | ||||||
| 			}); | 			}); | ||||||
| @ -51,6 +32,7 @@ class PlayAnimationTreeNode extends LogicNode { | |||||||
| 				action(mats); | 				action(mats); | ||||||
| 			}); | 			}); | ||||||
| 		} | 		} | ||||||
|  | 		 | ||||||
| 		runOutput(0); | 		runOutput(0); | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  | |||||||
| @ -1,41 +0,0 @@ | |||||||
| 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,6 +18,7 @@ class ProbabilisticOutputNode extends LogicNode { | |||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		if (sum > 1){ | 		if (sum > 1){ | ||||||
|  | 			trace(sum); | ||||||
| 			for (p in 0...probs.length) | 			for (p in 0...probs.length) | ||||||
| 				probs[p] /= sum; | 				probs[p] /= sum; | ||||||
| 		} | 		} | ||||||
|  | |||||||
| @ -1,64 +0,0 @@ | |||||||
| 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); |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| @ -1,16 +0,0 @@ | |||||||
| 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; |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| @ -1,33 +0,0 @@ | |||||||
| 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,8 +20,6 @@ class RpConfigNode extends LogicNode { | |||||||
| 			on ? leenkx.data.Config.raw.rp_ssrefr = true : leenkx.data.Config.raw.rp_ssrefr = false; | 			on ? leenkx.data.Config.raw.rp_ssrefr = true : leenkx.data.Config.raw.rp_ssrefr = false; | ||||||
|         case "Bloom": |         case "Bloom": | ||||||
| 			on ? leenkx.data.Config.raw.rp_bloom = true : leenkx.data.Config.raw.rp_bloom = false; | 			on ? leenkx.data.Config.raw.rp_bloom = true : leenkx.data.Config.raw.rp_bloom = false; | ||||||
| 		case "CA": |  | ||||||
| 			on ? leenkx.data.Config.raw.rp_chromatic_aberration = true : leenkx.data.Config.raw.rp_chromatic_aberration = false; |  | ||||||
|         case "GI": |         case "GI": | ||||||
| 			on ? leenkx.data.Config.raw.rp_gi = true : leenkx.data.Config.raw.rp_gi = false; | 			on ? leenkx.data.Config.raw.rp_gi = true : leenkx.data.Config.raw.rp_gi = false; | ||||||
| 		case "Motion Blur": | 		case "Motion Blur": | ||||||
|  | |||||||
| @ -1,23 +0,0 @@ | |||||||
| 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); |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| @ -1,38 +0,0 @@ | |||||||
| 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); |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| @ -1,21 +0,0 @@ | |||||||
| 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); |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| @ -1,206 +0,0 @@ | |||||||
| 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 |  | ||||||
| } |  | ||||||
| @ -1,55 +0,0 @@ | |||||||
| 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); |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| @ -1,74 +0,0 @@ | |||||||
| 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); |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| @ -1,81 +0,0 @@ | |||||||
| 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); |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| @ -1,23 +0,0 @@ | |||||||
| 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,16 +11,13 @@ class SetParticleSpeedNode extends LogicNode { | |||||||
| 	override function run(from: Int) { | 	override function run(from: Int) { | ||||||
| 		#if lnx_particles | 		#if lnx_particles | ||||||
| 		var object: Object = inputs[1].get(); | 		var object: Object = inputs[1].get(); | ||||||
| 		var slot: Int = inputs[2].get(); | 		var speed: Float = inputs[2].get(); | ||||||
| 		var speed: Float = inputs[3].get(); |  | ||||||
|  |  | ||||||
| 		if (object == null) return; | 		if (object == null) return; | ||||||
|  |  | ||||||
| 		var mo = cast(object, iron.object.MeshObject); | 		var mo = cast(object, iron.object.MeshObject); | ||||||
| 		var psys = mo.particleSystems != null ? mo.particleSystems[slot] :  | 		var psys = mo.particleSystems.length > 0 ? mo.particleSystems[0] : null; | ||||||
| 			mo.particleOwner != null && mo.particleOwner.particleSystems != null ? mo.particleOwner.particleSystems[slot] : null; | 		if (psys == null) mo.particleOwner.particleSystems[0]; | ||||||
|  |  | ||||||
| 		if (psys == null) return; |  | ||||||
|  |  | ||||||
| 		psys.speed = speed; | 		psys.speed = speed; | ||||||
|  |  | ||||||
|  | |||||||
| @ -1,39 +0,0 @@ | |||||||
| 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); |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| @ -1,23 +0,0 @@ | |||||||
| 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); |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| @ -1,17 +0,0 @@ | |||||||
| 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; |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| @ -1,18 +0,0 @@ | |||||||
| 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); |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| @ -1,17 +0,0 @@ | |||||||
| 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; |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| @ -1,18 +0,0 @@ | |||||||
| 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); |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| @ -1,17 +0,0 @@ | |||||||
| 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; |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| @ -1,18 +0,0 @@ | |||||||
| 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); |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| @ -1,105 +0,0 @@ | |||||||
| 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