diff --git a/Runtime/Files/FilePath.cs b/Runtime/Files/FilePath.cs
index 9b9df97..dd8cf53 100644
--- a/Runtime/Files/FilePath.cs
+++ b/Runtime/Files/FilePath.cs
@@ -23,6 +23,23 @@ namespace Rokojori
public string _fileExtension;
string _fullPath = null;
+ Trillean isDirectory = Trillean.Any;
+
+ public bool Exists()
+ {
+ return File.Exists( fullPath );
+ }
+
+ public void MarkAsFile()
+ {
+ isDirectory = Trillean.False;
+ }
+
+ public void MarkAsDirectory()
+ {
+ isDirectory = Trillean.True;
+ }
+
/**Only fileName without fileExtension */
public string fileName
{
@@ -40,12 +57,12 @@ namespace Rokojori
/**Combines fileName + fileExtension */
public string fullFileName => fileName + fileExtension;
- /***/
+ /** File extension including the dot, eg. ".svg", ".jpg", ".js" */
public string fileExtension
{
get
{
- if ( _fileExtension == null)
+ if ( _fileExtension == null )
{
_fileExtension = Path.GetExtension( path );
}
@@ -92,12 +109,23 @@ namespace Rokojori
public static FilePath Create( string path, FilePathType type = FilePathType.Relative, FilePath parent = null )
{
+
var rp = new FilePath();
+ if ( path.EndsWith( "/" ) )
+ {
+ path = path.ReplaceEnd( "/" );
+ rp.isDirectory = Trillean.True;
+ }
+
+ path = path.ReplaceStart( "/" );
+
rp.type = type;
rp.parent = parent;
rp.path = path;
+
+
return rp;
}
@@ -169,18 +197,79 @@ namespace Rokojori
return FilePath.Create( path, FilePathType.Relative, this );
}
- public FilePath MakeAbsolutePathRelative( string absolutePath )
+
+ public FilePath MakeAbsolutePathRelative( string otherAbsolutePath )
{
- absolutePath = FilePath.Normalize( absolutePath );
+ otherAbsolutePath = FilePath.Normalize( otherAbsolutePath );
var ownAbsolutePath = FilePath.Normalize( fullPath );
- if ( ! absolutePath.StartsWith( ownAbsolutePath ) )
+ if ( ! otherAbsolutePath.StartsWith( ownAbsolutePath ) )
{
- return null;
+ // A/B/C/D.x => own
+ // A/B/E/F.x => other
+
+ // A/B => common
+
+ // E/F => common relative
+ // /.. => own to common
+ // /../E/F => own to other
+
+ var commonPath = ownAbsolutePath.ExtractCommonStart( otherAbsolutePath );
+ var commonFilePath = FilePath.Absolute( commonPath );
+ var commonRelativeFilePath = commonFilePath.MakeAbsolutePathRelative( otherAbsolutePath );
+
+
+ var ownIsDir = Trillean.True == isDirectory || Trillean.False != isDirectory && fileExtension == "";
+
+ var itPath = ownAbsolutePath;
+
+ if ( ! ownIsDir )
+ {
+ itPath = absoluteParentPath;
+ }
+
+ itPath = itPath.ReplaceStart( commonPath );
+ var splits = RegexUtility.SplitPaths( itPath );
+
+ var dirs = "";
+
+ for ( int i = 0; i < splits.Count; i++ )
+ {
+ if ( i != 0 )
+ {
+ dirs += "/" ;
+ }
+
+ dirs += "..";
+ }
+
+ if ( ! commonRelativeFilePath.path.StartsWith( "/" ) )
+ {
+ dirs += "/";
+ }
+
+ // RJLog.Log(
+ // "\nown:", ownAbsolutePath,
+ // "\nother:", otherAbsolutePath,
+ // "\ncommonPath:", commonPath,
+ // "\ncommonRelativeFilePath:", commonRelativeFilePath.fullPath,
+ // "\ncommonRelativeFilePath.path:", commonRelativeFilePath.path,
+ // "\ndirs", dirs,
+ // "\nRelative:", dirs + commonRelativeFilePath.path
+ // );
+
+
+
+ return MakeRelative( dirs + commonRelativeFilePath.path );
}
- var relativePath = absolutePath.Substring( ownAbsolutePath.Length );
+ // A/B/C
+ // A/B/C/D
+ var relativePath = otherAbsolutePath.Substring( ownAbsolutePath.Length );
+ relativePath = relativePath.ReplaceStart( "/" );
+
+
return MakeRelative( relativePath );
}
diff --git a/Runtime/Files/FilesSync.cs b/Runtime/Files/FilesSync.cs
index db37ede..3da4c55 100644
--- a/Runtime/Files/FilesSync.cs
+++ b/Runtime/Files/FilesSync.cs
@@ -9,6 +9,18 @@ namespace Rokojori
{
public class FilesSync
{
+ public static void Move( string fromPath, string toPath )
+ {
+ string? destinationDirectory = Path.GetDirectoryName( toPath );
+
+ if ( ! string.IsNullOrEmpty( destinationDirectory ) && ! Directory.Exists( destinationDirectory ) )
+ {
+ Directory.CreateDirectory(destinationDirectory);
+ }
+
+ File.Move( fromPath, toPath );
+ }
+
public static void Delete( string path )
{
if ( string.IsNullOrWhiteSpace( path ) )
@@ -47,6 +59,11 @@ namespace Rokojori
Directory.CreateDirectory( path );
}
+ public static void EnsureParentDirectoryExists( string path )
+ {
+ EnsureDirectoryExists( RegexUtility.ParentPath( path ) );
+ }
+
public static void EnsureDirectoryExists( string path )
{
if ( DirectoryExists( path ) )
diff --git a/Runtime/Godot/Nodes.cs b/Runtime/Godot/Nodes.cs
index 26bb136..e24b465 100644
--- a/Runtime/Godot/Nodes.cs
+++ b/Runtime/Godot/Nodes.cs
@@ -4,6 +4,7 @@ using System.Collections.Generic;
using System;
using System.Threading.Tasks;
using System.Reflection;
+using Microsoft.VisualBasic;
namespace Rokojori
{
@@ -29,8 +30,21 @@ namespace Rokojori
{
var memberInfos = ReflectionHelper.GetDataMemberInfos( BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly );
+ memberInfos = memberInfos.Filter(
+ ( mi ) =>
+ {
+ if ( mi.IsMemberType() )
+ {
+ return false;
+ }
+
+ return true;
+ }
+ );
var memberNames = Lists.Map( memberInfos, m => m.Name );
+ // RJLog.Log( "Trying to copy:", memberNames );
+
ReflectionHelper.CopyDataMembersFromTo( from, to, memberNames );
}
diff --git a/Runtime/Procedural/Assets/Grass/GrassPatch.cs b/Runtime/Procedural/Assets/Grass/GrassPatch.cs
index 2a12ce3..a1e1efe 100644
--- a/Runtime/Procedural/Assets/Grass/GrassPatch.cs
+++ b/Runtime/Procedural/Assets/Grass/GrassPatch.cs
@@ -407,8 +407,10 @@ namespace Rokojori
return bladeSegments * 2 * ComputeNumBlades();
}
- public async Task CreatePatch()
+ public async Task CreatePatch( string patchName = null )
{
+ RJLog.Log( "Create Patch:", patchName );
+
if ( blades == 0 && bladesX == 0 && bladesZ == 0)
{
return;
@@ -433,7 +435,7 @@ namespace Rokojori
this.DestroyChildren();
- this.output = this.CreateChild( "Grass Patch Mesh" );
+ this.output = this.CreateChild( patchName );
@@ -452,6 +454,8 @@ namespace Rokojori
X_numTriangles = mg.indices.Count / 3;
output.Mesh = mg.GenerateMesh();
output.Mesh.SurfaceSetMaterial( 0, material );
+
+ output.Name = patchName;
}
else
{
@@ -510,6 +514,8 @@ namespace Rokojori
output.Mesh = MeshGeometry.GenerateTrianglesLODMesh( mgs, lerpingData, false );
output.Mesh.SurfaceSetMaterial( 0, material );
+ output.Name = patchName;
+
}
catch ( System.Exception e )
diff --git a/Runtime/Procedural/Baking/MultiBaker/MultiBaker.cs b/Runtime/Procedural/Baking/MultiBaker/MultiBaker.cs
index c019893..9781784 100644
--- a/Runtime/Procedural/Baking/MultiBaker/MultiBaker.cs
+++ b/Runtime/Procedural/Baking/MultiBaker/MultiBaker.cs
@@ -549,14 +549,16 @@ namespace Rokojori
var fd = fovDistance.ComputeFOVDistance( size / 2f );
- _cameraFOV = fd.X;
+ _cameraFOV = Mathf.Clamp( fd.X, 1, 179 );
_cameraDistance = fd.Y;
_outputScale = computeScale ? Cameras.ComputeCameraFittingScale( _cameraFOV, _cameraDistance ) : 1;
+
+ RJLog.Log( "Computed FOV Distance", _cameraFOV, _cameraDistance );
}
public float GetCameraFOV()
{
- return _cameraFOV;
+ return Mathf.Clamp( _cameraFOV, 1, 179 );
}
public float GetCameraDistance()
diff --git a/Runtime/Text/JSON/JSON.cs b/Runtime/Text/JSON/JSON.cs
index 5612e1a..348ec57 100644
--- a/Runtime/Text/JSON/JSON.cs
+++ b/Runtime/Text/JSON/JSON.cs
@@ -14,11 +14,21 @@ namespace Rokojori
return jsonData.Stringify();
}
+ public static void Save( string path, JSONData data )
+ {
+ FilesSync.SaveUTF8( path, Stringify( data ) );
+ }
+
public static JSONData Parse( string jsonString )
{
return new JSONParser().Parse( jsonString );
}
+ public static JSONData Load( string path )
+ {
+ return JSON.Parse( FilesSync.LoadUTF8( path ) );
+ }
+
public static string StringifyObject( object value )
{
var serializer = new JSONSerializer( new JSONSerializationSettings() );
diff --git a/Runtime/Text/Text.cs b/Runtime/Text/Text.cs
index aa3d984..117bd18 100644
--- a/Runtime/Text/Text.cs
+++ b/Runtime/Text/Text.cs
@@ -61,6 +61,26 @@ namespace Rokojori
return source;
}
+
+ public static string ExtractCommonStart( this string source, string other )
+ {
+ for ( int i = 0; i < source.Length && i < other.Length; i++ )
+ {
+ if ( source[ i ] != other[ i ] )
+ {
+ return source.Substring( 0, i );
+ }
+ }
+
+ if ( source.Length < other.Length )
+ {
+ return source;
+ }
+
+ return other;
+ }
+
+
}
diff --git a/Runtime/Tools/ReflectionHelper.cs b/Runtime/Tools/ReflectionHelper.cs
index 26fd282..1be7002 100644
--- a/Runtime/Tools/ReflectionHelper.cs
+++ b/Runtime/Tools/ReflectionHelper.cs
@@ -7,7 +7,7 @@ using Godot;
namespace Rokojori
{
- public class ReflectionHelper
+ public static class ReflectionHelper
{
public static T Create( Type type, params object[] args )
{
@@ -364,8 +364,29 @@ namespace Rokojori
public static bool HasDataMember( object instance, string memberName, BindingFlags flags = ReflectionHelper.defaultBindings )
{
return GetDataMemberInfo( instance, memberName, flags ) != null;
+ }
+
+ public static Type GetMemberType( this MemberInfo memberInfo )
+ {
+ if ( memberInfo is FieldInfo fi )
+ {
+ return fi.FieldType;
+ }
+
+ if ( memberInfo is PropertyInfo pi )
+ {
+ return pi.PropertyType;
+ }
+
+ return null;
}
+ public static bool IsMemberType( this MemberInfo memberInfo )
+ {
+ return GetMemberType( memberInfo ) == typeof( T );
+ }
+
+
public static T GetDataMemberValue( object instance, MemberInfo info )
{
diff --git a/Tools/gltf-export/GLTFExport.cs b/Tools/gltf-export/GLTFExport.cs
index 5a968a8..6f5024b 100644
--- a/Tools/gltf-export/GLTFExport.cs
+++ b/Tools/gltf-export/GLTFExport.cs
@@ -11,13 +11,94 @@ namespace Rokojori.Tools
{
public class GLTFExport
{
- public static void Save( Node3D node, string path, string temporaryPath = "res://--temporary--" )
- {
+ static string temporaryPath = "res://.rokojori/cache/temp";
+
+ public static void Save( Node3D node, string path, string texturesPath = null, string binPath = null )
+ {
+
+ FilesSync.EnsureParentDirectoryExists( path );
+
+ if ( binPath != null )
+ {
+ FilesSync.EnsureDirectoryExists( binPath );
+ }
+
+ if ( texturesPath != null )
+ {
+ FilesSync.EnsureDirectoryExists( texturesPath );
+ }
+
+
var state = new GltfState();
var doc = new GltfDocument();
doc.AppendFromScene( node, state );
- doc.WriteToFilesystem( state, path );
+
+ if ( texturesPath == null )
+ {
+ doc.WriteToFilesystem( state, path );
+ return;
+ }
+
+ var gltfFilePath = FilePath.Absolute( path ); // Defines file name and gltfPath;
+ var gltfParentFilePath = gltfFilePath.CreateAbsoluteParent();
+ var texturesDirPath = FilePath.Absolute( texturesPath ); // Only directory for textures
+ var binDirPath = FilePath.Absolute( binPath ); // Only directory for bins, name comes from gltf.
+
+ var temporaryFilePath = FilePath.Create( ProjectSettings.GlobalizePath( temporaryPath ) + "/" + gltfFilePath.fullFileName );
+ var temporaryDirPath = temporaryFilePath.CreateAbsoluteParent();
+
+ doc.WriteToFilesystem( state, temporaryFilePath.fullPath );
+
+ var earlyOut = false;
+
+ if ( earlyOut )
+ {
+ return;
+ }
+
+ var json = JSON.Load( temporaryFilePath.fullPath ).AsObject();
+
+ var images = json.GetArray( "images" );
+
+ for ( int i = 0; images != null && i < images.size; i++ )
+ {
+ var image = images.Get( i ).AsObject();
+ var uri = image.GetString( "uri" ).Replace( "%2F", "/" );
+
+ var textureFilePath = temporaryDirPath.MakeRelative( uri );
+ var newTexturesFilePath = texturesDirPath.MakeRelative( textureFilePath.fullFileName );
+ var newRelativeFilePath = gltfParentFilePath.MakeAbsolutePathRelative( newTexturesFilePath.fullPath );
+
+
+ // RJLog.Log( "Moving image", i, uri, newRelativeFilePath.path,"\n" + textureFilePath.fullPath, "\n" + newRelativeFilePath.fullPath );
+ image.Set( "uri", newRelativeFilePath.path.Replace( "/", "%2F" ) );
+
+ if ( ! newRelativeFilePath.Exists() )
+ {
+ FilesSync.Move( textureFilePath.fullPath, newTexturesFilePath.fullPath );
+ }
+
+ }
+
+ var buffers = json.GetArray( "buffers" );
+
+ for ( int i = 0; buffers != null && i < buffers.size; i++ )
+ {
+ var buffer = buffers.Get( i ).AsObject();
+ var uri = buffer.GetString( "uri" ).Replace( "%2F", "/" );
+
+ var bufferFilePath = temporaryDirPath.MakeRelative( uri );
+ var newBufferFilePath = binDirPath.MakeRelative( gltfFilePath.WithExtension( ".bin" ).fullFileName );
+ var newRelativeFilePath = gltfParentFilePath.MakeAbsolutePathRelative( newBufferFilePath.fullPath );
+
+ // RJLog.Log( "Moving buffer", i, uri, newRelativeFilePath.path, bufferFilePath.fullPath, newRelativeFilePath.fullPath );
+ buffer.Set( "uri", newRelativeFilePath.path.Replace( "/", "%2F" ) );
+ FilesSync.Move( bufferFilePath.fullPath, newBufferFilePath.fullPath );
+ }
+
+ JSON.Save( gltfFilePath.fullPath, json );
+ FilesSync.Delete( temporaryFilePath.fullPath );
return;
}
diff --git a/Tools/gltf-export/gltf-file/GLTFAssetInfo.cs b/Tools/gltf-export/gltf-file/GLTFAssetInfo.cs
new file mode 100644
index 0000000..309cafe
--- /dev/null
+++ b/Tools/gltf-export/gltf-file/GLTFAssetInfo.cs
@@ -0,0 +1,18 @@
+using System.Collections;
+using System.Collections.Generic;
+using Godot;
+using System;
+using System.Threading.Tasks;
+
+
+
+
+namespace Rokojori.Tools
+{
+ public class GLTFAssetInfo
+ {
+ public string generator;
+ public string version;
+
+ }
+}
diff --git a/Tools/gltf-export/gltf-file/GLTFAssetInfo.cs.uid b/Tools/gltf-export/gltf-file/GLTFAssetInfo.cs.uid
new file mode 100644
index 0000000..8d33b23
--- /dev/null
+++ b/Tools/gltf-export/gltf-file/GLTFAssetInfo.cs.uid
@@ -0,0 +1 @@
+uid://4cwq20m8ihng
diff --git a/Tools/gltf-export/gltf-file/GLTFBufferReference.cs b/Tools/gltf-export/gltf-file/GLTFBufferReference.cs
new file mode 100644
index 0000000..e78b2df
--- /dev/null
+++ b/Tools/gltf-export/gltf-file/GLTFBufferReference.cs
@@ -0,0 +1,17 @@
+using System.Collections;
+using System.Collections.Generic;
+using Godot;
+using System;
+using System.Threading.Tasks;
+
+
+
+
+namespace Rokojori.Tools
+{
+ public class GLTFBufferReference
+ {
+ public int byteLength;
+ public string uri;
+ }
+}
diff --git a/Tools/gltf-export/gltf-file/GLTFBufferReference.cs.uid b/Tools/gltf-export/gltf-file/GLTFBufferReference.cs.uid
new file mode 100644
index 0000000..c4384ae
--- /dev/null
+++ b/Tools/gltf-export/gltf-file/GLTFBufferReference.cs.uid
@@ -0,0 +1 @@
+uid://ctku4smqws1lv
diff --git a/Tools/gltf-export/gltf-file/GLTFFile.cs b/Tools/gltf-export/gltf-file/GLTFFile.cs
new file mode 100644
index 0000000..af528fb
--- /dev/null
+++ b/Tools/gltf-export/gltf-file/GLTFFile.cs
@@ -0,0 +1,18 @@
+using System.Collections;
+using System.Collections.Generic;
+using Godot;
+using System;
+using System.Threading.Tasks;
+
+
+
+
+namespace Rokojori.Tools
+{
+ public class GLTFFile
+ {
+ public GLTFAssetInfo asset;
+ public List buffers;
+ public List images;
+ }
+}
diff --git a/Tools/gltf-export/gltf-file/GLTFFile.cs.uid b/Tools/gltf-export/gltf-file/GLTFFile.cs.uid
new file mode 100644
index 0000000..05c4b6f
--- /dev/null
+++ b/Tools/gltf-export/gltf-file/GLTFFile.cs.uid
@@ -0,0 +1 @@
+uid://dvu7j1ysw4yw7
diff --git a/Tools/gltf-export/gltf-file/GLTFImageReference.cs b/Tools/gltf-export/gltf-file/GLTFImageReference.cs
new file mode 100644
index 0000000..5e4bb95
--- /dev/null
+++ b/Tools/gltf-export/gltf-file/GLTFImageReference.cs
@@ -0,0 +1,16 @@
+using System.Collections;
+using System.Collections.Generic;
+using Godot;
+using System;
+using System.Threading.Tasks;
+
+
+
+
+namespace Rokojori.Tools
+{
+ public class GLTFImageReference
+ {
+ public string uri;
+ }
+}
diff --git a/Tools/gltf-export/gltf-file/GLTFImageReference.cs.uid b/Tools/gltf-export/gltf-file/GLTFImageReference.cs.uid
new file mode 100644
index 0000000..f2cec59
--- /dev/null
+++ b/Tools/gltf-export/gltf-file/GLTFImageReference.cs.uid
@@ -0,0 +1 @@
+uid://bgwmjjymklkrj