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
 | 
			
		||||
    {
 | 
			
		||||
      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
 | 
			
		||||
{  
 | 
			
		||||
  public class Nodes
 | 
			
		||||
  public static class Nodes
 | 
			
		||||
  {
 | 
			
		||||
    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;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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>();
 | 
			
		||||
      ForEach<T>( root,
 | 
			
		||||
        t => 
 | 
			
		||||
        {
 | 
			
		||||
          if ( ! includeRoot && t == root )
 | 
			
		||||
          {
 | 
			
		||||
            return;          
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
          if ( filter == null || filter( t ) )
 | 
			
		||||
          { 
 | 
			
		||||
            list.Add( t );
 | 
			
		||||
| 
						 | 
				
			
			@ -146,6 +151,54 @@ namespace Rokojori
 | 
			
		|||
      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 )
 | 
			
		||||
    {
 | 
			
		||||
      var parent = node.GetParent();
 | 
			
		||||
| 
						 | 
				
			
			@ -158,6 +211,37 @@ namespace Rokojori
 | 
			
		|||
      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 )
 | 
			
		||||
    {
 | 
			
		||||
      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 );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public Vector2 center => ( min + max ) / 2;
 | 
			
		||||
    public Vector2 size =>  max - min;
 | 
			
		||||
    public void UnionWith( Box2 other )
 | 
			
		||||
    {
 | 
			
		||||
      min = min.Min( other.min );
 | 
			
		||||
| 
						 | 
				
			
			@ -46,7 +48,6 @@ namespace Rokojori
 | 
			
		|||
      max = max.Max( p );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public Vector2 size => max - min;
 | 
			
		||||
 | 
			
		||||
    public void GrowRelativeToSize( float amount )
 | 
			
		||||
    {     
 | 
			
		||||
| 
						 | 
				
			
			@ -68,6 +69,22 @@ namespace Rokojori
 | 
			
		|||
      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()
 | 
			
		||||
    {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -9,6 +9,13 @@ namespace Rokojori
 | 
			
		|||
    public Vector3 min = 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 )
 | 
			
		||||
    {
 | 
			
		||||
      var max = scale * 0.5f;
 | 
			
		||||
| 
						 | 
				
			
			@ -44,6 +51,8 @@ namespace Rokojori
 | 
			
		|||
      return point;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public float maxDistance => ( max - min ).Length();
 | 
			
		||||
 | 
			
		||||
    public static Vector3 Constrain( Vector3 point, Vector3 min, Vector3 max )
 | 
			
		||||
    {
 | 
			
		||||
      point = min.Max( point );
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -69,14 +69,26 @@ namespace Rokojori
 | 
			
		|||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    Vector3? explicitUp = null;
 | 
			
		||||
    public Vector3 up
 | 
			
		||||
    {
 | 
			
		||||
      get
 | 
			
		||||
      {
 | 
			
		||||
        if ( explicitUp != null )
 | 
			
		||||
        {
 | 
			
		||||
          return (Vector3) explicitUp;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        Update();
 | 
			
		||||
 | 
			
		||||
        return _basis.Y; 
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      set
 | 
			
		||||
      {
 | 
			
		||||
        explicitUp = value;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public Pose()
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -59,6 +59,12 @@ namespace Rokojori
 | 
			
		|||
      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();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    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 )
 | 
			
		||||
    {
 | 
			
		||||
      return Mathf.Atan2( direction.Y, direction.X );
 | 
			
		||||
| 
						 | 
				
			
			@ -305,6 +312,11 @@ namespace Rokojori
 | 
			
		|||
      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 )
 | 
			
		||||
    {
 | 
			
		||||
      if ( radians == 0 ) { return Quaternion.Identity; }
 | 
			
		||||
| 
						 | 
				
			
			@ -312,6 +324,11 @@ namespace Rokojori
 | 
			
		|||
      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 )
 | 
			
		||||
    {
 | 
			
		||||
      if ( radians == 0 ) { return Quaternion.Identity; }
 | 
			
		||||
| 
						 | 
				
			
			@ -319,18 +336,21 @@ namespace Rokojori
 | 
			
		|||
      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 )
 | 
			
		||||
    {
 | 
			
		||||
      var offset = node.GlobalPosition;
 | 
			
		||||
 | 
			
		||||
      var localScale = node.Scale;
 | 
			
		||||
      node.GlobalBasis = new Basis( quaternion );
 | 
			
		||||
      node.GlobalPosition = offset;
 | 
			
		||||
      node.Scale = localScale;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
      //SetGlobalRotationTo( node, quaternion );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static void SetGlobalRotationTo( Node3D node, Quaternion quaternion )
 | 
			
		||||
| 
						 | 
				
			
			@ -382,6 +402,17 @@ namespace Rokojori
 | 
			
		|||
      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 )
 | 
			
		||||
    {
 | 
			
		||||
      return GetGlobalUp( node );
 | 
			
		||||
| 
						 | 
				
			
			@ -447,20 +478,66 @@ namespace Rokojori
 | 
			
		|||
      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;
 | 
			
		||||
 | 
			
		||||
      Nodes.ForEach<VisualInstance3D>( node,
 | 
			
		||||
        ( vi )=>
 | 
			
		||||
        {
 | 
			
		||||
          if ( onlyVisible && ! vi.IsVisibleInTree() )
 | 
			
		||||
          {
 | 
			
		||||
            return;
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
          var nBounds = vi.GetAabb();
 | 
			
		||||
 | 
			
		||||
          nBounds.Size *= GetGlobalUniScale( vi );
 | 
			
		||||
          nBounds.Position += vi.GlobalPosition;
 | 
			
		||||
          nBounds.End += vi.GlobalPosition;
 | 
			
		||||
 | 
			
		||||
          worldBounds = worldBounds == null ? nBounds : ( ((Aabb)worldBounds).Merge( nBounds ) );
 | 
			
		||||
        }
 | 
			
		||||
      );
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -35,7 +35,7 @@ namespace Rokojori
 | 
			
		|||
 | 
			
		||||
    public static float Min( params float[] values )
 | 
			
		||||
    {
 | 
			
		||||
      var value = - float.MaxValue;
 | 
			
		||||
      var value = float.MaxValue;
 | 
			
		||||
 | 
			
		||||
      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 )
 | 
			
		||||
    {
 | 
			
		||||
      var coefficient = MathX.SmoothingCoefficient( duration * 1000f );
 | 
			
		||||
      return SmoothWithCoefficient( value, nextValue, coefficient, delta );
 | 
			
		||||
      var coefficient = MathX.SmoothingCoefficient( duration * 1000f, processDelta );
 | 
			
		||||
      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 )
 | 
			
		||||
    {
 | 
			
		||||
      var coefficient = MathX.SmoothingCoefficient( duration * 1000f );
 | 
			
		||||
      return SmoothWithCoefficient( value, nextValue, coefficient, delta );
 | 
			
		||||
      var coefficient = MathX.SmoothingCoefficient( duration * 1000f, processDelta );
 | 
			
		||||
      return SmoothWithCoefficient( value, nextValue, coefficient, delta, processDelta );
 | 
			
		||||
    } 
 | 
			
		||||
 | 
			
		||||
    public Quaternion SmoothWithCoefficient( Quaternion value, Quaternion nextValue, float coefficient, float delta, float processDelta = MathX.fps120Delta )
 | 
			
		||||
| 
						 | 
				
			
			@ -47,5 +47,44 @@ namespace Rokojori
 | 
			
		|||
 | 
			
		||||
      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]
 | 
			
		||||
    public Curve bladeScale = MathX.Curve( 1f );
 | 
			
		||||
 | 
			
		||||
    [Export]
 | 
			
		||||
    public Curve bladeHeight = MathX.Curve( 0.4f );
 | 
			
		||||
 | 
			
		||||
    [Export]
 | 
			
		||||
    public Curve bladeInGround = MathX.Curve( 0.05f );
 | 
			
		||||
 | 
			
		||||
    [Export]
 | 
			
		||||
    public Curve bladeWidth = MathX.Curve( 0.05f );
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -88,20 +94,20 @@ namespace Rokojori
 | 
			
		|||
    [Export]
 | 
			
		||||
    public Curve bladeBending2 = null;
 | 
			
		||||
 | 
			
		||||
    [Export]
 | 
			
		||||
    public Curve bladeHeight = MathX.Curve( 0.4f );
 | 
			
		||||
 | 
			
		||||
    [Export]
 | 
			
		||||
    public Curve bladeInGround = MathX.Curve( 0.05f );
 | 
			
		||||
 | 
			
		||||
    [Export]
 | 
			
		||||
    public Curve positionJitter = MathX.Curve( 0.05f );
 | 
			
		||||
 | 
			
		||||
    [Export]
 | 
			
		||||
    public Curve scaleByDistanceX = MathX.Curve( 1f, 1f );
 | 
			
		||||
    public Curve scaleByDistanceX = MathX.Curve( 1f );
 | 
			
		||||
 | 
			
		||||
    [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]
 | 
			
		||||
    public Curve yawRotation = MathX.Curve( 0f, 1f );
 | 
			
		||||
| 
						 | 
				
			
			@ -130,6 +136,8 @@ namespace Rokojori
 | 
			
		|||
        return;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      update = false; 
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
      var current = SerializedGodotObject.Create( this ); 
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -140,7 +148,7 @@ namespace Rokojori
 | 
			
		|||
        return;     
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      update = false; 
 | 
			
		||||
      
 | 
			
		||||
 | 
			
		||||
      CreatePatch();
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -183,6 +191,7 @@ namespace Rokojori
 | 
			
		|||
          var yaw = random.Sample( yawRotation );
 | 
			
		||||
          var rotationY = yaw * Mathf.Pi * 2f;
 | 
			
		||||
          var maxRotation = random.Sample( randomRotation );
 | 
			
		||||
          var normalBlendingAmount = random.Sample( normalBlending );
 | 
			
		||||
          var rotationOther = random.Next() * Mathf.DegToRad( maxRotation );
 | 
			
		||||
          var rotationX = random.Next() * rotationOther;
 | 
			
		||||
          var rotationZ = rotationOther - rotationX;
 | 
			
		||||
| 
						 | 
				
			
			@ -205,13 +214,24 @@ namespace Rokojori
 | 
			
		|||
         
 | 
			
		||||
          bladeMG.ApplyTransform( trsf );
 | 
			
		||||
 | 
			
		||||
          MeshGeometry clonedBladeMG = null;
 | 
			
		||||
 | 
			
		||||
          if ( createBackFaces )
 | 
			
		||||
          {
 | 
			
		||||
            clonedBladeMG = bladeMG.Clone();
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
          bladeMG.BlendNormals( normalBlendingDirection, normalBlendingAmount );        
 | 
			
		||||
          
 | 
			
		||||
          mg.Add( bladeMG );  
 | 
			
		||||
 | 
			
		||||
          if ( createBackFaces )
 | 
			
		||||
          {
 | 
			
		||||
            var blade2MG = bladeMG.Clone();
 | 
			
		||||
            blade2MG.FlipNormalDirection();   
 | 
			
		||||
            mg.Add( blade2MG );       
 | 
			
		||||
            
 | 
			
		||||
            clonedBladeMG.FlipNormalDirection();  
 | 
			
		||||
            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 )
 | 
			
		||||
    {
 | 
			
		||||
      for ( int i = 0; i < vertices.Count; i++ )
 | 
			
		||||
| 
						 | 
				
			
			@ -119,6 +142,7 @@ namespace Rokojori
 | 
			
		|||
      return u * ( segments + 1 ) + v;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
   
 | 
			
		||||
    public static MeshGeometry CreateFromUVFunction( Func<Vector2,Pose> uv, int uSegments, int vSegments )
 | 
			
		||||
    {
 | 
			
		||||
      var mg = new MeshGeometry();
 | 
			
		||||
| 
						 | 
				
			
			@ -275,6 +299,39 @@ namespace Rokojori
 | 
			
		|||
      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()
 | 
			
		||||
    {
 | 
			
		||||
      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 );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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 );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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  )
 | 
			
		||||
    {
 | 
			
		||||
      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 )
 | 
			
		||||
    {
 | 
			
		||||
      
 | 
			
		||||
      Orbit();
 | 
			
		||||
      Pan();      
 | 
			
		||||
      Zoom();
 | 
			
		||||
| 
						 | 
				
			
			@ -109,18 +110,21 @@ namespace Rokojori
 | 
			
		|||
    
 | 
			
		||||
    public override void _Input( InputEvent inputEvent )
 | 
			
		||||
    {     
 | 
			
		||||
      if ( inputEvent is InputEventMouseMotion )
 | 
			
		||||
      var mouseMotionEvent = inputEvent as InputEventMouseMotion;
 | 
			
		||||
 | 
			
		||||
      if ( mouseMotionEvent == null )
 | 
			
		||||
      {
 | 
			
		||||
        var eventMouseMotion = inputEvent as InputEventMouseMotion;
 | 
			
		||||
        motionDelta = eventMouseMotion.ScreenRelative;
 | 
			
		||||
        hasMotionDelta = true;
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      motionDelta = mouseMotionEvent.ScreenRelative;
 | 
			
		||||
      hasMotionDelta = true;      
 | 
			
		||||
      
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void Orbit()
 | 
			
		||||
    {
 | 
			
		||||
      if ( ! orbitButton.IsActive() )
 | 
			
		||||
      if ( ! Sensors.IsActive( orbitButton ) )
 | 
			
		||||
      {
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
| 
						 | 
				
			
			@ -134,7 +138,7 @@ namespace Rokojori
 | 
			
		|||
 | 
			
		||||
    void Pan()
 | 
			
		||||
    {
 | 
			
		||||
       if ( ! panButton.IsActive() )
 | 
			
		||||
      if ( ! Sensors.IsActive( panButton ) )
 | 
			
		||||
      {
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
| 
						 | 
				
			
			@ -148,12 +152,12 @@ namespace Rokojori
 | 
			
		|||
 | 
			
		||||
    void Zoom()
 | 
			
		||||
    {
 | 
			
		||||
      if ( zoomInButton.IsActive() )
 | 
			
		||||
      if ( Sensors.IsActive( zoomInButton ) )
 | 
			
		||||
      {
 | 
			
		||||
        distance *= Mathf.Pow( 1 + zoomStepInPercentage / 100f, 1 );
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      if ( zoomOutButton.IsActive() )
 | 
			
		||||
      if ( Sensors.IsActive( zoomOutButton ) )
 | 
			
		||||
      {
 | 
			
		||||
        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 )
 | 
			
		||||
      {
 | 
			
		||||
        // RJLog.Log( "sumPriority == 0", _cameraSlots.Count, Lists.Map( _cameraSlots, c => c.priority ) );
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -126,6 +127,7 @@ namespace Rokojori
 | 
			
		|||
        up = up.Normalized();
 | 
			
		||||
      }      
 | 
			
		||||
 | 
			
		||||
      // RJLog.Log( "Set Cam", position );
 | 
			
		||||
 | 
			
		||||
      camera.GlobalPosition = position;
 | 
			
		||||
      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