Reallusion Importer/Shaders, Native LODs, Selectors

This commit is contained in:
Josef 2025-06-10 15:16:36 +02:00
parent f025656b97
commit 8520186939
313 changed files with 13160 additions and 341 deletions

View File

@ -0,0 +1,97 @@
<?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="RemoveVirtualCamera3D.svg"
inkscape:version="1.2.2 (732a01da63, 2022-12-09)"
xml:space="preserve"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"><defs
id="defs8"><linearGradient
inkscape:collect="never"
id="linearGradient3074"><stop
style="stop-color:#e26708;stop-opacity:1;"
offset="0"
id="stop3070" /><stop
style="stop-color:#bb3c00;stop-opacity:1;"
offset="1"
id="stop3072" /></linearGradient><radialGradient
xlink:href="#linearGradient45008"
id="radialGradient3076"
cx="30.688875"
cy="30.069115"
fx="30.688875"
fy="30.069115"
r="14.05412"
gradientUnits="userSpaceOnUse" /><linearGradient
xlink:href="#linearGradient45008"
id="linearGradient45010"
x1="-31.87768"
y1="22.065159"
x2="-31.87768"
y2="48.78738"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(101.16951,-6.5921995)" /><linearGradient
id="linearGradient45008"><stop
style="stop-color:#e14500;stop-opacity:1;"
offset="0"
id="stop45004" /><stop
style="stop-color:#e17900;stop-opacity:1;"
offset="0.59811592"
id="stop45012" /><stop
style="stop-color:#e19c00;stop-opacity:1;"
offset="1"
id="stop45006" /></linearGradient><linearGradient
xlink:href="#linearGradient45008"
id="linearGradient46715"
x1="31.917692"
y1="47.524929"
x2="31.917692"
y2="22.632998"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(1.7923447e-6)" /></defs><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="8.0000004"
inkscape:cx="-8.8124996"
inkscape:cy="-12.687499"
inkscape:window-width="1920"
inkscape:window-height="1017"
inkscape:window-x="-8"
inkscape:window-y="-8"
inkscape:window-maximized="1"
inkscape:current-layer="g2210"
showguides="false" /><g
id="g2210"
transform="matrix(0.54328517,0,0,0.54328517,-9.4489315,-11.300948)"><path
fill="#fc7f7f"
id="path66330"
style="fill:#f78500;fill-opacity:1;stroke:none;stroke-width:1.65067;stroke-dasharray:3.30134, 1.65067;stroke-dashoffset:0;stroke-opacity:1"
d="m 34.253693,25.773391 a 4.9507597,4.9507597 0 0 0 -4.951825,4.581599 4.9507597,4.9507597 0 1 0 -4.952696,8.327678 l -8.37e-4,3.59095 a 1.6502534,1.6502534 0 0 0 1.649872,1.650636 l 9.901519,0.0022 a 1.6502534,1.6502534 0 0 0 1.650638,-1.649869 l 3.84e-4,-1.650254 4.949993,3.301659 0.0022,-9.901519 -4.951529,3.299355 6.78e-4,-2.920952 a 4.9507597,4.9507597 0 0 0 -3.298458,-8.631545 z" /><g
id="g15102"
style="stroke:#f00b00;stroke-opacity:1"><path
style="fill:none;fill-opacity:1;stroke:#f00b00;stroke-width:3.68131;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:markers stroke fill"
d="m 20.72898,24.341266 22.77693,22.3702"
id="path14277"
sodipodi:nodetypes="cc" /><path
style="fill:none;fill-opacity:1;stroke:#f00b00;stroke-width:3.68131;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:markers stroke fill"
d="m 43.50591,24.341266 -22.77693,22.3702"
id="path15098"
sodipodi:nodetypes="cc" /></g><path
fill="#fc7f7f"
id="path15104"
style="fill:#f78500;fill-opacity:0.68461537;stroke:none;stroke-width:1.65067;stroke-dasharray:3.30134, 1.65067;stroke-dashoffset:0;stroke-opacity:1"
d="m 34.253693,25.773391 a 4.9507597,4.9507597 0 0 0 -4.951825,4.581599 4.9507597,4.9507597 0 1 0 -4.952696,8.327678 l -8.37e-4,3.59095 a 1.6502534,1.6502534 0 0 0 1.649872,1.650636 l 9.901519,0.0022 a 1.6502534,1.6502534 0 0 0 1.650638,-1.649869 l 3.84e-4,-1.650254 4.949993,3.301659 0.0022,-9.901519 -4.951529,3.299355 6.78e-4,-2.920952 a 4.9507597,4.9507597 0 0 0 -3.298458,-8.631545 z" /></g></svg>

After

Width:  |  Height:  |  Size: 4.5 KiB

View File

