Docs + GLTF Exporter

This commit is contained in:
Josef 2025-07-14 12:35:37 +02:00
parent f054d9e48c
commit 537f3d7e96
38 changed files with 653 additions and 93 deletions

52
Icons/Help.svg Normal file
View File

@ -0,0 +1,52 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
height="16"
viewBox="0 0 16 16"
width="16"
version="1.1"
id="svg4"
sodipodi:docname="Help.svg"
inkscape:version="1.2.2 (732a01da63, 2022-12-09)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs8" />
<sodipodi:namedview
id="namedview6"
pagecolor="#505050"
bordercolor="#eeeeee"
borderopacity="1"
inkscape:showpageshadow="0"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#505050"
showgrid="false"
inkscape:zoom="20.727068"
inkscape:cx="11.506693"
inkscape:cy="9.6733412"
inkscape:window-width="1920"
inkscape:window-height="1017"
inkscape:window-x="-8"
inkscape:window-y="-8"
inkscape:window-maximized="1"
inkscape:current-layer="svg4" />
<rect
style="opacity:1;fill:#000000;fill-opacity:1;stroke:#ffffff;stroke-width:2.752;stroke-linecap:square;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill"
id="rect390"
width="12.238219"
height="12.238219"
x="1.8808906"
y="1.8808906"
ry="5.9782681" />
<g
aria-label="?"
id="text1260"
style="font-weight:900;font-size:12.0205px;line-height:1;font-family:Jost;-inkscape-font-specification:'Jost Heavy';opacity:1;fill:#ffffff;stroke-width:0.85861;stroke-linecap:square;stroke-linejoin:round;paint-order:markers stroke fill"
transform="matrix(1.0531609,0,0,1.0531609,-0.4748664,-0.17766869)">
<path
d="m 9.1489874,6.0467033 q 0,0.216369 -0.2043485,0.504861 Q 8.7523109,6.8280359 8.3075524,7.0444049 7.8627939,7.2487534 7.1415638,7.2487534 h -0.432738 l 0.24041,2.16369 h 1.9232801 l 0.036062,-0.745271 q 0.6370865,-0.1562665 1.2260911,-0.48082 0.601025,-0.3245535 0.985681,-0.8534555 0.384655,-0.528902 0.384655,-1.2861936 0,-0.673148 -0.288491,-1.178009 -0.288492,-0.504861 -0.781333,-0.841435 -0.4808196,-0.3485945 -1.0698241,-0.5168815 -0.5890045,-0.168287 -1.20205,-0.168287 -0.7092096,0 -1.3462961,0.192328 -0.625066,0.192328 -1.1900295,0.5649635 -0.5649635,0.360615 -1.081845,0.9015375 l 1.5025625,1.3342755 q 0.3726355,-0.5409225 0.9255785,-0.8053735 0.552943,-0.264451 1.0698246,-0.264451 0.360615,0 0.601025,0.096164 0.2524305,0.084143 0.3726355,0.264451 0.1322255,0.168287 0.132225,0.4207175 z M 6.4924568,11.215518 q 0,0.48082 0.3966765,0.841435 0.3966765,0.360615 1.0217426,0.360615 0.6370865,0 1.0217425,-0.360615 0.3966765,-0.360615 0.3966765,-0.841435 0,-0.48082 -0.3966765,-0.841435 -0.384656,-0.360615 -1.0217425,-0.360615 -0.6250661,0 -1.0217426,0.360615 -0.3966765,0.360615 -0.3966765,0.841435 z"
id="path2140" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.8 KiB

37
Icons/Help.svg.import Normal file
View File

@ -0,0 +1,37 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://ku3wjdsmv1lh"
path="res://.godot/imported/Help.svg-73996934a1ef8b9c260e47a975f69d9d.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://addons/rokojori_action_library/Icons/Help.svg"
dest_files=["res://.godot/imported/Help.svg-73996934a1ef8b9c260e47a975f69d9d.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1
svg/scale=1.0
editor/scale_with_editor_scale=false
editor/convert_colors_with_editor_theme=false

View File

@ -9,6 +9,16 @@ namespace Rokojori
[GlobalClass, Icon("res://addons/rokojori_action_library/Icons/Action.svg")] [GlobalClass, Icon("res://addons/rokojori_action_library/Icons/Action.svg")]
public partial class Action : NetworkNode public partial class Action : NetworkNode
{ {
[ExportToolButton( "(?) Help", Icon = "Help") ]
public Callable openHelpButton => Callable.From( ()=>
{
#if TOOLS
Rokojori.Tools.OnlineDocs.Open( GetType() );
#endif
}
);
[ExportToolButton( "Trigger Action in Editor")] [ExportToolButton( "Trigger Action in Editor")]
public Callable TriggerActionButton => Callable.From( ()=> Trigger() ); public Callable TriggerActionButton => Callable.From( ()=> Trigger() );

View File

@ -1,4 +1,5 @@
using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
@ -125,6 +126,16 @@ namespace Rokojori
return SaveUTF8( path, JSON.StringifyObject( obj ) ); return SaveUTF8( path, JSON.StringifyObject( obj ) );
} }
public static DateTimeOffset GetModificationTime( string path )
{
return File.GetLastWriteTimeUtc( path );
}
public static ISOTimeStamp GetModificationTimeStamp( string path )
{
return ISOTimeStamp.FromDate( GetModificationTime( path ) );
}
public static string LoadUTF8( string path ) public static string LoadUTF8( string path )
{ {
try try

View File

@ -209,6 +209,30 @@ namespace Rokojori
,false ); ,false );
} }
public static async Task ForEachAsync<T>( this Node root, Func<T,Task> callback ) where T:class
{
var walker = nodesWalker;
await walker.IterateAsync( root,
async ( n )=>
{
var t = n as T;
if ( t == null )
{
return;
}
await callback( t );
return;
}
,false );
return;
}
public static T GetSibling<T>( Node node ) where T:Node public static T GetSibling<T>( Node node ) where T:Node
{ {
if ( node == null ) if ( node == null )
@ -233,11 +257,14 @@ namespace Rokojori
t.Owner = parent.Owner; t.Owner = parent.Owner;
if ( name != null ) if ( name == null )
{ {
t.Name = name; name = t.GetType().Name;
} }
t.Name = name;
return t; return t;
} }

