diff --git a/External/Imposter/materials/albedo_material.material b/External/Imposter/materials/albedo_material.material index f5f4dd8..299a48d 100644 Binary files a/External/Imposter/materials/albedo_material.material and b/External/Imposter/materials/albedo_material.material differ diff --git a/External/Imposter/materials/depth_baker.gdshader b/External/Imposter/materials/depth_baker.gdshader index 1ca602f..69accd0 100644 --- a/External/Imposter/materials/depth_baker.gdshader +++ b/External/Imposter/materials/depth_baker.gdshader @@ -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)); } diff --git a/External/Imposter/materials/depth_baker.material b/External/Imposter/materials/depth_baker.material index 8d8425f..12788f0 100644 Binary files a/External/Imposter/materials/depth_baker.material and b/External/Imposter/materials/depth_baker.material differ diff --git a/External/Imposter/materials/dilatate.gdshader b/External/Imposter/materials/dilatate.gdshader index a4aa9b8..ab74aed 100644 --- a/External/Imposter/materials/dilatate.gdshader +++ b/External/Imposter/materials/dilatate.gdshader @@ -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; diff --git a/External/Imposter/materials/dilatate.material b/External/Imposter/materials/dilatate.material index ef72d46..c1078f7 100644 Binary files a/External/Imposter/materials/dilatate.material and b/External/Imposter/materials/dilatate.material differ diff --git a/External/Imposter/materials/normal_baker.gdshader b/External/Imposter/materials/normal_baker.gdshader index cf21be5..1c53860 100644 --- a/External/Imposter/materials/normal_baker.gdshader +++ b/External/Imposter/materials/normal_baker.gdshader @@ -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; } diff --git a/External/Imposter/materials/normal_baker.material b/External/Imposter/materials/normal_baker.material index cc39eb8..0d4e9ac 100644 Binary files a/External/Imposter/materials/normal_baker.material and b/External/Imposter/materials/normal_baker.material differ diff --git a/External/Imposter/materials/normal_baker.material.depren b/External/Imposter/materials/normal_baker.material.depren new file mode 100644 index 0000000..4303410 Binary files /dev/null and b/External/Imposter/materials/normal_baker.material.depren differ diff --git a/External/Imposter/materials/orm_baker.gdshader b/External/Imposter/materials/orm_baker.gdshader index d24df02..f999b83 100644 --- a/External/Imposter/materials/orm_baker.gdshader +++ b/External/Imposter/materials/orm_baker.gdshader @@ -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) { diff --git a/External/Imposter/materials/orm_baker.material b/External/Imposter/materials/orm_baker.material index f8ec72e..0b458ec 100644 Binary files a/External/Imposter/materials/orm_baker.material and b/External/Imposter/materials/orm_baker.material differ diff --git a/External/Imposter/materials/orm_baker.material.depren b/External/Imposter/materials/orm_baker.material.depren new file mode 100644 index 0000000..d477110 Binary files /dev/null and b/External/Imposter/materials/orm_baker.material.depren differ diff --git a/External/Imposter/materials/shaders/ImpostorShader.gdshader b/External/Imposter/materials/shaders/ImpostorShader.gdshader index db26085..4ffaf70 100644 --- a/External/Imposter/materials/shaders/ImpostorShader.gdshader +++ b/External/Imposter/materials/shaders/ImpostorShader.gdshader @@ -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; diff --git a/External/Imposter/materials/shaders/ImpostorShaderSimple.gdshader b/External/Imposter/materials/shaders/ImpostorShaderSimple.gdshader new file mode 100644 index 0000000..27bb52f --- /dev/null +++ b/External/Imposter/materials/shaders/ImpostorShaderSimple.gdshader @@ -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; +} diff --git a/External/Imposter/test.tres b/External/Imposter/test.tres new file mode 100644 index 0000000..e965bfb --- /dev/null +++ b/External/Imposter/test.tres @@ -0,0 +1,3 @@ +[gd_resource type="StandardMaterial3D" format=3 uid="uid://b8cbxcruu45c0"] + +[resource] diff --git a/Runtime/Actions/Action.cs b/Runtime/Actions/Action.cs new file mode 100644 index 0000000..f7a9472 --- /dev/null +++ b/Runtime/Actions/Action.cs @@ -0,0 +1,21 @@ + +using Godot; + + +namespace Rokojori +{ + [GlobalClass ] + public partial class Action : NetworkNode + { + public void Trigger() + { + _OnTrigger(); + } + + public virtual void _OnTrigger() + { + + } + } + +} \ No newline at end of file diff --git a/Runtime/Actions/ActionSequence.cs b/Runtime/Actions/ActionSequence.cs index 57ece00..1c25765 100644 --- a/Runtime/Actions/ActionSequence.cs +++ b/Runtime/Actions/ActionSequence.cs @@ -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 ) { diff --git a/Runtime/Actions/IterateActions.cs b/Runtime/Actions/IterateActions.cs new file mode 100644 index 0000000..7d983be --- /dev/null +++ b/Runtime/Actions/IterateActions.cs @@ -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(); + + if ( num == 0 ) + { + iterationIndex = 0; + return; + } + + iterationIndex = MathX.Repeat( iterationIndex, num ); + + Actions.Trigger( this.GetNthDirectChild( iterationIndex ) ); + + iterationIndex++; + } + } +} \ No newline at end of file diff --git a/Runtime/Actions/Node3D/CopyPose.cs b/Runtime/Actions/Node3D/CopyPose.cs new file mode 100644 index 0000000..f64e1db --- /dev/null +++ b/Runtime/Actions/Node3D/CopyPose.cs @@ -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; + } + } +} \ No newline at end of file diff --git a/Runtime/Animation/AnimationCurve.cs b/Runtime/Animation/AnimationCurve.cs new file mode 100644 index 0000000..5f2fffe --- /dev/null +++ b/Runtime/Animation/AnimationCurve.cs @@ -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 ); + } + } + +} \ No newline at end of file diff --git a/Runtime/Animation/AnimationCurve3D.cs b/Runtime/Animation/AnimationCurve3D.cs new file mode 100644 index 0000000..3c24991 --- /dev/null +++ b/Runtime/Animation/AnimationCurve3D.cs @@ -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 ); + } + } + +} \ No newline at end of file diff --git a/Runtime/Animation/AnimationManager.cs b/Runtime/Animation/AnimationManager.cs new file mode 100644 index 0000000..783105b --- /dev/null +++ b/Runtime/Animation/AnimationManager.cs @@ -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 _animating = new MultiMap(); + + 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(); + } + } + } +} \ No newline at end of file diff --git a/Runtime/Animation/Flash.cs b/Runtime/Animation/Flash.cs new file mode 100644 index 0000000..ccecb3e --- /dev/null +++ b/Runtime/Animation/Flash.cs @@ -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 _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(); + 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(); + + + 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(); + } + + + } +} \ No newline at end of file diff --git a/Runtime/Animation/Flash/Blue-Shield-FlashEffect.tres b/Runtime/Animation/Flash/Blue-Shield-FlashEffect.tres new file mode 100644 index 0000000..41f0c1e --- /dev/null +++ b/Runtime/Animation/Flash/Blue-Shield-FlashEffect.tres @@ -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") diff --git a/Runtime/Animation/Flash/Green-Charge-FlashEffect.tres b/Runtime/Animation/Flash/Green-Charge-FlashEffect.tres new file mode 100644 index 0000000..d8f953c --- /dev/null +++ b/Runtime/Animation/Flash/Green-Charge-FlashEffect.tres @@ -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") diff --git a/Runtime/Animation/Flash/Orange-Boost-FlashEffect.tres b/Runtime/Animation/Flash/Orange-Boost-FlashEffect.tres new file mode 100644 index 0000000..4c09d57 --- /dev/null +++ b/Runtime/Animation/Flash/Orange-Boost-FlashEffect.tres @@ -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") diff --git a/Runtime/Animation/Flash/Red-Hit-FlashEffect.tres b/Runtime/Animation/Flash/Red-Hit-FlashEffect.tres new file mode 100644 index 0000000..444d845 --- /dev/null +++ b/Runtime/Animation/Flash/Red-Hit-FlashEffect.tres @@ -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") diff --git a/Runtime/Animation/Flash/White-Blinking-FlashEffect.tres b/Runtime/Animation/Flash/White-Blinking-FlashEffect.tres new file mode 100644 index 0000000..647f4a9 --- /dev/null +++ b/Runtime/Animation/Flash/White-Blinking-FlashEffect.tres @@ -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") diff --git a/Runtime/Animation/FlashEffect.cs b/Runtime/Animation/FlashEffect.cs new file mode 100644 index 0000000..50226ec --- /dev/null +++ b/Runtime/Animation/FlashEffect.cs @@ -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; + + + + } +} \ No newline at end of file diff --git a/Runtime/Animation/HDRColor.cs b/Runtime/Animation/HDRColor.cs new file mode 100644 index 0000000..73fecb6 --- /dev/null +++ b/Runtime/Animation/HDRColor.cs @@ -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 ); + } + + } +} \ No newline at end of file diff --git a/Runtime/Animation/Shake/Shake.cs b/Runtime/Animation/Shake/Shake.cs new file mode 100644 index 0000000..7229dd0 --- /dev/null +++ b/Runtime/Animation/Shake/Shake.cs @@ -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 _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 ) + { + + } + } +} \ No newline at end of file diff --git a/Runtime/Animation/Shake/ShakeEffect.cs b/Runtime/Animation/Shake/ShakeEffect.cs new file mode 100644 index 0000000..e502d18 --- /dev/null +++ b/Runtime/Animation/Shake/ShakeEffect.cs @@ -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; + + + + } +} \ No newline at end of file diff --git a/Runtime/Animation/Transform/AnimateTransform.cs b/Runtime/Animation/Transform/AnimateTransform.cs new file mode 100644 index 0000000..3d7793b --- /dev/null +++ b/Runtime/Animation/Transform/AnimateTransform.cs @@ -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 _frameValues; + List _randomizations; + + public void OnAnimatorStart(){} + public void OnAnimatorEnd(){} + public void OnAnimatorCancel(){} + + + public override void _OnTrigger() + { + _frameValues = new List(); + _randomizations = new List(); + + 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 ); + } + } + ); + + + } + + + + + } +} \ No newline at end of file diff --git a/Runtime/Animation/Transform/TransformAnimations.cs b/Runtime/Animation/Transform/TransformAnimations.cs new file mode 100644 index 0000000..fcf0d59 --- /dev/null +++ b/Runtime/Animation/Transform/TransformAnimations.cs @@ -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 randomizations ) + { + var d = 0f; + + for ( int i = 0; i < curves.Length; i++ ) + { + d = Mathf.Max( d, curves[ i ].GetRandomizedEndTime( randomizations[ i ] ) ); + } + + return d; + + } + } +} \ No newline at end of file diff --git a/Runtime/Animation/Transform/TransformCurve.cs b/Runtime/Animation/Transform/TransformCurve.cs new file mode 100644 index 0000000..ae9f368 --- /dev/null +++ b/Runtime/Animation/Transform/TransformCurve.cs @@ -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; + + } + + + + + + } +} \ No newline at end of file diff --git a/Runtime/Animation/Transform/TransformTarget.cs b/Runtime/Animation/Transform/TransformTarget.cs new file mode 100644 index 0000000..10f730e --- /dev/null +++ b/Runtime/Animation/Transform/TransformTarget.cs @@ -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; + } + + } + } + +} \ No newline at end of file diff --git a/Runtime/Audio/AudioGraph/Basic/AudioSample.cs b/Runtime/Audio/AudioGraph/Basic/AudioSample.cs new file mode 100644 index 0000000..7feb68e --- /dev/null +++ b/Runtime/Audio/AudioGraph/Basic/AudioSample.cs @@ -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; } + } + + } +} \ No newline at end of file diff --git a/Runtime/Audio/AudioGraph/Basic/Constant.cs b/Runtime/Audio/AudioGraph/Basic/Constant.cs new file mode 100644 index 0000000..9221c00 --- /dev/null +++ b/Runtime/Audio/AudioGraph/Basic/Constant.cs @@ -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; + } + + } + + + } + +} \ No newline at end of file diff --git a/Runtime/Audio/AudioGraph/Effect/AudioEffectMono.cs b/Runtime/Audio/AudioGraph/Effect/AudioEffectMono.cs new file mode 100644 index 0000000..0f869fa --- /dev/null +++ b/Runtime/Audio/AudioGraph/Effect/AudioEffectMono.cs @@ -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 ); + } + + } + +} \ No newline at end of file diff --git a/Runtime/Audio/AudioGraph/Effect/AudioEffectStereo.cs b/Runtime/Audio/AudioGraph/Effect/AudioEffectStereo.cs new file mode 100644 index 0000000..3afb4df --- /dev/null +++ b/Runtime/Audio/AudioGraph/Effect/AudioEffectStereo.cs @@ -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 ); + } + + } + +} \ No newline at end of file diff --git a/Runtime/Audio/AudioGraph/Effect/Gain.cs b/Runtime/Audio/AudioGraph/Effect/Gain.cs new file mode 100644 index 0000000..5cb6736 --- /dev/null +++ b/Runtime/Audio/AudioGraph/Effect/Gain.cs @@ -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 ]; + } + + } + + + } + +} \ No newline at end of file diff --git a/Runtime/Audio/AudioGraph/Generators/AudioGeneratorMono.cs b/Runtime/Audio/AudioGraph/Generators/AudioGeneratorMono.cs new file mode 100644 index 0000000..b08e238 --- /dev/null +++ b/Runtime/Audio/AudioGraph/Generators/AudioGeneratorMono.cs @@ -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 ); + } + + } + +} \ No newline at end of file diff --git a/Runtime/Audio/AudioGraph/Generators/AudioGeneratorStereo.cs b/Runtime/Audio/AudioGraph/Generators/AudioGeneratorStereo.cs new file mode 100644 index 0000000..d241171 --- /dev/null +++ b/Runtime/Audio/AudioGraph/Generators/AudioGeneratorStereo.cs @@ -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 ); + } + + } + +} \ No newline at end of file diff --git a/Runtime/Audio/AudioGraph/Generators/BandLimitedWaveTable.cs b/Runtime/Audio/AudioGraph/Generators/BandLimitedWaveTable.cs new file mode 100644 index 0000000..de5bb95 --- /dev/null +++ b/Runtime/Audio/AudioGraph/Generators/BandLimitedWaveTable.cs @@ -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 entries = new List(); + 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; + } + } +} \ No newline at end of file diff --git a/Runtime/Audio/AudioGraph/Generators/PhaseGenerator.cs b/Runtime/Audio/AudioGraph/Generators/PhaseGenerator.cs new file mode 100644 index 0000000..c85e54d --- /dev/null +++ b/Runtime/Audio/AudioGraph/Generators/PhaseGenerator.cs @@ -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 pitch; + protected iPhaseGenerator _generator; + + public PhaseGenerator( AudioGraph ag, iPhaseGenerator generator ): base( ag ) + { + pitch = new AudioEventInput( 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; + } + } + + + } + +} \ No newline at end of file diff --git a/Runtime/Audio/AudioGraph/Generators/SineGenerator.cs b/Runtime/Audio/AudioGraph/Generators/SineGenerator.cs new file mode 100644 index 0000000..1898743 --- /dev/null +++ b/Runtime/Audio/AudioGraph/Generators/SineGenerator.cs @@ -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 ); + } + + } + + + } + +} \ No newline at end of file diff --git a/Runtime/Audio/AudioGraph/Generators/WaveTable.cs b/Runtime/Audio/AudioGraph/Generators/WaveTable.cs new file mode 100644 index 0000000..dc7ecaf --- /dev/null +++ b/Runtime/Audio/AudioGraph/Generators/WaveTable.cs @@ -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 ); + } + } +} \ No newline at end of file diff --git a/Runtime/Audio/AudioGraph/Generators/WaveTableGenerator.cs b/Runtime/Audio/AudioGraph/Generators/WaveTableGenerator.cs new file mode 100644 index 0000000..9a3b4ac --- /dev/null +++ b/Runtime/Audio/AudioGraph/Generators/WaveTableGenerator.cs @@ -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 ); + } + } + + + } + +} \ No newline at end of file diff --git a/Runtime/Audio/AudioGraph/Generators/iPhaseGenerator.cs b/Runtime/Audio/AudioGraph/Generators/iPhaseGenerator.cs new file mode 100644 index 0000000..0694c69 --- /dev/null +++ b/Runtime/Audio/AudioGraph/Generators/iPhaseGenerator.cs @@ -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 ); + } +} \ No newline at end of file diff --git a/Runtime/Audio/AudioGraph/Instruments/Instrument.cs b/Runtime/Audio/AudioGraph/Instruments/Instrument.cs new file mode 100644 index 0000000..a66a03b --- /dev/null +++ b/Runtime/Audio/AudioGraph/Instruments/Instrument.cs @@ -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 _voices = new List(); + + protected override void _Process() + { + + } + } +} \ No newline at end of file diff --git a/Runtime/Audio/AudioGraph/Instruments/MusicTimeLine.cs b/Runtime/Audio/AudioGraph/Instruments/MusicTimeLine.cs new file mode 100644 index 0000000..d39c7cf --- /dev/null +++ b/Runtime/Audio/AudioGraph/Instruments/MusicTimeLine.cs @@ -0,0 +1,32 @@ +using Godot; +using System.Reflection; +using System.Collections.Generic; +using System.Text; + +namespace Rokojori +{ + public class MusicTimeLine:AudioProcessor + { + + + public AudioEventInput bpm; + public AudioEventInput loop; + public AudioEventOutput timeLineRanges; + + public MusicTimeLine( AudioGraph ag ):base( ag ) + { + bpm = new AudioEventInput( this ); + loop = new AudioEventInput( this ); + + timeLineRanges = new AudioEventOutput( this); + } + + MusicTimeLineRange _loopRange; + float _bpm = 120; + + protected override void _Process() + { + + } + } +} \ No newline at end of file diff --git a/Runtime/Audio/AudioGraph/Instruments/MusicTimeLineRange.cs b/Runtime/Audio/AudioGraph/Instruments/MusicTimeLineRange.cs new file mode 100644 index 0000000..5c69a19 --- /dev/null +++ b/Runtime/Audio/AudioGraph/Instruments/MusicTimeLineRange.cs @@ -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; + } + +} \ No newline at end of file diff --git a/Runtime/Audio/AudioGraph/Instruments/NoteEvent.cs b/Runtime/Audio/AudioGraph/Instruments/NoteEvent.cs new file mode 100644 index 0000000..48639f8 --- /dev/null +++ b/Runtime/Audio/AudioGraph/Instruments/NoteEvent.cs @@ -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; + } +} \ No newline at end of file diff --git a/Runtime/Audio/AudioGraph/Instruments/Voice.cs b/Runtime/Audio/AudioGraph/Instruments/Voice.cs new file mode 100644 index 0000000..897ba8a --- /dev/null +++ b/Runtime/Audio/AudioGraph/Instruments/Voice.cs @@ -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(); + + } +} \ No newline at end of file diff --git a/Runtime/Audio/AudioGraph/Structure/AudioConnection.cs b/Runtime/Audio/AudioGraph/Structure/AudioConnection.cs new file mode 100644 index 0000000..97c8c59 --- /dev/null +++ b/Runtime/Audio/AudioGraph/Structure/AudioConnection.cs @@ -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() + { + + } + } + +} \ No newline at end of file diff --git a/Runtime/Audio/AudioGraph/Structure/AudioEvent.cs b/Runtime/Audio/AudioGraph/Structure/AudioEvent.cs new file mode 100644 index 0000000..b22b6fd --- /dev/null +++ b/Runtime/Audio/AudioGraph/Structure/AudioEvent.cs @@ -0,0 +1,22 @@ +using Godot; +using System.Reflection; +using System.Collections.Generic; +using System.Text; +using System; + +namespace Rokojori +{ + public abstract class AudioEvent : AudioConnection + { + protected List _events = new List(); + + public AudioEvent( AudioProcessor p, bool isInput ):base( p, isInput ) + { + } + + public override void Clear() + { + _events.Clear(); + } + } +} \ No newline at end of file diff --git a/Runtime/Audio/AudioGraph/Structure/AudioEventInput.cs b/Runtime/Audio/AudioGraph/Structure/AudioEventInput.cs new file mode 100644 index 0000000..682d7cd --- /dev/null +++ b/Runtime/Audio/AudioGraph/Structure/AudioEventInput.cs @@ -0,0 +1,58 @@ +using Godot; +using System.Reflection; +using System.Collections.Generic; +using System.Text; +using System; + +namespace Rokojori +{ + public class AudioEventInput : AudioEvent + { + public AudioEventInput( AudioProcessor p ):base( p, true ) + {} + + protected List> _outputs = new List>(); + + public List> connected => _outputs; + + public void _GetConnectedFrom( AudioEventOutput output ) + { + _outputs.Add( output ); + } + + public void _GetDisconnectedFrom( AudioEventOutput 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() ); + } + ); + } + + + } +} \ No newline at end of file diff --git a/Runtime/Audio/AudioGraph/Structure/AudioEventOutput.cs b/Runtime/Audio/AudioGraph/Structure/AudioEventOutput.cs new file mode 100644 index 0000000..f8314e9 --- /dev/null +++ b/Runtime/Audio/AudioGraph/Structure/AudioEventOutput.cs @@ -0,0 +1,46 @@ +using Godot; +using System.Reflection; +using System.Collections.Generic; +using System.Text; + +namespace Rokojori +{ + public class AudioEventOutput : AudioEvent + { + public AudioEventOutput( AudioProcessor p, bool autoClear = true ):base( p, autoClear ) + {} + + List> _inputs = new List>(); + + public List> connected => _inputs; + + public bool IsConnectedTo( AudioEventInput input ) + { + return _inputs.Contains( input ); + } + + public void ConnectTo( AudioEventInput input ) + { + _inputs.Add( input ); + input._GetConnectedFrom( this ); + } + + public void Disconnect( AudioEventInput input ) + { + _inputs.Remove( input ); + input._GetDisconnectedFrom( this ); + } + + public void Push( T t ) + { + _events.Add( t ); + } + + public List GetEventsList() + { + return _events; + } + + } + +} \ No newline at end of file diff --git a/Runtime/Audio/AudioGraph/Structure/AudioGraph.cs b/Runtime/Audio/AudioGraph/Structure/AudioGraph.cs new file mode 100644 index 0000000..9b9e3d8 --- /dev/null +++ b/Runtime/Audio/AudioGraph/Structure/AudioGraph.cs @@ -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 _nodes = new List(); + + 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; + } + + } + +} \ No newline at end of file diff --git a/Runtime/Audio/AudioGraph/Structure/AudioNode.cs b/Runtime/Audio/AudioGraph/Structure/AudioNode.cs new file mode 100644 index 0000000..e87f950 --- /dev/null +++ b/Runtime/Audio/AudioGraph/Structure/AudioNode.cs @@ -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 ); + } + + } + + + } +} \ No newline at end of file diff --git a/Runtime/Audio/AudioGraph/Structure/AudioProcessor.cs b/Runtime/Audio/AudioGraph/Structure/AudioProcessor.cs new file mode 100644 index 0000000..fe71ce5 --- /dev/null +++ b/Runtime/Audio/AudioGraph/Structure/AudioProcessor.cs @@ -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 _connections = new List(); + protected List _inputConnections = new List(); + protected List _outputConnections = new List(); + + 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; + } + } +} \ No newline at end of file diff --git a/Runtime/Audio/AudioGraph/Structure/AudioStream.cs b/Runtime/Audio/AudioGraph/Structure/AudioStream.cs new file mode 100644 index 0000000..419776a --- /dev/null +++ b/Runtime/Audio/AudioGraph/Structure/AudioStream.cs @@ -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 ]; + } + } +} \ No newline at end of file diff --git a/Runtime/Audio/AudioGraph/Structure/AudioStreamInput.cs b/Runtime/Audio/AudioGraph/Structure/AudioStreamInput.cs new file mode 100644 index 0000000..47c1fff --- /dev/null +++ b/Runtime/Audio/AudioGraph/Structure/AudioStreamInput.cs @@ -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 _outputs = new List(); + + public List 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 ]; } + } + + } + + +} \ No newline at end of file diff --git a/Runtime/Audio/AudioGraph/Structure/AudioStreamOutput.cs b/Runtime/Audio/AudioGraph/Structure/AudioStreamOutput.cs new file mode 100644 index 0000000..ad113d3 --- /dev/null +++ b/Runtime/Audio/AudioGraph/Structure/AudioStreamOutput.cs @@ -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 _inputs = new List(); + + public List 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; + } + + + + + } + +} \ No newline at end of file diff --git a/Runtime/Audio/AudioGraph/Test/SineWaveTest.cs b/Runtime/Audio/AudioGraph/Test/SineWaveTest.cs new file mode 100644 index 0000000..2e3ac9c --- /dev/null +++ b/Runtime/Audio/AudioGraph/Test/SineWaveTest.cs @@ -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( rest ); + } + + List frameBuffer = new List(); + + 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; + + } + } +} \ No newline at end of file diff --git a/Runtime/Audio/MathAudio.cs b/Runtime/Audio/MathAudio.cs new file mode 100644 index 0000000..b9a4eec --- /dev/null +++ b/Runtime/Audio/MathAudio.cs @@ -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 ); + } + + } +} \ No newline at end of file diff --git a/Runtime/Colors/ColorX.cs b/Runtime/Colors/ColorX.cs index 643e4a2..1e7f504 100644 --- a/Runtime/Colors/ColorX.cs +++ b/Runtime/Colors/ColorX.cs @@ -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 ); + } + } } \ No newline at end of file diff --git a/Runtime/Files/FilePath.cs b/Runtime/Files/FilePath.cs index c802115..fe2e722 100644 --- a/Runtime/Files/FilePath.cs +++ b/Runtime/Files/FilePath.cs @@ -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 ); diff --git a/Runtime/GDScript/GDScriptNames.cs b/Runtime/GDScript/GDScriptNames.cs new file mode 100644 index 0000000..552246e --- /dev/null +++ b/Runtime/GDScript/GDScriptNames.cs @@ -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(); + } + + + } + +} \ No newline at end of file diff --git a/Runtime/Godot/CachedResource.cs b/Runtime/Godot/CachedResource.cs new file mode 100644 index 0000000..4720d85 --- /dev/null +++ b/Runtime/Godot/CachedResource.cs @@ -0,0 +1,38 @@ +using Godot; +using System.Text; +using System.Collections.Generic; +using System; + +namespace Rokojori +{ + public class CachedResource where T:Resource + { + protected T _resource; + protected string _path; + public string path => _path; + protected Func _converter; + + public CachedResource( string path, Func converter = null ) + { + _path = path; + _converter = converter; + } + + public T Get() + { + if ( _resource == null ) + { + + _resource = _converter == null ? + ResourceLoader.Load( _path ) : + _converter( ResourceLoader.Load( _path ) ); + + RJLog.Log( _path, _resource ); + + + } + + return _resource; + } + } +} \ No newline at end of file diff --git a/Runtime/Godot/Editor/GodotNodeData.cs b/Runtime/Godot/Editor/GodotNodeData.cs index 419c334..5d8d9b4 100644 --- a/Runtime/Godot/Editor/GodotNodeData.cs +++ b/Runtime/Godot/Editor/GodotNodeData.cs @@ -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; } diff --git a/Runtime/Godot/Editor/TransformChange.cs b/Runtime/Godot/Editor/TransformChange.cs new file mode 100644 index 0000000..fa50f39 --- /dev/null +++ b/Runtime/Godot/Editor/TransformChange.cs @@ -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; + } + + } +} \ No newline at end of file diff --git a/Runtime/Godot/HierarchyName.cs b/Runtime/Godot/HierarchyName.cs index 9a80531..594af31 100644 --- a/Runtime/Godot/HierarchyName.cs +++ b/Runtime/Godot/HierarchyName.cs @@ -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(); diff --git a/Runtime/Godot/Nodes.cs b/Runtime/Godot/Nodes.cs index e2bdfdf..91359a7 100644 --- a/Runtime/Godot/Nodes.cs +++ b/Runtime/Godot/Nodes.cs @@ -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 AllIn( Node root, Func filter = null, bool includeRoot = true) where T:class { var list = new List(); + ForEach( 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( this Node parent, string name = null ) where T:Node,new() { return CreateChildIn( 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 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( this Node parent, System.Action action ) where T:Node + { + ForEachDirectChild( parent, action ); + } + + public static int NumDirectChildrenOf( 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( 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( Node parent, System.Action action ) where T:Node { if ( parent == null || action == null ) diff --git a/Runtime/Interactions/CharacterController/CharacterController.cs b/Runtime/Interactions/CharacterController/CharacterController.cs index 7a69e71..510dc25 100644 --- a/Runtime/Interactions/CharacterController/CharacterController.cs +++ b/Runtime/Interactions/CharacterController/CharacterController.cs @@ -50,11 +50,13 @@ namespace Rokojori if ( CharacterUpdateMode.Process == characterUpdateMode ) { this.delta = (float)delta; - Nodes.ForEach( actionsContainer, a => Actions.Trigger( a ) ); + Nodes.ForEachDirectChild( 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 ) diff --git a/Runtime/Interactions/CharacterController/CharacterControllerAction.cs b/Runtime/Interactions/CharacterController/CharacterControllerAction.cs index 1be0101..1af4526 100644 --- a/Runtime/Interactions/CharacterController/CharacterControllerAction.cs +++ b/Runtime/Interactions/CharacterController/CharacterControllerAction.cs @@ -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 ); } } diff --git a/Runtime/Interactions/CharacterController/CharacterMovement.cs b/Runtime/Interactions/CharacterController/CharacterMovement.cs index 43aee2b..80d31f2 100644 --- a/Runtime/Interactions/CharacterController/CharacterMovement.cs +++ b/Runtime/Interactions/CharacterController/CharacterMovement.cs @@ -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 ); } } diff --git a/Runtime/Interactions/CharacterController/Gravity.cs b/Runtime/Interactions/CharacterController/Gravity.cs new file mode 100644 index 0000000..67a960f --- /dev/null +++ b/Runtime/Interactions/CharacterController/Gravity.cs @@ -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 ); + } + } +} \ No newline at end of file diff --git a/Runtime/Interactions/CharacterController/GroundReset.cs b/Runtime/Interactions/CharacterController/GroundReset.cs new file mode 100644 index 0000000..4213057 --- /dev/null +++ b/Runtime/Interactions/CharacterController/GroundReset.cs @@ -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 ); + } + } +} \ No newline at end of file diff --git a/Runtime/Interactions/CharacterController/Jump.cs b/Runtime/Interactions/CharacterController/Jump.cs new file mode 100644 index 0000000..4c151c7 --- /dev/null +++ b/Runtime/Interactions/CharacterController/Jump.cs @@ -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 ); + } + + + } + } +} \ No newline at end of file diff --git a/Runtime/Interactions/CharacterController/MoveAndSlide.cs b/Runtime/Interactions/CharacterController/MoveAndSlide.cs index 3d4f099..9efb292 100644 --- a/Runtime/Interactions/CharacterController/MoveAndSlide.cs +++ b/Runtime/Interactions/CharacterController/MoveAndSlide.cs @@ -10,6 +10,7 @@ namespace Rokojori { public override void _OnTrigger() { + // RJLog.Log( controller.body.Velocity ); controller.body.MoveAndSlide(); } } diff --git a/Runtime/LOD/LODArrangement.cs b/Runtime/LOD/LODArrangement.cs new file mode 100644 index 0000000..5ba2b7d --- /dev/null +++ b/Runtime/LOD/LODArrangement.cs @@ -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; + } + } +} \ No newline at end of file diff --git a/Runtime/LOD/LODCameraDistanceRule.cs b/Runtime/LOD/LODCameraDistanceRule.cs new file mode 100644 index 0000000..bf15ce0 --- /dev/null +++ b/Runtime/LOD/LODCameraDistanceRule.cs @@ -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; + } + } + +} \ No newline at end of file diff --git a/Runtime/LOD/LODCameraPitchRule.cs b/Runtime/LOD/LODCameraPitchRule.cs new file mode 100644 index 0000000..137d430 --- /dev/null +++ b/Runtime/LOD/LODCameraPitchRule.cs @@ -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; + } + } +} \ No newline at end of file diff --git a/Runtime/LOD/LODCameraYawRule.cs b/Runtime/LOD/LODCameraYawRule.cs new file mode 100644 index 0000000..9d0c3f8 --- /dev/null +++ b/Runtime/LOD/LODCameraYawRule.cs @@ -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; + } + } +} \ No newline at end of file diff --git a/Runtime/LOD/LODLevel.cs b/Runtime/LOD/LODLevel.cs new file mode 100644 index 0000000..e319ed8 --- /dev/null +++ b/Runtime/LOD/LODLevel.cs @@ -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; + } + } +} \ No newline at end of file diff --git a/Runtime/LOD/LODLevelVisibilityRule.cs b/Runtime/LOD/LODLevelVisibilityRule.cs new file mode 100644 index 0000000..cb027f5 --- /dev/null +++ b/Runtime/LOD/LODLevelVisibilityRule.cs @@ -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; + } + } +} \ No newline at end of file diff --git a/Runtime/LOD/LODMultiMesh.cs b/Runtime/LOD/LODMultiMesh.cs new file mode 100644 index 0000000..d7a3df2 --- /dev/null +++ b/Runtime/LOD/LODMultiMesh.cs @@ -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; + } + } +} \ No newline at end of file diff --git a/Runtime/LOD/LODNode.cs b/Runtime/LOD/LODNode.cs new file mode 100644 index 0000000..4d670bd --- /dev/null +++ b/Runtime/LOD/LODNode.cs @@ -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; + } + } + + + + + } +} \ No newline at end of file diff --git a/Runtime/LOD/LODParent.cs b/Runtime/LOD/LODParent.cs index cf8c5f5..43ef97b 100644 --- a/Runtime/LOD/LODParent.cs +++ b/Runtime/LOD/LODParent.cs @@ -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; + } + } } \ No newline at end of file diff --git a/Runtime/LOD/MultiMeshGenerator.cs b/Runtime/LOD/MultiMeshGenerator.cs new file mode 100644 index 0000000..99bddb8 --- /dev/null +++ b/Runtime/LOD/MultiMeshGenerator.cs @@ -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.XZfromNode3D( blockSize, blockSize ); + + this.LogInfo( "Grid Created", grid ); + + var instances = Nodes.AllIn( source ); + + this.LogInfo( "Grabbed instances", instances.Count ); + + grid.AddAll( instances ); + + this.LogInfo( "Added instance to grid" ); + + + grid.ForEachCell( + ( cellID, list ) => + { + var meshList = new MapList(); + + 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( "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.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( list[ 0 ] ) ); + + cIDCount++; + } + ); + } + ); + + + } + } +} diff --git a/Runtime/Logging/RJLog.cs b/Runtime/Logging/RJLog.cs index 05c19b1..44cdefc 100644 --- a/Runtime/Logging/RJLog.cs +++ b/Runtime/Logging/RJLog.cs @@ -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(); diff --git a/Runtime/Math/FFT.cs b/Runtime/Math/FFT.cs new file mode 100644 index 0000000..910daba --- /dev/null +++ b/Runtime/Math/FFT.cs @@ -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 ); + } + + + } + + +} \ No newline at end of file diff --git a/Runtime/Math/Geometry/Box2.cs b/Runtime/Math/Geometry/Box2.cs index ed277b2..cf95867 100644 --- a/Runtime/Math/Geometry/Box2.cs +++ b/Runtime/Math/Geometry/Box2.cs @@ -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 ) diff --git a/Runtime/Math/Geometry/Line2.cs b/Runtime/Math/Geometry/Line2.cs index 8eedf36..43fe149 100644 --- a/Runtime/Math/Geometry/Line2.cs +++ b/Runtime/Math/Geometry/Line2.cs @@ -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; + } } } \ No newline at end of file diff --git a/Runtime/Math/Geometry/Plane3.cs b/Runtime/Math/Geometry/Plane3.cs index 8bcbf2d..f6b056b 100644 --- a/Runtime/Math/Geometry/Plane3.cs +++ b/Runtime/Math/Geometry/Plane3.cs @@ -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; diff --git a/Runtime/Math/Geometry/Pose.cs b/Runtime/Math/Geometry/Pose.cs index bba7b26..9e109ed 100644 --- a/Runtime/Math/Geometry/Pose.cs +++ b/Runtime/Math/Geometry/Pose.cs @@ -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 poses, List weights = null ) { diff --git a/Runtime/Math/Geometry/Sphere.cs b/Runtime/Math/Geometry/Sphere.cs index eada61d..52333ae 100644 --- a/Runtime/Math/Geometry/Sphere.cs +++ b/Runtime/Math/Geometry/Sphere.cs @@ -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 ); } diff --git a/Runtime/Math/Geometry/Triangle2.cs b/Runtime/Math/Geometry/Triangle2.cs new file mode 100644 index 0000000..7837784 --- /dev/null +++ b/Runtime/Math/Geometry/Triangle2.cs @@ -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(); + + 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 ); + } + + + + } +} \ No newline at end of file diff --git a/Runtime/Math/Geometry/Triangle3.cs b/Runtime/Math/Geometry/Triangle3.cs index 4f0059c..ea666a4 100644 --- a/Runtime/Math/Geometry/Triangle3.cs +++ b/Runtime/Math/Geometry/Triangle3.cs @@ -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 ); } + } } \ No newline at end of file diff --git a/Runtime/Math/Geometry/TriangleTest.cs b/Runtime/Math/Geometry/TriangleTest.cs new file mode 100644 index 0000000..f5dda0d --- /dev/null +++ b/Runtime/Math/Geometry/TriangleTest.cs @@ -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; + } + + } +} diff --git a/Runtime/Math/Math2D.cs b/Runtime/Math/Math2D.cs index 2003cd9..7afa250 100644 --- a/Runtime/Math/Math2D.cs +++ b/Runtime/Math/Math2D.cs @@ -8,6 +8,21 @@ namespace Rokojori { public class Math2D { + public static float LookingAtEachOtherAngle( Vector2 lookDirectionA, Vector2 lookDirectionB ) + { + return Dot( lookDirectionA, lookDirectionB ); + } + + public static bool LookingAtEachOther( Vector2 lookDirectionA, Vector2 lookDirectionB ) + { + return LookingAtEachOtherAngle( lookDirectionA, lookDirectionB ) > 0; + } + + public static bool LookingTowards( Vector2 from, Vector2 fromDirection, Vector2 to ) + { + return LookingAtEachOther( fromDirection.Normalized(), ( to - from ).Normalized() ); + } + public static float Dot( Vector2 a, Vector2 b ) { return a.Dot( b ); diff --git a/Runtime/Math/Math3D.cs b/Runtime/Math/Math3D.cs index f3b7bf8..e254b32 100644 --- a/Runtime/Math/Math3D.cs +++ b/Runtime/Math/Math3D.cs @@ -8,19 +8,46 @@ namespace Rokojori { public static class Math3D { + public static float LookingAtEachOtherAngle( Vector3 lookDirectionA, Vector3 lookDirectionB ) + { + return Dot( lookDirectionA, lookDirectionB ); + } + + public static bool LookingAtEachOther( Vector3 lookDirectionA, Vector3 lookDirectionB ) + { + return LookingAtEachOtherAngle( lookDirectionA, lookDirectionB ) > 0; + } + + public static bool LookingTowards( Vector3 from, Vector3 fromDirection, Vector3 to ) + { + return LookingAtEachOther( fromDirection, to - from ); + } + + public static Transform3D TRS( Vector3 translation, Quaternion rotation, Vector3 scale ) { var trsf = new Transform3D( new Basis( rotation ), translation ); - trsf.ScaledLocal( scale ); - - return trsf; + return trsf.ScaledLocal( scale ); } + public static Transform3D TRS( Vector3 translation, Vector4 rotation, Vector3 scale ) + { + var quaternion = new Quaternion( rotation.X, rotation.Y, rotation.Z, rotation.W ); + + return TRS( translation, quaternion.Normalized(), scale ); + } + + public static Transform3D TRS( Pose pose, Vector3 scale ) { return TRS( pose.position, pose.rotation, scale ); } + public static Vector4 QuaternionToVector4( Quaternion rotation ) + { + return new Vector4( rotation.X, rotation.Y, rotation.Z, rotation.W ); + } + public static Vector3 Clamp01( Vector3 v ) { return new Vector3( @@ -120,6 +147,23 @@ namespace Rokojori return center / points.Length; } + public static Vector3 Center( List points ) where N:Node3D + { + var center = Vector3.Zero; + + if ( points.Count == 0 ) + { + return center; + } + + for ( int i = 0; i < points.Count; i++ ) + { + center += points[ i ].GlobalPosition; + } + + return center / points.Count; + } + public static Vector3 Center( List points ) { var center = Vector3.Zero; @@ -235,6 +279,13 @@ namespace Rokojori return v; } + public static Basis AlignUp( Basis basis, Vector3 upDirection ) + { + basis.Y = upDirection; + basis.X = - basis.Z.Cross( upDirection ); + return basis.Orthonormalized(); + } + public static Quaternion AlignUp( Quaternion rotation, Vector3 upDirection ) { var basis = new Basis( rotation ); @@ -249,6 +300,13 @@ namespace Rokojori return AlignUp( quaternion, upDirection ); } + public static Quaternion AlignUpFromDirections( Vector3 upFrom, Vector3 upTo ) + { + var fromRotation = AlignUp( upFrom ); + + return AlignUp( fromRotation, upTo ); + } + public static float AngleXY( Vector3 direction ) { return Mathf.Atan2( direction.Y, direction.X ); @@ -267,19 +325,24 @@ namespace Rokojori return new Vector3( 0, y, z ); } - public static Basis AlignUp( Basis basis, Vector3 upDirection ) - { - basis.Y = upDirection; - basis.X = - basis.Z.Cross( upDirection ); - return basis.Orthonormalized(); - } + public static Vector3 Lerp( Vector3 a, Vector3 b, float lerp ) { return a.Lerp( b, lerp ); } - public static Quaternion LookRotation( Vector3 direction, Vector3 up ) + public static Quaternion LookRotation( Vector3 direction, bool useModelFront = false ) + { + if ( direction.Normalized() == Vector3.Up ) + { + return LookRotation( direction, Vector3.Back, useModelFront ); + } + + return LookRotation( direction, Vector3.Up, useModelFront ); + } + + public static Quaternion LookRotation( Vector3 direction, Vector3 up, bool useModelFront = false ) { if ( direction.Length() == 0 ) { @@ -290,7 +353,7 @@ namespace Rokojori t.Basis = Basis.Identity; t.Origin = Vector3.Zero; - t = t.LookingAt( direction, up ); + t = t.LookingAt( direction, up, useModelFront ); return t.Basis.GetRotationQuaternion(); } @@ -343,7 +406,20 @@ namespace Rokojori public static Quaternion YawPitchRotation( float yaw, float pitch ) { - return RotateXDegrees( pitch ) * RotateYDegrees( yaw ); + return RotateYDegrees( yaw ) * RotateXDegrees( pitch ); + } + + public static float GlobalYaw( Vector3 direction ) + { + return Mathf.Atan2( direction.Z, direction.X ); + } + + public static float GlobalPitch( Vector3 direction ) + { + var xz = new Vector2( direction.X, direction.Z ).Length(); + var y = direction.Y; + + return Mathf.Atan2( y, xz ); } public static void SetGlobalQuaternion( this Node3D node, Quaternion quaternion ) @@ -391,6 +467,12 @@ namespace Rokojori return Quaternion.Identity.Slerp( q, fraction ); } + public static Vector3 GetGlobalOffset( this Node3D node, Vector3 direction ) + { + return direction.X * node.GlobalRight() + + direction.Y * node.GlobalUp() + + direction.Z * node.GlobalForward() ; + } public static Vector3 GlobalForward( this Node3D node ) { diff --git a/Runtime/Math/MathX.cs b/Runtime/Math/MathX.cs index 0c12d35..8685ef8 100644 --- a/Runtime/Math/MathX.cs +++ b/Runtime/Math/MathX.cs @@ -133,6 +133,12 @@ namespace Rokojori public static float Clamp01( float value ) { return Mathf.Clamp( value, 0, 1 ); + } + + public static float RemapClamped( float value, float inMin, float inMax, float outMin, float outMax ) + { + var mapped = MathX.Clamp01( ( value - inMin ) / ( inMax - inMin ) ); + return mapped * ( outMax - outMin ) + outMin; } public static float Normalize( float value, float min, float max ) @@ -180,6 +186,13 @@ namespace Rokojori return value * 0.5f + 0.5f; } + public static int NextPowerOfTwo( int num ) + { + var p = Exponent( 2, num ); + + return Mathf.CeilToInt( p ); + } + public static float Repeat( float value, float length ) { while ( value > length ) @@ -309,7 +322,7 @@ namespace Rokojori return value; } - public static Curve Curve( float y0, float y1 ) + public static Curve Curve( float y0, float y1, float minValue = 0, float maxValue = 1 ) { var curve = new Curve(); curve.AddPoint( new Vector2( 0, y0 ) ); @@ -318,6 +331,9 @@ namespace Rokojori curve.SetPointRightMode( 0, Godot.Curve.TangentMode.Linear ); curve.SetPointLeftMode( 1, Godot.Curve.TangentMode.Linear ); + curve.MinValue = minValue; + curve.MaxValue = maxValue; + return curve; } diff --git a/Runtime/Math/RangeDouble.cs b/Runtime/Math/RangeDouble.cs index 2fbc947..ffd4c83 100644 --- a/Runtime/Math/RangeDouble.cs +++ b/Runtime/Math/RangeDouble.cs @@ -47,6 +47,26 @@ namespace Rokojori return false; } + public bool IsSmaller( RangeDouble other ) + { + if ( Overlaps( other ) ) + { + return false; + } + + return max < other.min; + } + + public bool IsBigger( RangeDouble other ) + { + if ( Overlaps( other ) ) + { + return false; + } + + return min > other.max; + } + public double center { get { return 0.5f * ( max + min ); } } public double range { get { return max - min; } } public double length { get { return max - min; } } diff --git a/Runtime/Networking/NetworkNode.cs b/Runtime/Networking/NetworkNode.cs new file mode 100644 index 0000000..548e85a --- /dev/null +++ b/Runtime/Networking/NetworkNode.cs @@ -0,0 +1,15 @@ + +using System.Diagnostics; +using System.Collections; +using System.Collections.Generic; +using System; +using Godot; + + +namespace Rokojori +{ + public partial class NetworkNode:Node + { + + } +} \ No newline at end of file diff --git a/Runtime/Paths.cs b/Runtime/Paths.cs new file mode 100644 index 0000000..c367469 --- /dev/null +++ b/Runtime/Paths.cs @@ -0,0 +1,16 @@ + +using System.Diagnostics; +using System.Collections; +using System.Collections.Generic; +using System; +using Godot; + + +namespace Rokojori +{ + public class Paths + { + public static readonly string AddonLibraryPath = "res://addons/rokojori_action_library/"; + public static readonly string RuntimePath = "res://addons/rokojori_action_library/Runtime/"; + } +} \ No newline at end of file diff --git a/Runtime/Procedural/Assets/Grass/GrassPatch.cs b/Runtime/Procedural/Assets/Grass/GrassPatch.cs index ae3a4b0..8eee333 100644 --- a/Runtime/Procedural/Assets/Grass/GrassPatch.cs +++ b/Runtime/Procedural/Assets/Grass/GrassPatch.cs @@ -24,6 +24,8 @@ namespace Rokojori [Export] public bool updateAlways; + [ExportGroup( "Blades")] + [Export( PropertyHint.Range, "0,100")] public int blades = 20; @@ -36,6 +38,7 @@ namespace Rokojori [Export] public int X_numBlades; + [ExportGroup( "Patch")] [Export] public float patchSize = 2; @@ -48,12 +51,17 @@ namespace Rokojori [Export] public Vector2 patchOffsetPosition = new Vector2( 0.5f, 0.5f ); + + [ExportGroup( "Blade Triangles")] [Export( PropertyHint.Range, "1,20")] public int bladeSegments = 3; [Export] public bool createBackFaces = true; + [Export] + public bool allowTrianglesOnEnds = true; + [Export] public int X_numTriangles; @@ -61,6 +69,7 @@ namespace Rokojori [Export] public Curve bladeSegmentMapping = MathX.Curve( 0, 1 ); + [ExportGroup( "Blade Segmentation")] [Export] public int uvSegmentColumns = 1; @@ -73,6 +82,7 @@ namespace Rokojori [Export] public Curve uvSegmentWeightsFar = null; + [ExportGroup( "Blade Shape")] [Export] public Curve bladeScale = MathX.Curve( 1f ); @@ -94,20 +104,24 @@ namespace Rokojori [Export] public Curve bladeBending2 = null; + [ExportGroup( "Blade Offset & Scale")] + [Export] public Curve positionJitter = MathX.Curve( 0.05f ); + [Export] + public Curve positionJitterX = MathX.Curve( 0.05f ); + + [Export] + public Curve positionJitterZ = MathX.Curve( 0.05f ); + [Export] public Curve scaleByDistanceX = MathX.Curve( 1f ); [Export] public Curve scaleByDistanceZ = MathX.Curve( 1f ); - [Export] - public Curve normalBlending = MathX.Curve( 0.5f ); - - [Export] - public Vector3 normalBlendingDirection = Vector3.Up; + [ExportGroup( "Blade Rotation")] [Export] public Curve yawRotation = MathX.Curve( 0f, 1f ); @@ -115,6 +129,17 @@ namespace Rokojori [Export] public Curve randomRotation = MathX.Curve( 0f, 20f ); + + [ExportGroup( "Blade Normals")] + [Export] + public Curve normalBlending = MathX.Curve( 0.5f ); + + [Export] + public Vector3 normalBlendingDirection = Vector3.Up; + + + + [ExportGroup( "Blade Filter")] [Export (PropertyHint.Range, "0,1")] public float filterTreshold = 1; @@ -127,6 +152,13 @@ namespace Rokojori [Export] public Vector2 positionToFilter = new Vector2( 0, 0 ); + [ExportGroup("LOD Levels")] + [Export] + public GrassPatchLODLevel[] lodLevels; + + [Export] + public int currentLODLevel = -1; + SerializedGodotObject _cached; public override void _Process( double delta ) @@ -161,8 +193,37 @@ namespace Rokojori public void CreatePatch() { + if ( blades == 0 && bladesX == 0 && bladesZ == 0) + { + return; + } + + if ( blades == 0 ) + { + if ( bladesX < bladesZ ) + { + bladesX = Mathf.Max( 1, bladesX ); + } + else + { + bladesZ = Mathf.Max( 1, bladesZ ); + } + } + + if ( lodLevels == null ) + { + lodLevels = new GrassPatchLODLevel[]{}; + } + + if ( output == null ) + { + this.output = this.CreateChild( "Grass Patch Mesh" ); + } + + var mg = new MeshGeometry(); + var cellSizeX = ( patchSizeX + patchSize ) / ( bladesX + blades ); var cellSizeZ = ( patchSizeZ + patchSize ) / ( bladesZ + blades ); @@ -196,6 +257,8 @@ namespace Rokojori var rotationX = random.Next() * rotationOther; var rotationZ = rotationOther - rotationX; var positionOffset = Math3D.OnCircleXZ( random.Next() * Mathf.Pi * 2f ) * random.Sample( positionJitter ); + positionOffset.X += random.Sample( positionJitterX ); + positionOffset.Z += random.Sample( positionJitterZ ); var worldPosition = position + _patchOffset + positionOffset; var filterValue = Noise.PerlinXZ( Math3D.XYasXZ( filterScale ) * worldPosition + Math3D.XYasXZ( filterOffset ) + -GlobalPosition * Math3D.XYasXZ( positionToFilter )); @@ -211,6 +274,18 @@ namespace Rokojori { continue; } + + var cL = Mathf.Clamp( currentLODLevel, -1, lodLevels.Length - 1 ); + + if ( cL != -1 ) + { + var lodFilterTreshold = lodLevels[ cL ].filter; + + if ( random.Next() >= lodFilterTreshold ) + { + continue; + } + } bladeMG.ApplyTransform( trsf ); @@ -243,8 +318,23 @@ namespace Rokojori output.Mesh = mg.GenerateMesh(); } + MeshGeometry CreateBlade( RandomEngine random, Vector3 position ) { + var bladeSegments = this.bladeSegments; + + var currentLodLevel = Mathf.Clamp( currentLODLevel, -1, lodLevels.Length - 1 ); + + var bladeScaleMultiplier = 1f; + var bladeWidthMultiplier = 1f; + + if ( currentLodLevel != -1 ) + { + bladeSegments = lodLevels[ currentLodLevel ].bladeSegments; + bladeScaleMultiplier = lodLevels[ currentLodLevel ].bladeScaleMultiplier; + bladeWidthMultiplier = lodLevels[ currentLodLevel].bladeWidthMultiplier; + } + var bmg = new MeshGeometry(); @@ -259,6 +349,8 @@ namespace Rokojori var minDistance = distancesToCenter.Length(); var scaling = scaleByDistanceX.Sample( distancesToCenter.X ) * scaleByDistanceZ.Sample( distancesToCenter.Y ); scaling *= random.Sample( bladeScale ); + scaling *= bladeScaleMultiplier; + size *= scaling; var firstIsTriangle = false; var lastIsTriangle = false; @@ -303,22 +395,19 @@ namespace Rokojori uvMin.Y = y * ySize; uvMax = uvMin + new Vector2( xSize, ySize ); - - // if ( X_numBlades < 6 ) - // { - // RJLog.Log( - - // " c,r:",uvSegmentColumns,uvSegmentRows, - // " index:", index, - // " x,y:", x,y, - // " xS,yS:",xSize,ySize, - // " uvMin:", uvMin, - // " uvMax:", uvMax - - // ); - // } } + var endingTriangles = allowTrianglesOnEnds; + + if ( currentLodLevel != -1 ) + { + var useTris = lodLevels[ currentLodLevel ].allowTrianglesOnEnd; + + endingTriangles = TrilleanLogic.ToBool( useTris, endingTriangles ); + } + + + for ( int i = 0; i <= bladeSegments; i++ ) { var t = (float)i / bladeSegments; @@ -331,12 +420,13 @@ namespace Rokojori var bb2 = bladeBending2 == null ? bladeBending : bladeBending2; var width = Mathf.Lerp( bladeWidth.Sample( v ), bw2.Sample( v ), bladeWidthLerp ); - width *= scaling; + width *= scaling * bladeWidthMultiplier; var bending = Mathf.Lerp( bladeBending.Sample( v ), bb2.Sample( v ), bladeBendLerp ) * Vector3.Forward.Z * scaling; var bendingNormalAngle = Mathf.LerpAngle( MathX.CurveAngle( bladeBending, v ), MathX.CurveAngle( bb2, v ), bladeBendLerp ); var bendingNormal = Math3D.NormalAngle( bendingNormalAngle ); + - if ( width < 0.005f && ( i == 0 || ( i == bladeSegments - 1 )) ) + if ( endingTriangles && width < 0.005f && ( i == 0 || ( i == bladeSegments - 1 )) ) { bmg.vertices.Add( new Vector3( 0, y, bending ) ); bmg.normals.Add( bendingNormal ); diff --git a/Runtime/Procedural/Assets/Grass/GrassPatchLODLevel.cs b/Runtime/Procedural/Assets/Grass/GrassPatchLODLevel.cs new file mode 100644 index 0000000..e073509 --- /dev/null +++ b/Runtime/Procedural/Assets/Grass/GrassPatchLODLevel.cs @@ -0,0 +1,30 @@ +using System.Collections; +using System.Collections.Generic; +using Godot; +using System; + + + +namespace Rokojori +{ + [Tool] + [GlobalClass] + public partial class GrassPatchLODLevel:Resource + { + [Export] + public int bladeSegments = 3; + + [Export] + public float bladeScaleMultiplier = 1; + + [Export] + public float bladeWidthMultiplier = 1; + + [Export( PropertyHint.Range, "0,1")] + public float filter = 1; + + [Export] + public Trillean allowTrianglesOnEnd; + + } +} \ No newline at end of file diff --git a/Runtime/Procedural/Baking/Baker.cs b/Runtime/Procedural/Baking/Baker.cs index 6a8b3f1..6f42829 100644 --- a/Runtime/Procedural/Baking/Baker.cs +++ b/Runtime/Procedural/Baking/Baker.cs @@ -80,13 +80,30 @@ namespace Rokojori [Export] public float outputTextureSize = 1; + public enum RotationMode + { + Yaw_Pitch, + Quaternion + } + + [ExportGroup("Rotation")] + + [Export] + public RotationMode rotationMode = RotationMode.Yaw_Pitch; + [Export( PropertyHint.Range, "-180,180")] public float yaw = 0; [Export( PropertyHint.Range, "-180,180")] public float pitch = 0; - public Quaternion bakingRotation => Math3D.YawPitchRotation( yaw, pitch ); + [Export] + public Quaternion rotationQuaternion; + + + public Quaternion bakingRotation => RotationMode.Yaw_Pitch == rotationMode ? + Math3D.YawPitchRotation( yaw, pitch ) : + rotationQuaternion; [Export] public float zoom = 1; @@ -146,16 +163,16 @@ namespace Rokojori if ( newDistance != distance ) { distance = newDistance; - RJLog.Log( "New Distance:", box, sphere, distance ); + // RJLog.Log( "New Distance:", box, sphere, distance ); } - var cameraRotation = Math3D.RotateXDegrees( pitch ) * Math3D.RotateYDegrees( yaw ); + var cameraRotation = bakingRotation; var offset = ( Vector3.Back * distance ) * cameraRotation ; camera.GlobalPosition = target.GlobalPosition + offset; camera.SetGlobalQuaternion( cameraRotation.Inverse() ); - RJLog.Log( "Set Rotation", cameraRotation, ">>", camera.GetGlobalQuaternion() ); + // RJLog.Log( "Set Rotation", cameraRotation, ">>", camera.GetGlobalQuaternion() ); outputScale = Cameras.ComputeCameraFittingScale( camera.Fov, distance ); diff --git a/Runtime/Procedural/Baking/BakingMaterialMode.cs b/Runtime/Procedural/Baking/BakingMaterialMode.cs new file mode 100644 index 0000000..11ad3e9 --- /dev/null +++ b/Runtime/Procedural/Baking/BakingMaterialMode.cs @@ -0,0 +1,19 @@ +using System.Collections; +using System.Collections.Generic; +using Godot; +using System; + + + +namespace Rokojori +{ + public enum BakingMaterialMode + { + Preview, + Albedo, + Normals, + ORM, + Depth + } + +} diff --git a/Runtime/Procedural/Baking/BakingMaterials/Albedo/Albedo From Standard.tres b/Runtime/Procedural/Baking/BakingMaterials/Albedo/Albedo From Standard.tres new file mode 100644 index 0000000..af3ae19 --- /dev/null +++ b/Runtime/Procedural/Baking/BakingMaterials/Albedo/Albedo From Standard.tres @@ -0,0 +1,61 @@ +[gd_resource type="Resource" script_class="SubMaterialTransfer" load_steps=16 format=3 uid="uid://dofm6e7wqggd5"] + +[ext_resource type="Resource" uid="uid://cwbo0avyyq0t" path="res://addons/rokojori_action_library/Runtime/Shading/Properties/Property Library/Color/Albedo Color.tres" id="1_v1mlf"] +[ext_resource type="Script" path="res://addons/rokojori_action_library/Runtime/Shading/Materials/Transfers/ColorPropertyTransfer.cs" id="2_8laus"] +[ext_resource type="Resource" uid="uid://bja8pltfjyt3x" path="res://addons/rokojori_action_library/Runtime/Shading/Properties/Property Library/Float/Alpha Scissor Threshold.tres" id="3_c5eok"] +[ext_resource type="Script" path="res://addons/rokojori_action_library/Runtime/Shading/Materials/SubMaterialTransfer.cs" id="3_lmjhi"] +[ext_resource type="Resource" uid="uid://dldbju3x0x2ow" path="res://addons/rokojori_action_library/Runtime/Shading/Properties/Property Library/Texture2D/Albedo Texture.tres" id="4_1ci3e"] +[ext_resource type="Script" path="res://addons/rokojori_action_library/Runtime/Shading/Materials/Transfers/FloatPropertyTransfer.cs" id="4_ht1tk"] +[ext_resource type="Script" path="res://addons/rokojori_action_library/Runtime/Shading/Materials/Transfers/Texture2DPropertyTransfer.cs" id="5_8463p"] +[ext_resource type="Resource" uid="uid://cn7nxc0qw7pan" path="res://addons/rokojori_action_library/Runtime/Shading/Properties/Property Library/Vector3/UV1 Offset.tres" id="8_78l5h"] +[ext_resource type="Script" path="res://addons/rokojori_action_library/Runtime/Shading/Materials/Transfers/Vector3PropertyTransfer.cs" id="9_sruv0"] +[ext_resource type="Resource" uid="uid://douianw6mx4p5" path="res://addons/rokojori_action_library/Runtime/Shading/Properties/Property Library/Vector3/UV1 Scale.tres" id="10_18hcy"] + +[sub_resource type="Resource" id="Resource_7u5wc"] +script = ExtResource("2_8laus") +from = ExtResource("1_v1mlf") +to = ExtResource("1_v1mlf") + +[sub_resource type="Resource" id="Resource_t22rd"] +script = ExtResource("4_ht1tk") +from = ExtResource("3_c5eok") +to = ExtResource("3_c5eok") + +[sub_resource type="Resource" id="Resource_17abg"] +script = ExtResource("5_8463p") +from = ExtResource("4_1ci3e") +to = ExtResource("4_1ci3e") + +[sub_resource type="Resource" id="Resource_cn00v"] +script = ExtResource("9_sruv0") +from = ExtResource("8_78l5h") +to = ExtResource("8_78l5h") + +[sub_resource type="Resource" id="Resource_ecqt0"] +script = ExtResource("9_sruv0") +from = ExtResource("10_18hcy") +to = ExtResource("10_18hcy") + +[resource] +script = ExtResource("3_lmjhi") +info = "Albedo From Standard" +canvasItem = false +fog = false +orm3D = true +standard3D = true +panoramaSky = false +particleProcess = false +physicalSky = false +placeholder = false +proceduralSky = false +shader = false +shaders = [] +bools = [] +colors = [SubResource("Resource_7u5wc")] +floats = [SubResource("Resource_t22rd")] +ints = [] +texture2D = [SubResource("Resource_17abg")] +vector2 = [] +vector3 = [SubResource("Resource_cn00v"), SubResource("Resource_ecqt0")] +vector4 = [] +custom = [] diff --git a/Runtime/Procedural/Baking/BakingMaterials/Albedo/Albedo Transfer.tres b/Runtime/Procedural/Baking/BakingMaterials/Albedo/Albedo Transfer.tres new file mode 100644 index 0000000..6a59428 --- /dev/null +++ b/Runtime/Procedural/Baking/BakingMaterials/Albedo/Albedo Transfer.tres @@ -0,0 +1,8 @@ +[gd_resource type="Resource" script_class="MaterialTransfer" load_steps=3 format=3 uid="uid://df4l75dnmduaf"] + +[ext_resource type="Script" path="res://addons/rokojori_action_library/Runtime/Shading/Materials/MaterialTransfer.cs" id="1_72s2u"] +[ext_resource type="Resource" uid="uid://dofm6e7wqggd5" path="res://addons/rokojori_action_library/Runtime/Procedural/Baking/BakingMaterials/Albedo/Albedo From Standard.tres" id="2_ss0ai"] + +[resource] +script = ExtResource("1_72s2u") +transfers = [ExtResource("2_ss0ai")] diff --git a/Runtime/Procedural/Baking/BakingMaterials/Depth/Depth From Standard.tres b/Runtime/Procedural/Baking/BakingMaterials/Depth/Depth From Standard.tres new file mode 100644 index 0000000..4e357a8 --- /dev/null +++ b/Runtime/Procedural/Baking/BakingMaterials/Depth/Depth From Standard.tres @@ -0,0 +1,61 @@ +[gd_resource type="Resource" script_class="SubMaterialTransfer" load_steps=16 format=3 uid="uid://crxw8r6q5uhpe"] + +[ext_resource type="Resource" uid="uid://cwbo0avyyq0t" path="res://addons/rokojori_action_library/Runtime/Shading/Properties/Property Library/Color/Albedo Color.tres" id="1_vhp84"] +[ext_resource type="Script" path="res://addons/rokojori_action_library/Runtime/Shading/Materials/Transfers/ColorPropertyTransfer.cs" id="2_awr2w"] +[ext_resource type="Resource" uid="uid://bja8pltfjyt3x" path="res://addons/rokojori_action_library/Runtime/Shading/Properties/Property Library/Float/Alpha Scissor Threshold.tres" id="3_baip0"] +[ext_resource type="Script" path="res://addons/rokojori_action_library/Runtime/Shading/Materials/Transfers/FloatPropertyTransfer.cs" id="4_r0n2g"] +[ext_resource type="Script" path="res://addons/rokojori_action_library/Runtime/Shading/Materials/SubMaterialTransfer.cs" id="5_iuqjk"] +[ext_resource type="Resource" uid="uid://dldbju3x0x2ow" path="res://addons/rokojori_action_library/Runtime/Shading/Properties/Property Library/Texture2D/Albedo Texture.tres" id="6_tcnva"] +[ext_resource type="Script" path="res://addons/rokojori_action_library/Runtime/Shading/Materials/Transfers/Texture2DPropertyTransfer.cs" id="7_pf381"] +[ext_resource type="Resource" uid="uid://cn7nxc0qw7pan" path="res://addons/rokojori_action_library/Runtime/Shading/Properties/Property Library/Vector3/UV1 Offset.tres" id="8_00usm"] +[ext_resource type="Script" path="res://addons/rokojori_action_library/Runtime/Shading/Materials/Transfers/Vector3PropertyTransfer.cs" id="9_sn3qs"] +[ext_resource type="Resource" uid="uid://douianw6mx4p5" path="res://addons/rokojori_action_library/Runtime/Shading/Properties/Property Library/Vector3/UV1 Scale.tres" id="10_5fqi2"] + +[sub_resource type="Resource" id="Resource_7u5wc"] +script = ExtResource("2_awr2w") +from = ExtResource("1_vhp84") +to = ExtResource("1_vhp84") + +[sub_resource type="Resource" id="Resource_t22rd"] +script = ExtResource("4_r0n2g") +from = ExtResource("3_baip0") +to = ExtResource("3_baip0") + +[sub_resource type="Resource" id="Resource_17abg"] +script = ExtResource("7_pf381") +from = ExtResource("6_tcnva") +to = ExtResource("6_tcnva") + +[sub_resource type="Resource" id="Resource_cn00v"] +script = ExtResource("9_sn3qs") +from = ExtResource("8_00usm") +to = ExtResource("8_00usm") + +[sub_resource type="Resource" id="Resource_ecqt0"] +script = ExtResource("9_sn3qs") +from = ExtResource("10_5fqi2") +to = ExtResource("10_5fqi2") + +[resource] +script = ExtResource("5_iuqjk") +info = "Depth From Standard" +canvasItem = false +fog = false +orm3D = true +standard3D = true +panoramaSky = false +particleProcess = false +physicalSky = false +placeholder = false +proceduralSky = false +shader = false +shaders = [] +bools = [] +colors = [SubResource("Resource_7u5wc")] +floats = [SubResource("Resource_t22rd")] +ints = [] +texture2D = [SubResource("Resource_17abg")] +vector2 = [] +vector3 = [SubResource("Resource_cn00v"), SubResource("Resource_ecqt0")] +vector4 = [] +custom = [] diff --git a/Runtime/Procedural/Baking/BakingMaterials/Depth/Depth Transfer.tres b/Runtime/Procedural/Baking/BakingMaterials/Depth/Depth Transfer.tres new file mode 100644 index 0000000..50d888b --- /dev/null +++ b/Runtime/Procedural/Baking/BakingMaterials/Depth/Depth Transfer.tres @@ -0,0 +1,8 @@ +[gd_resource type="Resource" script_class="MaterialTransfer" load_steps=3 format=3 uid="uid://b2rdhv8dq5k0d"] + +[ext_resource type="Script" path="res://addons/rokojori_action_library/Runtime/Shading/Materials/MaterialTransfer.cs" id="1_drmc6"] +[ext_resource type="Resource" uid="uid://crxw8r6q5uhpe" path="res://addons/rokojori_action_library/Runtime/Procedural/Baking/BakingMaterials/Depth/Depth From Standard.tres" id="2_o21o4"] + +[resource] +script = ExtResource("1_drmc6") +transfers = [ExtResource("2_o21o4")] diff --git a/Runtime/Procedural/Baking/BakingMaterials/Normal/Normal From Standard.tres b/Runtime/Procedural/Baking/BakingMaterials/Normal/Normal From Standard.tres new file mode 100644 index 0000000..cf8a475 --- /dev/null +++ b/Runtime/Procedural/Baking/BakingMaterials/Normal/Normal From Standard.tres @@ -0,0 +1,73 @@ +[gd_resource type="Resource" script_class="SubMaterialTransfer" load_steps=20 format=3 uid="uid://dgyihly620ymm"] + +[ext_resource type="Resource" uid="uid://cwbo0avyyq0t" path="res://addons/rokojori_action_library/Runtime/Shading/Properties/Property Library/Color/Albedo Color.tres" id="1_8s4ko"] +[ext_resource type="Script" path="res://addons/rokojori_action_library/Runtime/Shading/Materials/Transfers/ColorPropertyTransfer.cs" id="2_edgft"] +[ext_resource type="Resource" uid="uid://bja8pltfjyt3x" path="res://addons/rokojori_action_library/Runtime/Shading/Properties/Property Library/Float/Alpha Scissor Threshold.tres" id="3_8a5am"] +[ext_resource type="Script" path="res://addons/rokojori_action_library/Runtime/Shading/Materials/Transfers/FloatPropertyTransfer.cs" id="4_wnydu"] +[ext_resource type="Script" path="res://addons/rokojori_action_library/Runtime/Shading/Materials/SubMaterialTransfer.cs" id="5_atmxl"] +[ext_resource type="Resource" uid="uid://c6actnjj8i8o5" path="res://addons/rokojori_action_library/Runtime/Shading/Properties/Property Library/Float/Normal Scale.tres" id="5_iyqth"] +[ext_resource type="Resource" uid="uid://dldbju3x0x2ow" path="res://addons/rokojori_action_library/Runtime/Shading/Properties/Property Library/Texture2D/Albedo Texture.tres" id="6_l0r8p"] +[ext_resource type="Script" path="res://addons/rokojori_action_library/Runtime/Shading/Materials/Transfers/Texture2DPropertyTransfer.cs" id="7_l54r2"] +[ext_resource type="Resource" uid="uid://bmt10lgwm8ckx" path="res://addons/rokojori_action_library/Runtime/Shading/Properties/Property Library/Texture2D/Normal Texture.tres" id="9_fmy5n"] +[ext_resource type="Resource" uid="uid://cn7nxc0qw7pan" path="res://addons/rokojori_action_library/Runtime/Shading/Properties/Property Library/Vector3/UV1 Offset.tres" id="10_dw87i"] +[ext_resource type="Script" path="res://addons/rokojori_action_library/Runtime/Shading/Materials/Transfers/Vector3PropertyTransfer.cs" id="11_mpsiy"] +[ext_resource type="Resource" uid="uid://douianw6mx4p5" path="res://addons/rokojori_action_library/Runtime/Shading/Properties/Property Library/Vector3/UV1 Scale.tres" id="12_xnkjm"] + +[sub_resource type="Resource" id="Resource_7u5wc"] +script = ExtResource("2_edgft") +from = ExtResource("1_8s4ko") +to = ExtResource("1_8s4ko") + +[sub_resource type="Resource" id="Resource_t22rd"] +script = ExtResource("4_wnydu") +from = ExtResource("3_8a5am") +to = ExtResource("3_8a5am") + +[sub_resource type="Resource" id="Resource_4xt6c"] +script = ExtResource("4_wnydu") +from = ExtResource("5_iyqth") +to = ExtResource("5_iyqth") + +[sub_resource type="Resource" id="Resource_17abg"] +script = ExtResource("7_l54r2") +from = ExtResource("6_l0r8p") +to = ExtResource("6_l0r8p") + +[sub_resource type="Resource" id="Resource_0f0yq"] +script = ExtResource("7_l54r2") +from = ExtResource("9_fmy5n") +to = ExtResource("9_fmy5n") + +[sub_resource type="Resource" id="Resource_2y0hl"] +script = ExtResource("11_mpsiy") +from = ExtResource("10_dw87i") +to = ExtResource("10_dw87i") + +[sub_resource type="Resource" id="Resource_s484q"] +script = ExtResource("11_mpsiy") +from = ExtResource("12_xnkjm") +to = ExtResource("12_xnkjm") + +[resource] +script = ExtResource("5_atmxl") +info = "Normal From Standard" +canvasItem = false +fog = false +orm3D = true +standard3D = true +panoramaSky = false +particleProcess = false +physicalSky = false +placeholder = false +proceduralSky = false +shader = false +shaders = [] +bools = [] +colors = [SubResource("Resource_7u5wc")] +floats = [SubResource("Resource_t22rd"), SubResource("Resource_4xt6c")] +ints = [] +texture2D = [SubResource("Resource_17abg"), SubResource("Resource_0f0yq")] +vector2 = [] +vector3 = [SubResource("Resource_2y0hl"), SubResource("Resource_s484q")] +vector4 = [] +custom = [] diff --git a/Runtime/Procedural/Baking/BakingMaterials/Normal/Normal Transfer.tres b/Runtime/Procedural/Baking/BakingMaterials/Normal/Normal Transfer.tres new file mode 100644 index 0000000..bb42fd2 --- /dev/null +++ b/Runtime/Procedural/Baking/BakingMaterials/Normal/Normal Transfer.tres @@ -0,0 +1,8 @@ +[gd_resource type="Resource" script_class="MaterialTransfer" load_steps=3 format=3 uid="uid://h2ypphdx3inw"] + +[ext_resource type="Script" path="res://addons/rokojori_action_library/Runtime/Shading/Materials/MaterialTransfer.cs" id="1_0hco5"] +[ext_resource type="Resource" uid="uid://dgyihly620ymm" path="res://addons/rokojori_action_library/Runtime/Procedural/Baking/BakingMaterials/Normal/Normal From Standard.tres" id="2_cufcd"] + +[resource] +script = ExtResource("1_0hco5") +transfers = [ExtResource("2_cufcd")] diff --git a/Runtime/Procedural/Baking/BakingMaterials/ORM/ORM From Standard.tres b/Runtime/Procedural/Baking/BakingMaterials/ORM/ORM From Standard.tres new file mode 100644 index 0000000..cc67549 --- /dev/null +++ b/Runtime/Procedural/Baking/BakingMaterials/ORM/ORM From Standard.tres @@ -0,0 +1,113 @@ +[gd_resource type="Resource" script_class="SubMaterialTransfer" load_steps=36 format=3 uid="uid://kwk45f3p6ic4"] + +[ext_resource type="Resource" uid="uid://cwbo0avyyq0t" path="res://addons/rokojori_action_library/Runtime/Shading/Properties/Property Library/Color/Albedo Color.tres" id="1_b7tdx"] +[ext_resource type="Script" path="res://addons/rokojori_action_library/Runtime/Shading/Materials/Transfers/ColorPropertyTransfer.cs" id="2_vwrue"] +[ext_resource type="Resource" uid="uid://txmti1ghef1" path="res://addons/rokojori_action_library/Runtime/Shading/Properties/Property Library/TextureChannels/AO Texture Channel.tres" id="3_rkwtk"] +[ext_resource type="Resource" uid="uid://bja8pltfjyt3x" path="res://addons/rokojori_action_library/Runtime/Shading/Properties/Property Library/Float/Alpha Scissor Threshold.tres" id="3_u377f"] +[ext_resource type="Script" path="res://addons/rokojori_action_library/Runtime/Shading/Materials/Transfers/FloatPropertyTransfer.cs" id="4_ed7jn"] +[ext_resource type="Script" path="res://addons/rokojori_action_library/Runtime/Shading/Materials/Transfers/TextureChannelToVector4Transfer.cs" id="4_ubpsj"] +[ext_resource type="Resource" uid="uid://dy4yrg7cbx84g" path="res://addons/rokojori_action_library/Runtime/Shading/Properties/Property Library/Float/Metallic.tres" id="5_hkpx0"] +[ext_resource type="Script" path="res://addons/rokojori_action_library/Runtime/Shading/Materials/SubMaterialTransfer.cs" id="6_7dw2g"] +[ext_resource type="Resource" uid="uid://dn4k668h72myc" path="res://addons/rokojori_action_library/Runtime/Shading/Properties/Property Library/Float/Roughness.tres" id="6_8likw"] +[ext_resource type="Resource" uid="uid://xoj2836knmix" path="res://addons/rokojori_action_library/Runtime/Shading/Properties/Property Library/TextureChannels/Metallic Texture Channel.tres" id="6_v4lhi"] +[ext_resource type="Resource" uid="uid://dldbju3x0x2ow" path="res://addons/rokojori_action_library/Runtime/Shading/Properties/Property Library/Texture2D/Albedo Texture.tres" id="7_yqc2y"] +[ext_resource type="Resource" uid="uid://d2x1ijwsv2urr" path="res://addons/rokojori_action_library/Runtime/Shading/Properties/Property Library/TextureChannels/Roughness Texture Channel.tres" id="8_gljck"] +[ext_resource type="Script" path="res://addons/rokojori_action_library/Runtime/Shading/Materials/Transfers/Texture2DPropertyTransfer.cs" id="8_iwxjk"] +[ext_resource type="Resource" uid="uid://cow6rei03x5bs" path="res://addons/rokojori_action_library/Runtime/Shading/Properties/Property Library/Texture2D/AO Texture.tres" id="10_8mfy8"] +[ext_resource type="Resource" uid="uid://cn7nxc0qw7pan" path="res://addons/rokojori_action_library/Runtime/Shading/Properties/Property Library/Vector3/UV1 Offset.tres" id="10_y3nsy"] +[ext_resource type="Resource" uid="uid://csiwpeupf761o" path="res://addons/rokojori_action_library/Runtime/Shading/Properties/Property Library/Texture2D/Metallic Texture.tres" id="11_i36mp"] +[ext_resource type="Script" path="res://addons/rokojori_action_library/Runtime/Shading/Materials/Transfers/Vector3PropertyTransfer.cs" id="11_on6ta"] +[ext_resource type="Resource" uid="uid://douianw6mx4p5" path="res://addons/rokojori_action_library/Runtime/Shading/Properties/Property Library/Vector3/UV1 Scale.tres" id="12_a173h"] +[ext_resource type="Resource" uid="uid://bdjlvcpc13hl6" path="res://addons/rokojori_action_library/Runtime/Shading/Properties/Property Library/Texture2D/Roughness Texture.tres" id="12_u6pwu"] +[ext_resource type="Resource" uid="uid://bprgfki4joe6x" path="res://addons/rokojori_action_library/Runtime/Shading/Properties/Property Library/Vector4/AO Texture Channel.tres" id="16_56ntu"] +[ext_resource type="Resource" uid="uid://b0t27ujy2oqpd" path="res://addons/rokojori_action_library/Runtime/Shading/Properties/Property Library/Vector4/Metallic Texture Channel.tres" id="18_4frq6"] +[ext_resource type="Resource" uid="uid://cni6awfm7rw14" path="res://addons/rokojori_action_library/Runtime/Shading/Properties/Property Library/Vector4/Roughness Texture Channel.tres" id="19_gc6bk"] + +[sub_resource type="Resource" id="Resource_7u5wc"] +script = ExtResource("2_vwrue") +from = ExtResource("1_b7tdx") +to = ExtResource("1_b7tdx") + +[sub_resource type="Resource" id="Resource_odp7t"] +script = ExtResource("4_ubpsj") +from = ExtResource("3_rkwtk") +to = ExtResource("16_56ntu") + +[sub_resource type="Resource" id="Resource_klwhe"] +script = ExtResource("4_ubpsj") +from = ExtResource("6_v4lhi") +to = ExtResource("18_4frq6") + +[sub_resource type="Resource" id="Resource_h8iqk"] +script = ExtResource("4_ubpsj") +from = ExtResource("8_gljck") +to = ExtResource("19_gc6bk") + +[sub_resource type="Resource" id="Resource_t22rd"] +script = ExtResource("4_ed7jn") +from = ExtResource("3_u377f") +to = ExtResource("3_u377f") + +[sub_resource type="Resource" id="Resource_4xt6c"] +script = ExtResource("4_ed7jn") +from = ExtResource("5_hkpx0") +to = ExtResource("5_hkpx0") + +[sub_resource type="Resource" id="Resource_oahhf"] +script = ExtResource("4_ed7jn") +from = ExtResource("6_8likw") +to = ExtResource("6_8likw") + +[sub_resource type="Resource" id="Resource_17abg"] +script = ExtResource("8_iwxjk") +from = ExtResource("7_yqc2y") +to = ExtResource("7_yqc2y") + +[sub_resource type="Resource" id="Resource_0f0yq"] +script = ExtResource("8_iwxjk") +from = ExtResource("10_8mfy8") +to = ExtResource("10_8mfy8") + +[sub_resource type="Resource" id="Resource_76qjn"] +script = ExtResource("8_iwxjk") +from = ExtResource("11_i36mp") +to = ExtResource("11_i36mp") + +[sub_resource type="Resource" id="Resource_yhmds"] +script = ExtResource("8_iwxjk") +from = ExtResource("12_u6pwu") +to = ExtResource("12_u6pwu") + +[sub_resource type="Resource" id="Resource_2y0hl"] +script = ExtResource("11_on6ta") +from = ExtResource("10_y3nsy") +to = ExtResource("10_y3nsy") + +[sub_resource type="Resource" id="Resource_s484q"] +script = ExtResource("11_on6ta") +from = ExtResource("12_a173h") +to = ExtResource("12_a173h") + +[resource] +script = ExtResource("6_7dw2g") +info = "ORM From Standard" +canvasItem = false +fog = false +orm3D = true +standard3D = true +panoramaSky = false +particleProcess = false +physicalSky = false +placeholder = false +proceduralSky = false +shader = false +shaders = [] +bools = [] +colors = [SubResource("Resource_7u5wc")] +floats = [SubResource("Resource_t22rd"), SubResource("Resource_4xt6c"), SubResource("Resource_oahhf")] +ints = [] +texture2D = [SubResource("Resource_17abg"), SubResource("Resource_0f0yq"), SubResource("Resource_76qjn"), SubResource("Resource_yhmds")] +vector2 = [] +vector3 = [SubResource("Resource_2y0hl"), SubResource("Resource_s484q")] +vector4 = [] +custom = [SubResource("Resource_odp7t"), SubResource("Resource_klwhe"), SubResource("Resource_h8iqk")] diff --git a/Runtime/Procedural/Baking/BakingMaterials/ORM/ORM Transfer.tres b/Runtime/Procedural/Baking/BakingMaterials/ORM/ORM Transfer.tres new file mode 100644 index 0000000..91c0478 --- /dev/null +++ b/Runtime/Procedural/Baking/BakingMaterials/ORM/ORM Transfer.tres @@ -0,0 +1,8 @@ +[gd_resource type="Resource" script_class="MaterialTransfer" load_steps=3 format=3 uid="uid://dssb72y4ryvm7"] + +[ext_resource type="Script" path="res://addons/rokojori_action_library/Runtime/Shading/Materials/MaterialTransfer.cs" id="1_bqkgd"] +[ext_resource type="Resource" uid="uid://kwk45f3p6ic4" path="res://addons/rokojori_action_library/Runtime/Procedural/Baking/BakingMaterials/ORM/ORM From Standard.tres" id="2_tyxxw"] + +[resource] +script = ExtResource("1_bqkgd") +transfers = [ExtResource("2_tyxxw")] diff --git a/Runtime/Procedural/Baking/DilateTexture.cs b/Runtime/Procedural/Baking/DilateTexture.cs new file mode 100644 index 0000000..d13b7da --- /dev/null +++ b/Runtime/Procedural/Baking/DilateTexture.cs @@ -0,0 +1,69 @@ +using System.Collections; +using System.Collections.Generic; +using Godot; +using System; +using System.Threading.Tasks; + + + +namespace Rokojori +{ + [Tool] + [GlobalClass] + public partial class DilateTexture:Node + { + [ExportCategory("Readonly")] + [Export] + public SubViewport X_viewport; + + [Export] + public TextureRect X_textureRect; + + public static readonly string dilateMaterialPath = "res://addons/rokojori_action_library/External/Imposter/materials/dilatate.material"; + + public static Texture2DPropertyName alphaTex = Texture2DPropertyName.Create( "u_alpha_tex" ); + public static BoolPropertyName alphaOverwrite = BoolPropertyName.Create( "u_alpha_overwrite" ); + + public async Task Create( Texture2D texture2D, Texture2D alphaMask = null ) + { + var overwriteAlpha = alphaMask != null; + + if ( ! overwriteAlpha ) + { + alphaMask = texture2D; + } + + this.DestroyChildren(); + + X_viewport = this.CreateChild( "Dilate Viewport" ); + X_viewport.World2D = new World2D(); + X_viewport.Size = (Vector2I) texture2D.GetSize(); + + X_viewport.TransparentBg = true; + X_viewport.RenderTargetUpdateMode = SubViewport.UpdateMode.Always; + + X_textureRect = X_viewport.CreateChild( "Texture" ); + X_textureRect.StretchMode = TextureRect.StretchModeEnum.Keep; + X_textureRect.CustomMinimumSize = new Vector2( 4096, 4096 ); + + X_textureRect.Texture = texture2D; + + var material = ResourceLoader.Load( dilateMaterialPath ) as ShaderMaterial; + alphaTex.Set( material, alphaMask ); + alphaOverwrite.Set( material, overwriteAlpha ); + + X_textureRect.Material = material; + + await this.RequestNextFrame(); + + var viewportTexture = X_viewport.GetTexture(); + + await this.RequestNextFrame(); + + var image = viewportTexture.GetImage(); + + return ImageTexture.CreateFromImage( image ); + } + + } +} \ No newline at end of file diff --git a/Runtime/Procedural/Baking/MultiBakeMode.cs b/Runtime/Procedural/Baking/MultiBakeMode.cs new file mode 100644 index 0000000..a54ddd5 --- /dev/null +++ b/Runtime/Procedural/Baking/MultiBakeMode.cs @@ -0,0 +1,20 @@ +using System.Collections; +using System.Collections.Generic; +using Godot; +using System; + +using System.Threading.Tasks; + +namespace Rokojori +{ + public abstract class MultiBakeModeImplementation + { + public MultiBaker multiBaker; + public abstract MultiBaker.BakeMode GetBakeMode(); + public abstract int GetNumViews(); + public abstract void CreateBakes(); + public abstract void AssignMaterial( BakingMaterialMode mode, Texture2D texture ); + public abstract void CreateMaterial( bool preview ); + + } +} \ No newline at end of file diff --git a/Runtime/Procedural/Baking/MultiBakeModeBillboardBase.cs b/Runtime/Procedural/Baking/MultiBakeModeBillboardBase.cs new file mode 100644 index 0000000..9703402 --- /dev/null +++ b/Runtime/Procedural/Baking/MultiBakeModeBillboardBase.cs @@ -0,0 +1,53 @@ +using System.Collections; +using System.Collections.Generic; +using Godot; +using System; + +using System.Threading.Tasks; + +namespace Rokojori +{ + public abstract class MultiBakeModeBillboardBase:MultiBakeModeImplementation + { + public override void CreateMaterial( bool preview ) + { + var mb = multiBaker; + + var material = new StandardMaterial3D(); + + material.ShadingMode = preview ? BaseMaterial3D.ShadingModeEnum.Unshaded : BaseMaterial3D.ShadingModeEnum.PerPixel; + material.Transparency = BaseMaterial3D.TransparencyEnum.AlphaScissor; + material.AlphaScissorThreshold = 0.5f; + material.AlphaAntialiasingMode = BaseMaterial3D.AlphaAntiAliasing.AlphaToCoverageAndToOne; + material.AlphaAntialiasingEdge = 0.3f; + material.NormalEnabled = ! preview; + + Materials.Set( mb.X_outputMesh, material ); + } + + public override void AssignMaterial( BakingMaterialMode mode, Texture2D texture ) + { + var mb = multiBaker; + mb.CacheTexture( mode, texture ); + + var material = Materials.Get( mb.X_outputMesh ); + + + if ( BakingMaterialMode.Albedo == mode || BakingMaterialMode.Preview == mode ) + { + RJLog.Log( "Assign albedo" ); + material.AlbedoTexture = texture; + } + else if ( BakingMaterialMode.Normals == mode ) + { + RJLog.Log( "Assign normal" ); + material.NormalTexture = texture; + } + else if ( BakingMaterialMode.ORM == mode ) + { + material.OrmTexture = texture; + } + + } + } +} \ No newline at end of file diff --git a/Runtime/Procedural/Baking/MultiBakeModeCrossBraces.cs b/Runtime/Procedural/Baking/MultiBakeModeCrossBraces.cs new file mode 100644 index 0000000..0a1627c --- /dev/null +++ b/Runtime/Procedural/Baking/MultiBakeModeCrossBraces.cs @@ -0,0 +1,142 @@ +using System.Collections; +using System.Collections.Generic; +using Godot; +using System; + +using System.Threading.Tasks; + +namespace Rokojori +{ + public class MultiBakeModeCrossBraces:MultiBakeModeBillboardBase + { + public override MultiBaker.BakeMode GetBakeMode() + { + return MultiBaker.BakeMode.Cross_Braces; + } + + public override int GetNumViews() + { + var views = multiBaker.crossAngles * 2; + + if ( multiBaker.crossBottom ) + { + views ++; + } + + if ( multiBaker.crossTop ) + { + views ++; + } + + return views; + } + + public override void CreateBakes() + { + var fov = multiBaker.GetCameraFOV(); + var distance = multiBaker.GetCameraDistance(); + var outputScale = multiBaker.GetOutputScale(); + + var _bakers = multiBaker.bakers; + var mb = multiBaker; + + + _bakers.ForEach( + b => + { + b.useCustomFOV = true; + b.customFOV = fov; + + b.useCustomDistance = true; + b.customDistance = distance; + + b.rotationMode = Baker.RotationMode.Yaw_Pitch; + } + ); + + var index = 0; + var mg = new MeshGeometry(); + + var numTextures = GetNumViews(); + var textureAlignment = TextureMerger.ComputeTextureAlignment( numTextures ); + + + if ( mb.crossTop ) + { + _bakers[ index ].yaw = 0 + mb.crossAngleOffset; + _bakers[ index ].pitch = 90f; + + var uv = TextureMerger.GetUVRectangle( textureAlignment, index, true ); + + var q = new MeshGeometry(); + q.AddQuad( _bakers[ index ].bakingRotation, outputScale, uv.min, uv.max ); + + mg.Add( q ); + + index ++; + } + + if ( mb.crossBottom ) + { + _bakers[ index ].yaw = 0 + mb.crossAngleOffset; + _bakers[ index ].pitch = -90f; + + var uv = TextureMerger.GetUVRectangle( textureAlignment, index, true ); + var q = new MeshGeometry(); + q.AddQuad( _bakers[ index ].bakingRotation, outputScale, uv.min, uv.max ); + + mg.Add( q ); + + index ++; + } + + + for ( int i = 0; i < mb.crossAngles; i++ ) + { + for ( int side = 0; side < 2; side ++ ) + { + var angle = ( 180f * i ) / (float) mb.crossAngles; + + _bakers[ index ].yaw = side == 0 ? angle : ( MathX.Repeat( angle + 180f, 360f ) ); + _bakers[ index ].yaw += mb.crossAngleOffset; + _bakers[ index ].pitch = 0; + + var uv = TextureMerger.GetUVRectangle( textureAlignment, index, true ); + + var q = new MeshGeometry(); + q.AddQuad( _bakers[ index ].bakingRotation, outputScale, uv.min, uv.max ); + + if ( mb.crossBraces > 1 ) + { + var normal = Vector3.Forward * _bakers[ index ].bakingRotation; + var startOffset = normal * mb.crossSpreadDistance * 0.5f; + var endOffset = -startOffset; + + for ( int brace = 0; brace < mb.crossBraces; brace ++ ) + { + var bq = q.Clone(); + var lerpAmount = (float)brace / (float)( mb.crossBraces - 1 ); + var offset = startOffset.Lerp( endOffset, lerpAmount ); + bq.ApplyTranslation( offset ); + mg.Add( bq ); + } + } + else + { + mg.Add( q ); + } + + + index ++; + + } + + + + } + + + mb.X_outputMesh.Mesh = mg.GenerateMesh(); + } + } +} \ No newline at end of file diff --git a/Runtime/Procedural/Baking/MultiBakeModeCylinder.cs b/Runtime/Procedural/Baking/MultiBakeModeCylinder.cs new file mode 100644 index 0000000..d6247a9 --- /dev/null +++ b/Runtime/Procedural/Baking/MultiBakeModeCylinder.cs @@ -0,0 +1,155 @@ +using System.Collections; +using System.Collections.Generic; +using Godot; +using System; + +using System.Threading.Tasks; + +namespace Rokojori +{ + public class MultiBakeModeCylinder:MultiBakeModeBillboardBase + { + public override MultiBaker.BakeMode GetBakeMode() + { + return MultiBaker.BakeMode.Cylinder; + } + + public override int GetNumViews() + { + var cylinderViews = multiBaker.cylinderSides; + + if ( multiBaker.cylinderBottom ) + { + cylinderViews ++; + } + + if ( multiBaker.cylinderTop ) + { + cylinderViews ++; + } + + return cylinderViews; + } + + public override void CreateBakes() + { + var fov = multiBaker.GetCameraFOV(); + var distance = multiBaker.GetCameraDistance(); + var outputScale = multiBaker.GetOutputScale(); + + var _bakers = multiBaker.bakers; + var mb = multiBaker; + + + _bakers.ForEach( + b => + { + b.useCustomFOV = true; + b.customFOV = fov; + + b.useCustomDistance = true; + b.customDistance = distance; + + b.rotationMode = Baker.RotationMode.Yaw_Pitch; + } + ); + + var index = 0; + var mg = new MeshGeometry(); + + var numTextures = GetNumViews(); + var textureAlignment = TextureMerger.ComputeTextureAlignment( numTextures ); + + + if ( mb.cylinderTop ) + { + _bakers[ index ].yaw = 0; + _bakers[ index ].pitch = 90f; + + var uv = TextureMerger.GetUVRectangle( textureAlignment, index, true ); + + var q = new MeshGeometry(); + q.AddQuad( _bakers[ index ].bakingRotation, outputScale, uv.min, uv.max ); + + + if ( mb.cylinderTopOffset != 0 ) + { + var topOffset = Vector3.Up * mb.cylinderTopOffset * outputScale * 0.5f; + q.ApplyTranslation( topOffset, -4 ); + } + + mg.Add( q ); + + if ( mb.createBackFacesForTop ) + { + q.FlipNormalDirection(); + mg.Add( q ); + } + + index ++; + } + + if ( mb.cylinderBottom ) + { + _bakers[ index ].yaw = 0; + _bakers[ index ].pitch = -90f; + + var uv = TextureMerger.GetUVRectangle( textureAlignment, index, true ); + + var q = new MeshGeometry(); + q.AddQuad( _bakers[ index ].bakingRotation, outputScale, uv.min, uv.max ); + + if ( mb.cylinderBottomOffset != 0 ) + { + var bottomOffset = Vector3.Down * mb.cylinderBottomOffset * outputScale * 0.5f; + q.ApplyTranslation( bottomOffset, -4 ); + } + + mg.Add( q ); + + if ( mb.createBackFacesForBottom ) + { + q.FlipNormalDirection(); + mg.Add( q ); + } + + index ++; + } + + + for ( int i = 0; i < mb.cylinderSides; i++ ) + { + var angle = ( 360f * i ) / (float) mb.cylinderSides; + _bakers[ index ].yaw = angle; + _bakers[ index ].pitch = 0; + + var uv = TextureMerger.GetUVRectangle( textureAlignment, index, true ); + + + var q = new MeshGeometry(); + q.AddQuad( _bakers[ index ].bakingRotation, outputScale, uv.min, uv.max ); + + if ( mb.cylinderSideOffset != 0 ) + { + var sideOffset = Vector3.Forward *_bakers[ index ].bakingRotation * mb.cylinderSideOffset * outputScale * -0.5f; + q.ApplyTranslation( sideOffset, -4 ); + } + + mg.Add( q ); + + if ( mb.createBackFacesForSides ) + { + q.FlipNormalDirection(); + mg.Add( q ); + } + + index++; + + } + + + mb.X_outputMesh.Mesh = mg.GenerateMesh(); + + } + } +} \ No newline at end of file diff --git a/Runtime/Procedural/Baking/MultiBakeModeOctahedral.cs b/Runtime/Procedural/Baking/MultiBakeModeOctahedral.cs new file mode 100644 index 0000000..c992ec2 --- /dev/null +++ b/Runtime/Procedural/Baking/MultiBakeModeOctahedral.cs @@ -0,0 +1,130 @@ +using System.Collections; +using System.Collections.Generic; +using Godot; +using System; + +using System.Threading.Tasks; + +namespace Rokojori +{ + public class MultiBakeModeOctahedral:MultiBakeModeImplementation + { + public readonly string octahedralShader = "res://addons/rokojori_action_library/External/Imposter/materials/shaders/ImpostorShader.gdshader"; + + public override MultiBaker.BakeMode GetBakeMode() + { + return MultiBaker.BakeMode.Octahedral; + } + + public override int GetNumViews() + { + var views = multiBaker.octahedralSides * multiBaker.octahedralSides; + + return views; + } + + public override void CreateMaterial( bool preview ) + { + var mb = multiBaker; + var material = new ShaderMaterial(); + material.Shader = ResourceLoader.Load( octahedralShader ) as Shader; + + material.SetShaderParameter( "isFullSphere", mb.octahedralFullSphere ); + material.SetShaderParameter( "imposterFrames", Vector2.One * mb.octahedralSides ); + + Materials.Set( mb.X_outputMesh, material ); + } + + public override void AssignMaterial( BakingMaterialMode mode, Texture2D texture ) + { + var mb = multiBaker; + mb.CacheTexture( mode, texture ); + + var material = Materials.Get( mb.X_outputMesh ); + + if ( material == null ) + { + RJLog.Log( "Not the right material on mesh!" ); + return; + } + + if ( BakingMaterialMode.Albedo == mode || BakingMaterialMode.Preview == mode ) + { + material.SetShaderParameter( "imposterTextureAlbedo", texture ); + } + else if ( BakingMaterialMode.Normals == mode ) + { + material.SetShaderParameter( "imposterTextureNormal", texture ); + } + else if ( BakingMaterialMode.ORM == mode ) + { + material.SetShaderParameter( "imposterTextureOrm", texture ); + } + else if ( BakingMaterialMode.Depth == mode ) + { + material.SetShaderParameter( "imposterTextureDepth", texture ); + } + + + + } + + public override void CreateBakes() + { + var fov = multiBaker.GetCameraFOV(); + var distance = multiBaker.GetCameraDistance(); + var outputScale = multiBaker.GetOutputScale(); + + var _bakers = multiBaker.bakers; + var mb = multiBaker; + + + _bakers.ForEach( + b => + { + b.useCustomFOV = true; + b.customFOV = fov; + + b.useCustomDistance = true; + b.customDistance = distance; + + b.rotationMode = Baker.RotationMode.Quaternion; + } + ); + + + var numTextures = GetNumViews(); + var textureAlignment = TextureMerger.ComputeTextureAlignment( numTextures ); + + var toOP = 1f / ( mb.octahedralSides - 1 ); + var index = 0; + + for ( int x = 0; x < mb.octahedralSides; x++ ) + { + for ( int y = 0; y < mb.octahedralSides; y++ ) + { + var inverseX = ( mb.octahedralSides - 1 ) - x; + var inverseY = ( mb.octahedralSides - 1 ) - y; + var octahedralPosition = new Vector2( y, inverseX ) * toOP; + + var normal = OctahedralMapping.Map( octahedralPosition, mb.octahedralFullSphere ); + + _bakers[ index ].rotationQuaternion = Math3D.LookRotation( normal, true ).Normalized().Inverse(); + + index ++; + } + + + } + + var qm = new QuadMesh(); + mb.X_outputMesh.Mesh = qm; + var magicScale = 0.75f; + mb.X_outputMesh.Scale = Vector3.One * ( outputScale / mb.cameraZoom * magicScale); + + + } + + + } +} \ No newline at end of file diff --git a/Runtime/Procedural/Baking/MultiBakeModeSpherical.cs b/Runtime/Procedural/Baking/MultiBakeModeSpherical.cs new file mode 100644 index 0000000..5d5bf6d --- /dev/null +++ b/Runtime/Procedural/Baking/MultiBakeModeSpherical.cs @@ -0,0 +1,42 @@ +using System.Collections; +using System.Collections.Generic; +using Godot; +using System; + +using System.Threading.Tasks; + +namespace Rokojori +{ + public class MultiBakeModeSpherical:MultiBakeModeImplementation + { + public readonly string sphericalShader = "res://addons/rokojori_action_library/External/Imposter/materials/shaders/ImpostorShader.gdshader"; + + public override MultiBaker.BakeMode GetBakeMode() + { + return MultiBaker.BakeMode.Spherical; + } + + public override int GetNumViews() + { + var views = multiBaker.sphericalSides * multiBaker.sphericalRows; + + return views; + } + + public override void CreateMaterial( bool preview ) + { + + } + + public override void AssignMaterial( BakingMaterialMode mode, Texture2D texture ) + { + + } + + public override void CreateBakes() + { + + } + + } +} \ No newline at end of file diff --git a/Runtime/Procedural/Baking/MultiBaker.cs b/Runtime/Procedural/Baking/MultiBaker.cs index 1dc3ef9..7864588 100644 --- a/Runtime/Procedural/Baking/MultiBaker.cs +++ b/Runtime/Procedural/Baking/MultiBaker.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; using Godot; using System; - +using System.Threading.Tasks; namespace Rokojori { @@ -14,26 +14,120 @@ namespace Rokojori [Export] public bool initialize; + public bool bake; + + public bool saveTexture; + + [ExportGroup("Preview")] + [Export] - public bool setupViews; - + public bool preview_UpdateAlways = true; + + [Export] + public bool preview_DilateTextures = true; + + public enum MaterialMode + { + Full_Seperated, + Simple_Prebaked + } + + [ExportGroup("Material")] + [Export] + public MaterialMode materialMode; + + [ExportGroup("Material/Full Seperated")] + [Export] + public bool mmfs_Normals = true; + [Export] + public bool mmfs_Depth = true; + [Export] + public bool mmfs_ORM = true; + public enum BakeMode { - Cylindric_Billboard + Cylinder, + Cross_Braces, + Octahedral, + Spherical, + Cloud } + [ExportGroup("BakeMode")] + [Export] public BakeMode bakeMode; + + + [ExportGroup("BakeMode/Cylinder")] + [Export] + public int cylinderSides = 4; + [Export] + public bool createBackFacesForSides = false; + [Export] + public bool cylinderTop = false; + [Export] + public bool createBackFacesForTop = false; + [Export] + public bool cylinderBottom = false; + [Export] + public bool createBackFacesForBottom = false; + [Export( PropertyHint.Range, "-1,1" )] + public float cylinderSideOffset = 0; + [Export( PropertyHint.Range, "-1,1" )] + public float cylinderTopOffset = 0f; + [Export( PropertyHint.Range, "-1,1" )] + public float cylinderBottomOffset = 0f; + + [ExportGroup("BakeMode/Cross Braces")] + [Export] + public int crossAngles = 2; + [Export] + public int crossBraces = 3; + [Export] + public float crossSpreadDistance = 1; + [Export] + public float crossAngleOffset = 0; + + [Export] + public bool crossTop = false; + + [Export] + public bool crossBottom = false; + + + [ExportGroup("BakeMode/Octahedral")] + [Export] + public int octahedralSides = 4; + [Export] + public bool octahedralFullSphere = false; + + [ExportGroup("BakeMode/Spherical")] + [Export] + public int sphericalSides = 4; + [Export] + public int sphericalRows = 3; + [Export] + public float minPitch = -30; + [Export] + public float maxPitch = 30; + + [ExportGroup( "Object")] + [Export] public Node3D sourceTarget; + + [Export] + public bool autoCenter = false; [Export] - public Node outputTarget; + public Vector3 sourceOffset; + - [ExportGroup( "Camera Settings")] + [ExportGroup( "Camera")] [Export] public Baker.CameraDistanceDetectionType distanceDetectionType = Baker.CameraDistanceDetectionType.Automatic_Distance_Detection; @@ -52,34 +146,28 @@ namespace Rokojori [Export] public float customFOV = 75; - [Export] - public Baker.MeshMode meshMode = Baker.MeshMode.World_Scale; - [ExportGroup("Cylindric Billboard")] - - [Export] - public int cylinderSides = 4; - [Export] - public bool cylinderTop = false; - [Export] - public bool cylinderBottom = false; - [Export] - public float cylinderSideOffset = 0; - [Export] - public float cylinderTopOffset = 0.5f; - [Export] - public float cylinderBottomOffset = 0.5f; - [Export] - public MeshInstance3D cylinderMesh; + [ExportGroup( "Output")] - [ExportGroup("Viewport")] [Export] - public Vector2 textureSize = new Vector2( 2048, 2048 ); + public string outputDirectory; - [ExportGroup("Debugging")] + [Export] + public string outputFileName; + [Export] + public float outputQuality = 1f; + + [Export] + public Vector2 outputTextureSize = new Vector2( 2048, 2048 ); + + [Export] + public bool showOutputTexture = false; + + + [ExportGroup("Read Only")] [Export] public SubViewport X_bakingViewport; @@ -91,35 +179,287 @@ namespace Rokojori [Export] public WorldEnvironment X_worldEnvironment; + + [Export] + public DilateTexture X_dilateTexture; + + [Export] + public MeshInstance3D X_outputMesh; + + [Export] + public TextureMerger X_textureMerger; + + [Export] + public SetBakingMaterials X_setBakingMaterials; + + [Export] + public MeshInstance3D X_texturePreview; + + [Export] + public CsgMesh3D X_depthMapper; + + [Export] + public Texture2D X_bakedTextureAlbedo; + + [Export] + public Texture2D X_bakedTextureNormal; + + [Export] + public Texture2D X_bakedTextureORM; + + [Export] + public Texture2D X_bakedTextureDepth; + + + + + bool _initialized = false; + bool _baking = false; + + + SerializedGodotObject _cached; + + + public override void _Ready() + { + Initialize(); + } public override void _Process( double delta ) - { - if ( initialize ) + { + X_texturePreview.Visible = showOutputTexture; + + if ( _baking ) + { + return; + } + + if ( initialize || ! _initialized ) { initialize = false; Initialize(); } - if ( setupViews ) + + var current = SerializedGodotObject.Create( this ); + + var changed = _cached != null && ! _cached.Equals( current ); + + _cached = current; + + if ( bake || changed && preview_UpdateAlways ) { - setupViews = false; - CreateViews(); - SetupViews(); + bake = false; + Bake(); } + } - public void Initialize() + + public async Task Bake() { - if ( outputTarget == null ) + _baking = true; + + try { - outputTarget = this; + + this.LogInfo( "Started baking" ); + + Nodes.RemoveAndDeleteChildren( X_bakingTargetContainer ); + sourceTarget.DeepCopyTo( X_bakingTargetContainer ); + + if ( _bakers == null || _bakers.Count != GetNumViews() ) + { + CreateViews(); + await this.RequestNextFrame(); + } + + + SetupViews(); + + this.LogInfo( "Views set up" ); + await this.RequestNextFrame(); + + X_setBakingMaterials.SetTarget( X_bakingTargetContainer ); + + + var bakingMaterialModes = new List(); + + var preview_QuickMaterial = MaterialMode.Simple_Prebaked == materialMode; + + GetBakeModeImplementation().CreateMaterial( preview_QuickMaterial ); + + if ( preview_QuickMaterial ) + { + bakingMaterialModes.Add( BakingMaterialMode.Preview ); + } + else + { + bakingMaterialModes.Add( BakingMaterialMode.Albedo ); + + if ( mmfs_Normals ) + { + bakingMaterialModes.Add( BakingMaterialMode.Normals ); + } + + if ( mmfs_Depth ) + { + bakingMaterialModes.Add( BakingMaterialMode.Depth ); + } + + if ( mmfs_ORM ) + { + bakingMaterialModes.Add( BakingMaterialMode.ORM ); + } + } + + this.LogInfo( "Prepared baking modes" ); + + X_textureMerger.textureSize = outputTextureSize; + X_textureMerger.Initialize(); + X_textureMerger.CreateLayout(); + + this.LogInfo( "Prepared texture merger" ); + + var objectDistance = GetCameraDistance(); + + for ( int i = 0; i < bakingMaterialModes.Count; i++ ) + { + this.LogInfo( "Baking mode:", bakingMaterialModes[ i ] ); + + X_setBakingMaterials.mode = bakingMaterialModes[ i ]; + X_setBakingMaterials.ApplyBakingMaterials( objectDistance, _targetBoundingSphere.radius ); + + this.LogInfo( "Materials changed:", bakingMaterialModes[ i ] ); + + await this.RequestNextFrame(); + + Texture2D texture = X_textureMerger.X_textureMergerViewport.GetTexture(); + + this.LogInfo( "Texture created:", bakingMaterialModes[ i ] ); + + if ( preview_DilateTextures ) + { + this.LogInfo( "Dilating:", bakingMaterialModes[ i ] ); + texture = await CreateDilatedTexture(); + + this.LogInfo( "Dilating done:", bakingMaterialModes[ i ] ); + } + else + { + texture = Textures.Copy( texture ); + await this.RequestNextFrame(); + await this.RequestNextFrame(); + } + + + this.LogInfo( "Assigning Texture", bakingMaterialModes[ i ] ); + GetBakeModeImplementation().AssignMaterial( bakingMaterialModes[ i ], texture ); + + this.LogInfo( "Baking done:", bakingMaterialModes[ i ] ); + + await this.RequestNextFrame(); + + } + + await this.RequestNextFrame(); + + // this.LogInfo( "Baking done" ); + + } + catch ( System.Exception e ) + { + this.LogError( "Baking failed" ); + this.LogError( e ); } - Nodes.RemoveAndDeleteChildren( outputTarget ); + _baking = false; + return; - X_bakingViewport = outputTarget.CreateChild( "Multi Baker Viewport" ); + } + + + public async Task CreateDilatedTexture() + { + var viewports = GetAllViewports(); + var textures = Lists.Map( viewports, v => v.GetTexture() as Texture2D ); + var dilatedTextures = new List(); - X_bakingViewport.Size = (Vector2I) textureSize; + var index = 0; + + foreach ( var t in textures ) + { + index ++; + + var dilatedTexture = await X_dilateTexture.Create( t ); + dilatedTextures.Add( dilatedTexture ); + } + + X_textureMerger.sourceTextures = dilatedTextures.ToArray(); + X_textureMerger.sourceMode = TextureMerger.SourceMode.Textures; + + X_textureMerger.Initialize(); + + await this.RequestNextFrame(); + + X_textureMerger.CreateLayout(); + + await this.RequestNextFrame(); + + var finalTexture = await X_dilateTexture.Create( X_textureMerger.X_textureMergerViewport.GetTexture() ); + + return finalTexture; + } + + public List GetAllViewports() + { + return Nodes.AllIn( X_views, null, false ); + } + + List _bakeModeImplementations = new List() + { + new MultiBakeModeCylinder(), + new MultiBakeModeCrossBraces(), + new MultiBakeModeOctahedral() + }; + + public MultiBakeModeImplementation GetBakeModeImplementation() + { + var bm = _bakeModeImplementations.Find( bm => bm.GetBakeMode() == bakeMode ); + bm.multiBaker = this; + return bm; + } + + public void CacheTexture( BakingMaterialMode mode, Texture2D texture ) + { + if ( BakingMaterialMode.Albedo == mode ) + { + X_bakedTextureAlbedo = texture; + } + else if ( BakingMaterialMode.Normals == mode ) + { + X_bakedTextureNormal = texture; + } + else if ( BakingMaterialMode.ORM == mode ) + { + X_bakedTextureORM = texture; + } + else if ( BakingMaterialMode.Depth == mode ) + { + X_bakedTextureDepth = texture; + } + + } + + public void Initialize() + { + Nodes.RemoveAndDeleteChildren( this ); + + _bakers = null; + + X_bakingViewport = this.CreateChild( "Multi Baker Viewport" ); + + X_bakingViewport.Size = (Vector2I) outputTextureSize; X_bakingViewport.OwnWorld3D = true; X_bakingViewport.TransparentBg = true; @@ -132,47 +472,60 @@ namespace Rokojori X_views = X_bakingViewport.CreateChild( "Views" ); - sourceTarget.DeepCopyTo( X_bakingTargetContainer ); + X_dilateTexture = this.CreateChild( "Dilate Texture" ); + + X_textureMerger = this.CreateChild( "Texture Merger" ); + X_textureMerger.multiBaker = this; + X_textureMerger.Initialize(); + + X_outputMesh = this.CreateChild( "Output Mesh" ); + + X_texturePreview = this.CreateChild( "Texture Preview" ); + X_texturePreview.Mesh = new QuadMesh(); + X_texturePreview.Scale = Vector3.One * 100; + + var pm = new StandardMaterial3D(); + pm.Transparency = BaseMaterial3D.TransparencyEnum.AlphaScissor; + pm.ResourceLocalToScene = true; + + var vt = new ViewportTexture(); + vt.ViewportPath = X_textureMerger.X_textureMergerViewport.GetPath(); + pm.AlbedoTexture = vt; + + X_setBakingMaterials = this.CreateChild( "Set Baking Materials" ); + + Materials.Set( X_texturePreview, pm ); + + _initialized = true; } public int GetNumViews() { - if ( BakeMode.Cylindric_Billboard == bakeMode ) - { - var cylinderViews = cylinderSides; - - if ( cylinderBottom ) - { - cylinderViews ++; - } - - if ( cylinderTop ) - { - cylinderViews ++; - } - - return cylinderViews; - } - - return 0; + return GetBakeModeImplementation().GetNumViews(); } List _bakers; + public List bakers => _bakers; + public void CreateViews() - { + { + Nodes.RemoveAndDeleteChildren( X_views ); var numViews = GetNumViews(); _bakers = new List(); + var minViewsPerAxis = TextureMerger.ComputeTextureAlignment( numViews ).Y; + for ( int i = 0; i < numViews; i++ ) { var userIndex = ( i + 1 ); var bakingView = X_views.CreateChild( "Baking View " + userIndex ); bakingView.TransparentBg = true; + bakingView.Size = (Vector2I) ( outputTextureSize / minViewsPerAxis ); var bakingCamera = bakingView.CreateChild( "Camera View " + userIndex ); var baker = bakingView.CreateChild( "Baker " + userIndex ); @@ -181,6 +534,7 @@ namespace Rokojori baker.target = X_bakingTargetContainer; baker.viewport = bakingView; + baker.update = true; _bakers.Add( baker ); @@ -189,10 +543,40 @@ namespace Rokojori public void SetupViews() { - if ( BakeMode.Cylindric_Billboard == bakeMode ) + var numViews = GetNumViews(); + var minViewsPerAxis = TextureMerger.ComputeTextureAlignment( numViews ).Y; + + X_bakingViewport.Size = (Vector2I) outputTextureSize; + + for ( int i = 0; i < numViews; i++ ) { - CreateCylinderBillboardView(); + var baker = _bakers[ i ]; + var bakingView = baker.viewport as SubViewport; + bakingView.Size = (Vector2I) ( outputTextureSize / minViewsPerAxis ); } + + _targetBoundingSphere = null; + ComputeBoundingSphere(); + + if ( _targetBoundingSphere == null ) + { + this.LogError( "No bounding sphere created, ensure there are visible targets" ); + return; + } + + var bmi = GetBakeModeImplementation(); + + if ( bmi == null ) + { + return; + } + + bmi.CreateBakes(); + + /*if ( X_bakedTextureAlbedo != null ) + { + bmi.AssignMaterial( BakingMaterialMode.Albedo, X_bakedTextureAlbedo ); + }*/ } Sphere _targetBoundingSphere; @@ -212,11 +596,32 @@ namespace Rokojori void ComputeBoundingSphere() { + if ( X_bakingTargetContainer.GetChildCount() == 0 ) + { + _targetBoundingSphere = null; + return; + } + + var firstChild = X_bakingTargetContainer.GetChild( 0 ) as Node3D; + + if ( firstChild == null ) + { + _targetBoundingSphere = null; + return; + } + + if ( ! firstChild.Visible ) + { + firstChild.Visible = true; + } + var worldBounds = X_bakingTargetContainer.GetWorldBounds(); + + _targetBoundingSphere = Sphere.ContainingBox( worldBounds ); } - float GetCameraFOV() + public float GetCameraFOV() { if ( Baker.CameraFOVMode.Custom_Fov == fovMode ) { @@ -231,7 +636,7 @@ namespace Rokojori return Cameras.ComputeFOVForBillboard( originalFOV, targetBoundingSphere.radius, fovPlacingDistance ); } - float GetCameraDistance() + public float GetCameraDistance() { if ( Baker.CameraDistanceDetectionType.Custom_Distance == distanceDetectionType ) { @@ -243,78 +648,12 @@ namespace Rokojori return Cameras.ComputeCameraFrameFittingDistance( fov, targetBoundingSphere.radius / cameraZoom ); } - float GetOutputScale() + public float GetOutputScale() { var fov = GetCameraFOV(); var distance = GetCameraDistance(); return Cameras.ComputeCameraFittingScale( fov, distance ); } - - void CreateCylinderBillboardView() - { - _targetBoundingSphere = null; - var fov = GetCameraFOV(); - var distance = GetCameraDistance(); - var outputScale = GetOutputScale(); - - _bakers.ForEach( - b => - { - b.useCustomFOV = true; - b.customFOV = fov; - - b.useCustomDistance = true; - b.customDistance = distance; - } - ); - - var index = 0; - var mg = new MeshGeometry(); - - var numTextures = ( cylinderTop ? 1 : 0 ) + ( cylinderBottom ? 1 : 0 ) + cylinderSides; - var textureAlignment = TextureMerger.ComputeTextureAlignment( numTextures ); - - - if ( cylinderTop ) - { - _bakers[ index ].yaw = 0; - _bakers[ index ].pitch = 90f; - - var uvRectangle = TextureMerger.GetUVRectangle( textureAlignment, index ); - mg.AddQuad( _bakers[ index ].bakingRotation, outputScale, uvRectangle ); - - index ++; - } - - if ( cylinderBottom ) - { - _bakers[ index ].yaw = 0; - _bakers[ index ].pitch = -90f; - - var uvRectangle = TextureMerger.GetUVRectangle( textureAlignment, index ); - mg.AddQuad( _bakers[ index ].bakingRotation, outputScale, uvRectangle ); - - index ++; - } - - - for ( int i = 0; i < cylinderSides; i++ ) - { - var angle = ( 360f * i ) / (float) cylinderSides; - _bakers[ index + i ].yaw = angle; - _bakers[ index + i ].pitch = 0; - - var uv = TextureMerger.GetUVRectangle( textureAlignment, index + 1); - mg.AddQuad( _bakers[ index + i ].bakingRotation, outputScale, uv ); - } - - if ( cylinderMesh != null ) - { - cylinderMesh.Mesh = mg.GenerateMesh(); - } - } - - } } \ No newline at end of file diff --git a/Runtime/Procedural/Baking/SetBakingMaterials.cs b/Runtime/Procedural/Baking/SetBakingMaterials.cs index 54a20b4..4b32186 100644 --- a/Runtime/Procedural/Baking/SetBakingMaterials.cs +++ b/Runtime/Procedural/Baking/SetBakingMaterials.cs @@ -9,65 +9,142 @@ namespace Rokojori { [Tool] [GlobalClass] - public partial class SetBakingMaterials:RJAction + public partial class SetBakingMaterials:Node { [Export] public Node targetContainer; - public enum BakingMaterialMode - { - Albedo, - Normals, - ORM, - Depth - } - [Export] public BakingMaterialMode mode; - public override void _OnTrigger() + MapList _originalMaterials; + + public void SetTarget( Node targetContainer ) { - var baseMaterial = GetBaseMaterial(); + _originalMaterials = new MapList(); + + this.targetContainer = targetContainer; Nodes.ForEach( targetContainer, ( v )=> { - + _originalMaterials[ v ] = Materials.GetAll( v ); } ); } - - - public static readonly string materialsPath = "res://addons/rokojori_action_library/External/Imposter/materials/"; - static Dictionary _cachedMaterials = new Dictionary(); - - public static Material LoadMaterial( string materialName ) + public void ApplyBakingMaterials( float distance, float radius ) { - if ( _cachedMaterials.ContainsKey( materialName ) ) - { - return _cachedMaterials[ materialName ]; - } - - var loadedMaterial = ResourceLoader.Load( materialsPath + materialName ) as Material; - _cachedMaterials[ materialName ] = loadedMaterial; - - return loadedMaterial; + Nodes.ForEach( targetContainer, + ( v )=> + { + SetMaterial( v, distance, radius ); + } + ); } - public Material GetBaseMaterial() + + FloatPropertyName centerDistance = FloatPropertyName.Create( "centerDistance"); + FloatPropertyName rangeDistance = FloatPropertyName.Create( "rangeDistance"); + + void SetMaterial( VisualInstance3D v, float distance, float radius ) + { + var materials = _originalMaterials[ v ]; + + if ( BakingMaterialMode.Preview != mode ) + { + materials = CreateBakingMaterials( materials ); + } + + if ( BakingMaterialMode.Depth == mode ) + { + + materials.ForEach( + m => + { + centerDistance.Set( m, distance ); + rangeDistance.Set( m, radius ); + } + ); + } + + Materials.Set( v, materials ); + } + + List CreateBakingMaterials( List materials ) + { + return Lists.Map( materials, m => CreateBakingMaterial( m ) ); + } + + Material CreateBakingMaterial( Material originalMaterial ) + { + var bakingMaterial = LoadBakingMaterialForMode().Duplicate( true ) as Material; + + var materialTransfer = GetBakingMaterialTransfer(); + + this.LogInfo( mode, materialTransfer, originalMaterial, bakingMaterial ); + + materialTransfer.Transfer( originalMaterial, bakingMaterial ); + + return bakingMaterial; + } + + static readonly string transferPaths = "res://addons/rokojori_action_library/Runtime/Procedural/Baking/BakingMaterials/"; + + MaterialTransfer GetBakingMaterialTransfer() { switch ( mode ) { - case BakingMaterialMode.Albedo: return LoadMaterial( "albedo_material.material" ); - case BakingMaterialMode.Normals: return LoadMaterial( "normal_baker.material" ); - case BakingMaterialMode.ORM: return LoadMaterial( "orm_baker.material" ); - case BakingMaterialMode.Depth: return LoadMaterial( "depth_baker.material" ); - + case BakingMaterialMode.Albedo: return Load( transferPaths + "Albedo/Albedo Transfer.tres" ); + case BakingMaterialMode.Normals: return Load( transferPaths + "Normal/Normal Transfer.tres" ); + case BakingMaterialMode.ORM: return Load( transferPaths + "ORM/ORM Transfer.tres" ); + case BakingMaterialMode.Depth: return Load( transferPaths + "Depth/Depth Transfer.tres" ); } return null; } + public static readonly string materialsPath = "res://addons/rokojori_action_library/External/Imposter/materials/"; + public static readonly string depthMapPath = "res://addons/rokojori_action_library/Runtime/Shading/Shaders/Baking/DepthMap.material"; + + + public Material LoadBakingMaterialForMode() + { + switch ( mode ) + { + case BakingMaterialMode.Albedo: return Load( materialsPath + "albedo_material.material" ); + case BakingMaterialMode.Normals: return Load( materialsPath + "normal_baker.material" ); + case BakingMaterialMode.ORM: return Load( materialsPath + "orm_baker.material" ); + case BakingMaterialMode.Depth: return Load( depthMapPath ); + } + + return null; + } + + + static Dictionary _cachedResources = new Dictionary(); + + static R Load( string resourcePath ) where R:Resource + { + RJLog.Log( "Loading", resourcePath ); + + if ( ! _cachedResources.ContainsKey( resourcePath ) ) + { + + _cachedResources[ resourcePath ] = ResourceLoader.Load( resourcePath ); + + RJLog.Log( "Loaded", resourcePath, _cachedResources[ resourcePath ] ); + + if ( _cachedResources[ resourcePath ] == null ) + { + RJLog.Error( "Resource not found:", resourcePath ); + } + } + + return _cachedResources[ resourcePath ] as R; + } + + + } } \ No newline at end of file diff --git a/Runtime/Procedural/Baking/TextureMerger.cs b/Runtime/Procedural/Baking/TextureMerger.cs index e288a5c..b0fa127 100644 --- a/Runtime/Procedural/Baking/TextureMerger.cs +++ b/Runtime/Procedural/Baking/TextureMerger.cs @@ -13,6 +13,7 @@ namespace Rokojori { public enum SourceMode { + MultiBaker, Viewports, Textures } @@ -28,6 +29,10 @@ namespace Rokojori [Export] public SourceMode sourceMode; + [ExportGroup("Source/MultiBaker")] + [Export] + public MultiBaker multiBaker = null; + [ExportGroup("Source/Viewports")] [Export] @@ -63,6 +68,8 @@ namespace Rokojori [ExportGroup("Viewport")] [Export] public Vector2 textureSize = new Vector2( 2048, 2048 ); + [Export] + public float alphaThreshold = 0.1f; [Export] public BaseMaterial3D.TransparencyEnum transparencyMode = BaseMaterial3D.TransparencyEnum.AlphaScissor; @@ -98,18 +105,20 @@ namespace Rokojori outputTarget = this; } - Nodes.RemoveAndDeleteChildren( outputTarget ); + if ( X_textureMergerViewport == null ) + { + X_textureMergerViewport = outputTarget.CreateChild( "Texture Merger Viewport" ); + } - X_textureMergerViewport = outputTarget.CreateChild( "Texture Merger Viewport" ); + Nodes.RemoveAndDeleteChildren( X_textureMergerViewport ); X_mergerCamera = X_textureMergerViewport.CreateChild( "Texture Merger Camera" ); - X_mergerCamera.Projection = Camera3D.ProjectionType.Orthogonal; + X_mergerCamera.Projection = Camera3D.ProjectionType.Orthogonal; + X_mergerCamera.Size = 2; X_textureMergerViewport.Size = (Vector2I) textureSize; X_textureMergerViewport.OwnWorld3D = true; X_textureMergerViewport.TransparentBg = true; - - X_mergerCamera.Size = 1; } @@ -123,6 +132,23 @@ namespace Rokojori return; } + if ( SourceMode.MultiBaker == sourceMode ) + { + var viewports = multiBaker.GetAllViewports().ToArray(); + + var vpTextures = Arrays.Map( viewports, + s => + { + var vt = new ViewportTexture(); + vt.ViewportPath = s.GetPath(); + return vt; + } + ); + + _textures.AddRange( vpTextures ); + } + + if ( SourceMode.Viewports == sourceMode ) { if ( sourceViewportsContainer != null ) @@ -143,7 +169,7 @@ namespace Rokojori } } - void CreateLayout() + public void CreateLayout() { GrabTextures(); @@ -185,6 +211,8 @@ namespace Rokojori { var alignment = ComputeTextureAlignment( _textures.Count ); + // RJLog.Log( "Grid Alignment:", alignment ); + Nodes.RemoveAndDeleteChildrenOfType( X_textureMergerViewport ); for ( int i = 0; i < _textures.Count; i++ ) @@ -192,10 +220,12 @@ namespace Rokojori var mesh = X_textureMergerViewport.CreateChild( "Texture " + ( i + 1 ) ); var uvRectangle = GetUVRectangle( alignment, i ); - SetMeshCoordinates( mesh, uvRectangle ); + + SetMeshCoordinates( mesh, uvRectangle, i ); var material = new StandardMaterial3D(); material.Transparency = transparencyMode; + material.AlphaScissorThreshold = alphaThreshold; material.ShadingMode = BaseMaterial3D.ShadingModeEnum.Unshaded; material.AlbedoTexture = _textures[ i ]; @@ -209,34 +239,37 @@ namespace Rokojori { var mesh = outputTarget.CreateChild( "Texture " + ( i + 1 ) ); - SetMeshCoordinates( mesh, customPositions[ i ], customSizes[ i ] ); + SetMeshCoordinates( mesh, customPositions[ i ], customSizes[ i ], i ); var material = new StandardMaterial3D(); material.Transparency = transparencyMode; material.ShadingMode = BaseMaterial3D.ShadingModeEnum.Unshaded; + material.AlphaScissorThreshold = alphaThreshold; material.AlbedoTexture = _textures[ i ]; mesh.Material = material; } } - void SetMeshCoordinates( CsgMesh3D mesh, Rect2 rectangle ) + void SetMeshCoordinates( CsgMesh3D mesh, Box2 rectangle, int index ) { - SetMeshCoordinates( mesh, rectangle.Position, rectangle.Size ); + SetMeshCoordinates( mesh, rectangle.min, rectangle.max, index ); } - void SetMeshCoordinates( CsgMesh3D mesh, Vector2 start, Vector2 size ) + void SetMeshCoordinates( CsgMesh3D mesh, Vector2 min, Vector2 max, int index ) { - start = ConvertUVtoCameraSpace( start ); - var end = ConvertUVtoCameraSpace( start + size ); + var start = ConvertUVtoCameraSpace( min ); + var end = ConvertUVtoCameraSpace( max ); - size = end - start; + var size = end - start; var quadMesh = new QuadMesh(); quadMesh.Size = size; mesh.Mesh = quadMesh; - mesh.GlobalPosition = new Vector3( start.X - size.X , start.Y - size.Y, -1 ); + mesh.GlobalPosition = new Vector3( start.X + size.X/2, start.Y + size.Y/2, -1 ); + + // RJLog.Log( "Set Mesh", index, "Min:", min, ">>", start, "Max:", max, ">>", end ); } public static Vector2I ComputeTextureAlignment( int numElements ) @@ -246,22 +279,49 @@ namespace Rokojori var ceiled = Mathf.CeilToInt( root ); var floored = ceiled - 1; - var weight = Mathf.RoundToInt( root % 1 ); + if ( ceiled * floored >= numElements ) + { + return new Vector2I( ceiled, floored ); + } - var height = weight * ceiled + ( 1 - weight ) * floored; - - return new Vector2I( ceiled, height ); + return new Vector2I( ceiled, ceiled ); + } - public static Rect2 GetUVRectangle( Vector2I textureAlignment, int index ) + public static Box2 GetUVRectangle( Vector2I textureAlignment, int index, bool flipY ) + { + var b = GetUVRectangle( textureAlignment, index ); + + if ( ! flipY ) + { + return b; + } + + var uvA = b.min; + var uvB = b.max; + uvA.Y = 1f - uvA.Y; + uvB.Y = 1f - uvB.Y; + + b.min = uvA; + b.max = uvB; + + return b; + } + + public static Box2 GetUVRectangle( Vector2I textureAlignment, int index ) { var x = index % textureAlignment.X; - var y = index / textureAlignment.Y; + var y = index / textureAlignment.X; var xs = 1f / textureAlignment.X; var ys = 1f / textureAlignment.Y; - return new Rect2( x * xs, y * ys, new Vector2( xs, ys ) ); + var size = new Vector2( xs, ys ); + + var min = new Vector2( x * xs, y * ys ); + var max = min + size; + + return new Box2( min, max ); } diff --git a/Runtime/Procedural/Baking/Textures.cs b/Runtime/Procedural/Baking/Textures.cs index 2bdddfc..6884ca7 100644 --- a/Runtime/Procedural/Baking/Textures.cs +++ b/Runtime/Procedural/Baking/Textures.cs @@ -9,11 +9,20 @@ namespace Rokojori { public static class Textures { + + public static Texture2D Copy( Texture2D original ) + { + return ImageTexture.CreateFromImage( original.GetImage() ); + } + + public static void Save( Viewport viewport, string path, float quality = 0.75f ) + { + Save( viewport.GetTexture(), path, quality ); + } + public static void Save( Texture2D texture, string path, float quality = 0.75f ) { - var image = texture.GetImage(); - - Save( image, path ); + Save( texture.GetImage(), path, quality ); } public static void Save( Image image, string path, float quality = 0.75f ) diff --git a/Runtime/Procedural/ConnectionCircle.cs b/Runtime/Procedural/Connectables/ConnectionCircle.cs similarity index 100% rename from Runtime/Procedural/ConnectionCircle.cs rename to Runtime/Procedural/Connectables/ConnectionCircle.cs diff --git a/Runtime/Procedural/ConnectionPin.cs b/Runtime/Procedural/Connectables/ConnectionPin.cs similarity index 100% rename from Runtime/Procedural/ConnectionPin.cs rename to Runtime/Procedural/Connectables/ConnectionPin.cs diff --git a/Runtime/Procedural/ConnectionPinTester.cs b/Runtime/Procedural/Connectables/ConnectionPinTester.cs similarity index 100% rename from Runtime/Procedural/ConnectionPinTester.cs rename to Runtime/Procedural/Connectables/ConnectionPinTester.cs diff --git a/Runtime/Procedural/Mesh/LODMultiMeshInstance3D.cs b/Runtime/Procedural/Mesh/LODMultiMeshInstance3D.cs new file mode 100644 index 0000000..8ae2111 --- /dev/null +++ b/Runtime/Procedural/Mesh/LODMultiMeshInstance3D.cs @@ -0,0 +1,45 @@ +using System.Collections; +using System.Collections.Generic; +using Godot; +using System; + + + +namespace Rokojori +{ + [Tool] + [GlobalClass] + public partial class LODMultiMeshInstance3D:MultiMeshInstance3D + { + [Export] + public float cullDistance = 200; + + [Export] + public float cullRange = 100; + + [Export] + public Curve cullCurve = MathX.Curve( 0, 1 ); + + 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(); + + var cullAmount = MathX.NormalizeClamped( distance, cullDistance - cullRange, cullDistance ); + cullAmount = cullCurve == null ? cullAmount : cullCurve.Sample( cullAmount ); + + Multimesh.VisibleInstanceCount = Mathf.RoundToInt( Multimesh.InstanceCount * ( 1.0 - cullAmount ) ); + + } + } +} \ No newline at end of file diff --git a/Runtime/Procedural/Mesh/MassRenderer.cs b/Runtime/Procedural/Mesh/MassRenderer.cs new file mode 100644 index 0000000..89fce11 --- /dev/null +++ b/Runtime/Procedural/Mesh/MassRenderer.cs @@ -0,0 +1,217 @@ +using System.Collections; +using System.Collections.Generic; +using Godot; +using System; + + + +namespace Rokojori +{ + [Tool] + [GlobalClass] + public partial class MassRenderer:Node3D + { + public enum Mode + { + MultiMeshInstance3D, + Combined, + MeshInstance3D + } + + [Export] + public Mode mode = Mode.MultiMeshInstance3D; + + [Export] + public int randomizeQueue = 1984; + + [ExportGroup("Mesh")] + [Export] + public Mesh mesh; + + [Export] + public Material materialOveride; + + [ExportGroup("Layout")] + [Export] + public Vector3[] positions; + + [Export] + public Vector4[] rotations; + + [Export] + public Vector3[] scales; + + [ExportGroup("Outputs")] + [Export] + public LODMultiMeshInstance3D multiMeshNode; + [Export] + public Node3D meshesContainer; + [Export] + public MeshInstance3D combinedMesh; + + List _queuedTransforms = new List(); + + + + public void QueueObject( Transform3D transform ) + { + _queuedTransforms.Add( transform ); + } + + public void AddQueued() + { + if ( positions == null ) + { + positions = new Vector3[0]; + } + + if ( rotations == null ) + { + rotations = new Vector4[0]; + } + + if ( scales == null ) + { + scales = new Vector3[0]; + } + + if ( randomizeQueue >= 0 ) + { + _queuedTransforms = RandomList.Randomize( _queuedTransforms, LCG.WithSeed( randomizeQueue ) ); + } + + var queuedPositions = Lists.Map( _queuedTransforms, t => t.Origin ); + var queuedRotations = Lists.Map( _queuedTransforms, t => Math3D.QuaternionToVector4( t.Basis.GetRotationQuaternion() ) ); + var queuedScales = Lists.Map( _queuedTransforms, t => t.Basis.Scale ); + + positions = Arrays.Concat( positions, queuedPositions.ToArray() ); + rotations = Arrays.Concat( rotations, queuedRotations.ToArray() ); + scales = Arrays.Concat( scales, queuedScales.ToArray() ); + + _queuedTransforms.Clear(); + } + + public void Create() + { + AddQueued(); + + if ( Mode.MultiMeshInstance3D == mode ) + { + CreateMultiMeshes(); + } + else if ( Mode.MeshInstance3D == mode ) + { + CreateMeshes(); + } + else if ( Mode.Combined == mode ) + { + CreateCombined(); + } + + } + + public void Clear() + { + multiMeshNode = Nodes.EnsureValid( multiMeshNode ); + meshesContainer = Nodes.EnsureValid( meshesContainer ); + combinedMesh = Nodes.EnsureValid( combinedMesh ); + + + if ( multiMeshNode != null ) + { + multiMeshNode.Multimesh.InstanceCount = 0; + } + + if ( meshesContainer != null ) + { + Nodes.RemoveAndDeleteChildren( meshesContainer ); + } + + if ( combinedMesh != null ) + { + combinedMesh.Mesh = null; + } + } + + void CreateMultiMeshes() + { + multiMeshNode = Nodes.EnsureValid( multiMeshNode ); + + if ( multiMeshNode == null ) + { + multiMeshNode = this.CreateChild(); + multiMeshNode.Multimesh = new MultiMesh(); + multiMeshNode.Multimesh.TransformFormat = MultiMesh.TransformFormatEnum.Transform3D; + } + + var mm = multiMeshNode.Multimesh; + + + mm.Mesh = mesh; + mm.InstanceCount = positions.Length; + + if ( materialOveride != null ) + { + multiMeshNode.MaterialOverride = materialOveride; + } + + for ( int i = 0; i < positions.Length; i++ ) + { + var trsf = Math3D.TRS( positions[ i ], rotations[ i ], scales[ i ] ); + mm.SetInstanceTransform( i, trsf ); + } + + + + } + + void CreateMeshes() + { + if ( meshesContainer == null ) + { + meshesContainer = this.CreateChild(); + } + + for ( int i = 0; i < positions.Length; i++ ) + { + var trsf = Math3D.TRS( positions[ i ], rotations[ i ], scales[ i ] ); + + var child = meshesContainer.CreateChild(); + child.Mesh = mesh; + + if ( materialOveride != null ) + { + child.MaterialOverride = materialOveride; + } + + child.GlobalTransform = trsf; + } + } + + void CreateCombined() + { + if ( meshesContainer == null ) + { + combinedMesh = this.CreateChild(); + } + + var combinedMG = new MeshGeometry(); + + var meshMG = MeshGeometry.From( mesh as ArrayMesh ); + + for ( int i = 0; i < positions.Length; i++ ) + { + var trsf = Math3D.TRS( positions[ i ], rotations[ i ], scales[ i ] ); + combinedMG.Add( meshMG, trsf ); + } + + if ( materialOveride != null ) + { + combinedMesh.MaterialOverride = materialOveride; + } + + } + + + } +} \ No newline at end of file diff --git a/Runtime/Procedural/Mesh/MeshCombiner.cs b/Runtime/Procedural/Mesh/MeshCombiner.cs new file mode 100644 index 0000000..b9891b8 --- /dev/null +++ b/Runtime/Procedural/Mesh/MeshCombiner.cs @@ -0,0 +1,205 @@ +using System.Collections; +using System.Collections.Generic; +using Godot; +using System; + + + +namespace Rokojori +{ + [Tool] + [GlobalClass] + public partial class MeshCombiner:Node3D + { + [Export] + public Node3D[] sourceNodes; + + [ExportGroup( "Mesh")] + [Export] + public bool combineMeshes = true; + + public enum UVCombineMode + { + Keep, + Adjust_To_Combined_Material + } + + [Export] + public UVCombineMode uVCombineMode = UVCombineMode.Adjust_To_Combined_Material; + + [Export] + public Node3D pivot; + + [ExportGroup( "Material")] + [Export] + public bool combineMaterials = true; + + public enum TextureSizeMode + { + KeepOriginal, + Custom + } + + [Export] + public TextureSizeMode textureSizeMode = TextureSizeMode.Custom; + + [Export] + public Vector2I customTextureSize = new Vector2I( 1024, 1024 ); + + + [ExportGroup( "Output")] + [Export] + public MeshInstance3D outputMesh; + [Export] + public Material[] outputMaterials; + [Export] + public bool combine = false; + + public override void _Process( double delta ) + { + if ( ! combine ) + { + return; + } + + combine = false; + + Combine(); + } + + MultiMap _meshGeometryCache = new MultiMap(); + + MeshGeometry GetMeshGeometry( MeshSurface surface ) + { + if ( ! _meshGeometryCache.Has( surface.mesh, surface.index ) ) + { + var mg = MeshGeometry.From( surface.mesh as ArrayMesh, null, surface.index ); + _meshGeometryCache.Set( surface.mesh, surface.index, mg ); + + this.LogInfo( "Created mesh with triangles:", mg.numTriangles ); + } + + return _meshGeometryCache[ surface.mesh ][ surface.index ]; + } + + List> _surfaces; + List _materials; + MapList> _materiaList = new MapList>() ; + Dictionary _uvTransform = new Dictionary(); + + + public void Combine() + { + GrabSurfaces(); + GrabMaterials(); + + CombineMaterials(); + CombineMeshes(); + } + + void GrabSurfaces() + { + _surfaces = new List>(); + + foreach ( var n in sourceNodes ) + { + + MeshExtractor.ExtractSurfacesInHierarchy( n, _surfaces ); + } + } + + void GrabMaterials() + { + _materials = new List(); + _materiaList = new MapList>(); + + var set = new HashSet(); + + _surfaces.ForEach( + ( s )=> + { + _materiaList.Add( s.item.material, s ); + + if ( set.Contains( s.item.material ) ) + { + return; + } + + set.Add( s.item.material ); + _materials.Add( s.item.material ); + + } + ); + + + } + + void CombineMaterials() + { + + } + + void CombineMeshes() + { + var arrayMesh = new ArrayMesh(); + + this.LogInfo( "Combining", _surfaces.Count, "meshes" ); + + var index = 0; + var nm = 0; + var max = 100; + _materials.ForEach( + ( m )=> + { + Transform2D? uvTransform = _uvTransform.ContainsKey( m ) ? _uvTransform[ m ] : null; + + var surfaces = _materiaList[ m ]; + + this.LogInfo( "Combining for Material", m, surfaces.Count, "meshes" ); + + var meshGeometry = new MeshGeometry(); + + surfaces.ForEach( + ( s )=> + { + var smg = GetMeshGeometry( s.item ).Clone(); + + if ( uvTransform != null ) + { + smg.ApplyUVTransform( (Transform2D) uvTransform ); + } + + var trsf = s.transform; + + if ( pivot != null ) + { + trsf.Origin -= pivot.GlobalPosition; + } + + smg.ApplyTransform( trsf ); + + meshGeometry.Add( smg ); + } + ); + + meshGeometry.GenerateMesh( Mesh.PrimitiveType.Triangles, arrayMesh ); + + + arrayMesh.SurfaceSetMaterial( index, m ); + index ++; + + } + ); + + if ( outputMesh == null ) + { + outputMesh = this.CreateChild(); + } + + outputMesh.Mesh = arrayMesh; + outputMaterials = _materials.ToArray(); + } + + + } +} \ No newline at end of file diff --git a/Runtime/Procedural/Mesh/MeshExtractor.cs b/Runtime/Procedural/Mesh/MeshExtractor.cs new file mode 100644 index 0000000..b13f140 --- /dev/null +++ b/Runtime/Procedural/Mesh/MeshExtractor.cs @@ -0,0 +1,160 @@ +using System.Collections; +using System.Collections.Generic; +using Godot; +using System; + + + +namespace Rokojori +{ + public class MeshExtractor + { + public static bool CanExtract( Node3D n ) + { + if ( n is MeshInstance3D mi ) + { + return mi.Mesh != null; + } + + if ( n is MultiMeshInstance3D mmi ) + { + var mm = mmi.Multimesh; + return mm.Mesh != null; + } + + return false; + } + + public static List> ExtractMeshesInHierarchy( Node n, List> list = null ) + { + list = list == null ? new List>() : list; + + Nodes.ForEach( n, + + n => + { + ExtractMeshes( n, list ); + } + ); + + return list; + } + + public static List> ExtractMeshes( Node n, List> list = null ) + { + list = list == null ? new List>() : list; + + + if ( n is MeshInstance3D mi ) + { + var mesh = mi.Mesh; + + var singleMaterialMesh = new SingleMaterialMesh( mesh ); + + if ( mi.GetSurfaceOverrideMaterialCount() > 0 && mi.GetSurfaceOverrideMaterial( 0 ) != null ) + { + singleMaterialMesh.material = mi.GetSurfaceOverrideMaterial( 0 ); + } + + if ( mi.MaterialOverride != null ) + { + singleMaterialMesh.material = mi.MaterialOverride; + } + + list.Add( new Transformable( singleMaterialMesh, mi.GlobalTransform ) ); + } + + if ( n is MultiMeshInstance3D mmi ) + { + var mm = mmi.Multimesh; + + for ( var j = 0; j < mm.InstanceCount; j++ ) + { + var mesh = mm.Mesh; + var transform = mm.GetInstanceTransform( j ); + + var singleMaterialMesh = new SingleMaterialMesh( mesh ); + + if ( mmi.MaterialOverride != null ) + { + singleMaterialMesh.material = mmi.MaterialOverride; + } + + list.Add( new Transformable( singleMaterialMesh, transform ) ); + } + } + + return list; + } + + public static List> ExtractSurfacesInHierarchy( Node n, List> list = null ) + { + list = list == null ? new List>() : list; + + Nodes.ForEach( n, + + n => + { + ExtractSurfaces( n, list ); + } + ); + + return list; + } + + public static List> ExtractSurfaces( Node n, List> list = null ) + { + list = list == null ? new List>() : list; + + + if ( n is MeshInstance3D mi ) + { + var mesh = mi.Mesh; + + for ( int i = 0; i < mesh.GetSurfaceCount(); i++ ) + { + var surface = new MeshSurface( mesh, mesh.SurfaceGetMaterial( i ), i ); + + if ( i < mi.GetSurfaceOverrideMaterialCount() && mi.GetSurfaceOverrideMaterial( i ) != null ) + { + surface.material = mi.GetSurfaceOverrideMaterial( i ); + } + + if ( mi.MaterialOverride != null ) + { + surface.material = mi.MaterialOverride; + } + + list.Add( new Transformable( surface, mi.GlobalTransform ) ); + } + } + + if ( n is MultiMeshInstance3D mmi ) + { + var mm = mmi.Multimesh; + + for ( var j = 0; j < mm.InstanceCount; j++ ) + { + var mesh = mm.Mesh; + var transform = mm.GetInstanceTransform( j ); + + for ( int i = 0; i < mesh.GetSurfaceCount(); i++ ) + { + var surface = new MeshSurface( mesh, mesh.SurfaceGetMaterial( i ), i ); + + if ( mmi.MaterialOverride != null ) + { + surface.material = mmi.MaterialOverride; + } + + list.Add( new Transformable( surface, transform ) ); + } + } + } + + return list; + } + + } + +} \ No newline at end of file diff --git a/Runtime/Procedural/MeshGeometry.cs b/Runtime/Procedural/Mesh/MeshGeometry.cs similarity index 67% rename from Runtime/Procedural/MeshGeometry.cs rename to Runtime/Procedural/Mesh/MeshGeometry.cs index 972adc8..54c2533 100644 --- a/Runtime/Procedural/MeshGeometry.cs +++ b/Runtime/Procedural/Mesh/MeshGeometry.cs @@ -25,11 +25,11 @@ namespace Rokojori return From( (ArrayMesh)meshInstance3D.Mesh, meshInstance3D.GlobalTransform ); } - public static MeshGeometry From( ArrayMesh mesh, Transform3D? trsf = null ) + public static MeshGeometry From( ArrayMesh mesh, Transform3D? trsf = null, int index = 0 ) { var mg = new MeshGeometry(); - var arrays = mesh.SurfaceGetArrays( 0 ); + var arrays = mesh.SurfaceGetArrays( index ); var vertices = arrays[ (int) Mesh.ArrayType.Vertex ]; @@ -81,15 +81,83 @@ namespace Rokojori return mg; } - public void ApplyTransform( Transform3D trsf ) + public void ApplyTransform( Vector3 translation, Quaternion rotation, Vector3 scale, int start = 0, int length = -1 ) { - for ( int i = 0; i < vertices.Count; i++ ) + ApplyTransform( Math3D.TRS( translation, rotation, scale ), start, length ); + } + + public void ApplyTranslation( Vector3 translation, int start = 0, int length = -1 ) + { + if ( start < 0 ) + { + start = vertices.Count + start; + } + + length = length < 0 ? ( vertices.Count - start ) : length; + + var end = start + length; + + for ( int i = start; i < end; i++ ) + { + vertices[ i ] = vertices[ i ] + translation; + } + } + + + public void ApplyUVTransform( Transform2D transform, int start = 0, int length = -1 ) + { + if ( start < 0 ) + { + start = vertices.Count + start; + } + + length = length < 0 ? ( vertices.Count - start ) : length; + + var end = start + length; + + for ( int i = start; i < end; i++ ) + { + uvs[ i ] = transform * uvs[ i ]; + } + } + + + public void ApplyTransform( Transform3D trsf, int start = 0, int length = -1 ) + { + if ( start < 0 ) + { + start = vertices.Count + start; + } + + length = length < 0 ? ( vertices.Count - start ) : length; + + var end = start + length; + + for ( int i = start; i < end; i++ ) { vertices[ i ] = trsf * vertices[ i ]; normals[ i ] = trsf.Basis.GetRotationQuaternion() * normals[ i ]; } } + public void ApplyTransformWithPivot( Transform3D trsf, Vector3 pivot, int start = 0, int length = -1 ) + { + if ( start < 0 ) + { + start = vertices.Count + start; + } + + length = length < 0 ? ( vertices.Count - start ) : length; + + var end = start + length; + + for ( int i = start; i < end; i++ ) + { + vertices[ i ] = ( trsf * ( vertices[ i ] - pivot ) ) + pivot; + normals[ i ] = trsf.Basis.GetRotationQuaternion() * normals[ i ]; + } + } + public void BlendNormals( Vector3 direction, float amount ) { if ( amount <= 0 ) @@ -143,9 +211,43 @@ namespace Rokojori } - public static MeshGeometry CreateFromUVFunction( Func uv, int uSegments, int vSegments ) + public static MeshGeometry CreateFromUVFunction( Func uv, int uSegments, int vSegments, bool fullUVQuads = false ) { var mg = new MeshGeometry(); + + if ( fullUVQuads ) + { + var uv00 = new Vector2( 0, 0 ); + var uv10 = new Vector2( 1, 0 ); + var uv01 = new Vector2( 0, 1 ); + var uv11 = new Vector2( 1, 1 ); + + for ( int i = 0; i < uSegments; i++ ) + { + var u0 = (float)i / uSegments; + var u1 = (float)(i + 1 ) / uSegments; + + for ( int j = 0; j < vSegments; j++ ) + { + var v0 = (float)j / vSegments; + var v1 = (float)(j + 1 ) / vSegments; + + var p00 = uv( new Vector2( u0, v0 ) ); + var p10 = uv( new Vector2( u1, v0 ) ); + var p01 = uv( new Vector2( u0, v1 ) ); + var p11 = uv( new Vector2( u1, v1 ) ); + var n = ( p00.up + p10.up + p01.up + p11.up ) / 4f; + + mg.AddQuad( + p00.position, p10.position, p11.position, p01.position, + n, n, n, n, + uv00, uv10, uv11, uv01 + ); + } + } + + return mg; + } for ( int i = 0; i <= uSegments; i++ ) { @@ -291,6 +393,24 @@ namespace Rokojori Initialize( normals, uvs, colors, uvs2 ); } + public static MeshGeometry BillboardQuad( float size = 1 ) + { + var hs = size / 2f; + + var mg = new MeshGeometry(); + mg.AddQuad( + new Vector3( -hs, -hs, 0 ), new Vector3( hs, -hs, 0 ), + new Vector3( hs, hs, 0 ), new Vector3( -hs, hs, 0 ), + + Vector3.Forward, Vector3.Forward, Vector3.Forward, Vector3.Forward, + + new Vector2( 0, 0 ), new Vector2( 1, 0 ), + new Vector2( 1, 1 ), new Vector2( 0, 1 ) + ); + + return mg; + } + public void Initialize( bool normals = true, bool uvs = true, bool colors = false, bool uvs2 = false ) { this.normals = normals ? new List() : null; @@ -310,7 +430,7 @@ namespace Rokojori public void ComputeNormals() { - var dl = new DictionaryList(); + var dl = new MapList(); ForEachTriangle( ( t, va, vb, vc)=> @@ -360,54 +480,77 @@ namespace Rokojori indices[ i ] = indices[ i + 2 ]; indices[ i + 2 ] = b; } + } - public void Add( MeshGeometry mg ) + + + public void Add( MeshGeometry sourceGeometry, Transform3D? optionalTransform = null ) { var mappedIndices = new Dictionary(); - for ( int i = 0; i < mg.indices.Count; i++ ) - { - var mgIndex = mg.indices[ i ]; + var rotation = optionalTransform == null ? Quaternion.Identity : ( (Transform3D)optionalTransform ).Basis.GetRotationQuaternion(); + var transform = optionalTransform == null ? Transform3D.Identity : (Transform3D)optionalTransform; - if ( ! mappedIndices.ContainsKey( mgIndex ) ) + for ( int i = 0; i < sourceGeometry.indices.Count; i++ ) + { + var sourceIndex = sourceGeometry.indices[ i ]; + + if ( ! mappedIndices.ContainsKey( sourceIndex ) ) { var newIndex = vertices.Count; - if ( mgIndex >= mg.vertices.Count || mgIndex < 0 ) + if ( sourceIndex >= sourceGeometry.vertices.Count || sourceIndex < 0 ) { - RJLog.Log( "Out of range:", i, ">>", mgIndex, mg.vertices.Count, mg.indices ); + RJLog.Log( "Out of range:", i, ">>", sourceIndex, sourceGeometry.vertices.Count, sourceGeometry.indices ); } - var v = mg.vertices[ mgIndex ]; + var v = sourceGeometry.vertices[ sourceIndex ]; - vertices.Add( v ); - - if ( normals != null && mg.normals != null) + if ( optionalTransform == null ) { - normals.Add( mg.normals[ mgIndex ] ); + vertices.Add( v ); + } + else + { + vertices.Add( transform * v ); } - if ( uvs != null && mg.uvs != null) + + + if ( normals != null && sourceGeometry.normals != null) { - uvs.Add( mg.uvs[ mgIndex ] ); + if ( optionalTransform == null ) + { + normals.Add( sourceGeometry.normals[ sourceIndex ] ); + } + else + { + normals.Add( rotation * sourceGeometry.normals[ sourceIndex ] ); + } + } - if ( colors != null && mg.colors != null) + if ( uvs != null && sourceGeometry.uvs != null) { - colors.Add( mg.colors[ mgIndex ] ); + uvs.Add( sourceGeometry.uvs[ sourceIndex ] ); } - if ( uv2s != null && mg.uv2s != null) + if ( colors != null && sourceGeometry.colors != null) { - uv2s.Add( mg.uv2s[ mgIndex ] ); + colors.Add( sourceGeometry.colors[ sourceIndex ] ); + } + + if ( uv2s != null && sourceGeometry.uv2s != null) + { + uv2s.Add( sourceGeometry.uv2s[ sourceIndex ] ); } - mappedIndices[ mgIndex ] = newIndex; + mappedIndices[ sourceIndex ] = newIndex; } - indices.Add( mappedIndices[ mgIndex ] ); + indices.Add( mappedIndices[ sourceIndex ] ); } } @@ -433,6 +576,7 @@ namespace Rokojori ) { var index = vertices.Count; + Lists.Add( vertices, va, vb, vc ); Lists.Add( normals, na, nb, nc ); Lists.Add( uvs, uva, uvb, uvc ); @@ -469,24 +613,51 @@ namespace Rokojori Vector2 uva, Vector2 uvb, Vector2 uvc, Vector2 uvd ) { - AddTriangle( va, vb, vc, na, nb, nc, uva, uvb, uvc ); - AddTriangle( vc, vd, va, nc, nd, na, uvc, uvd, uva ); + + var index = vertices.Count; + + Lists.Add( vertices, va, vb, vc, vd ); + Lists.Add( normals, na, nb, nc, nd ); + Lists.Add( uvs, uva, uvb, uvc, uvd ); + + Lists.Add( indices, index, index + 1, index + 2 ); + Lists.Add( indices, index + 2, index + 3, index ); } - public void AddQuad( Quaternion rotation, float size, Rect2 rectangle ) + public void AddQuad( Quaternion rotation, float size, Box2 rectangle ) { - AddQuad( rotation, size, rectangle.Position, rectangle.End ); + AddQuad( rotation, size, rectangle.min, rectangle.max ); + } + + + public void DuplicateRange( int verticesStart, int verticesLength, int indexStart, int indicesLength ) + { + for ( int i = verticesStart; i < verticesStart + verticesLength; i++ ) + { + var v = vertices[ i ]; + var n = normals[ i ]; + var uv = uvs[ i ]; + + vertices.Add( v ) ; + normals.Add( n ); + uvs.Add( uv ); + } + + for ( int i = indexStart; i < indexStart + indicesLength; i++ ) + { + indices.Add( indices[ i ] ); + } } public void AddQuad( Quaternion rotation, float size, Vector2 uv00, Vector2 uv11 ) { var l = size * 0.5f; - var normal = Vector3.Forward * rotation; + var normal = Vector3.Back * rotation; var points = new List { - new Vector3( -l, -l, 0 ), new Vector3( l, -l, 0 ), - new Vector3( -l, l, 0 ), new Vector3( l, l, 0 ) + new Vector3( l, -l, 0 ), new Vector3( -l, -l, 0 ), + new Vector3( -l, l, 0 ), new Vector3( l, l, 0 ) }; for ( int i = 0; i < points.Count; i++ ) @@ -500,7 +671,7 @@ namespace Rokojori AddQuad( points[ 0 ], points[ 1 ], points[ 2 ], points[ 3 ], normal, normal, normal, normal, - uv00, uv10, uv11, uv10 + uv10, uv00, uv01, uv11 ); } @@ -574,7 +745,7 @@ namespace Rokojori } } - + public ArrayMesh GenerateMesh( Mesh.PrimitiveType type = Mesh.PrimitiveType.Triangles, ArrayMesh arrayMesh = null ) { diff --git a/Runtime/Procedural/Mesh/MeshSurface.cs b/Runtime/Procedural/Mesh/MeshSurface.cs new file mode 100644 index 0000000..099f3ee --- /dev/null +++ b/Runtime/Procedural/Mesh/MeshSurface.cs @@ -0,0 +1,23 @@ +using System.Collections; +using System.Collections.Generic; +using Godot; +using System; + + + +namespace Rokojori +{ + public class MeshSurface + { + public Mesh mesh; + public Material material; + public int index; + + public MeshSurface( Mesh mesh, Material material, int index = 0 ) + { + this.index = index; + this.mesh = mesh; + this.material = material; + } + } +} diff --git a/Runtime/Procedural/Mesh/SingleMaterialMesh.cs b/Runtime/Procedural/Mesh/SingleMaterialMesh.cs new file mode 100644 index 0000000..a95510c --- /dev/null +++ b/Runtime/Procedural/Mesh/SingleMaterialMesh.cs @@ -0,0 +1,21 @@ +using System.Collections; +using System.Collections.Generic; +using Godot; +using System; + + + +namespace Rokojori +{ + public class SingleMaterialMesh + { + public Mesh mesh; + public Material material; + + public SingleMaterialMesh( Mesh mesh, Material material = null ) + { + this.mesh = mesh; + this.material = material; + } + } +} diff --git a/Runtime/Procedural/Parametric/Deformer/Deformer.cs b/Runtime/Procedural/Parametric/Deformer/Deformer.cs index 89b3693..a617439 100644 --- a/Runtime/Procedural/Parametric/Deformer/Deformer.cs +++ b/Runtime/Procedural/Parametric/Deformer/Deformer.cs @@ -98,7 +98,7 @@ namespace Rokojori deformerMappings = new MappingData[ mappingSize]; } - RJLog.Log( "Mappings:", deformerMappings.Length, meshGeometry ); + this.LogInfo( "Mappings:", deformerMappings.Length, meshGeometry ); for ( int i = 0; i < meshGeometry.vertices.Count; i++ ) { diff --git a/Runtime/Procedural/Parametric/QuadBillboards/QuadBillboardData.cs b/Runtime/Procedural/Parametric/QuadBillboards/QuadBillboardData.cs new file mode 100644 index 0000000..af75b4e --- /dev/null +++ b/Runtime/Procedural/Parametric/QuadBillboards/QuadBillboardData.cs @@ -0,0 +1,14 @@ +using System.Collections; +using System.Collections.Generic; +using Godot; +using System; + + + +namespace Rokojori +{ + public class QuadBillboardData:PointData + { + + } +} \ No newline at end of file diff --git a/Runtime/Procedural/Parametric/QuadBillboards/QuadBillboardDataProcessor.cs b/Runtime/Procedural/Parametric/QuadBillboards/QuadBillboardDataProcessor.cs new file mode 100644 index 0000000..0cdf29c --- /dev/null +++ b/Runtime/Procedural/Parametric/QuadBillboards/QuadBillboardDataProcessor.cs @@ -0,0 +1,31 @@ +using System.Collections; +using System.Collections.Generic; +using Godot; +using System; + + + +namespace Rokojori +{ + [Tool] + [GlobalClass] + public partial class QuadBillboardDataProcessor:Node3D + { + int _updateID = 0; + + protected void IncrementUpdateID() + { + _updateID ++; + } + + public int UpdateID() + { + return _updateID; + } + + public virtual List Process( List data ) + { + return data; + } + } +} \ No newline at end of file diff --git a/Runtime/Procedural/Parametric/QuadBillboards/QuadBillboardMeshGenerator.cs b/Runtime/Procedural/Parametric/QuadBillboards/QuadBillboardMeshGenerator.cs new file mode 100644 index 0000000..9bb7070 --- /dev/null +++ b/Runtime/Procedural/Parametric/QuadBillboards/QuadBillboardMeshGenerator.cs @@ -0,0 +1,108 @@ +using System.Collections; +using System.Collections.Generic; +using Godot; +using System; + + + +namespace Rokojori +{ + [Tool] + [GlobalClass] + public partial class QuadBillboardMeshGenerator:MeshInstance3D + { + [Export] + public float quadSize = 0.0001f; + + List _updateIDs = new List(); + SerializedGodotObject _cachedObject; + + public override void _Process( double delta ) + { + Generate(); + } + + public void Generate() + { + var currentObject = SerializedGodotObject.Create( this ); + + var changed = ! currentObject.Equals( _cachedObject ); + + if ( ! changed ) + { + var updateList = Lists.Map( Nodes.GetDirectChildren( this ), p => p.UpdateID() ); + + if ( updateList.Count == _updateIDs.Count ) + { + for ( int i = 0; ! changed && i < _updateIDs.Count; i++ ) + { + if ( _updateIDs[ i ] != updateList[ i ] ) + { + changed = true; + } + } + + } + else + { + changed = true; + } + + if ( changed ) + { + _updateIDs = updateList; + } + } + + if ( ! changed ) + { + return; + } + + _cachedObject = currentObject; + + + var list = new List(); + + this.OnAllDirectChildren( + ( p )=> + { + list = p.Process( list ); + } + ); + + GenerateMesh( list ); + } + + void GenerateMesh( List data ) + { + if ( data.Count == 0 ) + { + RJLog.Log( "Not Creating" ); + return; + } + + RJLog.Log( "Creating:", data.Count ); + + var mg = new MeshGeometry(); + + data.ForEach( + ( qd )=> + { + if ( ! qd.visible ) + { + return; + } + + var q = MeshGeometry.BillboardQuad( quadSize ); + + q.ApplyTransform( qd.globalTransform3D ); + + mg.Add( q ); + } + ); + + Mesh = mg.GenerateMesh(); + } + } +} \ No newline at end of file diff --git a/Runtime/Procedural/Parametric/QuadBillboards/QuadBillboardsFromMesh.cs b/Runtime/Procedural/Parametric/QuadBillboards/QuadBillboardsFromMesh.cs new file mode 100644 index 0000000..31dbaad --- /dev/null +++ b/Runtime/Procedural/Parametric/QuadBillboards/QuadBillboardsFromMesh.cs @@ -0,0 +1,94 @@ +using System.Collections; +using System.Collections.Generic; +using Godot; +using System; + + + +namespace Rokojori +{ + [Tool] + [GlobalClass] + public partial class QuadBillboardsFromMesh:QuadBillboardDataProcessor + { + [Export] + public float density = 1; + + [Export] + public MeshInstance3D source; + + [Export] + public bool updateMesh = false; + + MeshGeometry _cached = null; + + public override List Process( List data ) + { + if ( source == null || source.Mesh == null ) + { + _cached = null; + return data; + } + + if ( _cached == null || updateMesh ) + { + _cached = MeshGeometry.From( source ); + } + + var parent = GetParent(); + + var tri = new Triangle3( Vector3.Zero, Vector3.One, Vector3.One * 2 ); + + _cached.ForEachTriangle( + ( t, a, b, c )=> + { + var tri = Triangle3.CreateFrom( _cached, t ); + tri = tri.Shrink( density / 2f ); + + if ( tri == null ) + { + return; + } + + var normal = tri.normal; + + var outline = tri.GetOutline(); + + var length = outline.ComputeLength(-1); + + while ( outline != null && outline.ComputeLength(0) > density ) + { + var numPoints = Mathf.Ceil( length / density ); + + for ( int i = 0; i < numPoints; i++ ) + { + var s = (float)i / ( numPoints ); + var p = outline.PositionAt( s ); + } + + tri = tri.Shrink( density ); + + if ( tri == null ) + { + outline =null; + } + else + { + outline = tri.GetOutline(); + } + + } + } + ); + + var d = new QuadBillboardData(); + d.position = GlobalPosition - parent.GlobalPosition; + d.rotation = this.GetGlobalQuaternion(); + d.scale = Scale; + d.visible = true; + + data.Add( d ); + return data; + } + } +} \ No newline at end of file diff --git a/Runtime/Procedural/Parametric/QuadBillboards/SingleQuadBillboard.cs b/Runtime/Procedural/Parametric/QuadBillboards/SingleQuadBillboard.cs new file mode 100644 index 0000000..2d28e87 --- /dev/null +++ b/Runtime/Procedural/Parametric/QuadBillboards/SingleQuadBillboard.cs @@ -0,0 +1,41 @@ +using System.Collections; +using System.Collections.Generic; +using Godot; +using System; + + + +namespace Rokojori +{ + [Tool] + [GlobalClass] + public partial class SingleQuadBillboard:QuadBillboardDataProcessor + { + TransformChange transformChange = new TransformChange(); + + + public override void _Process( double delta ) + { + var changed = transformChange.Check( this ); + + if ( changed ) + { + IncrementUpdateID(); + } + } + + + public override List Process( List data ) + { + var parent = GetParent(); + var d = new QuadBillboardData(); + d.position = GlobalPosition - parent.GlobalPosition; + d.rotation = this.GetGlobalQuaternion(); + d.scale = Scale; + d.visible = true; + + data.Add( d ); + return data; + } + } +} \ No newline at end of file diff --git a/Runtime/Procedural/Parametric/Tube/Tube.cs b/Runtime/Procedural/Parametric/Tube/Tube.cs index 3e0c174..80e9736 100644 --- a/Runtime/Procedural/Parametric/Tube/Tube.cs +++ b/Runtime/Procedural/Parametric/Tube/Tube.cs @@ -33,6 +33,9 @@ namespace Rokojori [Export] public TubeSegmentMode segmentMode = TubeSegmentMode.Fixed_Division; + [Export] + public bool useFullUVQuads = false; + [Export] public int fixedSplineSegmentDivisions = 20; @@ -252,7 +255,7 @@ namespace Rokojori } }, - radialSegments, splineSegments + radialSegments, splineSegments, useFullUVQuads ); return mg; diff --git a/Runtime/Procedural/Points/PointData.cs b/Runtime/Procedural/Points/PointData.cs new file mode 100644 index 0000000..110dc21 --- /dev/null +++ b/Runtime/Procedural/Points/PointData.cs @@ -0,0 +1,63 @@ +using System.Collections; +using System.Collections.Generic; +using Godot; +using System; + + + +namespace Rokojori +{ + public class PointData + { + public bool visible = false; + public Vector3 position = Vector3.Zero; + public bool useGlobalPosition = true; + public Quaternion rotation = Quaternion.Identity; + public bool useGlobalRotation = true; + public Vector3 scale = Vector3.One; + public int seed; + public Node3D parent; + + public Vector3 globalPosition => useGlobalPosition || parent == null ? + position : parent.ToGlobal( position ); + + public Quaternion globalRotation => useGlobalRotation || parent == null ? + rotation : parent.GetGlobalQuaternion() * rotation; + + + public Vector3 globalScale => parent == null ? scale : parent.Scale * scale; + + public Transform3D globalTransform3D + { + get + { + return Math3D.TRS( globalPosition, globalRotation, globalScale ); + } + } + + + + public void ApplyTransform( Node3D node3D ) + { + if ( useGlobalPosition ) + { + node3D.GlobalPosition = position; + } + else + { + node3D.Position = position; + } + + if ( useGlobalRotation ) + { + Math3D.SetGlobalRotationTo( node3D, rotation.Normalized() ); + } + else + { + node3D.Quaternion = rotation; + } + + node3D.Scale = scale; + } + } +} \ No newline at end of file diff --git a/Runtime/Procedural/Points/Transformable.cs b/Runtime/Procedural/Points/Transformable.cs new file mode 100644 index 0000000..61ad838 --- /dev/null +++ b/Runtime/Procedural/Points/Transformable.cs @@ -0,0 +1,27 @@ +using System.Collections; +using System.Collections.Generic; +using Godot; +using System; + + + +namespace Rokojori +{ + public class Transformable + { + public Transform3D transform; + public T item; + + public Transformable( T data, Transform3D transform3D ) + { + this.item = data; + this.transform = transform3D; + } + + public Transformable( T data ) + { + this.item = data; + transform = Transform3D.Identity; + } + } +} \ No newline at end of file diff --git a/Runtime/Procedural/Random/RandomFloat.cs b/Runtime/Procedural/Random/RandomFloat.cs new file mode 100644 index 0000000..707cb1a --- /dev/null +++ b/Runtime/Procedural/Random/RandomFloat.cs @@ -0,0 +1,33 @@ +using System.Collections; +using System.Collections.Generic; +using Godot; +using System; + + + +namespace Rokojori +{ + [Tool] + [GlobalClass ] + public partial class RandomFloat:Resource + { + [Export] + public float staticScale; + + [Export] + public float staticOffset; + + [Export] + public Curve seedCurveMultiply = MathX.Curve( 1, 1 ); + + [Export] + public float perlinScale = 1; + + [Export] + public Vector3 perlinOffset = Vector3.Zero; + + + + + } +} \ No newline at end of file diff --git a/Runtime/Procedural/Scatter/Discarder/Discarder.cs b/Runtime/Procedural/Scatter/Discarder/Discarder.cs index e22a226..ffacf3a 100644 --- a/Runtime/Procedural/Scatter/Discarder/Discarder.cs +++ b/Runtime/Procedural/Scatter/Discarder/Discarder.cs @@ -25,7 +25,7 @@ namespace Rokojori return true; } - protected override List _Scatter( List points ) + protected override List _Scatter( List points, Scatterer root ) { points.ForEach( p => { diff --git a/Runtime/Procedural/Scatter/Generators/GenerateFence.cs b/Runtime/Procedural/Scatter/Generators/GenerateFence.cs index 2a14748..0929fd6 100644 --- a/Runtime/Procedural/Scatter/Generators/GenerateFence.cs +++ b/Runtime/Procedural/Scatter/Generators/GenerateFence.cs @@ -43,7 +43,7 @@ namespace Rokojori SplineCurve last; LerpCurve3 equalSpacedCurve; - protected override List _Scatter( List points ) + protected override List _Scatter( List points, Scatterer root ) { CreateWeights(); var curve = spline.GetCurve(); diff --git a/Runtime/Procedural/Scatter/Generators/GenerateInBox.cs b/Runtime/Procedural/Scatter/Generators/GenerateInBox.cs index 3332ad7..04ed5d8 100644 --- a/Runtime/Procedural/Scatter/Generators/GenerateInBox.cs +++ b/Runtime/Procedural/Scatter/Generators/GenerateInBox.cs @@ -19,12 +19,12 @@ namespace Rokojori [Export] public bool xzOnly = true; [Export] - public float density = 10; + public float density = 1; [Export] public bool snapToWorldGrid = false; - protected override List _Scatter( List points ) + protected override List _Scatter( List points, Scatterer root ) { CreateWeights(); diff --git a/Runtime/Procedural/Scatter/Generators/GenerateOnSpline.cs b/Runtime/Procedural/Scatter/Generators/GenerateOnSpline.cs index e3adbec..df0c07a 100644 --- a/Runtime/Procedural/Scatter/Generators/GenerateOnSpline.cs +++ b/Runtime/Procedural/Scatter/Generators/GenerateOnSpline.cs @@ -23,7 +23,7 @@ namespace Rokojori SplineCurve last; LerpCurve3 equalSpacedCurve; - protected override List _Scatter( List points ) + protected override List _Scatter( List points, Scatterer root ) { CreateWeights(); var curve = spline.GetCurve(); diff --git a/Runtime/Procedural/Scatter/Generators/GeneratePinBoundary.cs b/Runtime/Procedural/Scatter/Generators/GeneratePinBoundary.cs index 3d09ade..0f13d0d 100644 --- a/Runtime/Procedural/Scatter/Generators/GeneratePinBoundary.cs +++ b/Runtime/Procedural/Scatter/Generators/GeneratePinBoundary.cs @@ -24,7 +24,7 @@ namespace Rokojori SplineCurve last; LerpCurve3 equalSpacedCurve; - protected override List _Scatter( List points ) + protected override List _Scatter( List points, Scatterer root ) { var curve = spline.GetCurve(); diff --git a/Runtime/Procedural/Scatter/Generators/GeneratorEntry.cs b/Runtime/Procedural/Scatter/Generators/GeneratorEntry.cs index 146cda7..ec4c789 100644 --- a/Runtime/Procedural/Scatter/Generators/GeneratorEntry.cs +++ b/Runtime/Procedural/Scatter/Generators/GeneratorEntry.cs @@ -14,6 +14,34 @@ namespace Rokojori [Export] public PackedScene packedScene; + [Export] + public Node3D node3D; + + [Export] + public bool refreshNode3D = false; + + [Export] + public bool useInstancing = false; + + protected PackedScene _cachedNode3DScene; + + public PackedScene GetPackedScene() + { + if ( packedScene != null ) + { + return packedScene; + } + + if ( refreshNode3D || _cachedNode3DScene == null ) + { + refreshNode3D = false; + _cachedNode3DScene = new PackedScene(); + _cachedNode3DScene.Pack( node3D ); + } + + return _cachedNode3DScene; + } + [Export] public float probability = 1; diff --git a/Runtime/Procedural/Scatter/Generators/GeneratorScatterer.cs b/Runtime/Procedural/Scatter/Generators/GeneratorScatterer.cs index 1320925..b3a77a2 100644 --- a/Runtime/Procedural/Scatter/Generators/GeneratorScatterer.cs +++ b/Runtime/Procedural/Scatter/Generators/GeneratorScatterer.cs @@ -60,8 +60,9 @@ namespace Rokojori var index = _FindElementIndexWithWeights( childGeneratorWeights, value ); var gs = childGenerators[ index ]; - p.scene = gs.packedScene != null ? gs.packedScene : packedScene; + p.scene = gs.GetPackedScene() != null ? gs.GetPackedScene() : packedScene; p.parent = gs.container != null ? gs.container : container; + p.instanced = gs.useInstancing; } } diff --git a/Runtime/Procedural/Scatter/ScatterList.cs b/Runtime/Procedural/Scatter/ScatterList.cs index a546254..10ade5d 100644 --- a/Runtime/Procedural/Scatter/ScatterList.cs +++ b/Runtime/Procedural/Scatter/ScatterList.cs @@ -22,6 +22,12 @@ namespace Rokojori public override void _Process( double delta ) { + if ( clearCache ) + { + clearCache = false; + ClearCache(); + } + if ( clearContainers ) { clearContainers = false; @@ -44,8 +50,13 @@ namespace Rokojori ScatterAndInstantiatePoints(); } - protected override List _Scatter( List points ) + protected override List _Scatter( List points, Scatterer root = null ) { + if ( root == null ) + { + root = this; + } + var output = points; Nodes.ForEachDirectChild( this, @@ -55,8 +66,10 @@ namespace Rokojori { return; } - - output = s.Scatter( output ); + + var before = output.Count ; + output = s.Scatter( output, root ); + RJLog.Log( "Processed", before, "to", output.Count ); } ); diff --git a/Runtime/Procedural/Scatter/ScatterPoint.cs b/Runtime/Procedural/Scatter/ScatterPoint.cs index 7f2be0e..2ccbd25 100644 --- a/Runtime/Procedural/Scatter/ScatterPoint.cs +++ b/Runtime/Procedural/Scatter/ScatterPoint.cs @@ -7,9 +7,9 @@ using System; namespace Rokojori { - public class ScatterPoint + public class ScatterPoint:PointData { - + public bool instanced = false; protected Scatterer _creator; public Scatterer creator => _creator; protected int _creatorID; @@ -21,20 +21,8 @@ namespace Rokojori this._creatorID = id; } - - public bool visible = false; - public Vector3 position = Vector3.Zero; - public bool useGlobalPosition = true; - public Quaternion rotation = Quaternion.Identity; - public bool useGlobalRotation = true; - public Vector3 scale = Vector3.One; + public PackedScene scene; - public Node3D parent; - public int seed; - - public Vector3 globalPosition => useGlobalPosition || parent == null ? - position : parent.ToGlobal( position ); - public bool CanBeReusedBy( ScatterPoint other ) { @@ -64,27 +52,6 @@ namespace Rokojori return node3D; } - void ApplyTransform( Node3D node3D ) - { - if ( useGlobalPosition ) - { - node3D.GlobalPosition = position; - } - else - { - node3D.Position = position; - } - - if ( useGlobalRotation ) - { - Math3D.SetGlobalRotationTo( node3D, rotation ); - } - else - { - node3D.Quaternion = rotation; - } - - node3D.Scale = scale; - } + } } \ No newline at end of file diff --git a/Runtime/Procedural/Scatter/Scatterer.cs b/Runtime/Procedural/Scatter/Scatterer.cs index 244d61f..a7c1f45 100644 --- a/Runtime/Procedural/Scatter/Scatterer.cs +++ b/Runtime/Procedural/Scatter/Scatterer.cs @@ -11,36 +11,66 @@ namespace Rokojori [GlobalClass, Icon("res://addons/rokojori_action_library/Icons/Scatterer.svg") ] public partial class Scatterer:Node3D { + [ExportGroup("Update Behaviour")] [Export] public bool update = false; [Export] public bool updateAlways = false; + + [ExportGroup("Spawing")] + [Export] + public bool removeDiscarded = false; + [Export] + public Node3D[] streamSources; + [Export] + public float streamCullDistance = 100; + + [Export] + public float streamFadeRange = 20; + + [Export] + public int maxPoints = 1000000; + + + [ExportGroup("Clean Up")] [Export] public Node3D[] containersToClearNodes; [Export] public bool clearContainers = false; + [Export] + public bool clearCache = false; - [Export] - public bool removeDiscarded = false; + + [ExportGroup("Read Only")] [Export] - public int READ_ONLY_createdPoints = 0; + public int X_createdPoints = 0; [Export] - public int READ_ONLY_instantiatedPoints = 0; + public int X_instantiatedPoints = 0; [Export] - public int READ_ONLY_reusedPoints = 0; + public int X_reusedPoints = 0; [Export] - public int READ_ONLY_remappedPoints = 0; + public int X_remappedPoints = 0; + + + List _streamPositions = new List(); public override void _Process( double delta ) { + if ( clearCache ) + { + clearCache = false; + ClearCache(); + } + if ( clearContainers ) { clearContainers = false; - ClearContainers(); + ClearContainers(); } + if ( ! ( update || updateAlways ) ) { return; @@ -69,9 +99,24 @@ namespace Rokojori } Dictionary _instantiatedPoints = new Dictionary(); + MapList _instancedMassRenderers = new MapList(); Dictionary _scattererIDs = new Dictionary(); + public void ClearCache() + { + _instantiatedPoints.Clear(); + _scattererIDs.Clear(); + + foreach ( var mmr in _instancedMassRenderers ) + { + mmr.Value.ForEach( mr => Nodes.RemoveAndDelete( mr ) ); + } + + _instancedMassRenderers.Clear(); + ClearContainers(); + } + int GetScattererID( Scatterer s ) { if ( ! _scattererIDs.ContainsKey( s ) ) @@ -96,21 +141,49 @@ namespace Rokojori ClearContainers(); } - var points = Scatter( new List() ); + foreach ( var mmr in _instancedMassRenderers ) + { + var list = mmr.Value; - READ_ONLY_createdPoints = points.Count; - READ_ONLY_instantiatedPoints = 0; - READ_ONLY_reusedPoints = 0; - READ_ONLY_remappedPoints = 0; + for ( int i = list.Count - 1; i >=0 ; i-- ) + { + list[ i ] = Nodes.EnsureValid( list[ i ] ); + + if ( list[ i ] == null ) + { + list.RemoveAt( i ); + } + else + { + list[ i ].Clear(); + } + } + } + + var points = Scatter( new List(), this ); + + X_createdPoints = points.Count; + X_instantiatedPoints = 0; + X_reusedPoints = 0; + X_remappedPoints = 0; var usedPoints = new HashSet(); points.RemoveAll( p => ! p.visible ); points.ForEach( p => - { + { var hash = GetScatterPointHash( p ); + if ( p.instanced ) + { + AddInstanced( p ); + usedPoints.Add( hash ); + return; + } + + + if ( ! CanReuse( hash, p ) ) { return; @@ -122,13 +195,13 @@ namespace Rokojori usedPoints.Add( hash ); - READ_ONLY_reusedPoints ++; + X_reusedPoints ++; return; } ); - var unused = new DictionaryList(); + var unused = new MapList(); foreach ( var vk in _instantiatedPoints ) { @@ -163,7 +236,7 @@ namespace Rokojori usedPoints.Add( hash ); - READ_ONLY_remappedPoints ++; + X_remappedPoints ++; return; } } @@ -188,7 +261,7 @@ namespace Rokojori _instantiatedPoints[ hash ] = ip; usedPoints.Add( hash ); - READ_ONLY_instantiatedPoints++; + X_instantiatedPoints++; } ); @@ -207,6 +280,67 @@ namespace Rokojori } ); + foreach ( var mmr in _instancedMassRenderers ) + { + mmr.Value.ForEach( mr => mr.Create() ); + } + + + } + + MapList> _sceneMeshes = new MapList>(); + + + public void AddInstanced( ScatterPoint sp ) + { + var packedScene = sp.scene; + + if ( ! _sceneMeshes.ContainsKey( packedScene ) ) + { + var node = sp.scene.Instantiate(); + var meshesWithMaterials = MeshExtractor.ExtractMeshesInHierarchy( node ); + + _sceneMeshes[ packedScene ] = meshesWithMaterials; + } + + var meshes = _sceneMeshes[ packedScene ]; + + meshes.ForEach( + ( m )=> + { + var massRenderer = GetMassRenderer( sp, m.item ); + var transform = sp.globalTransform3D; + massRenderer.QueueObject( transform ); + } + ); + } + + MassRenderer GetMassRenderer( ScatterPoint sp, SingleMaterialMesh mwm ) + { + if ( ! _instancedMassRenderers.ContainsKey( mwm.mesh ) ) + { + var massRenderer = new List(); + + _instancedMassRenderers[ mwm.mesh ] = massRenderer; + } + + var mms =_instancedMassRenderers[ mwm.mesh ]; + + var mm = mms.Find( m => m.materialOveride == mwm.material ); + + if ( mm == null ) + { + var c = sp.parent.CreateChild( "MassRenderer" ); + + c.materialOveride = mwm.material; + c.mesh = mwm.mesh; + + _instancedMassRenderers.Add( mwm.mesh, c ); + + return c; + } + + return mm; } bool CanReuse( string hash, ScatterPoint sp ) @@ -223,15 +357,57 @@ namespace Rokojori return canBeReused; } - public List Scatter( List points ) + public List Scatter( List points, Scatterer root = null ) { - var returnedPoints = _Scatter( points ); + if ( root == null ) + { + root = this; + } + + var returnedPoints = _Scatter( points, root ); if ( removeDiscarded ) { returnedPoints.RemoveAll( p => ! p.visible ); } + if ( root.streamSources != null && root.streamSources.Length > 0 ) + { + var fadeDistance = root.streamCullDistance - root.streamFadeRange; + + returnedPoints.RemoveAll( + ( p ) => + { + for ( int s = 0; s < root.streamSources.Length; s++ ) + { + var distance = ( p.globalPosition - root.streamSources[ s ].GlobalPosition ).Length(); + + if ( distance < fadeDistance ) + { + return false; + } + + var treshold = MathX.RemapClamped( distance, fadeDistance, root.streamCullDistance, 1, 0 ); + + var result = Noise.Perlin( p.globalPosition ) > treshold; + + if ( ! result ) + { + return false; + } + + } + + return true; + } + ); + } + + if ( returnedPoints.Count > root.maxPoints ) + { + returnedPoints.RemoveRange( root.maxPoints, returnedPoints.Count - root.maxPoints ); + } + return returnedPoints; } @@ -252,7 +428,7 @@ namespace Rokojori return enabled; } - protected virtual List _Scatter( List points ) + protected virtual List _Scatter( List points, Scatterer root ) { return points; } diff --git a/Runtime/Procedural/Scatter/Transform/ProjectOnColliders.cs b/Runtime/Procedural/Scatter/Transform/ProjectOnColliders.cs index c92fb60..8591052 100644 --- a/Runtime/Procedural/Scatter/Transform/ProjectOnColliders.cs +++ b/Runtime/Procedural/Scatter/Transform/ProjectOnColliders.cs @@ -53,7 +53,7 @@ namespace Rokojori [Export] public bool hitBackFaces = true; - protected override List _Scatter( List points ) + protected override List _Scatter( List points, Scatterer root ) { var world = GetWorld3D(); var ray = new PhysicsRayQueryParameters3D(); diff --git a/Runtime/Procedural/Scatter/Transform/RandomizeTransform.cs b/Runtime/Procedural/Scatter/Transform/RandomizeTransform.cs index 4efb476..a9cf012 100644 --- a/Runtime/Procedural/Scatter/Transform/RandomizeTransform.cs +++ b/Runtime/Procedural/Scatter/Transform/RandomizeTransform.cs @@ -55,7 +55,7 @@ namespace Rokojori public Vector3 scaleNoiseOffset = Vector3.Zero; - protected override List _Scatter( List points ) + protected override List _Scatter( List points, Scatterer root ) { var output = points; var ownOffset = ScattererOwnPosition.ComputeOffset( ownPositionMode, this ); @@ -77,8 +77,12 @@ namespace Rokojori p.position += positionOffset * positionRandom * positionOffsetScale; p.rotation *= Quaternion.FromEuler( rotationOffset * rotationRandom * rotationOffsetScale ); + + + p.scale *= Math3D.LerpComponents( componentScaleMinMultiply, componentScaleMaxMultiply, scaleRandom ); p.scale *= Mathf.Lerp( uniformScaleMultiplyMin, uniformScaleMultiplyMax, uniformScaleRandom ); + } ); diff --git a/Runtime/Random/LCG.cs b/Runtime/Random/LCG.cs index 3f010f5..0ba49c1 100644 --- a/Runtime/Random/LCG.cs +++ b/Runtime/Random/LCG.cs @@ -24,6 +24,15 @@ namespace Rokojori this.setParameters( (int)Mathf.Pow(2,32), 1664525, 1013904223 ); } + public static LCG WithSeed( int seed ) + { + var lcg = new LCG(); + lcg.SetSeed( seed ); + + return lcg; + } + + public static LCG Randomized() { var ms = Time.GetUnixTimeFromSystem() % 1; diff --git a/Runtime/Random/RandomEngine.cs b/Runtime/Random/RandomEngine.cs index dca93b9..a54d48d 100644 --- a/Runtime/Random/RandomEngine.cs +++ b/Runtime/Random/RandomEngine.cs @@ -9,6 +9,16 @@ namespace Rokojori { public abstract class RandomEngine { + public static RandomEngine CreateIfNull( RandomEngine r ) + { + if ( r == null ) + { + return new GodotRandom(); + } + + return r; + } + public abstract float Next(); public float Value( float scalar ) @@ -235,6 +245,15 @@ namespace Rokojori return default ( T ); } + if ( list.Count == 1 ) + { + var element = list[ 0 ]; + + list.Clear(); + + return element; + } + var index = this.IntegerExclusive( 0, list.Count ); var item = list[ index ]; diff --git a/Runtime/Random/RandomList.cs b/Runtime/Random/RandomList.cs new file mode 100644 index 0000000..d30bd93 --- /dev/null +++ b/Runtime/Random/RandomList.cs @@ -0,0 +1,47 @@ +using System.Collections; +using System.Collections.Generic; +using Godot; +using System; + + + +namespace Rokojori +{ + public class RandomList + { + List _list = new List(); + int _pointer = 0; + + public T Next() + { + var element = _list[ _pointer ]; + _pointer = ( _pointer + 1 ) % _list.Count; + + return element; + } + + public static List Randomize( List data, RandomEngine r ) + { + return new RandomList( data, r ).GetList(); + } + + public RandomList( List data, RandomEngine r = null ) + { + r = RandomEngine.CreateIfNull( r ); + var copy = new List(); + copy.AddRange( data ); + + while ( copy.Count > 0 ) + { + _list.Add( r.RemoveFrom( copy ) ); + } + } + + public List GetList() + { + return _list; + } + + + } +} \ No newline at end of file diff --git a/Runtime/Selectors/Selectors.cs b/Runtime/Selectors/Selectors.cs new file mode 100644 index 0000000..eaf5e70 --- /dev/null +++ b/Runtime/Selectors/Selectors.cs @@ -0,0 +1,37 @@ + +using System.Diagnostics; +using System.Collections; +using System.Collections.Generic; +using System; +using Godot; + + +namespace Rokojori +{ + public class Selectors + { + public static T GetFromDirectChildren( Node parent, RJSelector s ) where T:Node + { + T selected = null; + + Nodes.ForEachDirectChild( parent, + ( T t )=> + { + if ( selected != null ) + { + return; + } + + if ( s != null && s.Selects( t ) ) + { + selected = t; + } + } + ); + + return selected; + } + + } + +} \ No newline at end of file diff --git a/Runtime/Sensors/InputActionSensor.cs b/Runtime/Sensors/InputActionSensor.cs new file mode 100644 index 0000000..19e0fa6 --- /dev/null +++ b/Runtime/Sensors/InputActionSensor.cs @@ -0,0 +1,42 @@ + +using Godot; +using System.Collections.Generic; + +namespace Rokojori +{ + [Tool] + [GlobalClass,Icon("res://addons/rokojori_action_library/Icons/RJSensor.svg")] + public partial class InputActionSensor: Sensor + { + [Export] + public string inputActionName = ""; + + public enum PollMode + { + Button, + Half_Axis, + Release_Only_Button + } + + [Export] + public PollMode pollMode = PollMode.Button; + + + protected override void UpdateValue() + { + if ( PollMode.Button == pollMode ) + { + SetBoolValue( Input.IsActionPressed( inputActionName ) ); + } + else if ( PollMode.Release_Only_Button == pollMode ) + { + SetBoolValue( Input.IsActionJustReleased( inputActionName ) ); + } + else if ( PollMode.Half_Axis == pollMode ) + { + SetFloatValue( Input.GetActionRawStrength( inputActionName ) ); + } + } + + } +} \ No newline at end of file diff --git a/Runtime/Sensors/KeySensor.cs b/Runtime/Sensors/KeySensor.cs index 61402c8..ef0eab6 100644 --- a/Runtime/Sensors/KeySensor.cs +++ b/Runtime/Sensors/KeySensor.cs @@ -20,7 +20,7 @@ namespace Rokojori [Export] public Trillean shiftHold = Trillean.Any; - public bool modifiersEnabled => TrilleanLogic.AllAny( ctrlHold, altHold, shiftHold ); + public bool modifiersEnabled => ! TrilleanLogic.AllAny( ctrlHold, altHold, shiftHold ); public enum ModifiersMode { @@ -51,7 +51,7 @@ namespace Rokojori return; } - if ( keyEvent.Keycode != key) + if ( keyEvent.Keycode != key ) { return; } @@ -64,9 +64,10 @@ namespace Rokojori if ( checkModifiers ) { + if ( ! TrilleanLogic.Matches( ctrlHold, keyEvent.CtrlPressed ) ) { - _lastInput = 0; + _lastInput = 0; return; } @@ -98,6 +99,11 @@ namespace Rokojori return _wasActive; } + public override float GetValue() + { + return _value; + } + public override void UpdateValue( float value ) { _value = value; diff --git a/Runtime/Sensors/MouseScreenRelative.cs b/Runtime/Sensors/MouseScreenRelative.cs new file mode 100644 index 0000000..ae94fbd --- /dev/null +++ b/Runtime/Sensors/MouseScreenRelative.cs @@ -0,0 +1,93 @@ + +using Godot; + + +namespace Rokojori +{ + [GlobalClass,Icon("res://addons/rokojori_action_library/Icons/RJSensor.svg")] + public partial class MouseScreenRelative : RJSensor + { + public enum MouseMotionType + { + Right, + Left, + Up, + Down + } + + [Export] + public MouseMotionType motionType; + + bool _isActive = false; + bool _wasActive = false; + float _value = 0; + float axisActivationTreshold = 0.5f; + float _lastInput = 0; + + public override void _Process( double delta ) + { + UpdateValue( _lastInput ); + } + + public override void _Input( InputEvent ev ) + { + var mouseEvent = ev as InputEventMouseMotion ; + + if ( mouseEvent == null ) + { + return; + } + + var relativePosition = mouseEvent.Position / GetViewport().GetVisibleRect().Size * 2 - Vector2.One; + var position = 0f; + + // RJLog.Log( mouseEvent.Position ); + + if ( MouseMotionType.Left == motionType) + { + position = Mathf.Max( 0, -relativePosition.X ); + } + else if ( MouseMotionType.Right == motionType) + { + position = Mathf.Max( 0, relativePosition.X ); + } + else if ( MouseMotionType.Up == motionType) + { + position = Mathf.Max( 0, relativePosition.Y ); + } + if ( MouseMotionType.Down == motionType) + { + position = Mathf.Max( 0, -relativePosition.Y ); + } + + // RJLog.Log( "Motion:", motionType, motion * speedMultiply ); + + _lastInput = position; + + } + + public override bool IsActive() + { + return _isActive; + } + + public override bool WasActive() + { + return _wasActive; + } + + public override float GetValue() + { + return _value; + } + + public override void UpdateValue( float value ) + { + _value = value; + + _wasActive = _isActive; + _isActive = _value > axisActivationTreshold; + } + + } +} \ No newline at end of file diff --git a/Runtime/Sensors/Sensor.cs b/Runtime/Sensors/Sensor.cs new file mode 100644 index 0000000..1bd7e1d --- /dev/null +++ b/Runtime/Sensors/Sensor.cs @@ -0,0 +1,64 @@ + +using Godot; + + +namespace Rokojori +{ + [Tool] + [GlobalClass, Icon("res://addons/rokojori_action_library/Icons/RJSensor.svg")] + public partial class Sensor: Resource + { + [Export] + public bool continous = false; + + [ExportGroup("Read Only")] + [Export] + public float _value = 0; + [Export] + public bool _wasActive = false; + [Export] + public bool _active = false; + [Export] + public float _activeTreshold = 0.5f; + + public void ProcessSensor( SensorRunner runner, float delta ) + { + _wasActive = _active; + UpdateValue(); + } + + public bool isDown => ! _wasActive && _active; + public bool isUp => _wasActive && ! _active; + + public bool wasActive => _wasActive; + public bool isActive => _active; + public bool isHold => _active; + + public bool isInactive => ! _active; + + public bool isActivated => isDown; + public bool isDeactivated => isUp; + + public float value => _value; + + + protected virtual void UpdateValue() + { + + } + + protected void SetBoolValue( bool activeState ) + { + _active = activeState; + _value = activeState ? 1 : 0; + } + + protected void SetFloatValue( float value ) + { + _value = value; + _active = value > _activeTreshold; + } + + + } +} \ No newline at end of file diff --git a/Runtime/Sensors/SensorEvent.cs b/Runtime/Sensors/SensorEvent.cs new file mode 100644 index 0000000..0296e56 --- /dev/null +++ b/Runtime/Sensors/SensorEvent.cs @@ -0,0 +1,69 @@ + +using Godot; + + +namespace Rokojori +{ + public class SensorEvent + { + protected Sensor _sensor; + public Sensor sensor => _sensor; + + protected bool _cancelled = false; + public bool cancelled => _cancelled; + + public void Cancel() + { + _cancelled = true; + } + + public bool isDown => _sensor.isDown; + public bool isUp => _sensor.isUp; + + public bool wasActive => _sensor.wasActive; + public bool isActive => _sensor.isActive; + public bool isHold => _sensor.isHold; + + public bool isInactive => _sensor.isInactive; + + public bool isActivated => _sensor.isActivated; + public bool isDeactivated => _sensor.isDeactivated; + + public float value => _sensor.value; + + public bool IsSensor( Sensor s ) + { + return _sensor == s; + } + + public bool IsDown( Sensor s ) + { + return IsSensor( s ) && isDown; + } + + public bool IsUp( Sensor s ) + { + return IsSensor( s ) && isUp; + } + + public bool IsHold( Sensor s ) + { + return IsSensor( s ) && isHold; + } + } + + public class EditableSensorEvent:SensorEvent + { + public EditableSensorEvent( Sensor sensor ) + { + this._sensor = sensor; + } + + public void Reset() + { + _cancelled = false; + } + } + + +} \ No newline at end of file diff --git a/Runtime/Sensors/SensorGroup.cs b/Runtime/Sensors/SensorGroup.cs new file mode 100644 index 0000000..2319f84 --- /dev/null +++ b/Runtime/Sensors/SensorGroup.cs @@ -0,0 +1,14 @@ + +using Godot; +using System.Collections.Generic; + +namespace Rokojori +{ + [Tool] + [GlobalClass,Icon("res://addons/rokojori_action_library/Icons/RJSensor.svg")] + public partial class SensorGroup:Resource + { + [Export] + public Sensor[] sensors; + } +} \ No newline at end of file diff --git a/Runtime/Sensors/SensorInputHandler.cs b/Runtime/Sensors/SensorInputHandler.cs new file mode 100644 index 0000000..22cdb05 --- /dev/null +++ b/Runtime/Sensors/SensorInputHandler.cs @@ -0,0 +1,11 @@ + +using Godot; +using System.Collections.Generic; + +namespace Rokojori +{ + public interface SensorInputHandler + { + void _OnSensor( SensorEvent se ); + } +} \ No newline at end of file diff --git a/Runtime/Sensors/SensorManager.cs b/Runtime/Sensors/SensorManager.cs new file mode 100644 index 0000000..c6909f3 --- /dev/null +++ b/Runtime/Sensors/SensorManager.cs @@ -0,0 +1,131 @@ + +using Godot; +using System.Collections.Generic; + +namespace Rokojori +{ + [Tool] + [GlobalClass,Icon("res://addons/rokojori_action_library/Icons/RJSensor.svg")] + public partial class SensorManager: Node + { + [Export] + public bool initializeOnReady = true; + + [Export] + public Sensor[] sensors; + + [Export] + public SensorGroup[] sensorGroups; + + [Export] + public bool processSensors = false; + + + + List runners = new List(); + Dictionary sensorToRunner = new Dictionary(); + + + public override void _Ready() + { + if ( ! initializeOnReady ) + { + return; + } + + CreateRunners(); + } + + public override void _Process( double delta ) + { + if ( Engine.IsEditorHint() || ! processSensors ) + { + return; + } + + runners.ForEach( r => r.Update( (float) delta ) ); + } + + public void Register( Sensor s, SensorInputHandler sih ) + { + var sensorRunner = sensorToRunner[ s ]; + + if ( sensorRunner.listeners.Contains( sih ) ) + { + return; + } + + sensorRunner.listeners.Add( sih ); + } + + public void Unregister( Sensor s, SensorInputHandler sih ) + { + var sensorRunner = sensorToRunner[ s ]; + sensorRunner.listeners.Remove( sih ); + } + + public static void Register( SensorInputHandler handler, params Sensor[] sensors ) + { + var sm = Unique.Get(); + + + foreach ( var s in sensors ) + { + if ( s == null ) + { + continue; + } + + ( handler as Node ).LogInfo( "Registrating" ); + sm.Register( s, handler ); + } + } + + public static void Unregister( SensorInputHandler handler, params Sensor[] sensors ) + { + var sm = Unique.Get(); + + foreach ( var s in sensors ) + { + if ( s == null ) + { + continue; + } + + ( handler as Node ).LogInfo( "Unregistrating" ); + + sm.Unregister( s, handler ); + } + } + + void CreateRunners() + { + if ( sensors == null ) + { + sensors = new Sensor[]{}; + } + + if ( sensorGroups == null ) + { + sensorGroups = new SensorGroup[]{}; + } + + foreach ( var s in sensors ) + { + runners.Add( new SensorRunner( s ) ); + } + + foreach ( var g in sensorGroups ) + { + foreach ( var s in g.sensors ) + { + runners.Add( new SensorRunner( s ) ); + } + } + + runners.ForEach( r => sensorToRunner[ r.sensor ] = r ); + + this.LogInfo( "Created runners:", runners.Count ); + } + } +} \ No newline at end of file diff --git a/Runtime/Sensors/SensorRunner.cs b/Runtime/Sensors/SensorRunner.cs new file mode 100644 index 0000000..e439ae3 --- /dev/null +++ b/Runtime/Sensors/SensorRunner.cs @@ -0,0 +1,38 @@ + +using Godot; +using System.Collections.Generic; + +namespace Rokojori +{ + public class SensorRunner + { + public Sensor sensor; + public List listeners = new List(); + float _lastValue = 0; + + EditableSensorEvent sensorEvent; + + public SensorRunner( Sensor sensor ) + { + this.sensor = sensor; + + sensorEvent = new EditableSensorEvent( sensor ); + } + + public void Update( float delta ) + { + sensor.ProcessSensor( this, delta ); + + if ( ! sensor.continous && sensor.value == _lastValue ) + { + return; + } + + sensorEvent.Reset(); + + listeners.ForEach( l => l._OnSensor( sensorEvent ) ); + + _lastValue = sensor.value; + } + } +} \ No newline at end of file diff --git a/Runtime/Sensors/TriggerOnSensor.cs b/Runtime/Sensors/TriggerOnSensor.cs new file mode 100644 index 0000000..b69c752 --- /dev/null +++ b/Runtime/Sensors/TriggerOnSensor.cs @@ -0,0 +1,36 @@ + +using Godot; + + +namespace Rokojori +{ + [GlobalClass, Icon("res://addons/rokojori_action_library/Icons/RJOnEvent.svg") ] + public partial class TriggerOnSensor: Node, SensorInputHandler + { + [Export] + public Sensor sensor; + + [Export] + public RJAction action; + + public override void _Ready() + { + SensorManager.Register( this, sensor ); + } + + public override void _ExitTree() + { + SensorManager.Unregister( this, sensor ); + } + + public void _OnSensor( SensorEvent se ) + { + if ( se.IsDown( sensor ) ) + { + Actions.Trigger( action ); + } + } + + + } +} \ No newline at end of file diff --git a/Runtime/Shading/Library/Dithering.gdshareinc b/Runtime/Shading/Library/Dithering.gdshareinc new file mode 100644 index 0000000..ddb1367 --- /dev/null +++ b/Runtime/Shading/Library/Dithering.gdshareinc @@ -0,0 +1,28 @@ +const mat4 ditherMatrix = mat4( + vec4(0.0625, 0.5625, 0.1875, 0.6875), + vec4(0.8125, 0.3125, 0.9375, 0.4375), + vec4(0.25, 0.75, 0.125, 0.625), + vec4(1.0, 0.5, 0.875, 0.375)); + + +float dither( float alpha, vec2 pos ) +{ + int x = int( pos.x ) % 4; + int y = int( pos.y ) % 4; + + float res = 0.0; + vec4 temp; + + if (x == 0) {temp = ditherMatrix[0];} + else if (x == 1) temp = ditherMatrix[1]; + else if (x == 2) temp = ditherMatrix[2]; + else if (x == 3) temp = ditherMatrix[3]; + + + if (y == 0) {res = temp[0];} + else if (y == 1) res = temp[1]; + else if (y == 2) res = temp[2]; + else if (y == 3) res = temp[3]; + + return res; +} \ No newline at end of file diff --git a/Runtime/Shading/Library/Light.gdshaderinc b/Runtime/Shading/Library/Light.gdshaderinc new file mode 100644 index 0000000..147cb7e --- /dev/null +++ b/Runtime/Shading/Library/Light.gdshaderinc @@ -0,0 +1,11 @@ +float fresnel( vec3 _NORMAL, vec3 _VIEW, float amount ) +{ + return pow( ( 1.0 - clamp( dot( normalize( _NORMAL ), normalize( _VIEW ) ), 0.0, 1.0 ) ), amount ); +} + +float fresnelNormalized( vec3 _NORMAL, vec3 _VIEW, float amount ) +{ + return pow( ( 1.0 - clamp( dot( _NORMAL, _VIEW ), 0.0, 1.0 ) ), amount); +} + + diff --git a/Runtime/Shading/Library/Math.gdshaderinc b/Runtime/Shading/Library/Math.gdshaderinc index 3ed6fe2..6b8ea3b 100644 --- a/Runtime/Shading/Library/Math.gdshaderinc +++ b/Runtime/Shading/Library/Math.gdshaderinc @@ -28,6 +28,14 @@ float map( float value, float inMin, float inMax, float outMin, float outMax ) return mix( outMin, outMax, normalizeToRange( value, inMin, inMax ) ); } +vec2 map_v2( vec2 value, vec2 inMin, vec2 inMax, vec2 outMin, vec2 outMax ) +{ + float x = map( value.x, inMin.x, inMax.x, outMin.x, outMax.x ); + float y = map( value.y, inMin.y, inMax.y, outMin.y, outMax.y ); + + return vec2( x, y ); +} + float mapClamped( float value, float inMin, float inMax, float outMin, float outMax ) { return mix( outMin, outMax, normalizeToRange01( value, inMin, inMax ) ); @@ -38,4 +46,9 @@ float triangle( float value ) float inverted = 1.0 - value; return min( value, inverted ) * 2.0; +} + +vec2 onCircle( float angle ) +{ + return vec2( cos( angle ), sin( angle ) ); } \ No newline at end of file diff --git a/Runtime/Shading/Library/Noise.gdshaderinc b/Runtime/Shading/Library/Noise.gdshaderinc index d177219..f10420b 100644 --- a/Runtime/Shading/Library/Noise.gdshaderinc +++ b/Runtime/Shading/Library/Noise.gdshaderinc @@ -3,6 +3,92 @@ float random( vec2 uv ) return fract( sin( dot( uv.xy, vec2( 12.9898, 78.233 ) ) ) * 43758.5453123 ); } +vec2 random_v2( vec2 uv ) +{ + return vec2( fract( sin( dot( uv.xy, vec2( 12.9898,78.233 ) ) ) * 43758.5453123 ) ); +} + +vec3 random_v3( vec3 uvw ) +{ + + uvw = vec3( dot(uvw, vec3(127.1,311.7, 513.7) ), + dot(uvw, vec3(269.5,183.3, 396.5) ), + dot(uvw, vec3(421.3,314.1, 119.7) ) ); + + return -1.0 + 2.0 * fract(sin(uvw) * 43758.5453123); +} + +float perlin( vec2 uv ) +{ + vec2 uv_index = floor(uv); + vec2 uv_fract = fract(uv); + + vec2 blur = smoothstep(0.0, 1.0, uv_fract); + + return mix( mix( dot( random_v2(uv_index + vec2(0.0,0.0) ), uv_fract - vec2(0.0,0.0) ), + dot( random_v2(uv_index + vec2(1.0,0.0) ), uv_fract - vec2(1.0,0.0) ), blur.x), + mix( dot( random_v2(uv_index + vec2(0.0,1.0) ), uv_fract - vec2(0.0,1.0) ), + dot( random_v2(uv_index + vec2(1.0,1.0) ), uv_fract - vec2(1.0,1.0) ), blur.x), blur.y) + 0.5; +} + +float perlin3D( vec3 uvw ) +{ + vec3 gridIndex = floor( uvw ); + vec3 gridFract = fract( uvw ); + + vec3 blur = smoothstep( 0.0, 1.0, gridFract ); + + vec3 blb = gridIndex + vec3(0.0, 0.0, 0.0); + vec3 brb = gridIndex + vec3(1.0, 0.0, 0.0); + vec3 tlb = gridIndex + vec3(0.0, 1.0, 0.0); + vec3 trb = gridIndex + vec3(1.0, 1.0, 0.0); + vec3 blf = gridIndex + vec3(0.0, 0.0, 1.0); + vec3 brf = gridIndex + vec3(1.0, 0.0, 1.0); + vec3 tlf = gridIndex + vec3(0.0, 1.0, 1.0); + vec3 trf = gridIndex + vec3(1.0, 1.0, 1.0); + + vec3 gradBLB = random_v3( blb ); + vec3 gradBRB = random_v3( brb ); + vec3 gradTLB = random_v3( tlb ); + vec3 gradTRB = random_v3( trb ); + vec3 gradBLF = random_v3( blf ); + vec3 gradBRF = random_v3( brf ); + vec3 gradTLF = random_v3( tlf ); + vec3 gradTRF = random_v3( trf ); + + + vec3 distToPixelFromBLB = gridFract - vec3( 0.0, 0.0, 0.0 ); + vec3 distToPixelFromBRB = gridFract - vec3( 1.0, 0.0, 0.0 ); + vec3 distToPixelFromTLB = gridFract - vec3( 0.0, 1.0, 0.0 ); + vec3 distToPixelFromTRB = gridFract - vec3( 1.0, 1.0, 0.0 ); + vec3 distToPixelFromBLF = gridFract - vec3( 0.0, 0.0, 1.0 ); + vec3 distToPixelFromBRF = gridFract - vec3( 1.0, 0.0, 1.0 ); + vec3 distToPixelFromTLF = gridFract - vec3( 0.0, 1.0, 1.0 ); + vec3 distToPixelFromTRF = gridFract - vec3( 1.0, 1.0, 1.0 ); + + float dotBLB = dot( gradBLB, distToPixelFromBLB ); + float dotBRB = dot( gradBRB, distToPixelFromBRB ); + float dotTLB = dot( gradTLB, distToPixelFromTLB ); + float dotTRB = dot( gradTRB, distToPixelFromTRB ); + float dotBLF = dot( gradBLF, distToPixelFromBLF ); + float dotBRF = dot( gradBRF, distToPixelFromBRF ); + float dotTLF = dot( gradTLF, distToPixelFromTLF ); + float dotTRF = dot( gradTRF, distToPixelFromTRF ); + + + return mix( + mix( mix(dotBLB, dotBRB, blur.x), mix(dotTLB, dotTRB, blur.x), blur.y ), + mix( mix(dotBLF, dotBRF, blur.x), mix(dotTLF, dotTRF, blur.x), blur.y ), + blur.z + ) + 0.5; +} + + +float perlinPolar( vec2 uv ) +{ + return perlin( uv ) * 2.0 - 1.0; +} + float worley( vec2 uv, float columns, float rows ) { @@ -17,7 +103,7 @@ float worley( vec2 uv, float columns, float rows ) for ( int x= -1; x <= 1; x++ ) { vec2 neighbor = vec2( float( x ), float( y ) ); - vec2 point = random( index_uv + neighbor ); + vec2 point = random_v2( index_uv + neighbor ); vec2 diff = neighbor + point - fract_uv; float dist = length (diff ); @@ -42,7 +128,7 @@ vec2 voronoi( vec2 uv, float columns, float rows ) for ( int x= -1; x <= 1; x++ ) { vec2 neighbor = vec2( float( x ), float( y ) ); - vec2 point = random( index_uv + neighbor ); + vec2 point = random_v2( index_uv + neighbor ); vec2 diff = neighbor + point - fract_uv; float dist = length( diff ); @@ -57,4 +143,5 @@ vec2 voronoi( vec2 uv, float columns, float rows ) return minimum_point; -} \ No newline at end of file +} + diff --git a/Runtime/Shading/Library/Textures.gdshaderinc b/Runtime/Shading/Library/Textures.gdshaderinc new file mode 100644 index 0000000..3ec7e61 --- /dev/null +++ b/Runtime/Shading/Library/Textures.gdshaderinc @@ -0,0 +1,10 @@ +vec4 triplanarTexture( sampler2D sampler, vec3 weights, vec3 triplanerPosition ) +{ + vec4 sample = vec4( 0.0 ); + + sample += texture( sampler, triplanerPosition.xy ) * weights.z; + sample += texture( sampler, triplanerPosition.xz ) * weights.y; + sample += texture( sampler, triplanerPosition.zy * vec2( -1.0, 1.0 ) ) * weights.x; + + return sample; +} diff --git a/Runtime/Shading/Library/Time.gdshaderinc b/Runtime/Shading/Library/Time.gdshaderinc new file mode 100644 index 0000000..9b32faa --- /dev/null +++ b/Runtime/Shading/Library/Time.gdshaderinc @@ -0,0 +1,14 @@ + + + +float timeSine( float _TIME, float duration ) +{ + float t = TIME / duration * 2.0 * PI; + + return sin( t ); +} + +float timeSine01( float _TIME, float duration ) +{ + return timeSine( _TIME, duration ) * 0.5 + 0.5; +} \ No newline at end of file diff --git a/Runtime/Shading/Library/Transform.gdshaderinc b/Runtime/Shading/Library/Transform.gdshaderinc index a4a5eaa..261652b 100644 --- a/Runtime/Shading/Library/Transform.gdshaderinc +++ b/Runtime/Shading/Library/Transform.gdshaderinc @@ -4,11 +4,33 @@ vec3 localToWorld( vec3 _VERTEX, mat4 _MODEL_MATRIX ) return ( _MODEL_MATRIX * vec4( _VERTEX, 1.0 ) ).xyz; } +vec3 localToWorldDirection( vec3 _VERTEX, mat4 _MODEL_MATRIX ) +{ + mat4 mw = _MODEL_MATRIX; + mw[ 3 ][ 0 ] = 0.0; + mw[ 3 ][ 1 ] = 0.0; + mw[ 3 ][ 2 ] = 0.0; + mw[ 3 ][ 3 ] = 1.0; + + return ( mw * vec4( _VERTEX, 1.0 ) ).xyz; +} + vec3 worldToLocal( vec3 _VERTEX, mat4 _MODEL_MATRIX ) { return ( inverse( _MODEL_MATRIX ) * vec4( _VERTEX, 1.0 ) ).xyz; } +vec3 extractScale( mat3 _MODEL_NORMAL_MATRIX ) +{ + mat3 m = _MODEL_NORMAL_MATRIX; + + float x = length( vec3( m[ 0 ][ 0 ], m[ 1 ][ 0 ], m[ 2 ][ 0 ] ) ); + float y = length( vec3( m[ 0 ][ 1 ], m[ 1 ][ 1 ], m[ 2 ][ 1 ] ) ); + float z = length( vec3( m[ 0 ][ 2 ], m[ 1 ][ 2 ], m[ 2 ][ 2 ] ) ); + + return vec3( x, y, z ); +} + vec2 tilingOffset( vec2 uv, vec4 tilingOffset ) { uv *= tilingOffset.xy; @@ -23,4 +45,56 @@ vec2 tilingOffsetRepeat( vec2 uv, vec4 tilingOffset ) uv += tilingOffset.zw; return mod( uv, vec2(1,1) ); +} + +vec3 billboardWorldOffset( vec2 _UV, mat4 _INV_VIEW_MATRIX, mat4 _MODEL_MATRIX ) +{ + vec2 mappedUV = mix( vec2(-1,1), vec2( 1, -1 ), _UV ); + vec4 offset = vec4( mappedUV.x, mappedUV.y, 0, 0 ); + + offset = _INV_VIEW_MATRIX * offset; + mat4 mw = _MODEL_MATRIX; + mw[ 3 ][ 0 ] = 0.0; + mw[ 3 ][ 1 ] = 0.0; + mw[ 3 ][ 2 ] = 0.0; + + vec3 worldOffset = worldToLocal( offset.xyz, mw ); + worldOffset = normalize( worldOffset ); + + return worldOffset; +} + +vec2 rotate_v2( vec2 uv, float angle ) +{ + float s = sin( angle ); + float c = cos( angle ); + + + float x = uv.x; + float y = uv.y; + + uv.x = c * x - s * y; + uv.y = s * x + c * y; + + return uv; +} + +vec2 rotateAround_v2( vec2 uv, float angle, vec2 pivot ) +{ + uv -= pivot; + uv = rotate_v2( uv, angle ); + uv += pivot; + + return uv; +} + +vec3 cameraWorldPosition( mat4 _INV_VIEW_MATRIX ) +{ + return (_INV_VIEW_MATRIX * vec4(vec3(0.0), 1.0)).xyz; +} + +vec3 cameraWorldForward( mat4 _INV_VIEW_MATRIX ) +{ + vec3 pos = cameraWorldPosition( _INV_VIEW_MATRIX ); + return normalize( (_INV_VIEW_MATRIX * vec4( vec3(0.0,0.0,1.0), 1.0)).xyz - pos ); } \ No newline at end of file diff --git a/Runtime/Shading/Materials.cs b/Runtime/Shading/Materials.cs deleted file mode 100644 index 14b02c7..0000000 --- a/Runtime/Shading/Materials.cs +++ /dev/null @@ -1,61 +0,0 @@ -using Godot; -using System.Reflection; -using System.Collections.Generic; - -namespace Rokojori -{ - public class Materials - { - public static void Set( Node node, Material material, int index = 0 ) - { - if ( node is MeshInstance3D ) - { - var mi = (MeshInstance3D) node; - - mi.SetSurfaceOverrideMaterial( index, material ); - - if ( index == 0 && mi.MaterialOverride != null ) - { - mi.MaterialOverride = null; - } - - return; - } - - if ( node is CsgPrimitive3D ) - { - ReflectionHelper.SetMemberValue( node, "material", material ); - - var cp = (GpuParticles3D) node; - cp.ProcessMaterial = material; - - if ( cp.MaterialOverride != null ) - { - cp.MaterialOverride = null; - } - - return; - } - - if ( node is GpuParticles3D ) - { - var gp = (GpuParticles3D) node; - gp.ProcessMaterial = material; - - if ( gp.MaterialOverride != null ) - { - gp.MaterialOverride = null; - } - } - - if ( node is GeometryInstance3D ) - { - var gi = (GeometryInstance3D) node; - - gi.MaterialOverride = material; - return; - } - - } - } -} \ No newline at end of file diff --git a/Runtime/Shading/Materials/CustomMaterial.cs b/Runtime/Shading/Materials/CustomMaterial.cs new file mode 100644 index 0000000..66d1784 --- /dev/null +++ b/Runtime/Shading/Materials/CustomMaterial.cs @@ -0,0 +1,41 @@ +using Godot; +using System.Reflection; +using System.Collections.Generic; + +namespace Rokojori +{ + public partial class CustomMaterial:ShaderMaterial + { + public void CopyUniformsFrom( ShaderMaterial material ) + { + var uniforms = Shaders.GetUniformNames( material.Shader ); + + uniforms.ForEach( + u => + { + SetShaderParameter( u, material.GetShaderParameter( u ) ); + } + ); + } + + public static T CreateFrom( ShaderMaterial m ) where T:CustomMaterial, new() + { + var t = new T(); + t.CopyUniformsFrom( m ); + + return t; + } + + public static CachedResource Cached( string path ) where T:CustomMaterial, new() + { + var cachedResource = new CachedResource( path, + ( r ) => + { + return CreateFrom( r as ShaderMaterial ); + } + ); + + return cachedResource; + } + } +} \ No newline at end of file diff --git a/Runtime/Shading/Materials/CustomMaterialProperty.cs b/Runtime/Shading/Materials/CustomMaterialProperty.cs new file mode 100644 index 0000000..084b2cc --- /dev/null +++ b/Runtime/Shading/Materials/CustomMaterialProperty.cs @@ -0,0 +1,29 @@ +using Godot; +using System.Reflection; +using System.Collections.Generic; + +namespace Rokojori +{ + public class CustomMaterialProperty<[MustBeVariant] T> + { + public CustomMaterialProperty( CustomMaterial m, ShaderPropertyName n ) + { + _material = m; + _name = n; + } + + CustomMaterial _material; + ShaderPropertyName _name; + + public T Get() + { + return _name._Get( _material, default( T ) ); + } + + public void Set( T value ) + { + _name._Set( _material, value ); + } + + } +} \ No newline at end of file diff --git a/Runtime/Shading/Materials/MaterialTransfer.cs b/Runtime/Shading/Materials/MaterialTransfer.cs new file mode 100644 index 0000000..b0d1c4d --- /dev/null +++ b/Runtime/Shading/Materials/MaterialTransfer.cs @@ -0,0 +1,34 @@ +using Godot; +using System.Reflection; +using System.Collections.Generic; + +namespace Rokojori +{ + [Tool] + [GlobalClass] + public partial class MaterialTransfer:Resource + { + [Export] + public SubMaterialTransfer[] transfers; + + public void Transfer( Material source, Material target ) + { + Lists.From( transfers ).ForEach( + ( t )=> + { + if ( ! t.MatchesMaterial( source ) ) + { + RJLog.Log( t.info, "not matching:", source ); + return; + } + + RJLog.Log( t.info, "from", source, "to", target ); + + t.Transfer( source, target ); + + } + ); + } + } +} + diff --git a/Runtime/Shading/Materials/Materials.cs b/Runtime/Shading/Materials/Materials.cs new file mode 100644 index 0000000..d0b3719 --- /dev/null +++ b/Runtime/Shading/Materials/Materials.cs @@ -0,0 +1,179 @@ +using Godot; +using System.Reflection; +using System.Collections.Generic; + +namespace Rokojori +{ + public class Materials + { + + public static int CountOf( Node node ) + { + var s = new StandardMaterial3D(); + + if ( node is MeshInstance3D mi ) + { + return mi.GetSurfaceOverrideMaterialCount(); + } + + return 1; + } + + public static List GetAll( Node node, int index = 0 ) where M:Material + { + var list = new List(); + + var count = CountOf( node ); + + for ( int i = 0; i < count; i++ ) + { + list.Add( Get( node, index ) ); + } + + return list; + } + + public static M Get( Node node, int index = 0 ) where M:Material + { + if ( node is MeshInstance3D mi ) + { + var material = mi.GetSurfaceOverrideMaterial( index ) as M; + + if ( material != null ) + { + return material; + } + } + else if ( node is CsgPrimitive3D ) + { + return ReflectionHelper.GetMemberValue( node, "material" ) as M; + } + + else if ( node is GpuParticles3D gp) + { + return gp.ProcessMaterial as M; + } + + if ( node is GeometryInstance3D gi ) + { + return gi.MaterialOverride as M; + } + + return null; + + } + + public static void SetAll( Node node, Material material ) + { + var count = CountOf( node ); + + for ( int i = 0; i < count; i++ ) + { + Set( node, material, i ); + } + } + + public static void Set( Node node, List materials ) + { + var count = CountOf( node ); + + for ( int i = 0; i < count && i < materials.Count; i++ ) + { + Set( node, materials[ i ], i ); + } + } + + public static void Set( Node node, Material material, int index = 0 ) + { + if ( node is MeshInstance3D mi) + { + mi.SetSurfaceOverrideMaterial( index, material ); + + if ( index == 0 && mi.MaterialOverride != null ) + { + mi.MaterialOverride = null; + } + + return; + } + + if ( node is CsgPrimitive3D csg ) + { + ReflectionHelper.SetMemberValue( node, "material", material ); + + if ( csg.MaterialOverride != null ) + { + csg.MaterialOverride = null; + } + + return; + } + + if ( node is GpuParticles3D gp ) + { + gp.ProcessMaterial = material; + + if ( gp.MaterialOverride != null ) + { + gp.MaterialOverride = null; + } + } + + if ( node is GeometryInstance3D gi ) + { + gi.MaterialOverride = material; + return; + } + + } + + public static void AddOverlay( Node node, Material material, bool forceTop = true ) + { + if ( node is GeometryInstance3D gi ) + { + if ( gi.MaterialOverlay == null || forceTop ) + { + gi.MaterialOverlay = material; + } + else + { + gi.MaterialOverlay.NextPass = material; + } + + return; + } + } + + public static void RemoveOverlay( Node node, Material material, bool forceTop = true ) + { + + if ( node is GeometryInstance3D gi ) + { + if ( forceTop ) + { + gi.MaterialOverlay = null; + } + else if ( gi.MaterialOverlay == material ) + { + gi.MaterialOverlay = gi.MaterialOverlay.NextPass; + } + else + { + var mo = gi.MaterialOverlay; + + while ( mo != null && mo.NextPass != material ) + { + mo = mo.NextPass; + } + + var next = material.NextPass; + + mo.NextPass = next; + } + + return; + } + } + + } +} \ No newline at end of file diff --git a/Runtime/Shading/Materials/SubMaterialTransfer.cs b/Runtime/Shading/Materials/SubMaterialTransfer.cs new file mode 100644 index 0000000..fda068f --- /dev/null +++ b/Runtime/Shading/Materials/SubMaterialTransfer.cs @@ -0,0 +1,128 @@ +using Godot; +using System.Reflection; +using System.Collections.Generic; + +namespace Rokojori +{ + [Tool] + [GlobalClass] + public partial class SubMaterialTransfer:Resource + { + [Export] + public string info; + + [ExportGroup("Shader Matching/Standard Shaders")] + [Export] + public bool canvasItem; + [Export] + public bool fog; + [Export] + public bool orm3D; + [Export] + public bool standard3D; + [Export] + public bool panoramaSky; + [Export] + public bool particleProcess; + [Export] + public bool physicalSky; + [Export] + public bool placeholder; + [Export] + public bool proceduralSky; + [Export] + public bool shader; + + [ExportGroup("Shader Matching/Custom Shaders")] + [Export] + public Shader[] shaders; + + + public bool MatchesMaterial( Material m ) + { + if ( TypeTest( m, canvasItem ) ){ return true; } + if ( TypeTest( m, fog ) ){ return true; } + if ( TypeTest( m, orm3D ) ){ return true; } + if ( TypeTest( m, standard3D ) ){ return true; } + + if ( TypeTest( m, panoramaSky ) ){ return true; } + if ( TypeTest( m, particleProcess ) ){ return true; } + if ( TypeTest( m, physicalSky ) ){ return true; } + if ( TypeTest( m, placeholder ) ){ return true; } + + if ( TypeTest( m, proceduralSky ) ){ return true; } + if ( TypeTest( m, shader ) ){ return true; } + + var shaderMaterial = m as ShaderMaterial; + + if ( shaderMaterial == null ) + { + return false; + } + + for ( int i = 0; i < shaders.Length; i++ ) + { + if ( shaderMaterial.Shader == shaders[ i ] ) + { + return true; + } + } + + return false; + } + + bool TypeTest( Material m, bool isType ) where T:class + { + if ( ! isType ) + { + return false; + } + + return ( m as T ) != null; + } + + [ExportGroup("Property Transfers")] + [Export] + public BoolPropertyTransfer[] bools; + + [Export] + public ColorPropertyTransfer[] colors; + + [Export] + public FloatPropertyTransfer[] floats; + + [Export] + public IntPropertyTransfer[] ints; + + [Export] + public Texture2DPropertyTransfer[] texture2D; + + [Export] + public Vector2PropertyTransfer[] vector2; + + [Export] + public Vector3PropertyTransfer[] vector3; + + [Export] + public Vector4PropertyTransfer[] vector4; + + [Export] + public CustomMaterialTransfer[] custom; + + + public void Transfer( Material a, Material b ) + { + Arrays.ForEach( bools, p => p.Transfer( a, b ) ); + Arrays.ForEach( colors, p => p.Transfer( a, b ) ); + Arrays.ForEach( floats, p => p.Transfer( a, b ) ); + Arrays.ForEach( ints, p => p.Transfer( a, b ) ); + Arrays.ForEach( texture2D, p => p.Transfer( a, b ) ); + Arrays.ForEach( vector2, p => p.Transfer( a, b ) ); + Arrays.ForEach( vector3, p => p.Transfer( a, b ) ); + Arrays.ForEach( vector4, p => p.Transfer( a, b ) ); + } + + + } +} + diff --git a/Runtime/Shading/Materials/Transfers/BoolPropertyTransfer.cs b/Runtime/Shading/Materials/Transfers/BoolPropertyTransfer.cs new file mode 100644 index 0000000..14da046 --- /dev/null +++ b/Runtime/Shading/Materials/Transfers/BoolPropertyTransfer.cs @@ -0,0 +1,24 @@ +using Godot; +using System.Reflection; +using System.Collections.Generic; + +namespace Rokojori +{ + [Tool] + [GlobalClass] + public partial class BoolPropertyTransfer:Resource + { + [Export] + public BoolPropertyName from; + + [Export] + public BoolPropertyName to; + + public void Transfer( Material a, Material b ) + { + var v = from.Get( a ); + to.Set( b, v ); + } + } +} + diff --git a/Runtime/Shading/Materials/Transfers/ColorPropertyTransfer.cs b/Runtime/Shading/Materials/Transfers/ColorPropertyTransfer.cs new file mode 100644 index 0000000..23f2fbc --- /dev/null +++ b/Runtime/Shading/Materials/Transfers/ColorPropertyTransfer.cs @@ -0,0 +1,24 @@ +using Godot; +using System.Reflection; +using System.Collections.Generic; + +namespace Rokojori +{ + [Tool] + [GlobalClass] + public partial class ColorPropertyTransfer:Resource + { + [Export] + public ColorPropertyName from; + + [Export] + public ColorPropertyName to; + + public void Transfer( Material a, Material b ) + { + var v = from.Get( a ); + to.Set( b, v ); + } + } +} + diff --git a/Runtime/Shading/Materials/Transfers/CustomTransfer.cs b/Runtime/Shading/Materials/Transfers/CustomTransfer.cs new file mode 100644 index 0000000..b6fa1f8 --- /dev/null +++ b/Runtime/Shading/Materials/Transfers/CustomTransfer.cs @@ -0,0 +1,18 @@ +using Godot; +using System.Reflection; +using System.Collections.Generic; + +namespace Rokojori +{ + [Tool] + [GlobalClass] + public partial class CustomMaterialTransfer:Resource + { + public virtual void Transfer( Material a, Material b ) + { + + } + + } +} + diff --git a/Runtime/Shading/Materials/Transfers/FloatPropertyTransfer.cs b/Runtime/Shading/Materials/Transfers/FloatPropertyTransfer.cs new file mode 100644 index 0000000..a0237bb --- /dev/null +++ b/Runtime/Shading/Materials/Transfers/FloatPropertyTransfer.cs @@ -0,0 +1,24 @@ +using Godot; +using System.Reflection; +using System.Collections.Generic; + +namespace Rokojori +{ + [Tool] + [GlobalClass] + public partial class FloatPropertyTransfer:Resource + { + [Export] + public FloatPropertyName from; + + [Export] + public FloatPropertyName to; + + public void Transfer( Material a, Material b ) + { + var v = from.Get( a ); + to.Set( b, v ); + } + } +} + diff --git a/Runtime/Shading/Materials/Transfers/IntPropertyTransfer.cs b/Runtime/Shading/Materials/Transfers/IntPropertyTransfer.cs new file mode 100644 index 0000000..20fd7e0 --- /dev/null +++ b/Runtime/Shading/Materials/Transfers/IntPropertyTransfer.cs @@ -0,0 +1,25 @@ +using Godot; +using System.Reflection; +using System.Collections.Generic; + +namespace Rokojori +{ + [Tool] + [GlobalClass] + public partial class IntPropertyTransfer:Resource + { + [Export] + public IntPropertyName from; + + [Export] + public IntPropertyName to; + + public void Transfer( Material a, Material b ) + { + var v = from.Get( a ); + to.Set( b, v ); + } + + } +} + diff --git a/Runtime/Shading/Materials/Transfers/Texture2DPropertyTransfer.cs b/Runtime/Shading/Materials/Transfers/Texture2DPropertyTransfer.cs new file mode 100644 index 0000000..f280312 --- /dev/null +++ b/Runtime/Shading/Materials/Transfers/Texture2DPropertyTransfer.cs @@ -0,0 +1,35 @@ +using Godot; +using System.Reflection; +using System.Collections.Generic; + +namespace Rokojori +{ + [Tool] + [GlobalClass] + public partial class Texture2DPropertyTransfer:Resource + { + [Export] + public Texture2DPropertyName from; + + [Export] + public Texture2DPropertyName to; + + public void Transfer( Material a, Material b ) + { + var v = from.Get( a ); + + if ( v == null ) + { + return; + } + else + { + to.Set( b, v ); + } + + + } + + } +} + diff --git a/Runtime/Shading/Materials/Transfers/TextureChannelToVector4Transfer.cs b/Runtime/Shading/Materials/Transfers/TextureChannelToVector4Transfer.cs new file mode 100644 index 0000000..f51607d --- /dev/null +++ b/Runtime/Shading/Materials/Transfers/TextureChannelToVector4Transfer.cs @@ -0,0 +1,51 @@ +using Godot; +using System.Reflection; +using System.Collections.Generic; + +namespace Rokojori +{ + [Tool] + [GlobalClass] + public partial class TextureChannelToVector4Transfer:CustomMaterialTransfer + { + [Export] + public TextureChannelPropertyName from; + + [Export] + public Vector4PropertyName to; + + public override void Transfer( Material a, Material b ) + { + var v = from.Get( a ); + + var v4 = Vector4.Zero; + + if ( BaseMaterial3D.TextureChannel.Alpha == v ) + { + v4 = new Vector4( 0, 0, 0, 1 ); + } + else if ( BaseMaterial3D.TextureChannel.Red == v ) + { + v4 = new Vector4( 1, 0, 0, 0 ); + } + else if ( BaseMaterial3D.TextureChannel.Green == v ) + { + v4 = new Vector4( 0, 1, 0, 0 ); + } + else if ( BaseMaterial3D.TextureChannel.Blue == v ) + { + v4 = new Vector4( 0, 0, 1, 0 ); + } + else if ( BaseMaterial3D.TextureChannel.Grayscale == v ) + { + v4 = new Vector4( 1f/3f, 1f/3f, 1f/3f, 0 ); + } + + + to.Set( b, v4 ); + + } + + } +} + diff --git a/Runtime/Shading/Materials/Transfers/Vector2PropertyTransfer.cs b/Runtime/Shading/Materials/Transfers/Vector2PropertyTransfer.cs new file mode 100644 index 0000000..b1c00b5 --- /dev/null +++ b/Runtime/Shading/Materials/Transfers/Vector2PropertyTransfer.cs @@ -0,0 +1,25 @@ +using Godot; +using System.Reflection; +using System.Collections.Generic; + +namespace Rokojori +{ + [Tool] + [GlobalClass] + public partial class Vector2PropertyTransfer:Resource + { + [Export] + public Vector2PropertyName from; + + [Export] + public Vector2PropertyName to; + + public void Transfer( Material a, Material b ) + { + var v = from.Get( a ); + to.Set( b, v ); + } + + } +} + diff --git a/Runtime/Shading/Materials/Transfers/Vector3PropertyTransfer.cs b/Runtime/Shading/Materials/Transfers/Vector3PropertyTransfer.cs new file mode 100644 index 0000000..14fd6fa --- /dev/null +++ b/Runtime/Shading/Materials/Transfers/Vector3PropertyTransfer.cs @@ -0,0 +1,25 @@ +using Godot; +using System.Reflection; +using System.Collections.Generic; + +namespace Rokojori +{ + [Tool] + [GlobalClass] + public partial class Vector3PropertyTransfer:Resource + { + [Export] + public Vector3PropertyName from; + + [Export] + public Vector3PropertyName to; + + public void Transfer( Material a, Material b ) + { + var v = from.Get( a ); + to.Set( b, v ); + } + + } +} + diff --git a/Runtime/Shading/Materials/Transfers/Vector4PropertyTransfer.cs b/Runtime/Shading/Materials/Transfers/Vector4PropertyTransfer.cs new file mode 100644 index 0000000..8b73909 --- /dev/null +++ b/Runtime/Shading/Materials/Transfers/Vector4PropertyTransfer.cs @@ -0,0 +1,24 @@ +using Godot; +using System.Reflection; +using System.Collections.Generic; + +namespace Rokojori +{ + [Tool] + [GlobalClass] + public partial class Vector4PropertyTransfer:Resource + { + [Export] + public Vector4PropertyName from; + + [Export] + public Vector4PropertyName to; + + public void Transfer( Material a, Material b ) + { + var v = from.Get( a ); + to.Set( b, v ); + } + } +} + diff --git a/Runtime/Shading/Properties/BoolPropertyName.cs b/Runtime/Shading/Properties/BoolPropertyName.cs new file mode 100644 index 0000000..55016ac --- /dev/null +++ b/Runtime/Shading/Properties/BoolPropertyName.cs @@ -0,0 +1,28 @@ +using Godot; +using System.Reflection; +using System.Collections.Generic; + +namespace Rokojori +{ + [Tool] + [GlobalClass] + public partial class BoolPropertyName : ShaderPropertyName + { + public void Set( Material material, bool value ) + { + _Set( material, value ); + } + + public bool Get( Material material ) + { + return _Get( material, false ); + } + + public static BoolPropertyName Create( string name ) + { + var p = new BoolPropertyName(); + p.propertyName = name; + return p; + } + } +} \ No newline at end of file diff --git a/Runtime/Shading/Properties/ColorPropertyName.cs b/Runtime/Shading/Properties/ColorPropertyName.cs index 4f0424f..5747247 100644 --- a/Runtime/Shading/Properties/ColorPropertyName.cs +++ b/Runtime/Shading/Properties/ColorPropertyName.cs @@ -6,49 +6,23 @@ namespace Rokojori { [Tool] [GlobalClass] - public partial class ColorPropertyName : Resource + public partial class ColorPropertyName : ShaderPropertyName { - [Export] - public string propertyName; - public void Set( Material material, Color value ) { - if ( material is ShaderMaterial ) - { - var shaderMaterial = ( ShaderMaterial ) material; - shaderMaterial.SetShaderParameter( propertyName, value ); - - return; - } - - var propertyInfo = material.GetType().GetProperty( propertyName ); - - if ( propertyInfo == null ) - { - return; - } - - propertyInfo.SetValue( material, value ); - + _Set( material, value ); } public Color Get( Material material ) { - if ( material is ShaderMaterial ) - { - var m = ( ShaderMaterial ) material; - return (Color) m.GetShaderParameter( propertyName ); - } - - var propertyInfo = material.GetType().GetProperty( propertyName ); - - if ( propertyInfo == null ) - { - return default( Color ); - } - - return (Color) propertyInfo.GetValue( material ); + return _Get( material, Colors.Black ); + } + public static ColorPropertyName Create( string name ) + { + var p = new ColorPropertyName(); + p.propertyName = name; + return p; } } } \ No newline at end of file diff --git a/Runtime/Shading/Properties/Enums/TextureChannelPropertyName.cs b/Runtime/Shading/Properties/Enums/TextureChannelPropertyName.cs new file mode 100644 index 0000000..e5e2820 --- /dev/null +++ b/Runtime/Shading/Properties/Enums/TextureChannelPropertyName.cs @@ -0,0 +1,21 @@ +using Godot; +using System.Reflection; +using System.Collections.Generic; + +namespace Rokojori +{ + [Tool] + [GlobalClass] + public partial class TextureChannelPropertyName : ShaderPropertyName + { + public void Set( Material material, BaseMaterial3D.TextureChannel value ) + { + _Set( material, value ); + } + + public BaseMaterial3D.TextureChannel Get( Material material ) + { + return (BaseMaterial3D.TextureChannel)_Get( material, 0 ); + } + } +} \ No newline at end of file diff --git a/Runtime/Shading/Properties/FloatPropertyName.cs b/Runtime/Shading/Properties/FloatPropertyName.cs index 5acb5aa..d70debf 100644 --- a/Runtime/Shading/Properties/FloatPropertyName.cs +++ b/Runtime/Shading/Properties/FloatPropertyName.cs @@ -6,49 +6,23 @@ namespace Rokojori { [Tool] [GlobalClass] - public partial class FloatPropertyName : Resource + public partial class FloatPropertyName : ShaderPropertyName { - [Export] - public string propertyName; - public void Set( Material material, float value ) { - if ( material is ShaderMaterial ) - { - var shaderMaterial = ( ShaderMaterial ) material; - shaderMaterial.SetShaderParameter( propertyName, value ); - - return; - } - - var propertyInfo = material.GetType().GetProperty( propertyName ); - - if ( propertyInfo == null ) - { - return; - } - - propertyInfo.SetValue( material, value ); - + _Set( material, value ); } public float Get( Material material ) { - if ( material is ShaderMaterial ) - { - var m = ( ShaderMaterial ) material; - return (float) m.GetShaderParameter( propertyName ); - } - - var propertyInfo = material.GetType().GetProperty( propertyName ); - - if ( propertyInfo == null ) - { - return default( float ); - } - - return (float) propertyInfo.GetValue( material ); + return _Get( material, 0f ); + } + public static FloatPropertyName Create( string name ) + { + var fp = new FloatPropertyName(); + fp.propertyName = name; + return fp; } } } \ No newline at end of file diff --git a/Runtime/Shading/Properties/IntPropertyName.cs b/Runtime/Shading/Properties/IntPropertyName.cs new file mode 100644 index 0000000..f61f68d --- /dev/null +++ b/Runtime/Shading/Properties/IntPropertyName.cs @@ -0,0 +1,28 @@ +using Godot; +using System.Reflection; +using System.Collections.Generic; + +namespace Rokojori +{ + [Tool] + [GlobalClass] + public partial class IntPropertyName : ShaderPropertyName + { + public void Set( Material material, int value ) + { + _Set( material, value ); + } + + public int Get( Material material ) + { + return _Get( material, 0 ); + } + + public static IntPropertyName Create( string name ) + { + var p = new IntPropertyName(); + p.propertyName = name; + return p; + } + } +} \ No newline at end of file diff --git a/Runtime/Shading/Properties/Property Library/Color/Albedo Color.tres b/Runtime/Shading/Properties/Property Library/Color/Albedo Color.tres new file mode 100644 index 0000000..9ade7de --- /dev/null +++ b/Runtime/Shading/Properties/Property Library/Color/Albedo Color.tres @@ -0,0 +1,7 @@ +[gd_resource type="Resource" script_class="ColorPropertyName" load_steps=2 format=3 uid="uid://cwbo0avyyq0t"] + +[ext_resource type="Script" path="res://addons/rokojori_action_library/Runtime/Shading/Properties/ColorPropertyName.cs" id="1_qmhdp"] + +[resource] +script = ExtResource("1_qmhdp") +propertyName = "albedo_color" diff --git a/Runtime/Shading/Properties/Property Library/Float/Alpha Scissor Threshold.tres b/Runtime/Shading/Properties/Property Library/Float/Alpha Scissor Threshold.tres new file mode 100644 index 0000000..e971086 --- /dev/null +++ b/Runtime/Shading/Properties/Property Library/Float/Alpha Scissor Threshold.tres @@ -0,0 +1,7 @@ +[gd_resource type="Resource" script_class="FloatPropertyName" load_steps=2 format=3 uid="uid://bja8pltfjyt3x"] + +[ext_resource type="Script" path="res://addons/rokojori_action_library/Runtime/Shading/Properties/FloatPropertyName.cs" id="1_43g4x"] + +[resource] +script = ExtResource("1_43g4x") +propertyName = "alpha_scissor_threshold" diff --git a/Runtime/Shading/Properties/Property Library/Float/Metallic.tres b/Runtime/Shading/Properties/Property Library/Float/Metallic.tres new file mode 100644 index 0000000..468db7b --- /dev/null +++ b/Runtime/Shading/Properties/Property Library/Float/Metallic.tres @@ -0,0 +1,7 @@ +[gd_resource type="Resource" script_class="FloatPropertyName" load_steps=2 format=3 uid="uid://dy4yrg7cbx84g"] + +[ext_resource type="Script" path="res://addons/rokojori_action_library/Runtime/Shading/Properties/FloatPropertyName.cs" id="1_k46dx"] + +[resource] +script = ExtResource("1_k46dx") +propertyName = "metallic" diff --git a/Runtime/Shading/Properties/Property Library/Float/Normal Scale.tres b/Runtime/Shading/Properties/Property Library/Float/Normal Scale.tres new file mode 100644 index 0000000..9dec1d5 --- /dev/null +++ b/Runtime/Shading/Properties/Property Library/Float/Normal Scale.tres @@ -0,0 +1,7 @@ +[gd_resource type="Resource" script_class="FloatPropertyName" load_steps=2 format=3 uid="uid://c6actnjj8i8o5"] + +[ext_resource type="Script" path="res://addons/rokojori_action_library/Runtime/Shading/Properties/FloatPropertyName.cs" id="1_4k8vm"] + +[resource] +script = ExtResource("1_4k8vm") +propertyName = "normal_scale" diff --git a/Runtime/Shading/Properties/Property Library/Float/Roughness.tres b/Runtime/Shading/Properties/Property Library/Float/Roughness.tres new file mode 100644 index 0000000..56bf4a8 --- /dev/null +++ b/Runtime/Shading/Properties/Property Library/Float/Roughness.tres @@ -0,0 +1,7 @@ +[gd_resource type="Resource" script_class="FloatPropertyName" load_steps=2 format=3 uid="uid://dn4k668h72myc"] + +[ext_resource type="Script" path="res://addons/rokojori_action_library/Runtime/Shading/Properties/FloatPropertyName.cs" id="1_qnqwx"] + +[resource] +script = ExtResource("1_qnqwx") +propertyName = "" diff --git a/Runtime/Shading/Properties/Property Library/Texture2D/AO Texture.tres b/Runtime/Shading/Properties/Property Library/Texture2D/AO Texture.tres new file mode 100644 index 0000000..bbd86bf --- /dev/null +++ b/Runtime/Shading/Properties/Property Library/Texture2D/AO Texture.tres @@ -0,0 +1,7 @@ +[gd_resource type="Resource" script_class="Texture2DPropertyName" load_steps=2 format=3 uid="uid://cow6rei03x5bs"] + +[ext_resource type="Script" path="res://addons/rokojori_action_library/Runtime/Shading/Properties/Texture2DPropertyName.cs" id="1_ptfhg"] + +[resource] +script = ExtResource("1_ptfhg") +propertyName = "ao_texture" diff --git a/Runtime/Shading/Properties/Property Library/Texture2D/Albedo Texture.tres b/Runtime/Shading/Properties/Property Library/Texture2D/Albedo Texture.tres new file mode 100644 index 0000000..804a7c6 --- /dev/null +++ b/Runtime/Shading/Properties/Property Library/Texture2D/Albedo Texture.tres @@ -0,0 +1,7 @@ +[gd_resource type="Resource" script_class="Texture2DPropertyName" load_steps=2 format=3 uid="uid://dldbju3x0x2ow"] + +[ext_resource type="Script" path="res://addons/rokojori_action_library/Runtime/Shading/Properties/Texture2DPropertyName.cs" id="1_d1083"] + +[resource] +script = ExtResource("1_d1083") +propertyName = "albedo_texture" diff --git a/Runtime/Shading/Properties/Property Library/Texture2D/Metallic Texture.tres b/Runtime/Shading/Properties/Property Library/Texture2D/Metallic Texture.tres new file mode 100644 index 0000000..28e06d2 --- /dev/null +++ b/Runtime/Shading/Properties/Property Library/Texture2D/Metallic Texture.tres @@ -0,0 +1,7 @@ +[gd_resource type="Resource" script_class="Texture2DPropertyName" load_steps=2 format=3 uid="uid://csiwpeupf761o"] + +[ext_resource type="Script" path="res://addons/rokojori_action_library/Runtime/Shading/Properties/Texture2DPropertyName.cs" id="1_ygllj"] + +[resource] +script = ExtResource("1_ygllj") +propertyName = "metallic_texture" diff --git a/Runtime/Shading/Properties/Property Library/Texture2D/Normal Texture.tres b/Runtime/Shading/Properties/Property Library/Texture2D/Normal Texture.tres new file mode 100644 index 0000000..3fd29c8 --- /dev/null +++ b/Runtime/Shading/Properties/Property Library/Texture2D/Normal Texture.tres @@ -0,0 +1,7 @@ +[gd_resource type="Resource" script_class="Texture2DPropertyName" load_steps=2 format=3 uid="uid://bmt10lgwm8ckx"] + +[ext_resource type="Script" path="res://addons/rokojori_action_library/Runtime/Shading/Properties/Texture2DPropertyName.cs" id="1_i15gp"] + +[resource] +script = ExtResource("1_i15gp") +propertyName = "normal_texture" diff --git a/Runtime/Shading/Properties/Property Library/Texture2D/Roughness Texture.tres b/Runtime/Shading/Properties/Property Library/Texture2D/Roughness Texture.tres new file mode 100644 index 0000000..05ca089 --- /dev/null +++ b/Runtime/Shading/Properties/Property Library/Texture2D/Roughness Texture.tres @@ -0,0 +1,7 @@ +[gd_resource type="Resource" script_class="Texture2DPropertyName" load_steps=2 format=3 uid="uid://bdjlvcpc13hl6"] + +[ext_resource type="Script" path="res://addons/rokojori_action_library/Runtime/Shading/Properties/Texture2DPropertyName.cs" id="1_nilyf"] + +[resource] +script = ExtResource("1_nilyf") +propertyName = "roughness_texture" diff --git a/Runtime/Shading/Properties/Property Library/TextureChannels/AO Texture Channel.tres b/Runtime/Shading/Properties/Property Library/TextureChannels/AO Texture Channel.tres new file mode 100644 index 0000000..2e4a796 --- /dev/null +++ b/Runtime/Shading/Properties/Property Library/TextureChannels/AO Texture Channel.tres @@ -0,0 +1,7 @@ +[gd_resource type="Resource" script_class="TextureChannelPropertyName" load_steps=2 format=3 uid="uid://txmti1ghef1"] + +[ext_resource type="Script" path="res://addons/rokojori_action_library/Runtime/Shading/Properties/Enums/TextureChannelPropertyName.cs" id="1_0bu4b"] + +[resource] +script = ExtResource("1_0bu4b") +propertyName = "" diff --git a/Runtime/Shading/Properties/Property Library/TextureChannels/Metallic Texture Channel.tres b/Runtime/Shading/Properties/Property Library/TextureChannels/Metallic Texture Channel.tres new file mode 100644 index 0000000..8ce8543 --- /dev/null +++ b/Runtime/Shading/Properties/Property Library/TextureChannels/Metallic Texture Channel.tres @@ -0,0 +1,7 @@ +[gd_resource type="Resource" script_class="TextureChannelPropertyName" load_steps=2 format=3 uid="uid://xoj2836knmix"] + +[ext_resource type="Script" path="res://addons/rokojori_action_library/Runtime/Shading/Properties/Enums/TextureChannelPropertyName.cs" id="1_ruok1"] + +[resource] +script = ExtResource("1_ruok1") +propertyName = "metallic_texture_channel" diff --git a/Runtime/Shading/Properties/Property Library/TextureChannels/Roughness Texture Channel.tres b/Runtime/Shading/Properties/Property Library/TextureChannels/Roughness Texture Channel.tres new file mode 100644 index 0000000..544bb70 --- /dev/null +++ b/Runtime/Shading/Properties/Property Library/TextureChannels/Roughness Texture Channel.tres @@ -0,0 +1,7 @@ +[gd_resource type="Resource" script_class="TextureChannelPropertyName" load_steps=2 format=3 uid="uid://d2x1ijwsv2urr"] + +[ext_resource type="Script" path="res://addons/rokojori_action_library/Runtime/Shading/Properties/Enums/TextureChannelPropertyName.cs" id="1_p7os4"] + +[resource] +script = ExtResource("1_p7os4") +propertyName = "ao_texture_channel" diff --git a/Runtime/Shading/Properties/Property Library/Vector3/UV1 Offset.tres b/Runtime/Shading/Properties/Property Library/Vector3/UV1 Offset.tres new file mode 100644 index 0000000..a4a8d29 --- /dev/null +++ b/Runtime/Shading/Properties/Property Library/Vector3/UV1 Offset.tres @@ -0,0 +1,7 @@ +[gd_resource type="Resource" script_class="Vector3PropertyName" load_steps=2 format=3 uid="uid://cn7nxc0qw7pan"] + +[ext_resource type="Script" path="res://addons/rokojori_action_library/Runtime/Shading/Properties/Vector3PropertyName.cs" id="1_m0xye"] + +[resource] +script = ExtResource("1_m0xye") +propertyName = "uv1_scale" diff --git a/Runtime/Shading/Properties/Property Library/Vector3/UV1 Scale.tres b/Runtime/Shading/Properties/Property Library/Vector3/UV1 Scale.tres new file mode 100644 index 0000000..bec2190 --- /dev/null +++ b/Runtime/Shading/Properties/Property Library/Vector3/UV1 Scale.tres @@ -0,0 +1,7 @@ +[gd_resource type="Resource" script_class="Vector3PropertyName" load_steps=2 format=3 uid="uid://douianw6mx4p5"] + +[ext_resource type="Script" path="res://addons/rokojori_action_library/Runtime/Shading/Properties/Vector3PropertyName.cs" id="1_s8cc5"] + +[resource] +script = ExtResource("1_s8cc5") +propertyName = "uv1_scale" diff --git a/Runtime/Shading/Properties/Property Library/Vector3/UV2 Offset.tres b/Runtime/Shading/Properties/Property Library/Vector3/UV2 Offset.tres new file mode 100644 index 0000000..9182603 --- /dev/null +++ b/Runtime/Shading/Properties/Property Library/Vector3/UV2 Offset.tres @@ -0,0 +1,7 @@ +[gd_resource type="Resource" script_class="Vector3PropertyName" load_steps=2 format=3 uid="uid://bfcgvff3segr8"] + +[ext_resource type="Script" path="res://addons/rokojori_action_library/Runtime/Shading/Properties/Vector3PropertyName.cs" id="1_i1pux"] + +[resource] +script = ExtResource("1_i1pux") +propertyName = "uv2_offset" diff --git a/Runtime/Shading/Properties/Property Library/Vector3/UV2 Scale.tres b/Runtime/Shading/Properties/Property Library/Vector3/UV2 Scale.tres new file mode 100644 index 0000000..4ea5d92 --- /dev/null +++ b/Runtime/Shading/Properties/Property Library/Vector3/UV2 Scale.tres @@ -0,0 +1,7 @@ +[gd_resource type="Resource" script_class="Vector3PropertyName" load_steps=2 format=3 uid="uid://d4n17mebuyr8h"] + +[ext_resource type="Script" path="res://addons/rokojori_action_library/Runtime/Shading/Properties/Vector3PropertyName.cs" id="1_q6wy5"] + +[resource] +script = ExtResource("1_q6wy5") +propertyName = "uv2_scale" diff --git a/Runtime/Shading/Properties/Property Library/Vector4/AO Texture Channel.tres b/Runtime/Shading/Properties/Property Library/Vector4/AO Texture Channel.tres new file mode 100644 index 0000000..1dba653 --- /dev/null +++ b/Runtime/Shading/Properties/Property Library/Vector4/AO Texture Channel.tres @@ -0,0 +1,7 @@ +[gd_resource type="Resource" script_class="Vector4PropertyName" load_steps=2 format=3 uid="uid://bprgfki4joe6x"] + +[ext_resource type="Script" path="res://addons/rokojori_action_library/Runtime/Shading/Properties/Vector4PropertyName.cs" id="1_7pr4j"] + +[resource] +script = ExtResource("1_7pr4j") +propertyName = "" diff --git a/Runtime/Shading/Properties/Property Library/Vector4/Metallic Texture Channel.tres b/Runtime/Shading/Properties/Property Library/Vector4/Metallic Texture Channel.tres new file mode 100644 index 0000000..2f2772d --- /dev/null +++ b/Runtime/Shading/Properties/Property Library/Vector4/Metallic Texture Channel.tres @@ -0,0 +1,7 @@ +[gd_resource type="Resource" script_class="Vector4PropertyName" load_steps=2 format=3 uid="uid://b0t27ujy2oqpd"] + +[ext_resource type="Script" path="res://addons/rokojori_action_library/Runtime/Shading/Properties/Vector4PropertyName.cs" id="1_47ufn"] + +[resource] +script = ExtResource("1_47ufn") +propertyName = "metallic_texture_channel" diff --git a/Runtime/Shading/Properties/Property Library/Vector4/Roughness Texture Channel.tres b/Runtime/Shading/Properties/Property Library/Vector4/Roughness Texture Channel.tres new file mode 100644 index 0000000..3a313c6 --- /dev/null +++ b/Runtime/Shading/Properties/Property Library/Vector4/Roughness Texture Channel.tres @@ -0,0 +1,7 @@ +[gd_resource type="Resource" script_class="Vector4PropertyName" load_steps=2 format=3 uid="uid://cni6awfm7rw14"] + +[ext_resource type="Script" path="res://addons/rokojori_action_library/Runtime/Shading/Properties/Vector4PropertyName.cs" id="1_b1bto"] + +[resource] +script = ExtResource("1_b1bto") +propertyName = "ao_texture_channel" diff --git a/Runtime/Shading/Properties/ShaderPropertyName.cs b/Runtime/Shading/Properties/ShaderPropertyName.cs new file mode 100644 index 0000000..1a41755 --- /dev/null +++ b/Runtime/Shading/Properties/ShaderPropertyName.cs @@ -0,0 +1,99 @@ +using Godot; +using System; +using System.Reflection; +using System.Collections.Generic; + +namespace Rokojori +{ + public partial class ShaderPropertyName:Resource + { + [Export] + public string propertyName; + + string _cSharpName; + + MultiMap _propertyInfoCache = new MultiMap(); + + public void _Set<[MustBeVariant] T>( Material m, T value ) + { + if ( m is ShaderMaterial shaderMaterial ) + { + + // RJLog.Log( "_Set", value, value.GetType() ); + var variant = Variant.From( value ); + shaderMaterial.SetShaderParameter( propertyName, variant ); + return; + } + + _SetStandardValue( m, value ); + } + + public T _Get<[MustBeVariant] T>( Material material, T fallback ) + { + if ( material is ShaderMaterial shaderMaterial ) + { + return shaderMaterial.GetShaderParameter( propertyName ).As(); + } + + return _GetStandardValue( material, fallback ); + } + + protected void _SetStandardValue( Material material, object value ) + { + var p = _GetProperty( material ); + + if ( p == null ) + { + return; + } + + p.SetValue( material, value ); + } + + + + protected T _GetStandardValue( Material material, T fallback ) + { + var p = _GetProperty( material ); + + if ( p == null ) + { + return fallback; + } + + var value = p.GetValue( material ); + + RJLog.Log( "GetStandardValue", _cSharpName, p.Name, ">>", value ); + + return (T) p.GetValue( material ); + } + + protected PropertyInfo _GetProperty( Material material ) + { + var type = material.GetType(); + + if ( _cSharpName != null && _cSharpName != "" && _propertyInfoCache.Has( type, _cSharpName ) ) + { + RJLog.Log( type, ">>", material, "_cSharpName", _cSharpName ); + + return _propertyInfoCache[ type ][ _cSharpName ]; + } + + if ( ! RegexUtility.StartsWithUpperCase( propertyName ) ) + { + _cSharpName = GDScriptNames.ToCS( propertyName ); + } + else + { + _cSharpName = propertyName; + } + + var p = type.GetProperty( _cSharpName ); + RJLog.Log( "_cSharpName", _cSharpName, material ); + _propertyInfoCache.Set( type, _cSharpName, p ); + + return _propertyInfoCache[ type ][ _cSharpName ]; + } + + } +} \ No newline at end of file diff --git a/Runtime/Shading/Properties/Texture2DPropertyName.cs b/Runtime/Shading/Properties/Texture2DPropertyName.cs new file mode 100644 index 0000000..6ee42c8 --- /dev/null +++ b/Runtime/Shading/Properties/Texture2DPropertyName.cs @@ -0,0 +1,29 @@ +using Godot; +using System.Reflection; +using System.Collections.Generic; + +namespace Rokojori +{ + [Tool] + [GlobalClass] + public partial class Texture2DPropertyName : ShaderPropertyName + { + + public void Set( Material material, Texture2D value ) + { + _Set( material, value ); + } + + public Texture2D Get( Material material ) + { + return _Get( material, null ); + } + + public static Texture2DPropertyName Create( string name ) + { + var t = new Texture2DPropertyName(); + t.propertyName = name; + return t; + } + } +} \ No newline at end of file diff --git a/Runtime/Shading/Properties/Vector2PropertyName.cs b/Runtime/Shading/Properties/Vector2PropertyName.cs index ee0526b..5e93a35 100644 --- a/Runtime/Shading/Properties/Vector2PropertyName.cs +++ b/Runtime/Shading/Properties/Vector2PropertyName.cs @@ -6,48 +6,23 @@ namespace Rokojori { [Tool] [GlobalClass] - public partial class Vector2PropertyName : Resource + public partial class Vector2PropertyName : ShaderPropertyName { - [Export] - public string propertyName; - public void Set( Material material, Vector2 value ) { - if ( material is ShaderMaterial ) - { - var shaderMaterial = ( ShaderMaterial ) material; - shaderMaterial.SetShaderParameter( propertyName, value ); - return; - } - - var propertyInfo = material.GetType().GetProperty( propertyName ); - - if ( propertyInfo == null ) - { - return; - } - - propertyInfo.SetValue( material, value ); - + _Set( material, value ); } public Vector2 Get( Material material ) { - if ( material is ShaderMaterial ) - { - var m = ( ShaderMaterial ) material; - return (Vector2) m.GetShaderParameter( propertyName ); - } - - var propertyInfo = material.GetType().GetProperty( propertyName ); - - if ( propertyInfo == null ) - { - return default( Vector2 ); - } - - return (Vector2) propertyInfo.GetValue( material ); + return _Get( material, Vector2.Zero ); + } + public static Vector2PropertyName Create( string name ) + { + var p = new Vector2PropertyName(); + p.propertyName = name; + return p; } } } \ No newline at end of file diff --git a/Runtime/Shading/Properties/Vector3PropertyName.cs b/Runtime/Shading/Properties/Vector3PropertyName.cs index 84de195..ee8685e 100644 --- a/Runtime/Shading/Properties/Vector3PropertyName.cs +++ b/Runtime/Shading/Properties/Vector3PropertyName.cs @@ -6,49 +6,23 @@ namespace Rokojori { [Tool] [GlobalClass] - public partial class Vector3PropertyName : Resource + public partial class Vector3PropertyName : ShaderPropertyName { - [Export] - public string propertyName; - public void Set( Material material, Vector3 value ) { - if ( material is ShaderMaterial ) - { - var shaderMaterial = ( ShaderMaterial ) material; - shaderMaterial.SetShaderParameter( propertyName, value ); - - return; - } - - var propertyInfo = material.GetType().GetProperty( propertyName ); - - if ( propertyInfo == null ) - { - return; - } - - propertyInfo.SetValue( material, value ); - + _Set( material, value ); } public Vector3 Get( Material material ) { - if ( material is ShaderMaterial ) - { - var m = ( ShaderMaterial ) material; - return (Vector3) m.GetShaderParameter( propertyName ); - } - - var propertyInfo = material.GetType().GetProperty( propertyName ); - - if ( propertyInfo == null ) - { - return default( Vector3 ); - } - - return (Vector3) propertyInfo.GetValue( material ); + return _Get( material, Vector3.Zero ); + } + public static Vector3PropertyName Create( string name ) + { + var p = new Vector3PropertyName(); + p.propertyName = name; + return p; } } } \ No newline at end of file diff --git a/Runtime/Shading/Properties/Vector4PropertyName.cs b/Runtime/Shading/Properties/Vector4PropertyName.cs index bc1534a..7c34a0a 100644 --- a/Runtime/Shading/Properties/Vector4PropertyName.cs +++ b/Runtime/Shading/Properties/Vector4PropertyName.cs @@ -6,49 +6,24 @@ namespace Rokojori { [Tool] [GlobalClass] - public partial class Vector4PropertyName : Resource + public partial class Vector4PropertyName : ShaderPropertyName { - [Export] - public string propertyName; - public void Set( Material material, Vector4 value ) { - if ( material is ShaderMaterial ) - { - var shaderMaterial = ( ShaderMaterial ) material; - shaderMaterial.SetShaderParameter( propertyName, value ); - - return; - } - - var propertyInfo = material.GetType().GetProperty( propertyName ); - - if ( propertyInfo == null ) - { - return; - } - - propertyInfo.SetValue( material, value ); - + _Set( material, value ); } public Vector4 Get( Material material ) { - if ( material is ShaderMaterial ) - { - var m = ( ShaderMaterial ) material; - return (Vector4) m.GetShaderParameter( propertyName ); - } - - var propertyInfo = material.GetType().GetProperty( propertyName ); - - if ( propertyInfo == null ) - { - return default( Vector4 ); - } - - return (Vector4) propertyInfo.GetValue( material ); + return _Get( material, Vector4.Zero ); + } + public static Vector4PropertyName Create( string name ) + { + var p = new Vector4PropertyName(); + p.propertyName = name; + return p; } } + } \ No newline at end of file diff --git a/Runtime/Shading/Shaders/Baking/DepthMap.gdshader b/Runtime/Shading/Shaders/Baking/DepthMap.gdshader new file mode 100644 index 0000000..183ff6c --- /dev/null +++ b/Runtime/Shading/Shaders/Baking/DepthMap.gdshader @@ -0,0 +1,42 @@ +shader_type spatial; + +render_mode unshaded; + +#include "res://addons/rokojori_action_library/Runtime/Shading/Library/Math.gdshaderinc" +#include "res://addons/rokojori_action_library/Runtime/Shading/Library/Transform.gdshaderinc" +#include "res://addons/rokojori_action_library/Runtime/Shading/Library/Noise.gdshaderinc" +#include "res://addons/rokojori_action_library/Runtime/Shading/Library/Light.gdshaderinc" + + +uniform vec4 albedo : source_color; +uniform sampler2D texture_albedo : source_color, filter_linear_mipmap, repeat_enable; +uniform float alpha_scissor_threshold : hint_range(0.0, 1.0, 0.001); + +uniform float alpha_antialiasing_edge : hint_range(0.0, 1.0, 0.01); +uniform ivec2 albedo_texture_size; + +uniform float centerDistance = 100; +uniform float rangeDistance = 50; + +void vertex() +{ + +} + +void fragment() +{ + vec4 albedo_tex = texture( texture_albedo, UV); + vec3 worldPosition = ( INV_VIEW_MATRIX * vec4( VERTEX, 1.0 ) ).xyz; + float worldDistance = length( worldPosition - CAMERA_POSITION_WORLD ); + float range = rangeDistance / 2.0; + float value = normalizeToRange01( worldDistance, + centerDistance - range, centerDistance + range + ); + + ALBEDO = vec3( 1, 1, 1 ) * value; + + ALPHA *= albedo.a * albedo_tex.a; + ALPHA_SCISSOR_THRESHOLD = alpha_scissor_threshold; + ALPHA_ANTIALIASING_EDGE = alpha_antialiasing_edge; + ALPHA_TEXTURE_COORDINATE = UV * vec2(albedo_texture_size); +} diff --git a/Runtime/Shading/Shaders/Baking/DepthMap.material b/Runtime/Shading/Shaders/Baking/DepthMap.material new file mode 100644 index 0000000..f33d870 Binary files /dev/null and b/Runtime/Shading/Shaders/Baking/DepthMap.material differ diff --git a/Runtime/Shading/Shaders/Billboards/QuadBillboard.gdshader b/Runtime/Shading/Shaders/Billboards/QuadBillboard.gdshader new file mode 100644 index 0000000..ac0a3a5 --- /dev/null +++ b/Runtime/Shading/Shaders/Billboards/QuadBillboard.gdshader @@ -0,0 +1,133 @@ +shader_type spatial; + +render_mode blend_mix, depth_draw_opaque, cull_back, diffuse_burley, specular_schlick_ggx, alpha_to_coverage_and_one; + +#include "res://addons/rokojori_action_library/Runtime/Shading/Library/Math.gdshaderinc" +#include "res://addons/rokojori_action_library/Runtime/Shading/Library/Transform.gdshaderinc" +#include "res://addons/rokojori_action_library/Runtime/Shading/Library/Noise.gdshaderinc" +#include "res://addons/rokojori_action_library/Runtime/Shading/Library/Light.gdshaderinc" + +uniform float size = 1; +uniform float randomPosition; +uniform vec4 albedo : source_color; +uniform float rotation : hint_range(0,6.28) = 0; +uniform float rotationNoiseAmount = 0; +uniform float rotationNoiseScale = 0; +uniform sampler2D texture_albedo : source_color, filter_linear_mipmap, repeat_disable; +uniform sampler2D texture_normals : hint_roughness_normal, filter_linear_mipmap, repeat_disable; +uniform float normalBlendAmount: hint_range(0,1) = 0.5; +uniform vec3 normalBlendDirection = vec3( 0, 1, 0 ); +uniform float roughness: hint_range(0,1) = 1; +uniform float metalness: hint_range(0,1) = 1; +uniform float specular: hint_range(0,1) = 1; + + +uniform float alpha_scissor_threshold : hint_range(0.0, 1.0, 0.001); +uniform float alpha_antialiasing_edge : hint_range(0.0, 1.0, 0.01); +uniform ivec2 albedo_texture_size; + +uniform sampler2D texture_ambient_occlusion : hint_default_white, filter_linear_mipmap, repeat_enable; +uniform vec4 ao_texture_channel; +uniform float ao_light_affect : hint_range(0.0, 1.0, 0.01); + + +uniform float yMapStart; +uniform float yMapEnd; +uniform float yMapNoiseAmount; +uniform float yMapNoiseScale; +varying float yMapValue; +uniform sampler2D texture_yMapGradient : source_color, filter_linear_mipmap, repeat_disable; +varying float uvRotation; +uniform float discardNoiseScale = 1; +uniform float discardThreshold : hint_range(0,1) = 1; +varying float discardValue; +uniform float fresnelNormalSource : hint_range(0,1) = 0.5; +uniform vec4 fresnelColor : source_color; +uniform float fresnelSharpness : hint_range(0.001,10) = 1; +uniform float fresnelScale = 1; +varying float fresnelAmount; + +varying vec3 originalNormal; +uniform float windAmount = 1; +uniform vec2 windSpeed = vec2( 0.1, 0.1 ); +uniform float windNoiseScale = 1; +uniform float windStart = 0; +uniform float windEnd = 1; + +void vertex() +{ + vec4 worldVertex = MODEL_MATRIX *vec4( VERTEX, 1.0 ); + float windAngle = perlinPolar( TIME * windSpeed + windNoiseScale * vec2( worldVertex.x, worldVertex.z ) ); + float windStrength = perlinPolar( TIME * windSpeed + windNoiseScale * vec2( worldVertex.x, worldVertex.z ) + vec2( 1023.9, 334.90 ) ); + + vec2 windDirection = onCircle( windAngle * 6.28 ) * windStrength; + float yWindAmount = normalizeToRange01( worldVertex.y, windStart, windEnd ) * windAmount; + + float nrx = perlinPolar( NORMAL.yz + worldVertex.yz ); + float nry = perlinPolar( NORMAL.xz + worldVertex.xz ); + float nrz = perlinPolar( NORMAL.xy + worldVertex.xy ); + float px = perlinPolar( NORMAL.yz ); + float pz = perlinPolar( NORMAL.xy ); + float filter = perlin( vec2( px, pz ) * discardNoiseScale ); + + + discardValue = filter; + + vec3 worldOffset = billboardWorldOffset( UV, INV_VIEW_MATRIX, MODEL_MATRIX ); + + vec3 originalVertex = VERTEX; + + VERTEX = VERTEX + worldOffset * size + vec3( nrx, nry, nrz ) * randomPosition; + VERTEX = VERTEX + yWindAmount * vec3( windDirection.x, 0, windDirection.y ); + + vec3 vertexNormal = originalVertex; vertexNormal.y = 0.0; vertexNormal = normalize( vertexNormal ); + vec3 normal = mix( NORMAL, vertexNormal, fresnelNormalSource ); + vec3 worldNormal = normalize( MODEL_NORMAL_MATRIX * normal ); + vec3 camDir = normalize( CAMERA_DIRECTION_WORLD ); + fresnelAmount = fresnel( camDir, worldNormal, fresnelSharpness ) * fresnelScale; + + originalNormal = normalize( (vec4( localToWorldDirection( NORMAL, MODEL_MATRIX ), 1.0 ) * INV_VIEW_MATRIX ).xyz); + NORMAL = normalize( mix( NORMAL, normalBlendDirection, normalBlendAmount ) ); + + + float random = perlinPolar( vec2( worldVertex.x, worldVertex.z ) * yMapNoiseScale ); + uvRotation = rotation + rotationNoiseAmount * perlinPolar( vec2( worldVertex.x + worldVertex.y, worldVertex.z - worldVertex.y) * rotationNoiseScale ); + uvRotation = mod( uvRotation, 6.28); + float y = worldVertex.y + yMapNoiseAmount * random; + yMapValue = normalizeToRange01( y, yMapEnd, yMapStart ); + + + // rotatedUV = rotateAround_v2( UV, mod( rotation + random2 * rotationNoiseAmount, PI * 2.0 ), vec2( 0.5, 0.5 ) ); + +} + + +void fragment() +{ + if ( discardValue > discardThreshold ) + { + discard; + } + + + vec2 uv = rotateAround_v2( UV, uvRotation, vec2( 0.5, 0.5 ) ); + vec4 albedo_tex = texture( texture_albedo, uv ); + vec4 normalColor = texture( texture_normals, uv ); + vec4 yGradient = texture( texture_yMapGradient, vec2( 0, yMapValue ) ); + + ALBEDO = albedo.rgb * albedo_tex.rgb * yGradient.rgb + fresnelColor.rgb * fresnelAmount; + + NORMAL_MAP = normalColor.rgb; + + ALPHA *= albedo.a * albedo_tex.a; + ALPHA_SCISSOR_THRESHOLD = alpha_scissor_threshold; + ALPHA_ANTIALIASING_EDGE = alpha_antialiasing_edge; + ALPHA_TEXTURE_COORDINATE = UV * vec2( albedo_texture_size ); + + METALLIC = metalness; + ROUGHNESS = roughness; + SPECULAR = specular; + + AO = dot(texture( texture_ambient_occlusion, UV ), ao_texture_channel ); + AO_LIGHT_AFFECT = ao_light_affect; +} diff --git a/Runtime/Shading/Shaders/Billboards/SphericalBillboards.gdshader b/Runtime/Shading/Shaders/Billboards/SphericalBillboards.gdshader new file mode 100644 index 0000000..16ff38e --- /dev/null +++ b/Runtime/Shading/Shaders/Billboards/SphericalBillboards.gdshader @@ -0,0 +1,20 @@ +shader_type spatial; + +uniform vec4 albedo : source_color; +uniform sampler2D texture_albedo : source_color, filter_linear_mipmap, repeat_enable; + +uniform sampler2D texture_normal : filter_linear_mipmap, repeat_enable; +uniform float normal_scale = 1; +uniform float alpha_scissor_threshold : hint_range(0.0, 1.0, 0.001); +uniform float alpha_antialiasing_edge : hint_range(0.0, 1.0, 0.01); +uniform ivec2 albedo_texture_size; + +void vertex() +{ + +} + +void fragment() +{ + +} diff --git a/Runtime/Shading/Shaders/Development/Template_Spatial.gdshader b/Runtime/Shading/Shaders/Development/Template_Spatial.gdshader new file mode 100644 index 0000000..6d3a122 --- /dev/null +++ b/Runtime/Shading/Shaders/Development/Template_Spatial.gdshader @@ -0,0 +1,45 @@ +shader_type spatial; + +render_mode blend_mix, depth_draw_opaque, cull_back, diffuse_burley, specular_schlick_ggx; +render_mode blend_mix, depth_draw_opaque, cull_disabled, diffuse_burley, specular_schlick_ggx, alpha_to_coverage_and_one; + +#include "res://addons/rokojori_action_library/Runtime/Shading/Library/Math.gdshaderinc" + +uniform vec4 albedo : source_color; + +uniform sampler2D screenTexture: + hint_screen_texture, + repeat_disable, + filter_linear_mipmap; + +uniform float effectStrength: hint_range(0,1) = 1; + +void vertex() +{ + mat3 viewMatrix = VIEW_MATRIX; + + UV = UV * uv1_scale.xy + uv1_offset.xy; + + VERTEX += NORMAL * grow; +} + +void fragment() +{ + vec2 base_uv = UV; + + vec4 albedo_tex = texture(texture_albedo, base_uv); + ALBEDO = albedo.rgb * albedo_tex.rgb; + + float metallic_tex = dot(texture(texture_metallic, base_uv), metallic_texture_channel); + METALLIC = metallic_tex * metallic; + SPECULAR = specular; + + vec4 roughness_texture_channel = vec4(1.0, 0.0, 0.0, 0.0); + float roughness_tex = dot(texture(texture_roughness, base_uv), roughness_texture_channel); + ROUGHNESS = roughness_tex * roughness; + + ALPHA *= albedo.a * albedo_tex.a; + ALPHA_SCISSOR_THRESHOLD = alpha_scissor_threshold; + ALPHA_ANTIALIASING_EDGE = alpha_antialiasing_edge; + ALPHA_TEXTURE_COORDINATE = UV * vec2(albedo_texture_size); +} \ No newline at end of file diff --git a/Runtime/Shading/Shaders/Effects/FresnelOverlay/FresnelOverlay.cs b/Runtime/Shading/Shaders/Effects/FresnelOverlay/FresnelOverlay.cs new file mode 100644 index 0000000..c084906 --- /dev/null +++ b/Runtime/Shading/Shaders/Effects/FresnelOverlay/FresnelOverlay.cs @@ -0,0 +1,49 @@ +using System.Collections; +using System.Collections.Generic; +using System.Text.RegularExpressions; +using System.Text; +using Godot; + +namespace Rokojori +{ + public class FresnelOverlay + { + public static readonly CachedResource shader = new CachedResource( + "res://addons/rokojori_action_library/Runtime/Shading/Shaders/Effects/FresnelOverlay/FresnelOverlay.gdshader" + ); + + public static readonly ColorPropertyName albedo = ColorPropertyName.Create( "albedo" ); + public static readonly Texture2DPropertyName textureAlbedo = Texture2DPropertyName.Create( "texture_albedo" ); + public static readonly FloatPropertyName grow = FloatPropertyName.Create( "grow" ); + + public static readonly FloatPropertyName fresnelSharpness = FloatPropertyName.Create( "fresnelSharpness" ); + public static readonly FloatPropertyName fresnelMultiply = FloatPropertyName.Create( "fresnelMultiply" ); + public static readonly FloatPropertyName fresnelOffset = FloatPropertyName.Create( "fresnelOffset" ); + + } + + public partial class FresnelOverlayMaterial:CustomMaterial + { + public readonly CustomMaterialProperty albedo; + public readonly CustomMaterialProperty textureAlbedo; + public readonly CustomMaterialProperty grow; + + public readonly CustomMaterialProperty fresnelSharpness; + public readonly CustomMaterialProperty fresnelMultiply; + public readonly CustomMaterialProperty fresnelOffset; + + public FresnelOverlayMaterial() + { + Shader = FresnelOverlay.shader.Get(); + + albedo = new CustomMaterialProperty( this, FresnelOverlay.albedo ); + textureAlbedo = new CustomMaterialProperty( this, FresnelOverlay.textureAlbedo ); + grow = new CustomMaterialProperty( this, FresnelOverlay.grow ); + + fresnelSharpness = new CustomMaterialProperty( this, FresnelOverlay.fresnelSharpness ); + fresnelMultiply = new CustomMaterialProperty( this, FresnelOverlay.fresnelMultiply ); + fresnelOffset = new CustomMaterialProperty( this, FresnelOverlay.fresnelOffset ); + } + } + +} \ No newline at end of file diff --git a/Runtime/Shading/Shaders/Effects/FresnelOverlay/FresnelOverlay.gdshader b/Runtime/Shading/Shaders/Effects/FresnelOverlay/FresnelOverlay.gdshader new file mode 100644 index 0000000..62914f9 --- /dev/null +++ b/Runtime/Shading/Shaders/Effects/FresnelOverlay/FresnelOverlay.gdshader @@ -0,0 +1,31 @@ +// NOTE: Shader automatically converted from Godot Engine 4.3.rc.mono's StandardMaterial3D. + +shader_type spatial; +render_mode blend_mix, depth_draw_opaque, cull_back, diffuse_burley, specular_schlick_ggx, unshaded, shadows_disabled; + +#include "res://addons/rokojori_action_library/Runtime/Shading/Library/Light.gdshaderinc" +#include "res://addons/rokojori_action_library/Runtime/Shading/Library/Transform.gdshaderinc" + +uniform vec4 albedo : source_color; +uniform sampler2D texture_albedo : source_color, filter_linear_mipmap, repeat_enable; +uniform float grow : hint_range(-16.0, 16.0, 0.001); + +uniform float fresnelSharpness = 2; +uniform float fresnelMultiply = 1; +uniform float fresnelOffset = 0; + +void vertex() +{ + VERTEX += NORMAL * grow; +} + +void fragment() +{ + vec2 uv = UV; + float fresnelAmount = fresnel( NORMAL, VIEW, fresnelSharpness) * fresnelMultiply + fresnelOffset; + fresnelAmount = clamp( fresnelAmount, 0.0, 1.0 ); + vec4 albedo_tex = texture( texture_albedo, uv ); + + ALBEDO = albedo.rgb * albedo_tex.rgb; + ALPHA *= albedo.a * albedo_tex.a * fresnelAmount; +} diff --git a/Runtime/Shading/Shaders/Effects/ScanGradient/ScanGradient White.material b/Runtime/Shading/Shaders/Effects/ScanGradient/ScanGradient White.material new file mode 100644 index 0000000..74b0733 Binary files /dev/null and b/Runtime/Shading/Shaders/Effects/ScanGradient/ScanGradient White.material differ diff --git a/Runtime/Shading/Shaders/Effects/ScanGradient/ScanGradient.cs b/Runtime/Shading/Shaders/Effects/ScanGradient/ScanGradient.cs new file mode 100644 index 0000000..1cf0c6f --- /dev/null +++ b/Runtime/Shading/Shaders/Effects/ScanGradient/ScanGradient.cs @@ -0,0 +1,47 @@ +using System.Collections; +using System.Collections.Generic; +using System.Text.RegularExpressions; +using System.Text; +using Godot; + +namespace Rokojori +{ + public class ScanGradient + { + public static readonly CachedResource shader = new CachedResource( + "res://addons/rokojori_action_library/Runtime/Shading/Shaders/Effects/ScanGradient/ScanGradient.gdshader" + ); + + public static readonly ColorPropertyName albedo = ColorPropertyName.Create( "albedo" ); + public static readonly Texture2DPropertyName textureAlbedo = Texture2DPropertyName.Create( "texture_albedo" ); + public static readonly FloatPropertyName grow = FloatPropertyName.Create( "grow" ); + public static readonly FloatPropertyName offset = FloatPropertyName.Create( "offset" ); + + } + + public partial class ScanGradientMaterial:CustomMaterial + { + public static readonly CachedResource White = CustomMaterial.Cached( + "res://addons/rokojori_action_library/Runtime/Shading/Shaders/Effects/ScanGradient/ScanGradient White.material" + ); + + public readonly CustomMaterialProperty albedo; + public readonly CustomMaterialProperty textureAlbedo; + public readonly CustomMaterialProperty grow; + + public readonly CustomMaterialProperty offset; + + public ScanGradientMaterial() + { + Shader = ScanGradient.shader.Get(); + + albedo = new CustomMaterialProperty( this, ScanGradient.albedo ); + textureAlbedo = new CustomMaterialProperty( this, ScanGradient.textureAlbedo ); + grow = new CustomMaterialProperty( this, ScanGradient.grow ); + + offset = new CustomMaterialProperty( this, ScanGradient.offset ); + } + + } + +} \ No newline at end of file diff --git a/Runtime/Shading/Shaders/Effects/ScanGradient/ScanGradient.gdshader b/Runtime/Shading/Shaders/Effects/ScanGradient/ScanGradient.gdshader new file mode 100644 index 0000000..9afe4ea --- /dev/null +++ b/Runtime/Shading/Shaders/Effects/ScanGradient/ScanGradient.gdshader @@ -0,0 +1,57 @@ +// NOTE: Shader automatically converted from Godot Engine 4.3.rc.mono's StandardMaterial3D. + +shader_type spatial; +render_mode blend_add, depth_draw_opaque, cull_back, diffuse_burley, specular_schlick_ggx, unshaded, shadows_disabled; + +#include "res://addons/rokojori_action_library/Runtime/Shading/Library/Light.gdshaderinc" +#include "res://addons/rokojori_action_library/Runtime/Shading/Library/Transform.gdshaderinc" +#include "res://addons/rokojori_action_library/Runtime/Shading/Library/Math.gdshaderinc" +#include "res://addons/rokojori_action_library/Runtime/Shading/Library/Time.gdshaderinc" +#include "res://addons/rokojori_action_library/Runtime/Shading/Library/Noise.gdshaderinc" + +uniform vec4 albedo : source_color; +uniform sampler2D texture_albedo : source_color, filter_linear_mipmap, repeat_enable; +uniform float grow : hint_range(-16.0, 16.0, 0.001); + +uniform sampler2D scanTexture : source_color, filter_linear_mipmap, repeat_enable; +uniform float range; +uniform float offset; + +uniform float rangeWobble = 0.1; +uniform float rangeWobbleDuration = 1; +uniform float offsetWobble = 0.1; +uniform float offsetWobbleDuration = 1; + +uniform float noiseScale = 1; +uniform vec3 noiseOffset = vec3( 1, 1, 1 ); +uniform float noiseAmount: hint_range(0, 1) = 0.5; +uniform float rgbScale = 1; +uniform float alphaScale = 1; +varying vec3 localVertex; + + + +void vertex() +{ + VERTEX += NORMAL * grow; + + localVertex = VERTEX * ( MODEL_NORMAL_MATRIX ); +} + +void fragment() +{ + vec2 uv = UV; + float rangeWobbleValue = timeSine( TIME, rangeWobbleDuration ); + float offsetWobbleValue = timeSine( TIME, offsetWobbleDuration ); + + float animatedRange = range + rangeWobbleValue * rangeWobble; + float animatedOffset = - ( offset + offsetWobbleValue * offsetWobble ); + vec4 albedo_tex = texture( texture_albedo, uv ); + float scanUV = normalizeToRange01( localVertex.y, animatedOffset - animatedRange, animatedOffset + animatedRange ); + vec4 scanColor = texture( scanTexture, vec2( scanUV, 0 ) ); + + float noise = perlin3D( localVertex * noiseScale + noiseOffset * TIME ); + noise = mix( 1, noise, noiseAmount ); + ALBEDO = albedo.rgb * scanColor.rgb * rgbScale; + ALPHA *= albedo.a * albedo_tex.a * scanColor.a * alphaScale * noise; +} diff --git a/Runtime/Shading/Shaders/Effects/TriPlanarOverlay/AudioTest.tscn b/Runtime/Shading/Shaders/Effects/TriPlanarOverlay/AudioTest.tscn new file mode 100644 index 0000000..a250984 --- /dev/null +++ b/Runtime/Shading/Shaders/Effects/TriPlanarOverlay/AudioTest.tscn @@ -0,0 +1,16 @@ +[gd_scene load_steps=3 format=3 uid="uid://cijru7vghsaoe"] + +[ext_resource type="Script" path="res://addons/rokojori_action_library/Runtime/Audio/AudioGraph/Test/SineWaveTest.cs" id="1_tuspr"] + +[sub_resource type="AudioStreamGenerator" id="AudioStreamGenerator_cskhh"] +buffer_length = 0.1 + +[node name="Node3D" type="Node3D"] + +[node name="SineWaveTest" type="Node" parent="." node_paths=PackedStringArray("player")] +script = ExtResource("1_tuspr") +frequency = 61.445 +player = NodePath("../AudioStreamPlayer") + +[node name="AudioStreamPlayer" type="AudioStreamPlayer" parent="."] +stream = SubResource("AudioStreamGenerator_cskhh") diff --git a/Runtime/Shading/Shaders/Effects/TriPlanarOverlay/TriPlanarOverlay BlueShield.material b/Runtime/Shading/Shaders/Effects/TriPlanarOverlay/TriPlanarOverlay BlueShield.material new file mode 100644 index 0000000..95538bd Binary files /dev/null and b/Runtime/Shading/Shaders/Effects/TriPlanarOverlay/TriPlanarOverlay BlueShield.material differ diff --git a/Runtime/Shading/Shaders/Effects/TriPlanarOverlay/TriPlanarOverlay WhiteShield.material b/Runtime/Shading/Shaders/Effects/TriPlanarOverlay/TriPlanarOverlay WhiteShield.material new file mode 100644 index 0000000..99e07a9 Binary files /dev/null and b/Runtime/Shading/Shaders/Effects/TriPlanarOverlay/TriPlanarOverlay WhiteShield.material differ diff --git a/Runtime/Shading/Shaders/Effects/TriPlanarOverlay/TriPlanarOverlay.cs b/Runtime/Shading/Shaders/Effects/TriPlanarOverlay/TriPlanarOverlay.cs new file mode 100644 index 0000000..d24bd98 --- /dev/null +++ b/Runtime/Shading/Shaders/Effects/TriPlanarOverlay/TriPlanarOverlay.cs @@ -0,0 +1,50 @@ +using Godot; + +namespace Rokojori +{ + public class TriPlanarOverlay + { + public static readonly CachedResource shader = new CachedResource( + "res://addons/rokojori_action_library/Runtime/Shading/Shaders/Effects/TriPlanarOverlay/TriPlanarOverlay.gdshader" + ); + + public static readonly ColorPropertyName albedo = ColorPropertyName.Create( "albedo" ); + public static readonly FloatPropertyName uvBlendSharpness = FloatPropertyName.Create( "uv_blend_sharpness" ); + public static readonly Vector3PropertyName uvScale = Vector3PropertyName.Create( "uv_scale" ); + public static readonly Vector3PropertyName uvOffset = Vector3PropertyName.Create( "uv_offset" ); + public static readonly Vector3PropertyName uvMovement = Vector3PropertyName.Create( "uvMovement" ); + public static readonly Texture2DPropertyName textureAlbedo = Texture2DPropertyName.Create( "texture_albedo" ); + + } + + public partial class TriPlanarOverlayMaterial:CustomMaterial + { + public static readonly CachedResource BlueShield = CustomMaterial.Cached( + "res://addons/rokojori_action_library/Runtime/Shading/Shaders/Effects/TriPlanarOverlay/TriPlanarOverlay BlueShield.material" + ); + public static readonly CachedResource WhiteShield = CustomMaterial.Cached( + "res://addons/rokojori_action_library/Runtime/Shading/Shaders/Effects/TriPlanarOverlay/TriPlanarOverlay WhiteShield.material" + ); + + public readonly CustomMaterialProperty albedo; + public readonly CustomMaterialProperty uvBlendSharpness; + public readonly CustomMaterialProperty uvScale; + public readonly CustomMaterialProperty uvOffset; + public readonly CustomMaterialProperty uvMovement; + public readonly CustomMaterialProperty textureAlbedo; + + public TriPlanarOverlayMaterial() + { + Shader = TriPlanarOverlay.shader.Get(); + + albedo = new CustomMaterialProperty( this, TriPlanarOverlay.albedo ); + uvBlendSharpness = new CustomMaterialProperty( this, TriPlanarOverlay.uvBlendSharpness ); + uvScale = new CustomMaterialProperty( this, TriPlanarOverlay.uvScale ); + uvOffset = new CustomMaterialProperty( this, TriPlanarOverlay.uvOffset ); + uvMovement = new CustomMaterialProperty( this, TriPlanarOverlay.uvMovement ); + textureAlbedo = new CustomMaterialProperty( this, TriPlanarOverlay.textureAlbedo ); + } + + } + +} \ No newline at end of file diff --git a/Runtime/Shading/Shaders/Effects/TriPlanarOverlay/TriPlanarOverlay.gdshader b/Runtime/Shading/Shaders/Effects/TriPlanarOverlay/TriPlanarOverlay.gdshader new file mode 100644 index 0000000..b328f58 --- /dev/null +++ b/Runtime/Shading/Shaders/Effects/TriPlanarOverlay/TriPlanarOverlay.gdshader @@ -0,0 +1,43 @@ +// NOTE: Shader automatically converted from Godot Engine 4.3.rc.mono's StandardMaterial3D. + +shader_type spatial; +render_mode blend_mix, depth_draw_opaque, cull_back, diffuse_burley, specular_schlick_ggx, unshaded; + +#include "res://addons/rokojori_action_library/Runtime/Shading/Library/Light.gdshaderinc" +#include "res://addons/rokojori_action_library/Runtime/Shading/Library/Transform.gdshaderinc" +#include "res://addons/rokojori_action_library/Runtime/Shading/Library/Math.gdshaderinc" +#include "res://addons/rokojori_action_library/Runtime/Shading/Library/Time.gdshaderinc" +#include "res://addons/rokojori_action_library/Runtime/Shading/Library/Noise.gdshaderinc" +#include "res://addons/rokojori_action_library/Runtime/Shading/Library/Textures.gdshaderinc" + +uniform vec4 albedo : source_color; +uniform sampler2D texture_albedo : source_color, filter_linear_mipmap, repeat_enable; + +varying vec3 uv_triplanar_pos; +uniform float uv_blend_sharpness : hint_range(0.0, 150.0, 0.001); +varying vec3 uv_power_normal; + +uniform vec3 uv_scale; +uniform vec3 uv_offset; +uniform vec3 uvMovement; + +void vertex() +{ + vec3 normal = NORMAL; + + uv_power_normal = pow( abs (NORMAL ), vec3( uv_blend_sharpness ) ); + uv_power_normal /= dot( uv_power_normal, vec3( 1.0 ) ); + + vec3 scaledVertex = VERTEX * extractScale( MODEL_NORMAL_MATRIX ); + uv_triplanar_pos = scaledVertex * uv_scale + uv_offset + uvMovement * TIME; + uv_triplanar_pos *= vec3( 1.0, -1.0, 1.0 ); +} + + +void fragment() +{ + vec4 albedo_tex = triplanarTexture( texture_albedo, uv_power_normal, uv_triplanar_pos ); + + ALBEDO = albedo.rgb * albedo_tex.rgb; + ALPHA *= albedo.a * albedo_tex.a; +} diff --git a/Runtime/Shading/Shaders/Effects/TriPlanarOverlay/TrinPlanarOverlay BlueShield.material.depren b/Runtime/Shading/Shaders/Effects/TriPlanarOverlay/TrinPlanarOverlay BlueShield.material.depren new file mode 100644 index 0000000..b2a5177 Binary files /dev/null and b/Runtime/Shading/Shaders/Effects/TriPlanarOverlay/TrinPlanarOverlay BlueShield.material.depren differ diff --git a/Runtime/Shading/Shaders/PostProcessing/ChromaticAberration.material.depren b/Runtime/Shading/Shaders/PostProcessing/ChromaticAberration.material.depren new file mode 100644 index 0000000..d2eabf0 Binary files /dev/null and b/Runtime/Shading/Shaders/PostProcessing/ChromaticAberration.material.depren differ diff --git a/Runtime/Shading/Shaders/PostProcessing/ChromaticDIstortion.material b/Runtime/Shading/Shaders/PostProcessing/ChromaticDIstortion.material new file mode 100644 index 0000000..e0711d1 Binary files /dev/null and b/Runtime/Shading/Shaders/PostProcessing/ChromaticDIstortion.material differ diff --git a/Runtime/Shading/Shaders/PostProcessing/ChromaticDistortion.gdshader b/Runtime/Shading/Shaders/PostProcessing/ChromaticDistortion.gdshader new file mode 100644 index 0000000..d777972 --- /dev/null +++ b/Runtime/Shading/Shaders/PostProcessing/ChromaticDistortion.gdshader @@ -0,0 +1,40 @@ +shader_type canvas_item; + +#include "res://addons/rokojori_action_library/Runtime/Shading/Library/Colors.gdshaderinc" +#include "res://addons/rokojori_action_library/Runtime/Shading/Library/Math.gdshaderinc" + +uniform sampler2D screenTexture: hint_screen_texture, repeat_disable, filter_linear_mipmap; + +uniform sampler2D distortionTexture; +uniform float offsetR = 1; +uniform float offsetG = 1; +uniform float offsetB = 1; +uniform float maxScale: hint_range(1,2) = 1.25; +uniform float effectStrength: hint_range(0,1) = 1; +uniform sampler2D scrollingTexture; +uniform float scrollScale = 1; +uniform vec2 scrollDirection = vec2( 0.01,0.01 ); +uniform float scrollAmount = 0.1; +uniform float minScroll = -1; +uniform float maxScroll = 1; + +void fragment() +{ + vec2 scrollUV = mod( scrollScale * (UV + TIME * scrollDirection ), vec2(1,1) ) ; + float strength = texture( distortionTexture, UV ).r + + mix( minScroll, maxScroll, texture( scrollingTexture, scrollUV ).r ) * scrollAmount; + + + vec2 outerUV = ( UV - vec2(0.5,0.5) ) * maxScale + vec2( 0.5, 0.5 ); + + vec2 direction = ( UV - outerUV ) * effectStrength; + + vec4 red = texture( screenTexture, UV + strength * offsetR * direction ); + vec4 green = texture( screenTexture, UV + strength * offsetG * direction ); + vec4 blue = texture( screenTexture, UV + strength * offsetB * direction ); + + vec4 mixed = vec4( red.r, green.g, blue.b, 1 ); + + + COLOR = mixed; +} \ No newline at end of file diff --git a/Runtime/Shading/Shaders/PostProcessing/ColorCurves.gdshader b/Runtime/Shading/Shaders/PostProcessing/ColorCurves.gdshader index 4e6b5e1..6b096a4 100644 --- a/Runtime/Shading/Shaders/PostProcessing/ColorCurves.gdshader +++ b/Runtime/Shading/Shaders/PostProcessing/ColorCurves.gdshader @@ -39,16 +39,16 @@ uniform float lumaSaturationEffectStrength: hint_range(0,1) = 1; uniform float effectStrength : hint_range(0,1) = 1; void fragment() -{ +{ vec4 rgb = texture( screenTexture, UV ); vec4 originalRGB = rgb; rgb.r = mix( rgb.r, texture( rChannelCurve, vec2( rgb.r, 0.0 ) ).x, rChannelEffectStrength ); rgb.g = mix( rgb.g, texture( gChannelCurve, vec2( rgb.g, 0.0 ) ).x, gChannelEffectStrength ); rgb.b = mix( rgb.b, texture( bChannelCurve, vec2( rgb.b, 0.0 ) ).x, bChannelEffectStrength ); - + rgb = clamp01_v4( rgb ); - + vec3 hsl = RGBtoHSL( rgb.rgb ); hsl.r = mix( hsl.r, hsl.r + texture( hChannelCurve, vec2( hsl.r, 0.0 ) ).x - 0.5, hChannelEffectStrength ); diff --git a/Runtime/Shading/Shaders/Shaders.cs b/Runtime/Shading/Shaders/Shaders.cs new file mode 100644 index 0000000..d6de596 --- /dev/null +++ b/Runtime/Shading/Shaders/Shaders.cs @@ -0,0 +1,55 @@ +using Godot; +using System.Reflection; +using System.Collections.Generic; + +namespace Rokojori +{ + + public class Shaders + { + public static List GetUniformMembers( Shader shader ) + { + var list = new List(); + + var uniforms = shader.GetShaderUniformList(); + + foreach ( var u in uniforms ) + { + var d = u.AsGodotDictionary(); + + + var member = new UniformMember(); + + member.name = d[ "name" ].AsString(); + + member.type = (Variant.Type) d[ "type" ].AsInt32(); + + member.hint = (PropertyHint) d[ "hint" ].AsInt32(); + member.hintString = d[ "hint_string" ].AsString(); + member.usage = d[ "usage" ].AsInt32(); + + + list.Add( member ); + } + + return list; + } + + public static List GetUniformNames( Shader shader ) + { + var list = new List(); + + var uniforms = shader.GetShaderUniformList(); + + foreach ( var p in uniforms ) + { + var d = p.AsGodotDictionary(); + var name = d[ "name" ].ToString(); + + list.Add( name ); + } + + return list; + } + } +} \ No newline at end of file diff --git a/Runtime/Shading/Shaders/UniformMember.cs b/Runtime/Shading/Shaders/UniformMember.cs new file mode 100644 index 0000000..6f7b6b1 --- /dev/null +++ b/Runtime/Shading/Shaders/UniformMember.cs @@ -0,0 +1,34 @@ +using Godot; +using System.Reflection; +using System.Collections.Generic; + +namespace Rokojori +{ + public class UniformMember + { + public string name; + public Variant.Type type; + + public PropertyHint hint; + public string hintString; + public int usage; + + public override string ToString() + { + return type + " " + name + "( '" + hint + "', '" + hintString + "', '" + usage + "')"; + } + + public string GetPropertyNameType() + { + if ( type == Variant.Type.Object ) + { + return ( hintString + "" ); + } + else + { + return ( type + "" ); + } + } + + } +} \ No newline at end of file diff --git a/Runtime/Shading/Tools/CSShaderClassGenerator/CSShaderClassGenerator.cs b/Runtime/Shading/Tools/CSShaderClassGenerator/CSShaderClassGenerator.cs new file mode 100644 index 0000000..a2ec491 --- /dev/null +++ b/Runtime/Shading/Tools/CSShaderClassGenerator/CSShaderClassGenerator.cs @@ -0,0 +1,149 @@ +using Godot; +using System.Reflection; +using System.Collections.Generic; + +namespace Rokojori +{ + [Tool] + [GlobalClass] + public partial class CSShaderClassGenerator:Node + { + [Export] + public string resourcePath; + + [Export] + public bool generate; + + public override void _Process( double delta ) + { + if ( generate ) + { + generate = false; + Generate(); + } + } + + public static readonly string ShaderName = "${ShaderName}"; + public static readonly string ShaderResourcePath = "${ShaderResourcePath}"; + public static readonly string ShaderStaticPropertyNames = "${ShaderStaticPropertyNames}"; + public static readonly string ShaderInstancePropertiesDeclarations = "${ShaderInstancePropertiesDeclarations}"; + public static readonly string ShaderInstancePropertiesInitializers = "${ShaderInstancePropertiesInitializers}"; + public static readonly string MaterialPresets = "${MaterialPresets}"; + + public void Generate() + { + var shader = ResourceLoader.Load( resourcePath ); + var resourceParentPath = RegexUtility.ParentPath( resourcePath ); + var absoluteFilePath = FilePath.Absolute( ProjectSettings.GlobalizePath( resourcePath ) ); + var absoluteParentPath = absoluteFilePath.CreateAbsoluteParent(); + + RJLog.Log( resourceParentPath ); + + var shaderName = absoluteFilePath.fileName; + + var members = Shaders.GetUniformMembers( shader ); + + var templatePath = ProjectSettings.GlobalizePath( "res://addons/rokojori_action_library/Runtime/Shading/Tools/CSShaderClassGenerator/CSShaderClassTemplate.txt" ); + + var template = FilesSync.LoadUTF8( templatePath ); + + + + var staticProps = new List(); + var instanceProps = new List(); + var instanceInitializers = new List(); + + members.ForEach( + ( m )=> + { + var type = m.GetPropertyNameType(); + var name = m.name; + var csName = name.ToCamelCase(); + + var declaration = "public static readonly ${type}PropertyName ${csName} = ${type}PropertyName.Create( \"${name}\" );"; + var instance = "public readonly CustomMaterialProperty<${csType}> ${csName};"; + var init = "${csName} = new CustomMaterialProperty<${csType}>( this, ${ShaderName}.${csName} );"; + + var csType = type; + + if ( csType == "Float" ) + { + csType = "float"; + } + else if ( csType == "Int" ) + { + csType = "float"; + } + + var map = new StringMap(); + map[ "${type}" ] = type; + map[ "${csType}"] = csType; + map[ "${name}" ] = name; + map[ "${csName}" ] = csName; + map[ "${ShaderName}"] = shaderName; + + staticProps.Add( map.ReplaceAll( declaration ) ); + instanceProps.Add( map.ReplaceAll( instance ) ); + instanceInitializers.Add( map.ReplaceAll( init ) ); + } + ); + + var materials = FilesSync.GetFiles( absoluteParentPath.fullPath, + ( f ) => + { + return f.hasFileExtension( "material" ); + } + ); + + var presets = new List(); + + materials.ForEach( m => + { + var materialName = m.fileName; + var shortName = materialName; + + if ( materialName.ToLower().StartsWith( shaderName.ToLower() ) ) + { + shortName = shortName.Substring( shaderName.Length ); + } + + var declaration = + "public static readonly CachedResource<${ShaderName}Material> ${csName} = CustomMaterial.Cached<${ShaderName}Material>(\n" + + " \"${materialPath}\"\n" + + " );"; + + + var mMap = new StringMap(); + mMap[ "${materialPath}" ] = resourceParentPath + "/" + materialName + ".material"; + mMap[ "${ShaderName}" ] = shaderName; + mMap[ "${csName}" ] = RegexUtility.ToValidCSName( shortName ).ToPascalCase(); + + presets.Add( mMap.ReplaceAll( declaration ) ); + } + ); + + + var variables = new StringMap(); + variables[ CSShaderClassGenerator.ShaderName ] = shaderName; + variables[ CSShaderClassGenerator.ShaderResourcePath ] = resourcePath; + + variables[ CSShaderClassGenerator.ShaderStaticPropertyNames ] = Lists.Join( staticProps, "\n " ); + variables[ CSShaderClassGenerator.ShaderInstancePropertiesDeclarations ] = Lists.Join( instanceProps, "\n " ); + variables[ CSShaderClassGenerator.ShaderInstancePropertiesInitializers ] = Lists.Join( instanceInitializers, "\n " ); + + variables[ CSShaderClassGenerator.MaterialPresets ] = Lists.Join( presets, "\n " ); + + var shaderClass = variables.ReplaceAll( template ); + + var outputPath = absoluteFilePath.WithExtension( "cs" ); + + + + FilesSync.SaveUTF8( outputPath.fullPath, shaderClass ); + + this.LogInfo( "\nGenerated >> ", outputPath.fullPath, "\n" + shaderClass ); + + + } + } +} \ No newline at end of file diff --git a/Runtime/Shading/Tools/CSShaderClassGenerator/CSShaderClassTemplate.txt b/Runtime/Shading/Tools/CSShaderClassGenerator/CSShaderClassTemplate.txt new file mode 100644 index 0000000..a26b927 --- /dev/null +++ b/Runtime/Shading/Tools/CSShaderClassGenerator/CSShaderClassTemplate.txt @@ -0,0 +1,30 @@ +using Godot; + +namespace Rokojori +{ + public class ${ShaderName} + { + public static readonly CachedResource shader = new CachedResource( + "${ShaderResourcePath}" + ); + + ${ShaderStaticPropertyNames} + + } + + public partial class ${ShaderName}Material:CustomMaterial + { + ${MaterialPresets} + + ${ShaderInstancePropertiesDeclarations} + + public ${ShaderName}Material() + { + Shader = ${ShaderName}.shader.Get(); + + ${ShaderInstancePropertiesInitializers} + } + + } + +} \ No newline at end of file diff --git a/Runtime/Structures/Map.cs b/Runtime/Structures/Map.cs new file mode 100644 index 0000000..680b184 --- /dev/null +++ b/Runtime/Structures/Map.cs @@ -0,0 +1,29 @@ + +using System.Diagnostics; +using System.Collections; +using System.Collections.Generic; +using System; +using Godot; + + +namespace Rokojori +{ + public class Map : Dictionary + { + public void ForEach( Action callback ) + { + foreach ( var kv in this ) + { + callback( kv.Key, kv.Value ); + } + } + } + + public class StringMap : Map + { + public string ReplaceAll( string source ) + { + return RegexUtility.ReplaceMultiple( source, this ); + } + } +} \ No newline at end of file diff --git a/Runtime/Structures/DictionaryList.cs b/Runtime/Structures/MapList.cs similarity index 74% rename from Runtime/Structures/DictionaryList.cs rename to Runtime/Structures/MapList.cs index b373ea2..5f36989 100644 --- a/Runtime/Structures/DictionaryList.cs +++ b/Runtime/Structures/MapList.cs @@ -8,8 +8,16 @@ using Godot; namespace Rokojori { - public class DictionaryList:Dictionary> + public class MapList:Map> { + public void ForEach( Action> callback ) + { + foreach ( var vk in this ) + { + callback( vk.Key, vk.Value ); + } + } + public void Add( K key, V value ) { if ( ! ContainsKey( key ) ) diff --git a/Runtime/Structures/MultiMap.cs b/Runtime/Structures/MultiMap.cs new file mode 100644 index 0000000..8b97e06 --- /dev/null +++ b/Runtime/Structures/MultiMap.cs @@ -0,0 +1,56 @@ + +using System.Diagnostics; +using System.Collections; +using System.Collections.Generic; +using System; +using Godot; + + +namespace Rokojori +{ + public class MultiMap:Map> + { + + public bool Has( K1 key1, K2 key2 ) + { + if ( ! ContainsKey( key1 ) ) + { + return false; + } + + return this[ key1 ].ContainsKey( key2 ); + } + + public void Set( K1 key, K2 key2, V value ) + { + if ( ! ContainsKey( key ) ) + { + this[ key ] = new Map(); + } + + this[ key ][ key2 ]= value; + } + + public void Remove( K1 key, K2 key2, V value ) + { + if ( ! ContainsKey( key ) ) + { + return; + } + + var dictionary = this[ key ]; + + if ( ! dictionary.ContainsKey( key2 ) ) + { + return; + } + + dictionary.Remove( key2 ); + + if ( dictionary.Count == 0 ) + { + Remove( key ); + } + } + } +} \ No newline at end of file diff --git a/Runtime/Structures/Spatial/Grid2D.cs b/Runtime/Structures/Spatial/Grid2D.cs new file mode 100644 index 0000000..bd2aac8 --- /dev/null +++ b/Runtime/Structures/Spatial/Grid2D.cs @@ -0,0 +1,160 @@ + +using System.Diagnostics; +using System.Collections; +using System.Collections.Generic; +using System; +using Godot; + + +namespace Rokojori +{ + public class Grid2D + { + float _gridX; + float _gridY; + MapList _cells = new MapList(); + Dictionary _objectsInCell = new Dictionary(); + + Func _getPosition; + + public Grid2D( float sizeX, float sizeY, Func getPosition ) + { + _gridX = sizeX; + _gridY = sizeY; + _getPosition = getPosition; + } + + public static Grid2D XZfromNode3D( float x, float z ) where N:MeshInstance3D + { + return new Grid2D( x, z, t => Math2D.XZ( t.GlobalPosition ) ); + } + + public float WorldToGridX( float x ) + { + return x / _gridX; + } + + public float WorldToGridY( float y ) + { + return y / _gridY; + } + + public int WorldToGridIDX( float x ) + { + return Mathf.FloorToInt( WorldToGridX( x ) ); + } + + public int WorldToGridIDY( float y ) + { + return Mathf.FloorToInt( WorldToGridY( y ) ); + } + + public Vector2I WorldToGridID( Vector2 worldPosition ) + { + var idX = WorldToGridIDX( worldPosition.X ); + var idY = WorldToGridIDY( worldPosition.Y ); + return new Vector2I( idX, idY ); + } + + public Vector2 GridIDtoWorld( Vector2I id ) + { + return new Vector2( id.X * _gridX, id.Y * _gridY ); + } + + public bool Has( T obj ) + { + return _objectsInCell.ContainsKey( obj ); + } + + public Vector2I? GetRegistratedCell( T t ) + { + if ( ! _objectsInCell.ContainsKey( t ) ) + { + return null; + } + + return _objectsInCell[ t ]; + } + + public void Remove( T obj ) + { + if ( ! Has( obj ) ) + { + return; + } + + var cell = _objectsInCell[ obj ]; + _objectsInCell.Remove( obj ); + _cells.Remove( cell, obj ); + + } + + public void AddAll( List objects ) + { + objects.ForEach( o => Add( o ) ); + } + + public void Add( T obj ) + { + Remove( obj ); + + var id = WorldToGridID( _getPosition( obj ) ); + + _objectsInCell[ obj ] = id; + _cells.Add( id, obj ); + } + + public List GetAllAtID( Vector2I id ) + { + if ( ! _cells.ContainsKey( id ) ) + { + return _cells[ id ]; + } + + return new List(); + } + + public List GetAtWorldPosition( Vector2 worldPosition ) + { + return GetAllAtID( WorldToGridID( worldPosition ) ); + } + + public void ForEachCell( Action> action ) + { + foreach ( var id_objects in _cells ) + { + action( id_objects.Key, id_objects.Value ); + } + } + + public List GetInBox( Box2 box, List outputList = null ) + { + var start = box.min; + var end = box.max; + + var minID = WorldToGridID( start ); + var maxID = WorldToGridID( end ) + Vector2I.One; + + outputList = outputList == null ? new List() : outputList; + + for ( int i = minID.X; i < maxID.X; i++ ) + { + for ( int j = minID.Y; j < maxID.Y; j++ ) + { + var id = new Vector2I( i, j ); + + if ( ! _cells.ContainsKey( id ) ) + { + continue; + } + + outputList.AddRange( _cells[ id ] ); + } + } + + return outputList; + + } + + } +} \ No newline at end of file diff --git a/Runtime/Text/RegexUtility.cs b/Runtime/Text/RegexUtility.cs index 4498985..d31ad45 100644 --- a/Runtime/Text/RegexUtility.cs +++ b/Runtime/Text/RegexUtility.cs @@ -2,6 +2,7 @@ using System.Collections; using System.Collections.Generic; using System.Text.RegularExpressions; using System.Text; +using System; using System.Globalization; using Godot; @@ -25,6 +26,23 @@ namespace Rokojori } return source; + } + + public static bool IsUpperCase( string value, int position ) + { + if ( value == null || value.Length == 0 || value.Length <= position ) + { + return false; + } + + var ch = ( value[ position ] + "" ).ToUpper(); + + return ch == ( value[ position ] + "" ); + } + + public static bool StartsWithUpperCase( string value ) + { + return IsUpperCase( value, 0 ); } public static string NumberToString( double value ) @@ -423,20 +441,85 @@ namespace Rokojori return replaced; } - public static string ParentPathOrLastFragment( string path ) + public static string TrimToLastPathFragment( string path ) { var splits = SplitPaths( path ); return splits[ splits.Count - 1 ]; } + public static bool StartsWithPathProtocol( string path ) + { + return Matching( path, @"^\w+://" ); + } + + public static string ExtractPathProtocol( string path ) + { + if ( ! StartsWithPathProtocol( path) ) + { + return ""; + } + + var splitPosition = path.IndexOf( "://" ); + + return path.Substring( 0, splitPosition + 3 ) ; + } + + public static string ParentPath( string path ) + { + var protocol = ExtractPathProtocol( path ); + + if ( protocol.Length != 0 ) + { + path = path.Substring( protocol.Length ); + } + + var list = SplitPaths( path ); + + list.RemoveAt( list.Count -1 ); + + return protocol + Lists.Join( list, "/" ); + } + + public static List SplitPaths( string path ) { var splittedPaths = new List(); var normalizedPath = NormalizePath( path ); var splits = Split( normalizedPath, new Regex( @"\/" ) ); return splits; - } + } + + + public static string ToValidCSName( string source) + { + var output = new StringBuilder(); + + for ( int i = 0; i < source.Length; i++) + { + var s = source[ i ]; + var isDigit = Char.IsDigit( s ); + var isLetter = Char.IsLetter( s ); + var isAscii = Char.IsAscii( s ); + + var isDigitAsFirst = i == 0 && isDigit; + var isAllowed = ( isDigit || isLetter ) && isAscii; + + if ( isDigitAsFirst || ! isAllowed ) + { + output.Append( "_" ); + } + else + { + output.Append( source[ i ] ); + } + } + + source = output.ToString(); + + return source.ToCamelCase(); + + } public static string JoinPaths( List paths, int startIndex = 0, int length = -1 ) { diff --git a/Runtime/Time/ModulateTimeLineSpeed.cs b/Runtime/Time/ModulateTimeLineSpeed.cs new file mode 100644 index 0000000..9e1e279 --- /dev/null +++ b/Runtime/Time/ModulateTimeLineSpeed.cs @@ -0,0 +1,33 @@ + +using Godot; + + +namespace Rokojori +{ + [GlobalClass ] + public partial class ModulateTimeLineSpeed : RJSequenceAction + { + [Export] + public RJTimeLine timeline; + + [Export] + public AnimationCurve curve; + + + public override void _OnTrigger() + { + var tm = Unique.Get(); + + if ( tm == null || timeline == null || curve == null ) + { + return; + } + + var id = DispatchStart(); + + tm.Modulate( timeline, curve, r => { DispatchEnd( id ); }); + + } + + } +} \ No newline at end of file diff --git a/Runtime/Time/SetTimeLineSpeed.cs b/Runtime/Time/SetTimeLineSpeed.cs new file mode 100644 index 0000000..5bd7601 --- /dev/null +++ b/Runtime/Time/SetTimeLineSpeed.cs @@ -0,0 +1,23 @@ + +using Godot; + + +namespace Rokojori +{ + [GlobalClass ] + public partial class SetTimeLineSpeed : RJAction + { + [Export] + public RJTimeLine timeline; + + [Export] + public float speed = 1; + + public override void _OnTrigger() + { + var tm = Unique.Get(); + tm.SetSpeed( timeline, speed ); + } + + } +} \ No newline at end of file diff --git a/Runtime/Time/TimeLineEvent.cs b/Runtime/Time/TimeLineEvent.cs index e9684d0..14c5989 100644 --- a/Runtime/Time/TimeLineEvent.cs +++ b/Runtime/Time/TimeLineEvent.cs @@ -13,5 +13,6 @@ namespace Rokojori public long id; public bool persistent; public double position; + public bool wasInside = false; } } \ No newline at end of file diff --git a/Runtime/Time/TimeLineManager.cs b/Runtime/Time/TimeLineManager.cs index f9962bd..94ca1fd 100644 --- a/Runtime/Time/TimeLineManager.cs +++ b/Runtime/Time/TimeLineManager.cs @@ -15,6 +15,16 @@ namespace Rokojori [Export] public RJTimeLine[] timeLines; + [Export] + public RJTimeLine engineTimeScale; + + [Export] + public RJTimeLine realTimeTimeLine; + + [Export] + public bool computeRealtimeWithEngineDelta; + + List _runners = new List(); int _idCounter = 0; @@ -25,6 +35,65 @@ namespace Rokojori Initialize(); } + float _lastUpdate = 0; + DateTime lastUpdated = DateTime.Now; + float _estimatedDelta = 0; + float _estimatedDeltaFilter = 0.9f; + float _unscaledDelta = 0; + double _lastRealtimePosition = 0; + double _realtimePosition = 0; + + float _lastTimeScale = 1; + + public override void _Process( double delta ) + { + UpdateRealTime( delta ); + + if ( engineTimeScale != null ) + { + Engine.TimeScale = GetModulatedSpeed( engineTimeScale ); + } + + _runners.ForEach( r => r.UpdateTimeLine( unscaledTimeDelta, this ) ); + + } + + public void Modulate( RJTimeLine timeline, AnimationCurve c, Action onReady ) + { + var runner = GetRunner( GetTimeLineIndex( timeline ) ); + runner.Modulate( c, onReady ); + } + + void UpdateRealTime( double engineDelta ) + { + + var now = DateTime.Now; + var unscaled = (float) ( ( now - lastUpdated ).TotalSeconds ); + lastUpdated = now; + + if ( unscaled < 1f/20f ) + { + _estimatedDelta += _estimatedDeltaFilter * ( unscaled - _estimatedDelta ); + } + + if ( computeRealtimeWithEngineDelta ) + { + _unscaledDelta = (float)( Engine.TimeScale == 0 ? _estimatedDelta : ( engineDelta / Engine.TimeScale ) ); + } + else + { + _unscaledDelta = _estimatedDelta; + } + + + _lastRealtimePosition = _realtimePosition; + _realtimePosition += _unscaledDelta; + } + + public float unscaledTimeDelta => _unscaledDelta; + public double realtime => _realtimePosition; + + void Initialize() { if ( _initialized ) @@ -84,10 +153,7 @@ namespace Rokojori } - public override void _Process( double delta ) - { - _runners.ForEach( r => r.UpdateTimeLine( delta, this ) ); - } + public override void ScheduleEvent( int timeLineIndex, double position, int callbackID, bool isPersistent ) { @@ -122,16 +188,34 @@ namespace Rokojori public override double GetSpeed( int timeLineIndex ) { - var runner = GetRunner( timeLineIndex ); if ( runner == null ){ return 0; } + var runner = GetRunner( timeLineIndex ); if ( runner == null ){ return 1; } return runner.speed; } + public float GetModulatedSpeed( RJTimeLine timeline ) + { + var timeLineIndex = GetTimeLineIndex( timeline ); + var runner = GetRunner( timeLineIndex ); if ( runner == null ){ return 1; } + return runner.modulatedSpeed; + } + + public float GetSpeed( RJTimeLine timeline ) + { + return (float) GetSpeed( GetTimeLineIndex( timeline ) ); + } + public override void SetSpeed( int timeLineIndex, double speed ) { var runner = GetRunner( timeLineIndex ); if ( runner == null ){ return; } runner.speed = speed; } + public void SetSpeed( RJTimeLine timeline, double speed ) + { + SetSpeed( GetTimeLineIndex( timeline ), speed ); + } + + public override bool GetPlayState( int timeLineIndex ) { diff --git a/Runtime/Time/TimeLineRunner.cs b/Runtime/Time/TimeLineRunner.cs index ccdaa6e..f4619ef 100644 --- a/Runtime/Time/TimeLineRunner.cs +++ b/Runtime/Time/TimeLineRunner.cs @@ -21,6 +21,14 @@ namespace Rokojori List events = new List(); List spans = new List(); + AnimationCurve modulatorCurve; + float modulatorTime; + Vector3 modulatorRandomization; + Action modulatorOnReady = null; + float _lastModulation = 1; + + public float modulatedSpeed => (float)( speed * _lastModulation * deltaScale ); + public TimeLineRunner( RJTimeLine timeLine ) { this.timeLine = timeLine; @@ -28,15 +36,57 @@ namespace Rokojori playing = timeLine.AutoStart; } - public void UpdateTimeLine( double delta, TimeLineManager manager ) + public void Modulate( AnimationCurve curve, Action onReady ) + { + if ( modulatorOnReady != null ) + { + modulatorOnReady( false ); + } + + modulatorCurve = curve; + modulatorTime = 0; + modulatorRandomization = curve.Randomize(); + modulatorOnReady = onReady; + } + + public void UpdateTimeLine( float realtimeDelta, TimeLineManager manager ) { if ( ! playing ) { return; } + var modulation = 1f; + + if ( modulatorCurve != null ) + { + modulation = modulatorCurve.Sample( modulatorTime, modulatorRandomization ); + + _lastModulation = modulation; + modulatorTime += realtimeDelta; + + if ( modulatorTime >= modulatorCurve.GetRandomizedEndTime( modulatorRandomization ) ) + { + var mr = modulatorOnReady; + modulatorOnReady = null; + modulatorCurve = null; + modulatorTime = 0; + modulatorRandomization = Vector3.Zero; + + if ( mr != null ) + { + mr( true ); + } + + } + } + else + { + _lastModulation = 1; + } + lastPosition = position; - position += delta * deltaScale * speed; + position += realtimeDelta * deltaScale * speed * modulation; var isForward = speed >= 0; @@ -47,18 +97,46 @@ namespace Rokojori } + List AddRemoval( bool isPersistent, int i, List list ) + { + if ( isPersistent ) + { + return list; + } + + if ( list == null ) + { + list = new List(); + } + + list.Add( i ); + + return list; + } + + void ProcessForward( TimeLineManager manager ) { List eventRemovals = null; List spanRemovals = null; + var scheduler = Unique.Get(); + for ( int i = 0; i < events.Count; i++ ) { var eventPosition = events[ i ].position; + // 0 1 2 3 4 5 + // Last 3, Position 4 + // if ( ! RangeDouble.ContainsExclusiveMax( lastPosition, position, eventPosition ) ) { + if ( events[ i ].wasInside ) + { + eventRemovals = AddRemoval( events[ i ].persistent, i, eventRemovals ); + } + continue; } @@ -69,21 +147,14 @@ namespace Rokojori ); manager.EmitSignal( TimeLineManager.SignalName.OnEvent, events[ i ].id ); - - if ( events[ i ].persistent ) - { - continue; - } - - if ( eventRemovals == null ){ eventRemovals = new List(); } - - eventRemovals.Add( i ); + events[ i ].wasInside = true; } if ( eventRemovals != null ) { - Lists.RemoveIncreasingIndices( events, eventRemovals ); + eventRemovals.ForEach( ev => scheduler._RemoveEventEntry( ev ) ); + Lists.RemoveIncreasingSortedIndices( events, eventRemovals ); } if ( spans.Count == 0 ) @@ -93,14 +164,24 @@ namespace Rokojori var timelineSpan = new RangeDouble( lastPosition, position ); var span = new RangeDouble( 0, 1 ); + var isForward = lastPosition < position; for ( int i = 0; i < spans.Count; i++ ) { span.min = spans[ i ].start; span.max = spans[ i ].end; - if ( ! timelineSpan.Overlaps( span ) ) + var overlaps = timelineSpan.Overlaps( span ); + + // RJLog.Log( "Span", i, overlaps, spans[ i ].id, ">>", spans[ i ].start, spans[ i ].end, "||", lastPosition, position ); + + if ( ! overlaps ) { + if ( spans[ i ].wasInside ) + { + spanRemovals = AddRemoval( spans[ i ].persistent, i, spanRemovals ); + } + continue; } @@ -122,18 +203,17 @@ namespace Rokojori { spanType = TimeLineSpan.End; } + + spans[ i ].wasInside = true; - manager.EmitSignal( TimeLineManager.SignalName.OnSpan, spans[ i ].id, spanType ); + manager.EmitSignal( TimeLineManager.SignalName.OnSpan, spans[ i ].id, spanType ); - if ( spans[ i ].persistent ) - { - continue; - } - - if ( spanRemovals == null ){ spanRemovals = new List(); } - - spanRemovals.Add( i ); + } + if ( spanRemovals != null ) + { + spanRemovals.ForEach( ev => scheduler._RemoveSpanEntry( ev ) ); + Lists.RemoveIncreasingSortedIndices( spans, spanRemovals ); } } diff --git a/Runtime/Time/TimeLineScheduler.cs b/Runtime/Time/TimeLineScheduler.cs index 945c38e..ad40fb3 100644 --- a/Runtime/Time/TimeLineScheduler.cs +++ b/Runtime/Time/TimeLineScheduler.cs @@ -80,8 +80,9 @@ namespace Rokojori Dictionary> _eventActions = new Dictionary>(); Dictionary> _spanActions = new Dictionary>(); + void AttachListeners() - { + { if ( _listenersAttached ) { return; @@ -93,8 +94,22 @@ namespace Rokojori tm.OnEvent += ( id ) => OnEvent( id ); tm.OnSpan += ( id, type ) => onSpan( id, type ); + + } + public void _RemoveEventEntry( int id ) + { + _eventActions.Remove( id ); + } + + public void _RemoveSpanEntry( int id ) + { + _spanActions.Remove( id ); + } + + + void OnEvent( long s_id ) { var id = (int) s_id; @@ -106,12 +121,6 @@ namespace Rokojori _eventActions[ id ]( id ); - if ( _persistentEventsAndSpans.Contains( id ) ) - { - return; - } - - _eventActions.Remove( id ); } @@ -127,12 +136,6 @@ namespace Rokojori _spanActions[ id ]( id, type ); - if ( _persistentEventsAndSpans.Contains( id ) ) - { - return; - } - - _spanActions.Remove( id ); } } } \ No newline at end of file diff --git a/Runtime/Time/TimeLineSpan.cs b/Runtime/Time/TimeLineSpan.cs index 9157629..12e6c7b 100644 --- a/Runtime/Time/TimeLineSpan.cs +++ b/Runtime/Time/TimeLineSpan.cs @@ -19,6 +19,6 @@ namespace Rokojori public bool persistent; public double start; public double end; - public bool wasInside; + public bool wasInside = false; } } \ No newline at end of file diff --git a/Runtime/Time/TimeLines/GameTime.tres b/Runtime/Time/TimeLines/GameTime.tres new file mode 100644 index 0000000..7760bf8 --- /dev/null +++ b/Runtime/Time/TimeLines/GameTime.tres @@ -0,0 +1,4 @@ +[gd_resource type="RJTimeLine" format=3 uid="uid://frruktikky46"] + +[resource] +autoStart = true diff --git a/Runtime/Time/TimeLines/RealTime.tres b/Runtime/Time/TimeLines/RealTime.tres new file mode 100644 index 0000000..d237897 --- /dev/null +++ b/Runtime/Time/TimeLines/RealTime.tres @@ -0,0 +1,4 @@ +[gd_resource type="RJTimeLine" format=3 uid="uid://dmsepypwb6xek"] + +[resource] +autoStart = true diff --git a/Runtime/Tools/Arrays.cs b/Runtime/Tools/Arrays.cs index d4764eb..4064271 100644 --- a/Runtime/Tools/Arrays.cs +++ b/Runtime/Tools/Arrays.cs @@ -7,6 +7,16 @@ namespace Rokojori { public class Arrays { + public static T[] Concat( T[] a, T[] b ) + { + var o = new T[ a.Length + b.Length ]; + + a.CopyTo( o, 0 ); + b.CopyTo( o, a.Length ); + + return o; + } + public static int IndexOf( T[] values, T other ) { return Array.IndexOf( values, other ); diff --git a/Runtime/Tools/Lists.cs b/Runtime/Tools/Lists.cs index 25989ef..70a820e 100644 --- a/Runtime/Tools/Lists.cs +++ b/Runtime/Tools/Lists.cs @@ -212,9 +212,9 @@ namespace Rokojori return list.Count == 0 ? default(T) : list[ list.Count - 1 ]; } - public static void RemoveIncreasingIndices( List list, List removals ) + public static void RemoveIncreasingSortedIndices( List list, List increasinglySortedRemovals ) { - for ( var i = removals.Count - 1; i >= 0; i-- ) + for ( var i = increasinglySortedRemovals.Count - 1; i >= 0; i-- ) { list.RemoveAt( i ); } @@ -451,6 +451,7 @@ namespace Rokojori return default( T ); } + public static List Map( List inputList, Func mapper, List list = null ) { if ( list == null ) diff --git a/Runtime/Tools/Safe.cs b/Runtime/Tools/Safe.cs index 63a586b..1d2080f 100644 --- a/Runtime/Tools/Safe.cs +++ b/Runtime/Tools/Safe.cs @@ -10,7 +10,7 @@ namespace Rokojori { static readonly int maxWhileIterations = 1000 * 1000; - public static void While( Func condition, Action action, Action onTooManyIterations = null ) + public static void While( Func condition, System.Action action, System.Action onTooManyIterations = null ) { var it = 0; diff --git a/Runtime/Tools/Trillean.cs b/Runtime/Tools/Trillean.cs index 3288905..75cefa2 100644 --- a/Runtime/Tools/Trillean.cs +++ b/Runtime/Tools/Trillean.cs @@ -15,6 +15,16 @@ namespace Rokojori public static class TrilleanLogic { + public static bool ToBool( Trillean value, bool any = true ) + { + if ( Trillean.Any == value ) + { + return any; + } + + return Trillean.True == value; + } + public static bool Matches( Trillean value, bool state, bool anyValue = true ) { if ( Trillean.Any == value ) diff --git a/Runtime/UI/MouseModeTool.cs b/Runtime/UI/MouseModeTool.cs new file mode 100644 index 0000000..aa1527a --- /dev/null +++ b/Runtime/UI/MouseModeTool.cs @@ -0,0 +1,34 @@ + +using Godot; +using System; + +namespace Rokojori +{ + [GlobalClass] + public partial class MouseModeTool : Node + { + public enum EditMode + { + Read_Mouse_Mode, + Write_Mouse_Mode + } + + [Export] + public EditMode editMode = EditMode.Read_Mouse_Mode; + + [Export] + public Input.MouseModeEnum mouseMode = Input.MouseModeEnum.Visible; + + public override void _Process( double delta ) + { + if ( EditMode.Read_Mouse_Mode == editMode ) + { + mouseMode = Input.MouseMode; + } + else + { + Input.MouseMode = mouseMode; + } + } + } +} \ No newline at end of file diff --git a/Runtime/VirtualCameras/Effects/CameraEffect.cs b/Runtime/VirtualCameras/Effects/CameraEffect.cs new file mode 100644 index 0000000..6b1df36 --- /dev/null +++ b/Runtime/VirtualCameras/Effects/CameraEffect.cs @@ -0,0 +1,122 @@ + +using System.Diagnostics; +using System.Collections; +using System.Collections.Generic; +using System; +using Godot; + + +namespace Rokojori +{ + [Tool] + [GlobalClass] + public partial class CameraEffect:Resource + { + [Export] + public CameraEffectTargetAnimationCurve[] animations; + + [Export] + public RJTimeLine timeline; + + public float maxDuration + { + get + { + var max = 0f; + + for ( int i = 0; i < animations.Length; i++ ) + { + max = Mathf.Max( animations[ i ].duration, max ); + } + + return max; + } + + } + } + + public class CameraEffectRunner + { + public CameraEffect effect; + public Vector3 position; + public Quaternion rotation; + public float fov; + public float timePosition = 0; + + public bool isFinished => timePosition >= effect.maxDuration; + public float timelineOffset = 0; + + List randomOffsets = new List(); + + public CameraEffectRunner( CameraEffect effect ) + { + this.effect = effect; + Start(); + } + + void Start() + { + timelineOffset = TimeLineManager.GetPosition( effect.timeline ); + randomOffsets = new List(); + + Arrays.ForEach( effect.animations, ( a => randomOffsets.Add( a.Randomize() ) ) ) ; + } + + public void Update() + { + float timeLinePosition = TimeLineManager.GetPosition( effect.timeline ); + timePosition = timeLinePosition - timelineOffset; + + position = Vector3.Zero; + rotation = Quaternion.Identity; + fov = 0; + + for ( int i = 0; i < effect.animations.Length; i++) + { + Apply( effect.animations[ i ], i ); + } + } + + void Apply( CameraEffectTargetAnimationCurve curve, int index ) + { + var value = curve.Sample( timePosition, randomOffsets[ index ] ); + + if ( curve.flipScaleYRandomly && GodotRandom.Get().FlipCoin() ) + { + value = -value; + } + + if ( CameraEffectTargetType.Position_X == curve.target ) + { + position.X += value; + } + else if ( CameraEffectTargetType.Position_Y == curve.target ) + { + position.Y += value; + } + else if ( CameraEffectTargetType.Position_Z == curve.target ) + { + position.Z += value; + } + + else if ( CameraEffectTargetType.Rotation_X == curve.target ) + { + rotation *= Math3D.RotateXDegrees( value ); + } + else if ( CameraEffectTargetType.Rotation_Y == curve.target ) + { + rotation *= Math3D.RotateYDegrees( value ); + } + else if ( CameraEffectTargetType.Rotation_Z == curve.target ) + { + rotation *= Math3D.RotateZDegrees( value ); + } + + else if ( CameraEffectTargetType.FOV == curve.target ) + { + fov += value; + } + + } + } +} \ No newline at end of file diff --git a/Runtime/VirtualCameras/Effects/CameraEffectTargetAnimationCurve.cs b/Runtime/VirtualCameras/Effects/CameraEffectTargetAnimationCurve.cs new file mode 100644 index 0000000..2f30147 --- /dev/null +++ b/Runtime/VirtualCameras/Effects/CameraEffectTargetAnimationCurve.cs @@ -0,0 +1,21 @@ + +using System.Diagnostics; +using System.Collections; +using System.Collections.Generic; +using System; +using Godot; + + +namespace Rokojori +{ + [Tool] + [GlobalClass] + public partial class CameraEffectTargetAnimationCurve:AnimationCurve + { + [Export] + public CameraEffectTargetType target; + + [Export] + public bool flipScaleYRandomly = false; + } +} \ No newline at end of file diff --git a/Runtime/VirtualCameras/Effects/CameraEffectTargetType.cs b/Runtime/VirtualCameras/Effects/CameraEffectTargetType.cs new file mode 100644 index 0000000..f84f2c6 --- /dev/null +++ b/Runtime/VirtualCameras/Effects/CameraEffectTargetType.cs @@ -0,0 +1,21 @@ + +using System.Diagnostics; +using System.Collections; +using System.Collections.Generic; +using System; +using Godot; + + +namespace Rokojori +{ + public enum CameraEffectTargetType + { + Position_X, + Position_Y, + Position_Z, + Rotation_X, + Rotation_Y, + Rotation_Z, + FOV + } +} \ No newline at end of file diff --git a/Runtime/VirtualCameras/Effects/PlayCameraEffect.cs b/Runtime/VirtualCameras/Effects/PlayCameraEffect.cs new file mode 100644 index 0000000..0b057c2 --- /dev/null +++ b/Runtime/VirtualCameras/Effects/PlayCameraEffect.cs @@ -0,0 +1,56 @@ + +using System.Diagnostics; +using System.Collections; +using System.Collections.Generic; +using System; +using Godot; + + +namespace Rokojori +{ + [Tool] + [GlobalClass] + public partial class PlayCameraEffect:RJAction + { + [Export] + public CameraEffect cameraEffect; + + [Export] + public VirtualCamera3DSlot cameraSlot; + + [Export] + public int cameraSlotIndex = -1; + + [Export] + public RJSelector cameraSlotSelector; + + public override void _OnTrigger() + { + var resolvedSlot = cameraSlot; + + if ( resolvedSlot == null && cameraSlotIndex != -1 ) + { + var manager = Unique.Get(); + + if ( cameraSlotIndex != -1 ) + { + resolvedSlot = manager.GetSlot( cameraSlotIndex ); + } + else if ( cameraSlotSelector != null ) + { + resolvedSlot = Selectors.GetFromDirectChildren( manager, cameraSlotSelector ); + } + } + + if ( resolvedSlot == null ) + { + this.LogError( "No camera slot found" ); + return; + } + + resolvedSlot.SetCameraEffect( cameraEffect ); + } + + + } +} \ No newline at end of file diff --git a/Runtime/VirtualCameras/MouseEditorCamera.cs b/Runtime/VirtualCameras/MouseEditorCamera.cs index ead12be..11003f5 100644 --- a/Runtime/VirtualCameras/MouseEditorCamera.cs +++ b/Runtime/VirtualCameras/MouseEditorCamera.cs @@ -25,7 +25,7 @@ namespace Rokojori public float distance = 10; float smoothDistance = 10; - [ExportCategory("Orbit")] + [ExportGroup("Orbit")] [Export] public float yawSpeed = 1; @@ -45,13 +45,13 @@ namespace Rokojori [Export] public RJSensor[] orbitModifierButtons; - [ExportCategory("Pan")] + [ExportGroup("Pan")] [Export] - public float panSpeedX = 1f; + public float panSpeedX = 0.01f; [Export] - public float panSpeedY = 1f; + public float panSpeedY = 0.01f; [Export] public RJSensor panButton; @@ -59,7 +59,7 @@ namespace Rokojori [Export] public RJSensor[] panModifierButtons; - [ExportCategory("Zoom")] + [ExportGroup("Zoom")] [Export] public float zoomStepInPercentage = 10; @@ -86,6 +86,24 @@ namespace Rokojori [Export] public float zoomSmoothingCoefficient = 0.1f; Smoother smoother = new Smoother(); + + [ExportGroup("Move")] + [Export] + public RJSensor forwardButton; + [Export] + public RJSensor backwardsButton; + [Export] + public RJSensor leftButton; + [Export] + public RJSensor rightButton; + [Export] + public RJSensor upButton; + [Export] + public RJSensor downButton; + + [Export] + public float moveSpeed = 1; + public override void _Process( double delta ) { @@ -93,6 +111,7 @@ namespace Rokojori Orbit(); Pan(); Zoom(); + Move(); Apply( (float) delta ); @@ -165,6 +184,30 @@ namespace Rokojori distance = Mathf.Clamp( distance, minDistance, maxDistance ); } + Vector3 moveDirection = Vector3.Zero; + + void Move() + { + moveDirection = Vector3.Zero; + + if ( Sensors.IsActive( forwardButton ) || Sensors.IsActive( backwardsButton ) ) + { + moveDirection = ( Sensors.GetValue( forwardButton ) - Sensors.GetValue( backwardsButton ) ) * this.GlobalForward(); + } + + if ( Sensors.IsActive( rightButton ) || Sensors.IsActive( leftButton ) ) + { + moveDirection = ( Sensors.GetValue( rightButton ) - Sensors.GetValue( leftButton ) ) * this.GlobalRight(); + } + + if ( Sensors.IsActive( upButton ) || Sensors.IsActive( downButton ) ) + { + moveDirection = ( Sensors.GetValue( upButton ) - Sensors.GetValue( downButton ) ) * this.GlobalUp(); + } + + moveDirection = moveDirection.Normalized() * moveSpeed; + } + void Apply( float delta ) { @@ -172,6 +215,7 @@ namespace Rokojori GlobalRotation = new Vector3( Mathf.DegToRad( pitch ), Mathf.DegToRad( yaw ), 0 ); var forward = Math3D.GetGlobalForward( this ) * smoothDistance; + target -= moveDirection * delta; GlobalPosition = target + forward; } } diff --git a/Runtime/VirtualCameras/ThirdPersonCamera.cs b/Runtime/VirtualCameras/ThirdPersonCamera.cs index 17a749d..9af2f55 100644 --- a/Runtime/VirtualCameras/ThirdPersonCamera.cs +++ b/Runtime/VirtualCameras/ThirdPersonCamera.cs @@ -46,6 +46,9 @@ namespace Rokojori [Export] public RJSensor pitchNegativeAxis; + [Export] + public bool pitchIsRelative = false; + [Export] public float pitch = 0; @@ -66,6 +69,10 @@ namespace Rokojori [Export] public float distanceScale = 1; + [ExportGroup("Offset")] + [Export] + public Vector3 offset; + Smoother distanceSmoother = new Smoother(); float smoothedYaw = 0; @@ -80,17 +87,24 @@ namespace Rokojori return; } - var yawAxis = Sensors.PolarAxis( yawNegativeAxis, yawPositiveAxis ); + var yawAxis = Sensors.PolarAxis( yawNegativeAxis, yawPositiveAxis ); var pitchAxis = Sensors.PolarAxis( pitchNegativeAxis, pitchPositiveAxis ); yaw += yawAxis * yawSpeed * (float)delta; - yaw = MathX.Repeat( yaw, 360f ); + // yaw = MathX.Repeat( yaw, 360f ); - // pitch += pitchAxis * pitchSpeed * (float)delta; - // pitch = Mathf.Clamp( pitch, minPitch, maxPitch ); + if ( pitchIsRelative ) + { + pitch += pitchAxis * pitchSpeed * (float)delta; + pitch = Mathf.Clamp( pitch, minPitch, maxPitch ); + } + else + { + pitch = Mathf.Remap( pitchAxis, -1, 1, minPitch, maxPitch ); + } - + if ( Mathf.Abs( yaw - smoothedYaw ) > 180 ) { if ( yaw > smoothedYaw ) @@ -103,16 +117,18 @@ namespace Rokojori } } - // smoothedYaw = yawSmoother.SmoothForDuration( smoothedYaw, yaw, yawSmoothingDuration, (float) delta ); - // smoothedPitch = pitchSmoother.SmoothForDuration( smoothedPitch, pitch, pitchSmoothingDuration, (float) delta ); - smoothedYaw = yaw; - smoothedPitch = pitch; + smoothedPitch = pitchSmoother.SmoothForDuration( smoothedPitch, pitch, pitchSmoothingDuration, (float) delta ); + + // RJLog.Log( "Pitch", smoothedPitch ); + + // smoothedYaw = yaw; + // smoothedPitch = pitch; var distance = distanceForPitch.Sample( MathX.NormalizeClamped( pitch, minPitch, maxPitch ) ) * distanceScale; - GlobalPosition = target.GlobalPosition + Math3D.YawPitchRotation( smoothedYaw, smoothedPitch ) * Vector3.Forward * distance; + GlobalPosition = target.GlobalPosition + Math3D.YawPitchRotation( smoothedYaw, smoothedPitch ) * Vector3.Forward * distance + offset; - LookAt( target.GlobalPosition, Vector3.Up, true ); + LookAt( target.GlobalPosition + offset, Vector3.Up, true ); diff --git a/Runtime/VirtualCameras/VirtualCamera3DManager.cs b/Runtime/VirtualCameras/VirtualCamera3DManager.cs index 419b975..e9afd90 100644 --- a/Runtime/VirtualCameras/VirtualCamera3DManager.cs +++ b/Runtime/VirtualCameras/VirtualCamera3DManager.cs @@ -36,6 +36,11 @@ namespace Rokojori List _cameraSlots = new List(); + public VirtualCamera3DSlot GetSlot( int index ) + { + return _cameraSlots[ index ]; + } + public void SetActiveSlot( VirtualCamera3DSlot slot ) { _cameraSlots.ForEach( c => c.priority = c == slot ? 1 : 0 ); @@ -63,7 +68,6 @@ namespace Rokojori if ( sumPriority == 0 ) { - // RJLog.Log( "sumPriority == 0", _cameraSlots.Count, Lists.Map( _cameraSlots, c => c.priority ) ); return; } @@ -76,7 +80,7 @@ namespace Rokojori c => { var priority = MathF.Max( 0, c.smoothedPriority ); - var rotation = c.camera.GetCameraRotation(); + var rotation = c.GetCameraRotation(); if ( ! rotation.IsFinite() || rotation.Length() == 0 ) { @@ -86,23 +90,16 @@ namespace Rokojori rotation.Z = 0; rotation.W = 1; rotation = rotation.Normalized(); - //RJLog.Log( "Rotation was weird" ); } - else - { - //RJLog.Log( "Rotation is fine" ); - } - - //RJLog.Log( rotation.X, rotation.Y, rotation.Z, rotation.W ); var vUp = rotation * Vector3.Up; var vForward = rotation * Vector3.Forward; - position += priority * c.camera.GetCameraPosition(); + position += priority * c.GetCameraPosition(); up += priority * vUp; forward += priority * vForward; - fov += priority * c.camera.GetCameraFOV(); + fov += priority * c.GetCameraFOV(); } ); diff --git a/Runtime/VirtualCameras/VirtualCamera3DSlot.cs b/Runtime/VirtualCameras/VirtualCamera3DSlot.cs index 84b80ac..c1a0dc5 100644 --- a/Runtime/VirtualCameras/VirtualCamera3DSlot.cs +++ b/Runtime/VirtualCameras/VirtualCamera3DSlot.cs @@ -19,20 +19,86 @@ namespace Rokojori [Export] public float priority; + CameraEffectRunner cameraEffectRunner = null; + float _effectTimePosition = 0; float _smoothedPriority; public float smoothedPriority => _smoothedPriority; + Smoother smoother = new Smoother(); public void Update( double delta, VirtualCamera3DManager manager ) { - + if ( cameraEffectRunner != null ) + { + if ( cameraEffectRunner.isFinished ) + { + cameraEffectRunner = null; + } + else + { + cameraEffectRunner.Update(); + } + } + _smoothedPriority = smoother.SmoothWithCoefficient( _smoothedPriority, priority, manager.safeSmoothing, (float) delta, manager.smoothStepDelta ); } + public void SetCameraEffect( CameraEffect effect ) + { + cameraEffectRunner = new CameraEffectRunner( effect ); + } + + public Vector3 GetCameraPosition() + { + if ( camera == null ) + { + return Vector3.Zero; + } + + if ( cameraEffectRunner == null ) + { + return camera.GetCameraPosition(); + } + + var offset = camera.GetGlobalOffset( cameraEffectRunner.position ); + + return camera.GetCameraPosition() + offset; + } + + public Quaternion GetCameraRotation() + { + if ( camera == null ) + { + return Quaternion.Identity; + } + + if ( cameraEffectRunner == null ) + { + return camera.GetCameraRotation(); + } + + return camera.GetCameraRotation() * cameraEffectRunner.rotation; + } + + public float GetCameraFOV() + { + if ( camera == null ) + { + return 65; + } + + if ( cameraEffectRunner == null ) + { + return camera.GetCameraFOV(); + } + + return camera.GetCameraFOV() + cameraEffectRunner.fov; + } + public override void _OnTrigger() { var vm = GetParent();