@ -0,0 +1,37 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://beh11ebwsi3nj"
path="res://.godot/imported/RemoveVirtualCamera3D.svg-b229129ca4dd411d22a781c703e04259.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://addons/rokojori_action_library/Icons/RemoveVirtualCamera3D.svg"
dest_files=["res://.godot/imported/RemoveVirtualCamera3D.svg-b229129ca4dd411d22a781c703e04259.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

@ -0,0 +1,101 @@
<?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="SetActiveVirtualCamera3D.svg"
inkscape:version="1.2.2 (732a01da63, 2022-12-09)"
xml:space="preserve"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"><defs
id="defs8"><linearGradient
inkscape:collect="never"
id="linearGradient3074"><stop
style="stop-color:#e26708;stop-opacity:1;"
offset="0"
id="stop3070" /><stop
style="stop-color:#bb3c00;stop-opacity:1;"
offset="1"
id="stop3072" /></linearGradient><radialGradient
xlink:href="#linearGradient45008"
id="radialGradient3076"
cx="30.688875"
cy="30.069115"
fx="30.688875"
fy="30.069115"
r="14.05412"
gradientUnits="userSpaceOnUse" /><linearGradient
xlink:href="#linearGradient45008"
id="linearGradient45010"
x1="-31.87768"
y1="22.065159"
x2="-31.87768"
y2="48.78738"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(101.16951,-6.5921995)" /><linearGradient
id="linearGradient45008"><stop
style="stop-color:#e14500;stop-opacity:1;"
offset="0"
id="stop45004" /><stop
style="stop-color:#e17900;stop-opacity:1;"
offset="0.59811592"
id="stop45012" /><stop
style="stop-color:#e19c00;stop-opacity:1;"
offset="1"
id="stop45006" /></linearGradient><linearGradient
xlink:href="#linearGradient45008"
id="linearGradient46715"
x1="31.917692"
y1="47.524929"
x2="31.917692"
y2="22.632998"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(1.7923447e-6)" /></defs><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="22.627418"
inkscape:cx="12.308077"
inkscape:cy="11.203223"
inkscape:window-width="1920"
inkscape:window-height="1017"
inkscape:window-x="-8"
inkscape:window-y="-8"
inkscape:window-maximized="1"
inkscape:current-layer="g2210" /><g
id="g2210"
transform="matrix(0.54328517,0,0,0.54328517,-9.4489315,-11.300948)"><path
d="m 37.699583,28.035024 a 3.6424849,3.6424849 0 0 0 -3.642486,3.371726 3.6424849,3.6424849 0 1 0 -3.642484,6.127874 v 2.642016 a 1.2141617,1.2141617 0 0 0 1.214163,1.21416 h 7.284971 a 1.2141617,1.2141617 0 0 0 1.21416,-1.21416 v -1.214163 l 3.642488,2.428323 v -7.284969 l -3.642488,2.428323 v -2.149068 a 3.6424849,3.6424849 0 0 0 -2.428324,-6.350062 z"
fill="#fc7f7f"
id="path66330"
style="fill:#f78500;fill-opacity:1;stroke:none;stroke-width:1.21414;stroke-opacity:1" /><path
id="rect401"
style="color:#000000;fill:#f7b200;fill-opacity:1;stroke-width:1.84065;stroke-linecap:round;stroke-linejoin:round;-inkscape-stroke:none;paint-order:markers stroke fill"
d="m 19.840427,20.92696 c -1.240775,0 -2.297223,1.052853 -2.297223,2.293627 v 4.623206 h 3.681308 v -3.235525 h 3.21036 V 20.92696 Z m 19.959591,0 v 3.681308 h 3.249905 v 3.235525 h 3.681308 v -4.623206 c 0,-1.240774 -1.056447,-2.293627 -2.297223,-2.293627 z M 17.543204,43.208939 v 4.544115 c 0,1.240774 1.056448,2.297222 2.297223,2.297222 h 4.594445 v -3.681308 h -3.21036 v -3.160029 z m 25.506719,0 v 3.160029 h -3.249905 v 3.681308 h 4.63399 c 1.240776,0 2.297223,-1.056448 2.297223,-2.297222 v -4.544115 z" /><path
sodipodi:type="star"
style="fill:#f7b200;fill-opacity:1;stroke-width:3.68131;stroke-linecap:round;stroke-linejoin:round;paint-order:markers stroke fill"
id="path9403"
inkscape:flatsided="true"
sodipodi:sides="3"
sodipodi:cx="-5.8778248"
sodipodi:cy="-1.4584076"
sodipodi:r1="3.1847413"
sodipodi:r2="1.5923707"
sodipodi:arg1="0"
sodipodi:arg2="1.0471976"
inkscape:rounded="0"
inkscape:randomized="0"
d="m -2.6930835,-1.4584076 -4.7771119,2.7580668 0,-5.5161337 z"
transform="matrix(1.3392575,0,0,1.3392575,30.388942,37.803361)"
inkscape:transform-center-x="-0.5793031" /></g></svg>

After

Width:  |  Height:  |  Size: 4.5 KiB

View File

@ -0,0 +1,37 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://cwtmomxhbrhh4"
path="res://.godot/imported/SetActiveVirtualCamera3D.svg-daa089c09a78ae1c001e1e1ec4c40af0.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://addons/rokojori_action_library/Icons/SetActiveVirtualCamera3D.svg"
dest_files=["res://.godot/imported/SetActiveVirtualCamera3D.svg-daa089c09a78ae1c001e1e1ec4c40af0.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

@ -66,8 +66,8 @@
inkscape:deskcolor="#505050" inkscape:deskcolor="#505050"
showgrid="false" showgrid="false"
inkscape:zoom="22.627418" inkscape:zoom="22.627418"
inkscape:cx="8.1538247" inkscape:cx="12.308077"
inkscape:cy="2.320194" inkscape:cy="11.203223"
inkscape:window-width="1920" inkscape:window-width="1920"
inkscape:window-height="1017" inkscape:window-height="1017"
inkscape:window-x="-8" inkscape:window-x="-8"
@ -76,7 +76,7 @@
inkscape:current-layer="g2210" /><g inkscape:current-layer="g2210" /><g
id="g2210" id="g2210"
transform="matrix(0.54328517,0,0,0.54328517,-9.4489315,-11.300948)"><path transform="matrix(0.54328517,0,0,0.54328517,-9.4489315,-11.300948)"><path
d="m 35.176642,24.424961 a 6.0553193,6.0553193 0 0 0 -6.05532,5.605205 6.0553193,6.0553193 0 1 0 -6.055318,10.187067 v 4.392124 a 2.0184399,2.0184399 0 0 0 2.018441,2.018439 h 12.11064 a 2.0184399,2.0184399 0 0 0 2.018439,-2.018439 v -2.018441 l 6.055322,4.03688 V 34.517157 l -6.055322,4.036879 V 34.981395 A 6.0553193,6.0553193 0 0 0 35.176642,24.424961 Z" d="m 34.728314,26.051881 a 5.1679073,5.1679073 0 0 0 -5.167908,4.783758 5.1679073,5.1679073 0 1 0 -5.167906,8.694144 v 3.748454 a 1.7226359,1.7226359 0 0 0 1.722637,1.722635 h 10.335816 a 1.7226359,1.7226359 0 0 0 1.722635,-1.722635 v -1.722636 l 5.167909,3.445271 V 34.665058 l -5.167909,3.445271 v -3.049068 a 5.1679073,5.1679073 0 0 0 -3.445274,-9.00938 z"
fill="#fc7f7f" fill="#fc7f7f"
id="path66330" id="path66330"
style="fill:#f78500;fill-opacity:1;stroke:none;stroke-width:2.01842;stroke-opacity:1" /></g></svg> style="fill:#f78500;fill-opacity:1;stroke:none;stroke-width:1.72262;stroke-opacity:1" /></g></svg>

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

View File

@ -4,7 +4,7 @@ using Godot;
namespace Rokojori namespace Rokojori
{ {
[GlobalClass ] [Tool][GlobalClass ]
public partial class ActionReference : Action public partial class ActionReference : Action
{ {
[Export] [Export]

View File

@ -0,0 +1,30 @@
using Godot;
using System.Collections.Generic;
namespace Rokojori
{
[Tool]
[GlobalClass, Icon("res://addons/rokojori_action_library/Icons/ConditionalAction.svg")]
public partial class Once : Action
{
[Export]
public Action action;
[Export]
public bool canTrigger = true;
protected override void _OnTrigger()
{
if ( ! canTrigger )
{
return;
}
canTrigger = false;
Action.Trigger( action );
}
}
}

View File

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

View File

@ -4,7 +4,7 @@ using Godot;
namespace Rokojori namespace Rokojori
{ {
[GlobalClass ] [Tool][GlobalClass ]
public partial class IterateActions : Action public partial class IterateActions : Action
{ {
[ExportGroup( "Read Only")] [ExportGroup( "Read Only")]

View File

@ -4,7 +4,7 @@ using System.Collections.Generic;
namespace Rokojori namespace Rokojori
{ {
[GlobalClass] [Tool][GlobalClass]
public partial class LoadScene : SequenceAction public partial class LoadScene : SequenceAction
{ {
[Export] [Export]

View File

@ -4,7 +4,7 @@ using Godot;
namespace Rokojori namespace Rokojori
{ {
[GlobalClass ] [Tool][GlobalClass ]
public partial class SetNodeState : Action public partial class SetNodeState : Action
{ {
[Export] [Export]

View File

@ -4,7 +4,7 @@ using Godot;
namespace Rokojori namespace Rokojori
{ {
[GlobalClass ] [Tool][GlobalClass ]
public partial class CopyMousePosition : Action public partial class CopyMousePosition : Action
{ {
[Export] [Export]

View File

@ -4,7 +4,7 @@ using Godot;
namespace Rokojori namespace Rokojori
{ {
[GlobalClass, Tool ] [Tool][GlobalClass ]
public partial class CopyPose : Action public partial class CopyPose : Action
{ {
[Export] [Export]

View File

@ -3,7 +3,7 @@ using Godot;
namespace Rokojori namespace Rokojori
{ {
[GlobalClass ] [Tool][GlobalClass ]
public partial class LerpPosition : Action public partial class LerpPosition : Action
{ {
[Export] [Export]

View File

@ -3,7 +3,7 @@ using Godot;
namespace Rokojori namespace Rokojori
{ {
[GlobalClass] [Tool][GlobalClass]
public partial class PlayParticles:Action public partial class PlayParticles:Action
{ {
[Export] [Export]

View File

@ -3,7 +3,7 @@ using Godot;
namespace Rokojori namespace Rokojori
{ {
[GlobalClass] [Tool][GlobalClass]
public partial class PlaySound:Action public partial class PlaySound:Action
{ {
[Export] [Export]

View File

@ -4,7 +4,7 @@ using Godot;
namespace Rokojori namespace Rokojori
{ {
[GlobalClass, Icon("res://addons/rokojori_action_library/Icons/OnEvent.svg") ] [Tool][GlobalClass, Icon("res://addons/rokojori_action_library/Icons/OnEvent.svg") ]
public partial class OnPhysicsProcess : Node public partial class OnPhysicsProcess : Node
{ {
/** <summary for="field actions">Actions to execute</summary>*/ /** <summary for="field actions">Actions to execute</summary>*/

View File

@ -4,7 +4,7 @@ using Godot;
namespace Rokojori namespace Rokojori
{ {
[GlobalClass, Icon("res://addons/rokojori_action_library/Icons/OnEvent.svg") ] [Tool][GlobalClass, Icon("res://addons/rokojori_action_library/Icons/OnEvent.svg") ]
public partial class OnReady : Node public partial class OnReady : Node
{ {
/** <summary for="field actions">Actions to execute</summary>*/ /** <summary for="field actions">Actions to execute</summary>*/

View File

@ -4,7 +4,7 @@ using System.Collections.Generic;
namespace Rokojori namespace Rokojori
{ {
[GlobalClass, Icon("res://addons/rokojori_action_library/Icons/Parallel.svg") ] [Tool][GlobalClass, Icon("res://addons/rokojori_action_library/Icons/Parallel.svg") ]
public partial class Parallel : SequenceAction public partial class Parallel : SequenceAction
{ {
public enum Mode public enum Mode

View File

@ -0,0 +1,87 @@
using Godot;
using System.Collections.Generic;
namespace Rokojori
{
[Tool][GlobalClass, Icon("res://addons/rokojori_action_library/Icons/Parallel.svg") ]
public partial class RepeatSequence : SequenceAction
{
[Export]
public Action action;
[Export]
public int maxNumRepeats = 3;
[Export]
public float maxRepeatDuration = 60;
[Export]
public TimeLine timeLine;
protected override void _OnTrigger()
{
var id = DispatchStart();
if ( ! ( action is SequenceAction ) )
{
Trigger( action );
DispatchEnd( id );
return;
}
var sa = (SequenceAction) action;
var executed = 0;
System.Action<SequenceActionFinishedEvent> callBack = null;
var tl = TimeLineManager.Ensure( timeLine );
if ( maxNumRepeats <= 0 && ( tl == null || maxRepeatDuration <= 0 ) )
{
return;
}
var startTime = tl.position;
var endTime = tl.position + maxRepeatDuration;
callBack = ( a )=>
{
if ( ! a.success )
{
DispatchCancelled( id );
sa.onSequenceDone.RemoveAction( callBack );
return;
}
executed ++;
var finished = ( maxNumRepeats > 0 && executed >= maxNumRepeats ) ||
( tl != null && tl.position >= endTime );
if ( finished )
{
DispatchEnd( id );
sa.onSequenceDone.RemoveAction( callBack );
return;
}
else
{
Trigger( action );
}
};
sa.onSequenceDone.AddAction( callBack );
Trigger( action );
}
}
}

View File

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

View File

@ -4,7 +4,7 @@ using Godot;
namespace Rokojori namespace Rokojori
{ {
[GlobalClass] [Tool][GlobalClass]
public partial class Delay : SequenceAction public partial class Delay : SequenceAction
{ {
[Export] [Export]

View File

@ -0,0 +1,77 @@
using System;
using Godot;
namespace Rokojori
{
[Tool]
[GlobalClass ]
public partial class TweenTimeLineSpeed:SequenceAction
{
[Export]
public TimeLine targetTimeLine;
[Export]
public float timeLineSpeed;
[Export]
public TweenType tweenType = new TweenTimeCurve();
[Export]
public bool cacheSpeedOnStart = true;
[Export]
public TimeLine tweenTimeLine;
protected override void _OnTrigger()
{
if ( targetTimeLine == null )
{
return;
}
var tl = tweenTimeLine == null ? TimeLineManager.Get().realtimeTimeline : tweenTimeLine;
var start = targetTimeLine.position;
var fromSpeed = targetTimeLine.runner.speed;
var toSpeed = timeLineSpeed;
var sequenceID = DispatchStart();
var tweenType = this.tweenType;
if ( tweenType == null )
{
tweenType = TweenTimeCurve.defaultCurve;
}
TimeLineManager.ScheduleSpanIn( tl, 0, tweenType.GetTweenDuration(),
( span, type )=>
{
var timeNow = tl.position;
var elapsed = timeNow - start;
var state = tweenType.GetTweenPhaseForPhase( span.phase );
if ( ! cacheSpeedOnStart )
{
toSpeed = timeLineSpeed;
}
var lerpedSpeed = Mathf.Lerp( fromSpeed, toSpeed, state );
targetTimeLine.runner.speed = lerpedSpeed;
if ( type == TimeLineSpanUpdateType.End )
{
DispatchEnd( sequenceID );
}
}
);
}
}
}

View File

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

View File

@ -0,0 +1,70 @@
using System;
using Godot;
namespace Rokojori
{
[Tool]
[GlobalClass ]
public partial class TweenParticles:SequenceAction
{
[Export]
public GpuParticles3D particles3D;
[Export]
public TweenParticlesData tweenParticlesData;
[Export]
public TweenType tweenType = new TweenTimeCurve();
[Export]
public TimeLine timeLine;
protected override void _OnTrigger()
{
if ( particles3D == null )
{
return;
}
var tl = TimeLineManager.Ensure( timeLine );
var start = tl.position;
var fromData = new TweenParticlesData();
fromData.CopyFrom( particles3D );
var lerpData = tweenParticlesData.Clone( true );
var sequenceID = DispatchStart();
var tweenType = this.tweenType;
if ( tweenType == null )
{
tweenType = TweenTimeCurve.defaultCurve;
}
TimeLineManager.ScheduleSpanIn( tl, 0, tweenType.GetTweenDuration(),
( span, type )=>
{
var timeNow = tl.position;
var elapsed = timeNow - start;
var state = tweenType.GetTweenPhaseForPhase( span.phase );
TweenParticlesData.LerpTo( fromData, tweenParticlesData, state, lerpData );
lerpData.CopyTo( particles3D );
if ( type == TimeLineSpanUpdateType.End )
{
DispatchEnd( sequenceID );
}
}
);
}
}
}

View File

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

View File

@ -0,0 +1,120 @@
using System;
using Godot;
namespace Rokojori
{
[Tool]
[GlobalClass ]
public partial class TweenParticlesData:Resource
{
[Export]
public FloatValue amount;
[Export]
public FloatValue amountRatio;
[ExportGroup("Time")]
[Export]
public FloatValue lifeTime;
[Export]
public FloatValue speedScale;
[Export]
public FloatValue explosiveness;
[Export]
public FloatValue randomness;
public virtual TweenParticlesData Clone( bool deepClone )
{
var clone = new TweenParticlesData();
clone.amount = FloatValue.Clone( amount, deepClone );
clone.amountRatio = FloatValue.Clone( amountRatio, deepClone );
clone.lifeTime = FloatValue.Clone( lifeTime, deepClone );
clone.speedScale = FloatValue.Clone( speedScale, deepClone );
clone.explosiveness = FloatValue.Clone( explosiveness, deepClone );
clone.randomness = FloatValue.Clone( randomness, deepClone );
return clone;
}
public virtual void CopyFrom( GpuParticles3D particles )
{
amount = FloatValue.Create( particles.Amount );
amountRatio = FloatValue.Create( particles.AmountRatio );
lifeTime = FloatValue.Create( particles.Lifetime );
speedScale = FloatValue.Create( particles.SpeedScale );
explosiveness = FloatValue.Create( particles.Explosiveness );
randomness = FloatValue.Create( particles.Randomness );
}
public virtual void CopyFrom( TweenParticlesData tweenLightData )
{
amount = tweenLightData.amount;
amountRatio = tweenLightData.amountRatio;
lifeTime = tweenLightData.lifeTime;
speedScale = tweenLightData.speedScale;
explosiveness = tweenLightData.explosiveness;
randomness = tweenLightData.randomness;
}
public virtual void CopyTo( GpuParticles3D particles )
{
if ( amount != null )
{
particles.Amount = Mathf.RoundToInt( amount.value );
}
if ( amountRatio != null )
{
particles.AmountRatio = amountRatio.value;
particles.Emitting = amountRatio.value > 0;
}
if ( lifeTime != null )
{
particles.Lifetime = lifeTime.value;
}
if ( speedScale != null )
{
particles.SpeedScale = speedScale.value;
}
if ( explosiveness != null )
{
particles.Explosiveness = explosiveness.value;
}
if ( randomness != null )
{
particles.Randomness = randomness.value;
}
}
public static void LerpTo( TweenParticlesData a, TweenParticlesData b, float lerpAmount, TweenParticlesData output )
{
FloatValue.Lerp( a.amount, b.amount, lerpAmount, output.amount );
FloatValue.Lerp( a.amountRatio, b.amountRatio, lerpAmount, output.amountRatio );
FloatValue.Lerp( a.lifeTime, b.lifeTime, lerpAmount, output.lifeTime );
FloatValue.Lerp( a.speedScale, b.speedScale, lerpAmount, output.speedScale );
FloatValue.Lerp( a.explosiveness, b.explosiveness, lerpAmount, output.explosiveness );
FloatValue.Lerp( a.randomness, b.randomness, lerpAmount, output.randomness );
}
}
}

View File

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

View File

@ -0,0 +1,91 @@
using System;
using Godot;
namespace Rokojori
{
[Tool]
[GlobalClass ]
public partial class TweenPosition:SequenceAction
{
[Export]
public Node3D target;
[Export]
public Node3D endPosition;
[Export]
public Vector3 endOffset;
[Export]
public TweenType tweenType = new TweenTimeCurve();
[Export]
public bool cacheEndPositionOnStart = true;
[Export]
public TimeLine timeLine;
protected override void _OnTrigger()
{
if ( target == null )
{
return;
}
var tl = TimeLineManager.Ensure( timeLine );
var start = tl.position;
var fromPosition = target.GlobalPosition;
var toPosition = endOffset;
if ( endPosition != null )
{
toPosition += endPosition.GlobalPosition;
}
var sequenceID = DispatchStart();
var tweenType = this.tweenType;
if ( tweenType == null )
{
tweenType = TweenTimeCurve.defaultCurve;
}
TimeLineManager.ScheduleSpanIn( tl, 0, tweenType.GetTweenDuration(),
( span, type )=>
{
var timeNow = tl.position;
var elapsed = timeNow - start;
var state = tweenType.GetTweenPhaseForPhase( span.phase );
if ( ! cacheEndPositionOnStart )
{
toPosition = endOffset;
if ( endPosition != null )
{
toPosition += endPosition.GlobalPosition;
}
}
var lerpedPosition = fromPosition.Lerp( toPosition, state );
target.GlobalPosition = lerpedPosition;
if ( type == TimeLineSpanUpdateType.End )
{
DispatchEnd( sequenceID );
}
}
);
}
}
}

View File

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

View File

@ -8,6 +8,11 @@ namespace Rokojori
[GlobalClass ] [GlobalClass ]
public partial class TweenType : Resource public partial class TweenType : Resource
{ {
public virtual void SetFromAndTo( object a, object b )
{
}
public virtual float GetTweenDuration() public virtual float GetTweenDuration()
{ {
return 0.5f; return 0.5f;

View File

@ -5,7 +5,7 @@ using System.Text;
namespace Rokojori namespace Rokojori
{ {
[GlobalClass] [Tool][GlobalClass]
public partial class SineWaveTest: Node public partial class SineWaveTest: Node
{ {
[Export] [Export]

View File

@ -66,5 +66,20 @@ namespace Rokojori
ints.ForEach( i => list.AddRange( BitConverter.GetBytes( i ) ) ); ints.ForEach( i => list.AddRange( BitConverter.GetBytes( i ) ) );
return list; return list;
} }
public static List<byte> Convert( List<Vector4> v )
{
var list = new List<byte>();
v.ForEach(
f =>
{
list.AddRange( BitConverter.GetBytes( f.X ) );
list.AddRange( BitConverter.GetBytes( f.Y ) );
list.AddRange( BitConverter.GetBytes( f.Z ) );
list.AddRange( BitConverter.GetBytes( f.W ) );
}
);
return list;
}
} }
} }

View File

@ -1,9 +1,47 @@
using Godot; using Godot;
using System.Collections.Generic;
namespace Rokojori namespace Rokojori
{ {
public static class ColorX public static class ColorX
{ {
public static Color From( List<float> floats, float basis = 1.0f )
{
if ( floats.Count == 1 )
{
var grey = floats[ 0 ] / basis;
return new Color( grey, grey, grey, 1 );
}
if ( floats.Count == 2 )
{
var grey = floats[ 0 ] / basis;
var alpha = floats[ 1 ] / basis;
return new Color( grey, grey, grey, alpha );
}
if ( floats.Count == 3 )
{
var r = floats[ 0 ] / basis;
var g = floats[ 1 ] / basis;
var b = floats[ 2 ] / basis;
return new Color( r, g, b, 1 );
}
if ( floats.Count == 4 )
{
var r = floats[ 0 ] / basis;
var g = floats[ 1 ] / basis;
var b = floats[ 2 ] / basis;
var alpha = floats[ 3 ] / basis;
return new Color( r, g, b, alpha );
}
return new Color( 0, 0, 0, 0 );
}
public static Color UnblendBlack( this Color c, float treshold = 0 ) public static Color UnblendBlack( this Color c, float treshold = 0 )
{ {
if ( c.A <= treshold ) if ( c.A <= treshold )
@ -62,6 +100,83 @@ namespace Rokojori
return color.Gamma( 2.2f ); return color.Gamma( 2.2f );
} }
public static Vector3 ToVector3( this Color color )
{
return new Vector3( color.R, color.G, color.B );
}
public static Vector4 ToVector4( this Color color )
{
return new Vector4( color.R, color.G, color.B, color.A );
}
public static Color ToColor( this Vector4 color )
{
return new Color( color.X, color.Y, color.Z, color.W );
}
public static Color ToColor( this Vector3 color )
{
return new Color( color.X, color.Y, color.Z, 1f );
}
public static Color ToColor( this Vector3 vec, float alpha = 1 )
{
return new Color( vec.X, vec.Y, vec.Z, alpha);
}
public static Color ToColor( this Vector2 vec, float b = 0, float alpha = 1 )
{
return new Color( vec.X, vec.Y, b, alpha);
}
public enum EdgeMode
{
Clamp,
Repeat,
TransparentBlack,
}
public static Color GetPixel( this Image image, int x, int y, EdgeMode mode )
{
if ( x < 0 || y < 0 || x >= image.GetSize().X || y >= image.GetSize().Y )
{
if ( mode == EdgeMode.TransparentBlack )
{
return new Color( 0, 0, 0, 0 );
}
if ( mode == EdgeMode.Repeat )
{
x = MathX.Repeat( x, image.GetSize().X );
y = MathX.Repeat( y, image.GetSize().Y );
}
if ( mode == EdgeMode.Clamp )
{
x = Mathf.Clamp( x, 0, image.GetSize().X );
y = Mathf.Clamp( y, 0, image.GetSize().Y );
}
}
return image.GetPixel( x, y );
}
public static Color Sample( this Image image, Vector2 uv, EdgeMode mode )
{
var pixelUV = uv * image.GetSize();
var lowUV = pixelUV.FloorToInt().Max( 0 );
var highUV = ( lowUV + Vector2I.One ).Min( image.GetSize() - Vector2I.One );
var mix = pixelUV - lowUV;
var xTop = Lerp( image.GetPixel( lowUV.X, lowUV.Y, mode ), image.GetPixel( highUV.X, lowUV.Y, mode ), mix.X );
var xLow = Lerp( image.GetPixel( highUV.X, lowUV.Y, mode ), image.GetPixel( highUV.X, highUV.Y, mode ), mix.X );
return Lerp( xTop, xLow, mix.Y );
}
public static Color Blend( Color bottom, Color top ) public static Color Blend( Color bottom, Color top )
{ {
@ -151,10 +266,10 @@ namespace Rokojori
public static Color BlendColor( Color bottom, Color top ) public static Color BlendColor( Color bottom, Color top )
{ {
var hslA = HSLColor.FromRGBA( bottom ); var hslBottom = HSLColor.FromRGBA( bottom );
var hslB = HSLColor.FromRGBA( top ); var hslTop = HSLColor.FromRGBA( top );
var combined = bottom.A == 0 ? bottom : (Color)( new HSLColor( hslB.h, hslB.s, hslA.l, top.A ) ); var combined = bottom.A == 0 ? bottom : (Color)( new HSLColor( hslTop.h, hslTop.s, hslBottom.l, top.A ) );
return ColorX.Blend( bottom, combined ); return ColorX.Blend( bottom, combined );
} }
@ -211,11 +326,6 @@ namespace Rokojori
return new Color( rgb.X, rgb.Y, rgb.Z, a ); 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 ) public static Color FromVector4( Vector4 c )
{ {
return new Color( c.X, c.Y, c.Z, c.W ); return new Color( c.X, c.Y, c.Z, c.W );

View File

@ -81,8 +81,11 @@ namespace Rokojori
public static HSLColor FromRGBA( Color c ) public static HSLColor FromRGBA( Color c )
{ {
float h, s, l, a; float h = 0;
a = c.A; float s = 0;
float l = 0;
float a = c.A;
float cmin = Mathf.Min( Mathf.Min( c.R, c.G ), c.B ); float cmin = Mathf.Min( Mathf.Min( c.R, c.G ), c.B );
float cmax = Mathf.Max( Mathf.Max( c.R, c.G ), c.B ); float cmax = Mathf.Max( Mathf.Max( c.R, c.G ), c.B );

View File

@ -33,5 +33,87 @@ namespace Rokojori
return Mathf.RadToDeg( rads ); return Mathf.RadToDeg( rads );
} }
public static float ComputePixelDensityVertical( float fovDegrees, float distance, Vector2 screenSize )
{
float fovRadians = Mathf.DegToRad( fovDegrees );
float verticalViewSize = 2.0f * distance * Mathf.Tan( fovRadians / 2.0f );
float screenHeightPixels = screenSize.Y;
float pixelDensity = screenHeightPixels / verticalViewSize;
return pixelDensity;
}
public static float ComputeSizeOfPixelVertical( float fovDegrees, float distance, Vector2 screenSize )
{
return ComputePixelDensityVertical( fovDegrees, distance, screenSize );
}
public static float ComputePixelDistanceForSizeVertical( float fovDegrees, float size, Vector2 screenSize )
{
float fovRadians = Mathf.DegToRad(fovDegrees);
float screenHeightPixels = screenSize.Y;
float pixelDensity = 1.0f / size;
float distance = screenHeightPixels / (2.0f * pixelDensity * Mathf.Tan(fovRadians / 2.0f));
return distance;
}
public static float ComputePixelDensityHorizontal(float fovDegrees, float distance, Vector2 screenSize)
{
float aspectRatio = screenSize.X / screenSize.Y;
float verticalFovRad = Mathf.DegToRad( fovDegrees );
float horizontalFovRad = 2.0f * Mathf.Atan(Mathf.Tan( verticalFovRad / 2.0f ) * aspectRatio);
float horizontalViewSize = 2.0f * distance * Mathf.Tan( horizontalFovRad / 2.0f );
float pixelDensity = screenSize.X / horizontalViewSize;
return pixelDensity;
}
public static Vector3 GlobalToView( Transform3D transform, Vector3 globalPosition )
{
return globalPosition * transform;
}
public static Vector3 ViewToClip( Projection cameraProjection, Vector3 viewPosition )
{
var clip = cameraProjection * viewPosition;
return clip;
}
public static Vector2 ClipToScreen( Vector3 clipPosition )
{
var size = Vector2.One;
var clip = clipPosition.XY() * 0.5f + new Vector2( 0.5f, 0.5f );
return clip * size;
}
public static Vector2 ClipToScreen( Vector3 clipPosition, Vector2 screenPixelSize )
{
var clip = clipPosition.XY() * 0.5f + new Vector2( 0.5f, 0.5f );
return clip * screenPixelSize;
}
public static Vector2 GlobalToScreen( Vector3 globalPosition, Transform3D cameraTransform, Projection cameraProjection, Vector2 screenPixelSize )
{
var view = GlobalToView( cameraTransform, globalPosition );
var clip = ViewToClip( cameraProjection, view );
var screen = ClipToScreen( clip, screenPixelSize );
return screen;
}
} }
} }

View File

@ -3,7 +3,7 @@ using Godot;
namespace Rokojori namespace Rokojori
{ {
[GlobalClass] [Tool][GlobalClass]
public partial class RJAnimatableBody3D:AnimatableBody3D public partial class RJAnimatableBody3D:AnimatableBody3D
{ {

View File

@ -3,7 +3,7 @@ using Godot;
namespace Rokojori namespace Rokojori
{ {
[GlobalClass] [Tool][GlobalClass]
public partial class RJCharacterBody3D:CharacterBody3D public partial class RJCharacterBody3D:CharacterBody3D
{ {

View File

@ -0,0 +1,45 @@
using Godot;
using System.Text;
using System.Collections.Generic;
namespace Rokojori
{
public class GodotEditorHelper
{
public static string AbsoluteToResourcePath( string path )
{
#if TOOLS
if ( path != null )
{
return ProjectSettings.LocalizePath( path );
}
#endif
return path;
}
public static bool IsProjectPath( string path )
{
return path.StartsWith( "res://" ) || path.StartsWith( "user://" );
}
public static void UpdateFile( string path )
{
#if TOOLS
if ( ! IsProjectPath( path ) )
{
var pathBefore = path;
path = AbsoluteToResourcePath( path );
RJLog.Log( "Converted path", pathBefore, ">>", path );
}
var filesystem = EditorInterface.Singleton.GetResourceFilesystem();
filesystem.ReimportFiles( [ path ] );
#endif
}
}
}

View File

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

View File

@ -29,6 +29,18 @@ namespace Rokojori
} }
public static T FindSibling<T>( this Node node, Func<T,bool> evaluater = null ) where T:Node
{
if ( node == null )
{
return null;
}
var sibling = NodesWalker.Get().FindSibling( node, n => n as T != null );
return sibling as T;
}
public static T Find<T>( Node root, NodePathLocatorType type = NodePathLocatorType.DirectChildren, int parentOffset = 0 ) where T:Node public static T Find<T>( Node root, NodePathLocatorType type = NodePathLocatorType.DirectChildren, int parentOffset = 0 ) where T:Node
{ {
var it = root; var it = root;
@ -245,6 +257,17 @@ namespace Rokojori
return; return;
} }
public static double StartAsyncTimer( this Node node )
{
return Async.StartTimer();
}
public static async Task<double> WaitForAsyncTimer( this Node node, double time )
{
time = await Async.WaitIfExceeded( time, node );
return time;
}
public static T CreateChild<T>( this Node parent, string name = null ) where T:Node,new() public static T CreateChild<T>( this Node parent, string name = null ) where T:Node,new()
{ {
return CreateChildIn<T>( parent, name ); return CreateChildIn<T>( parent, name );

View File

@ -40,5 +40,7 @@ namespace Rokojori
return n.GetChildCount(); return n.GetChildCount();
} }
} }
} }

View File

@ -12,12 +12,13 @@ namespace Rokojori
[Export] [Export]
public string path = ""; public string path = "";
[Export] [ExportToolButton( "Read" )]
public bool load public Callable ReadButton => Callable.From(
{ ()=>
get => false; {
set { if ( value ) LoadScene(); } LoadScene();
} }
);
[Export] [Export]
public bool exportJSON = false; public bool exportJSON = false;

View File

@ -50,7 +50,7 @@ namespace Rokojori
{ {
var p = Parent( node ); var p = Parent( node );
if ( p == null || index<0 || index >= NumChildren( p ) ) if ( p == null || index < 0 || index >= NumChildren( p ) )
{ return null; } { return null; }
return ChildAt( p, index ); return ChildAt( p, index );
@ -74,11 +74,97 @@ namespace Rokojori
public bool HasSiblingAt( N node, int index ) public bool HasSiblingAt( N node, int index )
{ {
var p = Parent( node ); var p = Parent( node );
if ( p == null || index<0 || index >= NumChildren( p ) ) if ( p == null || index < 0 || index >= NumChildren( p ) )
{ return false; } {
return false;
}
return true; return true;
} }
public int NumSiblings( N node )
{
if ( node == null )
{
return 0;
}
var p = Parent( node );
if ( p == null )
{
return 0;
}
return NumChildren( p ) - 1;
}
public void IterateSiblings( N node, Action<N> action )
{
if ( node == null )
{
return;
}
var p = Parent( node );
if ( p == null )
{
return;
}
var numChildren = NumChildren( p );
for ( int i = 0; i < numChildren; i++ )
{
var child = ChildAt( p, i );
if ( child == node )
{
continue;
}
action( child );
}
}
public N FindSibling( N node, Func<N,bool> evaluater )
{
if ( node == null )
{
return null;
}
var p = Parent( node );
if ( p == null )
{
return null ;
}
var numChildren = NumChildren( p );
for ( int i = 0; i < numChildren; i++ )
{
var child = ChildAt( p, i );
if ( child == node )
{
continue;
}
var result = evaluater( child );
if ( result )
{
return child;
}
}
return null;
}
public N FirstChild( N node ) public N FirstChild( N node )
{ {

View File

@ -2,13 +2,21 @@ using Godot;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using Godot.Collections; using Godot.Collections;
using System.Drawing;
namespace Rokojori namespace Rokojori
{ {
[Tool]
[GlobalClass,Icon("res://addons/rokojori_action_library/Icons/Grabbable.svg")] [GlobalClass,Icon("res://addons/rokojori_action_library/Icons/Grabbable.svg")]
public partial class Grabbable:Node3D public partial class Grabbable:Node3D, iEnablable
{ {
[Export]
public bool enabled = true;
public bool IsEnabled() => enabled;
public void SetEnabled( bool enabled ) { this.enabled = enabled; }
[Export] [Export]
public Action onGrab; public Action onGrab;
@ -21,14 +29,46 @@ namespace Rokojori
[Export] [Export]
public RigidBody3D rigidBody3D; public RigidBody3D rigidBody3D;
[Export] Pointable pointable;
[Export]
public bool disablePointableDuringGrab = true;
[ExportGroup("Read Only")] [ExportGroup("Read Only")]
[Export] [Export]
public Grabber grabber; public Grabber grabber;
protected bool enablePointableOnRelease = false;
public void SetGrabber( Grabber grabber ) public void SetGrabber( Grabber grabber )
{ {
this.grabber = grabber; this.grabber = grabber;
if ( grabber != null && ( disablePointableDuringGrab || grabber.disablePointableDuringGrab ) )
{
if ( pointable == null )
{
pointable = this.FindSibling<Pointable>();
}
if ( pointable != null )
{
pointable.enabled = false;
enablePointableOnRelease = true;
}
}
if ( grabber == null )
{
if ( pointable != null && enablePointableOnRelease )
{
pointable.enabled = true;
}
enablePointableOnRelease = false;
}
if ( this.grabber != null ) if ( this.grabber != null )
{ {
Action.Trigger( onGrab ); Action.Trigger( onGrab );

View File

@ -5,7 +5,7 @@ using Godot.Collections;
namespace Rokojori namespace Rokojori
{ {
[Tool]
[GlobalClass,Icon("res://addons/rokojori_action_library/Icons/Grabber.svg")] [GlobalClass,Icon("res://addons/rokojori_action_library/Icons/Grabber.svg")]
public partial class Grabber:Node3D, SensorInputHandler public partial class Grabber:Node3D, SensorInputHandler
{ {
@ -27,6 +27,9 @@ namespace Rokojori
[Export] [Export]
public Node3D grabOffset; public Node3D grabOffset;
[Export]
public bool disablePointableDuringGrab = true;
[Export] [Export]
public Smoothing positionSmoothing; public Smoothing positionSmoothing;
@ -37,6 +40,7 @@ namespace Rokojori
[Export] [Export]
public Grabbable grabbable; public Grabbable grabbable;
public override void _Ready() public override void _Ready()

View File

@ -5,10 +5,15 @@ using Godot.Collections;
namespace Rokojori namespace Rokojori
{ {
[Tool]
[GlobalClass,Icon("res://addons/rokojori_action_library/Icons/Interactable.svg")] [GlobalClass,Icon("res://addons/rokojori_action_library/Icons/Interactable.svg")]
public partial class Interactable:Node3D public partial class Interactable:Node3D, iEnablable
{ {
[Export]
public bool enabled = true;
public bool IsEnabled() => enabled;
public void SetEnabled( bool enabled ) { this.enabled = enabled; }
[Export] [Export]
public Action onInteraction; public Action onInteraction;
} }

View File

@ -5,7 +5,7 @@ using Godot.Collections;
namespace Rokojori namespace Rokojori
{ {
[Tool]
[GlobalClass,Icon("res://addons/rokojori_action_library/Icons/Interactor.svg")] [GlobalClass,Icon("res://addons/rokojori_action_library/Icons/Interactor.svg")]
public partial class Interactor:Node3D, SensorInputHandler public partial class Interactor:Node3D, SensorInputHandler
{ {

View File

@ -5,10 +5,18 @@ using Godot.Collections;
namespace Rokojori namespace Rokojori
{ {
[Tool]
[GlobalClass,Icon("res://addons/rokojori_action_library/Icons/Pointable.svg")] [GlobalClass,Icon("res://addons/rokojori_action_library/Icons/Pointable.svg")]
public partial class Pointable:Node3D public partial class Pointable:Node3D, iEnablable
{ {
[Export]
public bool enabled = true;
public bool IsEnabled() => enabled;
public void SetEnabled( bool enabled ) { this.enabled = enabled; }
[Export] [Export]
public int pointingPriority; public int pointingPriority;

View File

@ -0,0 +1,71 @@
using Godot;
using System.Collections;
using System.Collections.Generic;
using Godot.Collections;
using System.Drawing;
namespace Rokojori
{
[Tool]
[GlobalClass]
public partial class InteractiveSelector:Selector
{
[Export]
public Trillean isPointable = Trillean.Any;
[Export]
public Trillean pointableEnabled = Trillean.Any;
[Export]
public Trillean isGrabbable = Trillean.Any;
[Export]
public Trillean grabbableEnabled = Trillean.Any;
[Export]
public Trillean isInteractable = Trillean.Any;
[Export]
public Trillean interactableEnabled = Trillean.Any;
public override bool Selects( Node node )
{
if ( ! Matches<Pointable>( node, isPointable, pointableEnabled ) )
{
return false;
}
if ( ! Matches<Grabbable>( node, isGrabbable, grabbableEnabled ) )
{
return false;
}
if ( ! Matches<Interactable>( node, isInteractable, interactableEnabled ) )
{
return false;
}
return true;
}
bool Matches<T>( Node node, Trillean isType, Trillean isEnabled ) where T:Node,iEnablable
{
var childNode = Nodes.Find<T>( node );
if ( ! TrilleanLogic.Matches( isType, childNode != null ) )
{
return false;
}
if ( ! TrilleanLogic.Matches( isEnabled, childNode != null && childNode.IsEnabled() ) )
{
return false;
}
return true;
}
}
}

View File

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

View File

@ -0,0 +1,23 @@
using Godot;
using System.Collections;
using System.Collections.Generic;
using Godot.Collections;
namespace Rokojori
{
[Tool]
[GlobalClass]
public partial class SetPointableEnabled:Action
{
[Export]
public Pointable pointable;
[Export]
public bool enabled = true;
protected override void _OnTrigger()
{
pointable.enabled = enabled;
}
}
}

View File

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

View File

@ -0,0 +1,14 @@
using Godot;
using System.Collections;
using System.Collections.Generic;
using Godot.Collections;
namespace Rokojori
{
public interface iEnablable
{
public bool IsEnabled();
public void SetEnabled( bool enabled );
}
}

View File

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

104
Runtime/LOD/LODBuilder.cs Normal file
View File

@ -0,0 +1,104 @@
using Godot;
using System.Collections;
using System.Collections.Generic;
namespace Rokojori
{
public class LODCameraDistanceLevel
{
public Mesh mesh;
public Material material;
public float distance;
}
public class LODBuilder
{
public enum Mode
{
CameraDistance
}
Mode mode = Mode.CameraDistance;
public static LODBuilder ByCameraDistance()
{
var builder = new LODBuilder();
builder.mode = Mode.CameraDistance;
return builder;
}
List<LODCameraDistanceLevel> levels = new List<LODCameraDistanceLevel>();
Material material;
public LODBuilder SetMaterial( Material material )
{
this.material = material;
return this;
}
public LODBuilder Add( float distance, Mesh mesh, Material material = null )
{
var level = new LODCameraDistanceLevel
{
mesh = mesh,
distance = distance,
material = material
};
levels.Add( level );
return this;
}
public LODNode Create( Node parent, string name = "LOD" )
{
var lodNode = parent.CreateChild<LODNode>( name );
var arrangement = new LODArrangement();
levels.Sort( ( a, b ) => Mathf.Sign( a.distance - b.distance ) );
var maxMap = new Dictionary<LODCameraDistanceLevel, float>();
for ( int i = 0; i < levels.Count; i++ )
{
if ( ( i + 1 ) < levels.Count )
{
maxMap[ levels[ i ] ] = levels[ i + 1 ].distance;
}
else
{
maxMap[ levels[ i ] ] = 10000f;
}
}
levels[ 0 ].distance = 0;
levels.ForEach(
( l )=>
{
var level = new LODLevel();
var distanceRule = new LODCameraDistanceRule();
distanceRule.minDistance = l.distance;
distanceRule.maxDistance = maxMap[ l ];
level.visibilityRules = [ distanceRule ];
level.mesh = l.mesh;
level.material = l.material != null ? l.material : material;
arrangement.levels = arrangement.levels.Add( level );
}
);
lodNode.arrangement = arrangement;
return lodNode;
}
}
}

View File

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

View File

@ -15,7 +15,7 @@ namespace Rokojori
public override bool Evaluate( LODNode lodNode ) public override bool Evaluate( LODNode lodNode )
{ {
return false; return Range.Contains( minDistance, maxDistance, lodNode.distance );
} }
} }

View File

@ -0,0 +1,61 @@
using Godot;
using System.Collections;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Rokojori
{
[Tool]
[GlobalClass]
public partial class LODMeshCreator:Node
{
[Export]
public MeshInstance3D[] meshes;
[Export]
public float[] distances;
[Export]
public Mesh.PrimitiveType primitiveType = Mesh.PrimitiveType.Triangles;
[Export]
public bool generateTangents = true;
[ExportToolButton("Create")]
public Callable CreateButton => Callable.From( ()=>{ Create(); } );
[ExportGroup("Output")]
[Export]
public MeshInstance3D lodMesh;
public async Task Create()
{
var list = new List<MeshGeometry>();
var index = 0;
foreach ( var m in meshes )
{
var mg = MeshGeometry.From( m.Mesh as ArrayMesh );
mg.lodEdgeLength = distances[ index ];
index++;
await this.RequestNextFrame();
}
var mesh = MeshGeometry.GenerateLODMesh( list, primitiveType, null, generateTangents );
await this.RequestNextFrame();
this.DestroyChildren();
await this.RequestNextFrame();
lodMesh = this.CreateChild<MeshInstance3D>( "LODMesh" );
lodMesh.Mesh = mesh;
lodMesh.Mesh.SurfaceSetMaterial( 0, MaterialSurfaceContainer.GetActiveFrom( meshes[ 0 ] ) );
}
}
}

View File

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

View File

@ -1,7 +1,6 @@
using Godot; using Godot;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using Godot.Collections;
namespace Rokojori namespace Rokojori
{ {
@ -9,9 +8,22 @@ namespace Rokojori
[GlobalClass] [GlobalClass]
public partial class LODNode:MeshInstance3D public partial class LODNode:MeshInstance3D
{ {
[Export] [Export]
public LODArrangement arrangement; public LODArrangement arrangement;
[Export]
public int currentLevelIndex = -1;
[Export]
public int drawElements = 0;
[Export]
public float distanceScale = 1;
[Export]
public MeshInstance3D lod0;
LODLevel _currentLevel; LODLevel _currentLevel;
public override void _Process( double delta ) public override void _Process( double delta )
@ -33,10 +45,34 @@ namespace Rokojori
return; return;
} }
currentLevelIndex = arrangement.levels.IndexOf( level );
if ( lod0 != null )
{
lod0.Visible = currentLevelIndex == 0;
}
if ( ! faceCount.ContainsKey( level.mesh ) )
{
if ( level.mesh is ArrayMesh am )
{
faceCount[ level.mesh ] = am.SurfaceGetArrayIndexLen( 0 );
}
else
{
faceCount[ level.mesh ] = level.mesh.GetFaces().Length;
}
}
drawElements = faceCount[ level.mesh ];
Mesh = level.mesh; Mesh = level.mesh;
MaterialOverride = level.material; MaterialOverride = level.material;
} }
Dictionary<Mesh,int> faceCount = new Dictionary<Mesh,int>();
public Camera3D camera public Camera3D camera
{ {
@ -73,6 +109,8 @@ namespace Rokojori
_cameraDirection = ( GlobalPosition - camera.GlobalPosition ); _cameraDirection = ( GlobalPosition - camera.GlobalPosition );
_hasCameraDirection = true;
return _cameraDirection; return _cameraDirection;
} }
} }
@ -86,12 +124,14 @@ namespace Rokojori
{ {
if ( _hasDistance ) if ( _hasDistance )
{ {
return _distance; return _distance * distanceScale;
} }
_distance = cameraDirection.Length(); _distance = cameraDirection.Length();
return _distance; _hasDistance = true;
return _distance * distanceScale;
} }
} }
@ -109,6 +149,8 @@ namespace Rokojori
_pitch = Math3D.GlobalPitch( cameraDirection ); _pitch = Math3D.GlobalPitch( cameraDirection );
_hasPitch = true;
return _pitch; return _pitch;
} }
} }
@ -127,6 +169,8 @@ namespace Rokojori
_yaw = Math3D.GlobalYaw( cameraDirection ); _yaw = Math3D.GlobalYaw( cameraDirection );
_hasYaw = true;
return _yaw; return _yaw;
} }
} }

View File

@ -0,0 +1,262 @@
using System.Collections;
using System.Collections.Generic;
using Godot;
using System;
using System.Linq;
namespace Rokojori
{
public class OcTree<T>:OcTreeNode<T>
{
protected Func<T,Vector3> _getPosition;
protected Func<List<T>,List<T>> _combinePoints;
protected Func<List<T>,List<T>, float,List<T>> _smoothPoints;
protected Vector3 _min;
protected Vector3 _max;
protected float _rootCellSize;
protected int _maxDepth = 64;
public int maxDepth => _maxDepth;
public OcTree( Func<T,Vector3> getPosition, Vector3 min, Vector3 max, float rootCellSize, int maxDepth )
{
this._getPosition = getPosition;
this._min = min;
this._max = max;
this._rootCellSize = rootCellSize;
this._maxDepth = maxDepth;
CreateRootCells();
}
public void SetCombiner( Func<List<T>,List<T>> combinePoints )
{
this._combinePoints = combinePoints;
}
public void SetSmoother( Func<List<T>,List<T>,float,List<T>> smoothPoints )
{
this._smoothPoints = smoothPoints;
}
public List<T> CombinePoints( List<T> values )
{
return _combinePoints( values );
}
public List<T> SmoothPoints( List<T> targetValues, List<T> sourceSmoothing, float amount )
{
return _smoothPoints( targetValues, sourceSmoothing, amount );
}
List<OcTreeCell<T>> _rootCells = new List<OcTreeCell<T>>();
public List<OcTreeCell<T>> rootCells => _rootCells;
public bool Insert( List<T> data )
{
var result = true;
data.ForEach(
( d )=>
{
result = result && Insert( d );
}
);
return result;
}
public bool Insert( T data )
{
var rootCell = GetRootCell( _getPosition( data ) );
if ( rootCell == null )
{
return false;
}
if ( ! rootCell.box.ContainsPoint( GetPosition( data ) ) )
{
RJLog.Log( "Box not containing point", rootCell.rootCellIndex );
}
return rootCell.Insert( data );
}
public int GetLevelForSize( float size )
{
var level = 0;
var it = _rootCellSize;
while ( it > size )
{
level ++;
it /= 2f;
}
return level;
}
public float GetSizeForLevel( int level )
{
var it = 0;
var size = _rootCellSize;
while ( it < level )
{
size /= 2f;
it ++;
}
return size;
}
public void CombineUntilSize( float size )
{
CombineUntilLevel( GetLevelForSize( size ) );
}
public void CombineUntilLevel( int minLevel, float smoothDown = 0.2f )
{
var levelMap = GetLevelMap();
var levels = levelMap.Keys.ToList();
levels.Sort();
var maxLevel = levels.Last();
for ( int i = maxLevel - 1; i > minLevel; i-- )
{
levelMap[ i ].ForEach(
( c )=>
{
c.Combine();
}
);
}
if ( smoothDown > 0 )
{
for ( int i = minLevel + 1; i < maxLevel - 1; i++ )
{
levelMap[ i ].ForEach(
( c )=>
{
c.SmoothDown( smoothDown );
}
);
}
}
}
public MapList<int,OcTreeCell<T>> GetLevelMap()
{
var walker = new OcTreeWalker<T>();
var levelMap = new MapList<int,OcTreeCell<T>>();
walker.Iterate(
this,
( n )=>
{
if ( n is OcTree<T> tree )
{
return;
}
var c = n as OcTreeCell<T>;
levelMap.Add( c.depth, c );
}
);
return levelMap;
}
Vector3 _startOffset = Vector3.Zero;
Vector3I _rootCellDimensions = Vector3I.Zero;
public Vector3I PositionToRootIndex( Vector3 position )
{
position -= _startOffset;
var index = ( position / ( Vector3.One * _rootCellSize ) ).FloorToInt();
return index;
}
public Vector3 GetPosition( T data )
{
return _getPosition( data );
}
public OcTreeCell<T> GetRootCell( Vector3 position )
{
var rootIndex = PositionToRootIndex( position );
return GetRootCellByRootIndex( rootIndex );
}
public OcTreeCell<T> GetRootCellByRootIndex( Vector3I rootCellIndex )
{
var rootCellFlatIndex = MathX.MultiIndexToFlatIndex( rootCellIndex, _rootCellDimensions );
if ( rootCellFlatIndex < 0 || rootCellFlatIndex >= _rootCells.Count )
{
return null;
}
return _rootCells[ rootCellFlatIndex ];
}
void CreateRootCells()
{
var rootCellSize3 = Vector3.One * _rootCellSize;
var start = _min.SnapFloored( rootCellSize3 );
var end = _max.SnapCeiled( rootCellSize3 );
var num = ( ( end - start ) / rootCellSize3 ).RoundToInt();
_rootCellDimensions = num;
_startOffset = start;
for ( int x = 0; x < num.X ; x ++ )
{
for ( int y = 0; y < num.Y; y++ )
{
for ( int z = 0; z < num.Z; z++ )
{
var rootIndex = new Vector3I( x, y, z );
var min = rootIndex * rootCellSize3 + start;
var max = min + rootCellSize3;
var cell = OcTreeCell<T>.Create( this, min, max, _rootCells.Count );
_rootCells.Add( cell );
// var positionRootIndex = PositionToRootIndex( cell.center );
// RJLog.Log(
// "Creating root cell",
// "Roots Index:", _rootCells.Count - 1,
// "Cell Index:", cell.rootCellIndex,
// "Center:", cell.center,
// "RootIndex:", rootIndex,
// "Get Self ByRootIndex:", GetRootCellByRootIndex( rootIndex ) == cell,
// "RootIndex From Position: ", positionRootIndex,
// "Get Self ByRootIndex From Position:", GetRootCellByRootIndex( positionRootIndex ) == cell
// );
}
}
}
}
}
}

View File

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

View File

@ -0,0 +1,202 @@
using System.Collections;
using System.Collections.Generic;
using Godot;
using System;
namespace Rokojori
{
public class OcTreeCell<T>:OcTreeNode<T>
{
OcTreeCell<T> _parent;
public OcTreeCell<T> parent => _parent;
OcTree<T> _tree;
public OcTree<T> tree => _tree;
List<OcTreeCell<T>> _cells;
public List<OcTreeCell<T>> cells => _cells;
public int numCells => _cells == null ? 0 : cells.Count;
List<T> _values;
public List<T> values => _values;
int _depth;
public int depth => _depth;
Vector3 _center;
public Vector3 center => _center;
Vector3 _size;
public Vector3 size => _size;
bool _isCombined = false;
public bool isCombined => _isCombined;
public bool canBeCombined => ! isLeaf && ! isCombined && numCells > 0;
public bool isRoot => _parent == null;
public bool isEmpty => _cells == null || _cells.Count == 0;
public bool isLeaf => _depth == tree.maxDepth;
Box3 _box;
public Box3 box => _box;
int _rootCellIndex = -1;
public int rootCellIndex => _rootCellIndex;
public static OcTreeCell<T> Create( OcTree<T> tree, Vector3 min, Vector3 max, int rootIndex )
{
var cell = new OcTreeCell<T>();
cell._tree = tree;
cell._center = (max + min ) / 2f;
cell._size = ( max - min );
cell._depth = 0;
cell._box = Box3.Create( min, max );
cell._rootCellIndex = rootIndex;
return cell;
}
public static OcTreeCell<T> Create( OcTreeCell<T> parent, int depth, Vector3 min, Vector3 max )
{
var cell = new OcTreeCell<T>();
cell._parent = parent;
cell._tree = parent.tree;
cell._center = (max + min ) / 2f;
cell._size = ( max - min );
cell._depth = depth;
cell._box = Box3.Create( min, max );
cell._rootCellIndex = parent.rootCellIndex;
return cell;
}
public Vector3 GetPointWithPolarUVW( Vector3 polarUVW )
{
var halfSize = _size / 2f;
return _center + halfSize * polarUVW;
}
public bool Insert( T data )
{
if ( isLeaf )
{
if ( _values == null )
{
_values = new List<T>();
}
values.Add( data );
return true;
}
Nest();
var position = tree.GetPosition( data );
var cell = GetChildCellFor( position );
if ( cell == null )
{
RJLog.Log( "No cell found in:", this.depth, this.center, ">> for: ", position );
return false;
}
return cell.Insert( data );
}
public void SmoothDown( float amount )
{
if ( isLeaf || cells == null )
{
return;
}
cells.ForEach(
( c )=>
{
c._values = tree.SmoothPoints( c.values, values, amount );
}
);
}
public void Combine()
{
if ( isLeaf || isCombined )
{
return;
}
if ( numCells == 0 )
{
_isCombined = true;
return;
}
var cellPoints = new List<T>();
cells.ForEach(
c =>
{
if ( c.values == null )
{
return;
}
cellPoints.AddRange( c.values );
}
);
_values = tree.CombinePoints( cellPoints );
_isCombined = true;
}
public OcTreeCell<T> GetChildCellFor( Vector3 position )
{
return _cells.Find( c => c.box.ContainsPoint( position ) );
}
public void Nest()
{
if ( _cells != null && _cells.Count == 8 )
{
return;
}
_cells = new List<OcTreeCell<T>>();
for ( int x = -1; x < 1; x ++ )
{
var x0 = x;
var x1 = x + 1;
for ( int y = -1; y < 1; y ++ )
{
var y0 = y;
var y1 = y + 1;
for ( int z = -1; z < 1; z ++ )
{
var z0 = z;
var z1 = z + 1;
var min = GetPointWithPolarUVW( new Vector3( x0, y0, z0 ) );
var max = GetPointWithPolarUVW( new Vector3( x1, y1, z1 ) );
_cells.Add( Create( this, depth + 1, min, max ) );
}
}
}
}
}
}

View File

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

View File

@ -0,0 +1,14 @@
using System.Collections;
using System.Collections.Generic;
using Godot;
using System;
namespace Rokojori
{
public class OcTreeNode<T>
{
}
}

View File

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

View File

@ -0,0 +1,53 @@
using System.Collections;
using System.Collections.Generic;
using Godot;
using System;
namespace Rokojori
{
public class OcTreeWalker<T>:TreeWalker<OcTreeNode<T>>
{
public override OcTreeNode<T> Parent( OcTreeNode<T> node )
{
if ( node is OcTree<T> )
{
return null;
}
var cell = node as OcTreeCell<T>;
return cell.isRoot ? cell.tree : cell.parent;
}
public override int NumChildren( OcTreeNode<T> node )
{
if ( node is OcTree<T> tree )
{
return tree.rootCells.Count;
}
var cell = node as OcTreeCell<T>;
return cell.numCells;
}
public override OcTreeNode<T> ChildAt( OcTreeNode<T> node, int index )
{
if ( index < 0 || index >= NumChildren( node ) )
{
return null;
}
if ( node is OcTree<T> tree )
{
return tree.rootCells[ index ];
}
var cell = node as OcTreeCell<T>;
return cell.cells[ index ];
}
}
}

View File

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

View File

@ -0,0 +1,51 @@
using System.Collections;
using System.Collections.Generic;
using Godot;
using System;
namespace Rokojori.PointClouds
{
public class Point
{
public Vector3 position;
public Color color;
public Vector3 normal;
public Vector2 uv;
public Point Lerp( Point point, float amount )
{
var p = new Point();
p.position = position.Lerp( point.position, amount );
p.normal = normal.Lerp( point.normal, amount ).Normalized();
p.uv = uv.Lerp( point.uv, amount );
p.color = color.ToVector4().Lerp( point.color.ToVector4(), amount ).ToColor();
return p;
}
public static Point AsAverage( List<Point> points )
{
var p = new Point();
var color = new Vector4();
for ( int i = 0; i < points.Count; i++ )
{
var d = 1f / ( i + 1 );
p.position += d * ( points[ i ].position - p.position );
p.normal += d * ( points[ i ].normal - p.normal );
p.uv += d * ( points[ i ].uv - p.uv );
color += d * ( points[ i ].color.ToVector4() - color );
}
p.normal = p.normal.Normalized();
p.color = color.ToColor();
return p;
}
}
}

View File

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

View File

@ -0,0 +1,66 @@
using System.Collections;
using System.Collections.Generic;
using Godot;
using System;
using System.Threading.Tasks;
namespace Rokojori.PointClouds
{
public class PointCloud
{
public List<Point> points = new List<Point>();
public Vector3 center;
public void ComputeCenter()
{
center = Math3D.ComputeAverage( points, p => p.position );
}
public Box3 boundingBox;
public void ComputeBoundingBox()
{
boundingBox = Box3.Create( points, p => p.position );
}
public void SortByDistanceToCenter()
{
ComputeCenter();
points.Sort(
( a, b ) =>
{
var dA = ( a.position - center ).LengthSquared();
var dB = ( b.position - center ).LengthSquared();
return Mathf.Sign( dB - dA );
}
);
}
public ArrayMesh GenerateMesh( float normalOffset = 0f )
{
var mg = GenerateMeshGeometry( normalOffset );
return mg.GenerateMesh( Mesh.PrimitiveType.Points, null, false );
}
public MeshGeometry GenerateMeshGeometry( float normalOffset = 0f )
{
var mg = new MeshGeometry( true, false, true, false );
mg.customMeshAttributes.Add( new MeshAttributeVector4List( 0 ) );
mg.customMeshAttributes.Add( new MeshAttributeVector4List( 1 ) );
points.ForEach(
( p ) =>
{
mg.AddPoint( p.position + p.normal * normalOffset, p.color.srgbToLinear(), p.normal, 0, p.uv, 1 );
}
);
return mg;
}
}
}

View File

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

View File

@ -0,0 +1,258 @@
using System.Collections;
using System.Collections.Generic;
using Godot;
using System;
using System.Threading.Tasks;
namespace Rokojori.PointClouds
{
[Tool]
[GlobalClass]
public partial class PointCloudGenerator:Node3D
{
[Export]
public MeshInstance3D mesh;
[Export]
public PointCloudSampler.SampleMode sampleMode = PointCloudSampler.SampleMode.Center;
[Export]
public float samplingPixelDensity = 0.01f;
[Export]
public float outputPixelDensity = 0.5f;
[Export]
public int numPoints = 0;
[Export]
public int squareTexture;
[Export]
public Material material;
[Export]
public bool createOutputs = false;
[Export]
public MeshInstance3D[] outputs;
[ExportGroup("Lerping")]
[Export]
public Curve lowerLerp;
[Export]
public Curve higherLerp;
[Export]
public int lerpSteps;
[ExportGroup("")]
[Export]
public LODNode lod;
[Export]
bool working = false;
[Export]
public string[] outputInfos = [];
[ExportToolButton( "Generate")]
public Callable GenerateButton => Callable.From(
() =>
{
GeneratePointCloud();
}
);
public async void GeneratePointCloud()
{
if ( working )
{
return;
}
working = true;
numPoints = -1;
this.LogInfo( "Generating Point Cloud" );
var sampler = new PointCloudSampler();
if ( samplingPixelDensity > 0 )
{
sampler.densityResolution = samplingPixelDensity;
}
sampler.materials = Materials.GetAll<Material>( mesh );
sampler.albedoTexture = sampler.materials.Map( m => Texture2DPropertyName.albedoTexture );
sampler.albedo = sampler.materials.Map( m => ColorPropertyName.albedo ); ;
var cloud = await sampler.SampleFromMesh( sampleMode, (ArrayMesh) mesh.Mesh, this );
this.LogInfo( "Sampled mesh" );
var time = Async.GetTimeMS();
// cloud.ComputeBoundingBox();
time = Async.PrintAndUpdateMS( time );
var longestCM = cloud.boundingBox.size.MaxDimension() * 100f;
var nextP2CM = MathX.NextPowerOfTwo( longestCM );
var rootSize = nextP2CM / 100f;
var ocMin = cloud.boundingBox.center - Vector3.One * rootSize;
var ocMax = cloud.boundingBox.center + Vector3.One * rootSize;
var depth = Mathf.CeilToInt( MathX.Exponent( 2, rootSize/ samplingPixelDensity ) );
var ocTree = new PointCloudOcTree( ocMin, ocMax, rootSize, depth );
var minSize = ocTree.GetSizeForLevel( depth );
this.LogInfo( "OcTree MinDepth", minSize._CM() );
this.RequestNextFrame();
time = Async.PrintAndUpdateMS( time, "Inserting to octree" );
ocTree.Insert( cloud.points );
time = Async.PrintAndUpdateMS( time, "Inserting to octree Done" );
var level = ocTree.GetLevelForSize( outputPixelDensity );
this.LogInfo( "Combining" );
this.RequestNextFrame();
time = Async.PrintAndUpdateMS( time, "Combining" );
ocTree.normalSeperation = false;
ocTree.CombineUntilLevel( level );
time = Async.PrintAndUpdateMS( time, "Combining Done" );
this.LogInfo( "Combining level", level, "Max Level", depth );
this.LogInfo( "Cloud Points:", cloud.points.Count );
if ( outputs != null )
{
outputs = [];
}
this.DestroyChildren();
var levelMap = ocTree.GetLevelMap();
var levelInstances = new List<MeshInstance3D>();
var lodBuilder = LODBuilder.ByCameraDistance();
lodBuilder.SetMaterial( material );
// lodBuilder.Add( 0, mesh.Mesh, mesh.GetSurfaceOverrideMaterial( 0 ) );
var meshGeometries = new List<MeshGeometry>();
var infos = new List<string>();
for ( int i = level + 1; i < depth; i++ )
{
var filledLevels = levelMap[ i ].Filter( l => l.values != null );
var points = new List<Point>();
filledLevels.ForEach(
l =>
{
points.AddRange( l.values );
}
);
var pc = new PointCloud();
pc.points = points;
pc.ComputeCenter();
pc.SortByDistanceToCenter();
var levelSize = ocTree.GetSizeForLevel( i );
var distance = Cameras.ComputePixelDistanceForSizeVertical( 60, levelSize, new Vector2( 1920, 1080 ) ) * 4;
// this.LogInfo(
// "Level", i,
// "Points", pc.points.Count,
// "Size", RegexUtility._CM( levelSize ),
// "Distance", RegexUtility._M( distance )
// );
var mg = pc.GenerateMeshGeometry( levelSize/2f );
meshGeometries.Add( mg );
infos.Add(
RJLog.Stringify(
"Level", i,
"Points", pc.points.Count,
"Size", levelSize._CM(),
"Vertices", mg.numVertices,
"Normals", mg.numNormals,
"UVs", mg.numUV2s,
"Colors", mg.numColors
)
);
var t = MathX.Map( i, level + 1, depth - 1, 1, 0 );
mg.lodEdgeLength = levelSize;
// mg.ApplyTranslation( new Vector3( 100 * t, 0, 0 ) );
var mesh = pc.GenerateMesh( levelSize / 2f );
lodBuilder.Add( levelSize, mesh );
if ( createOutputs )
{
var meshInstance = this.CreateChild<MeshInstance3D>();
meshInstance.Mesh = mesh;
meshInstance.MaterialOverride = material;
levelInstances.Add( meshInstance );
}
}
if ( createOutputs )
{
outputs = levelInstances.ToArray();
}
outputInfos = infos.ToArray();
var lodMesh = this.CreateChild<MeshInstance3D>( "LOD Mesh" );
meshGeometries.Reverse();
var mainGeometry = meshGeometries[ 0 ];
var lerpingData = new LODLerpingData();
lerpingData.lowerLevelWeights = lowerLerp;
lerpingData.higherLevelWeights = higherLerp;
lerpingData.lerpSteps = lerpSteps;
lodMesh.Mesh = MeshGeometry.GeneratePointsLODMesh( meshGeometries, lerpingData, false );
lodMesh.SetSurfaceOverrideMaterial( 0, material );
lod = lodBuilder.Create( this, "LOD" );
lod.lod0 = lod.CreateChild<MeshInstance3D>();
lod.lod0.Mesh = mesh.Mesh;
lod.lod0.MaterialOverride = mesh.GetSurfaceOverrideMaterial( 0 );
lod.distanceScale = 4;
// cloud.SortByDistanceToCenter();
// numPoints = cloud.points.Count;
// squareTexture = (int)Mathf.Pow( numPoints, 0.5f );
// output.Mesh = cloud.GenerateMesh();
working = false;
}
}
}

View File

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

View File

@ -0,0 +1,94 @@
using System.Collections;
using System.Collections.Generic;
using Godot;
using System;
using System.Threading.Tasks;
namespace Rokojori.PointClouds
{
public class PointCloudOcTree:OcTree<Point>
{
List<Vector3> compressedNormals = new List<Vector3>()
{
Vector3.Up,
Vector3.Down,
Vector3.Forward,
Vector3.Back,
Vector3.Right,
Vector3.Left
};
public bool normalSeperation = false;
public PointCloudOcTree( Vector3 min, Vector3 max, float rootCellSize, int maxDepth )
:base( null, min, max, rootCellSize, maxDepth )
{
this._getPosition = GetPointPosition;
this._combinePoints = CombinePoints;
this._smoothPoints = SmoothPoints;
}
protected Vector3 GetPointPosition( Point p )
{
return p.position;
}
protected int GetNormalIndex( Vector3 normal )
{
var closest = -1;
var closestDistance = 100f;
for ( int i = 0; i < compressedNormals.Count; i++ )
{
var d = compressedNormals[ i ].DistanceSquaredTo( normal );
if ( d < closestDistance )
{
closestDistance = d;
closest = i;
}
}
return closest;
}
protected List<Point> CombinePoints( List<Point> points )
{
if ( normalSeperation )
{
var lists = compressedNormals.Map( c => new List<Point>() );
points.ForEach(
( p )=>
{
var normalIndex = GetNormalIndex( p.normal );
lists[ normalIndex ].Add( p );
}
);
lists = lists.Filter( n => n.Count > 0 );
return lists.Map( l => Point.AsAverage( l ) );
}
return new List<Point>(){ Point.AsAverage( points ) };
}
protected List<Point> SmoothPoints( List<Point> targets, List<Point> points, float amount )
{
if ( targets == null || points == null )
{
return targets;
}
var average = Point.AsAverage( points );
return targets.Map( t => t.Lerp( average, amount ) );;
}
}
}

View File

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

View File

@ -0,0 +1,406 @@
using System.Collections;
using System.Collections.Generic;
using Godot;
using System;
using System.Threading.Tasks;
namespace Rokojori.PointClouds
{
public class PointCloudSampler
{
public enum SampleMode
{
Center,
Vertices,
CenterAndVertices,
Density
}
public List<Material> materials;
public List<Texture2DPropertyName> albedoTexture;
public List<ColorPropertyName> albedo;
public float densityResolution = 0.2f;
List<Image> albedoTextureImages;
SampleMode mode;
ArrayMesh mesh;
Node worker;
PointCloud pointCloud;
public PointCloud SampleFromMeshSync( SampleMode sampleMode, ArrayMesh mesh, Node worker )
{
this.mode = sampleMode;
this.mesh = mesh;
this.worker = worker;
pointCloud = new PointCloud();
GrabTextureImages();
for ( int i = 0; i < mesh.GetSurfaceCount(); i++ )
{
SampleSurfaceSync( mesh, i );
}
return pointCloud;
}
public async Task<PointCloud> SampleFromMesh( SampleMode sampleMode, ArrayMesh mesh, Node worker )
{
this.mode = sampleMode;
this.mesh = mesh;
this.worker = worker;
pointCloud = new PointCloud();
try
{
GrabTextureImages();
for ( int i = 0; i < mesh.GetSurfaceCount(); i++ )
{
await SampleSurface( mesh, i );
}
}
catch ( System.Exception e )
{
worker.LogError( e );
}
return pointCloud;
}
void GrabTextureImages()
{
var index = -1;
albedoTextureImages = new List<Image>();
albedoTexture.ForEach(
( at )=>
{
index++;
if ( at == null )
{
albedoTextureImages.Add( null );
}
var material = materials[ index ];
var texture = at.Get( material );
if ( texture == null )
{
albedoTextureImages.Add( null );
return;
}
var image = texture.GetImage();
albedoTextureImages.Add( image );
}
);
// worker.LogInfo( "Added image:", albedoTextureImages.Count );
}
public void SampleSurfaceSync( ArrayMesh mesh, int surface )
{
if ( SampleMode.Density == mode )
{
SampleDensitySync( mesh, surface );
return;
}
SampleSimpleSync( mesh, surface );
return;
}
void SampleDensitySync( ArrayMesh mesh, int surface )
{
var mdt = new MeshDataTool();
mdt.CreateFromSurface( mesh, surface );
var nullTriangles = 0;
var numFaces = mdt.GetFaceCount();
var subDivs = 0;
for ( int i = 0; i < mdt.GetFaceCount(); i++ )
{
// time = await Async.WaitIfExceeded( time, worker );
var positions = new List<Vector3>();
var normals = new List<Vector3>();
var uvs = new List<Vector2>();
for ( int j = 0; j < 3; j++ )
{
var index = mdt.GetFaceVertex( i, j );
var p = mdt.GetVertex( index );
var uv = mdt.GetVertexUV( index );
var n = mdt.GetVertexNormal( index );
positions.Add( p );
uvs.Add( uv );
normals.Add( n );
}
var triangle = Triangle3.CreateFrom( positions );
AddPoints( positions, uvs, normals );
var minArea = densityResolution;
var points = new List<Vector3>();
triangle.GetSubdivisionPoints( points, minArea );
// worker.LogInfo( triangle.area, points.Count );
points.ForEach(
( p )=>
{
var uv = (Vector2) triangle.Lerp( p, uvs[ 0 ], uvs[ 1 ], uvs[ 2 ] );
var n = (Vector3) triangle.Lerp( p, normals[ 0 ], normals[ 1 ], normals[ 2 ] );
AddPoint( p, uv, n );
subDivs ++;
}
);
// if ( triangle.area > minArea || triangle.longestEdgeLength > densityResolution )
// {
// worker.LogInfo( area );
// subDivs++;
// }
// triangle = triangle.Shrink( densityResolution );
// if ( triangle == null )
// {
// nullTriangles++;
// }
// var maxIterations = 1000;
// var iteration = 0;
// while ( iteration < maxIterations && triangle != null && ( triangle.area > minArea || triangle.longestEdgeLength > densityResolution ) )
// {
// var newUVs = positions.Map( p => (Vector2) triangle.Lerp( p, uvs[ 0 ], uvs[ 1 ], uvs[ 2 ] ) );
// var newNormals = positions.Map( p => (Vector3) triangle.Lerp( p, normals[ 0 ], normals[ 1 ], normals[ 2 ] ) );
// positions = triangle.points;
// uvs = newUVs;
// normals = newNormals;
// AddPoints( positions, uvs, normals );
// triangle = triangle.Shrink( densityResolution );
// iteration ++;
// }
}
worker.LogInfo( "Tris:", numFaces, "Nulls:", nullTriangles, "Subs:", subDivs );
}
void AddPoints( List<Vector3> p, List<Vector2> u, List<Vector3> n )
{
for ( int i = 0; i < p.Count; i++ )
{
AddPoint( p[ i ], u[ i ], n[ i ] );
}
}
void AddPoint( Vector3 p, Vector2 u, Vector3 n )
{
var point = new Point();
point.color = Colors.White;
point.position = p;
point.normal = n;
point.uv = u;
pointCloud.points.Add( point );
}
public async Task SampleSurface( ArrayMesh mesh, int surface )
{
await SampleSimple( mesh, surface );
return;
}
void SampleSimpleSync( ArrayMesh mesh, int surface )
{
var mdt = new MeshDataTool();
mdt.CreateFromSurface( mesh, surface );
var sampleVertices = SampleMode.Vertices == mode || SampleMode.CenterAndVertices == mode;
var sampleCenter = SampleMode.Center == mode || SampleMode.CenterAndVertices == mode;
for ( int i = 0; i < mdt.GetFaceCount(); i++ )
{
// time = await Async.WaitIfExceeded( time, worker );
var center = new Vector3( 0, 0, 0 );
var centerColor = new Vector4( 0, 0, 0, 0 );
var centerNormal = new Vector3( 0, 0, 0 );
var centerUV = new Vector2( 0, 0 );
for ( int j = 0; j < 3; j++ )
{
var index = mdt.GetFaceVertex( i, j );
var p = mdt.GetVertex( index );
var uv = mdt.GetVertexUV( index );
var n = mdt.GetVertexNormal( index );
var color = Colors.White;
var image = albedoTextureImages[ surface ];
if ( image != null )
{
color = image.Sample( uv, ColorX.EdgeMode.Repeat );
}
center += p;
centerColor += color.ToVector4();
centerNormal += n;
centerUV += uv;
if ( sampleVertices )
{
var point = new Point();
point.color = color;
point.position = p;
point.normal = n;
point.uv = uv;
pointCloud.points.Add( point );
}
}
if ( sampleCenter )
{
var point = new Point();
point.color = ( centerColor / 3.0f ).ToColor();
point.position = center / 3.0f;
point.normal = centerNormal / 3.0f;
point.uv = centerUV / 3.0f;
pointCloud.points.Add( point );
}
}
}
async Task SampleSimple( ArrayMesh mesh, int surface )
{
var mdt = new MeshDataTool();
mdt.CreateFromSurface( mesh, surface );
var sampleVertices = SampleMode.Vertices == mode || SampleMode.CenterAndVertices == mode;
var sampleCenter = SampleMode.Center == mode || SampleMode.CenterAndVertices == mode;
var time = Async.StartTimer();
var faces = mdt.GetFaceCount();
for ( int i = 0; i < faces; i++ )
{
time = await Async.WaitIfExceeded( time, worker );
if ( i % 10000 == 0 )
{
worker.LogInfo( "Tris:", i + "/" + faces, RegexUtility._FFF( ( 100f * i ) / (float)faces ) );
}
var center = new Vector3( 0, 0, 0 );
var centerColor = new Vector4( 0, 0, 0, 0 );
var centerNormal = new Vector3( 0, 0, 0 );
var centerUV = new Vector2( 0, 0 );
for ( int j = 0; j < 3; j++ )
{
var index = mdt.GetFaceVertex( i, j );
var p = mdt.GetVertex( index );
var uv = mdt.GetVertexUV( index );
var n = mdt.GetVertexNormal( index );
var color = Colors.White;
var image = albedoTextureImages[ surface ];
if ( image != null )
{
color = image.Sample( uv, ColorX.EdgeMode.Repeat );
}
center += p;
centerColor += color.ToVector4();
centerNormal += n;
centerUV += uv;
if ( sampleVertices )
{
var point = new Point();
point.color = color;
point.position = p;
point.normal = n;
point.uv = uv;
pointCloud.points.Add( point );
}
}
if ( sampleCenter )
{
var point = new Point();
point.color = ( centerColor / 3.0f ).ToColor();
point.position = center / 3.0f;
point.normal = centerNormal / 3.0f;
point.uv = centerUV / 3.0f;
pointCloud.points.Add( point );
}
if ( i == 0 )
{
pointCloud.boundingBox = Box3.Create( center, center );
}
else
{
pointCloud.boundingBox.IncludePoint( center );
}
}
}
}
}

View File

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

View File

@ -44,6 +44,21 @@ namespace Rokojori
return b; return b;
} }
public static Box3 Create<T>( List<T> data, System.Func<T,Vector3> getPosition )
{
var min = new Vector3( float.MaxValue, float.MaxValue, float.MaxValue );
var max = new Vector3( -float.MaxValue, -float.MaxValue, -float.MaxValue );
for ( int i = 0; i < data.Count; i++ )
{
var p = getPosition( data[ i ] );
min = min.Min( p );
max = max.Max( p );
}
return Create( min, max );
}
public Vector3 size => max - min; public Vector3 size => max - min;
public void IncludePoint( Vector3 p ) public void IncludePoint( Vector3 p )

View File

@ -114,7 +114,7 @@ namespace Rokojori
if ( i01 == null || ! ContainsPoint( (Vector2) i01 ) ) if ( i01 == null || ! ContainsPoint( (Vector2) i01 ) )
{ {
RJLog.Log( "i01", i01, i01 != null ? ContainsPoint( (Vector2) i01 ) : false ); // RJLog.Log( "i01", i01, i01 != null ? ContainsPoint( (Vector2) i01 ) : false );
return null; return null;
} }
@ -122,7 +122,7 @@ namespace Rokojori
if ( i12 == null || ! ContainsPoint( (Vector2) i12 ) ) if ( i12 == null || ! ContainsPoint( (Vector2) i12 ) )
{ {
RJLog.Log( "i12", i12, i12 != null ? ContainsPoint( (Vector2) i12 ) : false ); // RJLog.Log( "i12", i12, i12 != null ? ContainsPoint( (Vector2) i12 ) : false );
return null; return null;
} }
@ -130,7 +130,7 @@ namespace Rokojori
if ( i20 == null || ! ContainsPoint( (Vector2) i20 ) ) if ( i20 == null || ! ContainsPoint( (Vector2) i20 ) )
{ {
RJLog.Log( "i20", i20, i20 != null ? ContainsPoint( (Vector2) i20 ) : false ); // RJLog.Log( "i20", i20, i20 != null ? ContainsPoint( (Vector2) i20 ) : false );
return null; return null;
} }

View File

@ -30,6 +30,17 @@ namespace Rokojori
_needsUpdate = true; _needsUpdate = true;
} }
public static Triangle3 CreateFrom( List<Vector3> points, int offset = 0 )
{
var t = new Triangle3(
points[ 0 ],
points[ 1 ],
points[ 2 ]
);
return t;
}
public static Triangle3 CreateFrom( MeshGeometry mg, int a, int b, int c ) public static Triangle3 CreateFrom( MeshGeometry mg, int a, int b, int c )
{ {
var t = new Triangle3( var t = new Triangle3(
@ -144,6 +155,7 @@ namespace Rokojori
} }
public float area => ComputeTriangleArea( a, b, c ); public float area => ComputeTriangleArea( a, b, c );
public bool Intersects( Line3 line ) public bool Intersects( Line3 line )
{ {
@ -268,6 +280,11 @@ namespace Rokojori
return LerpCurve3.FromPoints( GetPoint( 0 ), GetPoint( 1 ), GetPoint( 2 ) ); return LerpCurve3.FromPoints( GetPoint( 0 ), GetPoint( 1 ), GetPoint( 2 ) );
} }
public List<Vector3> points => new List<Vector3>(){ a, b, c };
public Vector3 GetPoint( int i ) public Vector3 GetPoint( int i )
{ {
if ( i == 0 ) if ( i == 0 )
@ -286,6 +303,22 @@ namespace Rokojori
return a; return a;
} }
public Line3 GetEdge( int i )
{
var p0 = GetPoint( i );
var p1 = GetPoint( i % 3 );
return Line3.Create( p0, p1 );
}
public float GetEdgeLength( int i )
{
return GetEdge( i ).length;
}
public float longestEdgeLength => MathX.Min( GetEdgeLength( 0 ), GetEdgeLength( 1 ), GetEdgeLength( 2 ) );
public float shortestEdgeLength => MathX.Max( GetEdgeLength( 0 ), GetEdgeLength( 1 ), GetEdgeLength( 2 ) );
void SetPointsToLine( Line3 line, int index ) void SetPointsToLine( Line3 line, int index )
{ {
line.Set( GetPoint( index ), GetPoint( ( index + 1 ) % 3 ) ); line.Set( GetPoint( index ), GetPoint( ( index + 1 ) % 3 ) );
@ -484,8 +517,50 @@ namespace Rokojori
return new Triangle3( Math3D.XYasXZ( t.a ), Math3D.XYasXZ( t.b ), Math3D.XYasXZ( t.c ) ); return new Triangle3( Math3D.XYasXZ( t.a ), Math3D.XYasXZ( t.b ), Math3D.XYasXZ( t.c ) );
} }
public List<Triangle3> CenterSplit()
{
var m = center;
var subs = new List<Triangle3>
{
new Triangle3( a, b, m ),
new Triangle3( b, c, m ),
new Triangle3( c, a, m )
};
return subs;
}
public void GetSubdivisionPoints( List<Vector3> output, float minArea )
{
var processingList = new List<Triangle3>();
processingList.Add( this );
while ( processingList.Count > 0 )
{
var tri = processingList.Shift();
output.Add( tri.center );
var subs = tri.CenterSplit();
subs.ForEach(
st =>
{
if ( st.area > minArea )
{
processingList.Add( st );
}
}
);
}
}
public static bool InsideTriangle( Vector3 point, Vector3 a, Vector3 b, Vector3 c ) public static bool InsideTriangle( Vector3 point, Vector3 a, Vector3 b, Vector3 c )
{ {
var bary = GetBaryCentricCoordinate( point, a, b, c ); var bary = GetBaryCentricCoordinate( point, a, b, c );
@ -533,7 +608,66 @@ namespace Rokojori
return new Vector3( 1f - u - v, v, u ); return new Vector3( 1f - u - v, v, u );
} }
public Vector3? GetBaryCentricCoordinate( Vector3 point )
{
return GetBaryCentricCoordinate( point, a, b, c );
}
public Vector4? Lerp( Vector3 p, Vector4 x, Vector4 y, Vector4 z )
{
var bary = GetBaryCentricCoordinate( p );
if ( bary == null )
{
return null;
}
var w = ( Vector3 ) bary;
return w.Lerp( x, y, z );
}
public Vector3? Lerp( Vector3 p, Vector3 x, Vector3 y, Vector3 z )
{
var bary = GetBaryCentricCoordinate( p );
if ( bary == null )
{
return null;
}
var w = ( Vector3 ) bary;
return w.Lerp( x, y, z );
}
public Vector2? Lerp( Vector3 p, Vector2 x, Vector2 y, Vector2 z )
{
var bary = GetBaryCentricCoordinate( p );
if ( bary == null )
{
return null;
}
var w = ( Vector3 ) bary;
return w.Lerp( x, y, z );
}
public float? Lerp( Vector3 p, float x, float y, float z )
{
var bary = GetBaryCentricCoordinate( p );
if ( bary == null )
{
return null;
}
var w = ( Vector3 ) bary;
return w.Lerp( x, y, z );
}
} }
} }

View File

@ -6,8 +6,23 @@ using System;
namespace Rokojori namespace Rokojori
{ {
public class Math2D public static class Math2D
{ {
public static Vector2I RoundToInt( this Vector2 v )
{
return new Vector2I( Mathf.RoundToInt( v.X ), Mathf.RoundToInt( v.Y ) );
}
public static Vector2I FloorToInt( this Vector2 v )
{
return new Vector2I( Mathf.FloorToInt( v.X ), Mathf.FloorToInt( v.Y ) );
}
public static Vector2I CeilToInt( this Vector2 v )
{
return new Vector2I( Mathf.CeilToInt( v.X ), Mathf.CeilToInt( v.Y ) );
}
public static float LookingAtEachOtherAngle( Vector2 lookDirectionA, Vector2 lookDirectionB ) public static float LookingAtEachOtherAngle( Vector2 lookDirectionA, Vector2 lookDirectionB )
{ {
return Dot( lookDirectionA, lookDirectionB ); return Dot( lookDirectionA, lookDirectionB );
@ -90,5 +105,40 @@ namespace Rokojori
{ {
return clockwise ? Rotate90DegreesRight( v ) : Rotate90DegreesLeft( v ); return clockwise ? Rotate90DegreesRight( v ) : Rotate90DegreesLeft( v );
} }
public static Vector2 ComputeAverage( List<Vector2> points )
{
if ( points == null || points.Count == 0 )
{
return Vector2.Zero;
}
var mean = Vector2.Zero;
for ( int i = 0; i < points.Count; i++ )
{
mean += ( points[ i ] - mean ) / ( i + 1 );
}
return mean;
}
public static Vector2 ComputeAverage<T>( List<T> containers, Func<T,Vector2> getPoint )
{
if ( containers == null || containers.Count == 0 )
{
return Vector2.Zero;
}
var mean = Vector2.Zero;
for ( int i = 0; i < containers.Count; i++ )
{
mean += ( getPoint( containers[ i ] ) - mean ) / ( i + 1 );
}
return mean;
}
} }
} }

View File

@ -28,6 +28,98 @@ namespace Rokojori
return LookingAtEachOther( fromDirection, to - from ); return LookingAtEachOther( fromDirection, to - from );
} }
public static Vector4 Lerp( this Vector3 w, Vector4 x, Vector4 y, Vector4 z )
{
return w.X * x + w.Y * y + w.Z * z;
}
public static Vector3 Lerp( this Vector3 w, Vector3 x, Vector3 y, Vector3 z )
{
return w.X * x + w.Y * y + w.Z * z;
}
public static Vector2 Lerp( this Vector3 w, Vector2 x, Vector2 y, Vector2 z )
{
return w.X * x + w.Y * y + w.Z * z;
}
public static float Lerp( this Vector3 w, float x, float y, float z )
{
return w.X * x + w.Y * y + w.Z * z;
}
public static Vector3 ComputeAverage( List<Vector3> points )
{
if ( points == null || points.Count == 0 )
{
return Vector3.Zero;
}
var mean = Vector3.Zero;
for ( int i = 0; i < points.Count; i++ )
{
mean += ( points[ i ] - mean ) / ( i + 1 );
}
return mean;
}
public static Vector3 ComputeAverage<T>( List<T> containers, Func<T,Vector3> getPoint )
{
if ( containers == null || containers.Count == 0 )
{
return Vector3.Zero;
}
var mean = Vector3.Zero;
for ( int i = 0; i < containers.Count; i++ )
{
mean += ( getPoint( containers[ i ] ) - mean ) / ( i + 1 );
}
return mean;
}
public static Vector4 ComputeAverage( List<Vector4> points )
{
if ( points == null || points.Count == 0 )
{
return Vector4.Zero;
}
var mean = Vector4.Zero;
for ( int i = 0; i < points.Count; i++ )
{
mean += ( points[ i ] - mean ) / ( i + 1 );
}
return mean;
}
public static Vector4 ComputeAverage<T>( List<T> containers, Func<T,Vector4> getPoint )
{
if ( containers == null || containers.Count == 0 )
{
return Vector4.Zero;
}
var mean = Vector4.Zero;
for ( int i = 0; i < containers.Count; i++ )
{
mean += ( getPoint( containers[ i ] ) - mean ) / ( i + 1 );
}
return mean;
}
public static Transform3D TRS( Vector3 translation, Quaternion rotation, Vector3 scale ) public static Transform3D TRS( Vector3 translation, Quaternion rotation, Vector3 scale )
{ {
@ -267,6 +359,40 @@ namespace Rokojori
return quaternion; return quaternion;
} }
public static Vector3 GetMin<T>( List<T> data, Func<T,Vector3> getPosition )
{
var min = new Vector3( float.MaxValue, float.MaxValue, float.MaxValue );
for ( int i = 0; i < data.Count; i++ )
{
min = min.Min( getPosition( data[ i ] ) );
}
return min;
}
public static Vector3 GetMax<T>( List<T> data, Func<T,Vector3> getPosition )
{
var max = new Vector3( -float.MaxValue, -float.MaxValue, -float.MaxValue );
for ( int i = 0; i < data.Count; i++ )
{
max = max.Max( getPosition( data[ i ] ) );
}
return max;
}
public static float MaxDimension( this Vector3 v )
{
return MathX.Max( v.X, v.Y, v.Z );
}
public static float MinDimension( this Vector3 v )
{
return MathX.Min( v.X, v.Y, v.Z );
}
public static Vector3 MinGlobalPosition( Node3D a, Node3D b ) public static Vector3 MinGlobalPosition( Node3D a, Node3D b )
{ {
return a.GlobalPosition.Min( b.GlobalPosition ); return a.GlobalPosition.Min( b.GlobalPosition );
@ -287,7 +413,7 @@ namespace Rokojori
return a.Position.Max( b.Position ); return a.Position.Max( b.Position );
} }
public static Vector3 SnapRounded( Vector3 v, Vector3 snapping ) public static Vector3 SnapRounded( this Vector3 v, Vector3 snapping )
{ {
v.X = MathX.SnapRounded( v.X, snapping.X ); v.X = MathX.SnapRounded( v.X, snapping.X );
v.Y = MathX.SnapRounded( v.Y, snapping.Y ); v.Y = MathX.SnapRounded( v.Y, snapping.Y );
@ -296,7 +422,7 @@ namespace Rokojori
return v; return v;
} }
public static Vector3 SnapRoundedXZ( Vector3 v, float snapX, float snapZ ) public static Vector3 SnapRoundedXZ( this Vector3 v, float snapX, float snapZ )
{ {
v.X = MathX.SnapRounded( v.X, snapX ); v.X = MathX.SnapRounded( v.X, snapX );
v.Z = MathX.SnapRounded( v.Z, snapZ ); v.Z = MathX.SnapRounded( v.Z, snapZ );
@ -304,7 +430,7 @@ namespace Rokojori
return v; return v;
} }
public static Vector3 SnapCeiled( Vector3 v, Vector3 snapping ) public static Vector3 SnapCeiled( this Vector3 v, Vector3 snapping )
{ {
v.X = MathX.SnapCeiled( v.X, snapping.X ); v.X = MathX.SnapCeiled( v.X, snapping.X );
v.Y = MathX.SnapCeiled( v.Y, snapping.Y ); v.Y = MathX.SnapCeiled( v.Y, snapping.Y );
@ -313,7 +439,7 @@ namespace Rokojori
return v; return v;
} }
public static Vector3 SnapFloored( Vector3 v, Vector3 snapping ) public static Vector3 SnapFloored( this Vector3 v, Vector3 snapping )
{ {
v.X = MathX.SnapFloored( v.X, snapping.X ); v.X = MathX.SnapFloored( v.X, snapping.X );
v.Y = MathX.SnapFloored( v.Y, snapping.Y ); v.Y = MathX.SnapFloored( v.Y, snapping.Y );
@ -322,12 +448,58 @@ namespace Rokojori
return v; return v;
} }
public static Vector3I RoundToInt( this Vector3 v )
{
return new Vector3I( Mathf.RoundToInt( v.X ) , Mathf.RoundToInt( v.Y ), Mathf.RoundToInt( v.Z ) );
}
public static Vector3I FloorToInt( this Vector3 v )
{
return new Vector3I( Mathf.FloorToInt( v.X ) , Mathf.FloorToInt( v.Y ), Mathf.FloorToInt( v.Z ) );
}
public static Vector3I CeilToInt( this Vector3 v )
{
return new Vector3I( Mathf.CeilToInt( v.X ) , Mathf.CeilToInt( v.Y ), Mathf.CeilToInt( v.Z ) );
}
public static Vector2 XY( this Vector3 v )
{
return new Vector2( v.X, v.Y );
}
public static Vector2 YX( this Vector3 v )
{
return new Vector2( v.Y, v.X );
}
public static Vector2 XZ( this Vector3 v )
{
return new Vector2( v.X, v.Z );
}
public static Vector2 ZX( this Vector3 v )
{
return new Vector2( v.Z, v.X );
}
public static Vector2 YZ( this Vector3 v )
{
return new Vector2( v.Y, v.Z );
}
public static Vector2 ZY( this Vector3 v )
{
return new Vector2( v.Y, v.Z );
}
public static Basis AlignUp( Basis basis, Vector3 upDirection ) public static Basis AlignUp( Basis basis, Vector3 upDirection )
{ {
basis.Y = upDirection; basis.Y = upDirection;
basis.X = - basis.Z.Cross( upDirection ); basis.X = - basis.Z.Cross( upDirection );
return basis.Orthonormalized(); return basis.Orthonormalized();
} }
public static Quaternion AlignUp( Quaternion rotation, Vector3 upDirection ) public static Quaternion AlignUp( Quaternion rotation, Vector3 upDirection )
{ {

View File

@ -6,7 +6,7 @@ using System;
namespace Rokojori namespace Rokojori
{ {
public class MathX public static class MathX
{ {
public const float fps120Delta = 1/120f; public const float fps120Delta = 1/120f;
@ -143,6 +143,60 @@ namespace Rokojori
return Mathf.Atan2( circle.Y, circle.X ); return Mathf.Atan2( circle.Y, circle.X );
} }
public static int MultiIndexToFlatIndex( List<int> indices, List<int> sizes )
{
var index = 0;
var scale = 1;
for ( int i = sizes.Count - 1; i >= 0; i-- )
{
index += indices[ i ] * scale;
scale *= sizes[ i ];
}
return index;
}
public static int MultiIndexToFlatIndex( Vector3I indices, Vector3I sizes )
{
var index = 0;
index += indices.Z;
index += indices.Y * sizes.Z;
index += indices.X * sizes.Z * sizes.Y;
return index;
}
public static List<int> FlatIndexToMultiIndex( int index, List<int> sizes, List<int> multiIndex = null )
{
multiIndex = multiIndex == null ? new List<int>( new int[ sizes.Count ] ) : multiIndex;
for ( int i = sizes.Count - 1; i >= 0; i-- )
{
multiIndex[ i ] = index % sizes[ i ];
index /= sizes[i];
}
return multiIndex;
}
public static Vector3I FlatIndexToMultiIndex( int index, Vector3I sizes )
{
var multiIndex = Vector3I.Zero;
multiIndex.Z = index % sizes.Z;
index /= sizes.Z;
multiIndex.Y = index % sizes.Y;
index /= sizes.Y;
multiIndex.X = index % sizes.X;
return multiIndex;
}
public const float DegreesToRadians = Mathf.Pi / 180f; public const float DegreesToRadians = Mathf.Pi / 180f;
public const float RadiansToDegrees = 180f / Mathf.Pi; public const float RadiansToDegrees = 180f / Mathf.Pi;
@ -428,6 +482,23 @@ namespace Rokojori
return max; return max;
} }
public static float CurveAverage( this Curve c, int numSamples = 20 )
{
if ( c == null )
{
return 0;
}
var value = 0f;
for ( int i = 0; i < numSamples; i++ )
{
value += c.Sample( (float) i / ( numSamples - 1 ) );
}
return value / numSamples;
}
public static List<float> GetCurveWeights( Curve curve, int num, bool normalize = true ) public static List<float> GetCurveWeights( Curve curve, int num, bool normalize = true )
{ {
var sum = 0f; var sum = 0f;

View File

@ -3,7 +3,7 @@ using System.Collections.Generic;
namespace Rokojori namespace Rokojori
{ {
[GlobalClass] [Tool][GlobalClass]
public partial class LANNetworkBackend:NetworkBackend public partial class LANNetworkBackend:NetworkBackend
{ {
SceneMultiplayer multiplayer; SceneMultiplayer multiplayer;

View File

@ -3,7 +3,7 @@ using Godot;
namespace Rokojori namespace Rokojori
{ {
[GlobalClass,Icon("res://addons/rokojori_action_library/Icons/NetworkManager.svg")] [Tool][GlobalClass,Icon("res://addons/rokojori_action_library/Icons/NetworkManager.svg")]
public partial class NetworkManager:Node public partial class NetworkManager:Node
{ {
[Export] [Export]

View File

@ -4,7 +4,7 @@ using System.Collections.Generic;
namespace Rokojori namespace Rokojori
{ {
[GlobalClass] [Tool][GlobalClass]
public partial class AddNetworkNodes:Action public partial class AddNetworkNodes:Action
{ {
[Export] [Export]

View File

@ -4,7 +4,7 @@ using System.Collections.Generic;
namespace Rokojori namespace Rokojori
{ {
[GlobalClass] [Tool][GlobalClass]
public partial class JoinSession:Action public partial class JoinSession:Action
{ {
[Export] [Export]

View File

@ -4,7 +4,7 @@ using System.Collections.Generic;
namespace Rokojori namespace Rokojori
{ {
[GlobalClass] [Tool][GlobalClass]
public partial class NetworkSessionRequest:Resource public partial class NetworkSessionRequest:Resource
{ {
[Export] [Export]

View File

@ -4,7 +4,7 @@ using System.Collections.Generic;
namespace Rokojori namespace Rokojori
{ {
[GlobalClass] [Tool][GlobalClass]
public partial class StartSession:Action public partial class StartSession:Action
{ {
[Export] [Export]

View File

@ -4,7 +4,7 @@ using System.Collections.Generic;
namespace Rokojori namespace Rokojori
{ {
[GlobalClass] [Tool][GlobalClass]
public partial class NetworkTransform3D:NetworkNode public partial class NetworkTransform3D:NetworkNode
{ {
[Export] [Export]

View File

@ -4,7 +4,7 @@ using System.Collections.Generic;
namespace Rokojori namespace Rokojori
{ {
[GlobalClass] [Tool][GlobalClass]
public partial class NetworkTransform3DType:Resource public partial class NetworkTransform3DType:Resource
{ {
[ExportCategory("Position")] [ExportCategory("Position")]

View File

@ -2,6 +2,7 @@ using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using Godot; using Godot;
using System; using System;
using System.Threading.Tasks;
@ -24,6 +25,9 @@ namespace Rokojori
[Export] [Export]
public bool updateAlways; public bool updateAlways;
[Export]
public Material material;
[ExportGroup( "Patch")] [ExportGroup( "Patch")]
[Export] [Export]
public float patchSize = 2; public float patchSize = 2;
@ -233,6 +237,21 @@ namespace Rokojori
[Export] [Export]
public int currentLODLevel = -1; public int currentLODLevel = -1;
[Export]
public bool generateLODMesh = false;
[Export]
public float customLODEdgeLength = 0;
[Export]
public int lodLerpSteps = 2;
[Export]
public Curve lowCurve = MathX.Curve( 0, 1 );
[Export]
public Curve highCurve = MathX.Curve( 0, 1 );
// SerializedGodotObject _cached; // SerializedGodotObject _cached;
public override void _Process( double delta ) public override void _Process( double delta )
@ -287,7 +306,7 @@ namespace Rokojori
return bladeSegments * 2 * ComputeNumBlades(); return bladeSegments * 2 * ComputeNumBlades();
} }
public void CreatePatch() public async Task CreatePatch()
{ {
if ( blades == 0 && bladesX == 0 && bladesZ == 0) if ( blades == 0 && bladesX == 0 && bladesZ == 0)
{ {
@ -311,18 +330,12 @@ namespace Rokojori
lodLevels = new GrassPatchLODLevel[]{}; lodLevels = new GrassPatchLODLevel[]{};
} }
if ( output == null )
{
this.output = this.CreateChild<MeshInstance3D>( "Grass Patch Mesh" );
}
var mg = new MeshGeometry(); this.DestroyChildren();
this.output = this.CreateChild<MeshInstance3D>( "Grass Patch Mesh" );
var cellSizeX = ( patchSizeX + patchSize ) / ( bladesX + blades );
var cellSizeZ = ( patchSizeZ + patchSize ) / ( bladesZ + blades );
var random = new LCG(); var random = new LCG();
random.SetSeed( 1712 + seed ); random.SetSeed( 1712 + seed );
@ -330,7 +343,97 @@ namespace Rokojori
_maxWidth = MathX.CurveMaximum( bladeWidth ); _maxWidth = MathX.CurveMaximum( bladeWidth );
X_numBlades = 0; X_numBlades = 0;
if ( ! generateLODMesh )
{
var mg = CreatePatchGeometry();
X_numTriangles = mg.indices.Count / 3;
output.Mesh = mg.GenerateMesh();
output.Mesh.SurfaceSetMaterial( 0, material );
}
else
{
var cachedCurrentLODLevel = currentLODLevel;
try
{
var mgs = new List<MeshGeometry>();
var numTris = 0;
var time = this.StartAsyncTimer();
var edgeLength = ( MathX.CurveAverage( bladeHeight ) * MathX.CurveAverage( bladeScale ) ) / bladeSegments;
if ( customLODEdgeLength > 0 )
{
edgeLength = customLODEdgeLength;
}
this.LogInfo( "Edge Length", edgeLength );
for ( int i = 0; i < lodLevels.Length + 1; i++ )
{
time = await this.WaitForAsyncTimer( time );
var lodIndex = i - 1;
currentLODLevel = lodIndex;
var lodEdgeLengthScale = 1f;
if ( lodIndex >= 0 )
{
lodEdgeLengthScale = lodLevels[ lodIndex ].lodEdgeLengthScale;
}
var lodMG = CreatePatchGeometry();
lodMG.lodEdgeLength = edgeLength * lodEdgeLengthScale;
numTris += lodMG.vertices.Count;
lodMG.ApplyTranslation( new Vector3( 0, 0, 0 ) ) ;
mgs.Add( lodMG );
}
X_numTriangles = numTris;
LODLerpingData lerpingData = null;
if ( lodLerpSteps > 0 )
{
lerpingData = new LODLerpingData();
lerpingData.lowerLevelWeights = lowCurve;
lerpingData.higherLevelWeights = highCurve;
lerpingData.lerpSteps = lodLerpSteps;
}
output.Mesh = MeshGeometry.GenerateTrianglesLODMesh( mgs, lerpingData, false );
output.Mesh.SurfaceSetMaterial( 0, material );
}
catch ( System.Exception e )
{
this.LogError( e );
}
currentLODLevel = cachedCurrentLODLevel;
}
}
bool debugBlade = false;
MeshGeometry CreatePatchGeometry()
{
var random = new LCG();
random.SetSeed( 1712 + seed );
var mg = new MeshGeometry();
var cellSizeX = ( patchSizeX + patchSize ) / ( bladesX + blades );
var cellSizeZ = ( patchSizeZ + patchSize ) / ( bladesZ + blades );
var allBladesX = bladesX + blades; var allBladesX = bladesX + blades;
var allBladesZ = bladesZ + blades; var allBladesZ = bladesZ + blades;
@ -368,6 +471,11 @@ namespace Rokojori
var bladeMG = CreateBlade( random, worldPosition ); var bladeMG = CreateBlade( random, worldPosition );
if ( bladeMG.numNormals == 0 || bladeMG.numUVs == 0 )
{
this.LogInfo( "Invalid mg" );
}
if ( filterValue > filterTreshold ) if ( filterValue > filterTreshold )
{ {
@ -417,6 +525,11 @@ namespace Rokojori
} }
} }
if ( mg.numNormals == 0 || mg.numUVs == 0 )
{
this.LogInfo( "Invalid mg" );
}
if ( centerPatch ) if ( centerPatch )
{ {
@ -431,6 +544,11 @@ namespace Rokojori
var turbulenceAmount = random.Sample( vertexTurbulenceAmount ); var turbulenceAmount = random.Sample( vertexTurbulenceAmount );
var turbulenceScale = Vector3.One * ( vertexTurbulenceScale == null ? 1 : random.Sample( vertexTurbulenceScale ) ); var turbulenceScale = Vector3.One * ( vertexTurbulenceScale == null ? 1 : random.Sample( vertexTurbulenceScale ) );
if ( currentLODLevel != -1 )
{
turbulenceAmount *= lodLevels[ currentLODLevel ].turbulenceScale;
}
if ( vertexTurbulenceScaleX != null ) if ( vertexTurbulenceScaleX != null )
{ {
turbulenceScale.X *= random.Sample( vertexTurbulenceScaleX ); turbulenceScale.X *= random.Sample( vertexTurbulenceScaleX );
@ -479,16 +597,10 @@ namespace Rokojori
mg.ScaleZForY( scaleZForY ); mg.ScaleZForY( scaleZForY );
} }
return mg;
X_numTriangles = mg.indices.Count / 3;
output.Mesh = mg.GenerateMesh();
} }
bool debugBlade = false;
MeshGeometry CreateBlade( RandomEngine random, Vector3 position ) MeshGeometry CreateBlade( RandomEngine random, Vector3 position )
{ {
// if ( debugBlade ) // if ( debugBlade )

View File

@ -23,8 +23,14 @@ namespace Rokojori
[Export( PropertyHint.Range, "0,1")] [Export( PropertyHint.Range, "0,1")]
public float filter = 1; public float filter = 1;
[Export]
public float turbulenceScale = 0f;
[Export] [Export]
public Trillean allowTrianglesOnEnd; public Trillean allowTrianglesOnEnd;
[Export]
public float lodEdgeLengthScale = 1;
} }
} }

View File

@ -5,7 +5,6 @@ render_mode blend_mix, depth_draw_opaque, cull_back, diffuse_burley, specular_sc
#include "res://addons/rokojori_action_library/Runtime/Shading/Library/Math.gdshaderinc" #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/Transform.gdshaderinc"
#include "res://addons/rokojori_action_library/Runtime/Shading/Library/Noise.gdshaderinc"
#include "res://addons/rokojori_action_library/Runtime/Shading/Library/Colors.gdshaderinc" #include "res://addons/rokojori_action_library/Runtime/Shading/Library/Colors.gdshaderinc"
uniform vec4 albedo : source_color; uniform vec4 albedo : source_color;

View File

@ -0,0 +1,61 @@
using System.Collections;
using System.Collections.Generic;
using Godot;
using System;
using System.Linq;
using System.Threading.Tasks;
namespace Rokojori
{
[Tool]
[GlobalClass]
public partial class PixelDensityTool:Node
{
[Export]
public float fovDegrees = 60;
[Export]
public Vector2 screenSize = new Vector2( 1920, 1080 );
[Export]
public float startDistance = 1;
[Export]
public float endDistance = 4000;
[Export]
public int numEntries = 20;
[Export]
public float densityTablePower = 2;
[ExportToolButton( "Compute Pixel Density Tables")]
public Callable ExecuteButton => Callable.From( () =>
{
var list = new List<string>();
for ( int i = 0; i < numEntries; i++ )
{
var t = i / ( float ) ( numEntries - 1f );
t = Mathf.Pow( t, densityTablePower );
var distance = Mathf.Lerp( startDistance, endDistance, t );
var pixelDensity = Cameras.ComputePixelDensityVertical( fovDegrees, distance, screenSize );
var minSize = 1f / pixelDensity;
list.Add( "[" + distance._M()+ "] density: " + pixelDensity._FF() + "px/m - size:" + ( minSize < 1 ? minSize._CM() : minSize._M() ) + " " );
}
distanceToPixelSize = list.ToArray();
}
);
[Export]
public string[] distanceToPixelSize;
}
}

View File

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

View File

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

Some files were not shown because too many files have changed in this diff Show More