C++ Backend Removal
This commit is contained in:
parent
73cdec243f
commit
b852e63bf0
Binary file not shown.
|
@ -1,19 +1,20 @@
|
|||
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() {
|
||||
|
||||
void vertex()
|
||||
{
|
||||
POSITION = vec4(VERTEX, 1.0);
|
||||
CAMERA = INV_VIEW_MATRIX;
|
||||
}
|
||||
|
||||
void fragment() {
|
||||
void fragment()
|
||||
{
|
||||
|
||||
float depth = texture(DEPTH_TEXTURE, SCREEN_UV).x;
|
||||
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);
|
||||
|
@ -22,10 +23,10 @@ void fragment() {
|
|||
vec4 world = CAMERA * INV_PROJECTION_MATRIX * vec4(ndc, 1.0);
|
||||
vec3 world_position = world.xyz / world.w;
|
||||
|
||||
float aabb_full = depth_scaler;
|
||||
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;
|
||||
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));
|
||||
ALBEDO = vec3(clamp(world_position.z/aabb_full,0,1));
|
||||
}
|
||||
|
|
Binary file not shown.
|
@ -10,7 +10,7 @@ vec4 dilate(sampler2D a_tex, sampler2D tex, vec2 coords, vec2 texel_size, int di
|
|||
{
|
||||
|
||||
vec2 offsets[8] = {
|
||||
vec2(-1,0),vec2(1,0), vec2(0,1), vec2(0,-1),
|
||||
vec2(-1,0),vec2(1,0), vec2(0,1), vec2(0,-1),
|
||||
vec2(-1,1), vec2(1,1), vec2(1,-1), vec2(-1,-1)
|
||||
};
|
||||
|
||||
|
@ -34,7 +34,7 @@ vec4 dilate(sampler2D a_tex, sampler2D tex, vec2 coords, vec2 texel_size, int di
|
|||
else
|
||||
return vec4(off_sample.rgb, a_sample.a);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
return sample;
|
||||
|
|
Binary file not shown.
|
@ -1,12 +1,12 @@
|
|||
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 vec4 albedo_color : source_color;
|
||||
uniform sampler2D albedo_texture : 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);
|
||||
|
||||
|
@ -14,17 +14,19 @@ uniform vec3 uv1_scale;
|
|||
uniform vec3 uv1_offset;
|
||||
|
||||
|
||||
void vertex() {
|
||||
UV=UV*uv1_scale.xy+uv1_offset.xy;
|
||||
void vertex()
|
||||
{
|
||||
UV = UV * uv1_scale.xy + uv1_offset.xy;
|
||||
}
|
||||
|
||||
void fragment() {
|
||||
void fragment()
|
||||
{
|
||||
vec2 base_uv = UV;
|
||||
vec4 albedo_tex = texture(texture_albedo,base_uv);
|
||||
vec4 normal_tex = texture(normal_texture,base_uv);
|
||||
vec4 albedo = texture( albedo_texture, base_uv ) * albedo_color;
|
||||
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)
|
||||
if ( use_normalmap )
|
||||
{
|
||||
vec3 normalmap;
|
||||
normalmap.xy = normal_tex.xy * 2.0 - 1.0;
|
||||
|
@ -32,12 +34,12 @@ void fragment() {
|
|||
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)
|
||||
|
||||
ALBEDO = vec3( -NORMAL.x, NORMAL.y, -NORMAL.z) * 0.5 + 0.5;
|
||||
|
||||
if ( use_alpha_texture )
|
||||
{
|
||||
ALPHA = albedo_tex.a;
|
||||
ALPHA = albedo.a;
|
||||
ALPHA_SCISSOR_THRESHOLD = alpha_scissor_threshold;
|
||||
}
|
||||
|
||||
|
|
Binary file not shown.
Binary file not shown.
|
@ -31,7 +31,7 @@ void fragment() {
|
|||
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)
|
||||
{
|
||||
|
|
Binary file not shown.
Binary file not shown.
|
@ -32,6 +32,9 @@ uniform bool dither = false;
|
|||
uniform float scale = 1.0f;
|
||||
|
||||
uniform float depth_scale : hint_range(0, 1) = 0.0f;
|
||||
uniform float depthMapPivot = 0.5;
|
||||
uniform float depthMapScale = 1;
|
||||
uniform float depthMapOffset = 0;
|
||||
|
||||
uniform float normalmap_depth : hint_range(-5, 5) = 1.0f;
|
||||
|
||||
|
|
|
@ -0,0 +1,373 @@
|
|||
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;
|
||||
uniform vec2 indexOffset = vec2(0,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 = recalculateUV(uv_frame2, frame2, xy_frame2, quad_size, depth_scale, imposterTextureDepth);
|
||||
|
||||
vec4 albedoTex = texture( imposterTextureAlbedo, uv );
|
||||
ALBEDO = albedoTex.rgb;
|
||||
NORMAL = texture( imposterTextureNormal, uv ).rgb;
|
||||
|
||||
ALPHA = albedoTex.a;
|
||||
ALPHA_SCISSOR_THRESHOLD = 0.5;
|
||||
|
||||
vec4 ormTex = texture( imposterTextureOrm, uv );
|
||||
|
||||
METALLIC = ormTex.b * metallic;
|
||||
SPECULAR = specular;
|
||||
ROUGHNESS = ormTex.g * roughness;
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
[gd_resource type="StandardMaterial3D" format=3 uid="uid://b8cbxcruu45c0"]
|
||||
|
||||
[resource]
|
|
@ -0,0 +1,21 @@
|
|||
|
||||
using Godot;
|
||||
|
||||
|
||||
namespace Rokojori
|
||||
{
|
||||
[GlobalClass ]
|
||||
public partial class Action : NetworkNode
|
||||
{
|
||||
public void Trigger()
|
||||
{
|
||||
_OnTrigger();
|
||||
}
|
||||
|
||||
public virtual void _OnTrigger()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -53,7 +53,7 @@ namespace Rokojori
|
|||
|
||||
public void ProcessNext()
|
||||
{
|
||||
RJLog.Log( "@" + sequence.Name, "Index:", sequencablesIndex );
|
||||
// RJLog.Log( "@" + sequence.Name, "Index:", sequencablesIndex );
|
||||
|
||||
if ( sequencablesIndex == 0 )
|
||||
{
|
||||
|
@ -218,7 +218,7 @@ namespace Rokojori
|
|||
// RJLog.Log( "Running", HierarchyName.Of( this ), run.sequencables.Count );
|
||||
running.Add( run );
|
||||
run.ProcessNext();
|
||||
}
|
||||
}
|
||||
|
||||
public override void CancelAction( int id )
|
||||
{
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
|
||||
using Godot;
|
||||
|
||||
|
||||
namespace Rokojori
|
||||
{
|
||||
[GlobalClass ]
|
||||
public partial class IterateActions : RJAction
|
||||
{
|
||||
[ExportGroup( "Read Only")]
|
||||
[Export]
|
||||
public int iterationIndex = 0;
|
||||
|
||||
public override void _OnTrigger()
|
||||
{
|
||||
var num = this.NumDirectChildrenOf<RJAction>();
|
||||
|
||||
if ( num == 0 )
|
||||
{
|
||||
iterationIndex = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
iterationIndex = MathX.Repeat( iterationIndex, num );
|
||||
|
||||
Actions.Trigger( this.GetNthDirectChild<RJAction>( iterationIndex ) );
|
||||
|
||||
iterationIndex++;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
|
||||
using Godot;
|
||||
|
||||
|
||||
namespace Rokojori
|
||||
{
|
||||
[GlobalClass, Tool ]
|
||||
public partial class CopyPose : RJAction
|
||||
{
|
||||
[Export]
|
||||
public Node3D source;
|
||||
|
||||
[Export]
|
||||
public Node3D target;
|
||||
|
||||
|
||||
public override void _Process( double delta )
|
||||
{
|
||||
_OnTrigger();
|
||||
}
|
||||
|
||||
|
||||
public override void _OnTrigger()
|
||||
{
|
||||
if ( source == null || target == null )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
target.GlobalPosition = source.GlobalPosition;
|
||||
target.GlobalRotation = source.GlobalRotation;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,81 @@
|
|||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Text;
|
||||
using Godot;
|
||||
|
||||
namespace Rokojori
|
||||
{
|
||||
[Tool]
|
||||
[GlobalClass]
|
||||
public partial class AnimationCurve:Resource
|
||||
{
|
||||
[Export]
|
||||
public float duration = 1;
|
||||
|
||||
[Export]
|
||||
public float durationRandomRange = 0;
|
||||
|
||||
[Export]
|
||||
public float delay = 0;
|
||||
|
||||
[Export]
|
||||
public float delayRandomRange = 0;
|
||||
|
||||
|
||||
[Export]
|
||||
public Curve curve = MathX.Curve( 0, 0, -1, 1 );
|
||||
|
||||
[Export]
|
||||
public float scaleY = 1;
|
||||
|
||||
[Export]
|
||||
public float scaleRandomRange = 0;
|
||||
|
||||
public float GetRandomizedDuration( Vector3 random )
|
||||
{
|
||||
return duration + random.X;
|
||||
}
|
||||
|
||||
public float GetRandomizedDelay( Vector3 random )
|
||||
{
|
||||
return delay + random.Y;
|
||||
}
|
||||
|
||||
public float GetRandomizedEndTime( Vector3 random )
|
||||
{
|
||||
return GetRandomizedDuration( random ) + GetRandomizedDelay( random );
|
||||
}
|
||||
|
||||
public float GetRandomizedScale( Vector3 random )
|
||||
{
|
||||
return scaleY + random.Z;
|
||||
}
|
||||
|
||||
public Vector3 Randomize( RandomEngine random = null )
|
||||
{
|
||||
random = random == null ? GodotRandom.Get() : random;
|
||||
|
||||
return new Vector3(
|
||||
random.Polar() * durationRandomRange,
|
||||
random.Polar() * delayRandomRange,
|
||||
random.Polar() * scaleRandomRange
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
public float Sample( float time, float durationOffset = 0, float delayOffset = 0, float scaleOffset = 0 )
|
||||
{
|
||||
time -= Mathf.Max( 0f, delay + delayOffset );
|
||||
var phase = MathX.Clamp01( time / ( Mathf.Max( 0.001f, duration + durationOffset ) ) );
|
||||
|
||||
return curve.Sample( phase ) * ( scaleY + scaleOffset );
|
||||
}
|
||||
|
||||
public float Sample( float time, Vector3 randomization )
|
||||
{
|
||||
return Sample( time, randomization.X, randomization.Y, randomization.Z );
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,93 @@
|
|||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Text;
|
||||
using Godot;
|
||||
|
||||
namespace Rokojori
|
||||
{
|
||||
[Tool]
|
||||
[GlobalClass]
|
||||
public partial class AnimationCurve3D:Resource
|
||||
{
|
||||
[Export]
|
||||
public float duration = 1;
|
||||
|
||||
[Export]
|
||||
public float durationRandomRange = 0;
|
||||
|
||||
[Export]
|
||||
public float delay = 0;
|
||||
|
||||
[Export]
|
||||
public float delayRandomRange = 0;
|
||||
|
||||
|
||||
[Export]
|
||||
public Curve xCurve = MathX.Curve( 0, 0, -1, 1 );
|
||||
|
||||
[Export]
|
||||
public Curve yCurve = MathX.Curve( 0, 0, -1, 1 );
|
||||
|
||||
[Export]
|
||||
public Curve zCurve = MathX.Curve( 0, 0, -1, 1 );
|
||||
|
||||
|
||||
[Export]
|
||||
public float scaleAll = 1;
|
||||
|
||||
[Export]
|
||||
public float scaleAllRandomRange = 0;
|
||||
|
||||
public float GetRandomizedDuration( Vector3 random )
|
||||
{
|
||||
return duration + random.X;
|
||||
}
|
||||
|
||||
public float GetRandomizedDelay( Vector3 random )
|
||||
{
|
||||
return delay + random.Y;
|
||||
}
|
||||
|
||||
public float GetRandomizedEndTime( Vector3 random )
|
||||
{
|
||||
return GetRandomizedDuration( random ) + GetRandomizedDelay( random );
|
||||
}
|
||||
|
||||
public float GetRandomizedScale( Vector3 random )
|
||||
{
|
||||
return scaleAll + random.Z;
|
||||
}
|
||||
|
||||
public Vector3 Randomize( RandomEngine random = null )
|
||||
{
|
||||
random = random == null ? GodotRandom.Get() : random;
|
||||
|
||||
return new Vector3(
|
||||
random.Polar() * durationRandomRange,
|
||||
random.Polar() * delayRandomRange,
|
||||
random.Polar() * scaleAllRandomRange
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
public Vector3 Sample( float time, float durationOffset = 0, float delayOffset = 0, float scaleOffset = 0 )
|
||||
{
|
||||
time -= Mathf.Max( 0f, delay + delayOffset );
|
||||
var phase = MathX.Clamp01( time / ( Mathf.Max( 0.001f, duration + durationOffset ) ) );
|
||||
|
||||
|
||||
var x = xCurve.Sample( phase ) * ( scaleAll + scaleOffset );
|
||||
var y = yCurve.Sample( phase ) * ( scaleAll + scaleOffset );
|
||||
var z = zCurve.Sample( phase ) * ( scaleAll + scaleOffset );
|
||||
|
||||
return new Vector3( x, y, z );
|
||||
}
|
||||
|
||||
public Vector3 Sample( float time, Vector3 randomization )
|
||||
{
|
||||
return Sample( time, randomization.X, randomization.Y, randomization.Z );
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Text;
|
||||
using Godot;
|
||||
|
||||
namespace Rokojori
|
||||
{
|
||||
public interface Animator
|
||||
{
|
||||
public void OnAnimatorStart();
|
||||
public void OnAnimatorEnd();
|
||||
public void OnAnimatorCancel();
|
||||
}
|
||||
|
||||
/*
|
||||
public void OnAnimatorStart(){}
|
||||
public void OnAnimatorEnd(){}
|
||||
public void OnAnimatorCancel(){}
|
||||
*/
|
||||
|
||||
public class AnimationMember
|
||||
{
|
||||
string _name;
|
||||
public string name => _name;
|
||||
|
||||
public AnimationMember( string name )
|
||||
{
|
||||
_name = name;
|
||||
}
|
||||
}
|
||||
|
||||
public class AnimationManager
|
||||
{
|
||||
static MultiMap<Node,string,Animator> _animating = new MultiMap<Node,string,Animator>();
|
||||
|
||||
public static AnimationMember position = new AnimationMember( "position" );
|
||||
public static AnimationMember rotation = new AnimationMember( "rotation" );
|
||||
public static AnimationMember scale = new AnimationMember( "scale" );
|
||||
|
||||
public static Animator GetAnimator( Node node, AnimationMember member )
|
||||
{
|
||||
return _animating[ node ][ member.name ];
|
||||
}
|
||||
|
||||
public static bool IsAnimating( Animator animator, Node node, AnimationMember member )
|
||||
{
|
||||
return GetAnimator( node, member ) == animator;
|
||||
}
|
||||
|
||||
public static void StartAnimation( Animator animator, Node node, AnimationMember member )
|
||||
{
|
||||
var activeAnimator = GetAnimator( node, member );
|
||||
|
||||
if ( activeAnimator != null )
|
||||
{
|
||||
activeAnimator.OnAnimatorCancel();
|
||||
}
|
||||
|
||||
_animating.Set( node, member.name, animator );
|
||||
|
||||
}
|
||||
|
||||
public static void EndAnimation( Animator animator, Node node, AnimationMember member )
|
||||
{
|
||||
var activeAnimator = GetAnimator( node, member );
|
||||
|
||||
if ( activeAnimator != null )
|
||||
{
|
||||
activeAnimator.OnAnimatorCancel();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,261 @@
|
|||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Text;
|
||||
using Godot;
|
||||
|
||||
namespace Rokojori
|
||||
{
|
||||
[Tool]
|
||||
[GlobalClass]
|
||||
public partial class Flash:RJSequenceAction
|
||||
{
|
||||
[Export]
|
||||
public FlashEffect flashEffect;
|
||||
|
||||
[Export]
|
||||
public Node3D[] targets;
|
||||
|
||||
int animationID = -1;
|
||||
int actionID = -1;
|
||||
|
||||
Vector3 randomization;
|
||||
List<Material> _materials;
|
||||
|
||||
OmniLight3D light = null;
|
||||
|
||||
Color ComputeLightColor( Color color, float phase )
|
||||
{
|
||||
if ( phase < 5/100f )
|
||||
{
|
||||
var n = MathX.NormalizeClamped( phase, 0, 5/100f );
|
||||
color *= n;
|
||||
|
||||
}
|
||||
else if ( phase > 95/100f )
|
||||
{
|
||||
var n = MathX.NormalizeClamped( phase, 1, 95/100f );
|
||||
color *= n;
|
||||
}
|
||||
|
||||
return color;
|
||||
}
|
||||
|
||||
public override void _OnTrigger()
|
||||
{
|
||||
if ( actionID != -1 )
|
||||
{
|
||||
CancelAction( actionID );
|
||||
}
|
||||
|
||||
actionID = DispatchStart();
|
||||
|
||||
// RJLog.Log( "Setting actionID id", actionID, GetLastSequenceActionID() );
|
||||
|
||||
var flashCurve = flashEffect.flashCurve;
|
||||
var color = flashEffect.color.GetHDRColor();
|
||||
var timeline = flashEffect.timeline;
|
||||
|
||||
randomization = flashCurve.Randomize();
|
||||
var duration = flashCurve.GetRandomizedDuration( randomization );
|
||||
|
||||
|
||||
var transparentColor = color;
|
||||
var initialAlpha = transparentColor.A;
|
||||
|
||||
transparentColor.A = 0;
|
||||
|
||||
Material material = null;
|
||||
|
||||
|
||||
if ( FlashEffect.FlashLightMode.No_Light != flashEffect.lightMode )
|
||||
{
|
||||
light = targets[ 0 ].CreateChild<OmniLight3D>();
|
||||
light.LightColor = ComputeLightColor( color, 0 );
|
||||
light.LightEnergy = 0;
|
||||
light.OmniRange = flashEffect.lightRange;
|
||||
light.ShadowEnabled = flashEffect.lightHasShadows;
|
||||
|
||||
// RJLog.Log( "Created Light", light.Name );
|
||||
|
||||
}
|
||||
|
||||
if ( FlashEffect.MaterialMode.Flat_Standard3D == flashEffect.materialMode )
|
||||
{
|
||||
var standardMaterial = new StandardMaterial3D();
|
||||
standardMaterial.AlbedoColor = transparentColor;
|
||||
standardMaterial.ShadingMode = BaseMaterial3D.ShadingModeEnum.Unshaded;
|
||||
standardMaterial.Transparency = BaseMaterial3D.TransparencyEnum.Alpha;
|
||||
|
||||
material = standardMaterial;
|
||||
}
|
||||
else if ( FlashEffect.MaterialMode.Fresnel_FresnelOverlay == flashEffect.materialMode )
|
||||
{
|
||||
var fresnelMaterial = new FresnelOverlayMaterial();
|
||||
fresnelMaterial.albedo.Set( transparentColor );
|
||||
|
||||
material = fresnelMaterial;
|
||||
}
|
||||
else if ( FlashEffect.MaterialMode.Scan_ScanGradient == flashEffect.materialMode )
|
||||
{
|
||||
var scanMaterial = ScanGradientMaterial.White.Get();
|
||||
|
||||
scanMaterial.albedo.Set( transparentColor );
|
||||
|
||||
material = scanMaterial;
|
||||
}
|
||||
else if ( FlashEffect.MaterialMode.Shield_TriPlanarOverlay == flashEffect.materialMode )
|
||||
{
|
||||
var shieldMaterial = TriPlanarOverlayMaterial.WhiteShield.Get();
|
||||
|
||||
shieldMaterial.albedo.Set( transparentColor );
|
||||
|
||||
material = shieldMaterial;
|
||||
}
|
||||
else
|
||||
{
|
||||
material = flashEffect.customMaterial;
|
||||
flashEffect.customFlashColorProperty.Set( material, transparentColor );
|
||||
}
|
||||
|
||||
|
||||
|
||||
_materials = new List<Material>();
|
||||
|
||||
|
||||
Arrays.ForEach( targets,
|
||||
t =>
|
||||
{
|
||||
var m = (Material) material.Duplicate( true );
|
||||
|
||||
_materials.Add( m );
|
||||
|
||||
Materials.AddOverlay( t, m );
|
||||
|
||||
}
|
||||
);
|
||||
|
||||
var start = TimeLineManager.GetPosition( timeline );
|
||||
var end = start + duration;
|
||||
|
||||
animationID = TimeLineScheduler.ScheduleSpanIn( timeline, 0, duration,
|
||||
( int id, int type )=>
|
||||
{
|
||||
|
||||
if ( animationID != id )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
var phase = TimeLineManager.GetRangePhase( timeline, start, end );
|
||||
var value = flashCurve.Sample( phase );
|
||||
|
||||
|
||||
var phaseColor = color;
|
||||
phaseColor.A = value * initialAlpha;
|
||||
|
||||
if ( light != null )
|
||||
{
|
||||
light.LightEnergy = value * flashEffect.lightFlashCurveScale;
|
||||
light.LightColor = ComputeLightColor( color, phase );
|
||||
}
|
||||
|
||||
_materials.ForEach(
|
||||
( m )=>
|
||||
{
|
||||
if ( FlashEffect.MaterialMode.Flat_Standard3D == flashEffect.materialMode )
|
||||
{
|
||||
var sm = m as StandardMaterial3D;
|
||||
sm.AlbedoColor = phaseColor;
|
||||
}
|
||||
else if ( FlashEffect.MaterialMode.Fresnel_FresnelOverlay == flashEffect.materialMode )
|
||||
{
|
||||
var sm = m as FresnelOverlayMaterial;
|
||||
sm.albedo.Set( phaseColor );
|
||||
}
|
||||
else if ( FlashEffect.MaterialMode.Scan_ScanGradient == flashEffect.materialMode )
|
||||
{
|
||||
var sm = m as ScanGradientMaterial;
|
||||
sm.albedo.Set( phaseColor );
|
||||
sm.offset.Set( phase * 3 );
|
||||
}
|
||||
else if ( FlashEffect.MaterialMode.Shield_TriPlanarOverlay == flashEffect.materialMode )
|
||||
{
|
||||
var sm = m as TriPlanarOverlayMaterial;
|
||||
sm.albedo.Set( phaseColor );
|
||||
}
|
||||
else if ( FlashEffect.MaterialMode.Custom_Material == flashEffect.materialMode )
|
||||
{
|
||||
var sm = m as ShaderMaterial;
|
||||
flashEffect.customFlashColorProperty.Set( sm, phaseColor );
|
||||
}
|
||||
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
if ( type == TimeLineSpan.End )
|
||||
{
|
||||
if ( light != null )
|
||||
{
|
||||
light.LightEnergy = 0;
|
||||
light.LightColor = new Color( 0, 0, 0, 0 );
|
||||
}
|
||||
|
||||
CancelAction( actionID );
|
||||
DispatchEnd( actionID );
|
||||
}
|
||||
|
||||
}
|
||||
);
|
||||
|
||||
// RJLog.Log( "Processing animationID", animationID );
|
||||
|
||||
}
|
||||
|
||||
public override void CancelAction( int id )
|
||||
{
|
||||
// RJLog.Log( "CancelAction", id );
|
||||
|
||||
if ( false && actionID != id )
|
||||
{
|
||||
// RJLog.Log( "actionID != id", "id", id, "actionID", actionID );
|
||||
return;
|
||||
}
|
||||
|
||||
if ( light != null )
|
||||
{
|
||||
// RJLog.Log( "Removing Light", light.Name );
|
||||
Nodes.RemoveAndDelete( light );
|
||||
|
||||
light = null;
|
||||
}
|
||||
|
||||
|
||||
RemoveMaterials();
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
void RemoveMaterials()
|
||||
{
|
||||
if ( _materials.Count != targets.Length )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for ( int i = 0; i < targets.Length; i++ )
|
||||
{
|
||||
Materials.RemoveOverlay( targets[ i ], _materials[ i ] );
|
||||
}
|
||||
|
||||
_materials.Clear();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
[gd_resource type="Resource" script_class="FlashEffect" load_steps=8 format=3 uid="uid://bfxwx0fiejnk3"]
|
||||
|
||||
[ext_resource type="Script" path="res://addons/rokojori_action_library/Runtime/Animation/HDRColor.cs" id="1_ejko8"]
|
||||
[ext_resource type="Script" path="res://addons/rokojori_action_library/Runtime/Animation/AnimationCurve.cs" id="2_3i2og"]
|
||||
[ext_resource type="Script" path="res://addons/rokojori_action_library/Runtime/Animation/FlashEffect.cs" id="3_65ipm"]
|
||||
[ext_resource type="RJTimeLine" uid="uid://frruktikky46" path="res://addons/rokojori_action_library/Runtime/Time/TimeLines/GameTime.tres" id="4_urqjo"]
|
||||
|
||||
[sub_resource type="Resource" id="Resource_54hj8"]
|
||||
script = ExtResource("1_ejko8")
|
||||
color = Color(0.151445, 0.275065, 1, 1)
|
||||
colorMultiply = 1.0
|
||||
rgbMultiply = 3.0
|
||||
alphaMultiply = 1.0
|
||||
|
||||
[sub_resource type="Curve" id="Curve_tp3r5"]
|
||||
_data = [Vector2(0, 0), 0.0, -0.249925, 0, 0, Vector2(0.0955632, 0.982574), -2.27942, -2.27942, 0, 0, Vector2(0.238908, 0.442359), -1.89107, -1.89107, 0, 0, Vector2(0.982935, 0.00670242), -0.229111, 0.0, 0, 0]
|
||||
point_count = 4
|
||||
|
||||
[sub_resource type="Resource" id="Resource_pwp07"]
|
||||
script = ExtResource("2_3i2og")
|
||||
duration = 1.0
|
||||
durationRandomRange = 0.0
|
||||
delay = 0.0
|
||||
delayRandomRange = 0.0
|
||||
curve = SubResource("Curve_tp3r5")
|
||||
scaleY = 1.0
|
||||
scaleRandomRange = 0.0
|
||||
|
||||
[resource]
|
||||
script = ExtResource("3_65ipm")
|
||||
flashCurve = SubResource("Resource_pwp07")
|
||||
color = SubResource("Resource_54hj8")
|
||||
lightMode = 1
|
||||
lightRange = 5.0
|
||||
lightFlashCurveScale = 5.0
|
||||
lightHasShadows = true
|
||||
materialMode = 3
|
||||
timeline = ExtResource("4_urqjo")
|
|
@ -0,0 +1,38 @@
|
|||
[gd_resource type="Resource" script_class="FlashEffect" load_steps=8 format=3 uid="uid://drjdsp2qnceql"]
|
||||
|
||||
[ext_resource type="Script" path="res://addons/rokojori_action_library/Runtime/Animation/HDRColor.cs" id="1_yrhv1"]
|
||||
[ext_resource type="Script" path="res://addons/rokojori_action_library/Runtime/Animation/AnimationCurve.cs" id="2_cdv3p"]
|
||||
[ext_resource type="Script" path="res://addons/rokojori_action_library/Runtime/Animation/FlashEffect.cs" id="3_87ql1"]
|
||||
[ext_resource type="RJTimeLine" uid="uid://frruktikky46" path="res://addons/rokojori_action_library/Runtime/Time/TimeLines/GameTime.tres" id="4_t20yf"]
|
||||
|
||||
[sub_resource type="Resource" id="Resource_54hj8"]
|
||||
script = ExtResource("1_yrhv1")
|
||||
color = Color(0, 1, 0.306108, 1)
|
||||
colorMultiply = 1.0
|
||||
rgbMultiply = 2.0
|
||||
alphaMultiply = 1.0
|
||||
|
||||
[sub_resource type="Curve" id="Curve_tp3r5"]
|
||||
_data = [Vector2(0, 0), 0.0, 0.441476, 0, 0, Vector2(0.0957096, 0.959975), -1.01521, -1.01521, 0, 0, Vector2(0.9967, 0.0283505), -0.240285, 0.0, 0, 0]
|
||||
point_count = 3
|
||||
|
||||
[sub_resource type="Resource" id="Resource_pwp07"]
|
||||
script = ExtResource("2_cdv3p")
|
||||
duration = 0.6
|
||||
durationRandomRange = 0.0
|
||||
delay = 0.0
|
||||
delayRandomRange = 0.0
|
||||
curve = SubResource("Curve_tp3r5")
|
||||
scaleY = 1.0
|
||||
scaleRandomRange = 0.0
|
||||
|
||||
[resource]
|
||||
script = ExtResource("3_87ql1")
|
||||
flashCurve = SubResource("Resource_pwp07")
|
||||
color = SubResource("Resource_54hj8")
|
||||
lightMode = 1
|
||||
lightRange = 5.0
|
||||
lightFlashCurveScale = 2.0
|
||||
lightHasShadows = true
|
||||
materialMode = 2
|
||||
timeline = ExtResource("4_t20yf")
|
|
@ -0,0 +1,38 @@
|
|||
[gd_resource type="Resource" script_class="FlashEffect" load_steps=8 format=3 uid="uid://d06w8jorebto2"]
|
||||
|
||||
[ext_resource type="Script" path="res://addons/rokojori_action_library/Runtime/Animation/HDRColor.cs" id="1_c8lnw"]
|
||||
[ext_resource type="Script" path="res://addons/rokojori_action_library/Runtime/Animation/AnimationCurve.cs" id="2_w323b"]
|
||||
[ext_resource type="Script" path="res://addons/rokojori_action_library/Runtime/Animation/FlashEffect.cs" id="3_eqd4c"]
|
||||
[ext_resource type="RJTimeLine" uid="uid://frruktikky46" path="res://addons/rokojori_action_library/Runtime/Time/TimeLines/GameTime.tres" id="4_03b60"]
|
||||
|
||||
[sub_resource type="Resource" id="Resource_ny3sx"]
|
||||
script = ExtResource("1_c8lnw")
|
||||
color = Color(1, 0.472686, 0.335587, 1)
|
||||
colorMultiply = 1.0
|
||||
rgbMultiply = 4.0
|
||||
alphaMultiply = 1.0
|
||||
|
||||
[sub_resource type="Curve" id="Curve_tp3r5"]
|
||||
_data = [Vector2(0, 1), 0.0, -1.0, 0, 1, Vector2(1, 0), -1.0, 0.0, 1, 0]
|
||||
point_count = 2
|
||||
|
||||
[sub_resource type="Resource" id="Resource_pwp07"]
|
||||
script = ExtResource("2_w323b")
|
||||
duration = 0.5
|
||||
durationRandomRange = 0.0
|
||||
delay = 0.0
|
||||
delayRandomRange = 0.0
|
||||
curve = SubResource("Curve_tp3r5")
|
||||
scaleY = 1.0
|
||||
scaleRandomRange = 0.0
|
||||
|
||||
[resource]
|
||||
script = ExtResource("3_eqd4c")
|
||||
flashCurve = SubResource("Resource_pwp07")
|
||||
color = SubResource("Resource_ny3sx")
|
||||
lightMode = 1
|
||||
lightRange = 4.0
|
||||
lightFlashCurveScale = 1.0
|
||||
lightHasShadows = false
|
||||
materialMode = 1
|
||||
timeline = ExtResource("4_03b60")
|
|
@ -0,0 +1,34 @@
|
|||
[gd_resource type="Resource" script_class="FlashEffect" load_steps=8 format=3 uid="uid://cwdnm658q3jta"]
|
||||
|
||||
[ext_resource type="Script" path="res://addons/rokojori_action_library/Runtime/Animation/HDRColor.cs" id="1_nmdum"]
|
||||
[ext_resource type="Script" path="res://addons/rokojori_action_library/Runtime/Animation/AnimationCurve.cs" id="2_0sgd7"]
|
||||
[ext_resource type="Script" path="res://addons/rokojori_action_library/Runtime/Animation/FlashEffect.cs" id="3_7qcuh"]
|
||||
[ext_resource type="RJTimeLine" uid="uid://frruktikky46" path="res://addons/rokojori_action_library/Runtime/Time/TimeLines/GameTime.tres" id="4_8oykd"]
|
||||
|
||||
[sub_resource type="Resource" id="Resource_54hj8"]
|
||||
script = ExtResource("1_nmdum")
|
||||
color = Color(1, 0, 0, 1)
|
||||
colorMultiply = 1.0
|
||||
rgbMultiply = 1.0
|
||||
alphaMultiply = 1.0
|
||||
|
||||
[sub_resource type="Curve" id="Curve_tp3r5"]
|
||||
_data = [Vector2(0, 1), 0.0, -1.0, 0, 1, Vector2(1, 0), -1.0, 0.0, 1, 0]
|
||||
point_count = 2
|
||||
|
||||
[sub_resource type="Resource" id="Resource_pwp07"]
|
||||
script = ExtResource("2_0sgd7")
|
||||
duration = 0.5
|
||||
durationRandomRange = 0.0
|
||||
delay = 0.0
|
||||
delayRandomRange = 0.0
|
||||
curve = SubResource("Curve_tp3r5")
|
||||
scaleY = 1.0
|
||||
scaleRandomRange = 0.0
|
||||
|
||||
[resource]
|
||||
script = ExtResource("3_7qcuh")
|
||||
flashCurve = SubResource("Resource_pwp07")
|
||||
color = SubResource("Resource_54hj8")
|
||||
materialMode = 0
|
||||
timeline = ExtResource("4_8oykd")
|
|
@ -0,0 +1,38 @@
|
|||
[gd_resource type="Resource" script_class="FlashEffect" load_steps=8 format=3 uid="uid://pok7bfoksfmr"]
|
||||
|
||||
[ext_resource type="Script" path="res://addons/rokojori_action_library/Runtime/Animation/HDRColor.cs" id="1_pp4qy"]
|
||||
[ext_resource type="Script" path="res://addons/rokojori_action_library/Runtime/Animation/AnimationCurve.cs" id="2_f6f0o"]
|
||||
[ext_resource type="Script" path="res://addons/rokojori_action_library/Runtime/Animation/FlashEffect.cs" id="3_dq1j1"]
|
||||
[ext_resource type="RJTimeLine" uid="uid://frruktikky46" path="res://addons/rokojori_action_library/Runtime/Time/TimeLines/GameTime.tres" id="4_v17wj"]
|
||||
|
||||
[sub_resource type="Resource" id="Resource_ny3sx"]
|
||||
script = ExtResource("1_pp4qy")
|
||||
color = Color(1, 1, 1, 1)
|
||||
colorMultiply = 1.0
|
||||
rgbMultiply = 2.0
|
||||
alphaMultiply = 1.0
|
||||
|
||||
[sub_resource type="Curve" id="Curve_nqaq6"]
|
||||
_data = [Vector2(0, 1), 0.0, 0.0775112, 0, 0, Vector2(0.0779661, 0), 0.0, 0.0, 0, 0, Vector2(0.227119, 0), 0.113556, 0.113556, 0, 0, Vector2(0.244068, 0.913564), 0.0, 0.0, 0, 0, Vector2(0.308475, 0), 0.0, 0.0, 0, 0, Vector2(0.505085, 0), 0.0, 0.0, 0, 0, Vector2(0.518644, 0.697473), 0.0, 0.0, 0, 0, Vector2(0.60339, 0), 0.0, 0.0, 0, 0, Vector2(0.742794, 0), 0.0, 0.0, 0, 0, Vector2(0.749153, 0.438165), 0.0, 0.0, 0, 0, Vector2(0.80678, 0), 0.0, 0.0, 0, 0, Vector2(1, 0), 0.0442571, 0.0, 0, 0]
|
||||
point_count = 12
|
||||
|
||||
[sub_resource type="Resource" id="Resource_pwp07"]
|
||||
script = ExtResource("2_f6f0o")
|
||||
duration = 0.7
|
||||
durationRandomRange = 0.0
|
||||
delay = 0.0
|
||||
delayRandomRange = 0.0
|
||||
curve = SubResource("Curve_nqaq6")
|
||||
scaleY = 1.0
|
||||
scaleRandomRange = 0.0
|
||||
|
||||
[resource]
|
||||
script = ExtResource("3_dq1j1")
|
||||
flashCurve = SubResource("Resource_pwp07")
|
||||
color = SubResource("Resource_ny3sx")
|
||||
lightMode = 0
|
||||
lightRange = 2.0
|
||||
lightFlashCurveScale = 2.0
|
||||
lightHasShadows = false
|
||||
materialMode = 0
|
||||
timeline = ExtResource("4_v17wj")
|
|
@ -0,0 +1,70 @@
|
|||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Text;
|
||||
using Godot;
|
||||
|
||||
namespace Rokojori
|
||||
{
|
||||
[Tool]
|
||||
[GlobalClass]
|
||||
public partial class FlashEffect:Resource
|
||||
{
|
||||
|
||||
[ExportGroup( "Flash")]
|
||||
[Export]
|
||||
public AnimationCurve flashCurve;
|
||||
|
||||
[Export]
|
||||
public HDRColor color;
|
||||
|
||||
public enum FlashLightMode
|
||||
{
|
||||
No_Light,
|
||||
Origin_Of_First,
|
||||
Center_Of_All
|
||||
}
|
||||
|
||||
[ExportGroup( "Flash/Light")]
|
||||
|
||||
[Export]
|
||||
public FlashLightMode lightMode;
|
||||
|
||||
[Export]
|
||||
public float lightRange = 2;
|
||||
|
||||
[Export]
|
||||
public float lightFlashCurveScale = 2;
|
||||
|
||||
[Export]
|
||||
public bool lightHasShadows = false;
|
||||
|
||||
|
||||
public enum MaterialMode
|
||||
{
|
||||
Flat_Standard3D,
|
||||
Fresnel_FresnelOverlay,
|
||||
Scan_ScanGradient,
|
||||
Shield_TriPlanarOverlay,
|
||||
Custom_Material
|
||||
}
|
||||
|
||||
[ExportGroup( "Material")]
|
||||
|
||||
|
||||
[Export]
|
||||
public MaterialMode materialMode;
|
||||
|
||||
[Export]
|
||||
public Material customMaterial;
|
||||
|
||||
[Export]
|
||||
public ColorPropertyName customFlashColorProperty;
|
||||
|
||||
[Export]
|
||||
public RJTimeLine timeline;
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Text;
|
||||
using Godot;
|
||||
|
||||
namespace Rokojori
|
||||
{
|
||||
[Tool]
|
||||
[GlobalClass]
|
||||
public partial class HDRColor:Resource
|
||||
{
|
||||
[Export]
|
||||
public Color color;
|
||||
|
||||
[Export]
|
||||
public float colorMultiply = 1;
|
||||
|
||||
[Export]
|
||||
public float rgbMultiply = 1;
|
||||
|
||||
[Export]
|
||||
public float alphaMultiply = 1;
|
||||
|
||||
public Color GetHDRColor()
|
||||
{
|
||||
var v4 = ColorX.ToVector4( color ) * colorMultiply;
|
||||
|
||||
v4.X *= rgbMultiply;
|
||||
v4.Y *= rgbMultiply;
|
||||
v4.Z *= rgbMultiply;
|
||||
v4.W *= alphaMultiply;
|
||||
|
||||
return ColorX.FromVector4( v4 );
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Text;
|
||||
using Godot;
|
||||
|
||||
namespace Rokojori
|
||||
{
|
||||
[Tool]
|
||||
[GlobalClass]
|
||||
public partial class Shake:RJSequenceAction
|
||||
{
|
||||
[Export]
|
||||
public ShakeEffect shakeEffect;
|
||||
|
||||
[Export]
|
||||
public Node3D[] targets;
|
||||
|
||||
List<Transform3D> _targetValues;
|
||||
|
||||
int actionID = -1;
|
||||
int animationID = -1;
|
||||
|
||||
public override void _OnTrigger()
|
||||
{
|
||||
if ( actionID != -1 )
|
||||
{
|
||||
CancelAction( actionID );
|
||||
}
|
||||
|
||||
actionID = DispatchStart();
|
||||
|
||||
var random = shakeEffect.shakeCurve.Randomize();
|
||||
var timeline = shakeEffect.timeline;
|
||||
|
||||
var duration = shakeEffect.shakeCurve.GetRandomizedEndTime( random );
|
||||
|
||||
var start = TimeLineManager.GetPosition( shakeEffect.timeline );
|
||||
var end = start + duration;
|
||||
|
||||
|
||||
|
||||
animationID = TimeLineScheduler.ScheduleSpanIn( timeline, 0, duration,
|
||||
( int id, int type )=>
|
||||
{
|
||||
if ( animationID != id )
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
public override void CancelAction( int id )
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Text;
|
||||
using Godot;
|
||||
|
||||
namespace Rokojori
|
||||
{
|
||||
[Tool]
|
||||
[GlobalClass]
|
||||
public partial class ShakeEffect:Resource
|
||||
{
|
||||
|
||||
[Export]
|
||||
public AnimationCurve shakeCurve;
|
||||
|
||||
[Export]
|
||||
public Vector3 positionShake = Vector3.Zero;
|
||||
[Export]
|
||||
public Vector3 positionShakeRandom = Vector3.Zero;
|
||||
|
||||
[Export]
|
||||
public Vector3 rotationShake = Vector3.Zero;
|
||||
[Export]
|
||||
public Vector3 rotationShakeRandom = Vector3.Zero;
|
||||
|
||||
[Export]
|
||||
public Vector3 scaleShake = Vector3.Zero;
|
||||
[Export]
|
||||
public Vector3 scaleShakeRandom = Vector3.Zero;
|
||||
[Export]
|
||||
public bool scaleIsPercentage = true;
|
||||
|
||||
[Export]
|
||||
public RJTimeLine timeline;
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,89 @@
|
|||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Text;
|
||||
using Godot;
|
||||
|
||||
namespace Rokojori
|
||||
{
|
||||
[Tool]
|
||||
[GlobalClass]
|
||||
public partial class AnimateTransform:RJSequenceAction, Animator
|
||||
{
|
||||
|
||||
[Export]
|
||||
public TransformAnimations animations;
|
||||
|
||||
[Export]
|
||||
public Node3D target;
|
||||
|
||||
List<Vector3> _frameValues;
|
||||
List<Vector3> _randomizations;
|
||||
|
||||
public void OnAnimatorStart(){}
|
||||
public void OnAnimatorEnd(){}
|
||||
public void OnAnimatorCancel(){}
|
||||
|
||||
|
||||
public override void _OnTrigger()
|
||||
{
|
||||
_frameValues = new List<Vector3>();
|
||||
_randomizations = new List<Vector3>();
|
||||
|
||||
foreach ( var curve in animations.curves )
|
||||
{
|
||||
_frameValues.Add( TransformTargets.Get( target, curve.transformTarget ) );
|
||||
_randomizations.Add( curve.Randomize() );
|
||||
}
|
||||
|
||||
var timeline = animations.timeline;
|
||||
var duration = animations.GetMaxDuration( _randomizations );
|
||||
|
||||
var start = TimeLineManager.GetPosition( timeline );
|
||||
var end = start + animations.GetMaxDuration( _randomizations );
|
||||
|
||||
var actionID = DispatchStart();
|
||||
|
||||
foreach ( var c in animations.curves )
|
||||
{
|
||||
AnimationManager.StartAnimation( c, target, c.animationMember );
|
||||
}
|
||||
|
||||
TimeLineScheduler.ScheduleSpanIn( timeline, 0, duration,
|
||||
( int id, int type )=>
|
||||
{
|
||||
var timeNow = TimeLineManager.GetPosition( timeline );
|
||||
var elapsed = timeNow - start;
|
||||
|
||||
var index = 0;
|
||||
|
||||
foreach ( var c in animations.curves )
|
||||
{
|
||||
if ( AnimationManager.IsAnimating( c, target, c.animationMember ) )
|
||||
{
|
||||
c.Apply( elapsed, target, _randomizations[ index ], _frameValues[ index ] );
|
||||
}
|
||||
|
||||
index ++;
|
||||
}
|
||||
|
||||
if ( type == TimeLineSpan.End )
|
||||
{
|
||||
foreach ( var c in animations.curves )
|
||||
{
|
||||
AnimationManager.EndAnimation( c, target, c.animationMember );
|
||||
}
|
||||
|
||||
DispatchEnd( actionID );
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Text;
|
||||
using Godot;
|
||||
|
||||
namespace Rokojori
|
||||
{
|
||||
[Tool]
|
||||
[GlobalClass]
|
||||
public partial class TransformAnimations:Resource
|
||||
{
|
||||
[Export]
|
||||
public TransformCurve[] curves;
|
||||
|
||||
[Export]
|
||||
public RJTimeLine timeline;
|
||||
|
||||
public float GetMaxDuration( List<Vector3> randomizations )
|
||||
{
|
||||
var d = 0f;
|
||||
|
||||
for ( int i = 0; i < curves.Length; i++ )
|
||||
{
|
||||
d = Mathf.Max( d, curves[ i ].GetRandomizedEndTime( randomizations[ i ] ) );
|
||||
}
|
||||
|
||||
return d;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Text;
|
||||
using Godot;
|
||||
|
||||
namespace Rokojori
|
||||
{
|
||||
|
||||
[Tool]
|
||||
[GlobalClass]
|
||||
public partial class TransformCurve:AnimationCurve3D,Animator
|
||||
{
|
||||
public void OnAnimatorStart(){}
|
||||
public void OnAnimatorEnd(){}
|
||||
public void OnAnimatorCancel(){}
|
||||
|
||||
public enum OperatorMode
|
||||
{
|
||||
Replace,
|
||||
Add,
|
||||
Multiply
|
||||
}
|
||||
|
||||
[Export]
|
||||
public TransformTarget transformTarget;
|
||||
|
||||
[Export]
|
||||
public OperatorMode operatorMode;
|
||||
|
||||
|
||||
public AnimationMember animationMember => TransformTargets.ToAnimationMember( transformTarget );
|
||||
|
||||
|
||||
public void Apply( float time, Node3D node, Vector3 randomization, Vector3 originalValue )
|
||||
{
|
||||
var animatedValue = Sample( time, randomization );
|
||||
animatedValue = ApplyOperator( animatedValue, originalValue );
|
||||
|
||||
TransformTargets.Set( animatedValue, node, transformTarget );
|
||||
}
|
||||
|
||||
|
||||
Vector3 ApplyOperator( Vector3 animated, Vector3 original )
|
||||
{
|
||||
if ( OperatorMode.Replace == operatorMode )
|
||||
{
|
||||
return animated;
|
||||
}
|
||||
else if ( OperatorMode.Add == operatorMode )
|
||||
{
|
||||
return animated + original;
|
||||
}
|
||||
else if ( OperatorMode.Multiply == operatorMode )
|
||||
{
|
||||
return animated * original;
|
||||
}
|
||||
|
||||
return original;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,92 @@
|
|||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Text;
|
||||
using Godot;
|
||||
|
||||
namespace Rokojori
|
||||
{
|
||||
public enum TransformTarget
|
||||
{
|
||||
Global_Position,
|
||||
Global_Rotation,
|
||||
Local_Position,
|
||||
Local_Rotation,
|
||||
Local_Scale
|
||||
}
|
||||
|
||||
public class TransformTargets
|
||||
{
|
||||
public static AnimationMember ToAnimationMember( TransformTarget target )
|
||||
{
|
||||
if ( TransformTarget.Local_Position == target || TransformTarget.Global_Position == target )
|
||||
{
|
||||
return AnimationManager.position;
|
||||
}
|
||||
|
||||
if ( TransformTarget.Local_Rotation == target || TransformTarget.Global_Rotation == target )
|
||||
{
|
||||
return AnimationManager.rotation;
|
||||
}
|
||||
|
||||
if ( TransformTarget.Local_Scale == target )
|
||||
{
|
||||
return AnimationManager.scale;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static Vector3 Get( Node3D target, TransformTarget transformTarget )
|
||||
{
|
||||
if ( TransformTarget.Global_Position == transformTarget )
|
||||
{
|
||||
return target.GlobalPosition;
|
||||
}
|
||||
else if ( TransformTarget.Global_Rotation == transformTarget )
|
||||
{
|
||||
return target.GlobalRotation;
|
||||
}
|
||||
else if ( TransformTarget.Local_Position == transformTarget )
|
||||
{
|
||||
return target.Position;
|
||||
}
|
||||
else if ( TransformTarget.Local_Rotation == transformTarget )
|
||||
{
|
||||
return target.Rotation;
|
||||
}
|
||||
else if ( TransformTarget.Local_Scale == transformTarget )
|
||||
{
|
||||
return target.Scale;
|
||||
}
|
||||
|
||||
return Vector3.Zero;
|
||||
}
|
||||
|
||||
public static void Set( Vector3 value, Node3D target, TransformTarget transformTarget )
|
||||
{
|
||||
if ( TransformTarget.Global_Position == transformTarget )
|
||||
{
|
||||
target.GlobalPosition = value;
|
||||
}
|
||||
else if ( TransformTarget.Global_Rotation == transformTarget )
|
||||
{
|
||||
target.GlobalRotation = value;
|
||||
}
|
||||
else if ( TransformTarget.Local_Position == transformTarget )
|
||||
{
|
||||
target.Position = value;
|
||||
}
|
||||
else if ( TransformTarget.Local_Rotation == transformTarget )
|
||||
{
|
||||
target.Rotation = value;
|
||||
}
|
||||
else if ( TransformTarget.Local_Scale == transformTarget )
|
||||
{
|
||||
target.Scale = value;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
using Godot;
|
||||
using System.Reflection;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Rokojori
|
||||
{
|
||||
public class AudioSample
|
||||
{
|
||||
float[,] _channels;
|
||||
|
||||
float _sampleRate;
|
||||
|
||||
public float sampleRate => _sampleRate;
|
||||
|
||||
public static AudioSample Create( float[,] channelData, float sampleRate = 44100 )
|
||||
{
|
||||
var sample = new AudioSample();
|
||||
sample._channels = channelData;
|
||||
sample._sampleRate = sampleRate;
|
||||
|
||||
return sample;
|
||||
}
|
||||
|
||||
public static AudioSample Create( int channels, int numSamples, float sampleRate = 44100 )
|
||||
{
|
||||
var sample = new AudioSample();
|
||||
sample._channels = new float[ channels, numSamples ];
|
||||
sample._sampleRate = sampleRate;
|
||||
|
||||
return sample;
|
||||
}
|
||||
|
||||
public float Get( int sample, int channel = 0 )
|
||||
{
|
||||
return _channels[ channel, sample ];
|
||||
}
|
||||
|
||||
public float Left( int sample )
|
||||
{
|
||||
return _channels[ 0, sample ];
|
||||
}
|
||||
|
||||
public float Right( int sample )
|
||||
{
|
||||
return _channels[ 1, sample ];
|
||||
}
|
||||
|
||||
public float this[ int channel, int sample ]
|
||||
{
|
||||
get { return _channels[ channel, sample ]; }
|
||||
set { _channels[ channel, sample ] = value; }
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
using Godot;
|
||||
using System.Reflection;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Rokojori
|
||||
{
|
||||
public class Constant:AudioProcessor
|
||||
{
|
||||
public readonly AudioStreamOutput output;
|
||||
protected float _constantValue;
|
||||
|
||||
public Constant( AudioGraph ag, float value ): base( ag )
|
||||
{
|
||||
_constantValue = value;
|
||||
output = new AudioStreamOutput( this );
|
||||
}
|
||||
|
||||
public void SetConstant( float value )
|
||||
{
|
||||
_constantValue = value;
|
||||
}
|
||||
|
||||
|
||||
protected override void _Process()
|
||||
{
|
||||
for ( int i = 0; i < bufferSize; i++ )
|
||||
{
|
||||
output[ i ] = _constantValue;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
using Godot;
|
||||
using System.Reflection;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Rokojori
|
||||
{
|
||||
public abstract class AudioEffectMono:AudioProcessor
|
||||
{
|
||||
public readonly AudioStreamInput input;
|
||||
public readonly AudioStreamOutput output;
|
||||
|
||||
public AudioEffectMono( AudioGraph ag ): base( ag )
|
||||
{
|
||||
input = new AudioStreamInput( this );
|
||||
output = new AudioStreamOutput( this );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
using Godot;
|
||||
using System.Reflection;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Rokojori
|
||||
{
|
||||
public abstract class AudioEffectStereo:AudioProcessor
|
||||
{
|
||||
public readonly AudioStreamInput inputL;
|
||||
public readonly AudioStreamInput inputR;
|
||||
|
||||
public readonly AudioStreamOutput outputL;
|
||||
public readonly AudioStreamOutput outputR;
|
||||
|
||||
public AudioEffectStereo( AudioGraph ag ): base( ag )
|
||||
{
|
||||
inputL = new AudioStreamInput( this );
|
||||
inputR = new AudioStreamInput( this );
|
||||
|
||||
outputL = new AudioStreamOutput( this );
|
||||
outputR = new AudioStreamOutput( this );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
using Godot;
|
||||
using System.Reflection;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Rokojori
|
||||
{
|
||||
public class Gain:AudioEffectMono
|
||||
{
|
||||
public readonly AudioStreamInput gain;
|
||||
|
||||
public Gain( AudioGraph ag ): base( ag )
|
||||
{
|
||||
gain = new AudioStreamInput( this );
|
||||
}
|
||||
|
||||
protected override void _Process()
|
||||
{
|
||||
for ( int i = 0; i < bufferSize; i++ )
|
||||
{
|
||||
output[ i ] = gain[ i ] * input[ i ];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
using Godot;
|
||||
using System.Reflection;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Rokojori
|
||||
{
|
||||
public abstract class AudioGeneratorMono:AudioProcessor
|
||||
{
|
||||
public readonly AudioStreamOutput output;
|
||||
|
||||
public AudioGeneratorMono( AudioGraph ag ): base( ag )
|
||||
{
|
||||
output = new AudioStreamOutput( this );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
using Godot;
|
||||
using System.Reflection;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Rokojori
|
||||
{
|
||||
public abstract class AudioGeneratorStereo:AudioProcessor
|
||||
{
|
||||
public readonly AudioStreamOutput outputL;
|
||||
public readonly AudioStreamOutput outputR;
|
||||
|
||||
public AudioGeneratorStereo( AudioGraph ag ): base( ag )
|
||||
{
|
||||
outputL = new AudioStreamOutput( this );
|
||||
outputR = new AudioStreamOutput( this );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,198 @@
|
|||
using Godot;
|
||||
using System.Reflection;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Rokojori
|
||||
{
|
||||
public class DuoWaveTable
|
||||
{
|
||||
float[] _samples;
|
||||
float _lowPitch;
|
||||
float _highPitch;
|
||||
int _maxSamples;
|
||||
|
||||
float _weight = 0.5f;
|
||||
|
||||
public DuoWaveTable( float[] samples, float lowPitch, float highPitch )
|
||||
{
|
||||
_samples = samples;
|
||||
_lowPitch = lowPitch;
|
||||
_highPitch = highPitch;
|
||||
_maxSamples = samples.Length / 2;
|
||||
}
|
||||
|
||||
public static DuoWaveTable FromWaveTables( WaveTable low, WaveTable high, float lowPitch, float highPitch )
|
||||
{
|
||||
var samples = new float[ low.samples.Length * 2 ];
|
||||
|
||||
for ( int i = 0; i < low.samples.Length; i++ )
|
||||
{
|
||||
samples[ i * 2 ] = low.samples[ i ];
|
||||
}
|
||||
|
||||
for ( int i = 0; i < high.samples.Length; i++ )
|
||||
{
|
||||
samples[ i * 2 + 1 ] = high.samples[ i ] - low.samples[ i ];
|
||||
}
|
||||
|
||||
return new DuoWaveTable( samples, lowPitch, highPitch );
|
||||
|
||||
}
|
||||
|
||||
|
||||
public float ComputeWeightForPitch( float pitch )
|
||||
{
|
||||
return MathX.NormalizeClamped( pitch, _lowPitch, _highPitch );
|
||||
}
|
||||
|
||||
public void SetWeight( float weight )
|
||||
{
|
||||
this._weight = weight;
|
||||
}
|
||||
|
||||
public void SetWeightForPitch( float pitch )
|
||||
{
|
||||
SetWeight( ComputeWeightForPitch( pitch ) );
|
||||
}
|
||||
|
||||
public float Get( float phase )
|
||||
{
|
||||
var max = _maxSamples;
|
||||
|
||||
var floatingSamplePosition = phase * max;
|
||||
|
||||
var p0 = (int) floatingSamplePosition;
|
||||
var p1 = p0 == ( max - 1 ) ? 0 : ( p0 + 1 );
|
||||
|
||||
var mixAmount = floatingSamplePosition - p0;
|
||||
|
||||
var p0Index = p0 << 1;
|
||||
var p1Index = p1 << 1;
|
||||
|
||||
var low0 = _samples[ p0Index ];
|
||||
var diff0 = _samples[ p0Index + 1 ];
|
||||
|
||||
var low1 = _samples[ p1Index ];
|
||||
var diff1 = _samples[ p1Index + 1 ];
|
||||
|
||||
var mixed0 = low0 + _weight * diff0;
|
||||
var mixed1 = low1 + _weight * diff1;
|
||||
|
||||
return mixed0 + mixAmount * ( mixed1 - mixed0 );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public class BandLimitedWaveTable:iPhaseGenerator
|
||||
{
|
||||
List<DuoWaveTable> entries = new List<DuoWaveTable>();
|
||||
int entriesPerOctave = 3;
|
||||
float lowestPitch;
|
||||
float highestPitch;
|
||||
|
||||
|
||||
int _activeEntryIndex = 0;
|
||||
DuoWaveTable _activeEntry;
|
||||
|
||||
public int GetEntryForPitch( float pitch )
|
||||
{
|
||||
var entry = Mathf.Min( MathX.RemapClamped( pitch, lowestPitch, highestPitch, 0, entries.Count ), entries.Count - 1 );
|
||||
return (int) entry;
|
||||
}
|
||||
|
||||
public void SetPitchRange( float minPitch, float maxPitch )
|
||||
{
|
||||
_activeEntryIndex = GetEntryForPitch( maxPitch );
|
||||
_activeEntry = entries[ _activeEntryIndex ];
|
||||
_activeEntry.SetWeightForPitch( maxPitch );
|
||||
}
|
||||
|
||||
public float Get( float phase )
|
||||
{
|
||||
return _activeEntry.Get( phase );
|
||||
}
|
||||
|
||||
public BandLimitedWaveTable FromSines( Vector2[] sines, int size = 4096, int entriesPerOctave = 3, float lowestPitch = 0 )
|
||||
{
|
||||
var blwt = new BandLimitedWaveTable();
|
||||
blwt.entriesPerOctave = entriesPerOctave;
|
||||
blwt.lowestPitch = lowestPitch;
|
||||
|
||||
float frequency = MathAudio.PitchToFrequency( blwt.lowestPitch );
|
||||
float maxFrequency = 18000;
|
||||
|
||||
while ( frequency < maxFrequency )
|
||||
{
|
||||
for ( int i = 0; i < entriesPerOctave; i++ )
|
||||
{
|
||||
var lowPitchOffset = i / (float) entriesPerOctave;
|
||||
var lowFrequency = MathAudio.OffsetFrequencyBySemitones( frequency, lowPitchOffset );
|
||||
|
||||
if ( lowFrequency > maxFrequency )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var highPitchOffset = ( i + 1 ) / (float) entriesPerOctave;
|
||||
var highFrequency = MathAudio.OffsetFrequencyBySemitones( frequency, highPitchOffset );
|
||||
|
||||
var lowWaveTable = CreateWaveTable( lowFrequency, sines, size );
|
||||
var highWaveTable = CreateWaveTable( highFrequency, sines, size );
|
||||
|
||||
var lowPitch = MathAudio.FrequencyToPitch( lowFrequency );
|
||||
var highPitch = MathAudio.FrequencyToPitch( highFrequency );
|
||||
|
||||
var duoWaveTable = DuoWaveTable.FromWaveTables( lowWaveTable, highWaveTable, lowPitch, highPitch );
|
||||
|
||||
blwt.highestPitch = lowPitch;
|
||||
|
||||
entries.Add( duoWaveTable );
|
||||
}
|
||||
|
||||
|
||||
frequency *= 2;
|
||||
}
|
||||
|
||||
return blwt;
|
||||
}
|
||||
|
||||
WaveTable CreateWaveTable( float frequency, Vector2[] sines, int size )
|
||||
{
|
||||
var filterStart = FindFilterStart( frequency, sines.Length );
|
||||
var filterEnd = FindFilterEnd( frequency, sines.Length, filterStart );
|
||||
|
||||
return WaveTable.FromSines( sines, filterStart, filterEnd, size );
|
||||
}
|
||||
|
||||
int FindFilterStart( float baseFrequency, int numSines, float frequencyTreshold = 14000f )
|
||||
{
|
||||
for ( int i = 0; i < numSines; i++ )
|
||||
{
|
||||
var binFrequency = i * baseFrequency;
|
||||
|
||||
if ( binFrequency >= frequencyTreshold )
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return numSines;
|
||||
}
|
||||
|
||||
int FindFilterEnd( float baseFrequency, int numSines, int start, float frequencyTreshold = 18000f )
|
||||
{
|
||||
for ( int i = start; i < numSines; i++ )
|
||||
{
|
||||
var binFrequency = i * baseFrequency;
|
||||
|
||||
if ( binFrequency >= frequencyTreshold )
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return numSines;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
using Godot;
|
||||
using System.Reflection;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Rokojori
|
||||
{
|
||||
public class PhaseGenerator:AudioGeneratorMono
|
||||
{
|
||||
public readonly AudioEventInput<float> pitch;
|
||||
protected iPhaseGenerator _generator;
|
||||
|
||||
public PhaseGenerator( AudioGraph ag, iPhaseGenerator generator ): base( ag )
|
||||
{
|
||||
pitch = new AudioEventInput<float>( this );
|
||||
this._generator = generator;
|
||||
}
|
||||
|
||||
float _phase = 0;
|
||||
float _increment = 0.01f;
|
||||
|
||||
protected override void _Process()
|
||||
{
|
||||
if ( pitch.numEvents > 0 )
|
||||
{
|
||||
var p = pitch.lastEvent;
|
||||
var frequency = MathAudio.PitchToFrequency( p );
|
||||
_increment = frequency / audioGraph.sampleRate;
|
||||
}
|
||||
|
||||
for ( int i = 0; i < bufferSize; i++ )
|
||||
{
|
||||
_phase = MathAudio.IncrementPhase( _phase, _increment );
|
||||
|
||||
var value = _generator.Get( _phase );
|
||||
|
||||
output[ i ] = value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
using Godot;
|
||||
using System.Reflection;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Rokojori
|
||||
{
|
||||
public class SineGenerator:AudioGeneratorMono
|
||||
{
|
||||
public readonly AudioStreamInput frequency;
|
||||
|
||||
public SineGenerator( AudioGraph ag ): base( ag )
|
||||
{
|
||||
frequency = new AudioStreamInput( this );
|
||||
}
|
||||
|
||||
float _phase = 0;
|
||||
|
||||
protected override void _Process()
|
||||
{
|
||||
for ( int i = 0; i < bufferSize; i++ )
|
||||
{
|
||||
float increment = frequency[ i ] / audioGraph.sampleRate;
|
||||
|
||||
_phase += increment;
|
||||
_phase = MathX.Repeat( _phase, 1 );
|
||||
|
||||
output[ i ] = Mathf.Sin( _phase * Mathf.Pi* 2.0f );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
using Godot;
|
||||
using System.Reflection;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Rokojori
|
||||
{
|
||||
public class WaveTable:iPhaseGenerator
|
||||
{
|
||||
float[] _samples;
|
||||
|
||||
public float[] samples => _samples;
|
||||
|
||||
public void SetPitchRange( float minPitch, float maxPitch )
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public float Get( float phase )
|
||||
{
|
||||
var samplePosition = phase * _samples.Length;
|
||||
samplePosition = MathX.Repeat( samplePosition, _samples.Length );
|
||||
|
||||
var l = (int) samplePosition;
|
||||
var h = MathX.Repeat( l + 1, _samples.Length );
|
||||
|
||||
var lerp = samplePosition - l;
|
||||
|
||||
return Mathf.Lerp( _samples[ l ], _samples[ h ], lerp );
|
||||
|
||||
}
|
||||
|
||||
public static WaveTable FromSamples( float[] samples )
|
||||
{
|
||||
var wt = new WaveTable();
|
||||
wt._samples = samples;
|
||||
|
||||
return wt;
|
||||
}
|
||||
|
||||
public void Normalize()
|
||||
{
|
||||
var max = 0f;
|
||||
|
||||
for ( int i = 0; i < _samples.Length; i++ )
|
||||
{
|
||||
max = Mathf.Max( Mathf.Abs( _samples[ i ] ), max );
|
||||
}
|
||||
|
||||
for ( int i = 0; i < _samples.Length; i++ )
|
||||
{
|
||||
_samples[ i ] = _samples[ i ] / max;
|
||||
}
|
||||
}
|
||||
|
||||
public static WaveTable FromSines( Vector2[] magnitudesAndPhases, float filterStart = -1, float filterEnd = -1, int size = 4096 )
|
||||
{
|
||||
|
||||
var fftReal = new float[ size ];
|
||||
var fftImaginary = new float[ size ];
|
||||
|
||||
var hasNoFilter = ( filterStart == -1 && filterEnd == -1 );
|
||||
|
||||
|
||||
for ( int i = 0; i < magnitudesAndPhases.Length; i++ )
|
||||
{
|
||||
var filter = hasNoFilter ? 1 : ( 1f - MathX.NormalizeClamped( i, filterStart, filterEnd ) );
|
||||
|
||||
fftReal[ i ] = Mathf.Cos( magnitudesAndPhases[ i ].Y ) * magnitudesAndPhases[ i ].X * filter;
|
||||
fftImaginary[ i ] = Mathf.Sin( magnitudesAndPhases[ i ].Y ) * magnitudesAndPhases[ i ].X * filter;
|
||||
}
|
||||
|
||||
|
||||
FFT.Inverse( size, fftReal, fftImaginary );
|
||||
|
||||
return FromSamples( fftReal );
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
using Godot;
|
||||
using System.Reflection;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Rokojori
|
||||
{
|
||||
public class WaveTableGenerator:AudioGeneratorMono
|
||||
{
|
||||
public readonly AudioStreamInput frequency;
|
||||
protected WaveTable waveTable;
|
||||
|
||||
public WaveTableGenerator( AudioGraph ag, WaveTable waveTable ): base( ag )
|
||||
{
|
||||
frequency = new AudioStreamInput( this );
|
||||
this.waveTable = waveTable;
|
||||
}
|
||||
|
||||
float _phase = 0;
|
||||
|
||||
protected override void _Process()
|
||||
{
|
||||
for ( int i = 0; i < bufferSize; i++ )
|
||||
{
|
||||
float increment = frequency[ i ] / audioGraph.sampleRate;
|
||||
|
||||
_phase += increment;
|
||||
_phase = MathX.Repeat( _phase, 1 );
|
||||
|
||||
var value = waveTable.Get( _phase );
|
||||
|
||||
output[ i ] = waveTable.Get( _phase );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
using Godot;
|
||||
using System.Reflection;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Rokojori
|
||||
{
|
||||
public interface iPhaseGenerator
|
||||
{
|
||||
float Get( float phase );
|
||||
void SetPitchRange( float minPitch, float maxPitch );
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
using Godot;
|
||||
using System.Reflection;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Rokojori
|
||||
{
|
||||
public class Instrument:AudioProcessor
|
||||
{
|
||||
public Instrument( AudioGraph ag ):base( ag ){}
|
||||
|
||||
List<Voice> _voices = new List<Voice>();
|
||||
|
||||
protected override void _Process()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
using Godot;
|
||||
using System.Reflection;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Rokojori
|
||||
{
|
||||
public class MusicTimeLine:AudioProcessor
|
||||
{
|
||||
|
||||
|
||||
public AudioEventInput<float> bpm;
|
||||
public AudioEventInput<MusicTimeLineRange> loop;
|
||||
public AudioEventOutput<MusicTimeLineRange> timeLineRanges;
|
||||
|
||||
public MusicTimeLine( AudioGraph ag ):base( ag )
|
||||
{
|
||||
bpm = new AudioEventInput<float>( this );
|
||||
loop = new AudioEventInput<MusicTimeLineRange>( this );
|
||||
|
||||
timeLineRanges = new AudioEventOutput<MusicTimeLineRange>( this);
|
||||
}
|
||||
|
||||
MusicTimeLineRange _loopRange;
|
||||
float _bpm = 120;
|
||||
|
||||
protected override void _Process()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
using Godot;
|
||||
using System.Reflection;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Rokojori
|
||||
{
|
||||
public class MusicTimeLineRange
|
||||
{
|
||||
public double start;
|
||||
public double end;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
using Godot;
|
||||
using System.Reflection;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Rokojori
|
||||
{
|
||||
public class NoteEvent
|
||||
{
|
||||
public int id;
|
||||
public float pitch;
|
||||
public float volume;
|
||||
public float startPosition;
|
||||
public float duration;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
using Godot;
|
||||
using System.Reflection;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Rokojori
|
||||
{
|
||||
public abstract class Voice
|
||||
{
|
||||
NoteEvent _noteEvent;
|
||||
|
||||
public abstract void CancelNote();
|
||||
|
||||
public void Play( NoteEvent ne )
|
||||
{
|
||||
CancelNote();
|
||||
_noteEvent = ne;
|
||||
}
|
||||
|
||||
public abstract void Process();
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
using Godot;
|
||||
using System.Reflection;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Rokojori
|
||||
{
|
||||
public class AudioConnection : AudioNode
|
||||
{
|
||||
protected AudioProcessor _audioProcessor;
|
||||
public AudioProcessor audioProcessor => _audioProcessor;
|
||||
protected bool _isInput = true;
|
||||
public bool isInput => _isInput;
|
||||
|
||||
|
||||
public AudioConnection( AudioProcessor p, bool isInput ): base( p.audioGraph )
|
||||
{
|
||||
_audioProcessor = p;
|
||||
_isInput = isInput;
|
||||
p._Add( this );
|
||||
}
|
||||
|
||||
public override void Destroy()
|
||||
{
|
||||
_audioGraph = null;
|
||||
_audioProcessor = null;
|
||||
}
|
||||
|
||||
public virtual void PollDependencies()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
using Godot;
|
||||
using System.Reflection;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System;
|
||||
|
||||
namespace Rokojori
|
||||
{
|
||||
public abstract class AudioEvent<T> : AudioConnection
|
||||
{
|
||||
protected List<T> _events = new List<T>();
|
||||
|
||||
public AudioEvent( AudioProcessor p, bool isInput ):base( p, isInput )
|
||||
{
|
||||
}
|
||||
|
||||
public override void Clear()
|
||||
{
|
||||
_events.Clear();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
using Godot;
|
||||
using System.Reflection;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System;
|
||||
|
||||
namespace Rokojori
|
||||
{
|
||||
public class AudioEventInput<T> : AudioEvent<T>
|
||||
{
|
||||
public AudioEventInput( AudioProcessor p ):base( p, true )
|
||||
{}
|
||||
|
||||
protected List<AudioEventOutput<T>> _outputs = new List<AudioEventOutput<T>>();
|
||||
|
||||
public List<AudioEventOutput<T>> connected => _outputs;
|
||||
|
||||
public void _GetConnectedFrom( AudioEventOutput<T> output )
|
||||
{
|
||||
_outputs.Add( output );
|
||||
}
|
||||
|
||||
public void _GetDisconnectedFrom( AudioEventOutput<T> output )
|
||||
{
|
||||
_outputs.Remove( output );
|
||||
}
|
||||
|
||||
public int numEvents => _events.Count;
|
||||
|
||||
public T this[ int index ]
|
||||
{
|
||||
get { return _events[ index ]; }
|
||||
}
|
||||
|
||||
public T lastEvent => _events[ _events.Count -1 ];
|
||||
|
||||
public override void PollDependencies()
|
||||
{
|
||||
_events.Clear();
|
||||
|
||||
_outputs.ForEach(
|
||||
o =>
|
||||
{
|
||||
o.audioProcessor.Process();
|
||||
}
|
||||
);
|
||||
|
||||
_outputs.ForEach(
|
||||
o =>
|
||||
{
|
||||
_events.AddRange( o.GetEventsList() );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
using Godot;
|
||||
using System.Reflection;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Rokojori
|
||||
{
|
||||
public class AudioEventOutput<T> : AudioEvent<T>
|
||||
{
|
||||
public AudioEventOutput( AudioProcessor p, bool autoClear = true ):base( p, autoClear )
|
||||
{}
|
||||
|
||||
List<AudioEventInput<T>> _inputs = new List<AudioEventInput<T>>();
|
||||
|
||||
public List<AudioEventInput<T>> connected => _inputs;
|
||||
|
||||
public bool IsConnectedTo( AudioEventInput<T> input )
|
||||
{
|
||||
return _inputs.Contains( input );
|
||||
}
|
||||
|
||||
public void ConnectTo( AudioEventInput<T> input )
|
||||
{
|
||||
_inputs.Add( input );
|
||||
input._GetConnectedFrom( this );
|
||||
}
|
||||
|
||||
public void Disconnect( AudioEventInput<T> input )
|
||||
{
|
||||
_inputs.Remove( input );
|
||||
input._GetDisconnectedFrom( this );
|
||||
}
|
||||
|
||||
public void Push( T t )
|
||||
{
|
||||
_events.Add( t );
|
||||
}
|
||||
|
||||
public List<T> GetEventsList()
|
||||
{
|
||||
return _events;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,96 @@
|
|||
using Godot;
|
||||
using System.Reflection;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Rokojori
|
||||
{
|
||||
public class AudioGraph:AudioProcessor
|
||||
{
|
||||
public AudioGraph( float sampleRate = 44100, int bufferSize = 1024 ):base( null )
|
||||
{
|
||||
this.sampleRate = sampleRate;
|
||||
this.SetBufferSize( bufferSize );
|
||||
}
|
||||
|
||||
protected int _bufferSize = 1024;
|
||||
public override int bufferSize => audioGraph == null ? _bufferSize : audioGraph.bufferSize;
|
||||
|
||||
protected int _frameIndex = -1;
|
||||
public int frameIndex => _frameIndex;
|
||||
|
||||
public float sampleRate;
|
||||
public float bpm;
|
||||
|
||||
|
||||
public AudioProcessor output;
|
||||
|
||||
public override bool processed
|
||||
{
|
||||
get
|
||||
{
|
||||
if ( audioGraph == null )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return base.processed;
|
||||
}
|
||||
}
|
||||
|
||||
public override void SetProcessed()
|
||||
{
|
||||
if ( audioGraph == null )
|
||||
{
|
||||
_frameIndex ++;
|
||||
return;
|
||||
}
|
||||
|
||||
base.SetProcessed();
|
||||
|
||||
}
|
||||
|
||||
protected override void _Process()
|
||||
{
|
||||
_nodes.ForEach( n => n.Clear() );
|
||||
output.Process();
|
||||
}
|
||||
|
||||
List<AudioNode> _nodes = new List<AudioNode>();
|
||||
|
||||
public void _Add( AudioNode node )
|
||||
{
|
||||
_nodes.Add( node );
|
||||
}
|
||||
|
||||
public void Destroy( AudioNode node )
|
||||
{
|
||||
_nodes.Remove( node );
|
||||
node.Destroy();
|
||||
}
|
||||
|
||||
public void SetBufferSize( int bufferSize )
|
||||
{
|
||||
_bufferSize = bufferSize;
|
||||
_zeroBuffer = new float[ bufferSize ];
|
||||
|
||||
for ( int i = 0; i < bufferSize; i++ )
|
||||
{
|
||||
_zeroBuffer[ i ] = 0;
|
||||
}
|
||||
|
||||
_nodes.ForEach( n => n.UpdateBuffserSize() );
|
||||
}
|
||||
|
||||
float[] _zeroBuffer;
|
||||
|
||||
public float[] ClearBuffer( float[] buffer )
|
||||
{
|
||||
System.Array.Copy( _zeroBuffer, 0, buffer, 0, bufferSize );
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
using Godot;
|
||||
using System.Reflection;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Rokojori
|
||||
{
|
||||
public class AudioNode
|
||||
{
|
||||
protected AudioGraph _audioGraph;
|
||||
|
||||
public AudioGraph audioGraph => _audioGraph;
|
||||
|
||||
public virtual void Destroy()
|
||||
{
|
||||
_audioGraph = null;
|
||||
}
|
||||
|
||||
public virtual void Clear()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public virtual void UpdateBuffserSize()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public virtual int bufferSize => audioGraph.bufferSize;
|
||||
|
||||
public AudioNode( AudioGraph ag)
|
||||
{
|
||||
_audioGraph = ag;
|
||||
|
||||
if ( ag != null )
|
||||
{
|
||||
ag._Add( this );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
using Godot;
|
||||
using System.Reflection;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Rokojori
|
||||
{
|
||||
public class AudioProcessor:AudioNode
|
||||
{
|
||||
protected int _processedID = -1;
|
||||
protected List<AudioConnection> _connections = new List<AudioConnection>();
|
||||
protected List<AudioConnection> _inputConnections = new List<AudioConnection>();
|
||||
protected List<AudioConnection> _outputConnections = new List<AudioConnection>();
|
||||
|
||||
public AudioProcessor( AudioGraph ag ):base( ag )
|
||||
{}
|
||||
|
||||
public void _Add( AudioConnection ac )
|
||||
{
|
||||
_connections.Add( ac );
|
||||
|
||||
if ( ac.isInput )
|
||||
{
|
||||
_inputConnections.Add( ac );
|
||||
}
|
||||
else
|
||||
{
|
||||
_outputConnections.Add( ac );
|
||||
}
|
||||
}
|
||||
|
||||
public void Process()
|
||||
{
|
||||
if ( processed )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
SetProcessed();
|
||||
|
||||
for ( int i = 0; i < _inputConnections.Count; i++ )
|
||||
{
|
||||
_inputConnections[ i ].PollDependencies();
|
||||
}
|
||||
|
||||
_Process();
|
||||
|
||||
}
|
||||
|
||||
|
||||
protected virtual void _Process()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public virtual bool processed
|
||||
{
|
||||
get
|
||||
{
|
||||
return _processedID == _audioGraph.frameIndex;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void SetProcessed()
|
||||
{
|
||||
_processedID = _audioGraph.frameIndex;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
using Godot;
|
||||
using System.Reflection;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Rokojori
|
||||
{
|
||||
public class AudioStream : AudioConnection
|
||||
{
|
||||
protected float[] _buffer = new float[ 0 ];
|
||||
|
||||
public AudioStream( AudioProcessor p, bool isInput ):base( p, isInput )
|
||||
{
|
||||
_buffer = new float[ audioGraph.bufferSize ];
|
||||
}
|
||||
|
||||
public override void Clear()
|
||||
{
|
||||
_buffer = audioGraph.ClearBuffer( _buffer );
|
||||
}
|
||||
|
||||
public override void UpdateBuffserSize()
|
||||
{
|
||||
_buffer = new float[ audioGraph.bufferSize ];
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
using Godot;
|
||||
using System.Reflection;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System;
|
||||
|
||||
namespace Rokojori
|
||||
{
|
||||
public class AudioStreamInput : AudioStream
|
||||
{
|
||||
public AudioStreamInput( AudioProcessor p ):base( p, true )
|
||||
{}
|
||||
|
||||
protected List<AudioStreamOutput> _outputs = new List<AudioStreamOutput>();
|
||||
|
||||
public List<AudioStreamOutput> connected => _outputs;
|
||||
|
||||
public void _GetConnectedFrom( AudioStreamOutput output )
|
||||
{
|
||||
_outputs.Add( output );
|
||||
}
|
||||
|
||||
public void _GetDisconnectedFrom( AudioStreamOutput output )
|
||||
{
|
||||
_outputs.Remove( output );
|
||||
}
|
||||
|
||||
public override void PollDependencies()
|
||||
{
|
||||
_outputs.ForEach(
|
||||
o =>
|
||||
{
|
||||
o.audioProcessor.Process();
|
||||
}
|
||||
);
|
||||
|
||||
_outputs.ForEach(
|
||||
o =>
|
||||
{
|
||||
var buffer = o.GetOutputStreamBuffer();
|
||||
|
||||
for ( int i = 0; i < bufferSize; i++ )
|
||||
{
|
||||
_buffer[ i ] += buffer[ i ];
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
public float this[ int index ]
|
||||
{
|
||||
get { return _buffer[ index ]; }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
using Godot;
|
||||
using System.Reflection;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Rokojori
|
||||
{
|
||||
public class AudioStreamOutput : AudioStream
|
||||
{
|
||||
public AudioStreamOutput( AudioProcessor p, bool autoClear = true ):base( p, autoClear )
|
||||
{}
|
||||
|
||||
List<AudioStreamInput> _inputs = new List<AudioStreamInput>();
|
||||
|
||||
public List<AudioStreamInput> connected => _inputs;
|
||||
|
||||
public bool IsConnectedTo( AudioStreamInput input )
|
||||
{
|
||||
return _inputs.Contains( input );
|
||||
}
|
||||
|
||||
public void ConnectTo( AudioStreamInput input )
|
||||
{
|
||||
_inputs.Add( input );
|
||||
input._GetConnectedFrom( this );
|
||||
}
|
||||
|
||||
public void Disconnect( AudioStreamInput input )
|
||||
{
|
||||
_inputs.Remove( input );
|
||||
input._GetDisconnectedFrom( this );
|
||||
}
|
||||
|
||||
public float this[ int index ]
|
||||
{
|
||||
set{ _buffer[ index ] = value;}
|
||||
}
|
||||
|
||||
public float[] GetOutputStreamBuffer()
|
||||
{
|
||||
return _buffer;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,140 @@
|
|||
using Godot;
|
||||
using System.Reflection;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Rokojori
|
||||
{
|
||||
[GlobalClass]
|
||||
public partial class SineWaveTest: Node
|
||||
{
|
||||
[Export]
|
||||
public float frequency = 100;
|
||||
|
||||
[Export]
|
||||
public AudioStreamPlayer player;
|
||||
|
||||
AudioStreamGeneratorPlayback _playback;
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
CreateGraph();
|
||||
|
||||
if ( player.Stream is AudioStreamGenerator generator )
|
||||
{
|
||||
ag.sampleRate = generator.MixRate;
|
||||
|
||||
player.Play();
|
||||
_playback = (AudioStreamGeneratorPlayback) ( player.GetStreamPlayback() );
|
||||
|
||||
Fill();
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
WaveTableGenerator generator;
|
||||
AudioGraph ag;
|
||||
Constant constant;
|
||||
|
||||
public override void _Process( double delta )
|
||||
{
|
||||
Fill();
|
||||
}
|
||||
|
||||
void Fill()
|
||||
{
|
||||
if ( _playback == null )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
constant.SetConstant( frequency );
|
||||
|
||||
int framesAvailable = _playback.GetFramesAvailable();
|
||||
|
||||
if ( framesAvailable == 0 )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int blocks = 0;
|
||||
|
||||
while ( framesAvailable > frameBuffer.Count )
|
||||
{
|
||||
ProcessAudio();
|
||||
|
||||
blocks ++;
|
||||
}
|
||||
|
||||
|
||||
var frameBufferData = frameBuffer.ToArray();
|
||||
|
||||
var bufferSlice = new Vector2[ framesAvailable ];
|
||||
System.Array.Copy( frameBufferData, 0, bufferSlice, 0, framesAvailable );
|
||||
|
||||
_playback.PushBuffer( bufferSlice );
|
||||
|
||||
var rest = new Vector2[ frameBuffer.Count - framesAvailable ];
|
||||
System.Array.Copy( frameBufferData, framesAvailable, rest, 0, rest.Length );
|
||||
|
||||
frameBuffer = new List<Vector2>( rest );
|
||||
}
|
||||
|
||||
List<Vector2> frameBuffer = new List<Vector2>();
|
||||
|
||||
void ProcessAudio()
|
||||
{
|
||||
ag.Process();
|
||||
|
||||
var outputBuffer = generator.output.GetOutputStreamBuffer();
|
||||
var data = new Vector2[ ag.bufferSize ];
|
||||
|
||||
for ( int i = 0; i < ag.bufferSize ; i++ )
|
||||
{
|
||||
data[ i ] = outputBuffer[ i ] * Vector2.One;
|
||||
}
|
||||
|
||||
frameBuffer.AddRange( data );
|
||||
}
|
||||
|
||||
public void CreateGraph()
|
||||
{
|
||||
ag = new AudioGraph();
|
||||
|
||||
var sine = new float[ 2048 ];
|
||||
var square = new float[ 2048 ];
|
||||
|
||||
for ( int i = 0; i < sine.Length; i++ )
|
||||
{
|
||||
var p = i / 2048f;
|
||||
|
||||
var v = Mathf.Sin( p * Mathf.Pi * 2.0f );
|
||||
|
||||
sine[ i ] = v;
|
||||
square[ i ] = i < sine.Length / 2 ? 1 : -1;
|
||||
}
|
||||
|
||||
var sines = new Vector2[ 10 ];
|
||||
|
||||
sines[ 0 ] = Vector2.Zero;
|
||||
|
||||
for ( int i = 1; i < sines.Length; i++ )
|
||||
{
|
||||
sines[ i ] = new Vector2( Mathf.Pow( 0.5f, i - 1 ), 0 );
|
||||
}
|
||||
|
||||
var waveTable = WaveTable.FromSines( sines );
|
||||
waveTable.Normalize();
|
||||
generator = new WaveTableGenerator( ag, waveTable );
|
||||
|
||||
constant = new Constant( ag, frequency );
|
||||
|
||||
constant.output.ConnectTo( generator.frequency );
|
||||
|
||||
ag.output = generator;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,115 @@
|
|||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Godot;
|
||||
using System.Text;
|
||||
using System;
|
||||
|
||||
namespace Rokojori
|
||||
{
|
||||
public class MathAudio
|
||||
{
|
||||
public static int ConcertA_Pitch = 69;
|
||||
public static float ConcertA_Frequency = 440.0f;
|
||||
|
||||
public static float AmplitudeToDecibels( float amplitude )
|
||||
{
|
||||
return Mathf.Log(amplitude) * 20.0f / Mathf.Log(10.0f);
|
||||
}
|
||||
|
||||
public static float DecibelsToAmplitude( float decibels )
|
||||
{
|
||||
return Mathf.Pow(10.0f, decibels / 20.0f);
|
||||
}
|
||||
|
||||
public static float FrequencyToPitch( float frequency )
|
||||
{
|
||||
return ConcertA_Pitch + 12 * Mathf.Log(frequency / ConcertA_Frequency) / Mathf.Log(2.0f);
|
||||
}
|
||||
|
||||
public static float PitchToFrequency( float pitch )
|
||||
{
|
||||
return ConcertA_Frequency * Mathf.Pow( 2.0f, ( ( pitch - ConcertA_Pitch ) * (1.0f / 12.0f)));
|
||||
}
|
||||
|
||||
public static float SemitonesToFrequencyScaler( float semitones )
|
||||
{
|
||||
return Mathf.Pow( 2.0f, semitones / 12.0f);
|
||||
}
|
||||
|
||||
public static float OffsetFrequencyBySemitones( float frequency, float semitones )
|
||||
{
|
||||
return SemitonesToFrequencyScaler( semitones ) * frequency;
|
||||
}
|
||||
|
||||
public static float IncrementPhase( float phase, float increment )
|
||||
{
|
||||
return MathX.Repeat( phase + increment, 1 );
|
||||
}
|
||||
|
||||
public static float OneBeatToSeconds( float bpm )
|
||||
{
|
||||
return 60f / bpm;
|
||||
}
|
||||
|
||||
public static float OneSecondToBeats( float bpm )
|
||||
{
|
||||
return 1f / OneBeatToSeconds( bpm );
|
||||
}
|
||||
|
||||
public static float OneSampleToSeconds( float sampleRate )
|
||||
{
|
||||
return 1f / sampleRate;
|
||||
}
|
||||
|
||||
public static float OneSecondToSamples( float sampleRate )
|
||||
{
|
||||
return sampleRate;
|
||||
}
|
||||
|
||||
public static float OneBeatToSamples( float bpm, float sampleRate )
|
||||
{
|
||||
var seconds = OneBeatToSeconds( bpm );
|
||||
|
||||
return seconds * OneSecondToSamples( sampleRate );
|
||||
}
|
||||
|
||||
public static float OneSampleToBeats( float bpm, float sampleRate )
|
||||
{
|
||||
var seconds = OneBeatToSeconds( bpm );
|
||||
|
||||
return seconds * OneSecondToSamples( sampleRate );
|
||||
}
|
||||
|
||||
|
||||
public static float BeatsToSeconds( float beats, float bpm )
|
||||
{
|
||||
return beats * OneBeatToSeconds( bpm );
|
||||
}
|
||||
|
||||
public static float SecondsToBeats( float seconds, float bpm )
|
||||
{
|
||||
return seconds * OneSecondToBeats( bpm );
|
||||
}
|
||||
|
||||
public static float SamplesToSeconds( float samples, float sampleRate )
|
||||
{
|
||||
return samples * OneSampleToSeconds( sampleRate );
|
||||
}
|
||||
|
||||
public static float SecondsToSamples( float seconds, float sampleRate )
|
||||
{
|
||||
return seconds * OneSecondToSamples( sampleRate );
|
||||
}
|
||||
|
||||
public static float BeatsToSamples( float beats, float bpm, float sampleRate )
|
||||
{
|
||||
return beats * OneBeatToSamples( bpm, sampleRate );
|
||||
}
|
||||
|
||||
public static float SamplesToBeats( float samples, float bpm, float sampleRate )
|
||||
{
|
||||
return samples * OneSampleToBeats( bpm, sampleRate );
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -24,6 +24,16 @@ namespace Rokojori
|
|||
return new Color( rgb.X, rgb.Y, rgb.Z, a );
|
||||
}
|
||||
|
||||
public static Vector4 ToVector4( Color c )
|
||||
{
|
||||
return new Vector4( c.R, c.G, c.B, c.A );
|
||||
}
|
||||
|
||||
public static Color FromVector4( Vector4 c )
|
||||
{
|
||||
return new Color( c.X, c.Y, c.Z, c.W );
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
|
@ -54,13 +54,18 @@ namespace Rokojori
|
|||
}
|
||||
}
|
||||
|
||||
public bool hasFileExtension( string extension )
|
||||
public bool hasFileExtension( string extension, bool forceDot = true )
|
||||
{
|
||||
if ( fileExtension == null )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( forceDot && ! extension.StartsWith( "." ))
|
||||
{
|
||||
extension = "." + extension;
|
||||
}
|
||||
|
||||
return fileExtension.ToLower() == extension.ToLower();
|
||||
}
|
||||
|
||||
|
@ -93,6 +98,23 @@ namespace Rokojori
|
|||
rp.parent = parent;
|
||||
rp.path = path;
|
||||
|
||||
return rp;
|
||||
}
|
||||
|
||||
public FilePath WithExtension( string extension, bool forceDot = true )
|
||||
{
|
||||
var rp = new FilePath();
|
||||
|
||||
rp.type = type;
|
||||
rp.parent = parent;
|
||||
|
||||
if ( forceDot && ! extension.StartsWith( "." ) )
|
||||
{
|
||||
extension = "." + extension;
|
||||
}
|
||||
|
||||
rp.path = Path.ChangeExtension( path, extension );
|
||||
|
||||
return rp;
|
||||
}
|
||||
|
||||
|
@ -101,6 +123,14 @@ namespace Rokojori
|
|||
return FilePath.Create( path, FilePathType.Absolute );
|
||||
}
|
||||
|
||||
public FilePath CreateAbsoluteParent()
|
||||
{
|
||||
var path = this.fullPath;
|
||||
|
||||
return Absolute( RegexUtility.ParentPath( path ) );
|
||||
}
|
||||
|
||||
|
||||
public FilePath MakeRelative( string path )
|
||||
{
|
||||
return FilePath.Create( path, FilePathType.Relative, this );
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
using Godot;
|
||||
using System.Reflection;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Rokojori
|
||||
{
|
||||
public static class GDScriptNames
|
||||
{
|
||||
public static string ToCS( string gdScriptName )
|
||||
{
|
||||
return gdScriptName.ToPascalCase();
|
||||
}
|
||||
|
||||
public static string FromCS( string csName )
|
||||
{
|
||||
return csName.ToSnakeCase();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
using Godot;
|
||||
using System.Text;
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
|
||||
namespace Rokojori
|
||||
{
|
||||
public class CachedResource<T> where T:Resource
|
||||
{
|
||||
protected T _resource;
|
||||
protected string _path;
|
||||
public string path => _path;
|
||||
protected Func<Resource,T> _converter;
|
||||
|
||||
public CachedResource( string path, Func<Resource,T> converter = null )
|
||||
{
|
||||
_path = path;
|
||||
_converter = converter;
|
||||
}
|
||||
|
||||
public T Get()
|
||||
{
|
||||
if ( _resource == null )
|
||||
{
|
||||
|
||||
_resource = _converter == null ?
|
||||
ResourceLoader.Load<T>( _path ) :
|
||||
_converter( ResourceLoader.Load( _path ) );
|
||||
|
||||
RJLog.Log( _path, _resource );
|
||||
|
||||
|
||||
}
|
||||
|
||||
return _resource;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -66,6 +66,11 @@ namespace Rokojori
|
|||
|
||||
public override bool Equals( object? obj )
|
||||
{
|
||||
if ( obj == null )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var other = obj as SerializedGodotObject;
|
||||
|
||||
if ( members.Count != other.members.Count )
|
||||
|
@ -77,21 +82,27 @@ namespace Rokojori
|
|||
for ( int i = 0; i < members.Count; i++ )
|
||||
{
|
||||
var member = members[ i ];
|
||||
|
||||
if ( member == null )
|
||||
{
|
||||
return false ;
|
||||
}
|
||||
// RJLog.Log( member );
|
||||
var otherMember = other.members.Find( m => m.name == member.name );
|
||||
|
||||
if ( otherMember == null || ! member.Equals( otherMember ) )
|
||||
{
|
||||
// if ( otherMember == null )
|
||||
// {
|
||||
// RJLog.Log( member.name + " is not present" );
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// SerializedGodotObjectMember.debug = true;
|
||||
// var eq = member.Equals( otherMember );
|
||||
// SerializedGodotObjectMember.debug = false;
|
||||
// RJLog.Log( eq, member.name + " is different, should be:", member.value, "but is: " + otherMember.value );
|
||||
// }
|
||||
if ( otherMember == null )
|
||||
{
|
||||
// RJLog.Log( member.name + " is not present" );
|
||||
}
|
||||
else
|
||||
{
|
||||
// SerializedGodotObjectMember.debug = true;
|
||||
// var eq = member.Equals( otherMember );
|
||||
// // SerializedGodotObjectMember.debug = false;
|
||||
// // RJLog.Log( eq, member.name + " is different, should be:", member.value, "but is: " + otherMember.value );
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
@ -140,7 +151,7 @@ namespace Rokojori
|
|||
|
||||
var name = fields[ i ].Name;
|
||||
|
||||
if ( name.StartsWith( "X_" ) || name.StartsWith( "x_" ) )
|
||||
if ( name.StartsWith( "X_" ) || name.StartsWith( "x_" ) || name.StartsWith( "_" ) )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
using Godot;
|
||||
using System.Text;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Rokojori
|
||||
{
|
||||
public class TransformChange
|
||||
{
|
||||
Vector3 position = Vector3.Zero;
|
||||
Quaternion rotation = Quaternion.Identity;
|
||||
Vector3 scale = Vector3.One;
|
||||
|
||||
|
||||
public bool Check( Node3D source )
|
||||
{
|
||||
var changed = false;
|
||||
|
||||
if ( position != source.GlobalPosition )
|
||||
{
|
||||
changed = true;
|
||||
position = source.GlobalPosition;
|
||||
|
||||
}
|
||||
|
||||
var sourceRotation = source.GetGlobalQuaternion();
|
||||
|
||||
if ( rotation != sourceRotation )
|
||||
{
|
||||
changed = true;
|
||||
rotation = sourceRotation;
|
||||
}
|
||||
|
||||
if ( scale != source.Scale )
|
||||
{
|
||||
changed = true;
|
||||
scale = source.Scale;
|
||||
}
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -6,7 +6,7 @@ namespace Rokojori
|
|||
{
|
||||
public class HierarchyName
|
||||
{
|
||||
public static string Of( Node node, string seperator = "/" )
|
||||
public static string Of( Node node, string seperator = " ▸ " )
|
||||
{
|
||||
if ( node == null )
|
||||
{
|
||||
|
@ -19,8 +19,17 @@ namespace Rokojori
|
|||
|
||||
while ( it != null )
|
||||
{
|
||||
list.Add( it.Name );
|
||||
it = it.GetParent();
|
||||
var n = it.Name;
|
||||
|
||||
if ( RegexUtility.Matching( n, @"@\d+$" ) )
|
||||
{
|
||||
it = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
list.Add( it.Name );
|
||||
it = it.GetParent();
|
||||
}
|
||||
}
|
||||
|
||||
list.Reverse();
|
||||
|
|
|
@ -2,6 +2,8 @@ using Godot;
|
|||
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
|
||||
namespace Rokojori
|
||||
{
|
||||
|
@ -96,6 +98,7 @@ namespace Rokojori
|
|||
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 =>
|
||||
{
|
||||
|
@ -166,6 +169,12 @@ namespace Rokojori
|
|||
return t;
|
||||
}
|
||||
|
||||
public static async Task RequestNextFrame( this Node node )
|
||||
{
|
||||
await node.ToSignal( RenderingServer.Singleton, RenderingServerInstance.SignalName.FramePostDraw );
|
||||
return;
|
||||
}
|
||||
|
||||
public static T CreateChild<T>( this Node parent, string name = null ) where T:Node,new()
|
||||
{
|
||||
return CreateChildIn<T>( parent, name );
|
||||
|
@ -181,6 +190,16 @@ namespace Rokojori
|
|||
return copy;
|
||||
}
|
||||
|
||||
public static void LogInfo( this Node node, params object[] messages )
|
||||
{
|
||||
RJLog.Log( node, messages );
|
||||
}
|
||||
|
||||
public static void LogError( this Node node, params object[] messages )
|
||||
{
|
||||
RJLog.Error( node, messages );
|
||||
}
|
||||
|
||||
public static Node DeepCopyTo( this Node node, Node parent )
|
||||
{
|
||||
return CopyNodeHierarchy( node, parent );
|
||||
|
@ -199,8 +218,33 @@ namespace Rokojori
|
|||
return copy;
|
||||
}
|
||||
|
||||
public static T EnsureValid<T>( T node ) where T:Node
|
||||
{
|
||||
if ( GodotObject.IsInstanceValid( node ) )
|
||||
{
|
||||
if ( node.IsQueuedForDeletion() )
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static void RemoveAndDelete( Node node )
|
||||
{
|
||||
if ( ! GodotObject.IsInstanceValid( node ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if ( node.IsQueuedForDeletion() )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var parent = node.GetParent();
|
||||
|
||||
if ( parent != null )
|
||||
|
@ -208,6 +252,8 @@ namespace Rokojori
|
|||
parent.RemoveChild( node );
|
||||
}
|
||||
|
||||
|
||||
|
||||
node.QueueFree();
|
||||
}
|
||||
|
||||
|
@ -242,6 +288,11 @@ namespace Rokojori
|
|||
}
|
||||
|
||||
|
||||
public static void DestroyChildren( this Node parent, bool includeInternal = false )
|
||||
{
|
||||
RemoveAndDeleteChildren( parent, includeInternal );
|
||||
}
|
||||
|
||||
public static void RemoveAndDeleteChildren( Node parent, bool includeInternal = false )
|
||||
{
|
||||
if ( parent == null )
|
||||
|
@ -250,7 +301,7 @@ namespace Rokojori
|
|||
}
|
||||
|
||||
var numChildren = parent.GetChildCount( includeInternal );
|
||||
|
||||
|
||||
for ( int i = numChildren - 1; i >= 0; i-- )
|
||||
{
|
||||
var node = parent.GetChild( i, includeInternal );
|
||||
|
@ -312,6 +363,65 @@ namespace Rokojori
|
|||
return list;
|
||||
}
|
||||
|
||||
public static void OnAllDirectChildren<T>( this Node parent, System.Action<T> action ) where T:Node
|
||||
{
|
||||
ForEachDirectChild<T>( parent, action );
|
||||
}
|
||||
|
||||
public static int NumDirectChildrenOf<T>( this Node parent ) where T:Node
|
||||
{
|
||||
if ( parent == null )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
var numChildren = parent.GetChildCount();
|
||||
var typeIndex = 0;
|
||||
|
||||
for ( int i = 0; i < numChildren; i++ )
|
||||
{
|
||||
var node = parent.GetChild( i );
|
||||
|
||||
if ( node is T )
|
||||
{
|
||||
typeIndex ++;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return typeIndex;
|
||||
}
|
||||
|
||||
public static T GetNthDirectChild<T>( this Node parent, int index ) where T:Node
|
||||
{
|
||||
if ( parent == null )
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var numChildren = parent.GetChildCount();
|
||||
var typeIndex = 0;
|
||||
|
||||
for ( int i = 0; i < numChildren; i++ )
|
||||
{
|
||||
var node = parent.GetChild( i );
|
||||
|
||||
if ( node is T t )
|
||||
{
|
||||
if ( typeIndex == index )
|
||||
{
|
||||
return t;
|
||||
}
|
||||
|
||||
typeIndex ++;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
public static void ForEachDirectChild<T>( Node parent, System.Action<T> action ) where T:Node
|
||||
{
|
||||
if ( parent == null || action == null )
|
||||
|
|
|
@ -50,11 +50,13 @@ namespace Rokojori
|
|||
if ( CharacterUpdateMode.Process == characterUpdateMode )
|
||||
{
|
||||
this.delta = (float)delta;
|
||||
Nodes.ForEach<CharacterControllerAction>( actionsContainer, a => Actions.Trigger( a ) );
|
||||
Nodes.ForEachDirectChild<CharacterControllerAction>( actionsContainer, a => Actions.Trigger( a ) );
|
||||
}
|
||||
|
||||
positionSmoother.CopyPosition( graphics, body, rotationSmoothingDuration, delta );
|
||||
rotationSmoother.CopyRotation( graphics, body, positionSmoothingDuration, delta );
|
||||
// positionSmoother.CopyPosition( graphics, body, rotationSmoothingDuration, delta );
|
||||
// rotationSmoother.CopyRotation( graphics, body, positionSmoothingDuration, delta );
|
||||
|
||||
Pose.CopyTo( body, graphics );
|
||||
}
|
||||
|
||||
public override void _PhysicsProcess( double delta )
|
||||
|
|
|
@ -13,15 +13,25 @@ namespace Rokojori
|
|||
|
||||
public CharacterBody3D body => controller.body;
|
||||
|
||||
public void SetVelocity( Vector3 velocity, bool replace )
|
||||
public void AddVelocity( Vector3 velocity )
|
||||
{
|
||||
body.Velocity += velocity * controller.delta;
|
||||
}
|
||||
|
||||
public void SetVelocity( Vector3 velocity )
|
||||
{
|
||||
body.Velocity = velocity * controller.delta;
|
||||
}
|
||||
|
||||
public void Velocity( Vector3 velocity, bool replace )
|
||||
{
|
||||
if ( replace )
|
||||
{
|
||||
body.Velocity = velocity * controller.delta;
|
||||
SetVelocity( velocity );
|
||||
}
|
||||
else
|
||||
{
|
||||
body.Velocity += velocity * controller.delta;
|
||||
AddVelocity( velocity );
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -14,6 +14,36 @@ namespace Rokojori
|
|||
[Export]
|
||||
public bool overwriteVelocity = true;
|
||||
|
||||
[ExportGroup( "Actions" )]
|
||||
[Export]
|
||||
public RJAction onMoving;
|
||||
[Export]
|
||||
public RJAction onIdle;
|
||||
|
||||
[Export]
|
||||
public RJAction onForward;
|
||||
[Export]
|
||||
public RJAction onBackwards;
|
||||
[Export]
|
||||
public RJAction onStrafeLeft;
|
||||
[Export]
|
||||
public RJAction onStrafeRight;
|
||||
|
||||
|
||||
[ExportGroup( "Direction Source" )]
|
||||
[Export]
|
||||
public Node3D directionSource;
|
||||
|
||||
public enum DirectionProcessing
|
||||
{
|
||||
None,
|
||||
Zero_Y_And_Normalize,
|
||||
Project_On_TransformPlane
|
||||
}
|
||||
|
||||
[Export]
|
||||
public DirectionProcessing directionProcessing;
|
||||
|
||||
[ExportGroup( "Moving" )]
|
||||
|
||||
[Export]
|
||||
|
@ -35,23 +65,83 @@ namespace Rokojori
|
|||
[Export]
|
||||
public RJSensor strafeRight;
|
||||
|
||||
[Export]
|
||||
public bool useBodyDirection = true;
|
||||
|
||||
[ExportGroup( "Rotation" )]
|
||||
|
||||
[Export]
|
||||
public bool adjustRotation = true;
|
||||
|
||||
[Export]
|
||||
public Curve forwardToRotation = MathX.Curve( 0, 1 );
|
||||
|
||||
[Export( PropertyHint.Range, "0,1")]
|
||||
public float rotationSmoothingCoefficient = 0.1f;
|
||||
Smoother rotationSmoother = new Smoother();
|
||||
|
||||
|
||||
public override void _OnTrigger()
|
||||
{
|
||||
if ( ! body.IsOnFloor() )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var movement = Vector3.Zero;
|
||||
var body = controller.body;
|
||||
|
||||
movement += body.GlobalForward() * Sensors.GetValue( forward );
|
||||
movement -= body.GlobalForward() * Sensors.GetValue( backwards );
|
||||
var fw = Sensors.GetValue( forward );
|
||||
var bw = Sensors.GetValue( backwards );
|
||||
|
||||
movement += body.GlobalRight() * Sensors.GetValue( strafeRight );
|
||||
movement -= body.GlobalRight() * Sensors.GetValue( strafeLeft );
|
||||
var fbAxis = Sensors.PolarAxis( backwards, forward );
|
||||
|
||||
var dir = directionSource != null ? directionSource : body;
|
||||
var forwardDirection = dir.GlobalForward();
|
||||
var rightDirection = useBodyDirection ? body.GlobalRight() : dir.GlobalRight();
|
||||
|
||||
if ( DirectionProcessing.Zero_Y_And_Normalize == directionProcessing )
|
||||
{
|
||||
forwardDirection.Y = 0;
|
||||
rightDirection.Y = 0;
|
||||
}
|
||||
else if ( DirectionProcessing.Project_On_TransformPlane == directionProcessing )
|
||||
{
|
||||
var bodyPlane = Plane3.CreateFromNode3D( body );
|
||||
forwardDirection = bodyPlane.ConstrainToPlane( forwardDirection + body.GlobalPosition ) - body.GlobalPosition;
|
||||
rightDirection = bodyPlane.ConstrainToPlane( rightDirection + body.GlobalPosition ) - body.GlobalPosition;
|
||||
}
|
||||
|
||||
forwardDirection = forwardDirection.Normalized();
|
||||
rightDirection = rightDirection.Normalized();
|
||||
|
||||
movement += forwardDirection * Sensors.PolarAxis( backwards, forward ) * moveSpeed;
|
||||
|
||||
movement += rightDirection * Sensors.PolarAxis( strafeLeft, strafeRight ) * strafeSpeed ;
|
||||
|
||||
|
||||
|
||||
if ( body.IsOnFloor() )
|
||||
{
|
||||
movement *= onFloorMultiply;
|
||||
}
|
||||
|
||||
SetVelocity( movement, overwriteVelocity );
|
||||
if ( adjustRotation )
|
||||
{
|
||||
var currentRotation = body.GetGlobalQuaternion();
|
||||
var nextRotation = Math3D.LookRotation( forwardDirection );
|
||||
|
||||
var sm = 1f - rotationSmoothingCoefficient;
|
||||
sm = sm * sm;
|
||||
|
||||
var speed = MathX.Clamp01( movement.Length() / moveSpeed ) * Mathf.Max( fw, bw );
|
||||
|
||||
sm *= forwardToRotation.Sample( speed );
|
||||
|
||||
var rotation = rotationSmoother.SmoothWithCoefficient( currentRotation, nextRotation, sm, controller.delta );
|
||||
body.SetGlobalQuaternion( rotation );
|
||||
}
|
||||
|
||||
Velocity( movement, overwriteVelocity );
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
using Godot;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Godot.Collections;
|
||||
|
||||
namespace Rokojori
|
||||
{
|
||||
[GlobalClass]
|
||||
public partial class Gravity:CharacterControllerAction
|
||||
{
|
||||
[Export]
|
||||
public float strength = 10;
|
||||
|
||||
public override void _OnTrigger()
|
||||
{
|
||||
if ( body.IsOnFloor() )
|
||||
{
|
||||
return;
|
||||
}
|
||||
// RJLog.Log( controller.body.Velocity );
|
||||
AddVelocity( Vector3.Down * strength );
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
using Godot;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Godot.Collections;
|
||||
|
||||
namespace Rokojori
|
||||
{
|
||||
[GlobalClass]
|
||||
public partial class GroundReset:CharacterControllerAction
|
||||
{
|
||||
public override void _OnTrigger()
|
||||
{
|
||||
if ( ! body.IsOnFloor() )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
SetVelocity( Vector3.Zero );
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
using Godot;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Godot.Collections;
|
||||
|
||||
namespace Rokojori
|
||||
{
|
||||
[GlobalClass]
|
||||
public partial class Jump:CharacterControllerAction
|
||||
{
|
||||
[Export]
|
||||
public RJSensor button;
|
||||
|
||||
[Export]
|
||||
public float jumpStrength;
|
||||
|
||||
[Export]
|
||||
public float maxJumpDuration;
|
||||
|
||||
[Export]
|
||||
public Curve jumpCurveStrength = MathX.Curve( 1, 0 );
|
||||
|
||||
[Export]
|
||||
public float jumpedDuration;
|
||||
|
||||
[Export]
|
||||
public bool canJump = false;
|
||||
|
||||
[Export]
|
||||
public bool jumping = false;
|
||||
|
||||
|
||||
|
||||
public override void _OnTrigger()
|
||||
{
|
||||
if ( body.IsOnFloor() )
|
||||
{
|
||||
jumping = false;
|
||||
}
|
||||
|
||||
if ( ! ( jumping || body.IsOnFloor() ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ! button.IsActive() )
|
||||
{
|
||||
jumping = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ! jumping )
|
||||
{
|
||||
jumping = true;
|
||||
jumpedDuration = 0;
|
||||
}
|
||||
|
||||
jumpedDuration += controller.delta;
|
||||
|
||||
canJump = jumpedDuration < maxJumpDuration;
|
||||
|
||||
|
||||
if ( jumpedDuration < maxJumpDuration )
|
||||
{
|
||||
var jumpStrengthMultiply = jumpCurveStrength.Sample( jumpedDuration / maxJumpDuration );
|
||||
AddVelocity( Vector3.Up * jumpStrength );
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -10,6 +10,7 @@ namespace Rokojori
|
|||
{
|
||||
public override void _OnTrigger()
|
||||
{
|
||||
// RJLog.Log( controller.body.Velocity );
|
||||
controller.body.MoveAndSlide();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
using Godot;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Godot.Collections;
|
||||
|
||||
namespace Rokojori
|
||||
{
|
||||
[Tool]
|
||||
[GlobalClass]
|
||||
public partial class LODArrangement:Resource
|
||||
{
|
||||
[Export]
|
||||
public LODLevel[] levels;
|
||||
|
||||
public LODLevel GetLevel( LODNode node )
|
||||
{
|
||||
for ( int i = 0; i < levels.Length; i++ )
|
||||
{
|
||||
if ( ! levels[ i ].Evaluate( node ) )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
return levels[ i ];
|
||||
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
using Godot;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Godot.Collections;
|
||||
|
||||
namespace Rokojori
|
||||
{
|
||||
public partial class LODCameraDistanceRule:LODLevelVisibilityRule
|
||||
{
|
||||
[Export]
|
||||
public float minDistance;
|
||||
|
||||
[Export]
|
||||
public float maxDistance;
|
||||
|
||||
public override bool Evaluate( LODNode lodNode )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
using Godot;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Godot.Collections;
|
||||
|
||||
namespace Rokojori
|
||||
{
|
||||
public partial class LODCameraPitchRule:LODLevelVisibilityRule
|
||||
{
|
||||
[Export]
|
||||
public float pitch;
|
||||
|
||||
[Export]
|
||||
public float pitchRange;
|
||||
|
||||
public override bool Evaluate( LODNode lodNode )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
using Godot;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Godot.Collections;
|
||||
|
||||
namespace Rokojori
|
||||
{
|
||||
public partial class LODCameraYawRule:LODLevelVisibilityRule
|
||||
{
|
||||
[Export]
|
||||
public float yaw;
|
||||
|
||||
[Export]
|
||||
public float yawRange;
|
||||
|
||||
public override bool Evaluate( LODNode lodNode )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
using Godot;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Godot.Collections;
|
||||
|
||||
namespace Rokojori
|
||||
{
|
||||
[Tool]
|
||||
[GlobalClass]
|
||||
public partial class LODLevel:Resource
|
||||
{
|
||||
[Export]
|
||||
public LODLevelVisibilityRule[] visibilityRules;
|
||||
|
||||
[Export]
|
||||
public Mesh mesh;
|
||||
|
||||
[Export]
|
||||
public Material material;
|
||||
|
||||
public bool Evaluate( LODNode lodNode )
|
||||
{
|
||||
for ( int i = 0; i < visibilityRules.Length; i++ )
|
||||
{
|
||||
if ( ! visibilityRules[ i ].Evaluate( lodNode ) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
using Godot;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Godot.Collections;
|
||||
|
||||
namespace Rokojori
|
||||
{
|
||||
[Tool]
|
||||
[GlobalClass]
|
||||
public partial class LODLevelVisibilityRule:Resource
|
||||
{
|
||||
public virtual bool Evaluate( LODNode lodNode )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,131 @@
|
|||
using Godot;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Godot.Collections;
|
||||
|
||||
namespace Rokojori
|
||||
{
|
||||
[Tool]
|
||||
[GlobalClass]
|
||||
public partial class LODMultiMesh:MultiMeshInstance3D
|
||||
{
|
||||
[Export]
|
||||
public float minDistance = 0;
|
||||
|
||||
[Export]
|
||||
public float cullDistance = 100;
|
||||
|
||||
[Export]
|
||||
public Curve fillCurve = MathX.Curve( 0, 1 );
|
||||
|
||||
float _distance = -10;
|
||||
|
||||
[Export]
|
||||
public float distanceTreshold = 0.01f;
|
||||
|
||||
[Export( PropertyHint.Range, "0,1" )]
|
||||
public float scaleRange = 0.5f;
|
||||
|
||||
[Export]
|
||||
public float downMax = 0.1f;
|
||||
|
||||
[Export]
|
||||
Vector3[] cachedOrigins = null;
|
||||
|
||||
[Export]
|
||||
Vector3[] cachedBasis = null;
|
||||
|
||||
public void CacheTransforms()
|
||||
{
|
||||
var mm = Multimesh;
|
||||
cachedOrigins = new Vector3[ mm.InstanceCount ];
|
||||
cachedBasis = new Vector3[ mm.InstanceCount * 3 ];
|
||||
|
||||
|
||||
for ( int i = 0; i < mm.InstanceCount; i++ )
|
||||
{
|
||||
var trsf = mm.GetInstanceTransform( i );
|
||||
|
||||
cachedBasis[ i * 3 + 0 ] = trsf.Basis.Column0;
|
||||
cachedBasis[ i * 3 + 1 ] = trsf.Basis.Column1;
|
||||
cachedBasis[ i * 3 + 2 ] = trsf.Basis.Column2;
|
||||
|
||||
cachedOrigins[ i ] =( trsf.Origin );
|
||||
}
|
||||
}
|
||||
|
||||
public override void _Process(double delta)
|
||||
{
|
||||
var camera = GetViewport().GetCamera3D();
|
||||
|
||||
#if TOOLS
|
||||
|
||||
if ( Engine.IsEditorHint() )
|
||||
{
|
||||
camera = EditorInterface.Singleton.GetEditorViewport3D().GetCamera3D();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
var distance = ( camera.GlobalPosition - GlobalPosition ).Length();
|
||||
|
||||
if ( Mathf.Abs( _distance - distance ) < distanceTreshold )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_distance = distance;
|
||||
|
||||
var mapped = MathX.RemapClamped( distance, minDistance, cullDistance, 1, 0 );
|
||||
|
||||
var mm = Multimesh;
|
||||
|
||||
// this.LogInfo( "MM", (int) distance, ( (int) ( 100 * mapped ) ) + "%", (int)( fillCurve.Sample( mapped ) * mm.InstanceCount) + " instances" );
|
||||
|
||||
|
||||
if ( mm == null )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if ( distance >= cullDistance )
|
||||
{
|
||||
mm.VisibleInstanceCount = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
var count = Mathf.RoundToInt( fillCurve.Sample( mapped ) * mm.InstanceCount );
|
||||
|
||||
count = Mathf.Clamp( count, 0, mm.InstanceCount );
|
||||
|
||||
var fadeStart = count * ( 1 - scaleRange );
|
||||
|
||||
for ( int i = 0; i < count; i++ )
|
||||
{
|
||||
var cbasis = new Basis( cachedBasis[ i * 3 + 0 ], cachedBasis[ i * 3 + 1 ], cachedBasis[ i * 3 + 2 ] );
|
||||
|
||||
var trsf = new Transform3D( cbasis, cachedOrigins[ i ] );
|
||||
|
||||
if ( i >= fadeStart )
|
||||
{
|
||||
var amount = MathX.RemapClamped( i, fadeStart, count, 0, 1 );
|
||||
var basis = trsf.Basis.Scaled( new Vector3( 1, Mathf.Max( ( 1f - amount ), 0.0001f ), 1 ));
|
||||
|
||||
var offsetTrsf = new Transform3D( basis, trsf.Origin + downMax * Vector3.Down);
|
||||
|
||||
mm.SetInstanceTransform( i, offsetTrsf );
|
||||
}
|
||||
else
|
||||
{
|
||||
mm.SetInstanceTransform( i, trsf );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
mm.VisibleInstanceCount = count;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,138 @@
|
|||
using Godot;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Godot.Collections;
|
||||
|
||||
namespace Rokojori
|
||||
{
|
||||
[Tool]
|
||||
[GlobalClass]
|
||||
public partial class LODNode:MeshInstance3D
|
||||
{
|
||||
[Export]
|
||||
public LODArrangement arrangement;
|
||||
|
||||
LODLevel _currentLevel;
|
||||
|
||||
public override void _Process( double delta )
|
||||
{
|
||||
if ( arrangement == null )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_hasCameraDirection = false;
|
||||
_hasDistance = false;
|
||||
_hasPitch = false;
|
||||
_hasYaw = false;
|
||||
|
||||
var level = arrangement.GetLevel( this );
|
||||
|
||||
if ( level == _currentLevel )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Mesh = level.mesh;
|
||||
MaterialOverride = level.material;
|
||||
}
|
||||
|
||||
|
||||
public Camera3D camera
|
||||
{
|
||||
get
|
||||
{
|
||||
var _camera = GetViewport().GetCamera3D();
|
||||
|
||||
#if TOOLS
|
||||
|
||||
if ( Engine.IsEditorHint() )
|
||||
{
|
||||
_camera = EditorInterface.Singleton.GetEditorViewport3D().GetCamera3D();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
return _camera;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool _hasCameraDirection = false;
|
||||
Vector3 _cameraDirection;
|
||||
|
||||
public Vector3 cameraDirection
|
||||
{
|
||||
get
|
||||
{
|
||||
if ( _hasCameraDirection )
|
||||
{
|
||||
return _cameraDirection;
|
||||
}
|
||||
|
||||
_cameraDirection = ( GlobalPosition - camera.GlobalPosition );
|
||||
|
||||
return _cameraDirection;
|
||||
}
|
||||
}
|
||||
|
||||
bool _hasDistance = false;
|
||||
float _distance = 0;
|
||||
|
||||
public float distance
|
||||
{
|
||||
get
|
||||
{
|
||||
if ( _hasDistance )
|
||||
{
|
||||
return _distance;
|
||||
}
|
||||
|
||||
_distance = cameraDirection.Length();
|
||||
|
||||
return _distance;
|
||||
}
|
||||
}
|
||||
|
||||
bool _hasPitch = false;
|
||||
float _pitch = 0;
|
||||
|
||||
public float pitch
|
||||
{
|
||||
get
|
||||
{
|
||||
if ( _hasPitch )
|
||||
{
|
||||
return _pitch;
|
||||
}
|
||||
|
||||
_pitch = Math3D.GlobalPitch( cameraDirection );
|
||||
|
||||
return _pitch;
|
||||
}
|
||||
}
|
||||
|
||||
bool _hasYaw = false;
|
||||
float _yaw = 0;
|
||||
|
||||
public float yaw
|
||||
{
|
||||
get
|
||||
{
|
||||
if ( _hasYaw )
|
||||
{
|
||||
return _yaw;
|
||||
}
|
||||
|
||||
_yaw = Math3D.GlobalYaw( cameraDirection );
|
||||
|
||||
return _yaw;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
|
@ -9,30 +9,71 @@ namespace Rokojori
|
|||
[GlobalClass]
|
||||
public partial class LODParent:Node3D
|
||||
{
|
||||
[Export]
|
||||
public float cullDistance = 4000;
|
||||
public enum DistanceMode
|
||||
{
|
||||
Via_Array,
|
||||
Via_Curve
|
||||
}
|
||||
|
||||
[Export]
|
||||
public Curve distribution = MathX.Curve( 0, 1 );
|
||||
public DistanceMode mode;
|
||||
|
||||
[ExportGroup("Array Mode")]
|
||||
[Export]
|
||||
public bool arrayCulling = true;
|
||||
[Export]
|
||||
public float[] arrayTresholds;
|
||||
|
||||
[ExportGroup("Curve Mode")]
|
||||
[Export]
|
||||
public bool curveCulling = true;
|
||||
|
||||
[Export]
|
||||
public float curveCullDistance = 4000;
|
||||
|
||||
[Export]
|
||||
public Curve curveDistribution = MathX.Curve( 0, 1 );
|
||||
|
||||
[ExportGroup("LODs")]
|
||||
[Export]
|
||||
public Node3D[] lods;
|
||||
|
||||
[Export]
|
||||
public float updateDistance = 10;
|
||||
|
||||
[ExportGroup("Testing")]
|
||||
[Export]
|
||||
public float X_processingCameraDistance = 0;
|
||||
|
||||
[Export]
|
||||
public float X_realCameraDistance = 0;
|
||||
|
||||
[Export]
|
||||
public float X_selectedIndex = 0;
|
||||
|
||||
[Export]
|
||||
public bool testMode = false;
|
||||
|
||||
[Export]
|
||||
public float testDistance = 0;
|
||||
|
||||
[Export]
|
||||
public bool processOnlyWhenVisible = true;
|
||||
|
||||
float lastSquaredDistance = -1;
|
||||
int lastSelectedIndex = -1;
|
||||
|
||||
[Export]
|
||||
public int changeBlock = 0;
|
||||
|
||||
[Export]
|
||||
public int blocker = 0;
|
||||
|
||||
public override void _Process( double delta )
|
||||
{
|
||||
if ( processOnlyWhenVisible && ! Visible )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if ( blocker > 0 )
|
||||
{
|
||||
blocker --;
|
||||
|
@ -56,41 +97,78 @@ namespace Rokojori
|
|||
#endif
|
||||
|
||||
var squaredDistance = GlobalPosition.DistanceSquaredTo( camera.GlobalPosition );
|
||||
X_realCameraDistance = Mathf.Sqrt( squaredDistance );
|
||||
|
||||
|
||||
if ( testMode )
|
||||
{
|
||||
squaredDistance = testDistance * testDistance;
|
||||
}
|
||||
|
||||
if ( Mathf.Abs( lastSquaredDistance - squaredDistance ) < updateDistance * updateDistance )
|
||||
{
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
lastSquaredDistance = squaredDistance;
|
||||
|
||||
var realDistance = Mathf.Sqrt( lastSquaredDistance );
|
||||
|
||||
var normalizedDistance = MathX.NormalizeClamped( realDistance, 0, cullDistance );
|
||||
X_processingCameraDistance = realDistance;
|
||||
|
||||
var active = distribution.Sample( normalizedDistance ) * lods.Length;
|
||||
var selectedIndex = Mathf.Min( lods.Length - 1, Mathf.RoundToInt( active ) );
|
||||
var selectedIndex = GetLODIndex( realDistance );
|
||||
|
||||
X_selectedIndex = selectedIndex;
|
||||
|
||||
|
||||
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;
|
||||
|
||||
if ( selectedIndex >= lods.Length )
|
||||
{
|
||||
Arrays.ForEach( lods, l => NodeState.Set( l, false ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
var selectedLOD = lods[ selectedIndex ];
|
||||
Arrays.ForEach( lods, l => NodeState.Set( l, selectedLOD == l ) );
|
||||
}
|
||||
}
|
||||
|
||||
int GetLODIndex( float realDistance )
|
||||
{
|
||||
if ( DistanceMode.Via_Array == mode )
|
||||
{
|
||||
for ( int i = 0; i < arrayTresholds.Length; i++ )
|
||||
{
|
||||
if ( realDistance <= arrayTresholds[ i ] )
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return arrayCulling ? lods.Length : ( lods.Length - 1 );
|
||||
}
|
||||
else if ( DistanceMode.Via_Curve == mode )
|
||||
{
|
||||
if ( curveCulling && realDistance >= curveCullDistance )
|
||||
{
|
||||
return lods.Length;
|
||||
}
|
||||
|
||||
var normalizedDistance = MathX.NormalizeClamped( realDistance, 0, curveCullDistance );
|
||||
var active = curveDistribution.Sample( normalizedDistance ) * lods.Length;
|
||||
return Mathf.Min( lods.Length - 1, Mathf.RoundToInt( active ) );
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,121 @@
|
|||
using Godot;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Godot.Collections;
|
||||
|
||||
namespace Rokojori
|
||||
{
|
||||
[Tool]
|
||||
[GlobalClass]
|
||||
public partial class MultiMeshGenerator:Node
|
||||
{
|
||||
[Export]
|
||||
public Node3D source;
|
||||
|
||||
[Export]
|
||||
public Node3D output;
|
||||
|
||||
[Export]
|
||||
public float blockSize = 50;
|
||||
|
||||
[Export]
|
||||
public bool clearOutput = true;
|
||||
|
||||
[Export]
|
||||
public bool update = false;
|
||||
|
||||
public override void _Process( double delta )
|
||||
{
|
||||
if ( ! update )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
update = false;
|
||||
|
||||
Generate();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public void Generate()
|
||||
{
|
||||
if ( clearOutput )
|
||||
{
|
||||
Nodes.RemoveAndDeleteChildren( output );
|
||||
}
|
||||
|
||||
var grid = Grid2D<MeshInstance3D>.XZfromNode3D<MeshInstance3D>( blockSize, blockSize );
|
||||
|
||||
this.LogInfo( "Grid Created", grid );
|
||||
|
||||
var instances = Nodes.AllIn<MeshInstance3D>( source );
|
||||
|
||||
this.LogInfo( "Grabbed instances", instances.Count );
|
||||
|
||||
grid.AddAll( instances );
|
||||
|
||||
this.LogInfo( "Added instance to grid" );
|
||||
|
||||
|
||||
grid.ForEachCell(
|
||||
( cellID, list ) =>
|
||||
{
|
||||
var meshList = new MapList<Mesh,MeshInstance3D>();
|
||||
|
||||
list.ForEach(
|
||||
( n )=>
|
||||
{
|
||||
var m = n.Mesh;
|
||||
meshList.Add( m, n );
|
||||
}
|
||||
);
|
||||
|
||||
var cIDCount = 0;
|
||||
meshList.ForEach(
|
||||
( mesh, list ) =>
|
||||
{
|
||||
var cellIDInfo = "[" + cellID.X + "," + cellID.Y+"]";
|
||||
var mmi = output.CreateChild<LODMultiMesh>( "Cell" + cellIDInfo + "(" + cIDCount + ")" );
|
||||
|
||||
var mm = new MultiMesh();
|
||||
mm.Mesh = mesh;
|
||||
|
||||
mm.TransformFormat = MultiMesh.TransformFormatEnum.Transform3D;
|
||||
mm.InstanceCount = list.Count;
|
||||
mm.VisibleInstanceCount = list.Count;
|
||||
|
||||
var random = new LCG();
|
||||
random.SetSeed( 12134 + (int)( Noise.Perlin( cellID ) * 10083 ) );
|
||||
|
||||
var randomList = RandomList<MeshInstance3D>.Randomize( list, random );
|
||||
|
||||
var center = Math3D.Center( randomList );
|
||||
|
||||
mmi.GlobalPosition = center;
|
||||
|
||||
for ( int i = 0; i < randomList.Count; i++ )
|
||||
{
|
||||
var trsf = randomList[ i ].GlobalTransform;
|
||||
trsf.Origin -= center;
|
||||
|
||||
mm.SetInstanceTransform( i, trsf );
|
||||
}
|
||||
|
||||
mmi.Multimesh = mm;
|
||||
|
||||
mmi.CacheTransforms();
|
||||
|
||||
Materials.Set( mmi, Materials.Get<Material>( list[ 0 ] ) );
|
||||
|
||||
cIDCount++;
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -72,9 +72,9 @@ namespace Rokojori
|
|||
output.Append( obj.ToString() );
|
||||
}
|
||||
|
||||
static void LogMessage( string message )
|
||||
static void LogMessage( string message, int frameIndex = 3 )
|
||||
{
|
||||
var trace = GetTrace();
|
||||
var trace = GetTrace( frameIndex );
|
||||
GD.PrintRich("\n[b]" + message + "[/b]" );
|
||||
GD.PrintRich( trace );
|
||||
}
|
||||
|
@ -86,9 +86,9 @@ namespace Rokojori
|
|||
GD.PrintRich( trace );
|
||||
}
|
||||
|
||||
static void LogErrorMessage( string message )
|
||||
static void LogErrorMessage( string message, int frameIndex = 3 )
|
||||
{
|
||||
var trace = GetTrace();
|
||||
var trace = GetTrace( frameIndex );
|
||||
GD.PrintErr( "\n"+ message );
|
||||
GD.PrintRich( trace );
|
||||
}
|
||||
|
@ -98,10 +98,9 @@ namespace Rokojori
|
|||
return ( new System.Diagnostics.StackTrace( true ) ).ToString();
|
||||
}
|
||||
|
||||
static string GetTrace()
|
||||
static string GetTrace( int frameIndex = 3 )
|
||||
{
|
||||
var stackTrace = new System.Diagnostics.StackTrace( true );
|
||||
var frameIndex = 3;
|
||||
|
||||
var frame = stackTrace.GetFrame( frameIndex );
|
||||
|
||||
|
@ -145,11 +144,22 @@ namespace Rokojori
|
|||
LogMessage( GetLogString( objects ) );
|
||||
}
|
||||
|
||||
public static void Log( Node node, params object[] objects)
|
||||
{
|
||||
LogMessage( "[color=#55aaff][ " + HierarchyName.Of( node ) + " ][/color] " + GetLogString( objects ), 4 );
|
||||
}
|
||||
|
||||
public static void Error( params object[] objects)
|
||||
{
|
||||
LogErrorMessage( GetLogString( objects ) );
|
||||
}
|
||||
|
||||
public static void Error( Node node, params object[] objects)
|
||||
{
|
||||
LogErrorMessage( "[ " + HierarchyName.Of( node ) + " ] " + GetLogString( objects ), 4 );
|
||||
}
|
||||
|
||||
|
||||
public static string GetLogString( object[] objects)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
|
|
|
@ -0,0 +1,189 @@
|
|||
using Godot;
|
||||
|
||||
namespace Rokojori
|
||||
{
|
||||
public class FFT
|
||||
{
|
||||
class FloatSineTable
|
||||
{
|
||||
public float[] sineValues;
|
||||
|
||||
public FloatSineTable( int numBits )
|
||||
{
|
||||
int len = 1 << numBits;
|
||||
sineValues = new float[1 << numBits];
|
||||
|
||||
for ( int i = 0; i < len; i++ )
|
||||
{
|
||||
sineValues[i] = ( float ) Mathf.Sin( ( i * Mathf.Pi * 2.0 ) / len );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class BitReverseTable
|
||||
{
|
||||
public int[] reversedBits;
|
||||
|
||||
public BitReverseTable( int numBits )
|
||||
{
|
||||
reversedBits = new int[1 << numBits];
|
||||
|
||||
for ( int i = 0; i < reversedBits.Length; i++ )
|
||||
{
|
||||
reversedBits[i] = ReverseBits( i, numBits );
|
||||
}
|
||||
}
|
||||
|
||||
static int ReverseBits( int index, int numBits )
|
||||
{
|
||||
int i = 0;
|
||||
int rev = 0;
|
||||
|
||||
for ( i = rev = 0; i < numBits; i++ )
|
||||
{
|
||||
rev = ( rev << 1 ) | ( index & 1 );
|
||||
index >>= 1;
|
||||
}
|
||||
|
||||
return rev;
|
||||
}
|
||||
}
|
||||
|
||||
static readonly int MaxSizeLog2 = 16;
|
||||
|
||||
static BitReverseTable[] reverseTables = new BitReverseTable[ MaxSizeLog2 ];
|
||||
static FloatSineTable[] floatSineTables = new FloatSineTable[ MaxSizeLog2 ];
|
||||
|
||||
|
||||
static float[] GetFloatSineTable( int n )
|
||||
{
|
||||
FloatSineTable sineTable = floatSineTables[n];
|
||||
|
||||
if ( sineTable == null )
|
||||
{
|
||||
sineTable = new FloatSineTable( n );
|
||||
|
||||
floatSineTables[n] = sineTable;
|
||||
}
|
||||
|
||||
return sineTable.sineValues;
|
||||
}
|
||||
|
||||
static int[] GetReverseTable( int n )
|
||||
{
|
||||
BitReverseTable reverseTable = reverseTables[n];
|
||||
|
||||
if ( reverseTable == null )
|
||||
{
|
||||
reverseTable = new BitReverseTable( n );
|
||||
reverseTables[n] = reverseTable;
|
||||
}
|
||||
|
||||
return reverseTable.reversedBits;
|
||||
}
|
||||
|
||||
|
||||
static void Transform( int sign, int n, float[] real, float[] imaginary )
|
||||
{
|
||||
float scale = ( sign > 0 ) ? ( 2.0f / n ) : ( 0.5f );
|
||||
|
||||
int numBits = Rokojori.FFT.NumBits( n );
|
||||
|
||||
int[] reverseTable = GetReverseTable( numBits );
|
||||
float[] sineTable = GetFloatSineTable( numBits );
|
||||
|
||||
int mask = n - 1;
|
||||
int cosineOffset = n / 4; // phase offset between cos and sin
|
||||
|
||||
int i, j;
|
||||
|
||||
for ( i = 0; i < n; i++ )
|
||||
{
|
||||
j = reverseTable[i];
|
||||
|
||||
if ( j >= i )
|
||||
{
|
||||
float tempr = real[j] * scale;
|
||||
float tempi = imaginary[j] * scale;
|
||||
real[j] = real[i] * scale;
|
||||
imaginary[j] = imaginary[i] * scale;
|
||||
real[i] = tempr;
|
||||
imaginary[i] = tempi;
|
||||
}
|
||||
}
|
||||
|
||||
int mmax, stride;
|
||||
int numerator = sign * n;
|
||||
|
||||
for ( mmax = 1, stride = 2 * mmax; mmax < n; mmax = stride, stride = 2 * mmax )
|
||||
{
|
||||
int phase = 0;
|
||||
int phaseIncrement = numerator / ( 2 * mmax );
|
||||
|
||||
for ( int m = 0; m < mmax; ++m )
|
||||
{
|
||||
float wr = sineTable[( phase + cosineOffset ) & mask]; // cosine
|
||||
float wi = sineTable[phase];
|
||||
|
||||
for ( i = m; i < n; i += stride )
|
||||
{
|
||||
j = i + mmax;
|
||||
float tr = ( wr * real[j] ) - ( wi * imaginary[j] );
|
||||
float ti = ( wr * imaginary[j] ) + ( wi * real[j] );
|
||||
real[j] = real[i] - tr;
|
||||
imaginary[j] = imaginary[i] - ti;
|
||||
real[i] += tr;
|
||||
imaginary[i] += ti;
|
||||
}
|
||||
|
||||
phase = ( phase + phaseIncrement ) & mask;
|
||||
}
|
||||
|
||||
mmax = stride;
|
||||
}
|
||||
}
|
||||
|
||||
public static void CalculateMagnitudes( float[] ar, float[] ai, float[] magnitudes )
|
||||
{
|
||||
for ( int i = 0; i < magnitudes.Length; ++i )
|
||||
{
|
||||
magnitudes[i] = ( float ) Mathf.Sqrt( (ar[i] * ar[i] ) + ( ai[i] * ai[i] ));
|
||||
}
|
||||
}
|
||||
|
||||
static int NumBits( int powerOf2 )
|
||||
{
|
||||
int i;
|
||||
|
||||
for ( i = -1; powerOf2 > 0; powerOf2 = powerOf2 >> 1, i++ )
|
||||
;
|
||||
return i;
|
||||
}
|
||||
|
||||
public static void Forward( int n, float[] real, float[] imaginary )
|
||||
{
|
||||
Transform( 1, n, real, imaginary );
|
||||
}
|
||||
|
||||
public static void Forward( int n, float[] real )
|
||||
{
|
||||
float[] ai = new float[ real.Length ];
|
||||
|
||||
Forward( n, real, ai );
|
||||
}
|
||||
|
||||
public static void Inverse( int n, float[] real, float[] imaginary )
|
||||
{
|
||||
Transform( -1, n, real, imaginary );
|
||||
}
|
||||
|
||||
public static float FrequencyAtBin( int binIndex, int numBands, float sampleRate )
|
||||
{
|
||||
return binIndex * sampleRate / ( 2.0f * numBands );
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -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 )
|
||||
|
|
|
@ -25,8 +25,10 @@ namespace Rokojori
|
|||
public float A{ get { return end.Y - start.Y; } }
|
||||
public float B{ get { return start.X - end.X; } }
|
||||
public float C{ get { return A * start.X + B * start.Y; } }
|
||||
|
||||
public Vector2 direction { get { return end - start; } }
|
||||
public Vector2 reverseDirection { get { return start - end; } }
|
||||
|
||||
public float angle
|
||||
{
|
||||
get {
|
||||
|
@ -47,6 +49,8 @@ namespace Rokojori
|
|||
}
|
||||
}
|
||||
|
||||
public Vector2 center => ( end - start ) / 2f;
|
||||
|
||||
|
||||
public Vector2? InfiniteIntersectionOf( Line2 line2 )
|
||||
{
|
||||
|
@ -154,7 +158,24 @@ namespace Rokojori
|
|||
return ( point - ClosestPointToPoint( point ) ).Length();
|
||||
}
|
||||
|
||||
|
||||
public Vector2 GetAt( float t )
|
||||
{
|
||||
return direction * t + start;
|
||||
}
|
||||
|
||||
public void ScaleFromCenter( float scale )
|
||||
{
|
||||
var center = GetAt( 0.5f );
|
||||
var dir = direction * 0.5f;
|
||||
|
||||
start = center - dir * scale;
|
||||
end = center + dir * scale;
|
||||
}
|
||||
|
||||
public void Translate( Vector2 translation )
|
||||
{
|
||||
start += translation;
|
||||
end += translation;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -10,6 +10,15 @@ namespace Rokojori
|
|||
|
||||
public Plane3(){}
|
||||
|
||||
public static Plane3 CreateFromNode3D( Node3D n )
|
||||
{
|
||||
var p = new Plane3();
|
||||
p.SetFromNormalAndCoplanarPoint( n.GlobalUp(), n.GlobalPosition );
|
||||
|
||||
return p;
|
||||
|
||||
}
|
||||
|
||||
public void Set( Vector3 normal, float constant = 0 )
|
||||
{
|
||||
this.normal = normal;
|
||||
|
|
|
@ -196,6 +196,12 @@ namespace Rokojori
|
|||
return cValue + sValue + position;
|
||||
}
|
||||
|
||||
public static void CopyTo( Node3D source, Node3D target )
|
||||
{
|
||||
target.GlobalPosition = source.GlobalPosition;
|
||||
target.SetGlobalQuaternion( source.GetGlobalQuaternion() );
|
||||
}
|
||||
|
||||
|
||||
public static Pose Merge( List<Pose> poses, List<float> weights = null )
|
||||
{
|
||||
|
|
|
@ -62,6 +62,11 @@ namespace Rokojori
|
|||
|
||||
public static Sphere ContainingBox( Box3 box )
|
||||
{
|
||||
if ( box == null )
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return new Sphere( box.center, box.maxDistance / 2f );
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,153 @@
|
|||
using Godot;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
|
||||
namespace Rokojori
|
||||
{
|
||||
public class Triangle2
|
||||
{
|
||||
public Vector2 a;
|
||||
public Vector2 b;
|
||||
public Vector2 c;
|
||||
|
||||
bool _needsUpdate = true;
|
||||
Vector2 _center;
|
||||
|
||||
public Triangle2( Vector2 a, Vector2 b, Vector2 c )
|
||||
{
|
||||
this.a = a;
|
||||
this.b = b;
|
||||
this.c = c;
|
||||
}
|
||||
|
||||
public static Triangle2 AsXZ( Triangle3 t )
|
||||
{
|
||||
return new Triangle2( Math2D.XZ( t.a ), Math2D.XZ( t.b ), Math2D.XZ( t.c ) );
|
||||
}
|
||||
|
||||
public void Update()
|
||||
{
|
||||
if ( ! _needsUpdate )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_needsUpdate = false;
|
||||
_center = ( a + b +c ) /3f;
|
||||
}
|
||||
|
||||
public Vector2 center
|
||||
{
|
||||
get
|
||||
{
|
||||
Update();
|
||||
return _center;
|
||||
}
|
||||
}
|
||||
|
||||
public Line2 GetEdge( int index )
|
||||
{
|
||||
return index == 0 ? new Line2( a, b ) : index == 1 ? new Line2( b, c ) : new Line2( c, a) ;
|
||||
}
|
||||
|
||||
public Vector2 GetEdgeDirection( int index )
|
||||
{
|
||||
return GetEdge( index ).direction;
|
||||
}
|
||||
|
||||
public Vector2 GetInnerNormal( int index )
|
||||
{
|
||||
var edge = GetEdge( index );
|
||||
|
||||
var normal = Math2D.Rotate90DegreesRight( edge.direction.Normalized() );
|
||||
var flipNormal = - normal;
|
||||
var cnt = center;
|
||||
var edgeCenter = edge.center;
|
||||
var dist = ( cnt - edgeCenter ).Length() * 0.1f;
|
||||
|
||||
if ( ContainsPoint( edgeCenter + normal ) )
|
||||
{
|
||||
return normal;
|
||||
}
|
||||
|
||||
return -normal;
|
||||
}
|
||||
|
||||
public bool ContainsPoint( Vector2 p )
|
||||
{
|
||||
if ( true)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
var d1 = Sign( p, a, b );
|
||||
var d2 = Sign( p, b, c );
|
||||
var d3 = Sign( p, c, a );
|
||||
|
||||
var hasNegative = ( d1 < 0 ) || ( d2 < 0 ) || ( d3 < 0 );
|
||||
var hasPositive = ( d1 > 0 ) || ( d2 > 0 ) || ( d3 > 0 );
|
||||
|
||||
return ! ( hasNegative && hasPositive );
|
||||
}
|
||||
|
||||
|
||||
public Vector2 GetOuterNormal( int index )
|
||||
{
|
||||
return - GetInnerNormal( index );
|
||||
}
|
||||
|
||||
public Triangle2 Shrink( float distance )
|
||||
{
|
||||
var edges = new List<Line2>();
|
||||
|
||||
for ( int i = 0; i < 3; i++ )
|
||||
{
|
||||
edges.Add( GetEdge( i ) );
|
||||
edges[ i ].ScaleFromCenter( 10 );
|
||||
var t = GetInnerNormal( i ) * distance;
|
||||
edges[ i ].Translate( t );
|
||||
// RJLog.Log( i, ">", t );
|
||||
}
|
||||
|
||||
|
||||
var e0 = edges[ 0 ];
|
||||
var e1 = edges[ 1 ];
|
||||
var e2 = edges[ 2 ];
|
||||
|
||||
var i01 = e0.IntersectionOf( e1 );
|
||||
|
||||
if ( i01 == null || ! ContainsPoint( (Vector2) i01 ) )
|
||||
{
|
||||
RJLog.Log( "i01", i01, i01 != null ? ContainsPoint( (Vector2) i01 ) : false );
|
||||
return null;
|
||||
}
|
||||
|
||||
var i12 = e1.IntersectionOf( e2 );
|
||||
|
||||
if ( i12 == null || ! ContainsPoint( (Vector2) i12 ) )
|
||||
{
|
||||
RJLog.Log( "i12", i12, i12 != null ? ContainsPoint( (Vector2) i12 ) : false );
|
||||
return null;
|
||||
}
|
||||
|
||||
var i20 = e2.IntersectionOf( e0 );
|
||||
|
||||
if ( i20 == null || ! ContainsPoint( (Vector2) i20 ) )
|
||||
{
|
||||
RJLog.Log( "i20", i20, i20 != null ? ContainsPoint( (Vector2) i20 ) : false );
|
||||
return null;
|
||||
}
|
||||
|
||||
return new Triangle2( (Vector2) i20, (Vector2) i01, (Vector2) i12 );
|
||||
}
|
||||
|
||||
static float Sign( Vector2 p1, Vector2 p2, Vector2 p3)
|
||||
{
|
||||
return ( p1.X - p3.X ) * ( p2.Y - p3.Y ) - ( p2.X - p3.X ) * (p1.Y - p3.Y );
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
|
@ -21,6 +21,46 @@ namespace Rokojori
|
|||
_needsUpdate = true;
|
||||
}
|
||||
|
||||
public void SetFrom( MeshGeometry mg, int a, int b, int c )
|
||||
{
|
||||
this.a = mg.vertices[ a ];
|
||||
this.b = mg.vertices[ b ];
|
||||
this.c = mg.vertices[ c ];
|
||||
|
||||
_needsUpdate = true;
|
||||
}
|
||||
|
||||
public static Triangle3 CreateFrom( MeshGeometry mg, int a, int b, int c )
|
||||
{
|
||||
var t = new Triangle3(
|
||||
mg.vertices[ a ],
|
||||
mg.vertices[ b ],
|
||||
mg.vertices[ c ]
|
||||
);
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
public static Triangle3 CreateFrom( MeshGeometry mg, int triangleIndex )
|
||||
{
|
||||
var triangleOffset = triangleIndex;
|
||||
|
||||
var t = new Triangle3(
|
||||
mg.vertices[ triangleOffset ],
|
||||
mg.vertices[ triangleOffset + 1],
|
||||
mg.vertices[ triangleOffset + 2 ]
|
||||
);
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
public void SetFrom( MeshGeometry mg, int triangleIndex )
|
||||
{
|
||||
var triangleOffset = triangleIndex;
|
||||
|
||||
SetFrom( mg, triangleOffset, triangleOffset + 1, triangleOffset + 2 );
|
||||
}
|
||||
|
||||
public float perimeter
|
||||
{
|
||||
get
|
||||
|
@ -38,6 +78,7 @@ namespace Rokojori
|
|||
Vector3 _center;
|
||||
Sphere _boundingSphere;
|
||||
Plane3 _plane;
|
||||
Vector3 _normal;
|
||||
|
||||
bool _needsUpdate = false;
|
||||
|
||||
|
@ -46,6 +87,16 @@ namespace Rokojori
|
|||
_needsUpdate = true;
|
||||
}
|
||||
|
||||
|
||||
public Vector3 center
|
||||
{
|
||||
get
|
||||
{
|
||||
Update();
|
||||
return _center;
|
||||
}
|
||||
}
|
||||
|
||||
public Sphere boundingSphere
|
||||
{
|
||||
get
|
||||
|
@ -69,6 +120,10 @@ namespace Rokojori
|
|||
_plane = new Plane3();
|
||||
_plane.SetFromCoplanarPoints( a, b, c );
|
||||
|
||||
_normal = ( b - a ).Cross( c - a ).Normalized();
|
||||
|
||||
_center = ( a + b + c ) / 3f;
|
||||
|
||||
_needsUpdate = false;
|
||||
}
|
||||
|
||||
|
@ -208,6 +263,11 @@ namespace Rokojori
|
|||
return ( ( a - b ).Length() * h ) / 2f;
|
||||
}
|
||||
|
||||
public LerpCurve3 GetOutline()
|
||||
{
|
||||
return LerpCurve3.FromPoints( GetPoint( 0 ), GetPoint( 1 ), GetPoint( 2 ) );
|
||||
}
|
||||
|
||||
public Vector3 GetPoint( int i )
|
||||
{
|
||||
if ( i == 0 )
|
||||
|
@ -326,6 +386,105 @@ namespace Rokojori
|
|||
return false;
|
||||
}
|
||||
|
||||
public void Rotate( Quaternion q, Vector3? pivot = null )
|
||||
{
|
||||
if ( pivot != null )
|
||||
{
|
||||
Translate( -( (Vector3)pivot ));
|
||||
}
|
||||
|
||||
a = a * q;
|
||||
b = b * q;
|
||||
c = c * q;
|
||||
|
||||
if ( pivot != null )
|
||||
{
|
||||
Translate( ( (Vector3)pivot ));
|
||||
}
|
||||
}
|
||||
|
||||
public Triangle3 Clone()
|
||||
{
|
||||
return new Triangle3( a, b, c );
|
||||
}
|
||||
|
||||
public Vector3 normal
|
||||
{
|
||||
get
|
||||
{
|
||||
Update();
|
||||
return _normal;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public Quaternion GetXZAlignmentRotation()
|
||||
{
|
||||
return Math3D.AlignUp( normal );
|
||||
}
|
||||
|
||||
public void ScaleFromCenter( float scale )
|
||||
{
|
||||
var cnt = center;
|
||||
a = ( a - cnt ) * scale + cnt;
|
||||
b = ( b - cnt ) * scale + cnt;
|
||||
c = ( c - cnt ) * scale + cnt;
|
||||
}
|
||||
|
||||
public Triangle3 Shrink( float distance )
|
||||
{
|
||||
var n = normal;
|
||||
|
||||
var t = Offset( distance );
|
||||
|
||||
if ( t == null || Math3D.LookingAtEachOther( t.normal, n ) )
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return t;
|
||||
|
||||
}
|
||||
|
||||
public Triangle3 Offset( float distance )
|
||||
{
|
||||
var rotationXZ = GetXZAlignmentRotation();
|
||||
var clone = Clone();
|
||||
var originalCenter = center;
|
||||
|
||||
clone.Translate( - originalCenter );
|
||||
clone.Rotate( rotationXZ );
|
||||
|
||||
|
||||
var t2 = Triangle2.AsXZ( clone );
|
||||
|
||||
var shrinked = t2.Shrink( distance );
|
||||
|
||||
if ( shrinked == null )
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
clone = XYasXZ( shrinked );
|
||||
clone.Rotate( rotationXZ.Inverse() );
|
||||
clone.Translate( originalCenter );
|
||||
|
||||
return clone;
|
||||
}
|
||||
|
||||
public void Translate( Vector3 translation )
|
||||
{
|
||||
a += translation;
|
||||
b += translation;
|
||||
c += translation;
|
||||
}
|
||||
|
||||
public static Triangle3 XYasXZ( Triangle2 t )
|
||||
{
|
||||
return new Triangle3( Math3D.XYasXZ( t.a ), Math3D.XYasXZ( t.b ), Math3D.XYasXZ( t.c ) );
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static bool InsideTriangle( Vector3 point, Vector3 a, Vector3 b, Vector3 c )
|
||||
{
|
||||
|
@ -374,6 +533,7 @@ namespace Rokojori
|
|||
return new Vector3( 1f - u - v, v, u );
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,112 @@
|
|||
using Godot;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
|
||||
namespace Rokojori
|
||||
{
|
||||
[Tool]
|
||||
[GlobalClass]
|
||||
public partial class TriangleTest:Node3D
|
||||
{
|
||||
[Export]
|
||||
public bool alignUp = false;
|
||||
|
||||
[Export]
|
||||
public bool shrink = false;
|
||||
[Export]
|
||||
public float shrinkDistance = 0;
|
||||
|
||||
[Export]
|
||||
public bool scale = false;
|
||||
[Export]
|
||||
public float scaleAmount = 1;
|
||||
|
||||
[ExportGroup("Source")]
|
||||
[Export]
|
||||
public Node3D sA;
|
||||
[Export]
|
||||
public Node3D sB;
|
||||
[Export]
|
||||
public Node3D sC;
|
||||
|
||||
[ExportGroup("Target")]
|
||||
[Export]
|
||||
public Node3D tA;
|
||||
[Export]
|
||||
public Node3D tB;
|
||||
[Export]
|
||||
public Node3D tC;
|
||||
|
||||
[Export]
|
||||
public Vector3 offset = Vector3.Zero;
|
||||
|
||||
|
||||
|
||||
public override void _Process( double delta )
|
||||
{
|
||||
if ( alignUp )
|
||||
{
|
||||
AlignUp();
|
||||
}
|
||||
|
||||
if ( shrink )
|
||||
{
|
||||
Shrink();
|
||||
}
|
||||
|
||||
if ( scale )
|
||||
{
|
||||
ScaleFromCenter();
|
||||
}
|
||||
}
|
||||
|
||||
void AlignUp()
|
||||
{
|
||||
var s = GetSourceTriangle();
|
||||
var r = s.GetXZAlignmentRotation();
|
||||
|
||||
RJLog.Log( s.normal, r );
|
||||
|
||||
s.Rotate( r, s.center );
|
||||
|
||||
SetTargetTriangle( s );
|
||||
}
|
||||
|
||||
void Shrink()
|
||||
{
|
||||
var s = GetSourceTriangle();
|
||||
s = s.Offset( shrinkDistance );
|
||||
|
||||
if ( s == null )
|
||||
{
|
||||
RJLog.Log( "Shrinking failed" );
|
||||
SetTargetTriangle( GetSourceTriangle() );
|
||||
return;
|
||||
}
|
||||
|
||||
SetTargetTriangle( s );
|
||||
}
|
||||
|
||||
void ScaleFromCenter()
|
||||
{
|
||||
var s = GetSourceTriangle();
|
||||
s.ScaleFromCenter( scaleAmount );
|
||||
|
||||
SetTargetTriangle( s );
|
||||
}
|
||||
|
||||
Triangle3 GetSourceTriangle()
|
||||
{
|
||||
return new Triangle3( sA.GlobalPosition, sB.GlobalPosition, sC.GlobalPosition );
|
||||
}
|
||||
|
||||
void SetTargetTriangle( Triangle3 t )
|
||||
{
|
||||
tA.GlobalPosition = t.a + offset;
|
||||
tB.GlobalPosition = t.b + offset;
|
||||
tC.GlobalPosition = t.c + offset;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue