Baking/World Updates
This commit is contained in:
		
							parent
							
								
									99e0060a7a
								
							
						
					
					
						commit
						73cdec243f
					
				
										
											Binary file not shown.
										
									
								
							| 
						 | 
					@ -0,0 +1,31 @@
 | 
				
			||||||
 | 
					shader_type spatial;
 | 
				
			||||||
 | 
					render_mode unshaded, depth_test_disabled;
 | 
				
			||||||
 | 
					uniform float depth_scaler = 1.0f;
 | 
				
			||||||
 | 
					uniform vec4 col: source_color;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					uniform sampler2D DEPTH_TEXTURE:hint_depth_texture,filter_linear_mipmap;
 | 
				
			||||||
 | 
					varying mat4 CAMERA;
 | 
				
			||||||
 | 
					void vertex() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						POSITION = vec4(VERTEX, 1.0);
 | 
				
			||||||
 | 
						CAMERA = INV_VIEW_MATRIX;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void fragment() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  float depth = texture(DEPTH_TEXTURE, SCREEN_UV).x;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//Vulkan.z size range 0-1 ,penGL.z size range -1,1
 | 
				
			||||||
 | 
					  vec3 ndc = vec3(SCREEN_UV * 2.0 - 1.0, depth);
 | 
				
			||||||
 | 
					//  vec3 ndc = vec3(SCREEN_UV, depth) * 2.0 - 1.0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  vec4 world = CAMERA * INV_PROJECTION_MATRIX * vec4(ndc, 1.0);
 | 
				
			||||||
 | 
					  vec3 world_position = world.xyz / world.w;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					float aabb_full = depth_scaler;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					world_position *= 2.0; // going back to aabb but around 0
 | 
				
			||||||
 | 
					world_position.z = aabb_full/2.0 - world_position.z;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ALBEDO = vec3(clamp(world_position.z/aabb_full,0,1));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
										
											Binary file not shown.
										
									
								
							| 
						 | 
					@ -0,0 +1,46 @@
 | 
				
			||||||
 | 
					shader_type canvas_item;
 | 
				
			||||||
 | 
					render_mode blend_premul_alpha;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					uniform sampler2D u_alpha_tex;
 | 
				
			||||||
 | 
					uniform int u_distance = 16;
 | 
				
			||||||
 | 
					uniform bool u_alpha_overwrite = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					vec4 dilate(sampler2D a_tex, sampler2D tex, vec2 coords, vec2 texel_size, int dist, float alpha_cutoff)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						vec2 offsets[8] = {
 | 
				
			||||||
 | 
							vec2(-1,0),vec2(1,0), vec2(0,1), vec2(0,-1), 
 | 
				
			||||||
 | 
							vec2(-1,1), vec2(1,1), vec2(1,-1), vec2(-1,-1)
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						vec4 sample = textureLod(tex, coords, 0);
 | 
				
			||||||
 | 
						vec4 a_sample = textureLod(a_tex, coords, 0);
 | 
				
			||||||
 | 
						if(a_sample.a > alpha_cutoff)
 | 
				
			||||||
 | 
							return sample;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for(int curr_dist = 0; curr_dist < dist; curr_dist++)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							for(int o = 0; o < 8; o++)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								vec2 off_coords = offsets[o] * texel_size * float(curr_dist + 1);
 | 
				
			||||||
 | 
								vec4 off_sample = textureLod(tex, coords + off_coords, 0);
 | 
				
			||||||
 | 
								vec4 off_a_sample = textureLod(a_tex, coords + off_coords, 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if (off_a_sample.a > alpha_cutoff)
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									if(u_alpha_overwrite)
 | 
				
			||||||
 | 
										return off_sample;
 | 
				
			||||||
 | 
									else
 | 
				
			||||||
 | 
										return vec4(off_sample.rgb, a_sample.a);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return sample;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void fragment() {
 | 
				
			||||||
 | 
					    COLOR = dilate(u_alpha_tex, TEXTURE, UV, TEXTURE_PIXEL_SIZE, u_distance, 0.95);
 | 
				
			||||||
 | 
						//COLOR.a = 1.0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
										
											Binary file not shown.
										
									
								
							| 
						 | 
					@ -0,0 +1,44 @@
 | 
				
			||||||
 | 
					shader_type spatial;
 | 
				
			||||||
 | 
					render_mode blend_mix,depth_draw_opaque,cull_disabled,diffuse_burley,specular_schlick_ggx;
 | 
				
			||||||
 | 
					uniform vec4 albedo : source_color;
 | 
				
			||||||
 | 
					uniform sampler2D texture_albedo : source_color;
 | 
				
			||||||
 | 
					uniform sampler2D normal_texture : source_color;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					uniform bool use_normalmap = false;
 | 
				
			||||||
 | 
					uniform bool use_alpha_texture = false;
 | 
				
			||||||
 | 
					uniform float roughness : hint_range(0,1);
 | 
				
			||||||
 | 
					uniform float alpha_scissor_threshold :  hint_range(0,1);
 | 
				
			||||||
 | 
					uniform float normal_scale :  hint_range(-5,5);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					uniform vec3 uv1_scale;
 | 
				
			||||||
 | 
					uniform vec3 uv1_offset;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void vertex() {
 | 
				
			||||||
 | 
						UV=UV*uv1_scale.xy+uv1_offset.xy;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void fragment() {
 | 
				
			||||||
 | 
						vec2 base_uv = UV;
 | 
				
			||||||
 | 
						vec4 albedo_tex = texture(texture_albedo,base_uv);
 | 
				
			||||||
 | 
						vec4 normal_tex = texture(normal_texture,base_uv);
 | 
				
			||||||
 | 
						// 0.5 + -1.0 == -1.0 + 0.5
 | 
				
			||||||
 | 
						//ALBEDO = vec3(1.0 - NORMAL.y, 1.0 - NORMAL.x, - NORMAL.z)* 0.5;
 | 
				
			||||||
 | 
						if(use_normalmap)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							vec3 normalmap;
 | 
				
			||||||
 | 
							normalmap.xy = normal_tex.xy * 2.0 - 1.0;
 | 
				
			||||||
 | 
							normalmap.z = sqrt(max(0.0, 1.0 - dot(normalmap.xy, normalmap.xy)));
 | 
				
			||||||
 | 
							NORMAL = normalize(mix(NORMAL, TANGENT * normalmap.x + BINORMAL * normalmap.y + NORMAL * normalmap.z, normal_scale));
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						ALBEDO = vec3(-NORMAL.x, NORMAL.y, -NORMAL.z) * 0.5 + 0.5;
 | 
				
			||||||
 | 
						if(use_alpha_texture)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							ALPHA = albedo_tex.a;
 | 
				
			||||||
 | 
							ALPHA_SCISSOR_THRESHOLD = alpha_scissor_threshold;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
										
											Binary file not shown.
										
									
								
							| 
						 | 
					@ -0,0 +1,41 @@
 | 
				
			||||||
 | 
					shader_type spatial;
 | 
				
			||||||
 | 
					render_mode blend_mix,depth_draw_opaque,cull_disabled,diffuse_burley,specular_schlick_ggx;
 | 
				
			||||||
 | 
					uniform vec4 albedo : source_color;
 | 
				
			||||||
 | 
					uniform sampler2D texture_albedo : source_color;
 | 
				
			||||||
 | 
					uniform sampler2D ao_texture : source_color;
 | 
				
			||||||
 | 
					uniform vec4 ao_texture_channel;
 | 
				
			||||||
 | 
					uniform sampler2D roughness_texture : source_color;
 | 
				
			||||||
 | 
					uniform vec4  roughness_texture_channel;
 | 
				
			||||||
 | 
					uniform sampler2D metallic_texture : source_color;
 | 
				
			||||||
 | 
					uniform vec4  metallic_texture_channel;
 | 
				
			||||||
 | 
					uniform bool use_alpha_texture = false;
 | 
				
			||||||
 | 
					uniform float roughness : hint_range(0,1) = 1.0;
 | 
				
			||||||
 | 
					uniform float metallic : hint_range(0,1) = 0.0;
 | 
				
			||||||
 | 
					uniform float alpha_scissor_threshold :  hint_range(0,1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					uniform bool use_ao_texture = false;
 | 
				
			||||||
 | 
					uniform bool use_roughness_texture = false;
 | 
				
			||||||
 | 
					uniform bool use_metallic_texture = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					uniform vec3 uv1_scale;
 | 
				
			||||||
 | 
					uniform vec3 uv1_offset;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void vertex() {
 | 
				
			||||||
 | 
						UV=UV*uv1_scale.xy+uv1_offset.xy;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void fragment() {
 | 
				
			||||||
 | 
						vec2 base_uv = UV;
 | 
				
			||||||
 | 
						vec4 albedo_tex = texture(texture_albedo,base_uv);
 | 
				
			||||||
 | 
						float ao_tex = mix(1.0, dot(texture(ao_texture,base_uv), ao_texture_channel), float(use_ao_texture));
 | 
				
			||||||
 | 
						float rougness_tex = mix(1.0,dot(texture(roughness_texture,base_uv), roughness_texture_channel), float(use_roughness_texture));
 | 
				
			||||||
 | 
						float metallic_tex = mix(1.0,dot(texture(metallic_texture,base_uv), metallic_texture_channel), float(use_metallic_texture));
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						ALBEDO = vec3(ao_tex, rougness_tex * roughness, metallic_tex * metallic);
 | 
				
			||||||
 | 
						if(use_alpha_texture)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							ALPHA = albedo_tex.a;
 | 
				
			||||||
 | 
							ALPHA_SCISSOR_THRESHOLD = alpha_scissor_threshold;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
										
											Binary file not shown.
										
									
								
							| 
						 | 
					@ -0,0 +1,409 @@
 | 
				
			||||||
 | 
					shader_type spatial;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					render_mode blend_mix, depth_draw_opaque, cull_back, diffuse_burley, specular_schlick_ggx;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					uniform vec4 albedo : source_color = vec4(1, 1, 1, 1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					uniform float specular = 0.5f;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					uniform float metallic = 1.0f;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					uniform float roughness : hint_range(0.0f, 1.0f) = 1.0f;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					uniform sampler2D imposterTextureAlbedo : source_color;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					uniform sampler2D imposterTextureNormal : hint_normal;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					uniform sampler2D imposterTextureDepth : hint_default_white;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					uniform sampler2D imposterTextureOrm : hint_default_white;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					uniform vec2 imposterFrames = vec2(16.0f, 16.0f);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					uniform vec3 positionOffset = vec3(0.0f);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					uniform bool isFullSphere = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					uniform float alpha_clamp = 0.5f;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					uniform bool dither = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					uniform float scale = 1.0f;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					uniform float depth_scale : hint_range(0, 1) = 0.0f;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					uniform float normalmap_depth : hint_range(-5, 5)  = 1.0f;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					uniform float aabb_max = 1.0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					varying vec2 uv_frame1;
 | 
				
			||||||
 | 
					varying vec2 xy_frame1;
 | 
				
			||||||
 | 
					varying flat vec2 frame1;
 | 
				
			||||||
 | 
					varying flat vec3 frame1_normal;
 | 
				
			||||||
 | 
					varying vec2 uv_frame2;
 | 
				
			||||||
 | 
					varying vec2 xy_frame2;
 | 
				
			||||||
 | 
					varying flat vec2 frame2;
 | 
				
			||||||
 | 
					varying flat vec3 frame2_normal;
 | 
				
			||||||
 | 
					varying vec2 uv_frame3;
 | 
				
			||||||
 | 
					varying vec2 xy_frame3;
 | 
				
			||||||
 | 
					varying flat vec2 frame3;
 | 
				
			||||||
 | 
					varying flat vec3 frame3_normal;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					varying vec4 quad_blend_weights;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					vec2 VecToSphereOct(vec3 pivotToCamera)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						vec3 octant = sign(pivotToCamera);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						//  |x| + |y| + |z| = 1
 | 
				
			||||||
 | 
						float sum = dot(pivotToCamera, octant);
 | 
				
			||||||
 | 
						vec3 octahedron = pivotToCamera / sum;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (octahedron.y < 0.0f)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							vec3 absolute = abs(octahedron);
 | 
				
			||||||
 | 
							octahedron.xz = octant.xz * vec2(1.0f - absolute.z, 1.0f - absolute.x);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return octahedron.xz;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					vec2 VecToHemiSphereOct(vec3 pivotToCamera)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						pivotToCamera.y = max(pivotToCamera.y, 0.001);
 | 
				
			||||||
 | 
						pivotToCamera = normalize(pivotToCamera);
 | 
				
			||||||
 | 
						vec3 octant = sign(pivotToCamera);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						//  |x| + |y| + |z| = 1
 | 
				
			||||||
 | 
						float sum = dot(pivotToCamera, octant);
 | 
				
			||||||
 | 
						vec3 octahedron = pivotToCamera / sum;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return vec2(
 | 
				
			||||||
 | 
						octahedron.x + octahedron.z,
 | 
				
			||||||
 | 
						octahedron.z - octahedron.x);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					vec2 VectorToGrid(vec3 vec)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (isFullSphere)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							return VecToSphereOct(vec);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							return VecToHemiSphereOct(vec);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//for sphere
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					vec3 OctaSphereEnc(vec2 coord)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						coord = (coord - 0.5) * 2.0;
 | 
				
			||||||
 | 
						vec3 position = vec3(coord.x, 0.0f, coord.y);
 | 
				
			||||||
 | 
						vec2 absolute = abs(position.xz);
 | 
				
			||||||
 | 
						position.y = 1.0f - absolute.x - absolute.y;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (position.y < 0.0f)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							position.xz = sign(position.xz) * vec2(1.0f - absolute.y, 1.0f - absolute.x);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return position;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//for hemisphere
 | 
				
			||||||
 | 
					vec3 OctaHemiSphereEnc(vec2 coord)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						vec3 position = vec3(coord.x - coord.y, 0.0f, -1.0 + coord.x + coord.y);
 | 
				
			||||||
 | 
						vec2 absolute = abs(position.xz);
 | 
				
			||||||
 | 
						position.y = 1.0f - absolute.x - absolute.y;
 | 
				
			||||||
 | 
						return position;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					vec3 GridToVector(vec2 coord)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (isFullSphere)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							return OctaSphereEnc(coord);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							return OctaHemiSphereEnc(coord);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					vec3 FrameXYToRay(vec2 frame, vec2 frameCountMinusOne)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						vec2 f = (frame.xy/ frameCountMinusOne);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						vec3 vec = GridToVector(f);
 | 
				
			||||||
 | 
						vec = normalize(vec);
 | 
				
			||||||
 | 
						return vec;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					vec3 SpriteProjection(vec3 pivotToCameraRayLocal, vec2 size, vec2 loc_uv)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						vec3 z = normalize(pivotToCameraRayLocal);
 | 
				
			||||||
 | 
						vec3 x, y;
 | 
				
			||||||
 | 
						vec3 up = vec3(0,1,0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (abs(z.y) > 0.999f)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							up = vec3(0,0,-1);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						x = normalize(cross(up, z));
 | 
				
			||||||
 | 
						y = normalize(cross(x, z));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						loc_uv -= vec2(0.5,0.5);
 | 
				
			||||||
 | 
						vec2 uv = (loc_uv) * 2.0; //-1 to 1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						vec3 newX = x * uv.x;
 | 
				
			||||||
 | 
						vec3 newY = y * uv.y;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						vec2 vecSize = size * 0.5;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						newX *= vecSize.x;
 | 
				
			||||||
 | 
						newY *= vecSize.y;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return newX + newY;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					vec4 quadBlendWieghts(vec2 coords)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						vec4 res;
 | 
				
			||||||
 | 
						/* 0 0 0
 | 
				
			||||||
 | 
						0 0 0
 | 
				
			||||||
 | 
						1 0 0 */
 | 
				
			||||||
 | 
						res.x = min(1.0f - coords.x, 1.0f - coords.y);
 | 
				
			||||||
 | 
						/* 1 0 0
 | 
				
			||||||
 | 
						0 0 0
 | 
				
			||||||
 | 
						0 0 1 */
 | 
				
			||||||
 | 
						res.y = abs(coords.x - coords.y);
 | 
				
			||||||
 | 
						/* 0 0 1
 | 
				
			||||||
 | 
						0 0 0
 | 
				
			||||||
 | 
						0 0 0 */
 | 
				
			||||||
 | 
						res.z = min(coords.x, coords.y);
 | 
				
			||||||
 | 
						/* 0 0 0
 | 
				
			||||||
 | 
						0 0 1
 | 
				
			||||||
 | 
						0 1 1 */
 | 
				
			||||||
 | 
						res.w = ceil(coords.x - coords.y);
 | 
				
			||||||
 | 
						//res.xyz /= (res.x + res.y + res.z);
 | 
				
			||||||
 | 
						return res;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					vec2 virtualPlaneUV(vec3 plane_normal,vec3  plane_x, vec3  plane_y, vec3 pivotToCameraRay, vec3 vertexToCameraRay, float size)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						plane_normal = normalize(plane_normal);
 | 
				
			||||||
 | 
						plane_x = normalize(plane_x);
 | 
				
			||||||
 | 
						plane_y = normalize(plane_y);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						float projectedNormalRayLength = dot(plane_normal, pivotToCameraRay);
 | 
				
			||||||
 | 
						float projectedVertexRayLength = dot(plane_normal, vertexToCameraRay);
 | 
				
			||||||
 | 
						float offsetLength = projectedNormalRayLength/projectedVertexRayLength;
 | 
				
			||||||
 | 
						vec3 offsetVector = vertexToCameraRay * offsetLength - pivotToCameraRay;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						vec2 duv = vec2(
 | 
				
			||||||
 | 
									dot(plane_x , offsetVector),
 | 
				
			||||||
 | 
									dot(plane_y, offsetVector)
 | 
				
			||||||
 | 
						);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						//we are in space -1 to 1
 | 
				
			||||||
 | 
						duv /= 2.0 * size;
 | 
				
			||||||
 | 
						duv += 0.5;
 | 
				
			||||||
 | 
						return duv;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void calcuateXYbasis(vec3 plane_normal, out vec3 plane_x, out vec3 plane_y)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						vec3 up = vec3(0,1,0);
 | 
				
			||||||
 | 
							//cross product doesnt work if we look directly from bottom
 | 
				
			||||||
 | 
						if (abs(plane_normal.y) > 0.999f)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							up = vec3(0,0,1);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						plane_x = normalize(cross(plane_normal, up));
 | 
				
			||||||
 | 
						plane_y = normalize(cross(plane_x, plane_normal));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					vec3 projectOnPlaneBasis(vec3 ray, vec3 plane_normal, vec3 plane_x, vec3 plane_y)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						//reproject plane normal onto planeXY basos
 | 
				
			||||||
 | 
						return normalize(vec3(
 | 
				
			||||||
 | 
							dot(plane_x,ray),
 | 
				
			||||||
 | 
							dot(plane_y,ray),
 | 
				
			||||||
 | 
							dot(plane_normal,ray)
 | 
				
			||||||
 | 
						));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void vertex()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						vec2 framesMinusOne = imposterFrames - vec2(1);
 | 
				
			||||||
 | 
						vec3 cameraPos_WS = (INV_VIEW_MATRIX * vec4(vec3(0), 1.0)).xyz;
 | 
				
			||||||
 | 
						vec3 cameraPos_OS = (inverse(MODEL_MATRIX) * vec4(cameraPos_WS, 1.0)).xyz;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						//TODO: check if this is correct. We are using orho projected images, so
 | 
				
			||||||
 | 
						// camera far away
 | 
				
			||||||
 | 
						vec3 pivotToCameraRay = (cameraPos_OS) * 10.0;
 | 
				
			||||||
 | 
						vec3 pivotToCameraDir = normalize(cameraPos_OS);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						vec2 grid = VectorToGrid(pivotToCameraDir);
 | 
				
			||||||
 | 
						//bias and scale to 0 to 1
 | 
				
			||||||
 | 
						grid = clamp((grid + 1.0) * 0.5, vec2(0, 0), vec2(1, 1));
 | 
				
			||||||
 | 
						grid *= framesMinusOne;
 | 
				
			||||||
 | 
						grid = clamp(grid, vec2(0), vec2(framesMinusOne));
 | 
				
			||||||
 | 
						vec2 gridFloor = min(floor(grid), framesMinusOne);
 | 
				
			||||||
 | 
						vec2 gridFract = fract(grid);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						//radius * 2
 | 
				
			||||||
 | 
						vec2 size = vec2(2.0) * scale;
 | 
				
			||||||
 | 
						vec3 projected = SpriteProjection(pivotToCameraDir, size, UV);
 | 
				
			||||||
 | 
						vec3 vertexToCameraRay = (pivotToCameraRay - (projected));
 | 
				
			||||||
 | 
						vec3 vertexToCameraDir = normalize(vertexToCameraRay);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						frame1 = gridFloor;
 | 
				
			||||||
 | 
						quad_blend_weights = quadBlendWieghts(gridFract);
 | 
				
			||||||
 | 
						//convert frame coordinate to octahedron direction
 | 
				
			||||||
 | 
						vec3 projectedQuadADir = FrameXYToRay(frame1, framesMinusOne);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						frame2 = clamp(frame1 + mix(vec2(0, 1), vec2(1, 0), quad_blend_weights.w), vec2(0,0), framesMinusOne);
 | 
				
			||||||
 | 
						vec3 projectedQuadBDir = FrameXYToRay(frame2, framesMinusOne);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						frame3 = clamp(frame1 + vec2(1), vec2(0,0), framesMinusOne);
 | 
				
			||||||
 | 
						vec3 projectedQuadCDir = FrameXYToRay(frame3, framesMinusOne);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						frame1_normal = (MODELVIEW_MATRIX *vec4(projectedQuadADir, 0)).xyz;
 | 
				
			||||||
 | 
						frame2_normal = (MODELVIEW_MATRIX *vec4(projectedQuadBDir, 0)).xyz;
 | 
				
			||||||
 | 
						frame3_normal = (MODELVIEW_MATRIX *vec4(projectedQuadCDir, 0)).xyz;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						//calcute virtual planes projections
 | 
				
			||||||
 | 
						vec3 plane_x1, plane_y1, plane_x2, plane_y2, plane_x3, plane_y3;
 | 
				
			||||||
 | 
						calcuateXYbasis(projectedQuadADir, plane_x1, plane_y1);
 | 
				
			||||||
 | 
						uv_frame1 = virtualPlaneUV(projectedQuadADir, plane_x1, plane_y1, pivotToCameraRay, vertexToCameraRay, scale);
 | 
				
			||||||
 | 
						xy_frame1 = projectOnPlaneBasis(-vertexToCameraDir, projectedQuadADir, plane_x1, plane_y1).xy;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						calcuateXYbasis(projectedQuadBDir, plane_x2, plane_y2);
 | 
				
			||||||
 | 
						uv_frame2 = virtualPlaneUV(projectedQuadBDir, plane_x2, plane_y2, pivotToCameraRay, vertexToCameraRay, scale);
 | 
				
			||||||
 | 
						xy_frame2 = projectOnPlaneBasis(-vertexToCameraDir, projectedQuadBDir, plane_x2, plane_y2).xy;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						calcuateXYbasis(projectedQuadCDir, plane_x3, plane_y3);
 | 
				
			||||||
 | 
						uv_frame3 = virtualPlaneUV(projectedQuadCDir, plane_x3, plane_y3, pivotToCameraRay, vertexToCameraRay, scale);
 | 
				
			||||||
 | 
						xy_frame3 = projectOnPlaneBasis(-vertexToCameraDir, projectedQuadCDir, plane_x3, plane_y3).xy;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						//to fragment shader
 | 
				
			||||||
 | 
						VERTEX.xyz = projected + positionOffset;
 | 
				
			||||||
 | 
						VERTEX.xyz +=pivotToCameraDir* aabb_max;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						NORMAL = normalize(pivotToCameraDir);
 | 
				
			||||||
 | 
						TANGENT= normalize(cross(NORMAL,vec3(0.0, 1.0, 0.0)));
 | 
				
			||||||
 | 
						BINORMAL = normalize(cross(TANGENT,NORMAL));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					vec4 blenderColors(vec2 uv_1, vec2 uv_2, vec2 uv_3, vec4 grid_weights, sampler2D atlasTexture)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						vec4 quad_a, quad_b, quad_c;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						quad_a = textureLod(atlasTexture, uv_1, 0.0f);
 | 
				
			||||||
 | 
						quad_b = textureLod(atlasTexture, uv_2, 0.0f);
 | 
				
			||||||
 | 
						quad_c = textureLod(atlasTexture, uv_3, 0.0f);
 | 
				
			||||||
 | 
						return quad_a * grid_weights.x + quad_b * grid_weights.y + quad_c * grid_weights.z;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					vec3 normal_from_normalmap(vec4 normalTex, vec3 tangent, vec3 binormal, vec3 f_norm)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						vec3 normalmap;
 | 
				
			||||||
 | 
						normalmap.xy = normalTex.xy * 2.0 - 1.0;
 | 
				
			||||||
 | 
						normalmap.z = sqrt(max(0.0, 1.0 - dot(normalmap.xy, normalmap.xy)));
 | 
				
			||||||
 | 
						normalmap = normalize(normalmap);
 | 
				
			||||||
 | 
						return normalize(tangent * normalmap.x + binormal * normalmap.y + f_norm * normalmap.z);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					vec3 blendedNormals(vec2 uv_1, vec3 f1_n,
 | 
				
			||||||
 | 
										vec2 uv_2, vec3 f2_n,
 | 
				
			||||||
 | 
										vec2 uv_3, vec3 f3_n,
 | 
				
			||||||
 | 
										vec3 tangent, vec3 binormal,
 | 
				
			||||||
 | 
										vec4 grid_weights, sampler2D atlasTexture)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						vec4 quad_a, quad_b, quad_c;
 | 
				
			||||||
 | 
						quad_a = textureLod(atlasTexture, uv_1, 0.0f);
 | 
				
			||||||
 | 
						quad_b = textureLod(atlasTexture, uv_2, 0.0f);
 | 
				
			||||||
 | 
						quad_c = textureLod(atlasTexture, uv_3, 0.0f);
 | 
				
			||||||
 | 
						vec3 norm1 = normal_from_normalmap(quad_a, tangent, binormal, f1_n);
 | 
				
			||||||
 | 
						vec3 norm2 = normal_from_normalmap(quad_b, tangent, binormal, f2_n);
 | 
				
			||||||
 | 
						vec3 norm3 = normal_from_normalmap(quad_c, tangent, binormal, f3_n);
 | 
				
			||||||
 | 
						return normalize(norm1 * grid_weights.x + norm2 * grid_weights.y + norm3 * grid_weights.z);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					vec2 recalculateUV(vec2 uv_f,  vec2 frame, vec2 xy_f, vec2 frame_size, float d_scale, sampler2D depthTexture)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						//clamp for parallax sampling
 | 
				
			||||||
 | 
						uv_f = clamp(uv_f, vec2(0), vec2(1));
 | 
				
			||||||
 | 
						vec2 uv_quad = frame_size * (frame + uv_f);
 | 
				
			||||||
 | 
						//paralax
 | 
				
			||||||
 | 
						vec4 n_depth = (textureLod( depthTexture, uv_quad, 0 ));
 | 
				
			||||||
 | 
						uv_f = xy_f * (0.5-n_depth.r) * d_scale + uv_f;
 | 
				
			||||||
 | 
						//clamp parallax offset
 | 
				
			||||||
 | 
						uv_f = clamp(uv_f, vec2(0), vec2(1));
 | 
				
			||||||
 | 
						uv_f =  frame_size * (frame + uv_f);
 | 
				
			||||||
 | 
						//clamped full UV
 | 
				
			||||||
 | 
						return clamp(uv_f, vec2(0), vec2(1));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void fragment()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						vec2 quad_size = vec2(1.0f) / imposterFrames;
 | 
				
			||||||
 | 
						vec2 uv_f1 = recalculateUV(uv_frame1, frame1, xy_frame1, quad_size, depth_scale, imposterTextureDepth);
 | 
				
			||||||
 | 
						vec2 uv_f2 = recalculateUV(uv_frame2, frame2, xy_frame2, quad_size, depth_scale, imposterTextureDepth);
 | 
				
			||||||
 | 
						vec2 uv_f3 = recalculateUV(uv_frame3, frame3, xy_frame3, quad_size, depth_scale, imposterTextureDepth);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						vec4 baseTex = blenderColors(uv_f1, uv_f2,  uv_f3, quad_blend_weights, imposterTextureAlbedo);
 | 
				
			||||||
 | 
						vec4 ormTex = blenderColors(uv_f1, uv_f2, uv_f3, quad_blend_weights, imposterTextureOrm);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						vec3 normalTex = blendedNormals(uv_f1, frame1_normal,
 | 
				
			||||||
 | 
													uv_f2, frame2_normal,
 | 
				
			||||||
 | 
													uv_f3, frame3_normal,
 | 
				
			||||||
 | 
													TANGENT, BINORMAL,
 | 
				
			||||||
 | 
														quad_blend_weights, imposterTextureNormal);
 | 
				
			||||||
 | 
						ALBEDO = baseTex.rgb * albedo.rgb;
 | 
				
			||||||
 | 
						NORMAL =normalTex.xyz;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if(dither)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							float opacity =  baseTex.a;
 | 
				
			||||||
 | 
							int x = int(FRAGCOORD.x) % 4;
 | 
				
			||||||
 | 
							int y = int(FRAGCOORD.y) % 4;
 | 
				
			||||||
 | 
							int index = x + y * 4;
 | 
				
			||||||
 | 
							float limit = 0.0;
 | 
				
			||||||
 | 
							if (x < 8) {
 | 
				
			||||||
 | 
								if (index == 0) limit = 0.0625;
 | 
				
			||||||
 | 
								if (index == 1) limit = 0.5625;
 | 
				
			||||||
 | 
								if (index == 2) limit = 0.1875;
 | 
				
			||||||
 | 
								if (index == 3) limit = 0.6875;
 | 
				
			||||||
 | 
								if (index == 4) limit = 0.8125;
 | 
				
			||||||
 | 
								if (index == 5) limit = 0.3125;
 | 
				
			||||||
 | 
								if (index == 6) limit = 0.9375;
 | 
				
			||||||
 | 
								if (index == 7) limit = 0.4375;
 | 
				
			||||||
 | 
								if (index == 8) limit = 0.25;
 | 
				
			||||||
 | 
								if (index == 9) limit = 0.75;
 | 
				
			||||||
 | 
								if (index == 10) limit = 0.125;
 | 
				
			||||||
 | 
								if (index == 11) limit = 0.625;
 | 
				
			||||||
 | 
								if (index == 12) limit = 1.0;
 | 
				
			||||||
 | 
								if (index == 13) limit = 0.5;
 | 
				
			||||||
 | 
								if (index == 14) limit = 0.875;
 | 
				
			||||||
 | 
								if (index == 15) limit = 0.375;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							// Is this pixel below the opacity limit? Skip drawing it
 | 
				
			||||||
 | 
							if (opacity < limit * alpha_clamp)
 | 
				
			||||||
 | 
							discard;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						else {
 | 
				
			||||||
 | 
							ALPHA = float(baseTex.a>alpha_clamp);
 | 
				
			||||||
 | 
							ALPHA_SCISSOR_THRESHOLD = 0.5;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						METALLIC = ormTex.b * metallic;
 | 
				
			||||||
 | 
						SPECULAR = specular;
 | 
				
			||||||
 | 
						ROUGHNESS = ormTex.g * roughness;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,278 @@
 | 
				
			||||||
 | 
					shader_type spatial;
 | 
				
			||||||
 | 
					render_mode blend_mix, depth_prepass_alpha, cull_back, unshaded;
 | 
				
			||||||
 | 
					uniform vec4 albedo : source_color = vec4(1, 1, 1, 1);
 | 
				
			||||||
 | 
					uniform float specular = 0.5f;
 | 
				
			||||||
 | 
					uniform float metallic = 0.0f;
 | 
				
			||||||
 | 
					uniform float roughness : hint_range(0, 1) = 1.0f;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					uniform sampler2D imposterTextureAlbedo : source_color;
 | 
				
			||||||
 | 
					uniform vec2 imposterFrames = vec2(16.0f, 16.0f);
 | 
				
			||||||
 | 
					uniform vec3 positionOffset = vec3(0.0f);
 | 
				
			||||||
 | 
					uniform bool isFullSphere = true;
 | 
				
			||||||
 | 
					uniform float alpha_clamp = 0.5f;
 | 
				
			||||||
 | 
					uniform float scale = 1.0f;
 | 
				
			||||||
 | 
					uniform float depth_scale : hint_range(0, 1) = 0.0f;
 | 
				
			||||||
 | 
					uniform float aabb_max = 1.0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					varying vec2 uv_frame1;
 | 
				
			||||||
 | 
					varying vec2 xy_frame1;
 | 
				
			||||||
 | 
					varying flat vec2 frame1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					varying vec4 quad_blend_weights;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					vec2 VecToSphereOct(vec3 pivotToCamera)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						vec3 octant = sign(pivotToCamera);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						//  |x| + |y| + |z| = 1
 | 
				
			||||||
 | 
						float sum = dot(pivotToCamera, octant);
 | 
				
			||||||
 | 
						vec3 octahedron = pivotToCamera / sum;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (octahedron.y < 0.0f)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							vec3 absolute = abs(octahedron);
 | 
				
			||||||
 | 
							octahedron.xz = octant.xz * vec2(1.0f - absolute.z, 1.0f - absolute.x);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return octahedron.xz;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					vec2 VecToHemiSphereOct(vec3 pivotToCamera)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						pivotToCamera.y = max(pivotToCamera.y, 0.001);
 | 
				
			||||||
 | 
						pivotToCamera = normalize(pivotToCamera);
 | 
				
			||||||
 | 
						vec3 octant = sign(pivotToCamera);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						//  |x| + |y| + |z| = 1
 | 
				
			||||||
 | 
						float sum = dot(pivotToCamera, octant);
 | 
				
			||||||
 | 
						vec3 octahedron = pivotToCamera / sum;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return vec2(
 | 
				
			||||||
 | 
						octahedron.x + octahedron.z,
 | 
				
			||||||
 | 
						octahedron.z - octahedron.x);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					vec2 VectorToGrid(vec3 vec)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (isFullSphere)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							return VecToSphereOct(vec);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							return VecToHemiSphereOct(vec);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//for sphere
 | 
				
			||||||
 | 
					vec3 OctaSphereEnc(vec2 coord)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						coord = (coord - 0.5) * 2.0;
 | 
				
			||||||
 | 
						vec3 position = vec3(coord.x, 0.0f, coord.y);
 | 
				
			||||||
 | 
						vec2 absolute = abs(position.xz);
 | 
				
			||||||
 | 
						position.y = 1.0f - absolute.x - absolute.y;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (position.y < 0.0f)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							position.xz = sign(position.xz) * vec2(1.0f - absolute.y, 1.0f - absolute.x);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return position;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//for hemisphere
 | 
				
			||||||
 | 
					vec3 OctaHemiSphereEnc(vec2 coord)
 | 
				
			||||||
 | 
					{	
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						vec3 position = vec3(coord.x - coord.y, 0.0f, -1.0 + coord.x + coord.y);
 | 
				
			||||||
 | 
						vec2 absolute = abs(position.xz);
 | 
				
			||||||
 | 
						position.y = 1.0f - absolute.x - absolute.y;
 | 
				
			||||||
 | 
						return position;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					vec3 GridToVector(vec2 coord)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (isFullSphere)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							return OctaSphereEnc(coord);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							return OctaHemiSphereEnc(coord);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					vec3 FrameXYToRay(vec2 frame, vec2 frameCountMinusOne)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						//divide frame x y by framecount minus one to get 0-1
 | 
				
			||||||
 | 
						vec2 f = (frame.xy/ frameCountMinusOne);
 | 
				
			||||||
 | 
						//bias and scale to -1 to 1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						vec3 vec = GridToVector(f);
 | 
				
			||||||
 | 
						vec = normalize(vec);
 | 
				
			||||||
 | 
						return vec;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					vec3 SpriteProjection(vec3 pivotToCameraRayLocal, vec2 size, vec2 loc_uv)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						vec3 z = normalize(pivotToCameraRayLocal);
 | 
				
			||||||
 | 
						vec3 x, y;
 | 
				
			||||||
 | 
						vec3 up = vec3(0,1,0);
 | 
				
			||||||
 | 
							//cross product doesnt work if we look directly from bottom
 | 
				
			||||||
 | 
						if (abs(z.y) > 0.999f)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							up = vec3(0,0,-1);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						x = normalize(cross(up, z));
 | 
				
			||||||
 | 
						y = normalize(cross(x, z));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						loc_uv -= vec2(0.5,0.5);
 | 
				
			||||||
 | 
						vec2 uv = (loc_uv) * 2.0; //-1 to 1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						vec3 newX = x * uv.x;
 | 
				
			||||||
 | 
						vec3 newY = y * uv.y;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						vec2 vecSize = size * 0.5;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						newX *= vecSize.x;
 | 
				
			||||||
 | 
						newY *= vecSize.y;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return newX + newY;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					vec4 quadBlendWieghts(vec2 coords)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						vec4 res;
 | 
				
			||||||
 | 
						/* 0 0 0
 | 
				
			||||||
 | 
						0 0 0
 | 
				
			||||||
 | 
						1 0 0 */
 | 
				
			||||||
 | 
						res.x = min(1.0f - coords.x, 1.0f - coords.y);
 | 
				
			||||||
 | 
						/* 1 0 0
 | 
				
			||||||
 | 
						0 0 0
 | 
				
			||||||
 | 
						0 0 1 */
 | 
				
			||||||
 | 
						res.y = abs(coords.x - coords.y);
 | 
				
			||||||
 | 
						/* 0 0 1
 | 
				
			||||||
 | 
						0 0 0
 | 
				
			||||||
 | 
						0 0 0 */
 | 
				
			||||||
 | 
						res.z = min(coords.x, coords.y);
 | 
				
			||||||
 | 
						/* 0 0 0
 | 
				
			||||||
 | 
						0 0 1
 | 
				
			||||||
 | 
						0 1 1 */
 | 
				
			||||||
 | 
						res.w = ceil(coords.x - coords.y);
 | 
				
			||||||
 | 
						//res.xyz /= (res.x + res.y + res.z);
 | 
				
			||||||
 | 
						return res;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//this function works well in orthogonal projection. It works okeyish with further distances of perspective projection
 | 
				
			||||||
 | 
					vec2 virtualPlaneUV(vec3 plane_normal,vec3  plane_x, vec3  plane_y, vec3 pivotToCameraRay, vec3 vertexToCameraRay, float size)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						plane_normal = normalize(plane_normal);
 | 
				
			||||||
 | 
						plane_x = normalize(plane_x);
 | 
				
			||||||
 | 
						plane_y = normalize(plane_y);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						float projectedNormalRayLength = dot(plane_normal, pivotToCameraRay);
 | 
				
			||||||
 | 
						float projectedVertexRayLength = dot(plane_normal, vertexToCameraRay);
 | 
				
			||||||
 | 
						float offsetLength = projectedNormalRayLength/projectedVertexRayLength;
 | 
				
			||||||
 | 
						vec3 offsetVector = vertexToCameraRay * offsetLength - pivotToCameraRay;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						vec2 duv = vec2(
 | 
				
			||||||
 | 
									dot(plane_x , offsetVector),
 | 
				
			||||||
 | 
									dot(plane_y, offsetVector)
 | 
				
			||||||
 | 
						);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						//we are in space -1 to 1
 | 
				
			||||||
 | 
						duv /= 2.0 * size;
 | 
				
			||||||
 | 
						duv += 0.5;
 | 
				
			||||||
 | 
						return duv;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void calcuateXYbasis(vec3 plane_normal, out vec3 plane_x, out vec3 plane_y)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						vec3 up = vec3(0,1,0);
 | 
				
			||||||
 | 
							//cross product doesnt work if we look directly from bottom
 | 
				
			||||||
 | 
						if (abs(plane_normal.y) > 0.999f)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							up = vec3(0,0,1);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						plane_x = normalize(cross(plane_normal, up));
 | 
				
			||||||
 | 
						plane_y = normalize(cross(plane_x, plane_normal));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					vec3 projectOnPlaneBasis(vec3 ray, vec3 plane_normal, vec3 plane_x, vec3 plane_y)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						//reproject plane normal onto planeXY basos
 | 
				
			||||||
 | 
						return normalize(vec3( 
 | 
				
			||||||
 | 
							dot(plane_x,ray), 
 | 
				
			||||||
 | 
							dot(plane_y,ray), 
 | 
				
			||||||
 | 
							dot(plane_normal,ray) 
 | 
				
			||||||
 | 
						));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void vertex()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						vec2 framesMinusOne = imposterFrames - vec2(1);
 | 
				
			||||||
 | 
						vec3 cameraPos_WS = (INV_VIEW_MATRIX * vec4(vec3(0), 1.0)).xyz;
 | 
				
			||||||
 | 
						vec3 cameraPos_OS = (inverse(MODEL_MATRIX) * vec4(cameraPos_WS, 1.0)).xyz;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						//TODO: check if this is correct. We are using orho projected images, so
 | 
				
			||||||
 | 
						// camera far away
 | 
				
			||||||
 | 
						vec3 pivotToCameraRay = (cameraPos_OS) * 10.0;
 | 
				
			||||||
 | 
						vec3 pivotToCameraDir = normalize(cameraPos_OS);
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						vec2 grid = VectorToGrid(pivotToCameraDir);
 | 
				
			||||||
 | 
						//bias and scale to 0 to 1
 | 
				
			||||||
 | 
						grid = clamp((grid + 1.0) * 0.5, vec2(0, 0), vec2(1, 1));
 | 
				
			||||||
 | 
						grid *= framesMinusOne;
 | 
				
			||||||
 | 
						grid = clamp(grid, vec2(0), vec2(framesMinusOne));
 | 
				
			||||||
 | 
						vec2 gridFloor = min(floor(grid), framesMinusOne);
 | 
				
			||||||
 | 
						vec2 gridFract = fract(grid);
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						//radius * 2
 | 
				
			||||||
 | 
						vec2 size = vec2(2.0) * scale;
 | 
				
			||||||
 | 
						vec3 projected = SpriteProjection(pivotToCameraDir, size, UV);
 | 
				
			||||||
 | 
						vec3 vertexToCameraRay = (pivotToCameraRay - (projected));
 | 
				
			||||||
 | 
						vec3 vertexToCameraDir = normalize(vertexToCameraRay);
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						frame1 = gridFloor;
 | 
				
			||||||
 | 
						quad_blend_weights = quadBlendWieghts(gridFract);
 | 
				
			||||||
 | 
						//convert frame coordinate to octahedron direction
 | 
				
			||||||
 | 
						vec3 projectedQuadADir = FrameXYToRay(frame1, framesMinusOne);
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						//calcute virtual planes projections
 | 
				
			||||||
 | 
						vec3 plane_x1, plane_y1;
 | 
				
			||||||
 | 
						calcuateXYbasis(projectedQuadADir, plane_x1, plane_y1);
 | 
				
			||||||
 | 
						uv_frame1 = virtualPlaneUV(projectedQuadADir, plane_x1, plane_y1, pivotToCameraRay, vertexToCameraRay, scale);
 | 
				
			||||||
 | 
						xy_frame1 = projectOnPlaneBasis(-vertexToCameraDir, projectedQuadADir, plane_x1, plane_y1).xy;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						//to fragment shader
 | 
				
			||||||
 | 
						VERTEX.xyz = projected + positionOffset;
 | 
				
			||||||
 | 
						VERTEX.xyz +=pivotToCameraDir* aabb_max;
 | 
				
			||||||
 | 
						vec3 v0 = abs(NORMAL.y) < 0.999 ? vec3(0.0, 1.0, 0.0) : vec3(0.0, 0.0, 1.0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						NORMAL = normalize(pivotToCameraDir);
 | 
				
			||||||
 | 
						TANGENT= normalize(cross(NORMAL,v0));
 | 
				
			||||||
 | 
						BINORMAL = normalize(cross(TANGENT,NORMAL));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					vec2 recalculateUV(vec2 uv_f,  vec2 frame, vec2 xy_f, vec2 frame_size)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						//clamp for parallax sampling
 | 
				
			||||||
 | 
						uv_f = clamp(uv_f, vec2(0), vec2(1));
 | 
				
			||||||
 | 
						return frame_size * (frame + uv_f);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void fragment()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						vec2 quad_size = vec2(1.0f) / imposterFrames;
 | 
				
			||||||
 | 
						vec2 uv_f1 = recalculateUV(uv_frame1, frame1, xy_frame1, quad_size);
 | 
				
			||||||
 | 
						vec4 baseTex = textureLod(imposterTextureAlbedo, uv_f1, 0.0f);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ALBEDO = baseTex.rgb;
 | 
				
			||||||
 | 
						ALPHA = float(baseTex.a>alpha_clamp);
 | 
				
			||||||
 | 
						ALPHA_SCISSOR_THRESHOLD = 0.5;
 | 
				
			||||||
 | 
						ROUGHNESS = 1.0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,29 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					using Godot;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Rokojori
 | 
				
			||||||
 | 
					{  
 | 
				
			||||||
 | 
					  [Tool]
 | 
				
			||||||
 | 
					  [GlobalClass]
 | 
				
			||||||
 | 
					  public partial class TriggerActionInEditor : Node
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public RJAction action;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public bool execute;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public override void _Process( double delta )
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      if ( ! execute )
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      execute = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      action.Trigger();
 | 
				
			||||||
 | 
					    } 
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -54,6 +54,16 @@ namespace Rokojori
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public bool hasFileExtension( string extension )
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      if ( fileExtension == null )
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        return false;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      return fileExtension.ToLower() == extension.ToLower();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public string fullPath
 | 
					    public string fullPath
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
      get
 | 
					      get
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,37 @@
 | 
				
			||||||
 | 
					using Godot;
 | 
				
			||||||
 | 
					using System.Text;
 | 
				
			||||||
 | 
					using System.Collections.Generic;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Rokojori
 | 
				
			||||||
 | 
					{  
 | 
				
			||||||
 | 
					  public class Cameras
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    public static float ComputeCameraFrameFittingDistance( Camera3D camera, float radius )
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      var fovRadians = Mathf.DegToRad( camera.Fov );
 | 
				
			||||||
 | 
					      return ( radius * 2 ) / Mathf.Tan( fovRadians / 2.0f ); 
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static float ComputeCameraFrameFittingDistance( float fovDegrees, float radius )
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      var fovRadians = Mathf.DegToRad( fovDegrees );
 | 
				
			||||||
 | 
					      return ( radius * 2 ) / Mathf.Tan( fovRadians / 2.0f ); 
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static float ComputeCameraFittingScale( float fovDegrees, float distance )
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      var fovRadians = Mathf.DegToRad( fovDegrees );
 | 
				
			||||||
 | 
					      return distance / ( 0.5f / Mathf.Tan( fovRadians / 2.0f ) ); 
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static float ComputeFOVForBillboard( float fovDegrees, float radius, float placingDistance )
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      var fovRadians = Mathf.DegToRad( fovDegrees );
 | 
				
			||||||
 | 
					      var d = ( radius * Mathf.Tan( fovRadians / 2f ) ) / placingDistance; 
 | 
				
			||||||
 | 
					      var rads = 2f * Mathf.Atan( d );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      return Mathf.RadToDeg( rads ); 
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -5,7 +5,7 @@ using System;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace Rokojori
 | 
					namespace Rokojori
 | 
				
			||||||
{  
 | 
					{  
 | 
				
			||||||
  public class Nodes
 | 
					  public static class Nodes
 | 
				
			||||||
  {
 | 
					  {
 | 
				
			||||||
    public static T Find<T>( Node root, NodePathLocatorType type = NodePathLocatorType.DirectChildren, int parentOffset = 0 ) where T:Node
 | 
					    public static T Find<T>( Node root, NodePathLocatorType type = NodePathLocatorType.DirectChildren, int parentOffset = 0 ) where T:Node
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
| 
						 | 
					@ -93,12 +93,17 @@ namespace Rokojori
 | 
				
			||||||
      return list;
 | 
					      return list;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public static List<T> AllIn<T>( Node root, Func<T,bool> filter = null) where T:class
 | 
					    public static List<T> AllIn<T>( Node root, Func<T,bool> filter = null, bool includeRoot = true) where T:class
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
      var list = new List<T>();
 | 
					      var list = new List<T>();
 | 
				
			||||||
      ForEach<T>( root,
 | 
					      ForEach<T>( root,
 | 
				
			||||||
        t => 
 | 
					        t => 
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
 | 
					          if ( ! includeRoot && t == root )
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            return;          
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
          if ( filter == null || filter( t ) )
 | 
					          if ( filter == null || filter( t ) )
 | 
				
			||||||
          { 
 | 
					          { 
 | 
				
			||||||
            list.Add( t );
 | 
					            list.Add( t );
 | 
				
			||||||
| 
						 | 
					@ -144,6 +149,54 @@ namespace Rokojori
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      return GetDirectChild<T>( parent );
 | 
					      return GetDirectChild<T>( parent );
 | 
				
			||||||
 | 
					    } 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static T CreateChildIn<T>( Node parent, string name = null ) where T:Node,new()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      var t = new T();
 | 
				
			||||||
 | 
					      parent.AddChild( t );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      t.Owner = parent.Owner;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if ( name != null )
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        t.Name = name;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      return t;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static T CreateChild<T>( this Node parent, string name = null ) where T:Node,new()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      return CreateChildIn<T>( parent, name );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static Node CopyNode( Node node, Node parent )
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      var copy = node.Duplicate();
 | 
				
			||||||
 | 
					      
 | 
				
			||||||
 | 
					      parent.AddChild( copy );
 | 
				
			||||||
 | 
					      copy.Owner = parent.Owner;
 | 
				
			||||||
 | 
					      
 | 
				
			||||||
 | 
					      return copy;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static Node DeepCopyTo( this Node node, Node parent )
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      return CopyNodeHierarchy( node, parent );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static Node CopyNodeHierarchy( Node from, Node parent )
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      var copy = CopyNode( from, parent );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      for ( int i = 0; i < from.GetChildCount(); i++ )
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        var child = from.GetChild( i );
 | 
				
			||||||
 | 
					        CopyNodeHierarchy( child, copy );
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      return copy;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public static void RemoveAndDelete( Node node )
 | 
					    public static void RemoveAndDelete( Node node )
 | 
				
			||||||
| 
						 | 
					@ -158,6 +211,37 @@ namespace Rokojori
 | 
				
			||||||
      node.QueueFree();
 | 
					      node.QueueFree();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static void RemoveAndDeleteAll<N>( List<N> nodes ) where N:Node
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      nodes.ForEach( n => RemoveAndDelete( n ) );
 | 
				
			||||||
 | 
					    } 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static void RemoveAndDeleteChildrenOfType<T>( Node parent, bool includeInternal = false ) where T:Node
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      if ( parent == null )
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      var numChildren = parent.GetChildCount( includeInternal );
 | 
				
			||||||
 | 
					      
 | 
				
			||||||
 | 
					      for ( int i = numChildren - 1; i >= 0; i-- )
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        var node = parent.GetChild( i, includeInternal );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        var t = node as T;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if ( t == null )
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					          continue;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        parent.RemoveChild( node );
 | 
				
			||||||
 | 
					        node.QueueFree();
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public static void RemoveAndDeleteChildren( Node parent, bool includeInternal = false )
 | 
					    public static void RemoveAndDeleteChildren( Node parent, bool includeInternal = false )
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
      if ( parent == null )
 | 
					      if ( parent == null )
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,69 @@
 | 
				
			||||||
 | 
					using Godot;
 | 
				
			||||||
 | 
					using System.Collections;
 | 
				
			||||||
 | 
					using System.Collections.Generic;
 | 
				
			||||||
 | 
					using Godot.Collections;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Rokojori
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  [GlobalClass]
 | 
				
			||||||
 | 
					  public partial class CharacterController:Node
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public CharacterBody3D body;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public enum CharacterUpdateMode
 | 
				
			||||||
 | 
					    { 
 | 
				
			||||||
 | 
					      Process,
 | 
				
			||||||
 | 
					      Physics_Process
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public CharacterUpdateMode characterUpdateMode = CharacterUpdateMode.Process;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public Node actionsContainer;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public Node3D graphics;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public float rotationSmoothingDuration = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Smoother rotationSmoother = new Smoother();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public float positionSmoothingDuration = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Smoother positionSmoother = new Smoother();
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    public float delta = 0;   
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public override void _Process( double delta )
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      if ( graphics == null || body == null )
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if ( CharacterUpdateMode.Process == characterUpdateMode )
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        this.delta = (float)delta;
 | 
				
			||||||
 | 
					        Nodes.ForEach<CharacterControllerAction>( actionsContainer, a => Actions.Trigger( a ) );
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      positionSmoother.CopyPosition( graphics, body, rotationSmoothingDuration, delta );
 | 
				
			||||||
 | 
					      rotationSmoother.CopyRotation( graphics, body, positionSmoothingDuration, delta );    
 | 
				
			||||||
 | 
					    } 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public override void _PhysicsProcess( double delta )
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      if ( CharacterUpdateMode.Physics_Process == characterUpdateMode )
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        this.delta = (float)delta;
 | 
				
			||||||
 | 
					        Nodes.ForEach<RJAction>( actionsContainer, a => Actions.Trigger( a ) );
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,32 @@
 | 
				
			||||||
 | 
					using Godot;
 | 
				
			||||||
 | 
					using System.Collections;
 | 
				
			||||||
 | 
					using System.Collections.Generic;
 | 
				
			||||||
 | 
					using Godot.Collections;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Rokojori
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  [GlobalClass]
 | 
				
			||||||
 | 
					  public partial class CharacterControllerAction:RJAction
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public CharacterController controller;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public CharacterBody3D body => controller.body;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void SetVelocity( Vector3 velocity, bool replace )
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      if ( replace )
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        body.Velocity = velocity * controller.delta;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      else
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        body.Velocity += velocity * controller.delta;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,59 @@
 | 
				
			||||||
 | 
					using Godot;
 | 
				
			||||||
 | 
					using System.Collections;
 | 
				
			||||||
 | 
					using System.Collections.Generic;
 | 
				
			||||||
 | 
					using Godot.Collections;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Rokojori
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  [GlobalClass]
 | 
				
			||||||
 | 
					  public partial class CharacterMovement:CharacterControllerAction
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public float onFloorMultiply = 0f;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public bool overwriteVelocity = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [ExportGroup( "Moving" )]
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public float moveSpeed;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public RJSensor forward;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public RJSensor backwards;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [ExportGroup( "Strafing" )]
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public float strafeSpeed;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public RJSensor strafeLeft;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public RJSensor strafeRight;   
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public override void _OnTrigger()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      var movement = Vector3.Zero;
 | 
				
			||||||
 | 
					      var body = controller.body;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      movement += body.GlobalForward() * Sensors.GetValue( forward );
 | 
				
			||||||
 | 
					      movement -= body.GlobalForward() * Sensors.GetValue( backwards );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      movement += body.GlobalRight() * Sensors.GetValue( strafeRight );
 | 
				
			||||||
 | 
					      movement -= body.GlobalRight() * Sensors.GetValue( strafeLeft );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if ( body.IsOnFloor() )
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        movement *= onFloorMultiply;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      SetVelocity( movement, overwriteVelocity );
 | 
				
			||||||
 | 
					      
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,16 @@
 | 
				
			||||||
 | 
					using Godot;
 | 
				
			||||||
 | 
					using System.Collections;
 | 
				
			||||||
 | 
					using System.Collections.Generic;
 | 
				
			||||||
 | 
					using Godot.Collections;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Rokojori
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  [GlobalClass]
 | 
				
			||||||
 | 
					  public partial class MoveAndSlide:CharacterControllerAction
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    public override void _OnTrigger()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      controller.body.MoveAndSlide();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,96 @@
 | 
				
			||||||
 | 
					using Godot;
 | 
				
			||||||
 | 
					using System.Collections;
 | 
				
			||||||
 | 
					using System.Collections.Generic;
 | 
				
			||||||
 | 
					using Godot.Collections;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Rokojori
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  [Tool]
 | 
				
			||||||
 | 
					  [GlobalClass]
 | 
				
			||||||
 | 
					  public partial class LODParent:Node3D
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public float cullDistance = 4000;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public Curve distribution = MathX.Curve( 0, 1 );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public Node3D[] lods;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public float updateDistance = 10;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    float lastSquaredDistance = -1;
 | 
				
			||||||
 | 
					    int lastSelectedIndex = -1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public int changeBlock = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public int blocker = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public override void _Process( double delta )
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      if ( blocker > 0 )
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        blocker --;
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if ( lods == null || lods.Length == 0 )
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      var camera = GetViewport().GetCamera3D();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#if TOOLS 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if ( Engine.IsEditorHint() )
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        camera = EditorInterface.Singleton.GetEditorViewport3D().GetCamera3D();
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					       
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      var squaredDistance = GlobalPosition.DistanceSquaredTo( camera.GlobalPosition );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if ( Mathf.Abs( lastSquaredDistance - squaredDistance ) < updateDistance * updateDistance )
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					      } 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      lastSquaredDistance = squaredDistance;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      var realDistance = Mathf.Sqrt( lastSquaredDistance );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      var normalizedDistance = MathX.NormalizeClamped( realDistance, 0, cullDistance );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      var active = distribution.Sample( normalizedDistance ) * lods.Length;
 | 
				
			||||||
 | 
					      var selectedIndex = Mathf.Min( lods.Length - 1, Mathf.RoundToInt( active ) ); 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      
 | 
				
			||||||
 | 
					      if ( lastSelectedIndex == selectedIndex )
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      // RJLog.Log( 
 | 
				
			||||||
 | 
					      //   "realDistance:", realDistance,
 | 
				
			||||||
 | 
					      //   "normalizedDistance:", normalizedDistance,
 | 
				
			||||||
 | 
					      //   "active:", active,
 | 
				
			||||||
 | 
					      //   "selectedIndex:", selectedIndex
 | 
				
			||||||
 | 
					      // );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      var selectedLOD = lods[ selectedIndex ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      lastSelectedIndex = selectedIndex;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      Arrays.ForEach( lods, l => NodeState.Set( l, selectedLOD == l ) );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      blocker = changeBlock;
 | 
				
			||||||
 | 
					    } 
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -28,6 +28,8 @@ namespace Rokojori
 | 
				
			||||||
      return new Box2( min, max );
 | 
					      return new Box2( min, max );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public Vector2 center => ( min + max ) / 2;
 | 
				
			||||||
 | 
					    public Vector2 size =>  max - min;
 | 
				
			||||||
    public void UnionWith( Box2 other )
 | 
					    public void UnionWith( Box2 other )
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
      min = min.Min( other.min );
 | 
					      min = min.Min( other.min );
 | 
				
			||||||
| 
						 | 
					@ -46,7 +48,6 @@ namespace Rokojori
 | 
				
			||||||
      max = max.Max( p );
 | 
					      max = max.Max( p );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public Vector2 size => max - min;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public void GrowRelativeToSize( float amount )
 | 
					    public void GrowRelativeToSize( float amount )
 | 
				
			||||||
    {     
 | 
					    {     
 | 
				
			||||||
| 
						 | 
					@ -68,6 +69,22 @@ namespace Rokojori
 | 
				
			||||||
      return true;
 | 
					      return true;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public float DistanceTo( Vector2 point )
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      if ( ContainsPoint( point ) )
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        return 0;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      var c = center;
 | 
				
			||||||
 | 
					      var s = size;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      var dx = Mathf.Max( Mathf.Abs( point.X - c.X) - s.X / 2, 0);
 | 
				
			||||||
 | 
					      var dy = Mathf.Max( Mathf.Abs( point.Y - c.Y) - s.Y / 2, 0);
 | 
				
			||||||
 | 
					      
 | 
				
			||||||
 | 
					      return Mathf.Sqrt( dx * dx + dy * dy ); 
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public void EnsureCorrectness()
 | 
					    public void EnsureCorrectness()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -9,6 +9,13 @@ namespace Rokojori
 | 
				
			||||||
    public Vector3 min = Vector3.Zero;
 | 
					    public Vector3 min = Vector3.Zero;
 | 
				
			||||||
    public Vector3 max = Vector3.Zero;
 | 
					    public Vector3 max = Vector3.Zero;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public Vector3 center => ( max + min ) / 2f;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   public static implicit operator Box3( Aabb aabb )
 | 
				
			||||||
 | 
					    {  
 | 
				
			||||||
 | 
					      return Box3.Create( aabb.Position, aabb.End );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public static Box3 FromPositionAndScale( Vector3 position, Vector3 scale )
 | 
					    public static Box3 FromPositionAndScale( Vector3 position, Vector3 scale )
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
      var max = scale * 0.5f;
 | 
					      var max = scale * 0.5f;
 | 
				
			||||||
| 
						 | 
					@ -44,6 +51,8 @@ namespace Rokojori
 | 
				
			||||||
      return point;
 | 
					      return point;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public float maxDistance => ( max - min ).Length();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public static Vector3 Constrain( Vector3 point, Vector3 min, Vector3 max )
 | 
					    public static Vector3 Constrain( Vector3 point, Vector3 min, Vector3 max )
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
      point = min.Max( point );
 | 
					      point = min.Max( point );
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -69,14 +69,26 @@ namespace Rokojori
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Vector3? explicitUp = null;
 | 
				
			||||||
    public Vector3 up
 | 
					    public Vector3 up
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
      get
 | 
					      get
 | 
				
			||||||
      {
 | 
					      {
 | 
				
			||||||
 | 
					        if ( explicitUp != null )
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					          return (Vector3) explicitUp;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        Update();
 | 
					        Update();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return _basis.Y; 
 | 
					        return _basis.Y; 
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      set
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        explicitUp = value;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public Pose()
 | 
					    public Pose()
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -58,6 +58,12 @@ namespace Rokojori
 | 
				
			||||||
      var offset = radius * Vector3.One;
 | 
					      var offset = radius * Vector3.One;
 | 
				
			||||||
      return Box3.Create( center -offset, center + offset );
 | 
					      return Box3.Create( center -offset, center + offset );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static Sphere ContainingBox( Box3 box )
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      return new Sphere( box.center, box.maxDistance / 2f );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -242,6 +242,13 @@ namespace Rokojori
 | 
				
			||||||
      return aligned.GetRotationQuaternion();
 | 
					      return aligned.GetRotationQuaternion();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static Quaternion AlignUp( Vector3 upDirection, Quaternion? q = null )
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      var quaternion = q == null ? Quaternion.Identity : (Quaternion) q;
 | 
				
			||||||
 | 
					      return AlignUp( quaternion, upDirection );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public static float AngleXY( Vector3 direction )
 | 
					    public static float AngleXY( Vector3 direction )
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
      return Mathf.Atan2( direction.Y, direction.X );
 | 
					      return Mathf.Atan2( direction.Y, direction.X );
 | 
				
			||||||
| 
						 | 
					@ -305,6 +312,11 @@ namespace Rokojori
 | 
				
			||||||
      return Quaternion.FromEuler( new Vector3( radians, 0, 0 ) );
 | 
					      return Quaternion.FromEuler( new Vector3( radians, 0, 0 ) );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static Quaternion RotateXDegrees( float degrees )
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      return RotateX( Mathf.DegToRad( degrees ) );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public static Quaternion RotateY( float radians )
 | 
					    public static Quaternion RotateY( float radians )
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
      if ( radians == 0 ) { return Quaternion.Identity; }
 | 
					      if ( radians == 0 ) { return Quaternion.Identity; }
 | 
				
			||||||
| 
						 | 
					@ -312,6 +324,11 @@ namespace Rokojori
 | 
				
			||||||
      return Quaternion.FromEuler( new Vector3( 0, radians, 0 ) );
 | 
					      return Quaternion.FromEuler( new Vector3( 0, radians, 0 ) );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static Quaternion RotateYDegrees( float degrees )
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      return RotateY( Mathf.DegToRad( degrees ) );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public static Quaternion RotateZ( float radians )
 | 
					    public static Quaternion RotateZ( float radians )
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
      if ( radians == 0 ) { return Quaternion.Identity; }
 | 
					      if ( radians == 0 ) { return Quaternion.Identity; }
 | 
				
			||||||
| 
						 | 
					@ -319,18 +336,21 @@ namespace Rokojori
 | 
				
			||||||
      return Quaternion.FromEuler( new Vector3( 0, 0, radians ) );
 | 
					      return Quaternion.FromEuler( new Vector3( 0, 0, radians ) );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static Quaternion RotateZDegrees( float degrees )
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      return RotateZ( Mathf.DegToRad( degrees ) );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static Quaternion YawPitchRotation( float yaw, float pitch )
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      return RotateXDegrees( pitch ) * RotateYDegrees( yaw );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public static void SetGlobalQuaternion( this Node3D node, Quaternion quaternion )
 | 
					    public static void SetGlobalQuaternion( this Node3D node, Quaternion quaternion )
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
      var offset = node.GlobalPosition;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      var localScale = node.Scale;
 | 
					      var localScale = node.Scale;
 | 
				
			||||||
      node.GlobalBasis = new Basis( quaternion );
 | 
					      node.GlobalBasis = new Basis( quaternion );
 | 
				
			||||||
      node.GlobalPosition = offset;
 | 
					 | 
				
			||||||
      node.Scale = localScale;
 | 
					      node.Scale = localScale;
 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      //SetGlobalRotationTo( node, quaternion );
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public static void SetGlobalRotationTo( Node3D node, Quaternion quaternion )
 | 
					    public static void SetGlobalRotationTo( Node3D node, Quaternion quaternion )
 | 
				
			||||||
| 
						 | 
					@ -382,6 +402,17 @@ namespace Rokojori
 | 
				
			||||||
      return -node.GlobalBasis.Z;
 | 
					      return -node.GlobalBasis.Z;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static Vector3 GetGlobalScale( Node3D node )
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      return node.GlobalTransform.Basis.Scale;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static float GetGlobalUniScale( Node3D node )
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      var scale3 = GetGlobalScale( node );
 | 
				
			||||||
 | 
					      return MathX.Max( scale3.X, scale3.Y, scale3.Z );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public static Vector3 GlobalUp( this Node3D node )
 | 
					    public static Vector3 GlobalUp( this Node3D node )
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
      return GetGlobalUp( node );
 | 
					      return GetGlobalUp( node );
 | 
				
			||||||
| 
						 | 
					@ -447,20 +478,66 @@ namespace Rokojori
 | 
				
			||||||
      node.GlobalPosition = gp;
 | 
					      node.GlobalPosition = gp;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public static Aabb? GetWorldBounds( this Node3D node )
 | 
					    public static Vector3 Average( List<Vector3> vectors )
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
      return GetWorldBoundsFrom( node );
 | 
					      var average = Vector3.Zero;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      vectors.ForEach( v => average += v );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if ( average == Vector3.Zero )
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        return vectors[ 0 ];
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      return ( average / vectors.Count ).Normalized();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public static Aabb? GetWorldBoundsFrom( Node3D node )
 | 
					    public static Vector3 BlendNormals( Vector3 a, Vector3 b, float amount )
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      if ( amount <= 0 )
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        return a;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if ( amount >= 1 )
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        return b;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      var n = a.Lerp( b, amount );
 | 
				
			||||||
 | 
					      var length = n.Length();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if ( length > 0 )
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        return n / length;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      return amount <= 0.5 ? a : b;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static Aabb? GetWorldBounds( this Node3D node, bool onlyVisible = true )
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      return GetWorldBoundsFrom( node, onlyVisible );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static Aabb? GetWorldBoundsFrom( Node3D node, bool onlyVisible = true )
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
      Aabb? worldBounds = null;
 | 
					      Aabb? worldBounds = null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      Nodes.ForEach<VisualInstance3D>( node,
 | 
					      Nodes.ForEach<VisualInstance3D>( node,
 | 
				
			||||||
        ( vi )=>
 | 
					        ( vi )=>
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
 | 
					          if ( onlyVisible && ! vi.IsVisibleInTree() )
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
          var nBounds = vi.GetAabb();
 | 
					          var nBounds = vi.GetAabb();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          nBounds.Size *= GetGlobalUniScale( vi );
 | 
				
			||||||
 | 
					          nBounds.Position += vi.GlobalPosition;
 | 
				
			||||||
 | 
					          nBounds.End += vi.GlobalPosition;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
          worldBounds = worldBounds == null ? nBounds : ( ((Aabb)worldBounds).Merge( nBounds ) );
 | 
					          worldBounds = worldBounds == null ? nBounds : ( ((Aabb)worldBounds).Merge( nBounds ) );
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      );
 | 
					      );
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -35,7 +35,7 @@ namespace Rokojori
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public static float Min( params float[] values )
 | 
					    public static float Min( params float[] values )
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
      var value = - float.MaxValue;
 | 
					      var value = float.MaxValue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      for ( int i = 0; i < values.Length; i++ )
 | 
					      for ( int i = 0; i < values.Length; i++ )
 | 
				
			||||||
      {
 | 
					      {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -10,8 +10,8 @@ namespace Rokojori
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public float SmoothForDuration( float value, float nextValue, float duration, float delta, float processDelta = MathX.fps120Delta )
 | 
					    public float SmoothForDuration( float value, float nextValue, float duration, float delta, float processDelta = MathX.fps120Delta )
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
      var coefficient = MathX.SmoothingCoefficient( duration * 1000f );
 | 
					      var coefficient = MathX.SmoothingCoefficient( duration * 1000f, processDelta );
 | 
				
			||||||
      return SmoothWithCoefficient( value, nextValue, coefficient, delta );
 | 
					      return SmoothWithCoefficient( value, nextValue, coefficient, delta, processDelta );
 | 
				
			||||||
    } 
 | 
					    } 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -31,8 +31,8 @@ namespace Rokojori
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public Quaternion SmoothForDuration( Quaternion value, Quaternion nextValue, float duration, float delta, float processDelta = MathX.fps120Delta )
 | 
					    public Quaternion SmoothForDuration( Quaternion value, Quaternion nextValue, float duration, float delta, float processDelta = MathX.fps120Delta )
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
      var coefficient = MathX.SmoothingCoefficient( duration * 1000f );
 | 
					      var coefficient = MathX.SmoothingCoefficient( duration * 1000f, processDelta );
 | 
				
			||||||
      return SmoothWithCoefficient( value, nextValue, coefficient, delta );
 | 
					      return SmoothWithCoefficient( value, nextValue, coefficient, delta, processDelta );
 | 
				
			||||||
    } 
 | 
					    } 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public Quaternion SmoothWithCoefficient( Quaternion value, Quaternion nextValue, float coefficient, float delta, float processDelta = MathX.fps120Delta )
 | 
					    public Quaternion SmoothWithCoefficient( Quaternion value, Quaternion nextValue, float coefficient, float delta, float processDelta = MathX.fps120Delta )
 | 
				
			||||||
| 
						 | 
					@ -46,6 +46,45 @@ namespace Rokojori
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      return value;
 | 
					      return value;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public Vector3 SmoothForDuration( Vector3 value, Vector3 nextValue, float duration, float delta, float processDelta = MathX.fps120Delta )
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      var coefficient = MathX.SmoothingCoefficient( duration * 1000f, processDelta );
 | 
				
			||||||
 | 
					      return SmoothWithCoefficient( value, nextValue, coefficient, delta, processDelta );
 | 
				
			||||||
    } 
 | 
					    } 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public Vector3 SmoothWithCoefficient( Vector3 value, Vector3 nextValue, float coefficient, float delta, float processDelta = MathX.fps120Delta )
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      overflowDelta += delta;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      while ( overflowDelta > processDelta )
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        value = value.Lerp( nextValue, coefficient );
 | 
				
			||||||
 | 
					        overflowDelta -= processDelta;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      return value;
 | 
				
			||||||
 | 
					    }  
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void CopyPosition( Node3D source, Node3D target, float duration, float delta )
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      target.GlobalPosition = SmoothForDuration( target.GlobalPosition, source.GlobalPosition, duration, delta );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void CopyPosition( Node3D source, Node3D target, float duration, double delta )
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      CopyPosition( source, target, duration, (float) delta );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void CopyRotation( Node3D source, Node3D target, float duration, float delta )
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      target.SetGlobalQuaternion( SmoothForDuration( target.GetGlobalQuaternion(), source.GetGlobalQuaternion(), duration, delta ) );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void CopyRotation( Node3D source, Node3D target, float duration, double delta )
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      CopyRotation( source, target, duration, (float) delta );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -76,6 +76,12 @@ namespace Rokojori
 | 
				
			||||||
    [Export]
 | 
					    [Export]
 | 
				
			||||||
    public Curve bladeScale = MathX.Curve( 1f );
 | 
					    public Curve bladeScale = MathX.Curve( 1f );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public Curve bladeHeight = MathX.Curve( 0.4f );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public Curve bladeInGround = MathX.Curve( 0.05f );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    [Export]
 | 
					    [Export]
 | 
				
			||||||
    public Curve bladeWidth = MathX.Curve( 0.05f );
 | 
					    public Curve bladeWidth = MathX.Curve( 0.05f );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -88,20 +94,20 @@ namespace Rokojori
 | 
				
			||||||
    [Export]
 | 
					    [Export]
 | 
				
			||||||
    public Curve bladeBending2 = null;
 | 
					    public Curve bladeBending2 = null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    [Export]
 | 
					 | 
				
			||||||
    public Curve bladeHeight = MathX.Curve( 0.4f );
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    [Export]
 | 
					 | 
				
			||||||
    public Curve bladeInGround = MathX.Curve( 0.05f );
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    [Export]
 | 
					    [Export]
 | 
				
			||||||
    public Curve positionJitter = MathX.Curve( 0.05f );
 | 
					    public Curve positionJitter = MathX.Curve( 0.05f );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    [Export]
 | 
					    [Export]
 | 
				
			||||||
    public Curve scaleByDistanceX = MathX.Curve( 1f, 1f );
 | 
					    public Curve scaleByDistanceX = MathX.Curve( 1f );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    [Export]
 | 
					    [Export]
 | 
				
			||||||
    public Curve scaleByDistanceZ = MathX.Curve( 1f, 1f );
 | 
					    public Curve scaleByDistanceZ = MathX.Curve( 1f );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public Curve normalBlending = MathX.Curve( 0.5f );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public Vector3 normalBlendingDirection = Vector3.Up;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    [Export]
 | 
					    [Export]
 | 
				
			||||||
    public Curve yawRotation = MathX.Curve( 0f, 1f );
 | 
					    public Curve yawRotation = MathX.Curve( 0f, 1f );
 | 
				
			||||||
| 
						 | 
					@ -130,6 +136,8 @@ namespace Rokojori
 | 
				
			||||||
        return;
 | 
					        return;
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      update = false; 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      var current = SerializedGodotObject.Create( this ); 
 | 
					      var current = SerializedGodotObject.Create( this ); 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -140,7 +148,7 @@ namespace Rokojori
 | 
				
			||||||
        return;     
 | 
					        return;     
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      update = false; 
 | 
					      
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      CreatePatch();
 | 
					      CreatePatch();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -183,6 +191,7 @@ namespace Rokojori
 | 
				
			||||||
          var yaw = random.Sample( yawRotation );
 | 
					          var yaw = random.Sample( yawRotation );
 | 
				
			||||||
          var rotationY = yaw * Mathf.Pi * 2f;
 | 
					          var rotationY = yaw * Mathf.Pi * 2f;
 | 
				
			||||||
          var maxRotation = random.Sample( randomRotation );
 | 
					          var maxRotation = random.Sample( randomRotation );
 | 
				
			||||||
 | 
					          var normalBlendingAmount = random.Sample( normalBlending );
 | 
				
			||||||
          var rotationOther = random.Next() * Mathf.DegToRad( maxRotation );
 | 
					          var rotationOther = random.Next() * Mathf.DegToRad( maxRotation );
 | 
				
			||||||
          var rotationX = random.Next() * rotationOther;
 | 
					          var rotationX = random.Next() * rotationOther;
 | 
				
			||||||
          var rotationZ = rotationOther - rotationX;
 | 
					          var rotationZ = rotationOther - rotationX;
 | 
				
			||||||
| 
						 | 
					@ -204,14 +213,25 @@ namespace Rokojori
 | 
				
			||||||
          }              
 | 
					          }              
 | 
				
			||||||
         
 | 
					         
 | 
				
			||||||
          bladeMG.ApplyTransform( trsf );
 | 
					          bladeMG.ApplyTransform( trsf );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          MeshGeometry clonedBladeMG = null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          if ( createBackFaces )
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            clonedBladeMG = bladeMG.Clone();
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          bladeMG.BlendNormals( normalBlendingDirection, normalBlendingAmount );        
 | 
				
			||||||
          
 | 
					          
 | 
				
			||||||
          mg.Add( bladeMG );  
 | 
					          mg.Add( bladeMG );  
 | 
				
			||||||
 | 
					
 | 
				
			||||||
          if ( createBackFaces )
 | 
					          if ( createBackFaces )
 | 
				
			||||||
          {
 | 
					          {
 | 
				
			||||||
            var blade2MG = bladeMG.Clone();
 | 
					            
 | 
				
			||||||
            blade2MG.FlipNormalDirection();   
 | 
					            clonedBladeMG.FlipNormalDirection();  
 | 
				
			||||||
            mg.Add( blade2MG );       
 | 
					            clonedBladeMG.BlendNormals( normalBlendingDirection, normalBlendingAmount );   
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            mg.Add( clonedBladeMG );       
 | 
				
			||||||
          }          
 | 
					          }          
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,169 @@
 | 
				
			||||||
 | 
					using System.Collections;
 | 
				
			||||||
 | 
					using System.Collections.Generic;
 | 
				
			||||||
 | 
					using Godot;
 | 
				
			||||||
 | 
					using System;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Rokojori
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  [Tool]
 | 
				
			||||||
 | 
					  [GlobalClass]
 | 
				
			||||||
 | 
					  public partial class Baker:Node
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    public enum CameraDistanceDetectionType
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      Automatic_Distance_Detection,
 | 
				
			||||||
 | 
					      Custom_Distance
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    public enum CameraFOVMode
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      Keep_Fov,
 | 
				
			||||||
 | 
					      Compute_Fov_With_Distance,
 | 
				
			||||||
 | 
					      Custom_Fov
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public enum MeshMode
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      World_Scale,
 | 
				
			||||||
 | 
					      Custom_Scale
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public bool update = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public bool updateAlways = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public Viewport viewport;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public Node3D target;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public Camera3D camera;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public float originalFOV = 75;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public bool assignFOV = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public float placingDistance = 500;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public float computedFOV = 75;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public bool useCustomFOV = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public float customFOV = 75;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public bool useCustomDistance = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public float customDistance = 50;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public float outputScale = 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public Node3D outputTexture;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public float outputTextureSize = 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [Export( PropertyHint.Range, "-180,180")]
 | 
				
			||||||
 | 
					    public float yaw = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [Export( PropertyHint.Range, "-180,180")]
 | 
				
			||||||
 | 
					    public float pitch = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public Quaternion bakingRotation => Math3D.YawPitchRotation( yaw, pitch );
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public float zoom = 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public float distance = 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public override void _Process( double delta )
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      if ( ! ( update || updateAlways ) )
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      update = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      Bake();
 | 
				
			||||||
 | 
					      
 | 
				
			||||||
 | 
					    } 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void Bake()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      if ( viewport == null || target == null || camera == null )
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      var box = target.GetWorldBounds();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if ( box == null )
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        RJLog.Log( "No target" );
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      var sphere = Sphere.ContainingBox( box );
 | 
				
			||||||
 | 
					      camera.Fov = originalFOV;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      var billboardFOV = camera.Fov;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      
 | 
				
			||||||
 | 
					      if ( assignFOV )
 | 
				
			||||||
 | 
					      { 
 | 
				
			||||||
 | 
					        computedFOV = Cameras.ComputeFOVForBillboard( originalFOV, sphere.radius, placingDistance );
 | 
				
			||||||
 | 
					        billboardFOV = computedFOV;
 | 
				
			||||||
 | 
					        camera.Fov = billboardFOV;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if ( useCustomFOV )
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        billboardFOV = customFOV;
 | 
				
			||||||
 | 
					        camera.Fov = billboardFOV;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      var newDistance = useCustomDistance ? customDistance : Cameras.ComputeCameraFrameFittingDistance( billboardFOV, sphere.radius / zoom );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if ( newDistance != distance )
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        distance = newDistance;
 | 
				
			||||||
 | 
					        RJLog.Log( "New Distance:", box, sphere, distance );
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      var cameraRotation = Math3D.RotateXDegrees( pitch ) * Math3D.RotateYDegrees( yaw ); 
 | 
				
			||||||
 | 
					      var offset = ( Vector3.Back * distance ) * cameraRotation ;
 | 
				
			||||||
 | 
					      camera.GlobalPosition = target.GlobalPosition + offset;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      camera.SetGlobalQuaternion( cameraRotation.Inverse() );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      RJLog.Log( "Set Rotation", cameraRotation, ">>", camera.GetGlobalQuaternion()  );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      outputScale = Cameras.ComputeCameraFittingScale( camera.Fov, distance );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if ( outputTexture != null )
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        outputTexture.Scale = Vector3.One * outputScale;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,320 @@
 | 
				
			||||||
 | 
					using System.Collections;
 | 
				
			||||||
 | 
					using System.Collections.Generic;
 | 
				
			||||||
 | 
					using Godot;
 | 
				
			||||||
 | 
					using System;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Rokojori
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  [Tool]
 | 
				
			||||||
 | 
					  [GlobalClass]
 | 
				
			||||||
 | 
					  public partial class MultiBaker:Node
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public bool initialize;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public bool setupViews;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public enum BakeMode
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      Cylindric_Billboard
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public BakeMode bakeMode;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public Node3D sourceTarget;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public Node outputTarget;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					    [ExportGroup( "Camera Settings")]
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public Baker.CameraDistanceDetectionType distanceDetectionType = Baker.CameraDistanceDetectionType.Automatic_Distance_Detection;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public float customDistance = 50;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public float cameraZoom = 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public Baker.CameraFOVMode fovMode = Baker.CameraFOVMode.Compute_Fov_With_Distance;
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public float originalFOV = 75;
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public float fovPlacingDistance = 200;
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public float customFOV = 75;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public Baker.MeshMode meshMode = Baker.MeshMode.World_Scale;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [ExportGroup("Cylindric Billboard")]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public int cylinderSides = 4;
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public bool cylinderTop = false;
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public bool cylinderBottom = false;
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public float cylinderSideOffset = 0;
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public float cylinderTopOffset = 0.5f;
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public float cylinderBottomOffset = 0.5f;
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public MeshInstance3D cylinderMesh;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [ExportGroup("Viewport")]
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public Vector2 textureSize = new Vector2( 2048, 2048 );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [ExportGroup("Debugging")]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public SubViewport X_bakingViewport;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public Node3D X_bakingTargetContainer;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public Node X_views;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public WorldEnvironment X_worldEnvironment;    
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    public override void _Process( double delta )
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      if ( initialize )
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        initialize = false;
 | 
				
			||||||
 | 
					        Initialize();
 | 
				
			||||||
 | 
					      } 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if ( setupViews )
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        setupViews = false;
 | 
				
			||||||
 | 
					        CreateViews();
 | 
				
			||||||
 | 
					        SetupViews();
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    } 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void Initialize()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      if ( outputTarget == null )
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        outputTarget = this;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      Nodes.RemoveAndDeleteChildren( outputTarget );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      X_bakingViewport = outputTarget.CreateChild<SubViewport>( "Multi Baker Viewport" );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      X_bakingViewport.Size = (Vector2I) textureSize;
 | 
				
			||||||
 | 
					      X_bakingViewport.OwnWorld3D = true;
 | 
				
			||||||
 | 
					      X_bakingViewport.TransparentBg = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      X_worldEnvironment = X_bakingViewport.CreateChild<WorldEnvironment>( "Multi Baker Environment" );
 | 
				
			||||||
 | 
					      X_worldEnvironment.Environment = new Godot.Environment();
 | 
				
			||||||
 | 
					      X_worldEnvironment.Environment.AmbientLightSource = Godot.Environment.AmbientSource.Color;
 | 
				
			||||||
 | 
					      X_worldEnvironment.Environment.AmbientLightColor = HSLColor.white;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      X_bakingTargetContainer = X_bakingViewport.CreateChild<Node3D>( "Target Container" );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      X_views = X_bakingViewport.CreateChild<Node>( "Views" );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      sourceTarget.DeepCopyTo( X_bakingTargetContainer );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public int GetNumViews()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      if ( BakeMode.Cylindric_Billboard == bakeMode )
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        var cylinderViews = cylinderSides;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if ( cylinderBottom )
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					          cylinderViews ++;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if ( cylinderTop )
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					          cylinderViews ++;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return cylinderViews;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      return 0;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    List<Baker> _bakers;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void CreateViews()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      Nodes.RemoveAndDeleteChildren( X_views );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      var numViews = GetNumViews();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      _bakers = new List<Baker>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      for ( int i = 0; i < numViews; i++ )
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        var userIndex = ( i + 1 );
 | 
				
			||||||
 | 
					        var bakingView = X_views.CreateChild<SubViewport>( "Baking View " + userIndex );
 | 
				
			||||||
 | 
					        bakingView.TransparentBg = true;
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        var bakingCamera = bakingView.CreateChild<Camera3D>( "Camera View " + userIndex );
 | 
				
			||||||
 | 
					        var baker = bakingView.CreateChild<Baker>( "Baker " + userIndex );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        baker.camera   = bakingCamera;
 | 
				
			||||||
 | 
					        baker.target   = X_bakingTargetContainer;
 | 
				
			||||||
 | 
					        baker.viewport = bakingView;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        baker.update = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        _bakers.Add( baker );
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void SetupViews()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      if ( BakeMode.Cylindric_Billboard == bakeMode )
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        CreateCylinderBillboardView();
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Sphere _targetBoundingSphere;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Sphere targetBoundingSphere 
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      get 
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        if ( _targetBoundingSphere == null )
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					          ComputeBoundingSphere();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return _targetBoundingSphere;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void ComputeBoundingSphere()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      var worldBounds = X_bakingTargetContainer.GetWorldBounds();
 | 
				
			||||||
 | 
					      _targetBoundingSphere = Sphere.ContainingBox( worldBounds );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    float GetCameraFOV()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      if ( Baker.CameraFOVMode.Custom_Fov == fovMode )
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        return customFOV;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if ( Baker.CameraFOVMode.Keep_Fov == fovMode )
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        return originalFOV;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      return Cameras.ComputeFOVForBillboard( originalFOV, targetBoundingSphere.radius, fovPlacingDistance );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    float GetCameraDistance()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      if ( Baker.CameraDistanceDetectionType.Custom_Distance == distanceDetectionType )
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        return customDistance;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      var fov = GetCameraFOV();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      return Cameras.ComputeCameraFrameFittingDistance( fov, targetBoundingSphere.radius / cameraZoom );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    float GetOutputScale()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      var fov = GetCameraFOV();
 | 
				
			||||||
 | 
					      var distance = GetCameraDistance();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      return Cameras.ComputeCameraFittingScale( fov, distance );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void CreateCylinderBillboardView()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      _targetBoundingSphere = null;
 | 
				
			||||||
 | 
					      var fov = GetCameraFOV();
 | 
				
			||||||
 | 
					      var distance = GetCameraDistance();
 | 
				
			||||||
 | 
					      var outputScale = GetOutputScale();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      _bakers.ForEach(
 | 
				
			||||||
 | 
					        b =>
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					          b.useCustomFOV = true;
 | 
				
			||||||
 | 
					          b.customFOV = fov;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          b.useCustomDistance = true;
 | 
				
			||||||
 | 
					          b.customDistance = distance;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      var index = 0;
 | 
				
			||||||
 | 
					      var mg = new MeshGeometry();  
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      var numTextures = ( cylinderTop ? 1 : 0 ) + ( cylinderBottom ? 1 : 0 ) + cylinderSides;
 | 
				
			||||||
 | 
					      var textureAlignment = TextureMerger.ComputeTextureAlignment( numTextures );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if ( cylinderTop )
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        _bakers[ index ].yaw = 0;
 | 
				
			||||||
 | 
					        _bakers[ index ].pitch = 90f;        
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        var uvRectangle = TextureMerger.GetUVRectangle( textureAlignment, index );       
 | 
				
			||||||
 | 
					        mg.AddQuad( _bakers[ index ].bakingRotation, outputScale, uvRectangle );  
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        index ++;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if ( cylinderBottom )
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        _bakers[ index ].yaw = 0;
 | 
				
			||||||
 | 
					        _bakers[ index ].pitch = -90f;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        var uvRectangle = TextureMerger.GetUVRectangle( textureAlignment, index );       
 | 
				
			||||||
 | 
					        mg.AddQuad( _bakers[ index ].bakingRotation, outputScale, uvRectangle );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        index ++;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      
 | 
				
			||||||
 | 
					      for ( int i = 0; i < cylinderSides; i++ )
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        var angle = ( 360f * i ) / (float) cylinderSides;
 | 
				
			||||||
 | 
					        _bakers[ index + i ].yaw = angle;
 | 
				
			||||||
 | 
					        _bakers[ index + i ].pitch = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        var uv = TextureMerger.GetUVRectangle( textureAlignment, index + 1);       
 | 
				
			||||||
 | 
					        mg.AddQuad( _bakers[ index + i ].bakingRotation, outputScale, uv );
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if ( cylinderMesh != null )
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        cylinderMesh.Mesh = mg.GenerateMesh();
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,45 @@
 | 
				
			||||||
 | 
					using System.Collections;
 | 
				
			||||||
 | 
					using System.Collections.Generic;
 | 
				
			||||||
 | 
					using Godot;
 | 
				
			||||||
 | 
					using System;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Rokojori
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  public class OctahedralMapping
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    public static Vector3 Hemisphere( Vector2 uv )
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      var position = new Vector3 (uv.X - uv.Y, 0, -1.0f + uv.X + uv.Y );
 | 
				
			||||||
 | 
					      var absolute = position.Abs();
 | 
				
			||||||
 | 
					      position.Y = 1.0f - absolute.X - absolute.Z;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      return position;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static Vector3 Sphere( Vector2 uv )
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      uv = uv * 2.0f - Vector2.One;
 | 
				
			||||||
 | 
					      var position = new Vector3( uv.X, 0, uv.Y );
 | 
				
			||||||
 | 
					      var absolute = position.Abs();
 | 
				
			||||||
 | 
					      position.Y = 1.0f - absolute.X - absolute.Z;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if ( position.Y < 0 )
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        var sign = position.Sign();
 | 
				
			||||||
 | 
					        position.X = sign.X * ( 1.0f - absolute.Z );
 | 
				
			||||||
 | 
					        position.Z = sign.Z * ( 1.0f - absolute.X );
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      return position;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static Vector3 Map( Vector2 uv, bool sphere )
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      var position = sphere ? Sphere( uv ) : Hemisphere( uv );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      return position.Normalized();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,28 @@
 | 
				
			||||||
 | 
					using System.Collections;
 | 
				
			||||||
 | 
					using System.Collections.Generic;
 | 
				
			||||||
 | 
					using Godot;
 | 
				
			||||||
 | 
					using System;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Rokojori
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  [Tool]
 | 
				
			||||||
 | 
					  [GlobalClass]
 | 
				
			||||||
 | 
					  public partial class SaveViewportTexture:RJAction
 | 
				
			||||||
 | 
					  { 
 | 
				
			||||||
 | 
					    [Export] 
 | 
				
			||||||
 | 
					    public SubViewport viewport;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public string path = "";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public float quality = 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public override void _OnTrigger()
 | 
				
			||||||
 | 
					    {      
 | 
				
			||||||
 | 
					      Textures.Save( viewport.GetTexture(), path, quality );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,73 @@
 | 
				
			||||||
 | 
					using System.Collections;
 | 
				
			||||||
 | 
					using System.Collections.Generic;
 | 
				
			||||||
 | 
					using Godot;
 | 
				
			||||||
 | 
					using System;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Rokojori
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  [Tool]
 | 
				
			||||||
 | 
					  [GlobalClass]
 | 
				
			||||||
 | 
					  public partial class SetBakingMaterials:RJAction
 | 
				
			||||||
 | 
					  {  
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public Node targetContainer;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public enum BakingMaterialMode
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      Albedo,
 | 
				
			||||||
 | 
					      Normals,
 | 
				
			||||||
 | 
					      ORM,
 | 
				
			||||||
 | 
					      Depth
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public BakingMaterialMode mode;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public override void _OnTrigger()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      var baseMaterial = GetBaseMaterial();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      Nodes.ForEach<VisualInstance3D>( targetContainer,
 | 
				
			||||||
 | 
					        ( v )=>
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static readonly string materialsPath = "res://addons/rokojori_action_library/External/Imposter/materials/";
 | 
				
			||||||
 | 
					    static Dictionary<string,Material> _cachedMaterials = new Dictionary<string, Material>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static Material LoadMaterial( string materialName )
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      if ( _cachedMaterials.ContainsKey( materialName ) )
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        return _cachedMaterials[ materialName ];
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      var loadedMaterial = ResourceLoader.Load( materialsPath + materialName ) as Material;
 | 
				
			||||||
 | 
					      _cachedMaterials[ materialName ] = loadedMaterial;
 | 
				
			||||||
 | 
					      
 | 
				
			||||||
 | 
					      return loadedMaterial;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public Material GetBaseMaterial()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      switch ( mode )
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        case BakingMaterialMode.Albedo:  return LoadMaterial( "albedo_material.material" );
 | 
				
			||||||
 | 
					        case BakingMaterialMode.Normals: return LoadMaterial( "normal_baker.material" );
 | 
				
			||||||
 | 
					        case BakingMaterialMode.ORM:     return LoadMaterial( "orm_baker.material" );
 | 
				
			||||||
 | 
					        case BakingMaterialMode.Depth:   return LoadMaterial( "depth_baker.material" );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      return null;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,269 @@
 | 
				
			||||||
 | 
					using System.Collections;
 | 
				
			||||||
 | 
					using System.Collections.Generic;
 | 
				
			||||||
 | 
					using Godot;
 | 
				
			||||||
 | 
					using System;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Rokojori
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  [Tool]
 | 
				
			||||||
 | 
					  [GlobalClass]
 | 
				
			||||||
 | 
					  public partial class TextureMerger:Node
 | 
				
			||||||
 | 
					  {    
 | 
				
			||||||
 | 
					    public enum SourceMode
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      Viewports,
 | 
				
			||||||
 | 
					      Textures
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public bool initialize;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public bool createLayout;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [ExportGroup("Source")]
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public SourceMode sourceMode;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    [ExportGroup("Source/Viewports")]
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public Node sourceViewportsContainer = null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public SubViewport[] sourceViewports;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [ExportGroup("Source/Textures")]
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public Texture2D[] sourceTextures;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [ExportGroup("Output")]
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public Node outputTarget;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public enum LayoutMode
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      Grid,
 | 
				
			||||||
 | 
					      Custom
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public LayoutMode layoutMode = LayoutMode.Grid;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [ExportGroup("Output/Custom")]
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public Vector2[] customPositions;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public Vector2[] customSizes;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [ExportGroup("Viewport")]
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public Vector2 textureSize = new Vector2( 2048, 2048 );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public BaseMaterial3D.TransparencyEnum transparencyMode = BaseMaterial3D.TransparencyEnum.AlphaScissor;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [ExportGroup("Debugging")]
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public SubViewport X_textureMergerViewport;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public Camera3D X_mergerCamera;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    List<Texture2D> _textures = new List<Texture2D>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public override void _Process( double delta )
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      if ( initialize )
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        initialize = false;
 | 
				
			||||||
 | 
					        Initialize();
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if ( createLayout )
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        createLayout = false;
 | 
				
			||||||
 | 
					        CreateLayout();
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void Initialize()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      if ( outputTarget == null )
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        outputTarget = this;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      Nodes.RemoveAndDeleteChildren( outputTarget );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      X_textureMergerViewport = outputTarget.CreateChild<SubViewport>( "Texture Merger Viewport" );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      X_mergerCamera = X_textureMergerViewport.CreateChild<Camera3D>( "Texture Merger Camera" );
 | 
				
			||||||
 | 
					      X_mergerCamera.Projection = Camera3D.ProjectionType.Orthogonal;       
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      X_textureMergerViewport.Size = (Vector2I) textureSize;
 | 
				
			||||||
 | 
					      X_textureMergerViewport.OwnWorld3D = true;
 | 
				
			||||||
 | 
					      X_textureMergerViewport.TransparentBg = true;
 | 
				
			||||||
 | 
					      
 | 
				
			||||||
 | 
					      X_mergerCamera.Size = 1;  
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void GrabTextures()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      _textures = new List<Texture2D>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if ( SourceMode.Textures == sourceMode )
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        _textures.AddRange( sourceTextures );
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if ( SourceMode.Viewports == sourceMode )
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        if ( sourceViewportsContainer != null )
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					          sourceViewports = Nodes.AllIn<SubViewport>( sourceViewportsContainer, null, false ).ToArray();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        var vpTextures = Arrays.Map( sourceViewports, 
 | 
				
			||||||
 | 
					          s => 
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            var vt = new ViewportTexture();
 | 
				
			||||||
 | 
					            vt.ViewportPath = s.GetPath();
 | 
				
			||||||
 | 
					            return vt;          
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        _textures.AddRange( vpTextures );
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void CreateLayout()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      GrabTextures();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if ( LayoutMode.Grid == layoutMode )
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        CreateGridLayout();
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      else if ( LayoutMode.Custom == layoutMode )
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        CreateCustomLayout();
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Vector2 ConvertUVtoCameraSpace( Vector2 uv )
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      var scale = Vector2.One;
 | 
				
			||||||
 | 
					      var w = 1;
 | 
				
			||||||
 | 
					      var h = 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if ( X_textureMergerViewport.Size.X != X_textureMergerViewport.Size.Y )
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        if ( X_textureMergerViewport.Size.X > X_textureMergerViewport.Size.Y )
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					          w = X_textureMergerViewport.Size.X / X_textureMergerViewport.Size.Y;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        else
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					          h = X_textureMergerViewport.Size.Y / X_textureMergerViewport.Size.X;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      var x = Mathf.Remap( uv.X, 0, 1, -w, w );
 | 
				
			||||||
 | 
					      var y = Mathf.Remap( uv.Y, 0, 1, -h, h );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      return new Vector2( x, y );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void CreateGridLayout()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      var alignment = ComputeTextureAlignment( _textures.Count );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      Nodes.RemoveAndDeleteChildrenOfType<CsgMesh3D>( X_textureMergerViewport );
 | 
				
			||||||
 | 
					      
 | 
				
			||||||
 | 
					      for ( int i = 0; i < _textures.Count; i++ )
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        var mesh = X_textureMergerViewport.CreateChild<CsgMesh3D>( "Texture " + ( i + 1 ) );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        var uvRectangle = GetUVRectangle( alignment, i );
 | 
				
			||||||
 | 
					        SetMeshCoordinates( mesh, uvRectangle );      
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        var material = new StandardMaterial3D();
 | 
				
			||||||
 | 
					        material.Transparency = transparencyMode;
 | 
				
			||||||
 | 
					        material.ShadingMode = BaseMaterial3D.ShadingModeEnum.Unshaded;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        material.AlbedoTexture = _textures[ i ];
 | 
				
			||||||
 | 
					        mesh.Material = material;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void CreateCustomLayout()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      for ( int i = 0; i < _textures.Count; i++ )
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        var mesh = outputTarget.CreateChild<CsgMesh3D>( "Texture " + ( i + 1 ) );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        SetMeshCoordinates( mesh, customPositions[ i ], customSizes[ i ] );
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        var material = new StandardMaterial3D();
 | 
				
			||||||
 | 
					        material.Transparency = transparencyMode;
 | 
				
			||||||
 | 
					        material.ShadingMode = BaseMaterial3D.ShadingModeEnum.Unshaded;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        material.AlbedoTexture = _textures[ i ];
 | 
				
			||||||
 | 
					        mesh.Material = material;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void SetMeshCoordinates( CsgMesh3D mesh, Rect2 rectangle )
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      SetMeshCoordinates( mesh, rectangle.Position, rectangle.Size ); 
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void SetMeshCoordinates( CsgMesh3D mesh, Vector2 start, Vector2 size )
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      start =  ConvertUVtoCameraSpace( start );
 | 
				
			||||||
 | 
					      var end   =  ConvertUVtoCameraSpace( start + size );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      size = end - start;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      var quadMesh = new QuadMesh();
 | 
				
			||||||
 | 
					      quadMesh.Size = size;
 | 
				
			||||||
 | 
					      mesh.Mesh = quadMesh;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      mesh.GlobalPosition = new Vector3( start.X - size.X , start.Y - size.Y, -1 );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static Vector2I ComputeTextureAlignment( int numElements )
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      var root    = Mathf.Sqrt( numElements );     
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      var ceiled  = Mathf.CeilToInt( root );
 | 
				
			||||||
 | 
					      var floored = ceiled - 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      var weight  = Mathf.RoundToInt( root % 1 );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      var height = weight * ceiled + ( 1 - weight ) * floored;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      return new Vector2I( ceiled, height );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static Rect2 GetUVRectangle( Vector2I textureAlignment, int index )
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      var x = index % textureAlignment.X;
 | 
				
			||||||
 | 
					      var y = index / textureAlignment.Y;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      var xs = 1f / textureAlignment.X;
 | 
				
			||||||
 | 
					      var ys = 1f / textureAlignment.Y;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      return new Rect2( x * xs, y * ys, new Vector2( xs, ys ) );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,64 @@
 | 
				
			||||||
 | 
					using System.Collections;
 | 
				
			||||||
 | 
					using System.Collections.Generic;
 | 
				
			||||||
 | 
					using Godot;
 | 
				
			||||||
 | 
					using System;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Rokojori
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  public static class Textures
 | 
				
			||||||
 | 
					  {    
 | 
				
			||||||
 | 
					    public static void Save( Texture2D texture, string path, float quality = 0.75f )
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      var image = texture.GetImage();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      Save( image, path );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static void Save( Image image, string path, float quality = 0.75f )
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      var fp = FilePath.Absolute( path );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      var output = ".png";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if ( 
 | 
				
			||||||
 | 
					        fp.hasFileExtension( ".jpg" ) ||
 | 
				
			||||||
 | 
					        fp.hasFileExtension( ".exr" ) ||
 | 
				
			||||||
 | 
					        fp.hasFileExtension( ".webp" )
 | 
				
			||||||
 | 
					      )
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        output = fp.fileExtension;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      switch ( output )
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        case ".jpg": 
 | 
				
			||||||
 | 
					        { 
 | 
				
			||||||
 | 
					          image.SaveJpg( path, quality ); 
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        case ".png": 
 | 
				
			||||||
 | 
					        { 
 | 
				
			||||||
 | 
					          image.SavePng( path ); 
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        case ".webp": 
 | 
				
			||||||
 | 
					        { 
 | 
				
			||||||
 | 
					          image.SaveWebp( path, quality < 1, quality ); 
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        case ".exr": 
 | 
				
			||||||
 | 
					        { 
 | 
				
			||||||
 | 
					          image.SaveExr( path ); 
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,141 @@
 | 
				
			||||||
 | 
					using Godot;
 | 
				
			||||||
 | 
					using System.Collections;
 | 
				
			||||||
 | 
					using System.Collections.Generic;
 | 
				
			||||||
 | 
					using System.Text.RegularExpressions;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Rokojori
 | 
				
			||||||
 | 
					{  
 | 
				
			||||||
 | 
					  public class HeightMapData
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    int width;
 | 
				
			||||||
 | 
					    int height;
 | 
				
			||||||
 | 
					    float[] data;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    public static HeightMapData Create( int w, int h, float[] data = null)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      var hmd = new HeightMapData();
 | 
				
			||||||
 | 
					      hmd.width = w;
 | 
				
			||||||
 | 
					      hmd.height = h;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      hmd.data = data == null ? new float[ w * h ] : data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      return hmd;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public float Get( int x, int y )
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      x = Mathf.Clamp( x, 0, width - 1 );
 | 
				
			||||||
 | 
					      y = Mathf.Clamp( y, 0, height - 1 ); 
 | 
				
			||||||
 | 
					      return data[ x + y * width ];
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public float GetLerped( float x, float y )
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      var x0 = Mathf.FloorToInt( x );
 | 
				
			||||||
 | 
					      var x1 = x0 + 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      var y0 = Mathf.FloorToInt( y );
 | 
				
			||||||
 | 
					      var y1 = y0 + 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      var xL = x - x0;
 | 
				
			||||||
 | 
					      var yL = y - y0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      //   xy xy
 | 
				
			||||||
 | 
					      //   00 10
 | 
				
			||||||
 | 
					      //   10 11
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      var upValue   = Mathf.Lerp( Get( x0, y0 ), Get( x1, y0 ), xL );
 | 
				
			||||||
 | 
					      var downValue = Mathf.Lerp( Get( x0, y1 ), Get( x1, y1 ), xL );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      return Mathf.Lerp( upValue, downValue, yL );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void Set( int x, int y, float value )
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      if ( x < 0 || y < 0 || x >= width || y >= height )
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					      } 
 | 
				
			||||||
 | 
					      
 | 
				
			||||||
 | 
					      data[ x + y * width ] = value;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public Vector3 GetNormal( int x, int y, float strength = 8f )
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      var tl = Get( x-1, y-1 ); 
 | 
				
			||||||
 | 
					      var  l = Get( x-1, y );   
 | 
				
			||||||
 | 
					      var bl = Get( x-1, y+1) ; 
 | 
				
			||||||
 | 
					      
 | 
				
			||||||
 | 
					      var  t = Get( x, y-1 );   
 | 
				
			||||||
 | 
					      var  b = Get( x, y+1 );   
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      var tr = Get( x+1, y-1 ); 
 | 
				
			||||||
 | 
					      var  r = Get( x+1, y );   
 | 
				
			||||||
 | 
					      var br = Get( x+1, y+1 ); 
 | 
				
			||||||
 | 
					      
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      var nx = ( tr + 2.0f * r + br ) - ( tl + 2.0f * l + bl );
 | 
				
			||||||
 | 
					      var nz = ( bl + 2.0f * b + br ) - ( tl + 2.0f * t + tr );
 | 
				
			||||||
 | 
					      var ny = 1.0f / strength;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      return -new Vector3( nx, ny, nz ).Normalized();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public Vector3 GetNormal( float x, float y, float strength = 1f )
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      return GetNormal( (int)x, (int)y, strength );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public MeshGeometry GenerateMeshGeometry( Vector3 scale )
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      var func = ( Vector2 uv )=>
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        var p = new Pose();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        var x = uv.X * width;
 | 
				
			||||||
 | 
					        var z = uv.Y * height;
 | 
				
			||||||
 | 
					        var y = GetLerped( x, z );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        p.position = new Vector3( x * scale.X, y * scale.Y, z * scale.Z );
 | 
				
			||||||
 | 
					        p.up = GetNormal( x, z );
 | 
				
			||||||
 | 
					        return p;
 | 
				
			||||||
 | 
					      
 | 
				
			||||||
 | 
					      };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      return MeshGeometry.CreateFromUVFunction( func, width - 1, height - 1 );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public HeightMapData CreateLowerResolution( float averageVsMaxFilter = 0.5f )
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      var lw = width/2;
 | 
				
			||||||
 | 
					      var lh = height/2;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      var lower = Create( lw, lh );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      for ( int i = 0; i < lw; i++ )
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        for ( int j = 0; j < lh; j++ )
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					          var x0 = i * 2;
 | 
				
			||||||
 | 
					          var x1 = x0 + 1;
 | 
				
			||||||
 | 
					          var y0 = j * 2;
 | 
				
			||||||
 | 
					          var y1 = y0 +1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          var s0 = Get( x0, y0 );
 | 
				
			||||||
 | 
					          var s1 = Get( x1, y0 );
 | 
				
			||||||
 | 
					          var s2 = Get( x0, y1 );
 | 
				
			||||||
 | 
					          var s3 = Get( x1, y1 );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          var maxValue = MathX.Max( s0, s1, s2, s3 );
 | 
				
			||||||
 | 
					          var avgValue = (s0 + s1 + s2 + s3 ) / 4f;
 | 
				
			||||||
 | 
					          var value = Mathf.Lerp( avgValue, maxValue, averageVsMaxFilter );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          lower.Set( i, j, value );
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      return lower;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,109 @@
 | 
				
			||||||
 | 
					using Godot;
 | 
				
			||||||
 | 
					using System.Collections;
 | 
				
			||||||
 | 
					using System.Collections.Generic;
 | 
				
			||||||
 | 
					using System.Text.RegularExpressions;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Rokojori
 | 
				
			||||||
 | 
					{  
 | 
				
			||||||
 | 
					  [Tool]
 | 
				
			||||||
 | 
					  [GlobalClass]
 | 
				
			||||||
 | 
					  public partial class LODHeightMapGeometry:LODParent
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public float size = 500;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public float height = 1000;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public int resolution = 128;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public float noiseScale = 0.0025f;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public float noiseScaleAmount = 10f;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public float noiseScale2 = 0.025f;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public float noiseScale2Amount = 1f;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [Export]
 | 
				
			||||||
 | 
					    public float noiseScale3 = 0.15f;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public float noiseScale3Amount = 0.1f;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public int levels = 6;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public Material material;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void Create()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      var hmd = HeightMapData.Create( resolution, resolution );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      var offsetX = GlobalPosition.X - size/2f;
 | 
				
			||||||
 | 
					      var offsetY = GlobalPosition.Z - size/2f;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      var weights = noiseScaleAmount + noiseScale2Amount + noiseScale3Amount;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      for ( int i = 0; i < 128; i ++ )
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        for ( int j = 0; j < 128; j++ )
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					          var x = i / (float)resolution * size + offsetX;
 | 
				
			||||||
 | 
					          var y = j / (float)resolution * size + offsetY;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          var v = Noise.Perlin( new Vector2( x, y ) * noiseScale ) * noiseScaleAmount ;
 | 
				
			||||||
 | 
					          var v2 = Noise.Perlin( new Vector2( x, y ) * noiseScale2 ) * noiseScale2Amount;
 | 
				
			||||||
 | 
					          var v3 = Noise.Perlin( new Vector2( x, y ) * noiseScale3 ) * noiseScale3Amount;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          hmd.Set( i, j, ( v + v2 + v3 ) / weights );
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      var level = 1;
 | 
				
			||||||
 | 
					      var ratio = size / resolution;
 | 
				
			||||||
 | 
					      var scale = new Vector3( ratio * level, height, ratio * level );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      var meshInstance3D = this.CreateChild<MeshInstance3D>();
 | 
				
			||||||
 | 
					      meshInstance3D.Mesh = hmd.GenerateMeshGeometry( scale ).GenerateMesh();
 | 
				
			||||||
 | 
					      Materials.Set( meshInstance3D, material );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      var offset = size / -2;
 | 
				
			||||||
 | 
					      meshInstance3D.Position = new Vector3( offset, 0, offset );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      var levelScale = 2;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      var meshes = new List<Node3D>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      meshes.Add( meshInstance3D );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      while ( level < levels )
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        hmd = hmd.CreateLowerResolution();
 | 
				
			||||||
 | 
					        level ++;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        scale = new Vector3( ratio * levelScale , height, ratio * levelScale );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        var lowMeshInstance3D = this.CreateChild<MeshInstance3D>();
 | 
				
			||||||
 | 
					        lowMeshInstance3D.Mesh = hmd.GenerateMeshGeometry( scale ).GenerateMesh();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Materials.Set( lowMeshInstance3D, material );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        lowMeshInstance3D.Position = new Vector3( offset, 0, offset );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        levelScale *= 2;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        meshes.Add( lowMeshInstance3D );
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      lods = meshes.ToArray();
 | 
				
			||||||
 | 
					      
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -90,6 +90,29 @@ namespace Rokojori
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void BlendNormals( Vector3 direction, float amount )
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      if ( amount <= 0 )
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if ( amount >= 1 )
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					         for ( int i = 0; i < normals.Count; i++ )
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					          normals[ i ] = direction;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      for ( int i = 0; i < normals.Count; i++ )
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        normals[ i ] = Math3D.BlendNormals( normals[ i ], direction, amount );
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public void Offset( Vector3 offset )
 | 
					    public void Offset( Vector3 offset )
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
      for ( int i = 0; i < vertices.Count; i++ )
 | 
					      for ( int i = 0; i < vertices.Count; i++ )
 | 
				
			||||||
| 
						 | 
					@ -119,6 +142,7 @@ namespace Rokojori
 | 
				
			||||||
      return u * ( segments + 1 ) + v;
 | 
					      return u * ( segments + 1 ) + v;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   
 | 
				
			||||||
    public static MeshGeometry CreateFromUVFunction( Func<Vector2,Pose> uv, int uSegments, int vSegments )
 | 
					    public static MeshGeometry CreateFromUVFunction( Func<Vector2,Pose> uv, int uSegments, int vSegments )
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
      var mg = new MeshGeometry();
 | 
					      var mg = new MeshGeometry();
 | 
				
			||||||
| 
						 | 
					@ -265,7 +289,7 @@ namespace Rokojori
 | 
				
			||||||
    public MeshGeometry( bool normals = true, bool uvs = true, bool colors = false, bool uvs2 = false )
 | 
					    public MeshGeometry( bool normals = true, bool uvs = true, bool colors = false, bool uvs2 = false )
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
      Initialize( normals, uvs, colors, uvs2 );
 | 
					      Initialize( normals, uvs, colors, uvs2 );
 | 
				
			||||||
    }
 | 
					    } 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public void Initialize( bool normals = true, bool uvs = true, bool colors = false, bool uvs2 = false )
 | 
					    public void Initialize( bool normals = true, bool uvs = true, bool colors = false, bool uvs2 = false )
 | 
				
			||||||
    {      
 | 
					    {      
 | 
				
			||||||
| 
						 | 
					@ -275,6 +299,39 @@ namespace Rokojori
 | 
				
			||||||
      this.colors = colors ? new List<Color>() : null;      
 | 
					      this.colors = colors ? new List<Color>() : null;      
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public Vector3 GetRawNormal( int triangleIndex )
 | 
				
			||||||
 | 
					    { 
 | 
				
			||||||
 | 
					      var va = vertices[ indices[ triangleIndex * 3 ] ];
 | 
				
			||||||
 | 
					      var vb = vertices[ indices[ triangleIndex * 3 + 1] ]; 
 | 
				
			||||||
 | 
					      var vc = vertices[ indices[ triangleIndex * 3 + 2 ] ]; 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      return Math3D.ComputeNormal( va, vb, vc );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void ComputeNormals()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      var dl = new DictionaryList<int,Vector3>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      ForEachTriangle(
 | 
				
			||||||
 | 
					        ( t, va, vb, vc)=>
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					          var normal = GetRawNormal( t );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          dl.Add( va, normal );
 | 
				
			||||||
 | 
					          dl.Add( vb, normal );
 | 
				
			||||||
 | 
					          dl.Add( vc, normal );
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      foreach ( var e in dl )
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        var normalIndex = e.Key;
 | 
				
			||||||
 | 
					        var normal = Math3D.Average( e.Value ); 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        normals[ normalIndex ] = normal; 
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public MeshGeometry Clone()
 | 
					    public MeshGeometry Clone()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
      var mg = new MeshGeometry();
 | 
					      var mg = new MeshGeometry();
 | 
				
			||||||
| 
						 | 
					@ -355,9 +412,17 @@ namespace Rokojori
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public void AddTriangle( int a, int b, int c )    
 | 
					    public void AddTriangle( int a, int b, int c, bool flip = false )    
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
      Lists.Add( indices, a, b, c );
 | 
					      if ( flip )
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        Lists.Add( indices, c, b, a );
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      else
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        Lists.Add( indices, a, b, c );
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -408,9 +473,49 @@ namespace Rokojori
 | 
				
			||||||
      AddTriangle( vc, vd, va,  nc, nd, na,  uvc, uvd, uva );
 | 
					      AddTriangle( vc, vd, va,  nc, nd, na,  uvc, uvd, uva );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public void AddQuad( int lt, int rt, int lb, int rb )    
 | 
					    public void AddQuad( Quaternion rotation, float size, Rect2 rectangle )
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
      Lists.Add( indices, lb, rt, lt, rt, lb, rb );
 | 
					      AddQuad( rotation, size, rectangle.Position, rectangle.End );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void AddQuad( Quaternion rotation, float size, Vector2 uv00, Vector2 uv11 )
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      var l = size * 0.5f;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      var normal = Vector3.Forward * rotation;
 | 
				
			||||||
 | 
					      var points = new List<Vector3>
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        new Vector3( -l, -l, 0 ), new Vector3( l, -l, 0 ),
 | 
				
			||||||
 | 
					        new Vector3( -l,  l, 0 ), new Vector3( l,  l, 0 )
 | 
				
			||||||
 | 
					      };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      for ( int i = 0; i < points.Count; i++ )
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        points[ i ] = points[ i ] * rotation; 
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      var uv10 = new Vector2( uv11.X, uv00.Y );
 | 
				
			||||||
 | 
					      var uv01 = new Vector2( uv00.X, uv11.Y );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      AddQuad(
 | 
				
			||||||
 | 
					        points[ 0 ], points[ 1 ], points[ 2 ], points[ 3 ],
 | 
				
			||||||
 | 
					        normal, normal, normal, normal,
 | 
				
			||||||
 | 
					        uv00, uv10, uv11, uv10
 | 
				
			||||||
 | 
					      );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void AddQuad( int lt, int rt, int lb, int rb, bool flip = false )    
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      if ( flip )
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        AddQuad( rt, lt, rb, lb, false );
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      else
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        Lists.Add( indices, lb, rt, lt, rt, lb, rb );
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					     
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,338 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					using Godot;
 | 
				
			||||||
 | 
					using Rokojori;
 | 
				
			||||||
 | 
					using System.Collections.Generic;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Rokojori
 | 
				
			||||||
 | 
					{  
 | 
				
			||||||
 | 
					  [Tool]
 | 
				
			||||||
 | 
					  [GlobalClass, Icon("res://addons/rokojori_action_library/Icons/Spline.svg") ]
 | 
				
			||||||
 | 
					  public partial class Cuboid : Node3D
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public float size = 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public float widthExtension = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public float heightExtension = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public float depthExtension = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public float borderSize = 0.1f;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public MeshInstance3D output;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public bool update = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public bool updateAlways = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public override void _Process( double delta )
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      if ( ! ( update || updateAlways ) )
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      update = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      Create();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public float X()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      return size + widthExtension;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public float Y()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      return size + heightExtension;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public float Z()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      return size + depthExtension;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    MeshGeometry mg = null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void Create()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      var maxBorderSize = ( MathX.Min( widthExtension, heightExtension, depthExtension )  + size ) / 2f;
 | 
				
			||||||
 | 
					      borderSize = Mathf.Clamp( borderSize, 0, maxBorderSize );
 | 
				
			||||||
 | 
					      mg = new MeshGeometry();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      for ( int i = 0; i < 24; i++ )
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        var p = GetPointAt( i );
 | 
				
			||||||
 | 
					        mg.vertices.Add( p );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if ( i < 4 )
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					          mg.normals.Add( Vector3.Up );
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        else if ( i < 12 )
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					          var cornerIndex = i - 4;
 | 
				
			||||||
 | 
					          mg.normals.Add( GetEdgeNormal( cornerIndex ) );
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        else if ( i < 20 )
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					          var cornerIndex = i - 12;
 | 
				
			||||||
 | 
					          mg.normals.Add( GetEdgeNormal( cornerIndex ) );
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        else
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					          mg.normals.Add( Vector3.Down );
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        mg.uvs.Add( Vector2.Zero );
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      mg.AddQuad( 0, 1, 2, 3 );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      // back 
 | 
				
			||||||
 | 
					      AddSide( 2, 1 );
 | 
				
			||||||
 | 
					      // right
 | 
				
			||||||
 | 
					      AddSide( 7, 3 );
 | 
				
			||||||
 | 
					      // left
 | 
				
			||||||
 | 
					      AddSide( 0, 4 );
 | 
				
			||||||
 | 
					      //front
 | 
				
			||||||
 | 
					      AddSide( 5, 6 );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      // side b,r
 | 
				
			||||||
 | 
					      AddSide( 3, 2 );
 | 
				
			||||||
 | 
					      // side f, r
 | 
				
			||||||
 | 
					      AddSide( 6, 7 );
 | 
				
			||||||
 | 
					      // side f, l
 | 
				
			||||||
 | 
					      AddSide( 4, 5 );
 | 
				
			||||||
 | 
					      // side b, l
 | 
				
			||||||
 | 
					      AddSide( 1, 0 );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      
 | 
				
			||||||
 | 
					      // left, back
 | 
				
			||||||
 | 
					      AddCorner( 0, 1, 0 );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      // back, right
 | 
				
			||||||
 | 
					      AddCorner( 2, 3, 1 );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      // left, front
 | 
				
			||||||
 | 
					      AddCorner( 5, 4, 2 );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      // front, right
 | 
				
			||||||
 | 
					      AddCorner( 7, 6, 3 );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      // back
 | 
				
			||||||
 | 
					      AddFaceSide( 0, 1, 2, 1 );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      // right
 | 
				
			||||||
 | 
					      AddFaceSide( 1, 3, 7, 3 );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      // front
 | 
				
			||||||
 | 
					      AddFaceSide( 3, 2, 5, 6 );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      // left
 | 
				
			||||||
 | 
					      AddFaceSide( 2, 0, 0, 4 );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      mg.AddQuad( 20, 21, 22, 23, true );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      output.Mesh = mg.GenerateMesh();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void AddSide( int a, int b )
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      mg.AddQuad( te( a ), te( b ), be( a ), be( b ) );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void AddFaceSide( int fA, int fB, int eA, int eB )
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      mg.AddQuad( tf( fB ), tf( fA), te( eA ), te( eB ) );      
 | 
				
			||||||
 | 
					      mg.AddQuad( bf( fB ), bf( fA), be( eA ), be( eB ), true );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void AddCorner( int eA, int eB, int f )
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      mg.AddTriangle( te( eA ), te( eB ), tf( f ), true );
 | 
				
			||||||
 | 
					      mg.AddTriangle( be( eA ), be( eB ), bf( f ) );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    int tf( int index ) 
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      return index;
 | 
				
			||||||
 | 
					    } 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    int te( int index )
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      return index + 4;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    int be( int index )
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      return index + 12;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    int bf( int index )
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      return index + 20;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public Vector3 GetEdgeNormal( int index )
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      switch ( index )
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        case 1: case 2: return Vector3.Back;
 | 
				
			||||||
 | 
					        case 3: case 7: return Vector3.Right;
 | 
				
			||||||
 | 
					        case 0: case 4: return Vector3.Left;
 | 
				
			||||||
 | 
					        case 5: case 6: return Vector3.Forward;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      return Vector3.Zero;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public Vector3 GetOffsetFor( int left, int front )
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      var x = borderSize * Mathf.Sign( left );
 | 
				
			||||||
 | 
					      var z = borderSize * Mathf.Sign( front );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      return new Vector3( x, 0, z );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public Vector3 GetBoundingCornerAt( int index, bool top )
 | 
				
			||||||
 | 
					    { 
 | 
				
			||||||
 | 
					      //      -Z
 | 
				
			||||||
 | 
					      //    0    1 
 | 
				
			||||||
 | 
					      //    *----*
 | 
				
			||||||
 | 
					      //-X  |    | +X  
 | 
				
			||||||
 | 
					      //    *----*
 | 
				
			||||||
 | 
					      //    2    3  
 | 
				
			||||||
 | 
					      //      +Z    
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      var y = Y() / ( top ? 2 : -2 );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if ( 0 == index )
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        return new Vector3( -X()/2, y, +Z()/2 );
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      else if ( 1 == index )
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        return new Vector3( +X()/2, y, +Z()/2 );
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      else if ( 2 == index )
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        return new Vector3( -X()/2, y, -Z()/2 );
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      else if ( 3 == index )
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        return new Vector3( +X()/2, y, -Z()/2 );
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      return Vector3.Zero;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public Vector3 GetPointAt( int index )
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      // 00-03 4 Top Face 
 | 
				
			||||||
 | 
					      // 04-11 8 Top Edges
 | 
				
			||||||
 | 
					      // 12-19 8 Bottom Edges
 | 
				
			||||||
 | 
					      // 20-23 4 Bottom Face
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if ( index < 4 )
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        return GetFacePoint( index, true );
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      else if ( index < 12 )
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        return GetEdgePoint( index - 4, true );
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      else if ( index < 20 )
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        return GetEdgePoint( index - 12, false );
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      else if ( index < 24 )
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        return GetFacePoint( index - 20, false );
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      return Vector3.Zero;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Vector3 GetFacePoint( int index, bool top )
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      var cornerPoint = GetBoundingCornerAt( index, top );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      var offset = Vector3.Zero;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if ( 0 == index )
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        offset = GetOffsetFor( 1, -1 );
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      else if ( 1 == index )
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        offset = GetOffsetFor( -1, -1 );
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      else if ( 2 == index )
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        offset = GetOffsetFor( 1, 1 );
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      else if ( 3 == index )
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        offset = GetOffsetFor( -1, 1 );
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      return cornerPoint + offset;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Vector3 GetEdgePoint( int index, bool top )
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      var cornerIndex = index / 2;
 | 
				
			||||||
 | 
					      var cornerPoint = GetBoundingCornerAt( cornerIndex, top );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if ( top )
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        cornerPoint.Y -= borderSize;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      else
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        cornerPoint.Y += borderSize;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      var offset = Vector3.Zero;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if ( 0 == cornerIndex )
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        offset = index == 0 ? GetOffsetFor( 0, -1 ) : GetOffsetFor( 1, 0 );
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      else if ( 1 == cornerIndex )
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        offset = index == 2 ? GetOffsetFor( -1, 0 ) : GetOffsetFor( 0, -1 );
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      else if ( 2 == cornerIndex )
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        offset = index == 4 ? GetOffsetFor( 0, 1 ) : GetOffsetFor( 1, 0 );
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      else if ( 3 == cornerIndex )
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        offset = index == 6 ? GetOffsetFor( -1, 0 ) : GetOffsetFor( 0, 1 );
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      return cornerPoint + offset;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,110 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					using Godot;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Rokojori
 | 
				
			||||||
 | 
					{  
 | 
				
			||||||
 | 
					  [GlobalClass,Icon("res://addons/rokojori_action_library/Icons/RJSensor.svg")]
 | 
				
			||||||
 | 
					  public partial class KeySensor : RJSensor
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public Key key;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [ExportGroup( "Modifiers")]
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public Trillean ctrlHold = Trillean.Any;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public Trillean altHold = Trillean.Any;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public Trillean shiftHold = Trillean.Any;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public bool modifiersEnabled => TrilleanLogic.AllAny( ctrlHold, altHold, shiftHold );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public enum ModifiersMode
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      Hold_Modifiers_Only_On_Down,
 | 
				
			||||||
 | 
					      Hold_Modifiers_All_The_Time
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public ModifiersMode modifiersMode;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    bool _isActive;
 | 
				
			||||||
 | 
					    bool _wasActive;
 | 
				
			||||||
 | 
					    float _value = 0;
 | 
				
			||||||
 | 
					    float axisActivationTreshold = 0.5f;
 | 
				
			||||||
 | 
					    float _lastInput = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public override void _Process( double delta )
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      UpdateValue( _lastInput );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public override void _Input( InputEvent ev )
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      var keyEvent = ev as InputEventKey;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if ( keyEvent == null )
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					      } 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if ( keyEvent.Keycode != key)
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      var checkModifiers = modifiersEnabled && 
 | 
				
			||||||
 | 
					                           ( 
 | 
				
			||||||
 | 
					                            ModifiersMode.Hold_Modifiers_All_The_Time == modifiersMode ||
 | 
				
			||||||
 | 
					                           _lastInput == 0 && ModifiersMode.Hold_Modifiers_Only_On_Down == modifiersMode 
 | 
				
			||||||
 | 
					                           );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if ( checkModifiers )
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        if ( ! TrilleanLogic.Matches( ctrlHold, keyEvent.CtrlPressed ) )
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					          _lastInput = 0;
 | 
				
			||||||
 | 
					          return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if ( ! TrilleanLogic.Matches( altHold, keyEvent.AltPressed ) )
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					          _lastInput = 0;
 | 
				
			||||||
 | 
					          return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if ( ! TrilleanLogic.Matches( shiftHold, keyEvent.ShiftPressed ) )
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					          _lastInput = 0;
 | 
				
			||||||
 | 
					          return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      _lastInput = keyEvent.IsPressed() ? 1 : 0; 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public override bool IsActive()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      return _isActive;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public override bool WasActive()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      return _wasActive;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public override void UpdateValue( float value )
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      _value = value;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      _wasActive = _isActive;
 | 
				
			||||||
 | 
					      _isActive = _value > axisActivationTreshold;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,95 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					using Godot;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Rokojori
 | 
				
			||||||
 | 
					{  
 | 
				
			||||||
 | 
					  [GlobalClass,Icon("res://addons/rokojori_action_library/Icons/RJSensor.svg")]
 | 
				
			||||||
 | 
					  public partial class MouseMotionDelta : RJSensor
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    public enum MouseMotionType
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      Right,
 | 
				
			||||||
 | 
					      Left,
 | 
				
			||||||
 | 
					      Up,
 | 
				
			||||||
 | 
					      Down
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public MouseMotionType motionType;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public float speedMultiply = 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    bool _isActive = false;
 | 
				
			||||||
 | 
					    bool _wasActive = false;
 | 
				
			||||||
 | 
					    float _value = 0;
 | 
				
			||||||
 | 
					    float axisActivationTreshold = 0.5f;
 | 
				
			||||||
 | 
					    float _lastInput = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public override void _Process( double delta )
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      UpdateValue( _lastInput );
 | 
				
			||||||
 | 
					      _lastInput = 0;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public override void _Input( InputEvent ev )
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      var mouseEvent = ev as InputEventMouseMotion ;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if ( mouseEvent == null )
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					      } 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      var delta = mouseEvent.ScreenRelative;
 | 
				
			||||||
 | 
					      var motion = 0f;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if ( MouseMotionType.Left == motionType)
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        motion = Mathf.Max( 0, -delta.X );
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      else if ( MouseMotionType.Right == motionType)
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        motion = Mathf.Max( 0, delta.X );
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      else if ( MouseMotionType.Up == motionType)
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        motion = Mathf.Max( 0, delta.Y );
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      if ( MouseMotionType.Down == motionType)
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        motion = Mathf.Max( 0, -delta.Y );
 | 
				
			||||||
 | 
					      } 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      // RJLog.Log( "Motion:", motionType, motion * speedMultiply );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      _lastInput = motion * speedMultiply;
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public override bool IsActive()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      return _isActive;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public override bool WasActive()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      return _wasActive;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public override float GetValue()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      return _value;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public override void UpdateValue( float value )
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      _value = value;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      _wasActive = _isActive;
 | 
				
			||||||
 | 
					      _isActive = _value > axisActivationTreshold;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,61 @@
 | 
				
			||||||
 | 
					using Godot;
 | 
				
			||||||
 | 
					using System.Reflection;
 | 
				
			||||||
 | 
					using System.Collections.Generic;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Rokojori
 | 
				
			||||||
 | 
					{ 
 | 
				
			||||||
 | 
					  public class Materials
 | 
				
			||||||
 | 
					  { 
 | 
				
			||||||
 | 
					    public static void Set( Node node, Material material, int index = 0 )
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      if ( node is MeshInstance3D )
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        var mi = (MeshInstance3D) node;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        mi.SetSurfaceOverrideMaterial( index, material );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if ( index == 0 && mi.MaterialOverride != null )
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					          mi.MaterialOverride = null;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if ( node is CsgPrimitive3D )
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        ReflectionHelper.SetMemberValue( node, "material", material );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        var cp = (GpuParticles3D) node;
 | 
				
			||||||
 | 
					        cp.ProcessMaterial = material;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if ( cp.MaterialOverride != null )
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					          cp.MaterialOverride = null;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if ( node is GpuParticles3D )
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        var gp = (GpuParticles3D) node;
 | 
				
			||||||
 | 
					        gp.ProcessMaterial = material;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if ( gp.MaterialOverride != null )
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					          gp.MaterialOverride = null;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if ( node is GeometryInstance3D )
 | 
				
			||||||
 | 
					      { 
 | 
				
			||||||
 | 
					        var gi = (GeometryInstance3D) node;
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        gi.MaterialOverride = material;
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -12,6 +12,18 @@ namespace Rokojori
 | 
				
			||||||
      return Array.IndexOf( values, other );
 | 
					      return Array.IndexOf( values, other );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static U[] Map<T,U>( T[] values, Func<T,U> mapper  )
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      var u = new U[ values.Length ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      for ( int i = 0; i < values.Length; i++ )
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        u[ i ] = mapper( values[ i ] );
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      return u;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public static int FindIndex<T>( T[] values, Func<T,bool> predicate  )
 | 
					    public static int FindIndex<T>( T[] values, Func<T,bool> predicate  )
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
      for ( int i = 0; i < values.Length; i++ )
 | 
					      for ( int i = 0; i < values.Length; i++ )
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,49 @@
 | 
				
			||||||
 | 
					using System.Collections;
 | 
				
			||||||
 | 
					using System.Collections.Generic;
 | 
				
			||||||
 | 
					using System;
 | 
				
			||||||
 | 
					using System.Reflection;
 | 
				
			||||||
 | 
					using System.Text.RegularExpressions;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Rokojori
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  public enum Trillean
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    False,
 | 
				
			||||||
 | 
					    True,
 | 
				
			||||||
 | 
					    Any
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public static class TrilleanLogic
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    public static bool Matches( Trillean value, bool state, bool anyValue = true )
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      if ( Trillean.Any == value )
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        return anyValue;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      var boolValue = Trillean.True == value;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      return boolValue == state;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static bool AllAny( params Trillean[] values )
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      if ( values == null || values.Length == 0 )
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        return false;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      for ( int i = 0; i < values.Length; i++ )
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        if ( values[ i ] != Trillean.Any )
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					          return false;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      return true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -89,6 +89,7 @@ namespace Rokojori
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    public override void _Process( double delta )
 | 
					    public override void _Process( double delta )
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
 | 
					      
 | 
				
			||||||
      Orbit();
 | 
					      Orbit();
 | 
				
			||||||
      Pan();      
 | 
					      Pan();      
 | 
				
			||||||
      Zoom();
 | 
					      Zoom();
 | 
				
			||||||
| 
						 | 
					@ -109,18 +110,21 @@ namespace Rokojori
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    public override void _Input( InputEvent inputEvent )
 | 
					    public override void _Input( InputEvent inputEvent )
 | 
				
			||||||
    {     
 | 
					    {     
 | 
				
			||||||
      if ( inputEvent is InputEventMouseMotion )
 | 
					      var mouseMotionEvent = inputEvent as InputEventMouseMotion;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if ( mouseMotionEvent == null )
 | 
				
			||||||
      {
 | 
					      {
 | 
				
			||||||
        var eventMouseMotion = inputEvent as InputEventMouseMotion;
 | 
					        return;
 | 
				
			||||||
        motionDelta = eventMouseMotion.ScreenRelative;
 | 
					 | 
				
			||||||
        hasMotionDelta = true;
 | 
					 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      motionDelta = mouseMotionEvent.ScreenRelative;
 | 
				
			||||||
 | 
					      hasMotionDelta = true;      
 | 
				
			||||||
      
 | 
					      
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    void Orbit()
 | 
					    void Orbit()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
      if ( ! orbitButton.IsActive() )
 | 
					      if ( ! Sensors.IsActive( orbitButton ) )
 | 
				
			||||||
      {
 | 
					      {
 | 
				
			||||||
        return;
 | 
					        return;
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
| 
						 | 
					@ -134,7 +138,7 @@ namespace Rokojori
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    void Pan()
 | 
					    void Pan()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
       if ( ! panButton.IsActive() )
 | 
					      if ( ! Sensors.IsActive( panButton ) )
 | 
				
			||||||
      {
 | 
					      {
 | 
				
			||||||
        return;
 | 
					        return;
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
| 
						 | 
					@ -148,12 +152,12 @@ namespace Rokojori
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    void Zoom()
 | 
					    void Zoom()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
      if ( zoomInButton.IsActive() )
 | 
					      if ( Sensors.IsActive( zoomInButton ) )
 | 
				
			||||||
      {
 | 
					      {
 | 
				
			||||||
        distance *= Mathf.Pow( 1 + zoomStepInPercentage / 100f, 1 );
 | 
					        distance *= Mathf.Pow( 1 + zoomStepInPercentage / 100f, 1 );
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      if ( zoomOutButton.IsActive() )
 | 
					      if ( Sensors.IsActive( zoomOutButton ) )
 | 
				
			||||||
      {
 | 
					      {
 | 
				
			||||||
        distance *= Mathf.Pow( 1 + zoomStepInPercentage / 100f, -1 );
 | 
					        distance *= Mathf.Pow( 1 + zoomStepInPercentage / 100f, -1 );
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,123 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					using System.Diagnostics;
 | 
				
			||||||
 | 
					using System.Collections;
 | 
				
			||||||
 | 
					using System.Collections.Generic;
 | 
				
			||||||
 | 
					using System;
 | 
				
			||||||
 | 
					using Godot;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Rokojori
 | 
				
			||||||
 | 
					{  
 | 
				
			||||||
 | 
					  [Tool]
 | 
				
			||||||
 | 
					  [GlobalClass]
 | 
				
			||||||
 | 
					  public partial class ThirdPersonCamera:VirtualCamera3D
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public Node3D target;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [ExportGroup("Yaw")]
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public float yawSpeed;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public RJSensor yawPositiveAxis;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public RJSensor yawNegativeAxis;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public float yaw = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public float yawSmoothingDuration = 0.1f;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Smoother yawSmoother = new Smoother();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [ExportGroup("Pitch")]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public float pitchSpeed;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public RJSensor pitchPositiveAxis;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public RJSensor pitchNegativeAxis;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public float pitch = 0; 
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public float minPitch = -10;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public float maxPitch = 80;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public float pitchSmoothingDuration = 0.1f;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Smoother pitchSmoother = new Smoother();    
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public Curve distanceForPitch = MathX.Curve( 1, 1 );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public float distanceScale = 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Smoother distanceSmoother = new Smoother();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    float smoothedYaw      = 0;
 | 
				
			||||||
 | 
					    float smoothedPitch    = 0;
 | 
				
			||||||
 | 
					    float smoothedDistance = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public override void _Process( double delta )
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      if ( target == null )
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      var yawAxis = Sensors.PolarAxis( yawNegativeAxis, yawPositiveAxis );
 | 
				
			||||||
 | 
					      var pitchAxis = Sensors.PolarAxis( pitchNegativeAxis, pitchPositiveAxis );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      
 | 
				
			||||||
 | 
					      yaw += yawAxis * yawSpeed * (float)delta;      
 | 
				
			||||||
 | 
					      yaw = MathX.Repeat( yaw, 360f );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      // pitch += pitchAxis * pitchSpeed * (float)delta;     
 | 
				
			||||||
 | 
					      // pitch = Mathf.Clamp( pitch, minPitch, maxPitch );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      
 | 
				
			||||||
 | 
					      if ( Mathf.Abs( yaw - smoothedYaw ) > 180 )
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        if ( yaw > smoothedYaw )
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					          smoothedYaw += 360;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        else if ( yaw < smoothedYaw )
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					          smoothedYaw -= 360;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      // smoothedYaw = yawSmoother.SmoothForDuration( smoothedYaw, yaw, yawSmoothingDuration, (float) delta );
 | 
				
			||||||
 | 
					      // smoothedPitch = pitchSmoother.SmoothForDuration( smoothedPitch, pitch, pitchSmoothingDuration, (float) delta );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      smoothedYaw = yaw;
 | 
				
			||||||
 | 
					      smoothedPitch = pitch;
 | 
				
			||||||
 | 
					      var distance = distanceForPitch.Sample( MathX.NormalizeClamped( pitch, minPitch, maxPitch ) ) * distanceScale;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      GlobalPosition =  target.GlobalPosition + Math3D.YawPitchRotation( smoothedYaw, smoothedPitch ) * Vector3.Forward * distance;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      LookAt( target.GlobalPosition, Vector3.Up, true );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -63,6 +63,7 @@ namespace Rokojori
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      if ( sumPriority == 0 )
 | 
					      if ( sumPriority == 0 )
 | 
				
			||||||
      {
 | 
					      {
 | 
				
			||||||
 | 
					        // RJLog.Log( "sumPriority == 0", _cameraSlots.Count, Lists.Map( _cameraSlots, c => c.priority ) );
 | 
				
			||||||
        return;
 | 
					        return;
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -126,6 +127,7 @@ namespace Rokojori
 | 
				
			||||||
        up = up.Normalized();
 | 
					        up = up.Normalized();
 | 
				
			||||||
      }      
 | 
					      }      
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      // RJLog.Log( "Set Cam", position );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      camera.GlobalPosition = position;
 | 
					      camera.GlobalPosition = position;
 | 
				
			||||||
      camera.LookAt( position - forward, up );
 | 
					      camera.LookAt( position - forward, up );
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,254 @@
 | 
				
			||||||
 | 
					using Godot;
 | 
				
			||||||
 | 
					using System.Collections;
 | 
				
			||||||
 | 
					using System.Collections.Generic;
 | 
				
			||||||
 | 
					using System.Text.RegularExpressions;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Rokojori
 | 
				
			||||||
 | 
					{  
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  [Tool]
 | 
				
			||||||
 | 
					  [GlobalClass]
 | 
				
			||||||
 | 
					  public partial class WorldMap:Node
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public WorldMapDefinition worldMapDefinition;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public Node regionsContainer;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public Node3D[] streamTargets;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public float streamTargetRadius = 500;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public WorldMapLayerSetting[] layerSettings;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public float hueScale = 0.01f;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public enum LoadingStatus
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      Not_Loaded,
 | 
				
			||||||
 | 
					      Loading,
 | 
				
			||||||
 | 
					      Loaded,
 | 
				
			||||||
 | 
					      Unloading,
 | 
				
			||||||
 | 
					      Error
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public class WorldMapRegionInfo
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      public float loadingTime;
 | 
				
			||||||
 | 
					      public string id;
 | 
				
			||||||
 | 
					      public LoadingStatus status;
 | 
				
			||||||
 | 
					      public WorldRegion region;
 | 
				
			||||||
 | 
					      public List<WorldMapLayerInfo> layerInfos = new List<WorldMapLayerInfo>();  
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public class WorldMapLayerInfo
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      public LoadingStatus status;
 | 
				
			||||||
 | 
					      public WorldMapLayer layer;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    protected Dictionary<string,WorldMapRegionInfo> _loadedRegions = new Dictionary<string, WorldMapRegionInfo>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public override void _Process( double delta )
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      StreamRegions();
 | 
				
			||||||
 | 
					    } 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public Vector2 WorldToRegions( Vector3 worldPosition )
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      return Math2D.XZ( worldPosition / worldMapDefinition.regionSize );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public Vector3 RegionsToWorld( Vector2 regionsPosition )
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      return Math3D.XYasXZ( regionsPosition ) * worldMapDefinition.regionSize;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public string CreateRegionID( Vector2 regionsPosition )
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      var x = Mathf.RoundToInt( regionsPosition.X );
 | 
				
			||||||
 | 
					      var y = Mathf.RoundToInt( regionsPosition.Y );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      return CreateRegionID( x, y );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public string CreateRegionID( int x, int y)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      return x + "," + y; 
 | 
				
			||||||
 | 
					    }    
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public Vector2I ParseID( string id )
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      var numbers = id.Split( "," );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      var x = RegexUtility.ParseInt( numbers[ 0 ] );
 | 
				
			||||||
 | 
					      var y = RegexUtility.ParseInt( numbers[ 1 ] ); 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      return new Vector2I( x, y );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public Box2 GetXZWorldBoxOfRegion( int regionX, int regionY )
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      var boxStart = RegionsToWorld( new Vector2( regionX, regionY ) ) ;
 | 
				
			||||||
 | 
					      var boxEnd   = boxStart + new Vector3( worldMapDefinition.regionSize, 0, worldMapDefinition.regionSize );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      return new Box2( Math2D.XZ( boxStart ), Math2D.XZ( boxEnd ) );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    protected void ClearRegions()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      Nodes.RemoveAndDeleteChildren( regionsContainer );
 | 
				
			||||||
 | 
					      _loadedRegions.Clear();
 | 
				
			||||||
 | 
					    } 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    protected void StreamRegions()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      var regionsToStream = new HashSet<string>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      for ( int i = 0; i < streamTargets.Length; i++ )
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        GetStreamRegions( streamTargets[ i ], regionsToStream );
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      foreach ( var s in regionsToStream )
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        if ( _loadedRegions.ContainsKey( s ) )
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					          continue;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        LoadRegion( s );
 | 
				
			||||||
 | 
					      } 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      foreach ( var s in _loadedRegions )
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        if ( regionsToStream.Contains( s.Key ) )
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					          continue;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        UnloadRegion( s.Key );
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void GetStreamRegions( Node3D streamTarget, HashSet<string> regions )
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      var streamPosition = streamTarget.GlobalPosition;
 | 
				
			||||||
 | 
					      
 | 
				
			||||||
 | 
					      var streamMin = streamPosition - Vector3.One * streamTargetRadius;
 | 
				
			||||||
 | 
					      var streamMax = streamPosition + Vector3.One * streamTargetRadius;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      var positionMin = WorldToRegions( streamPosition - Vector3.One * streamTargetRadius );
 | 
				
			||||||
 | 
					      var positionMax = WorldToRegions( streamPosition + Vector3.One * streamTargetRadius );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      positionMin = positionMin.Floor();
 | 
				
			||||||
 | 
					      positionMax = positionMax.Ceil();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      // RJLog.Log( "CheckRegions", streamMin, streamMax, positionMin, positionMax );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      var min = new Vector2I( (int)positionMin.X, (int)positionMin.Y );
 | 
				
			||||||
 | 
					      var max = new Vector2I( (int)positionMax.X, (int)positionMax.Y );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      var index = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      var streamPositionXZ = Math2D.XZ( streamPosition );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      for ( int i = min.X; i <= max.X; i++ )
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        for ( int j = min.Y; j <= max.Y; j++ )
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					          var regionID = CreateRegionID( i, j );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          if ( regions.Contains( regionID ) )
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            continue;
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          var boxXZ = GetXZWorldBoxOfRegion( i, j );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          var distance = boxXZ.DistanceTo( streamPositionXZ );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          if ( index < 3 )
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            // RJLog.Log( i, j, boxXZ.center, distance );
 | 
				
			||||||
 | 
					            index++;
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					          
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          if ( distance > streamTargetRadius )
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            continue;
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          regions.Add( regionID );
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void LoadRegion( string regionID )
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      var regionIndex = ParseID( regionID );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      // RJLog.Log( "Loading regionID:", regionID, regionIndex.X, regionIndex.Y );
 | 
				
			||||||
 | 
					      var regionInfo = new WorldMapRegionInfo();
 | 
				
			||||||
 | 
					      regionInfo.id = regionID;
 | 
				
			||||||
 | 
					      regionInfo.loadingTime = Time.GetTicksMsec() / 1000f;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					      _loadedRegions[ regionID ] = regionInfo;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      var worldRegion = regionsContainer.CreateChild<WorldRegion>( regionID );
 | 
				
			||||||
 | 
					      worldRegion.indexX = regionIndex.X;
 | 
				
			||||||
 | 
					      worldRegion.indexZ = regionIndex.Y;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      var s = worldMapDefinition.regionSize;
 | 
				
			||||||
 | 
					      var h = s / 2f;
 | 
				
			||||||
 | 
					       var hue = Noise.PerlinXZ( RegionsToWorld( regionIndex ) * hueScale );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      var material = new StandardMaterial3D();
 | 
				
			||||||
 | 
					      material.AlbedoColor = new HSLColor( hue * 360, 1, 0.5f );
 | 
				
			||||||
 | 
					      material.Metallic = 0.2f;
 | 
				
			||||||
 | 
					      material.Roughness = 0.6f;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      worldRegion.GlobalPosition = RegionsToWorld( regionIndex ) + new Vector3( h, 0, h );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      // var lodHM = worldRegion.CreateChild<LODHeightMapGeometry>();
 | 
				
			||||||
 | 
					      // lodHM.material = material;
 | 
				
			||||||
 | 
					      // lodHM.Create();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      var box = worldRegion.CreateChild<CsgBox3D>( "Box" );
 | 
				
			||||||
 | 
					      box.Size = new Vector3( s, 1, s );  
 | 
				
			||||||
 | 
					      box.Material = material;
 | 
				
			||||||
 | 
					      
 | 
				
			||||||
 | 
					      
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      regionInfo.region = worldRegion;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      
 | 
				
			||||||
 | 
					    } 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void UnloadRegion( string regionID )
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      // RJLog.Log( "Unloading regionID:", regionID );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      var info = _loadedRegions[ regionID ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      Nodes.RemoveAndDelete( info.region );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      _loadedRegions.Remove( regionID );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,22 @@
 | 
				
			||||||
 | 
					using Godot;
 | 
				
			||||||
 | 
					using System.Collections;
 | 
				
			||||||
 | 
					using System.Collections.Generic;
 | 
				
			||||||
 | 
					using System.Text.RegularExpressions;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Rokojori
 | 
				
			||||||
 | 
					{  
 | 
				
			||||||
 | 
					  [Tool]
 | 
				
			||||||
 | 
					  [GlobalClass]
 | 
				
			||||||
 | 
					  public partial class WorldMapDefinition:Resource
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public string worldMapPath;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public float regionSize = 250;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public WorldMapLayerDefinition[] layers; 
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,51 @@
 | 
				
			||||||
 | 
					using Godot;
 | 
				
			||||||
 | 
					using System.Collections;
 | 
				
			||||||
 | 
					using System.Collections.Generic;
 | 
				
			||||||
 | 
					using System.Text.RegularExpressions;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Rokojori
 | 
				
			||||||
 | 
					{  
 | 
				
			||||||
 | 
					  [Tool]
 | 
				
			||||||
 | 
					  [GlobalClass]
 | 
				
			||||||
 | 
					  public partial class WorldMapEditor:WorldMap
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public Node layerEditorsContainer;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public bool streaming = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public bool createEmptyRegions = false;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public PackedScene emptyRegion;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [ExportGroup("Debugging")]
 | 
				
			||||||
 | 
					    [Export]  
 | 
				
			||||||
 | 
					    public int numTiles = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    bool wasStreaming = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public override void _Process( double delta )
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      if ( ! streaming )
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        wasStreaming = false;
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      numTiles = _loadedRegions.Count;
 | 
				
			||||||
 | 
					      
 | 
				
			||||||
 | 
					      if ( ! wasStreaming )
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        ClearRegions();
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      StreamRegions();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      wasStreaming = true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,16 @@
 | 
				
			||||||
 | 
					using Godot;
 | 
				
			||||||
 | 
					using System.Collections;
 | 
				
			||||||
 | 
					using System.Collections.Generic;
 | 
				
			||||||
 | 
					using System.Text.RegularExpressions;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Rokojori
 | 
				
			||||||
 | 
					{  
 | 
				
			||||||
 | 
					  [Tool]
 | 
				
			||||||
 | 
					  [GlobalClass]
 | 
				
			||||||
 | 
					  public partial class WorldMapLayer:Node
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public WorldMapLayerDefinition layerDefinition;   
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,16 @@
 | 
				
			||||||
 | 
					using Godot;
 | 
				
			||||||
 | 
					using System.Collections;
 | 
				
			||||||
 | 
					using System.Collections.Generic;
 | 
				
			||||||
 | 
					using System.Text.RegularExpressions;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Rokojori
 | 
				
			||||||
 | 
					{  
 | 
				
			||||||
 | 
					  [Tool]
 | 
				
			||||||
 | 
					  [GlobalClass]
 | 
				
			||||||
 | 
					  public partial class WorldMapLayerDefinition:Resource
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public string layerName;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,16 @@
 | 
				
			||||||
 | 
					using Godot;
 | 
				
			||||||
 | 
					using System.Collections;
 | 
				
			||||||
 | 
					using System.Collections.Generic;
 | 
				
			||||||
 | 
					using System.Text.RegularExpressions;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Rokojori
 | 
				
			||||||
 | 
					{  
 | 
				
			||||||
 | 
					  [Tool]
 | 
				
			||||||
 | 
					  [GlobalClass]
 | 
				
			||||||
 | 
					  public partial class WorldMapLayerEditor:Node
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public WorldMapLayerDefinition layerDefinition;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,18 @@
 | 
				
			||||||
 | 
					using Godot;
 | 
				
			||||||
 | 
					using System.Collections;
 | 
				
			||||||
 | 
					using System.Collections.Generic;
 | 
				
			||||||
 | 
					using System.Text.RegularExpressions;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Rokojori
 | 
				
			||||||
 | 
					{  
 | 
				
			||||||
 | 
					  [Tool]
 | 
				
			||||||
 | 
					  [GlobalClass]
 | 
				
			||||||
 | 
					  public partial class WorldMapLayerSetting:Resource
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public WorldMapLayerDefinition layerDefinition;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public bool active = true;    
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,18 @@
 | 
				
			||||||
 | 
					using Godot;
 | 
				
			||||||
 | 
					using System.Collections;
 | 
				
			||||||
 | 
					using System.Collections.Generic;
 | 
				
			||||||
 | 
					using System.Text.RegularExpressions;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Rokojori
 | 
				
			||||||
 | 
					{  
 | 
				
			||||||
 | 
					  [Tool]
 | 
				
			||||||
 | 
					  [GlobalClass]
 | 
				
			||||||
 | 
					  public partial class WorldRegion:Node3D
 | 
				
			||||||
 | 
					  {    
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public int indexX = 0;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    [Export]
 | 
				
			||||||
 | 
					    public int indexZ = 0;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Loading…
	
		Reference in New Issue