View File

@ -1,6 +1,7 @@
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System; using System;
using System.Threading.Tasks;
namespace Rokojori namespace Rokojori
{ {
@ -585,6 +586,25 @@ namespace Rokojori
} }
} }
public async Task IterateAsync( N node, Func<N,Task> callback, bool childrenOnly = false )
{
var end = IterationEndOf( node );
var it = node;
if ( childrenOnly )
{
it = NextNode( it );
}
while ( it != end )
{
await callback( it );
it = NextNode( it );
}
return;
}
public N Find( N node, Predicate<N> predicate, bool childrenOnly ) public N Find( N node, Predicate<N> predicate, bool childrenOnly )
{ {
var end = IterationEndOf( node ); var end = IterationEndOf( node );

View File

@ -0,0 +1 @@
uid://c1upyoky3f0we

View File

@ -21,19 +21,6 @@ namespace Rokojori
protected List<NetworkNodeMember> _networkNodeMembers = null; protected List<NetworkNodeMember> _networkNodeMembers = null;
protected NetworkNodeSlot _networkNodeSlot = new NetworkNodeSlot(); protected NetworkNodeSlot _networkNodeSlot = new NetworkNodeSlot();
#if TOOLS
[ExportGroup("Help")]
[ExportToolButton( "Open Online Docs")]
public Callable openOnlineDocsButton => Callable.From(
()=>
{
OnlineDocs.Open( GetType() );
}
);
#endif
public virtual List<NetworkNodeMember> GetNetworkNodeMembers() public virtual List<NetworkNodeMember> GetNetworkNodeMembers()
{ {
if ( _networkNodeMembers != null ) if ( _networkNodeMembers != null )

View File

@ -8,13 +8,42 @@ using System.Threading.Tasks;
namespace Rokojori namespace Rokojori
{ {
/** <summary for="class GrassPatch">
<title>
Creates a grass patch 3D model asset.
</title>
<description>
The GrassPatch has various settings to create a different styles of grass.
It allows to change the shapes of the blades, their number and distribution, their triangle count,
rotation and scale, LOD levels and much more.
</description>
</summary>
*/
[Tool] [Tool]
[GlobalClass, Icon("res://addons/rokojori_action_library/Icons/Scatterer.svg") ] [GlobalClass, Icon("res://addons/rokojori_action_library/Icons/Scatterer.svg") ]
public partial class GrassPatch:Node3D public partial class GrassPatch:Node3D
{ {
[ExportToolButton( "(?) Help", Icon = "Help") ]
public Callable openHelpButton => Callable.From( ()=>
{
#if TOOLS
Rokojori.Tools.OnlineDocs.Open( GetType() );
#endif
}
);
/** <summary for="field output">The output where the mesh is stored, gets generated automatically.</summary>*/
[Export] [Export]
public MeshInstance3D output; public MeshInstance3D output;
/** <summary for="field seed">The seed for the randomizers</summary>*/
[Export] [Export]
public int seed = 1984; public int seed = 1984;
@ -25,94 +54,194 @@ namespace Rokojori
CreatePatch(); CreatePatch();
} }
); );
/** <summary for="field material">The material the grass will be assigned</summary>*/
[Export] [Export]
public Material material; public Material material;
/** <summary for="field patchSize">Patch size for x and z</summary>*/
[ExportGroup( "Patch")] [ExportGroup( "Patch")]
[Export] [Export]
public float patchSize = 2; public float patchSize = 2;
/** <summary for="field patchSizeX">Additional patch size for x</summary>*/
[Export] [Export]
public float patchSizeX = 0; public float patchSizeX = 0;
/** <summary for="field patchSizeZ">Additional patch size for z</summary>*/
[Export] [Export]
public float patchSizeZ = 0; public float patchSizeZ = 0;
/** <summary for="field patchOffsetPosition">Patch offset in x/z</summary>*/
[Export] [Export]
public Vector2 patchOffsetPosition = new Vector2( 0.5f, 0.5f ); public Vector2 patchOffsetPosition = new Vector2( 0.5f, 0.5f );
/** <summary for="field centerPatch">If centerPatch is on, the patch will be centered automatically</summary>*/
[Export] [Export]
public bool centerPatch = false; public bool centerPatch = false;
/** <summary for="field centerPatchComputationHeightTreshold">
If on, this determines which vertices should be counted to compute the center of the patch.
<p>
All vertices in y that are below, will not count to compute the center.
This can be usefull to align more to the upper more visible parts, it usually has only a small impact.
</p>
</summary>*/
[Export] [Export]
public float centerPatchComputationHeightTreshold = 0.1f; public float centerPatchComputationHeightTreshold = 0.1f;
[ExportGroup( "Blades")] [ExportGroup( "Blades")]
/** <summary for="field blades">The number of blades it will generate in x and z</summary>*/
[Export( PropertyHint.Range, "0,100")] [Export( PropertyHint.Range, "0,100")]
public int blades = 20; public int blades = 20;
/** <summary for="field bladesX">Additional number of blades it will generate in x</summary>*/
[Export( PropertyHint.Range, "0,100")] [Export( PropertyHint.Range, "0,100")]
public int bladesX = 0; public int bladesX = 0;
/** <summary for="field bladesZ">Additional number of blades it will generate in z</summary>*/
[Export( PropertyHint.Range, "0,100")] [Export( PropertyHint.Range, "0,100")]
public int bladesZ = 0; public int bladesZ = 0;
/** <summary for="field X_numBlades">This is an output/debug property, and will show how many blades were created</summary>*/
[Export] [Export]
public int X_numBlades; public int X_numBlades;
/** <summary for="field bladeSegments">This defines the number of segments (vertical divisions) for each blade.</summary>*/
[ExportGroup( "Triangles")] [ExportGroup( "Triangles")]
[Export( PropertyHint.Range, "1,256")] [Export( PropertyHint.Range, "1,256")]
public int bladeSegments = 3; public int bladeSegments = 3;
/** <summary for="field createBackFaces">This generates back faces, if it is disabled the material should not use backface culling</summary>*/
[Export] [Export]
public bool createBackFaces = true; public bool createBackFaces = true;
/** <summary for="field allowTrianglesOnEnds">
If on, this allows the ends of the blades to be merged as triangles instead of quads.
<p>
This is usefull for creating very low poly grass blades with only one triangle.
The width of the blade needs to be very close to zero at the start or end to be applied.
</p>
</summary>*/
[Export] [Export]
public bool allowTrianglesOnEnds = true; public bool allowTrianglesOnEnds = true;
/** <summary for="field X_numTriangles">This is an output/debug property, and will show how many triangels were created</summary>*/
[Export] [Export]
public int X_numTriangles; public int X_numTriangles;
/** <summary for="field bladeSegmentMapping">
Allows to use a non-linear distortion for the segmentation.
<p>
Normally, the blade shapes are segmented by taking an equal distance of divisions throughout the
height of the blade.
But since this low poly shapes won't be represented well with this approach,
zhis setting allows to distort the segmentation, some shapes are better represented with it.
</p>
</summary>*/
[Export] [Export]
public Curve bladeSegmentMapping = MathX.Curve( 0, 1 ); public Curve bladeSegmentMapping = MathX.Curve( 0, 1 );
/** <summary for="field uvSegmentColumns">
The number of splits in U for the blades, when generating the uv coordinates.
<p>
This allows to use multiple regions for the blades, so that blades can have different material settings.
</p>
</summary>*/
[ExportGroup( "Segmentation")] [ExportGroup( "Segmentation")]
[Export] [Export]
public int uvSegmentColumns = 1; public int uvSegmentColumns = 1;
/** <summary for="field uvSegmentRows">
The number of splits in V for the blades, when generating the uv coordinates.
<p>
This allows to use multiple regions for the blades, so that blades can have different material settings.
</p>
</summary>*/
[Export] [Export]
public int uvSegmentRows = 1; public int uvSegmentRows = 1;
/** <summary for="field uvSegmentWeightsClose">
The distribution for the UV region selection.
<p>
This will change the propability of the uv regions, so that some regions can
be used more often than other regions.
</p>
</summary>*/
[Export] [Export]
public Curve uvSegmentWeightsClose = MathX.Curve( 1f ); public Curve uvSegmentWeightsClose = MathX.Curve( 1f );
/** <summary for="field uvSegmentWeightsFar">
The distribution for the UV region selection for blades outside the center.
<p>
This will change the propability of the uv regions outside the center.
This allows to have different distribution dependening on the position.
</p>
</summary>*/
[Export] [Export]
public Curve uvSegmentWeightsFar = null; public Curve uvSegmentWeightsFar = null;
/** <summary for="field uvSegmentDistortion">
Blending factor between undistorted and distorted U mapping.
<p>
This warps/pulls the strength of the close or far distribution.
</p>
</summary>*/
[Export( PropertyHint.Range, "0,1" )] [Export( PropertyHint.Range, "0,1" )]
public float uvSegmentDistortion = 0.5f; public float uvSegmentDistortion = 0.5f;
/** <summary for="field uvSegmentMaxRange">
Defines how far the U mapping can distort.
</summary>*/
[Export( PropertyHint.Range, "0,0.5" )] [Export( PropertyHint.Range, "0,0.5" )]
public float uvSegmentMaxRange = 0.3f; public float uvSegmentMaxRange = 0.3f;
[ExportGroup( "Shape")] /** <summary for="field bladeHeight">
Defines the height of the blades, the curve defines the random distribution.
<p>
The total y size is sum of the blade height and baldInGround.
</p>
</summary>*/
[ExportGroup( "Shape")]
[Export] [Export]
public Curve bladeHeight = MathX.Curve( 0.4f ); public Curve bladeHeight = MathX.Curve( 0.4f );
/** <summary for="field bladeInGround">
The amount the blade is continued below 0 in y. See also bladeHeight.
</summary>*/
[Export] [Export]
public Curve bladeInGround = MathX.Curve( 0.05f ); public Curve bladeInGround = MathX.Curve( 0.05f );
/** <summary for="field bladeWidth">
The width of the blade defined over its height.
</summary>*/
[Export] [Export]
public Curve bladeWidth = MathX.Curve( 0.05f ); public Curve bladeWidth = MathX.Curve( 0.05f );
/** <summary for="field bladeWidth2">
Optional: A second width (as randomizer) of the blade defined over its height.
<p>
The randomizer chooses a blend value before the creation and will use the resulting curve as width.
This means it will not randomly jump between the first and second width, but uses an interpolated curve.
</p>
</summary>*/
[Export] [Export]
public Curve bladeWidth2 = null; public Curve bladeWidth2 = null;

View File

@ -8,6 +8,20 @@ using System.Threading.Tasks;
namespace Rokojori namespace Rokojori
{ {
/** <summary for="class MeshCombiner">
<title>
Combines multiples meshes and/or materials into one mesh or material.
</title>
<description>
</description>
</summary>
*/
[Tool] [Tool]
[GlobalClass] [GlobalClass]
public partial class MeshCombiner:Node3D public partial class MeshCombiner:Node3D
@ -29,7 +43,7 @@ namespace Rokojori
} }
[Export] [Export]
public UVCombineMode uVCombineMode = UVCombineMode.Adjust_To_Combined_Material; public UVCombineMode uvCombineMode = UVCombineMode.Adjust_To_Combined_Material;
[Export] [Export]
public Node3D pivot; public Node3D pivot;
@ -50,6 +64,9 @@ namespace Rokojori
[Export] [Export]
public int randomSortSeed = 1234; public int randomSortSeed = 1234;
[Export]
public X_MeshGeometryModifierResource[] meshModifiers;
[ExportGroup( "Material")] [ExportGroup( "Material")]
[Export] [Export]
public bool combineMaterials = true; public bool combineMaterials = true;
@ -466,7 +483,7 @@ namespace Rokojori
if ( isCombinedMaterial && ! combineMeshes ) if ( isCombinedMaterial && ! combineMeshes )
{ {
meshGeometry.GenerateMesh( Mesh.PrimitiveType.Triangles, arrayMesh ); meshGeometry.Apply( meshModifiers ).GenerateMesh( Mesh.PrimitiveType.Triangles, arrayMesh );
arrayMesh.SurfaceSetMaterial( index, usedMaterial ); arrayMesh.SurfaceSetMaterial( index, usedMaterial );
var meshInstance = outputContainer.CreateChild<MeshInstance3D>(); var meshInstance = outputContainer.CreateChild<MeshInstance3D>();
@ -494,7 +511,7 @@ namespace Rokojori
if ( combineMeshes ) if ( combineMeshes )
{ {
meshGeometry.GenerateMesh( Mesh.PrimitiveType.Triangles, arrayMesh ); meshGeometry.Apply( meshModifiers ).GenerateMesh( Mesh.PrimitiveType.Triangles, arrayMesh );
arrayMesh.SurfaceSetMaterial( index, usedMaterial ); arrayMesh.SurfaceSetMaterial( index, usedMaterial );
index ++; index ++;
@ -513,7 +530,7 @@ namespace Rokojori
var combinedMG = MeshGeometry.Combine( meshes ); var combinedMG = MeshGeometry.Combine( meshes );
this.LogInfo( "Combed meshes, num tris:", combinedMG.numTriangles ); this.LogInfo( "Combed meshes, num tris:", combinedMG.numTriangles );
combinedMG.GenerateMesh( Mesh.PrimitiveType.Triangles, arrayMesh ); combinedMG.Apply( meshModifiers ).GenerateMesh( Mesh.PrimitiveType.Triangles, arrayMesh );
this.LogInfo( "Add surface", index); this.LogInfo( "Add surface", index);
arrayMesh.SurfaceSetMaterial( index, material ); arrayMesh.SurfaceSetMaterial( index, material );

View File

@ -1496,6 +1496,23 @@ namespace Rokojori
return GenerateLODMesh( mgs, Mesh.PrimitiveType.Triangles, null, generateTangents, lerpingData ); return GenerateLODMesh( mgs, Mesh.PrimitiveType.Triangles, null, generateTangents, lerpingData );
} }
public MeshGeometry Apply( X_MeshGeometryModifierResource[] modifiers )
{
if ( modifiers == null || modifiers.Length == 0 )
{
return this;
}
var mg = this;
modifiers.ForEach(
( m )=>
{
mg = m.Modify( mg );
}
);
return mg;
}
public ArrayMesh GenerateMesh( public ArrayMesh GenerateMesh(

View File

@ -9,7 +9,8 @@ namespace Rokojori
{ {
public class MeshGeometryModifier public class MeshGeometryModifier
{ {
public MeshGeometry Modify( MeshGeometry mg ){ return mg; } public virtual MeshGeometry Modify( MeshGeometry mg ){ return mg; }
} }
} }

View File

@ -0,0 +1,21 @@
using System.Collections;
using System.Collections.Generic;
using Godot;
using System;
namespace Rokojori
{
[Tool]
[GlobalClass]
public partial class NormalBlender:X_MeshGeometryModifierResource
{
public NormalBlenderSettings settings;
public override MeshGeometry Modify( MeshGeometry mg )
{
return settings.Blend( mg );
}
}
}

View File

@ -0,0 +1 @@
uid://do0gp7573fyp1

View File

@ -0,0 +1,19 @@
using System.Collections;
using System.Collections.Generic;
using Godot;
using System;
namespace Rokojori
{
public class NormalBlenderModifier:MeshGeometryModifier
{
public NormalBlenderSettings settings;
public override MeshGeometry Modify( MeshGeometry mg )
{
return settings.Blend( mg );
}
}
}

View File

@ -0,0 +1 @@
uid://dei88uuxuyt3x

View File

@ -0,0 +1,55 @@
using System.Collections;
using System.Collections.Generic;
using Godot;
using System;
namespace Rokojori
{
[Tool]
[GlobalClass]
public partial class NormalBlenderSettings:Resource
{
public enum Mode
{
Over_Y
}
[Export]
public Mode mode;
[Export(PropertyHint.Range,"0,1")]
public float normalBlendAmount = 0.5f;
[ExportGroup("Over Y")]
[Export]
public Vector3 overY_blendDirection = Vector3.Up;
[Export]
public float overY_blendStartY = 0;
[Export]
public float overY_blendEndY = 0.25f;
[Export]
public Curve overY_blendCurve;
public MeshGeometry Blend( MeshGeometry mg )
{
if ( Mode.Over_Y == mode )
{
return BlendOverY( mg );
}
return mg;
}
MeshGeometry BlendOverY( MeshGeometry mg )
{
mg.BlendNormalsOverY( overY_blendDirection, normalBlendAmount, overY_blendStartY, overY_blendEndY, overY_blendCurve );
return mg;
}
}
}

View File

@ -0,0 +1 @@
uid://dbaxttonr78f5

View File

@ -15,16 +15,14 @@ namespace Rokojori
public float weight; public float weight;
} }
public class SplinesDeformModifier public class SplinesDeformModifier:MeshGeometryModifier
{ {
public SplineCurve[] sourceSplines = new SplineCurve[ 0 ]; public SplineCurve[] sourceSplines = new SplineCurve[ 0 ];
public SplineCurve[] targetSplines = new SplineCurve[ 0 ]; public SplineCurve[] targetSplines = new SplineCurve[ 0 ];
public SplinesDeformerSettings settings; public SplinesDeformerSettings settings;
public override MeshGeometry Modify( MeshGeometry mg )
public MeshGeometry Modify( MeshGeometry mg )
{ {
var mappings = CreateSourceMappings( mg ); var mappings = CreateSourceMappings( mg );
return CreateDeformed( mg, mappings ); return CreateDeformed( mg, mappings );

View File

@ -0,0 +1,20 @@
using System.Collections;
using System.Collections.Generic;
using Godot;
using System;
namespace Rokojori
{
[Tool]
[GlobalClass]
public partial class X_MeshGeometryModifierResource:Resource
{
public virtual MeshGeometry Modify( MeshGeometry mg )
{
return mg;
}
}
}

View File

@ -0,0 +1 @@
uid://bbs2ht7j11aoq

View File

@ -501,7 +501,7 @@ namespace Rokojori
if ( index == 0 ) if ( index == 0 )
{ {
RJLog.Log( tokens.Map( t => t.type + ": '" + t.match + "'" ).Join( "\n" ) ); // RJLog.Log( tokens.Map( t => t.type + ": '" + t.match + "'" ).Join( "\n" ) );
} }
return blocks; return blocks;
} }

View File

@ -0,0 +1 @@
uid://dqh0ll5kxudrb

View File

@ -1,29 +0,0 @@
using System.Diagnostics;
using System.Collections;
using System.Collections.Generic;
using System;
using Godot;
namespace Rokojori
{
[GlobalClass]
[Tool]
public partial class Date:Resource
{
public int year = 1;
public int month = 1;
public int day = 1;
public int hours = 0;
public int minutes = 0;
public int second = 0;
public float milliseconds = 0;
public int utcHoursOffset = 0;
public int utcMinutesOffset = 0;
public float utcMillisecondsOffset = 0;
}
}

View File

@ -1 +0,0 @@
uid://cvl5k8x7rrybt

View File

@ -10,6 +10,10 @@ namespace Rokojori
{ {
public static class DateMath public static class DateMath
{ {
public static DateTimeOffset InThePast( int hours )
{
return DateTimeOffset.UtcNow.AddHours( - hours );
}
public static bool IsNewerThan( this DateTimeOffset a, DateTimeOffset b ) public static bool IsNewerThan( this DateTimeOffset a, DateTimeOffset b )
{ {

View File

@ -0,0 +1,37 @@
using System.Diagnostics;
using System.Collections;
using System.Collections.Generic;
using System;
using Godot;
namespace Rokojori
{
public class ISOTimeStamp
{
public string value;
public static ISOTimeStamp Create( string value )
{
var t = new ISOTimeStamp();
t.value = value;
return t;
}
public static DateTimeOffset ToDate( ISOTimeStamp stamp )
{
return DateTimeOffset.Parse( stamp.value );
}
public static ISOTimeStamp FromDate( DateTimeOffset date )
{
return Create( date.ToString("o") );
}
public static ISOTimeStamp Now()
{
return ISOTimeStamp.FromDate( DateTimeOffset.Now );
}
}
}

View File

@ -0,0 +1 @@
uid://cj240k4nm1fyq

View File

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System; using System;
using System.Reflection; using System.Reflection;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using Godot;
namespace Rokojori namespace Rokojori
{ {
@ -415,10 +416,30 @@ namespace Rokojori
{ {
dataMembers.ForEach( dataMembers.ForEach(
dm => dm =>
{
try
{ {
var value = GetDataMemberValue<object>( source, dm ); var value = GetDataMemberValue<object>( source, dm );
SetDataMemberValue( target, dm, value ); SetDataMemberValue( target, dm, value );
} }
catch( System.Exception e )
{
if ( source is Node n && target is Node n2 )
{
n.LogInfo( "Could not copy to ", dm, HierarchyName.Of( n2 ) );
}
else if ( source is Resource r && target is Resource r2 )
{
r.LogInfo( "Could not copy to ", dm, HierarchyName.Of( r2 ) );
}
else
{
RJLog.Log( "Could not copy from ", source, "to ", dm, ">>", target );
}
}
}
); );
} }

View File

@ -41,6 +41,41 @@ namespace Rokojori.DocGenerator
return "CSharp.svg"; return "CSharp.svg";
} }
public static List<string> GetExports( FieldInfo fieldInfo )
{
var result = new List<string>();
var exportAttributes = fieldInfo.GetCustomAttributes( typeof(ExportAttribute), false);
foreach ( ExportAttribute export in exportAttributes )
{
if ( export.Hint == PropertyHint.None)
{
result.Add( "Export" );
}
else if ( ! string.IsNullOrEmpty( export.HintString ) )
{
result.Add( $"Export( PropertyHint.{export.Hint}, \"{export.HintString}\" )" );
}
else
{
result.Add( $"Export( PropertyHint.{export.Hint} )" );
}
}
return result;
}
public static string GetOutputFilePath( string outputPath, Type classType )
{
var typeName = GetDocsType.Of( classType );
var outputPathName = typeName.Replace( "`", "-" ).Replace( "<", "-" ).Replace( ">", "-" );
return FilePath.Join( outputPath, outputPathName + ".json" );
}
public ClassDocInfo Create( string filePath, Type classType, string definitionType, List<string> icons ) public ClassDocInfo Create( string filePath, Type classType, string definitionType, List<string> icons )
{ {
var data = filePath == null ? "" : FilesSync.LoadUTF8( filePath ); var data = filePath == null ? "" : FilesSync.LoadUTF8( filePath );
@ -51,9 +86,9 @@ namespace Rokojori.DocGenerator
var baseTypeName = classType.BaseType; var baseTypeName = classType.BaseType;
var typeNameSpace = classType.Namespace; var typeNameSpace = classType.Namespace;
RJLog.Log( typeName, "Comments:", comments.entries.Count ); // RJLog.Log( typeName, "Comments:", comments.entries.Count );
info.modificationTime = FilesSync.GetModificationTimeStamp( filePath );
info.name = typeName; info.name = typeName;
info.csNameSpace = typeNameSpace; info.csNameSpace = typeNameSpace;
info.definitionType = definitionType; info.definitionType = definitionType;
@ -160,6 +195,7 @@ namespace Rokojori.DocGenerator
memberInfo.dataType = GetDocsType.Of( f.FieldType ); memberInfo.dataType = GetDocsType.Of( f.FieldType );
memberInfo.generics = Lists.Map( f.FieldType.GetGenericArguments(), t => t + "" ); memberInfo.generics = Lists.Map( f.FieldType.GetGenericArguments(), t => t + "" );
memberInfo.doc = comments.FindDoc( "field", f.Name ); memberInfo.doc = comments.FindDoc( "field", f.Name );
memberInfo.attributes = GetExports( f );
if ( f.IsStatic ) if ( f.IsStatic )
{ {

View File

@ -77,6 +77,7 @@ namespace Rokojori.DocGenerator
public string icon; public string icon;
public string sourcePath; public string sourcePath;
public string definitionType; public string definitionType;
public ISOTimeStamp modificationTime;
public List<string> generics = new List<string>(); public List<string> generics = new List<string>();
public List<string> interfaces = new List<string>(); public List<string> interfaces = new List<string>();

View File

@ -40,10 +40,10 @@ namespace Rokojori.DocGenerator
public bool shaders = true; public bool shaders = true;
[Export] [Export]
public bool processOnlyNewerThanCheckTime = true; public bool processOnlyNewer = true;
[Export] [Export]
public float hoursCheckTime; public int hoursCheckTime = 12;
[ExportToolButton( "Create")] [ExportToolButton( "Create")]
public Callable createButton => Callable.From( ()=>{ Generate(); } ); public Callable createButton => Callable.From( ()=>{ Generate(); } );
@ -77,6 +77,7 @@ namespace Rokojori.DocGenerator
var generator = new DocGenerator(); var generator = new DocGenerator();
generator.shaders = shaders; generator.shaders = shaders;
generator.cSharp = c_sharp; generator.cSharp = c_sharp;
generator.checkChangeTime = processOnlyNewer;
var icons = Lists.Map( FilesSync.GetFiles( absoluteIconPath, ( fp => fp.fileExtension == ".svg" ) ), fp => fp.fullFileName ); var icons = Lists.Map( FilesSync.GetFiles( absoluteIconPath, ( fp => fp.fileExtension == ".svg" ) ), fp => fp.fullFileName );
generator.Generate( absoluteLibraryPath, absoluteOutputPath, icons ); generator.Generate( absoluteLibraryPath, absoluteOutputPath, icons );

View File

@ -70,7 +70,7 @@ namespace Rokojori.DocGenerator
entries.Add( entry ); entries.Add( entry );
RJLog.Log( "Doc Comment:", entry.type, entry.name, ">>\n", entry.doc ); // RJLog.Log( "Doc Comment:", entry.type, entry.name, ">>\n", entry.doc );
} }
); );

View File

@ -30,7 +30,6 @@ namespace Rokojori.DocGenerator
List<string> icons = new List<string>(); List<string> icons = new List<string>();
public bool checkChangeTime = false; public bool checkChangeTime = false;
public DateTimeOffset changeTime;
public List<FilePath> classFiles = new List<FilePath>(); public List<FilePath> classFiles = new List<FilePath>();
public List<ClassTypeEntry> classTypes = new List<ClassTypeEntry>(); public List<ClassTypeEntry> classTypes = new List<ClassTypeEntry>();
@ -62,7 +61,7 @@ namespace Rokojori.DocGenerator
if ( isShader ) if ( isShader )
{ {
RJLog.Log( "SHADER: FILE", f.absolutePath ); // RJLog.Log( "SHADER: FILE", f.absolutePath );
} }
return ( cSharp && isCS ) || ( shaders && isShader ); return ( cSharp && isCS ) || ( shaders && isShader );
@ -70,15 +69,16 @@ namespace Rokojori.DocGenerator
true true
); );
if ( checkChangeTime ) // if ( checkChangeTime )
{ // {
classFiles = await classFiles.FilterAsync( // classFiles = await classFiles.FilterAsync(
async ( f ) => // async ( f ) =>
{ // {
return await Git.IsFileNewerThan( f.absolutePath, changeTime ); // let generated =
} // return await Git.IsFileNewerThan( f.absolutePath, changeTime );
); // }
} // );
// }
} }
void IncludeDefaultTypes() void IncludeDefaultTypes()
@ -121,7 +121,7 @@ namespace Rokojori.DocGenerator
var fileContent = FilesSync.LoadUTF8( cf.fullPath ); var fileContent = FilesSync.LoadUTF8( cf.fullPath );
var definitions = CSharpLexer.GetAllObjectDefinitions( fileContent ); var definitions = CSharpLexer.GetAllObjectDefinitions( fileContent );
RJLog.Log( cf.fileName, ">>", definitions.Join( "," ) ); // RJLog.Log( cf.fileName, ">>", definitions.Join( "," ) );
for ( int i = 0; i < definitions.Count; i++ ) for ( int i = 0; i < definitions.Count; i++ )
{ {
@ -129,7 +129,7 @@ namespace Rokojori.DocGenerator
var definitionType = sequence[ 0 ]; var definitionType = sequence[ 0 ];
var definitionName = sequence[ 1 ]; var definitionName = sequence[ 1 ];
RJLog.Log( "Adding definition:", definitionName.match ); // RJLog.Log( "Adding definition:", definitionName.match );
AddCSharpsClassType( cf, definitionName.match, definitionType.match, namespaceLabel, assembly ); AddCSharpsClassType( cf, definitionName.match, definitionType.match, namespaceLabel, assembly );
} }
@ -140,7 +140,7 @@ namespace Rokojori.DocGenerator
shaderType.path = cf.absolutePath; shaderType.path = cf.absolutePath;
shaderTypes.Add( shaderType ); shaderTypes.Add( shaderType );
RJLog.Log( "SHADER: Adding definition:", cf.absolutePath ); // RJLog.Log( "SHADER: Adding definition:", cf.absolutePath );
} }
@ -163,11 +163,38 @@ namespace Rokojori.DocGenerator
return; return;
} }
if ( checkChangeTime )
{
var outputPath = ClassDocGenerator.GetOutputFilePath( this.outputPath, type );
// RJLog.Log( "Checking change times:", outputPath, cf.fullPath );
if ( FilesSync.FileExists( outputPath ) )
{
var outputModificationTime = FilesSync.GetModificationTime( outputPath );
var classModificationTime = FilesSync.GetModificationTime( cf.fullPath );
// RJLog.Log( "Change times are:", outputModificationTime, classModificationTime );
if ( outputModificationTime.IsNewerThan( classModificationTime ) )
{
// RJLog.Log( "File not changed:", type, cf.fullPath );
return;
}
}
else
{
}
}
var entry = new ClassTypeEntry(); var entry = new ClassTypeEntry();
entry.type = type; entry.type = type;
entry.path = cf.fullPath; entry.path = cf.fullPath;
entry.definitionType = definitionType; entry.definitionType = definitionType;
classTypes.Add( entry); classTypes.Add( entry);
} }
@ -181,28 +208,30 @@ namespace Rokojori.DocGenerator
{ {
var cdg = new ClassDocGenerator(); var cdg = new ClassDocGenerator();
var cinfo = cdg.Create( c.path, c.type, c.definitionType, icons ); var cinfo = cdg.Create( c.path, c.type, c.definitionType, icons );
var outputPathName = cinfo.name.Replace( "`", "-" ).Replace( "<", "-" ).Replace( ">", "-" ); var outputPath = ClassDocGenerator.GetOutputFilePath( this.outputPath, c.type );
// var outputPathName = cinfo.name.Replace( "`", "-" ).Replace( "<", "-" ).Replace( ">", "-" );
var outputPath = FilePath.Join( this.outputPath, outputPathName + ".json" ); // var outputPath = FilePath.Join( this.outputPath, outputPathName + ".json" );
FilesSync.SaveJSON( outputPath, cinfo ); FilesSync.SaveJSON( outputPath, cinfo );
RJLog.Log( "Updated", c.type.Name, c.path );
} }
); );
RJLog.Log( "SHADER: shaderTypes", shaderTypes.Count ); // RJLog.Log( "SHADER: shaderTypes", shaderTypes.Count );
shaderTypes.ForEach( shaderTypes.ForEach(
( s ) => ( s ) =>
{ {
try try
{ {
RJLog.Log( "SHADER: DOC", s.path ); // RJLog.Log( "SHADER: DOC", s.path, path );
var sdg = new ShaderDocGenerator(); var sdg = new ShaderDocGenerator();
var cinfo = sdg.Create( s.path ); var cinfo = sdg.Create( s.path, path );
var prefix = cinfo.definitionType + "." ; var prefix = cinfo.definitionType + "." ;
var outputPathName = prefix + cinfo.name; var outputPathName = prefix + cinfo.name;
var outputPath = FilePath.Join( this.outputPath, outputPathName + ".json" ); var outputPath = FilePath.Join( this.outputPath, outputPathName + ".json" );
RJLog.Log( "SHADER: SAVING", s.path, ">>", outputPathName ); // RJLog.Log( "SHADER: SAVING", s.path, ">>", outputPathName );
FilesSync.SaveJSON( outputPath, cinfo ); FilesSync.SaveJSON( outputPath, cinfo );
} }
catch ( System.Exception e ) catch ( System.Exception e )

View File

@ -5,11 +5,12 @@ using System.Diagnostics;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace Rokojori.Tools namespace Rokojori.Tools
{ {
public class OnlineDocs public class OnlineDocs
{ {
static string docsPath = "https://rokojori.com/en/labs/godot/docs/4.3/"; static string docsPath = "https://rokojori.com/en/labs/godot/docs/4.4/";
public static void Open( System.Type type ) public static void Open( System.Type type )
{ {
var name = type.Name + "-class"; var name = type.Name + "-class";

View File

@ -20,7 +20,8 @@ namespace Rokojori.DocGenerator
List<LexerEvent> _lexerEvents; List<LexerEvent> _lexerEvents;
LexerList tokens; LexerList tokens;
public ClassDocInfo Create( string filePath )
public ClassDocInfo Create( string filePath, string rootPath )
{ {
var fileContent = FilesSync.LoadUTF8( filePath ); var fileContent = FilesSync.LoadUTF8( filePath );
_lexerEvents = GDShaderLexer.Lex( fileContent ); _lexerEvents = GDShaderLexer.Lex( fileContent );
@ -36,6 +37,7 @@ namespace Rokojori.DocGenerator
} }
info.name = RegexUtility.TrimToLastPathFragment( filePath ).ReplaceEnd( GDShaderIncludeExtension, "" ).ReplaceEnd( GDShaderExtension, "" ); info.name = RegexUtility.TrimToLastPathFragment( filePath ).ReplaceEnd( GDShaderIncludeExtension, "" ).ReplaceEnd( GDShaderExtension, "" );
info.sourcePath = FilePath.Join( "Runtime", filePath.ReplaceStart( rootPath ) );
GetIncludes(); GetIncludes();
GetUniforms(); GetUniforms();
@ -49,7 +51,7 @@ namespace Rokojori.DocGenerator
{ {
var includePrexix = "#include "; var includePrexix = "#include ";
RJLog.Log( "Processing includes:", tokens.GetAll( LexerMatcherLibrary.CInstructionMatcher ).events.Count ); // RJLog.Log( "Processing includes:", tokens.GetAll( LexerMatcherLibrary.CInstructionMatcher ).events.Count );
tokens.ForEach( LexerMatcherLibrary.CInstructionMatcher, tokens.ForEach( LexerMatcherLibrary.CInstructionMatcher,
( t )=> ( t )=>
@ -57,11 +59,11 @@ namespace Rokojori.DocGenerator
var match = t.match; var match = t.match;
RJLog.Log( "Processing include:", match ); // RJLog.Log( "Processing include:", match );
if ( ! match.StartsWith( includePrexix ) ) if ( ! match.StartsWith( includePrexix ) )
{ {
RJLog.Log( "Processing include:", match ); // RJLog.Log( "Processing include:", match );
return; return;
} }

View File

@ -0,0 +1,29 @@
using System.Collections;
using System.Collections.Generic;
using Godot;
using System;
using System.Threading.Tasks;
namespace Rokojori.Tools
{
[Tool]
[GlobalClass]
public partial class ExportGLTF:Action
{
[Export]
public Node3D root;
[Export]
public string path;
protected override void _OnTrigger()
{
#if TOOLS
GLTFExport.Save( root, path );
#endif
}
}
}

View File

@ -0,0 +1 @@
uid://cbwdx0p5m7qoe