Merge branch 'master' into mac
This commit is contained in:
@@ -137,7 +137,7 @@ extern FLAXENGINE_API const Char* ToString(const BuildConfiguration configuratio
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Game cooking temporary data.
|
/// Game cooking temporary data.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
API_CLASS(Sealed, Namespace="FlaxEditor") class FLAXENGINE_API CookingData : public PersistentScriptingObject
|
API_CLASS(Sealed, Namespace="FlaxEditor") class FLAXENGINE_API CookingData : public ScriptingObject
|
||||||
{
|
{
|
||||||
DECLARE_SCRIPTING_TYPE(CookingData);
|
DECLARE_SCRIPTING_TYPE(CookingData);
|
||||||
public:
|
public:
|
||||||
|
|||||||
@@ -171,7 +171,7 @@ CookingData::Statistics::Statistics()
|
|||||||
}
|
}
|
||||||
|
|
||||||
CookingData::CookingData(const SpawnParams& params)
|
CookingData::CookingData(const SpawnParams& params)
|
||||||
: PersistentScriptingObject(params)
|
: ScriptingObject(params)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -179,12 +179,12 @@ bool UWPPlatformTools::OnDeployBinaries(CookingData& data)
|
|||||||
if (file)
|
if (file)
|
||||||
{
|
{
|
||||||
auto now = DateTime::Now();
|
auto now = DateTime::Now();
|
||||||
file->WriteTextFormatted(
|
file->WriteText(StringAnsi::Format(
|
||||||
fileTemplate.Get()
|
fileTemplate.Get()
|
||||||
, gameSettings->ProductName.ToStringAnsi()
|
, gameSettings->ProductName.ToStringAnsi()
|
||||||
, gameSettings->CompanyName.ToStringAnsi()
|
, gameSettings->CompanyName.ToStringAnsi()
|
||||||
, now.GetYear()
|
, now.GetYear()
|
||||||
);
|
));
|
||||||
hasError = file->HasError();
|
hasError = file->HasError();
|
||||||
Delete(file);
|
Delete(file);
|
||||||
}
|
}
|
||||||
@@ -210,10 +210,10 @@ bool UWPPlatformTools::OnDeployBinaries(CookingData& data)
|
|||||||
bool hasError = true;
|
bool hasError = true;
|
||||||
if (file)
|
if (file)
|
||||||
{
|
{
|
||||||
file->WriteTextFormatted(
|
file->WriteText(StringAnsi::Format(
|
||||||
fileTemplate.Get()
|
fileTemplate.Get()
|
||||||
, defaultNamespace.ToStringAnsi() // {0} Default Namespace
|
, defaultNamespace.ToStringAnsi() // {0} Default Namespace
|
||||||
);
|
));
|
||||||
hasError = file->HasError();
|
hasError = file->HasError();
|
||||||
Delete(file);
|
Delete(file);
|
||||||
}
|
}
|
||||||
@@ -264,11 +264,11 @@ bool UWPPlatformTools::OnDeployBinaries(CookingData& data)
|
|||||||
bool hasError = true;
|
bool hasError = true;
|
||||||
if (file)
|
if (file)
|
||||||
{
|
{
|
||||||
file->WriteTextFormatted(
|
file->WriteText(StringAnsi::Format(
|
||||||
fileTemplate.Get()
|
fileTemplate.Get()
|
||||||
, autoRotationPreferences.Get()
|
, autoRotationPreferences.Get()
|
||||||
, preferredLaunchWindowingMode.Get()
|
, preferredLaunchWindowingMode.Get()
|
||||||
);
|
));
|
||||||
hasError = file->HasError();
|
hasError = file->HasError();
|
||||||
Delete(file);
|
Delete(file);
|
||||||
}
|
}
|
||||||
@@ -296,12 +296,12 @@ bool UWPPlatformTools::OnDeployBinaries(CookingData& data)
|
|||||||
bool hasError = true;
|
bool hasError = true;
|
||||||
if (file)
|
if (file)
|
||||||
{
|
{
|
||||||
file->WriteTextFormatted(
|
file->WriteText(StringAnsi::Format(
|
||||||
fileTemplate.Get()
|
fileTemplate.Get()
|
||||||
, projectName.ToStringAnsi() // {0} Project Name
|
, projectName.ToStringAnsi() // {0} Project Name
|
||||||
, mode // {1} Platform Mode
|
, mode // {1} Platform Mode
|
||||||
, projectGuid.ToStringAnsi() // {2} Project ID
|
, projectGuid.ToStringAnsi() // {2} Project ID
|
||||||
);
|
));
|
||||||
hasError = file->HasError();
|
hasError = file->HasError();
|
||||||
Delete(file);
|
Delete(file);
|
||||||
}
|
}
|
||||||
@@ -344,14 +344,14 @@ bool UWPPlatformTools::OnDeployBinaries(CookingData& data)
|
|||||||
bool hasError = true;
|
bool hasError = true;
|
||||||
if (file)
|
if (file)
|
||||||
{
|
{
|
||||||
file->WriteTextFormatted(
|
file->WriteText(StringAnsi::Format(
|
||||||
fileTemplate.Get()
|
fileTemplate.Get()
|
||||||
, projectName.ToStringAnsi() // {0} Project Name
|
, projectName.ToStringAnsi() // {0} Project Name
|
||||||
, mode // {1} Platform Mode
|
, mode // {1} Platform Mode
|
||||||
, projectGuid.Get() // {2} Project ID
|
, projectGuid.Get() // {2} Project ID
|
||||||
, filesInclude.ToString().ToStringAnsi() // {3} Files to include
|
, filesInclude.ToString().ToStringAnsi() // {3} Files to include
|
||||||
, defaultNamespace.ToStringAnsi() // {4} Default Namespace
|
, defaultNamespace.ToStringAnsi() // {4} Default Namespace
|
||||||
);
|
));
|
||||||
hasError = file->HasError();
|
hasError = file->HasError();
|
||||||
Delete(file);
|
Delete(file);
|
||||||
}
|
}
|
||||||
@@ -394,13 +394,13 @@ bool UWPPlatformTools::OnDeployBinaries(CookingData& data)
|
|||||||
bool hasError = true;
|
bool hasError = true;
|
||||||
if (file)
|
if (file)
|
||||||
{
|
{
|
||||||
file->WriteTextFormatted(
|
file->WriteText(StringAnsi::Format(
|
||||||
fileTemplate.Get()
|
fileTemplate.Get()
|
||||||
, projectName.ToStringAnsi() // {0} Display Name
|
, projectName.ToStringAnsi() // {0} Display Name
|
||||||
, gameSettings->CompanyName.ToStringAnsi() // {1} Company Name
|
, gameSettings->CompanyName.ToStringAnsi() // {1} Company Name
|
||||||
, productId.ToStringAnsi() // {2} Product ID
|
, productId.ToStringAnsi() // {2} Product ID
|
||||||
, defaultNamespace.ToStringAnsi() // {3} Default Namespace
|
, defaultNamespace.ToStringAnsi() // {3} Default Namespace
|
||||||
);
|
));
|
||||||
hasError = file->HasError();
|
hasError = file->HasError();
|
||||||
Delete(file);
|
Delete(file);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -102,6 +102,16 @@ namespace FlaxEditor.CustomEditors
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
protected virtual void OnModified()
|
protected virtual void OnModified()
|
||||||
{
|
{
|
||||||
|
var parent = ParentEditor;
|
||||||
|
while (parent != null)
|
||||||
|
{
|
||||||
|
if (parent is SyncPointEditor syncPointEditor)
|
||||||
|
{
|
||||||
|
syncPointEditor.OnModified();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
parent = parent.ParentEditor;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ namespace FlaxEditor.GUI.Timeline
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="undo">The undo/redo to use for the history actions recording. Optional, can be null to disable undo support.</param>
|
/// <param name="undo">The undo/redo to use for the history actions recording. Optional, can be null to disable undo support.</param>
|
||||||
public AnimationTimeline(FlaxEditor.Undo undo)
|
public AnimationTimeline(FlaxEditor.Undo undo)
|
||||||
: base(PlaybackButtons.Play | PlaybackButtons.Stop, undo, false, false)
|
: base(PlaybackButtons.Play | PlaybackButtons.Stop, undo, false, true)
|
||||||
{
|
{
|
||||||
PlaybackState = PlaybackStates.Seeking;
|
PlaybackState = PlaybackStates.Seeking;
|
||||||
ShowPreviewValues = false;
|
ShowPreviewValues = false;
|
||||||
@@ -61,6 +61,7 @@ namespace FlaxEditor.GUI.Timeline
|
|||||||
// Setup track types
|
// Setup track types
|
||||||
TrackArchetypes.Add(AnimationChannelTrack.GetArchetype());
|
TrackArchetypes.Add(AnimationChannelTrack.GetArchetype());
|
||||||
TrackArchetypes.Add(AnimationChannelDataTrack.GetArchetype());
|
TrackArchetypes.Add(AnimationChannelDataTrack.GetArchetype());
|
||||||
|
TrackArchetypes.Add(AnimationEventTrack.GetArchetype());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -16,8 +16,8 @@ namespace FlaxEditor.GUI.Timeline.GUI
|
|||||||
private readonly Timeline _timeline;
|
private readonly Timeline _timeline;
|
||||||
private float[] _tickSteps;
|
private float[] _tickSteps;
|
||||||
private float[] _tickStrengths;
|
private float[] _tickStrengths;
|
||||||
private bool _leftMouseDown;
|
private bool _isSelecting;
|
||||||
private Vector2 _leftMouseDownPos = Vector2.Minimum;
|
private Vector2 _selectingStartPos = Vector2.Minimum;
|
||||||
private Vector2 _mousePos = Vector2.Minimum;
|
private Vector2 _mousePos = Vector2.Minimum;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -33,7 +33,7 @@ namespace FlaxEditor.GUI.Timeline.GUI
|
|||||||
|
|
||||||
private void UpdateSelectionRectangle()
|
private void UpdateSelectionRectangle()
|
||||||
{
|
{
|
||||||
var selectionRect = Rectangle.FromPoints(_leftMouseDownPos, _mousePos);
|
var selectionRect = Rectangle.FromPoints(_selectingStartPos, _mousePos);
|
||||||
_timeline.OnKeyframesSelection(null, this, selectionRect);
|
_timeline.OnKeyframesSelection(null, this, selectionRect);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -41,20 +41,17 @@ namespace FlaxEditor.GUI.Timeline.GUI
|
|||||||
public override bool OnMouseDown(Vector2 location, MouseButton button)
|
public override bool OnMouseDown(Vector2 location, MouseButton button)
|
||||||
{
|
{
|
||||||
if (base.OnMouseDown(location, button))
|
if (base.OnMouseDown(location, button))
|
||||||
{
|
|
||||||
_leftMouseDown = false;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
|
||||||
|
|
||||||
_mousePos = location;
|
_mousePos = location;
|
||||||
if (button == MouseButton.Left)
|
if (button == MouseButton.Left)
|
||||||
{
|
{
|
||||||
// Start selecting
|
// Start selecting
|
||||||
_leftMouseDown = true;
|
_isSelecting = true;
|
||||||
_leftMouseDownPos = location;
|
_selectingStartPos = location;
|
||||||
StartMouseCapture();
|
|
||||||
_timeline.OnKeyframesDeselect(null);
|
_timeline.OnKeyframesDeselect(null);
|
||||||
Focus();
|
Focus();
|
||||||
|
StartMouseCapture();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -65,19 +62,16 @@ namespace FlaxEditor.GUI.Timeline.GUI
|
|||||||
public override bool OnMouseUp(Vector2 location, MouseButton button)
|
public override bool OnMouseUp(Vector2 location, MouseButton button)
|
||||||
{
|
{
|
||||||
_mousePos = location;
|
_mousePos = location;
|
||||||
|
if (_isSelecting && button == MouseButton.Left)
|
||||||
if (_leftMouseDown && button == MouseButton.Left)
|
|
||||||
{
|
{
|
||||||
// End selecting
|
// End selecting
|
||||||
_leftMouseDown = false;
|
_isSelecting = false;
|
||||||
EndMouseCapture();
|
EndMouseCapture();
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (base.OnMouseUp(location, button))
|
if (base.OnMouseUp(location, button))
|
||||||
{
|
|
||||||
_leftMouseDown = false;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -88,7 +82,7 @@ namespace FlaxEditor.GUI.Timeline.GUI
|
|||||||
_mousePos = location;
|
_mousePos = location;
|
||||||
|
|
||||||
// Selecting
|
// Selecting
|
||||||
if (_leftMouseDown)
|
if (_isSelecting)
|
||||||
{
|
{
|
||||||
UpdateSelectionRectangle();
|
UpdateSelectionRectangle();
|
||||||
return;
|
return;
|
||||||
@@ -100,11 +94,24 @@ namespace FlaxEditor.GUI.Timeline.GUI
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override void OnLostFocus()
|
public override void OnLostFocus()
|
||||||
{
|
{
|
||||||
_leftMouseDown = false;
|
if (_isSelecting)
|
||||||
|
{
|
||||||
|
_isSelecting = false;
|
||||||
|
EndMouseCapture();
|
||||||
|
}
|
||||||
|
|
||||||
base.OnLostFocus();
|
base.OnLostFocus();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override void OnEndMouseCapture()
|
||||||
|
{
|
||||||
|
_isSelecting = false;
|
||||||
|
EndMouseCapture();
|
||||||
|
|
||||||
|
base.OnEndMouseCapture();
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override bool IntersectsContent(ref Vector2 locationParent, out Vector2 location)
|
public override bool IntersectsContent(ref Vector2 locationParent, out Vector2 location)
|
||||||
{
|
{
|
||||||
@@ -217,9 +224,9 @@ namespace FlaxEditor.GUI.Timeline.GUI
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Draw selection rectangle
|
// Draw selection rectangle
|
||||||
if (_leftMouseDown)
|
if (_isSelecting)
|
||||||
{
|
{
|
||||||
var selectionRect = Rectangle.FromPoints(_leftMouseDownPos, _mousePos);
|
var selectionRect = Rectangle.FromPoints(_selectingStartPos, _mousePos);
|
||||||
Render2D.FillRectangle(selectionRect, Color.Orange * 0.4f);
|
Render2D.FillRectangle(selectionRect, Color.Orange * 0.4f);
|
||||||
Render2D.DrawRectangle(selectionRect, Color.Orange);
|
Render2D.DrawRectangle(selectionRect, Color.Orange);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -91,7 +91,7 @@ namespace FlaxEditor.GUI.Timeline
|
|||||||
/// <param name="media">The media.</param>
|
/// <param name="media">The media.</param>
|
||||||
protected ProxyBase(TTrack track, TMedia media)
|
protected ProxyBase(TTrack track, TMedia media)
|
||||||
{
|
{
|
||||||
Track = track ?? throw new ArgumentNullException(nameof(track));
|
Track = track;
|
||||||
Media = media ?? throw new ArgumentNullException(nameof(media));
|
Media = media ?? throw new ArgumentNullException(nameof(media));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -215,7 +215,7 @@ namespace FlaxEditor.GUI.Timeline
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets a value indicating whether this media can be resized (duration changed).
|
/// Gets a value indicating whether this media can be resized (duration changed).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool CanResize;
|
public bool CanResize = true;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="Media"/> class.
|
/// Initializes a new instance of the <see cref="Media"/> class.
|
||||||
@@ -229,8 +229,9 @@ namespace FlaxEditor.GUI.Timeline
|
|||||||
/// Called when showing timeline context menu to the user. Can be used to add custom buttons.
|
/// Called when showing timeline context menu to the user. Can be used to add custom buttons.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="menu">The menu.</param>
|
/// <param name="menu">The menu.</param>
|
||||||
|
/// <param name="time">The time (in seconds) at which context menu is shown (user clicked on a timeline).</param>
|
||||||
/// <param name="controlUnderMouse">The found control under the mouse cursor.</param>
|
/// <param name="controlUnderMouse">The found control under the mouse cursor.</param>
|
||||||
public virtual void OnTimelineShowContextMenu(ContextMenu.ContextMenu menu, Control controlUnderMouse)
|
public virtual void OnTimelineContextMenu(ContextMenu.ContextMenu menu, float time, Control controlUnderMouse)
|
||||||
{
|
{
|
||||||
if (CanDelete && Track.Media.Count > Track.MinMediaCount)
|
if (CanDelete && Track.Media.Count > Track.MinMediaCount)
|
||||||
menu.AddButton("Delete media", Delete);
|
menu.AddButton("Delete media", Delete);
|
||||||
@@ -341,14 +342,15 @@ namespace FlaxEditor.GUI.Timeline
|
|||||||
var style = Style.Current;
|
var style = Style.Current;
|
||||||
var bounds = new Rectangle(Vector2.Zero, Size);
|
var bounds = new Rectangle(Vector2.Zero, Size);
|
||||||
|
|
||||||
var fillColor = style.Background * 1.5f;
|
var fillColor = BackgroundColor.A > 0.0f ? BackgroundColor : style.Background * 1.5f;
|
||||||
Render2D.FillRectangle(bounds, fillColor);
|
Render2D.FillRectangle(bounds, fillColor);
|
||||||
|
|
||||||
var isMovingWholeMedia = _isMoving && !_startMoveRightEdge && !_startMoveLeftEdge;
|
var isMovingWholeMedia = _isMoving && !_startMoveRightEdge && !_startMoveLeftEdge;
|
||||||
var borderHighlightColor = style.BorderHighlighted;
|
var borderHighlightColor = style.BorderHighlighted;
|
||||||
var moveColor = style.ProgressNormal;
|
var moveColor = style.ProgressNormal;
|
||||||
|
var selectedColor = style.BackgroundSelected;
|
||||||
var moveThickness = 2.0f;
|
var moveThickness = 2.0f;
|
||||||
var borderColor = isMovingWholeMedia ? moveColor : (IsMouseOver ? borderHighlightColor : style.BorderNormal);
|
var borderColor = isMovingWholeMedia ? moveColor : (Timeline.SelectedMedia.Contains(this) ? selectedColor : (IsMouseOver ? borderHighlightColor : style.BorderNormal));
|
||||||
Render2D.DrawRectangle(bounds, borderColor, isMovingWholeMedia ? moveThickness : 1.0f);
|
Render2D.DrawRectangle(bounds, borderColor, isMovingWholeMedia ? moveThickness : 1.0f);
|
||||||
if (_startMoveLeftEdge)
|
if (_startMoveLeftEdge)
|
||||||
{
|
{
|
||||||
@@ -384,9 +386,26 @@ namespace FlaxEditor.GUI.Timeline
|
|||||||
_startMoveDuration = DurationFrames;
|
_startMoveDuration = DurationFrames;
|
||||||
_startMoveLeftEdge = MoveLeftEdgeRect.Contains(ref location) && CanResize;
|
_startMoveLeftEdge = MoveLeftEdgeRect.Contains(ref location) && CanResize;
|
||||||
_startMoveRightEdge = MoveRightEdgeRect.Contains(ref location) && CanResize;
|
_startMoveRightEdge = MoveRightEdgeRect.Contains(ref location) && CanResize;
|
||||||
|
|
||||||
StartMouseCapture(true);
|
StartMouseCapture(true);
|
||||||
|
if (_startMoveLeftEdge || _startMoveRightEdge)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (Root.GetKey(KeyboardKeys.Control))
|
||||||
|
{
|
||||||
|
// Add/Remove selection
|
||||||
|
if (_timeline.SelectedMedia.Contains(this))
|
||||||
|
_timeline.Deselect(this);
|
||||||
|
else
|
||||||
|
_timeline.Select(this, true);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Select
|
||||||
|
if (!_timeline.SelectedMedia.Contains(this))
|
||||||
|
_timeline.Select(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
_timeline.OnKeyframesMove(null, this, location, true, false);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -417,7 +436,8 @@ namespace FlaxEditor.GUI.Timeline
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
StartFrame = _startMoveStartFrame + moveDelta;
|
// Move with global timeline selection
|
||||||
|
_timeline.OnKeyframesMove(null, this, location, false, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (StartFrame != startFrame || DurationFrames != durationFrames)
|
if (StartFrame != startFrame || DurationFrames != durationFrames)
|
||||||
@@ -436,6 +456,15 @@ namespace FlaxEditor.GUI.Timeline
|
|||||||
{
|
{
|
||||||
if (button == MouseButton.Left && _isMoving)
|
if (button == MouseButton.Left && _isMoving)
|
||||||
{
|
{
|
||||||
|
if (!_startMoveLeftEdge && !_startMoveRightEdge && !Root.GetKey(KeyboardKeys.Control))
|
||||||
|
{
|
||||||
|
var moveLocationDelta = Root.MousePosition - _startMoveLocation;
|
||||||
|
if (moveLocationDelta.Length < 4.0f)
|
||||||
|
{
|
||||||
|
// No move so just select itself
|
||||||
|
_timeline.Select(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
EndMoving();
|
EndMoving();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -443,6 +472,20 @@ namespace FlaxEditor.GUI.Timeline
|
|||||||
return base.OnMouseUp(location, button);
|
return base.OnMouseUp(location, button);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override bool OnMouseDoubleClick(Vector2 location, MouseButton button)
|
||||||
|
{
|
||||||
|
if (base.OnMouseDoubleClick(location, button))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (PropertiesEditObject != null)
|
||||||
|
{
|
||||||
|
Timeline.ShowEditPopup(PropertiesEditObject, PointToParent(Timeline, location), Track);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override void OnEndMouseCapture()
|
public override void OnEndMouseCapture()
|
||||||
{
|
{
|
||||||
@@ -484,6 +527,8 @@ namespace FlaxEditor.GUI.Timeline
|
|||||||
private void EndMoving()
|
private void EndMoving()
|
||||||
{
|
{
|
||||||
_isMoving = false;
|
_isMoving = false;
|
||||||
|
if (_startMoveLeftEdge || _startMoveRightEdge)
|
||||||
|
{
|
||||||
_startMoveLeftEdge = false;
|
_startMoveLeftEdge = false;
|
||||||
_startMoveRightEdge = false;
|
_startMoveRightEdge = false;
|
||||||
|
|
||||||
@@ -500,6 +545,12 @@ namespace FlaxEditor.GUI.Timeline
|
|||||||
_durationFrames = endMoveDuration;
|
_durationFrames = endMoveDuration;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Global timeline selection moving end
|
||||||
|
_timeline.OnKeyframesMove(null, this, _mouseLocation, false, true);
|
||||||
|
}
|
||||||
|
|
||||||
EndMouseCapture();
|
EndMouseCapture();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -219,6 +219,10 @@ namespace FlaxEditor.GUI.Timeline
|
|||||||
private bool _isRightMouseButtonDown;
|
private bool _isRightMouseButtonDown;
|
||||||
private Vector2 _rightMouseButtonDownPos;
|
private Vector2 _rightMouseButtonDownPos;
|
||||||
private Vector2 _rightMouseButtonMovePos;
|
private Vector2 _rightMouseButtonMovePos;
|
||||||
|
private Vector2 _mediaMoveStartPos;
|
||||||
|
private int[] _mediaMoveStartFrames;
|
||||||
|
private List<Track> _mediaMoveStartTracks;
|
||||||
|
private byte[][] _mediaMoveStartData;
|
||||||
private float _zoom = 1.0f;
|
private float _zoom = 1.0f;
|
||||||
private bool _isMovingPositionHandle;
|
private bool _isMovingPositionHandle;
|
||||||
private bool _canPlayPause = true, _canStop = true;
|
private bool _canPlayPause = true, _canStop = true;
|
||||||
@@ -1375,7 +1379,7 @@ namespace FlaxEditor.GUI.Timeline
|
|||||||
if (!addToSelection)
|
if (!addToSelection)
|
||||||
{
|
{
|
||||||
SelectedTracks.Clear();
|
SelectedTracks.Clear();
|
||||||
SelectedMedia.Clear();
|
OnKeyframesDeselect(null);
|
||||||
}
|
}
|
||||||
SelectedMedia.Add(media);
|
SelectedMedia.Add(media);
|
||||||
OnSelectionChanged();
|
OnSelectionChanged();
|
||||||
@@ -1425,19 +1429,13 @@ namespace FlaxEditor.GUI.Timeline
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Deletes the selected tracks/media events.
|
/// Deletes the selected tracks.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="withUndo">True if use undo/redo action for track removing.</param>
|
/// <param name="withUndo">True if use undo/redo action for track removing.</param>
|
||||||
public void DeleteSelection(bool withUndo = true)
|
public void DeleteSelectedTracks(bool withUndo = true)
|
||||||
{
|
{
|
||||||
if (SelectedMedia.Count > 0)
|
if (SelectedTracks.Count == 0)
|
||||||
{
|
return;
|
||||||
throw new NotImplementedException("TODO: removing selected media events");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (SelectedTracks.Count > 0)
|
|
||||||
{
|
|
||||||
// Delete selected tracks
|
|
||||||
var tracks = new List<Track>(SelectedTracks.Count);
|
var tracks = new List<Track>(SelectedTracks.Count);
|
||||||
for (int i = 0; i < SelectedTracks.Count; i++)
|
for (int i = 0; i < SelectedTracks.Count; i++)
|
||||||
{
|
{
|
||||||
@@ -1466,7 +1464,6 @@ namespace FlaxEditor.GUI.Timeline
|
|||||||
OnTracksChanged();
|
OnTracksChanged();
|
||||||
MarkAsEdited();
|
MarkAsEdited();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Deletes the track.
|
/// Deletes the track.
|
||||||
@@ -1539,6 +1536,33 @@ namespace FlaxEditor.GUI.Timeline
|
|||||||
MarkAsEdited();
|
MarkAsEdited();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds the media.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="track">The track to add media to.</param>
|
||||||
|
/// <param name="media">The media to add.</param>
|
||||||
|
/// <param name="withUndo">True if use undo/redo action for media adding.</param>
|
||||||
|
public void AddMedia(Track track, Media media, bool withUndo = true)
|
||||||
|
{
|
||||||
|
if (track == null || media == null)
|
||||||
|
throw new ArgumentNullException();
|
||||||
|
if (media.Track != null)
|
||||||
|
throw new InvalidOperationException();
|
||||||
|
if (withUndo && Undo != null && Undo.Enabled)
|
||||||
|
{
|
||||||
|
var before = EditTrackAction.CaptureData(track);
|
||||||
|
track.AddMedia(media);
|
||||||
|
var after = EditTrackAction.CaptureData(track);
|
||||||
|
Undo.AddAction(new EditTrackAction(this, track, before, after));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
track.AddMedia(media);
|
||||||
|
}
|
||||||
|
MarkAsEdited();
|
||||||
|
Select(media);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Called to delete media.
|
/// Called to delete media.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -1551,19 +1575,13 @@ namespace FlaxEditor.GUI.Timeline
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Duplicates the selected tracks/media events.
|
/// Duplicates the selected tracks.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="withUndo">True if use undo/redo action for track duplication.</param>
|
/// <param name="withUndo">True if use undo/redo action for track duplication.</param>
|
||||||
public void DuplicateSelection(bool withUndo = true)
|
public void DuplicateSelectedTracks(bool withUndo = true)
|
||||||
{
|
{
|
||||||
if (SelectedMedia.Count > 0)
|
if (SelectedTracks.Count == 0)
|
||||||
{
|
return;
|
||||||
throw new NotImplementedException("TODO: duplicating selected media events");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (SelectedTracks.Count > 0)
|
|
||||||
{
|
|
||||||
// Duplicate selected tracks
|
|
||||||
var tracks = new List<Track>(SelectedTracks.Count);
|
var tracks = new List<Track>(SelectedTracks.Count);
|
||||||
for (int i = 0; i < SelectedTracks.Count; i++)
|
for (int i = 0; i < SelectedTracks.Count; i++)
|
||||||
{
|
{
|
||||||
@@ -1634,7 +1652,6 @@ namespace FlaxEditor.GUI.Timeline
|
|||||||
MarkAsEdited();
|
MarkAsEdited();
|
||||||
SelectedTracks[0].Focus();
|
SelectedTracks[0].Focus();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Splits the media (all or selected only) at the given frame.
|
/// Splits the media (all or selected only) at the given frame.
|
||||||
@@ -1885,6 +1902,9 @@ namespace FlaxEditor.GUI.Timeline
|
|||||||
if (!ContainsFocus)
|
if (!ContainsFocus)
|
||||||
Focus();
|
Focus();
|
||||||
|
|
||||||
|
var timelinePos = MediaPanel.PointFromParent(this, location);
|
||||||
|
var time = (timelinePos.X - StartOffset) / (UnitsPerSecond * Zoom);
|
||||||
|
|
||||||
var controlUnderMouse = GetChildAtRecursive(location);
|
var controlUnderMouse = GetChildAtRecursive(location);
|
||||||
var mediaUnderMouse = controlUnderMouse;
|
var mediaUnderMouse = controlUnderMouse;
|
||||||
while (mediaUnderMouse != null && !(mediaUnderMouse is Media))
|
while (mediaUnderMouse != null && !(mediaUnderMouse is Media))
|
||||||
@@ -1895,19 +1915,31 @@ namespace FlaxEditor.GUI.Timeline
|
|||||||
var menu = new ContextMenu.ContextMenu();
|
var menu = new ContextMenu.ContextMenu();
|
||||||
if (mediaUnderMouse is Media media)
|
if (mediaUnderMouse is Media media)
|
||||||
{
|
{
|
||||||
media.OnTimelineShowContextMenu(menu, controlUnderMouse);
|
media.OnTimelineContextMenu(menu, time, controlUnderMouse);
|
||||||
if (media.PropertiesEditObject != null)
|
if (media.PropertiesEditObject != null)
|
||||||
{
|
{
|
||||||
menu.AddButton("Edit media", () => ShowEditPopup(media.PropertiesEditObject, ref location, media.Track));
|
menu.AddButton("Edit media", () => ShowEditPopup(media.PropertiesEditObject, location, media.Track));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
OnKeyframesDeselect(null);
|
||||||
|
foreach (var track in _tracks)
|
||||||
|
{
|
||||||
|
if (Mathf.IsInRange(timelinePos.Y, track.Top, track.Bottom))
|
||||||
|
{
|
||||||
|
track.OnTimelineContextMenu(menu, time);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (PropertiesEditObject != null)
|
if (PropertiesEditObject != null)
|
||||||
{
|
{
|
||||||
menu.AddButton("Edit timeline", () => ShowEditPopup(PropertiesEditObject, ref location, this));
|
menu.AddButton("Edit timeline", () => ShowEditPopup(PropertiesEditObject, location, this));
|
||||||
}
|
}
|
||||||
if (_tracks.Count > 1)
|
if (_tracks.Count > 1)
|
||||||
{
|
{
|
||||||
menu.AddButton("Sort tracks", SortTracks).TooltipText = "Sorts sub tracks alphabetically";
|
menu.AddButton("Sort tracks", SortTracks).TooltipText = "Sorts tracks alphabetically";
|
||||||
}
|
}
|
||||||
menu.AddSeparator();
|
menu.AddSeparator();
|
||||||
menu.AddButton("Reset zoom", () => Zoom = 1.0f);
|
menu.AddButton("Reset zoom", () => Zoom = 1.0f);
|
||||||
@@ -2067,6 +2099,9 @@ namespace FlaxEditor.GUI.Timeline
|
|||||||
case KeyboardKeys.S:
|
case KeyboardKeys.S:
|
||||||
Split(CurrentFrame);
|
Split(CurrentFrame);
|
||||||
return true;
|
return true;
|
||||||
|
case KeyboardKeys.Delete:
|
||||||
|
OnKeyframesDelete(null);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
@@ -2089,7 +2124,7 @@ namespace FlaxEditor.GUI.Timeline
|
|||||||
/// <param name="obj">The object.</param>
|
/// <param name="obj">The object.</param>
|
||||||
/// <param name="location">The show location (in timeline space).</param>
|
/// <param name="location">The show location (in timeline space).</param>
|
||||||
/// <param name="undoContext">The undo context object.</param>
|
/// <param name="undoContext">The undo context object.</param>
|
||||||
protected virtual void ShowEditPopup(object obj, ref Vector2 location, object undoContext = null)
|
public virtual void ShowEditPopup(object obj, Vector2 location, object undoContext = null)
|
||||||
{
|
{
|
||||||
var popup = new PropertiesEditPopup(this, obj, undoContext);
|
var popup = new PropertiesEditPopup(this, obj, undoContext);
|
||||||
popup.Show(this, location);
|
popup.Show(this, location);
|
||||||
@@ -2145,6 +2180,11 @@ namespace FlaxEditor.GUI.Timeline
|
|||||||
if (_tracks[i] is IKeyframesEditorContext trackContext)
|
if (_tracks[i] is IKeyframesEditorContext trackContext)
|
||||||
trackContext.OnKeyframesDeselect(editor);
|
trackContext.OnKeyframesDeselect(editor);
|
||||||
}
|
}
|
||||||
|
if (SelectedMedia.Count != 0)
|
||||||
|
{
|
||||||
|
SelectedMedia.Clear();
|
||||||
|
OnSelectionChanged();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
@@ -2152,17 +2192,38 @@ namespace FlaxEditor.GUI.Timeline
|
|||||||
{
|
{
|
||||||
var globalControl = _backgroundArea;
|
var globalControl = _backgroundArea;
|
||||||
var globalRect = Rectangle.FromPoints(control.PointToParent(globalControl, selection.UpperLeft), control.PointToParent(globalControl, selection.BottomRight));
|
var globalRect = Rectangle.FromPoints(control.PointToParent(globalControl, selection.UpperLeft), control.PointToParent(globalControl, selection.BottomRight));
|
||||||
|
var mediaControl = MediaPanel;
|
||||||
|
var mediaRect = Rectangle.FromPoints(mediaControl.PointFromParent(globalRect.UpperLeft), mediaControl.PointFromParent(globalRect.BottomRight));
|
||||||
|
var selectionChanged = false;
|
||||||
|
if (SelectedMedia.Count != 0)
|
||||||
|
{
|
||||||
|
SelectedMedia.Clear();
|
||||||
|
selectionChanged = true;
|
||||||
|
}
|
||||||
for (int i = 0; i < _tracks.Count; i++)
|
for (int i = 0; i < _tracks.Count; i++)
|
||||||
{
|
{
|
||||||
if (_tracks[i] is IKeyframesEditorContext trackContext)
|
if (_tracks[i] is IKeyframesEditorContext trackContext)
|
||||||
trackContext.OnKeyframesSelection(editor, globalControl, globalRect);
|
trackContext.OnKeyframesSelection(editor, globalControl, globalRect);
|
||||||
|
|
||||||
|
foreach (var media in _tracks[i].Media)
|
||||||
|
{
|
||||||
|
if (media.Bounds.Intersects(ref mediaRect))
|
||||||
|
{
|
||||||
|
SelectedMedia.Add(media);
|
||||||
|
selectionChanged = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (selectionChanged)
|
||||||
|
{
|
||||||
|
OnSelectionChanged();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public int OnKeyframesSelectionCount()
|
public int OnKeyframesSelectionCount()
|
||||||
{
|
{
|
||||||
int result = 0;
|
int result = SelectedMedia.Count;
|
||||||
for (int i = 0; i < _tracks.Count; i++)
|
for (int i = 0; i < _tracks.Count; i++)
|
||||||
{
|
{
|
||||||
if (_tracks[i] is IKeyframesEditorContext trackContext)
|
if (_tracks[i] is IKeyframesEditorContext trackContext)
|
||||||
@@ -2179,6 +2240,45 @@ namespace FlaxEditor.GUI.Timeline
|
|||||||
if (_tracks[i] is IKeyframesEditorContext trackContext)
|
if (_tracks[i] is IKeyframesEditorContext trackContext)
|
||||||
trackContext.OnKeyframesDelete(editor);
|
trackContext.OnKeyframesDelete(editor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Delete selected media events
|
||||||
|
if (SelectedMedia.Count != 0)
|
||||||
|
{
|
||||||
|
if (Undo != null && Undo.Enabled)
|
||||||
|
{
|
||||||
|
// Undo per-track
|
||||||
|
if (_mediaMoveStartTracks == null)
|
||||||
|
_mediaMoveStartTracks = new List<Track>();
|
||||||
|
else
|
||||||
|
_mediaMoveStartTracks.Clear();
|
||||||
|
for (var i = 0; i < SelectedMedia.Count; i++)
|
||||||
|
{
|
||||||
|
var media = SelectedMedia[i];
|
||||||
|
if (!_mediaMoveStartTracks.Contains(media.Track))
|
||||||
|
_mediaMoveStartTracks.Add(media.Track);
|
||||||
|
}
|
||||||
|
_mediaMoveStartData = new byte[_mediaMoveStartTracks.Count][];
|
||||||
|
for (int i = 0; i < _mediaMoveStartData.Length; i++)
|
||||||
|
_mediaMoveStartData[i] = EditTrackAction.CaptureData(_mediaMoveStartTracks[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var media in SelectedMedia.ToArray())
|
||||||
|
OnDeleteMedia(media);
|
||||||
|
|
||||||
|
if (Undo != null && Undo.Enabled)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < _mediaMoveStartData.Length; i++)
|
||||||
|
{
|
||||||
|
var track = _mediaMoveStartTracks[i];
|
||||||
|
var before = _mediaMoveStartData[i];
|
||||||
|
var after = EditTrackAction.CaptureData(track);
|
||||||
|
if (!Utils.ArraysEqual(before, after))
|
||||||
|
AddBatchedUndoAction(new EditTrackAction(this, track, before, after));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MarkAsEdited();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
@@ -2190,6 +2290,60 @@ namespace FlaxEditor.GUI.Timeline
|
|||||||
if (_tracks[i] is IKeyframesEditorContext trackContext)
|
if (_tracks[i] is IKeyframesEditorContext trackContext)
|
||||||
trackContext.OnKeyframesMove(editor, _backgroundArea, location, start, end);
|
trackContext.OnKeyframesMove(editor, _backgroundArea, location, start, end);
|
||||||
}
|
}
|
||||||
|
if (SelectedMedia.Count != 0)
|
||||||
|
{
|
||||||
|
location = MediaPanel.PointFromParent(location);
|
||||||
|
if (start)
|
||||||
|
{
|
||||||
|
// Start moving selected media events
|
||||||
|
_mediaMoveStartPos = location;
|
||||||
|
_mediaMoveStartFrames = new int[SelectedMedia.Count];
|
||||||
|
if (_mediaMoveStartTracks == null)
|
||||||
|
_mediaMoveStartTracks = new List<Track>();
|
||||||
|
else
|
||||||
|
_mediaMoveStartTracks.Clear();
|
||||||
|
for (var i = 0; i < SelectedMedia.Count; i++)
|
||||||
|
{
|
||||||
|
var media = SelectedMedia[i];
|
||||||
|
_mediaMoveStartFrames[i] = media.StartFrame;
|
||||||
|
if (!_mediaMoveStartTracks.Contains(media.Track))
|
||||||
|
_mediaMoveStartTracks.Add(media.Track);
|
||||||
|
}
|
||||||
|
if (Undo != null && Undo.Enabled)
|
||||||
|
{
|
||||||
|
// Undo per-track
|
||||||
|
_mediaMoveStartData = new byte[_mediaMoveStartTracks.Count][];
|
||||||
|
for (int i = 0; i < _mediaMoveStartData.Length; i++)
|
||||||
|
_mediaMoveStartData[i] = EditTrackAction.CaptureData(_mediaMoveStartTracks[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (end)
|
||||||
|
{
|
||||||
|
// End moving selected media events
|
||||||
|
if (_mediaMoveStartData != null)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < _mediaMoveStartData.Length; i++)
|
||||||
|
{
|
||||||
|
var track = _mediaMoveStartTracks[i];
|
||||||
|
var before = _mediaMoveStartData[i];
|
||||||
|
var after = EditTrackAction.CaptureData(track);
|
||||||
|
if (!Utils.ArraysEqual(before, after))
|
||||||
|
AddBatchedUndoAction(new EditTrackAction(this, track, before, after));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
MarkAsEdited();
|
||||||
|
_mediaMoveStartTracks.Clear();
|
||||||
|
_mediaMoveStartFrames = null;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Move selected media events
|
||||||
|
var moveLocationDelta = location - _mediaMoveStartPos;
|
||||||
|
var moveDelta = (int)(moveLocationDelta.X / (UnitsPerSecond * Zoom) * FramesPerSecond);
|
||||||
|
for (var i = 0; i < SelectedMedia.Count; i++)
|
||||||
|
SelectedMedia[i].StartFrame = _mediaMoveStartFrames[i] + moveDelta;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
|
|||||||
@@ -1028,6 +1028,15 @@ namespace FlaxEditor.GUI.Timeline
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Called when showing timeline context menu to the user. Can be used to add custom buttons.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="menu">The menu.</param>
|
||||||
|
/// <param name="time">The time (in seconds) at which context menu is shown (user clicked on a timeline).</param>
|
||||||
|
public virtual void OnTimelineContextMenu(ContextMenu.ContextMenu menu, float time)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Called when context menu is being prepared to show. Can be used to add custom options.
|
/// Called when context menu is being prepared to show. Can be used to add custom options.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -1051,7 +1060,7 @@ namespace FlaxEditor.GUI.Timeline
|
|||||||
if (CanRename)
|
if (CanRename)
|
||||||
menu.AddButton("Rename", "F2", StartRenaming);
|
menu.AddButton("Rename", "F2", StartRenaming);
|
||||||
if (CanCopyPaste)
|
if (CanCopyPaste)
|
||||||
menu.AddButton("Duplicate", "Ctrl+D", () => Timeline.DuplicateSelection());
|
menu.AddButton("Duplicate", "Ctrl+D", () => Timeline.DuplicateSelectedTracks());
|
||||||
menu.AddButton("Delete", "Del", Delete);
|
menu.AddButton("Delete", "Del", Delete);
|
||||||
if (CanExpand)
|
if (CanExpand)
|
||||||
{
|
{
|
||||||
@@ -1292,12 +1301,12 @@ namespace FlaxEditor.GUI.Timeline
|
|||||||
StartRenaming();
|
StartRenaming();
|
||||||
return true;
|
return true;
|
||||||
case KeyboardKeys.Delete:
|
case KeyboardKeys.Delete:
|
||||||
_timeline.DeleteSelection();
|
_timeline.DeleteSelectedTracks();
|
||||||
return true;
|
return true;
|
||||||
case KeyboardKeys.D:
|
case KeyboardKeys.D:
|
||||||
if (Root.GetKey(KeyboardKeys.Control) && CanCopyPaste)
|
if (Root.GetKey(KeyboardKeys.Control) && CanCopyPaste)
|
||||||
{
|
{
|
||||||
_timeline.DuplicateSelection();
|
_timeline.DuplicateSelectedTracks();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|||||||
283
Source/Editor/GUI/Timeline/Tracks/AnimationEventTrack.cs
Normal file
283
Source/Editor/GUI/Timeline/Tracks/AnimationEventTrack.cs
Normal file
@@ -0,0 +1,283 @@
|
|||||||
|
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using FlaxEditor.CustomEditors;
|
||||||
|
using FlaxEditor.GUI.ContextMenu;
|
||||||
|
using FlaxEditor.Scripting;
|
||||||
|
using FlaxEditor.Utilities;
|
||||||
|
using FlaxEngine;
|
||||||
|
using FlaxEngine.GUI;
|
||||||
|
using Object = FlaxEngine.Object;
|
||||||
|
|
||||||
|
namespace FlaxEditor.GUI.Timeline.Tracks
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The timeline media for <see cref="AnimEvent"/> and <see cref="AnimContinuousEvent"/>.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class AnimationEventMedia : Media
|
||||||
|
{
|
||||||
|
private sealed class ProxyEditor : SyncPointEditor
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override IEnumerable<object> UndoObjects => Values;
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override void Initialize(LayoutElementsContainer layout)
|
||||||
|
{
|
||||||
|
base.Initialize(layout);
|
||||||
|
|
||||||
|
var instance = (AnimEvent)Values[0];
|
||||||
|
var scriptType = TypeUtils.GetObjectType(instance);
|
||||||
|
var editor = CustomEditorsUtil.CreateEditor(scriptType, false);
|
||||||
|
layout.Object(Values, editor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private sealed class Proxy : ProxyBase<AnimationEventTrack, AnimationEventMedia>
|
||||||
|
{
|
||||||
|
[EditorDisplay("General", EditorDisplayAttribute.InlineStyle), CustomEditor(typeof(ProxyEditor))]
|
||||||
|
public AnimEvent Event
|
||||||
|
{
|
||||||
|
get => Media.Instance;
|
||||||
|
set => Media.Instance = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Proxy(AnimationEventTrack track, AnimationEventMedia media)
|
||||||
|
: base(track, media)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool _isRegisteredForScriptsReload;
|
||||||
|
private string _instanceTypeName;
|
||||||
|
private byte[] _instanceData;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The event type.
|
||||||
|
/// </summary>
|
||||||
|
public ScriptType Type;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The event instance.
|
||||||
|
/// </summary>
|
||||||
|
public AnimEvent Instance;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// True if event is continuous (with duration), not a single frame.
|
||||||
|
/// </summary>
|
||||||
|
public bool IsContinuous;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="AnimationEventMedia"/> class.
|
||||||
|
/// </summary>
|
||||||
|
public AnimationEventMedia()
|
||||||
|
{
|
||||||
|
PropertiesEditObject = new Proxy(null, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnScriptsReloadBegin()
|
||||||
|
{
|
||||||
|
if (Instance)
|
||||||
|
{
|
||||||
|
_instanceTypeName = Type.TypeName;
|
||||||
|
Type = ScriptType.Null;
|
||||||
|
_instanceData = FlaxEngine.Json.JsonSerializer.SaveToBytes(Instance);
|
||||||
|
Object.Destroy(ref Instance);
|
||||||
|
ScriptsBuilder.ScriptsReloadEnd += OnScriptsReloadEnd;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnScriptsReloadEnd()
|
||||||
|
{
|
||||||
|
ScriptsBuilder.ScriptsReloadEnd -= OnScriptsReloadEnd;
|
||||||
|
Type = TypeUtils.GetType(_instanceTypeName);
|
||||||
|
if (Type == ScriptType.Null)
|
||||||
|
{
|
||||||
|
Editor.LogError("Missing anim event type " + _instanceTypeName);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Instance = (AnimEvent)Type.CreateInstance();
|
||||||
|
FlaxEngine.Json.JsonSerializer.LoadFromBytes(Instance, _instanceData, Globals.EngineBuildNumber);
|
||||||
|
_instanceData = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes track for the specified type.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="type">The type.</param>
|
||||||
|
public void Init(ScriptType type)
|
||||||
|
{
|
||||||
|
Type = type;
|
||||||
|
IsContinuous = new ScriptType(typeof(AnimContinuousEvent)).IsAssignableFrom(type);
|
||||||
|
CanDelete = true;
|
||||||
|
CanSplit = IsContinuous;
|
||||||
|
CanResize = IsContinuous;
|
||||||
|
TooltipText = Surface.SurfaceUtils.GetVisualScriptTypeDescription(type);
|
||||||
|
Instance = (AnimEvent)type.CreateInstance();
|
||||||
|
BackgroundColor = Instance.Color;
|
||||||
|
if (!_isRegisteredForScriptsReload)
|
||||||
|
{
|
||||||
|
_isRegisteredForScriptsReload = true;
|
||||||
|
ScriptsBuilder.ScriptsReloadBegin += OnScriptsReloadBegin;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void OnDurationFramesChanged()
|
||||||
|
{
|
||||||
|
if (Type != ScriptType.Null)
|
||||||
|
DurationFrames = IsContinuous ? Mathf.Max(DurationFrames, 2) : 1;
|
||||||
|
|
||||||
|
base.OnDurationFramesChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override void OnDestroy()
|
||||||
|
{
|
||||||
|
Type = ScriptType.Null;
|
||||||
|
Object.Destroy(ref Instance);
|
||||||
|
if (_isRegisteredForScriptsReload)
|
||||||
|
{
|
||||||
|
_isRegisteredForScriptsReload = false;
|
||||||
|
ScriptsBuilder.ScriptsReloadBegin -= OnScriptsReloadBegin;
|
||||||
|
}
|
||||||
|
|
||||||
|
base.OnDestroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The timeline track for <see cref="AnimEvent"/> and <see cref="AnimContinuousEvent"/>.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class AnimationEventTrack : Track
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the archetype.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The archetype.</returns>
|
||||||
|
public static TrackArchetype GetArchetype()
|
||||||
|
{
|
||||||
|
return new TrackArchetype
|
||||||
|
{
|
||||||
|
TypeId = 19,
|
||||||
|
Name = "Animation Event",
|
||||||
|
Create = options => new AnimationEventTrack(ref options),
|
||||||
|
Load = LoadTrack,
|
||||||
|
Save = SaveTrack,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void LoadTrack(int version, Track track, BinaryReader stream)
|
||||||
|
{
|
||||||
|
var e = (AnimationEventTrack)track;
|
||||||
|
var count = stream.ReadInt32();
|
||||||
|
while (e.Media.Count > count)
|
||||||
|
e.RemoveMedia(e.Media.Last());
|
||||||
|
while (e.Media.Count < count)
|
||||||
|
e.AddMedia(new AnimationEventMedia());
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
var m = (AnimationEventMedia)e.Media[i];
|
||||||
|
m.StartFrame = (int)stream.ReadSingle();
|
||||||
|
m.DurationFrames = (int)stream.ReadSingle();
|
||||||
|
var typeName = stream.ReadStrAnsi(13);
|
||||||
|
var type = TypeUtils.GetType(typeName);
|
||||||
|
if (type == ScriptType.Null)
|
||||||
|
throw new Exception($"Unknown type {typeName}.");
|
||||||
|
m.Init(type);
|
||||||
|
stream.ReadJson(m.Instance);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void SaveTrack(Track track, BinaryWriter stream)
|
||||||
|
{
|
||||||
|
var e = (AnimationEventTrack)track;
|
||||||
|
var count = e.Media.Count;
|
||||||
|
stream.Write(count);
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
var m = (AnimationEventMedia)e.Media[i];
|
||||||
|
stream.Write((float)m.StartFrame);
|
||||||
|
stream.Write((float)m.DurationFrames);
|
||||||
|
stream.WriteStrAnsi(m.Type.TypeName, 13);
|
||||||
|
stream.WriteJson(m.Instance);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public AnimationEventTrack(ref TrackCreateOptions options)
|
||||||
|
: base(ref options)
|
||||||
|
{
|
||||||
|
// Add button
|
||||||
|
const float buttonSize = 14;
|
||||||
|
var addButton = new Button
|
||||||
|
{
|
||||||
|
Text = "+",
|
||||||
|
TooltipText = "Add events",
|
||||||
|
AutoFocus = true,
|
||||||
|
AnchorPreset = AnchorPresets.MiddleRight,
|
||||||
|
IsScrollable = false,
|
||||||
|
Offsets = new Margin(-buttonSize - 2 + _muteCheckbox.Offsets.Left, buttonSize, buttonSize * -0.5f, buttonSize),
|
||||||
|
Parent = this,
|
||||||
|
};
|
||||||
|
addButton.ButtonClicked += OnAddButtonClicked;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnAddButtonClicked(Button button)
|
||||||
|
{
|
||||||
|
var cm = new ContextMenu.ContextMenu();
|
||||||
|
OnContextMenu(cm);
|
||||||
|
cm.Show(button.Parent, button.BottomLeft);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override void OnTimelineContextMenu(ContextMenu.ContextMenu menu, float time)
|
||||||
|
{
|
||||||
|
base.OnTimelineContextMenu(menu, time);
|
||||||
|
|
||||||
|
AddContextMenu(menu, time);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void OnContextMenu(ContextMenu.ContextMenu menu)
|
||||||
|
{
|
||||||
|
base.OnContextMenu(menu);
|
||||||
|
|
||||||
|
menu.AddSeparator();
|
||||||
|
AddContextMenu(menu, 0.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddContextMenu(ContextMenu.ContextMenu menu, float time)
|
||||||
|
{
|
||||||
|
var addEvent = menu.AddChildMenu("Add Anim Event");
|
||||||
|
var addContinuousEvent = menu.AddChildMenu("Add Anim Continuous Event");
|
||||||
|
var animEventTypes = Editor.Instance.CodeEditing.All.Get().Where(x => new ScriptType(typeof(AnimEvent)).IsAssignableFrom(x));
|
||||||
|
foreach (var type in animEventTypes)
|
||||||
|
{
|
||||||
|
if (type.IsAbstract || !type.CanCreateInstance)
|
||||||
|
continue;
|
||||||
|
var add = new ScriptType(typeof(AnimContinuousEvent)).IsAssignableFrom(type) ? addContinuousEvent : addEvent;
|
||||||
|
var b = add.ContextMenu.AddButton(type.Name);
|
||||||
|
b.TooltipText = Surface.SurfaceUtils.GetVisualScriptTypeDescription(type);
|
||||||
|
b.Tag = type;
|
||||||
|
b.Parent.Tag = time;
|
||||||
|
b.ButtonClicked += OnAddAnimEvent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void OnAddAnimEvent(ContextMenuButton button)
|
||||||
|
{
|
||||||
|
var type = (ScriptType)button.Tag;
|
||||||
|
var time = (float)button.Parent.Tag;
|
||||||
|
var media = new AnimationEventMedia();
|
||||||
|
media.Init(type);
|
||||||
|
media.StartFrame = (int)(time * Timeline.FramesPerSecond);
|
||||||
|
media.DurationFrames = media.IsContinuous ? Mathf.Max(Timeline.DurationFrames / 10, 10) : 1;
|
||||||
|
Timeline.AddMedia(this, media);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -324,12 +324,12 @@ namespace FlaxEditor.GUI.Timeline.Tracks
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override void OnTimelineShowContextMenu(ContextMenu.ContextMenu menu, Control controlUnderMouse)
|
public override void OnTimelineContextMenu(ContextMenu.ContextMenu menu, float time, Control controlUnderMouse)
|
||||||
{
|
{
|
||||||
if (((CameraCutTrack)Track).Camera)
|
if (((CameraCutTrack)Track).Camera)
|
||||||
menu.AddButton("Refresh thumbnails", () => UpdateThumbnails());
|
menu.AddButton("Refresh thumbnails", () => UpdateThumbnails());
|
||||||
|
|
||||||
base.OnTimelineShowContextMenu(menu, controlUnderMouse);
|
base.OnTimelineContextMenu(menu, time, controlUnderMouse);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
|
|||||||
@@ -101,9 +101,9 @@ namespace FlaxEditor.GUI.Timeline.Tracks
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override void OnTimelineShowContextMenu(ContextMenu.ContextMenu menu, Control controlUnderMouse)
|
public override void OnTimelineContextMenu(ContextMenu.ContextMenu menu, float time, Control controlUnderMouse)
|
||||||
{
|
{
|
||||||
base.OnTimelineShowContextMenu(menu, controlUnderMouse);
|
base.OnTimelineContextMenu(menu, time, controlUnderMouse);
|
||||||
|
|
||||||
if (controlUnderMouse is GradientEditor.StopControl stop)
|
if (controlUnderMouse is GradientEditor.StopControl stop)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -140,7 +140,7 @@ void OnVisualScriptingDebugFlow()
|
|||||||
void OnLogMessage(LogType type, const StringView& msg);
|
void OnLogMessage(LogType type, const StringView& msg);
|
||||||
|
|
||||||
ManagedEditor::ManagedEditor()
|
ManagedEditor::ManagedEditor()
|
||||||
: PersistentScriptingObject(SpawnParams(ObjectID, ManagedEditor::TypeInitializer))
|
: ScriptingObject(SpawnParams(ObjectID, ManagedEditor::TypeInitializer))
|
||||||
{
|
{
|
||||||
// Link events
|
// Link events
|
||||||
auto editor = ((NativeBinaryModule*)GetBinaryModuleFlaxEngine())->Assembly;
|
auto editor = ((NativeBinaryModule*)GetBinaryModuleFlaxEngine())->Assembly;
|
||||||
@@ -476,11 +476,6 @@ void ManagedEditor::OnEditorAssemblyLoaded(MAssembly* assembly)
|
|||||||
CreateManaged();
|
CreateManaged();
|
||||||
}
|
}
|
||||||
|
|
||||||
String ManagedEditor::ToString() const
|
|
||||||
{
|
|
||||||
return TEXT("ManagedEditor");
|
|
||||||
}
|
|
||||||
|
|
||||||
void ManagedEditor::DestroyManaged()
|
void ManagedEditor::DestroyManaged()
|
||||||
{
|
{
|
||||||
// Ensure to cleanup managed stuff
|
// Ensure to cleanup managed stuff
|
||||||
@@ -499,5 +494,5 @@ void ManagedEditor::DestroyManaged()
|
|||||||
Internal_OnVisualScriptingDebugFlow = nullptr;
|
Internal_OnVisualScriptingDebugFlow = nullptr;
|
||||||
|
|
||||||
// Base
|
// Base
|
||||||
PersistentScriptingObject::DestroyManaged();
|
ScriptingObject::DestroyManaged();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ namespace CSG
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Managed Editor root object
|
/// Managed Editor root object
|
||||||
/// </summary>
|
/// </summary>
|
||||||
class ManagedEditor : public PersistentScriptingObject
|
class ManagedEditor : public ScriptingObject
|
||||||
{
|
{
|
||||||
DECLARE_SCRIPTING_TYPE_NO_SPAWN(ManagedEditor);
|
DECLARE_SCRIPTING_TYPE_NO_SPAWN(ManagedEditor);
|
||||||
|
|
||||||
@@ -144,7 +144,6 @@ private:
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
// [PersistentScriptingObject]
|
// [ScriptingObject]
|
||||||
String ToString() const override;
|
|
||||||
void DestroyManaged() override;
|
void DestroyManaged() override;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -4,7 +4,6 @@
|
|||||||
|
|
||||||
#include "Engine/Core/Delegate.h"
|
#include "Engine/Core/Delegate.h"
|
||||||
#include "Engine/Core/Types/String.h"
|
#include "Engine/Core/Types/String.h"
|
||||||
#include "Engine/Core/Collections/Array.h"
|
|
||||||
#include "Engine/Scripting/ScriptingType.h"
|
#include "Engine/Scripting/ScriptingType.h"
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -163,6 +163,7 @@ namespace FlaxEditor.Viewport.Previews
|
|||||||
: base(useWidgets)
|
: base(useWidgets)
|
||||||
{
|
{
|
||||||
Task.Begin += OnBegin;
|
Task.Begin += OnBegin;
|
||||||
|
ScriptsBuilder.ScriptsReloadBegin += OnScriptsReloadBegin;
|
||||||
|
|
||||||
// Setup preview scene
|
// Setup preview scene
|
||||||
_previewModel = new AnimatedModel
|
_previewModel = new AnimatedModel
|
||||||
@@ -275,6 +276,12 @@ namespace FlaxEditor.Viewport.Previews
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void OnScriptsReloadBegin()
|
||||||
|
{
|
||||||
|
// Prevent any crashes due to dangling references to anim events
|
||||||
|
_previewModel.ResetAnimation();
|
||||||
|
}
|
||||||
|
|
||||||
private int ComputeLODIndex(SkinnedModel model)
|
private int ComputeLODIndex(SkinnedModel model)
|
||||||
{
|
{
|
||||||
if (PreviewActor.ForcedLOD != -1)
|
if (PreviewActor.ForcedLOD != -1)
|
||||||
@@ -428,6 +435,7 @@ namespace FlaxEditor.Viewport.Previews
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override void OnDestroy()
|
public override void OnDestroy()
|
||||||
{
|
{
|
||||||
|
ScriptsBuilder.ScriptsReloadBegin -= OnScriptsReloadBegin;
|
||||||
Object.Destroy(ref _floorModel);
|
Object.Destroy(ref _floorModel);
|
||||||
Object.Destroy(ref _previewModel);
|
Object.Destroy(ref _previewModel);
|
||||||
NodesMask = null;
|
NodesMask = null;
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ namespace FlaxEditor.Viewport.Previews
|
|||||||
// Playback Speed
|
// Playback Speed
|
||||||
{
|
{
|
||||||
var playbackSpeed = ViewWidgetButtonMenu.AddButton("Playback Speed");
|
var playbackSpeed = ViewWidgetButtonMenu.AddButton("Playback Speed");
|
||||||
var playbackSpeedValue = new FloatValueBox(-1, 90, 2, 70.0f, 0.0f, 10000.0f, 0.001f)
|
var playbackSpeedValue = new FloatValueBox(-1, 90, 2, 70.0f, -10000.0f, 10000.0f, 0.001f)
|
||||||
{
|
{
|
||||||
Parent = playbackSpeed
|
Parent = playbackSpeed
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -254,6 +254,7 @@ namespace FlaxEditor.Windows.Assets
|
|||||||
Enabled = false
|
Enabled = false
|
||||||
};
|
};
|
||||||
_timeline.Modified += MarkAsEdited;
|
_timeline.Modified += MarkAsEdited;
|
||||||
|
_timeline.SetNoTracksText("Loading...");
|
||||||
|
|
||||||
// Asset properties
|
// Asset properties
|
||||||
_propertiesPresenter = new CustomEditorPresenter(null);
|
_propertiesPresenter = new CustomEditorPresenter(null);
|
||||||
@@ -361,6 +362,7 @@ namespace FlaxEditor.Windows.Assets
|
|||||||
_timeline.Load(_asset);
|
_timeline.Load(_asset);
|
||||||
_undo.Clear();
|
_undo.Clear();
|
||||||
_timeline.Enabled = true;
|
_timeline.Enabled = true;
|
||||||
|
_timeline.SetNoTracksText(null);
|
||||||
ClearEditedFlag();
|
ClearEditedFlag();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -347,6 +347,7 @@ namespace FlaxEditor.Windows.Assets
|
|||||||
};
|
};
|
||||||
_timeline.Modified += OnTimelineModified;
|
_timeline.Modified += OnTimelineModified;
|
||||||
_timeline.SelectionChanged += OnTimelineSelectionChanged;
|
_timeline.SelectionChanged += OnTimelineSelectionChanged;
|
||||||
|
_timeline.SetNoTracksText("Loading...");
|
||||||
|
|
||||||
// Properties editor
|
// Properties editor
|
||||||
var propertiesEditor = new CustomEditorPresenter(_undo, string.Empty);
|
var propertiesEditor = new CustomEditorPresenter(_undo, string.Empty);
|
||||||
@@ -527,6 +528,7 @@ namespace FlaxEditor.Windows.Assets
|
|||||||
// Setup
|
// Setup
|
||||||
_undo.Clear();
|
_undo.Clear();
|
||||||
_timeline.Enabled = true;
|
_timeline.Enabled = true;
|
||||||
|
_timeline.SetNoTracksText(null);
|
||||||
_propertiesEditor.Select(new GeneralProxy(this));
|
_propertiesEditor.Select(new GeneralProxy(this));
|
||||||
ClearEditedFlag();
|
ClearEditedFlag();
|
||||||
}
|
}
|
||||||
|
|||||||
70
Source/Engine/Animations/AnimEvent.cpp
Normal file
70
Source/Engine/Animations/AnimEvent.cpp
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
|
||||||
|
|
||||||
|
#include "AnimEvent.h"
|
||||||
|
#include "Engine/Scripting/BinaryModule.h"
|
||||||
|
#include "Engine/Scripting/ManagedSerialization.h"
|
||||||
|
#include "Engine/Serialization/SerializationFwd.h"
|
||||||
|
#include "Engine/Serialization/Serialization.h"
|
||||||
|
|
||||||
|
AnimEvent::AnimEvent(const SpawnParams& params)
|
||||||
|
: ScriptingObject(params)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void AnimEvent::Serialize(SerializeStream& stream, const void* otherObj)
|
||||||
|
{
|
||||||
|
SERIALIZE_GET_OTHER_OBJ(AnimEvent);
|
||||||
|
|
||||||
|
#if !COMPILE_WITHOUT_CSHARP
|
||||||
|
// Handle C# objects data serialization
|
||||||
|
if (Flags & ObjectFlags::IsManagedType)
|
||||||
|
{
|
||||||
|
stream.JKEY("V");
|
||||||
|
if (other)
|
||||||
|
{
|
||||||
|
ManagedSerialization::SerializeDiff(stream, GetOrCreateManagedInstance(), other->GetOrCreateManagedInstance());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ManagedSerialization::Serialize(stream, GetOrCreateManagedInstance());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Handle custom scripting objects data serialization
|
||||||
|
if (Flags & ObjectFlags::IsCustomScriptingType)
|
||||||
|
{
|
||||||
|
stream.JKEY("D");
|
||||||
|
_type.Module->SerializeObject(stream, this, other);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AnimEvent::Deserialize(DeserializeStream& stream, ISerializeModifier* modifier)
|
||||||
|
{
|
||||||
|
#if !COMPILE_WITHOUT_CSHARP
|
||||||
|
// Handle C# objects data serialization
|
||||||
|
if (Flags & ObjectFlags::IsManagedType)
|
||||||
|
{
|
||||||
|
auto* const v = SERIALIZE_FIND_MEMBER(stream, "V");
|
||||||
|
if (v != stream.MemberEnd() && v->value.IsObject() && v->value.MemberCount() != 0)
|
||||||
|
{
|
||||||
|
ManagedSerialization::Deserialize(v->value, GetOrCreateManagedInstance());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Handle custom scripting objects data serialization
|
||||||
|
if (Flags & ObjectFlags::IsCustomScriptingType)
|
||||||
|
{
|
||||||
|
auto* const v = SERIALIZE_FIND_MEMBER(stream, "D");
|
||||||
|
if (v != stream.MemberEnd() && v->value.IsObject() && v->value.MemberCount() != 0)
|
||||||
|
{
|
||||||
|
_type.Module->DeserializeObject(v->value, this, modifier);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AnimContinuousEvent::AnimContinuousEvent(const SpawnParams& params)
|
||||||
|
: AnimEvent(params)
|
||||||
|
{
|
||||||
|
}
|
||||||
72
Source/Engine/Animations/AnimEvent.h
Normal file
72
Source/Engine/Animations/AnimEvent.h
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Engine/Scripting/ScriptingObject.h"
|
||||||
|
#include "Engine/Core/ISerializable.h"
|
||||||
|
#if USE_EDITOR
|
||||||
|
#include "Engine/Core/Math/Color.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
class AnimatedModel;
|
||||||
|
class Animation;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The animation notification event triggered during animation playback.
|
||||||
|
/// </summary>
|
||||||
|
API_CLASS(Abstract) class FLAXENGINE_API AnimEvent : public ScriptingObject, public ISerializable
|
||||||
|
{
|
||||||
|
DECLARE_SCRIPTING_TYPE(AnimEvent);
|
||||||
|
|
||||||
|
#if USE_EDITOR
|
||||||
|
/// <summary>
|
||||||
|
/// Event display color in the Editor.
|
||||||
|
/// </summary>
|
||||||
|
API_FIELD(Attributes="HideInEditor, NoSerialize") Color Color = Color::White;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Animation event notification.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="actor">The animated model actor instance.</param>
|
||||||
|
/// <param name="anim">The source animation.</param>
|
||||||
|
/// <param name="time">The current animation time (in seconds).</param>
|
||||||
|
/// <param name="deltaTime">The current animation tick delta time (in seconds).</param>
|
||||||
|
API_FUNCTION() virtual void OnEvent(AnimatedModel* actor, Animation* anim, float time, float deltaTime)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// [ISerializable]
|
||||||
|
void Serialize(SerializeStream& stream, const void* otherObj) override;
|
||||||
|
void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The animation notification event (with duration) triggered during animation playback that contains begin and end (event notification is received as a tick).
|
||||||
|
/// </summary>
|
||||||
|
API_CLASS(Abstract) class FLAXENGINE_API AnimContinuousEvent : public AnimEvent
|
||||||
|
{
|
||||||
|
DECLARE_SCRIPTING_TYPE(AnimContinuousEvent);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Animation notification called before the first event.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="actor">The animated model actor instance.</param>
|
||||||
|
/// <param name="anim">The source animation.</param>
|
||||||
|
/// <param name="time">The current animation time (in seconds).</param>
|
||||||
|
/// <param name="deltaTime">The current animation tick delta time (in seconds).</param>
|
||||||
|
API_FUNCTION() virtual void OnBegin(AnimatedModel* actor, Animation* anim, float time, float deltaTime)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Animation notification called after the last event (guaranteed to be always called).
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="actor">The animated model actor instance.</param>
|
||||||
|
/// <param name="anim">The source animation.</param>
|
||||||
|
/// <param name="time">The current animation time (in seconds).</param>
|
||||||
|
/// <param name="deltaTime">The current animation tick delta time (in seconds).</param>
|
||||||
|
API_FUNCTION() virtual void OnEnd(AnimatedModel* actor, Animation* anim, float time, float deltaTime)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -14,7 +14,13 @@ namespace AnimationUtils
|
|||||||
template<class T>
|
template<class T>
|
||||||
FORCE_INLINE static T GetZero()
|
FORCE_INLINE static T GetZero()
|
||||||
{
|
{
|
||||||
return 0.0f;
|
return T();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<>
|
||||||
|
FORCE_INLINE int32 GetZero<int32>()
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
|
|||||||
@@ -769,6 +769,12 @@ public:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// An animation spline represented by a set of keyframes, each representing a value point.
|
||||||
|
/// </summary>
|
||||||
|
template<typename T>
|
||||||
|
using StepCurve = Curve<T, StepCurveKeyframe<T>>;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// An animation spline represented by a set of keyframes, each representing an endpoint of a linear curve.
|
/// An animation spline represented by a set of keyframes, each representing an endpoint of a linear curve.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -186,6 +186,7 @@ namespace Serialization
|
|||||||
|
|
||||||
// Raw keyframes data
|
// Raw keyframes data
|
||||||
stream.WriteInt32(keyframes.Count());
|
stream.WriteInt32(keyframes.Count());
|
||||||
|
static_assert(TIsPODType<T>::Value, "Raw bytes serialization only for raw POD types.");
|
||||||
stream.WriteBytes(keyframes.Get(), keyframes.Count() * sizeof(KeyFrame));
|
stream.WriteBytes(keyframes.Get(), keyframes.Count() * sizeof(KeyFrame));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -209,6 +210,7 @@ namespace Serialization
|
|||||||
int32 keyframesCount;
|
int32 keyframesCount;
|
||||||
stream.ReadInt32(&keyframesCount);
|
stream.ReadInt32(&keyframesCount);
|
||||||
keyframes.Resize(keyframesCount, false);
|
keyframes.Resize(keyframesCount, false);
|
||||||
|
static_assert(TIsPODType<T>::Value, "Raw bytes serialization only for raw POD types.");
|
||||||
stream.ReadBytes(keyframes.Get(), keyframes.Count() * sizeof(KeyFrame));
|
stream.ReadBytes(keyframes.Get(), keyframes.Count() * sizeof(KeyFrame));
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include "AnimGraph.h"
|
#include "AnimGraph.h"
|
||||||
#include "Engine/Animations/Animations.h"
|
#include "Engine/Animations/Animations.h"
|
||||||
|
#include "Engine/Animations/AnimEvent.h"
|
||||||
#include "Engine/Content/Assets/SkinnedModel.h"
|
#include "Engine/Content/Assets/SkinnedModel.h"
|
||||||
#include "Engine/Graphics/Models/SkeletonData.h"
|
#include "Engine/Graphics/Models/SkeletonData.h"
|
||||||
#include "Engine/Scripting/Scripting.h"
|
#include "Engine/Scripting/Scripting.h"
|
||||||
@@ -91,6 +92,9 @@ void AnimGraphInstanceData::Clear()
|
|||||||
State.Resize(0);
|
State.Resize(0);
|
||||||
NodesPose.Resize(0);
|
NodesPose.Resize(0);
|
||||||
Slots.Resize(0);
|
Slots.Resize(0);
|
||||||
|
for (const auto& e : Events)
|
||||||
|
((AnimContinuousEvent*)e.Instance)->OnEnd((AnimatedModel*)Object, e.Anim, 0.0f, 0.0f);
|
||||||
|
Events.Resize(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AnimGraphInstanceData::ClearState()
|
void AnimGraphInstanceData::ClearState()
|
||||||
@@ -103,6 +107,9 @@ void AnimGraphInstanceData::ClearState()
|
|||||||
State.Resize(0);
|
State.Resize(0);
|
||||||
NodesPose.Resize(0);
|
NodesPose.Resize(0);
|
||||||
Slots.Clear();
|
Slots.Clear();
|
||||||
|
for (const auto& e : Events)
|
||||||
|
((AnimContinuousEvent*)e.Instance)->OnEnd((AnimatedModel*)Object, e.Anim, 0.0f, 0.0f);
|
||||||
|
Events.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void AnimGraphInstanceData::Invalidate()
|
void AnimGraphInstanceData::Invalidate()
|
||||||
@@ -246,6 +253,8 @@ void AnimGraphExecutor::Update(AnimGraphInstanceData& data, float dt)
|
|||||||
// Initialize buckets
|
// Initialize buckets
|
||||||
ResetBuckets(context, &_graph);
|
ResetBuckets(context, &_graph);
|
||||||
}
|
}
|
||||||
|
for (auto& e : data.Events)
|
||||||
|
e.Hit = false;
|
||||||
|
|
||||||
// Init empty nodes data
|
// Init empty nodes data
|
||||||
context.EmptyNodes.RootMotion = RootMotionData::Identity;
|
context.EmptyNodes.RootMotion = RootMotionData::Identity;
|
||||||
@@ -279,6 +288,19 @@ void AnimGraphExecutor::Update(AnimGraphInstanceData& data, float dt)
|
|||||||
if (animResult == nullptr)
|
if (animResult == nullptr)
|
||||||
animResult = GetEmptyNodes();
|
animResult = GetEmptyNodes();
|
||||||
}
|
}
|
||||||
|
if (data.Events.Count() != 0)
|
||||||
|
{
|
||||||
|
ANIM_GRAPH_PROFILE_EVENT("Events");
|
||||||
|
for (int32 i = data.Events.Count() - 1; i >= 0; i--)
|
||||||
|
{
|
||||||
|
const auto& e = data.Events[i];
|
||||||
|
if (!e.Hit)
|
||||||
|
{
|
||||||
|
((AnimContinuousEvent*)e.Instance)->OnEnd((AnimatedModel*)context.Data->Object, e.Anim, 0.0f, 0.0f);
|
||||||
|
data.Events.RemoveAt(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Allow for external override of the local pose (eg. by the ragdoll)
|
// Allow for external override of the local pose (eg. by the ragdoll)
|
||||||
data.LocalPoseOverride(animResult);
|
data.LocalPoseOverride(animResult);
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
#define ANIM_GRAPH_MULTI_BLEND_2D_MAX_TRIS 32
|
#define ANIM_GRAPH_MULTI_BLEND_2D_MAX_TRIS 32
|
||||||
#define ANIM_GRAPH_MAX_STATE_TRANSITIONS 64
|
#define ANIM_GRAPH_MAX_STATE_TRANSITIONS 64
|
||||||
#define ANIM_GRAPH_MAX_CALL_STACK 100
|
#define ANIM_GRAPH_MAX_CALL_STACK 100
|
||||||
|
#define ANIM_GRAPH_MAX_EVENTS 64
|
||||||
|
|
||||||
class AnimGraph;
|
class AnimGraph;
|
||||||
class AnimSubGraph;
|
class AnimSubGraph;
|
||||||
@@ -267,6 +268,7 @@ struct FLAXENGINE_API AnimGraphSlot
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
class FLAXENGINE_API AnimGraphInstanceData
|
class FLAXENGINE_API AnimGraphInstanceData
|
||||||
{
|
{
|
||||||
|
friend AnimGraphExecutor;
|
||||||
public:
|
public:
|
||||||
|
|
||||||
// ---- Quick documentation ----
|
// ---- Quick documentation ----
|
||||||
@@ -384,7 +386,7 @@ public:
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The slots animations.
|
/// The slots animations.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Array<AnimGraphSlot> Slots;
|
Array<AnimGraphSlot, InlinedAllocation<4>> Slots;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
@@ -402,6 +404,18 @@ public:
|
|||||||
/// Invalidates the update timer.
|
/// Invalidates the update timer.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
void Invalidate();
|
void Invalidate();
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
struct Event
|
||||||
|
{
|
||||||
|
AnimEvent* Instance;
|
||||||
|
Animation* Anim;
|
||||||
|
AnimGraphNode* Node;
|
||||||
|
bool Hit;
|
||||||
|
};
|
||||||
|
|
||||||
|
Array<Event, InlinedAllocation<8>> Events;
|
||||||
};
|
};
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -5,7 +5,9 @@
|
|||||||
#include "Engine/Content/Assets/SkeletonMask.h"
|
#include "Engine/Content/Assets/SkeletonMask.h"
|
||||||
#include "Engine/Content/Assets/AnimationGraphFunction.h"
|
#include "Engine/Content/Assets/AnimationGraphFunction.h"
|
||||||
#include "Engine/Animations/AlphaBlend.h"
|
#include "Engine/Animations/AlphaBlend.h"
|
||||||
|
#include "Engine/Animations/AnimEvent.h"
|
||||||
#include "Engine/Animations/InverseKinematics.h"
|
#include "Engine/Animations/InverseKinematics.h"
|
||||||
|
#include "Engine/Level/Actors/AnimatedModel.h"
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
@@ -146,6 +148,7 @@ Variant AnimGraphExecutor::SampleAnimation(AnimGraphNode* node, bool loop, float
|
|||||||
if (anim == nullptr || !anim->IsLoaded())
|
if (anim == nullptr || !anim->IsLoaded())
|
||||||
return Value::Null;
|
return Value::Null;
|
||||||
PROFILE_CPU_ASSET(anim);
|
PROFILE_CPU_ASSET(anim);
|
||||||
|
const float oldTimePos = prevTimePos;
|
||||||
|
|
||||||
// Calculate actual time position within the animation node (defined by length and loop mode)
|
// Calculate actual time position within the animation node (defined by length and loop mode)
|
||||||
const float pos = GetAnimPos(newTimePos, startTimePos, loop, length);
|
const float pos = GetAnimPos(newTimePos, startTimePos, loop, length);
|
||||||
@@ -180,6 +183,81 @@ Variant AnimGraphExecutor::SampleAnimation(AnimGraphNode* node, bool loop, float
|
|||||||
ExtractRootMotion(mapping, rootNodeIndex, anim, animPos, animPrevPos, nodes->Nodes[rootNodeIndex], nodes->RootMotion);
|
ExtractRootMotion(mapping, rootNodeIndex, anim, animPos, animPrevPos, nodes->Nodes[rootNodeIndex], nodes->RootMotion);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Collect events
|
||||||
|
if (anim->Events.Count() != 0)
|
||||||
|
{
|
||||||
|
ANIM_GRAPH_PROFILE_EVENT("Events");
|
||||||
|
auto& context = Context.Get();
|
||||||
|
float eventTimeMin = animPrevPos;
|
||||||
|
float eventTimeMax = animPos;
|
||||||
|
if (loop)
|
||||||
|
{
|
||||||
|
// Check if animation looped
|
||||||
|
const float posNotLooped = startTimePos + oldTimePos;
|
||||||
|
if (posNotLooped < 0.0f || posNotLooped > length)
|
||||||
|
{
|
||||||
|
if (context.DeltaTime * speed < 0)
|
||||||
|
{
|
||||||
|
// Playback backwards
|
||||||
|
Swap(eventTimeMin, eventTimeMax);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const float eventTime = animPos / static_cast<float>(anim->Data.FramesPerSecond);
|
||||||
|
const float eventDeltaTime = (animPos - animPrevPos) / static_cast<float>(anim->Data.FramesPerSecond);
|
||||||
|
for (const auto& track : anim->Events)
|
||||||
|
{
|
||||||
|
for (const auto& k : track.Second.GetKeyframes())
|
||||||
|
{
|
||||||
|
if (!k.Value.Instance)
|
||||||
|
continue;
|
||||||
|
const float duration = k.Value.Duration > 1 ? k.Value.Duration : 0.0f;
|
||||||
|
if (k.Time <= eventTimeMax && eventTimeMin <= k.Time + duration)
|
||||||
|
{
|
||||||
|
int32 stateIndex = -1;
|
||||||
|
if (duration > 1)
|
||||||
|
{
|
||||||
|
// Begin for continuous event
|
||||||
|
for (stateIndex = 0; stateIndex < context.Data->Events.Count(); stateIndex++)
|
||||||
|
{
|
||||||
|
const auto& e = context.Data->Events[stateIndex];
|
||||||
|
if (e.Instance == k.Value.Instance && e.Node == node)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (stateIndex == context.Data->Events.Count())
|
||||||
|
{
|
||||||
|
auto& e = context.Data->Events.AddOne();
|
||||||
|
e.Instance = k.Value.Instance;
|
||||||
|
e.Anim = anim;
|
||||||
|
e.Node = node;
|
||||||
|
ASSERT(k.Value.Instance->Is<AnimContinuousEvent>());
|
||||||
|
((AnimContinuousEvent*)k.Value.Instance)->OnBegin((AnimatedModel*)context.Data->Object, anim, eventTime, eventDeltaTime);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Event
|
||||||
|
k.Value.Instance->OnEvent((AnimatedModel*)context.Data->Object, anim, eventTime, eventDeltaTime);
|
||||||
|
if (stateIndex != -1)
|
||||||
|
context.Data->Events[stateIndex].Hit = true;
|
||||||
|
}
|
||||||
|
else if (duration > 1)
|
||||||
|
{
|
||||||
|
// End for continuous event
|
||||||
|
for (int32 i = 0; i < context.Data->Events.Count(); i++)
|
||||||
|
{
|
||||||
|
const auto& e = context.Data->Events[i];
|
||||||
|
if (e.Instance == k.Value.Instance && e.Node == node)
|
||||||
|
{
|
||||||
|
((AnimContinuousEvent*)k.Value.Instance)->OnEnd((AnimatedModel*)context.Data->Object, anim, eventTime, eventDeltaTime);
|
||||||
|
context.Data->Events.RemoveAt(i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return nodes;
|
return nodes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ public:
|
|||||||
CameraCut = 16,
|
CameraCut = 16,
|
||||||
//AnimationChannel = 17,
|
//AnimationChannel = 17,
|
||||||
//AnimationChannelData = 18,
|
//AnimationChannelData = 18,
|
||||||
|
//AnimationEvent = 19,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class Flags
|
enum class Flags
|
||||||
|
|||||||
@@ -7,17 +7,17 @@
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents a single audio device.
|
/// Represents a single audio device.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
API_CLASS(NoSpawn) class AudioDevice : public PersistentScriptingObject
|
API_CLASS(NoSpawn) class AudioDevice : public ScriptingObject
|
||||||
{
|
{
|
||||||
DECLARE_SCRIPTING_TYPE_NO_SPAWN(AudioDevice);
|
DECLARE_SCRIPTING_TYPE_NO_SPAWN(AudioDevice);
|
||||||
|
|
||||||
explicit AudioDevice()
|
explicit AudioDevice()
|
||||||
: PersistentScriptingObject(SpawnParams(Guid::New(), TypeInitializer))
|
: ScriptingObject(SpawnParams(Guid::New(), TypeInitializer))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
AudioDevice(const AudioDevice& other)
|
AudioDevice(const AudioDevice& other)
|
||||||
: PersistentScriptingObject(SpawnParams(Guid::New(), TypeInitializer))
|
: ScriptingObject(SpawnParams(Guid::New(), TypeInitializer))
|
||||||
{
|
{
|
||||||
Name = other.Name;
|
Name = other.Name;
|
||||||
InternalName = other.InternalName;
|
InternalName = other.InternalName;
|
||||||
|
|||||||
@@ -6,10 +6,13 @@
|
|||||||
#include "Engine/Profiler/ProfilerCPU.h"
|
#include "Engine/Profiler/ProfilerCPU.h"
|
||||||
#include "Engine/Content/Factories/BinaryAssetFactory.h"
|
#include "Engine/Content/Factories/BinaryAssetFactory.h"
|
||||||
#include "Engine/Animations/CurveSerialization.h"
|
#include "Engine/Animations/CurveSerialization.h"
|
||||||
|
#include "Engine/Animations/AnimEvent.h"
|
||||||
|
#include "Engine/Scripting/Scripting.h"
|
||||||
#include "Engine/Threading/Threading.h"
|
#include "Engine/Threading/Threading.h"
|
||||||
#include "Engine/Serialization/MemoryReadStream.h"
|
#include "Engine/Serialization/MemoryReadStream.h"
|
||||||
#if USE_EDITOR
|
#if USE_EDITOR
|
||||||
#include "Engine/Serialization/MemoryWriteStream.h"
|
#include "Engine/Serialization/MemoryWriteStream.h"
|
||||||
|
#include "Engine/Level/Level.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
REGISTER_BINARY_ASSET(Animation, "FlaxEngine.Animation", false);
|
REGISTER_BINARY_ASSET(Animation, "FlaxEngine.Animation", false);
|
||||||
@@ -19,6 +22,21 @@ Animation::Animation(const SpawnParams& params, const AssetInfo* info)
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if USE_EDITOR
|
||||||
|
|
||||||
|
void Animation::OnScriptsReloadStart()
|
||||||
|
{
|
||||||
|
for (auto& e : Events)
|
||||||
|
{
|
||||||
|
for (auto& k : e.Second.GetKeyframes())
|
||||||
|
{
|
||||||
|
Level::ScriptsReloadRegisterObject((ScriptingObject*&)k.Value.Instance);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
Animation::InfoData Animation::GetInfo() const
|
Animation::InfoData Animation::GetInfo() const
|
||||||
{
|
{
|
||||||
ScopeLock lock(Locker);
|
ScopeLock lock(Locker);
|
||||||
@@ -127,7 +145,7 @@ void Animation::LoadTimeline(BytesContainer& result) const
|
|||||||
const float fpsInv = 1.0f / fps;
|
const float fpsInv = 1.0f / fps;
|
||||||
stream.WriteFloat(fps);
|
stream.WriteFloat(fps);
|
||||||
stream.WriteInt32((int32)Data.Duration);
|
stream.WriteInt32((int32)Data.Duration);
|
||||||
int32 tracksCount = Data.Channels.Count();
|
int32 tracksCount = Data.Channels.Count() + Events.Count();
|
||||||
for (auto& channel : Data.Channels)
|
for (auto& channel : Data.Channels)
|
||||||
tracksCount +=
|
tracksCount +=
|
||||||
(channel.Position.GetKeyframes().HasItems() ? 1 : 0) +
|
(channel.Position.GetKeyframes().HasItems() ? 1 : 0) +
|
||||||
@@ -214,6 +232,24 @@ void Animation::LoadTimeline(BytesContainer& result) const
|
|||||||
trackIndex++;
|
trackIndex++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
for (auto& e : Events)
|
||||||
|
{
|
||||||
|
// Animation Event track
|
||||||
|
stream.WriteByte(19); // Track Type
|
||||||
|
stream.WriteByte(0); // Track Flags
|
||||||
|
stream.WriteInt32(-1); // Parent Index
|
||||||
|
stream.WriteInt32(0); // Children Count
|
||||||
|
stream.WriteString(e.First, -13); // Name
|
||||||
|
stream.Write(&Color32::White); // Color
|
||||||
|
stream.WriteInt32(e.Second.GetKeyframes().Count()); // Events Count
|
||||||
|
for (const auto& k : e.Second.GetKeyframes())
|
||||||
|
{
|
||||||
|
stream.WriteFloat(k.Time);
|
||||||
|
stream.WriteFloat(k.Value.Duration);
|
||||||
|
stream.WriteStringAnsi(k.Value.TypeName, 13);
|
||||||
|
stream.WriteJson(k.Value.Instance);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
result.Copy(stream.GetHandle(), stream.GetPosition());
|
result.Copy(stream.GetHandle(), stream.GetPosition());
|
||||||
}
|
}
|
||||||
@@ -253,6 +289,7 @@ bool Animation::SaveTimeline(BytesContainer& data)
|
|||||||
|
|
||||||
// Tracks
|
// Tracks
|
||||||
Data.Channels.Clear();
|
Data.Channels.Clear();
|
||||||
|
Events.Clear();
|
||||||
Dictionary<int32, int32> animationChannelTrackIndexToChannelIndex;
|
Dictionary<int32, int32> animationChannelTrackIndexToChannelIndex;
|
||||||
animationChannelTrackIndexToChannelIndex.EnsureCapacity(tracksCount * 3);
|
animationChannelTrackIndexToChannelIndex.EnsureCapacity(tracksCount * 3);
|
||||||
for (int32 trackIndex = 0; trackIndex < tracksCount; trackIndex++)
|
for (int32 trackIndex = 0; trackIndex < tracksCount; trackIndex++)
|
||||||
@@ -325,6 +362,36 @@ bool Animation::SaveTimeline(BytesContainer& data)
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case 19:
|
||||||
|
{
|
||||||
|
// Animation Event
|
||||||
|
int32 count;
|
||||||
|
stream.ReadInt32(&count);
|
||||||
|
auto& eventTrack = Events.AddOne();
|
||||||
|
eventTrack.First = name;
|
||||||
|
eventTrack.Second.Resize(count);
|
||||||
|
for (int32 i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
auto& k = eventTrack.Second.GetKeyframes()[i];
|
||||||
|
stream.ReadFloat(&k.Time);
|
||||||
|
stream.ReadFloat(&k.Value.Duration);
|
||||||
|
stream.ReadStringAnsi(&k.Value.TypeName, 13);
|
||||||
|
const ScriptingTypeHandle typeHandle = Scripting::FindScriptingType(k.Value.TypeName);
|
||||||
|
k.Value.Instance = NewObject<AnimEvent>(typeHandle);
|
||||||
|
stream.ReadJson(k.Value.Instance);
|
||||||
|
if (!k.Value.Instance)
|
||||||
|
{
|
||||||
|
LOG(Error, "Failed to spawn object of type {0}.", String(k.Value.TypeName));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!_registeredForScriptingReload)
|
||||||
|
{
|
||||||
|
_registeredForScriptingReload = true;
|
||||||
|
Level::ScriptsReloadStart.Bind<Animation, &Animation::OnScriptsReloadStart>(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
LOG(Error, "Unsupported track type {0} for animation.", trackType);
|
LOG(Error, "Unsupported track type {0} for animation.", trackType);
|
||||||
return true;
|
return true;
|
||||||
@@ -364,7 +431,7 @@ bool Animation::Save(const StringView& path)
|
|||||||
MemoryWriteStream stream(4096);
|
MemoryWriteStream stream(4096);
|
||||||
|
|
||||||
// Info
|
// Info
|
||||||
stream.WriteInt32(100);
|
stream.WriteInt32(101);
|
||||||
stream.WriteDouble(Data.Duration);
|
stream.WriteDouble(Data.Duration);
|
||||||
stream.WriteDouble(Data.FramesPerSecond);
|
stream.WriteDouble(Data.FramesPerSecond);
|
||||||
stream.WriteBool(Data.EnableRootMotion);
|
stream.WriteBool(Data.EnableRootMotion);
|
||||||
@@ -381,6 +448,22 @@ bool Animation::Save(const StringView& path)
|
|||||||
Serialization::Serialize(stream, anim.Scale);
|
Serialization::Serialize(stream, anim.Scale);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Animation events
|
||||||
|
stream.WriteInt32(Events.Count());
|
||||||
|
for (int32 i = 0; i < Events.Count(); i++)
|
||||||
|
{
|
||||||
|
auto& e = Events[i];
|
||||||
|
stream.WriteString(e.First, 172);
|
||||||
|
stream.WriteInt32(e.Second.GetKeyframes().Count());
|
||||||
|
for (const auto& k : e.Second.GetKeyframes())
|
||||||
|
{
|
||||||
|
stream.WriteFloat(k.Time);
|
||||||
|
stream.WriteFloat(k.Value.Duration);
|
||||||
|
stream.WriteStringAnsi(k.Value.TypeName, 17);
|
||||||
|
stream.WriteJson(k.Value.Instance);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Set data to the chunk asset
|
// Set data to the chunk asset
|
||||||
auto chunk0 = GetOrCreateChunk(0);
|
auto chunk0 = GetOrCreateChunk(0);
|
||||||
ASSERT(chunk0 != nullptr);
|
ASSERT(chunk0 != nullptr);
|
||||||
@@ -419,6 +502,24 @@ void Animation::OnSkinnedModelUnloaded(Asset* obj)
|
|||||||
MappingCache.Remove(i);
|
MappingCache.Remove(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Animation::OnScriptingDispose()
|
||||||
|
{
|
||||||
|
// Dispose any events to prevent crashes (scripting is released before content)
|
||||||
|
for (auto& e : Events)
|
||||||
|
{
|
||||||
|
for (auto& k : e.Second.GetKeyframes())
|
||||||
|
{
|
||||||
|
if (k.Value.Instance)
|
||||||
|
{
|
||||||
|
Delete(k.Value.Instance);
|
||||||
|
k.Value.Instance = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BinaryAsset::OnScriptingDispose();
|
||||||
|
}
|
||||||
|
|
||||||
Asset::LoadResult Animation::load()
|
Asset::LoadResult Animation::load()
|
||||||
{
|
{
|
||||||
// Get stream with animations data
|
// Get stream with animations data
|
||||||
@@ -432,6 +533,7 @@ Asset::LoadResult Animation::load()
|
|||||||
switch (headerVersion)
|
switch (headerVersion)
|
||||||
{
|
{
|
||||||
case 100:
|
case 100:
|
||||||
|
case 101:
|
||||||
{
|
{
|
||||||
stream.ReadInt32(&headerVersion);
|
stream.ReadInt32(&headerVersion);
|
||||||
stream.ReadDouble(&Data.Duration);
|
stream.ReadDouble(&Data.Duration);
|
||||||
@@ -471,13 +573,72 @@ Asset::LoadResult Animation::load()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Animation events
|
||||||
|
if (headerVersion >= 101)
|
||||||
|
{
|
||||||
|
int32 eventTracksCount;
|
||||||
|
stream.ReadInt32(&eventTracksCount);
|
||||||
|
Events.Resize(eventTracksCount, false);
|
||||||
|
#if !USE_EDITOR
|
||||||
|
StringAnsi typeName;
|
||||||
|
#endif
|
||||||
|
for (int32 i = 0; i < eventTracksCount; i++)
|
||||||
|
{
|
||||||
|
auto& e = Events[i];
|
||||||
|
stream.ReadString(&e.First, 172);
|
||||||
|
int32 eventsCount;
|
||||||
|
stream.ReadInt32(&eventsCount);
|
||||||
|
e.Second.GetKeyframes().Resize(eventsCount);
|
||||||
|
for (auto& k : e.Second.GetKeyframes())
|
||||||
|
{
|
||||||
|
stream.ReadFloat(&k.Time);
|
||||||
|
stream.ReadFloat(&k.Value.Duration);
|
||||||
|
#if USE_EDITOR
|
||||||
|
StringAnsi& typeName = k.Value.TypeName;
|
||||||
|
#endif
|
||||||
|
stream.ReadStringAnsi(&typeName, 17);
|
||||||
|
const ScriptingTypeHandle typeHandle = Scripting::FindScriptingType(typeName);
|
||||||
|
k.Value.Instance = NewObject<AnimEvent>(typeHandle);
|
||||||
|
stream.ReadJson(k.Value.Instance);
|
||||||
|
if (!k.Value.Instance)
|
||||||
|
{
|
||||||
|
LOG(Error, "Failed to spawn object of type {0}.", String(typeName));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
#if USE_EDITOR
|
||||||
|
if (!_registeredForScriptingReload)
|
||||||
|
{
|
||||||
|
_registeredForScriptingReload = true;
|
||||||
|
Level::ScriptsReloadStart.Bind<Animation, &Animation::OnScriptsReloadStart>(this);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return LoadResult::Ok;
|
return LoadResult::Ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Animation::unload(bool isReloading)
|
void Animation::unload(bool isReloading)
|
||||||
{
|
{
|
||||||
|
#if USE_EDITOR
|
||||||
|
if (_registeredForScriptingReload)
|
||||||
|
{
|
||||||
|
_registeredForScriptingReload = false;
|
||||||
|
Level::ScriptsReloadStart.Unbind<Animation, &Animation::OnScriptsReloadStart>(this);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
ClearCache();
|
ClearCache();
|
||||||
Data.Dispose();
|
Data.Dispose();
|
||||||
|
for (const auto& e : Events)
|
||||||
|
{
|
||||||
|
for (const auto& k : e.Second.GetKeyframes())
|
||||||
|
{
|
||||||
|
if (k.Value.Instance)
|
||||||
|
Delete(k.Value.Instance);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Events.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
AssetChunksFlag Animation::getChunksToPreload() const
|
AssetChunksFlag Animation::getChunksToPreload() const
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
#include "Engine/Animations/AnimationData.h"
|
#include "Engine/Animations/AnimationData.h"
|
||||||
|
|
||||||
class SkinnedModel;
|
class SkinnedModel;
|
||||||
|
class AnimEvent;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Asset that contains an animation spline represented by a set of keyframes, each representing an endpoint of a linear curve.
|
/// Asset that contains an animation spline represented by a set of keyframes, each representing an endpoint of a linear curve.
|
||||||
@@ -18,7 +19,7 @@ DECLARE_BINARY_ASSET_HEADER(Animation, 1);
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Contains basic information about the animation asset contents.
|
/// Contains basic information about the animation asset contents.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
API_STRUCT() struct InfoData
|
API_STRUCT() struct FLAXENGINE_API InfoData
|
||||||
{
|
{
|
||||||
DECLARE_SCRIPTING_TYPE_NO_SPAWN(InfoData);
|
DECLARE_SCRIPTING_TYPE_NO_SPAWN(InfoData);
|
||||||
|
|
||||||
@@ -48,6 +49,25 @@ DECLARE_BINARY_ASSET_HEADER(Animation, 1);
|
|||||||
API_FIELD() int32 MemoryUsage;
|
API_FIELD() int32 MemoryUsage;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Contains <see cref="AnimEvent"/> instance.
|
||||||
|
/// </summary>
|
||||||
|
struct FLAXENGINE_API AnimEventData
|
||||||
|
{
|
||||||
|
float Duration = 0.0f;
|
||||||
|
AnimEvent* Instance = nullptr;
|
||||||
|
#if USE_EDITOR
|
||||||
|
StringAnsi TypeName;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
#if USE_EDITOR
|
||||||
|
bool _registeredForScriptingReload = false;
|
||||||
|
void OnScriptsReloadStart();
|
||||||
|
#endif
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -55,6 +75,11 @@ public:
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
AnimationData Data;
|
AnimationData Data;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The animation events (keyframes per named track).
|
||||||
|
/// </summary>
|
||||||
|
Array<Pair<String, StepCurve<AnimEventData>>> Events;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Contains the mapping for every skeleton node to the animation data channels.
|
/// Contains the mapping for every skeleton node to the animation data channels.
|
||||||
/// Can be used for a simple lookup or to check if a given node is animated (unused nodes are using -1 index).
|
/// Can be used for a simple lookup or to check if a given node is animated (unused nodes are using -1 index).
|
||||||
@@ -138,6 +163,11 @@ private:
|
|||||||
|
|
||||||
void OnSkinnedModelUnloaded(Asset* obj);
|
void OnSkinnedModelUnloaded(Asset* obj);
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
// [BinaryAsset]
|
||||||
|
void OnScriptingDispose() override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
// [BinaryAsset]
|
// [BinaryAsset]
|
||||||
|
|||||||
@@ -624,7 +624,9 @@ void VisualScriptExecutor::ProcessGroupFunction(Box* boxBase, Node* node, Value&
|
|||||||
{
|
{
|
||||||
// Evaluate method parameter value from the current scope
|
// Evaluate method parameter value from the current scope
|
||||||
auto& scope = ThreadStacks.Get().Stack->Scope;
|
auto& scope = ThreadStacks.Get().Stack->Scope;
|
||||||
value = scope->Parameters[boxBase->ID - 1];
|
int32 index = boxBase->ID - 1;
|
||||||
|
if (index < scope->Parameters.Length())
|
||||||
|
value = scope->Parameters.Get()[index];
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
#include "Engine/Core/Delegate.h"
|
#include "Engine/Core/Delegate.h"
|
||||||
#include "Engine/Core/Types/String.h"
|
#include "Engine/Core/Types/String.h"
|
||||||
#include "Engine/Core/Types/DateTime.h"
|
#include "Engine/Core/Types/DateTime.h"
|
||||||
|
#include "Engine/Core/Collections/Array.h"
|
||||||
#include "Engine/Platform/CriticalSection.h"
|
#include "Engine/Platform/CriticalSection.h"
|
||||||
#include "Engine/Serialization/FileReadStream.h"
|
#include "Engine/Serialization/FileReadStream.h"
|
||||||
#include "Engine/Threading/ThreadLocal.h"
|
#include "Engine/Threading/ThreadLocal.h"
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ ExportAssetResult AssetExporters::ExportModel(ExportAssetContext& context)
|
|||||||
DeleteMe<FileWriteStream> outputDeleteMe(output);
|
DeleteMe<FileWriteStream> outputDeleteMe(output);
|
||||||
|
|
||||||
const auto name = StringUtils::GetFileNameWithoutExtension(asset->GetPath()).ToStringAnsi();
|
const auto name = StringUtils::GetFileNameWithoutExtension(asset->GetPath()).ToStringAnsi();
|
||||||
output->WriteTextFormatted("# Exported model {0}\n", name.Get());
|
output->WriteText(StringAnsi::Format("# Exported model {0}\n", name.Get()));
|
||||||
|
|
||||||
// Extract all meshes
|
// Extract all meshes
|
||||||
const auto& lod = asset->LODs[lodIndex];
|
const auto& lod = asset->LODs[lodIndex];
|
||||||
@@ -63,12 +63,12 @@ ExportAssetResult AssetExporters::ExportModel(ExportAssetContext& context)
|
|||||||
}
|
}
|
||||||
auto ib = stream.Read<byte>(indicesCount * ibStride);
|
auto ib = stream.Read<byte>(indicesCount * ibStride);
|
||||||
|
|
||||||
output->WriteTextFormatted("# Mesh {0}\n", meshIndex);
|
output->WriteText(StringAnsi::Format("# Mesh {0}\n", meshIndex));
|
||||||
|
|
||||||
for (uint32 i = 0; i < vertices; i++)
|
for (uint32 i = 0; i < vertices; i++)
|
||||||
{
|
{
|
||||||
auto v = vb0[i].Position;
|
auto v = vb0[i].Position;
|
||||||
output->WriteTextFormatted("v {0} {1} {2}\n", v.X, v.Y, v.Z);
|
output->WriteText(StringAnsi::Format("v {0} {1} {2}\n", v.X, v.Y, v.Z));
|
||||||
}
|
}
|
||||||
|
|
||||||
output->WriteChar('\n');
|
output->WriteChar('\n');
|
||||||
@@ -76,7 +76,7 @@ ExportAssetResult AssetExporters::ExportModel(ExportAssetContext& context)
|
|||||||
for (uint32 i = 0; i < vertices; i++)
|
for (uint32 i = 0; i < vertices; i++)
|
||||||
{
|
{
|
||||||
auto v = vb1[i].TexCoord;
|
auto v = vb1[i].TexCoord;
|
||||||
output->WriteTextFormatted("vt {0} {1}\n", Float16Compressor::Decompress(v.X), Float16Compressor::Decompress(v.Y));
|
output->WriteText(StringAnsi::Format("vt {0} {1}\n", Float16Compressor::Decompress(v.X), Float16Compressor::Decompress(v.Y)));
|
||||||
}
|
}
|
||||||
|
|
||||||
output->WriteChar('\n');
|
output->WriteChar('\n');
|
||||||
@@ -84,7 +84,7 @@ ExportAssetResult AssetExporters::ExportModel(ExportAssetContext& context)
|
|||||||
for (uint32 i = 0; i < vertices; i++)
|
for (uint32 i = 0; i < vertices; i++)
|
||||||
{
|
{
|
||||||
auto v = vb1[i].Normal.ToVector3() * 2.0f - 1.0f;
|
auto v = vb1[i].Normal.ToVector3() * 2.0f - 1.0f;
|
||||||
output->WriteTextFormatted("vn {0} {1} {2}\n", v.X, v.Y, v.Z);
|
output->WriteText(StringAnsi::Format("vn {0} {1} {2}\n", v.X, v.Y, v.Z));
|
||||||
}
|
}
|
||||||
|
|
||||||
output->WriteChar('\n');
|
output->WriteChar('\n');
|
||||||
@@ -97,7 +97,7 @@ ExportAssetResult AssetExporters::ExportModel(ExportAssetContext& context)
|
|||||||
auto i0 = vertexStart + *t++;
|
auto i0 = vertexStart + *t++;
|
||||||
auto i1 = vertexStart + *t++;
|
auto i1 = vertexStart + *t++;
|
||||||
auto i2 = vertexStart + *t++;
|
auto i2 = vertexStart + *t++;
|
||||||
output->WriteTextFormatted("f {0}/{0}/{0} {1}/{1}/{1} {2}/{2}/{2}\n", i0, i1, i2);
|
output->WriteText(StringAnsi::Format("f {0}/{0}/{0} {1}/{1}/{1} {2}/{2}/{2}\n", i0, i1, i2));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -108,7 +108,7 @@ ExportAssetResult AssetExporters::ExportModel(ExportAssetContext& context)
|
|||||||
auto i0 = vertexStart + *t++;
|
auto i0 = vertexStart + *t++;
|
||||||
auto i1 = vertexStart + *t++;
|
auto i1 = vertexStart + *t++;
|
||||||
auto i2 = vertexStart + *t++;
|
auto i2 = vertexStart + *t++;
|
||||||
output->WriteTextFormatted("f {0}/{0}/{0} {1}/{1}/{1} {2}/{2}/{2}\n", i0, i1, i2);
|
output->WriteText(StringAnsi::Format("f {0}/{0}/{0} {1}/{1}/{1} {2}/{2}/{2}\n", i0, i1, i2));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -146,7 +146,7 @@ ExportAssetResult AssetExporters::ExportSkinnedModel(ExportAssetContext& context
|
|||||||
DeleteMe<FileWriteStream> outputDeleteMe(output);
|
DeleteMe<FileWriteStream> outputDeleteMe(output);
|
||||||
|
|
||||||
const auto name = StringUtils::GetFileNameWithoutExtension(asset->GetPath()).ToStringAnsi();
|
const auto name = StringUtils::GetFileNameWithoutExtension(asset->GetPath()).ToStringAnsi();
|
||||||
output->WriteTextFormatted("# Exported model {0}\n", name.Get());
|
output->WriteText(StringAnsi::Format("# Exported model {0}\n", name.Get()));
|
||||||
|
|
||||||
// Extract all meshes
|
// Extract all meshes
|
||||||
const auto& lod = asset->LODs[lodIndex];
|
const auto& lod = asset->LODs[lodIndex];
|
||||||
@@ -168,12 +168,12 @@ ExportAssetResult AssetExporters::ExportSkinnedModel(ExportAssetContext& context
|
|||||||
auto vb0 = stream.Read<VB0SkinnedElementType>(vertices);
|
auto vb0 = stream.Read<VB0SkinnedElementType>(vertices);
|
||||||
auto ib = stream.Read<byte>(indicesCount * ibStride);
|
auto ib = stream.Read<byte>(indicesCount * ibStride);
|
||||||
|
|
||||||
output->WriteTextFormatted("# Mesh {0}\n", meshIndex);
|
output->WriteText(StringAnsi::Format("# Mesh {0}\n", meshIndex));
|
||||||
|
|
||||||
for (uint32 i = 0; i < vertices; i++)
|
for (uint32 i = 0; i < vertices; i++)
|
||||||
{
|
{
|
||||||
auto v = vb0[i].Position;
|
auto v = vb0[i].Position;
|
||||||
output->WriteTextFormatted("v {0} {1} {2}\n", v.X, v.Y, v.Z);
|
output->WriteText(StringAnsi::Format("v {0} {1} {2}\n", v.X, v.Y, v.Z));
|
||||||
}
|
}
|
||||||
|
|
||||||
output->WriteChar('\n');
|
output->WriteChar('\n');
|
||||||
@@ -181,7 +181,7 @@ ExportAssetResult AssetExporters::ExportSkinnedModel(ExportAssetContext& context
|
|||||||
for (uint32 i = 0; i < vertices; i++)
|
for (uint32 i = 0; i < vertices; i++)
|
||||||
{
|
{
|
||||||
auto v = vb0[i].TexCoord;
|
auto v = vb0[i].TexCoord;
|
||||||
output->WriteTextFormatted("vt {0} {1}\n", Float16Compressor::Decompress(v.X), Float16Compressor::Decompress(v.Y));
|
output->WriteText(StringAnsi::Format("vt {0} {1}\n", Float16Compressor::Decompress(v.X), Float16Compressor::Decompress(v.Y)));
|
||||||
}
|
}
|
||||||
|
|
||||||
output->WriteChar('\n');
|
output->WriteChar('\n');
|
||||||
@@ -189,7 +189,7 @@ ExportAssetResult AssetExporters::ExportSkinnedModel(ExportAssetContext& context
|
|||||||
for (uint32 i = 0; i < vertices; i++)
|
for (uint32 i = 0; i < vertices; i++)
|
||||||
{
|
{
|
||||||
auto v = vb0[i].Normal.ToVector3() * 2.0f - 1.0f;
|
auto v = vb0[i].Normal.ToVector3() * 2.0f - 1.0f;
|
||||||
output->WriteTextFormatted("vn {0} {1} {2}\n", v.X, v.Y, v.Z);
|
output->WriteText(StringAnsi::Format("vn {0} {1} {2}\n", v.X, v.Y, v.Z));
|
||||||
}
|
}
|
||||||
|
|
||||||
output->WriteChar('\n');
|
output->WriteChar('\n');
|
||||||
@@ -202,7 +202,7 @@ ExportAssetResult AssetExporters::ExportSkinnedModel(ExportAssetContext& context
|
|||||||
auto i0 = vertexStart + *t++;
|
auto i0 = vertexStart + *t++;
|
||||||
auto i1 = vertexStart + *t++;
|
auto i1 = vertexStart + *t++;
|
||||||
auto i2 = vertexStart + *t++;
|
auto i2 = vertexStart + *t++;
|
||||||
output->WriteTextFormatted("f {0}/{0}/{0} {1}/{1}/{1} {2}/{2}/{2}\n", i0, i1, i2);
|
output->WriteText(StringAnsi::Format("f {0}/{0}/{0} {1}/{1}/{1} {2}/{2}/{2}\n", i0, i1, i2));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -213,7 +213,7 @@ ExportAssetResult AssetExporters::ExportSkinnedModel(ExportAssetContext& context
|
|||||||
auto i0 = vertexStart + *t++;
|
auto i0 = vertexStart + *t++;
|
||||||
auto i1 = vertexStart + *t++;
|
auto i1 = vertexStart + *t++;
|
||||||
auto i2 = vertexStart + *t++;
|
auto i2 = vertexStart + *t++;
|
||||||
output->WriteTextFormatted("f {0}/{0}/{0} {1}/{1}/{1} {2}/{2}/{2}\n", i0, i1, i2);
|
output->WriteText(StringAnsi::Format("f {0}/{0}/{0} {1}/{1}/{1} {2}/{2}/{2}\n", i0, i1, i2));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,9 @@
|
|||||||
#include "Engine/Serialization/FileWriteStream.h"
|
#include "Engine/Serialization/FileWriteStream.h"
|
||||||
#include "Engine/Serialization/MemoryWriteStream.h"
|
#include "Engine/Serialization/MemoryWriteStream.h"
|
||||||
#include "Engine/Debug/Exceptions/Exceptions.h"
|
#include "Engine/Debug/Exceptions/Exceptions.h"
|
||||||
|
#if USE_EDITOR
|
||||||
|
#include "Engine/Core/Collections/Array.h"
|
||||||
|
#endif
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
#define LOG_ENABLE_FILE (!PLATFORM_SWITCH)
|
#define LOG_ENABLE_FILE (!PLATFORM_SWITCH)
|
||||||
|
|||||||
@@ -1,25 +0,0 @@
|
|||||||
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
|
|
||||||
|
|
||||||
#include "Object.h"
|
|
||||||
#include "ObjectsRemovalService.h"
|
|
||||||
|
|
||||||
RemovableObject::~RemovableObject()
|
|
||||||
{
|
|
||||||
#if BUILD_DEBUG
|
|
||||||
// Prevent removing object that is still reverenced by the removal service
|
|
||||||
ASSERT(!ObjectsRemovalService::IsInPool(this));
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void RemovableObject::DeleteObjectNow()
|
|
||||||
{
|
|
||||||
ObjectsRemovalService::Dereference(this);
|
|
||||||
|
|
||||||
OnDeleteObject();
|
|
||||||
}
|
|
||||||
|
|
||||||
void RemovableObject::DeleteObject(float timeToLive, bool useGameTime)
|
|
||||||
{
|
|
||||||
// Add to deferred remove (or just update timeout but don't remove object here)
|
|
||||||
ObjectsRemovalService::Add(this, timeToLive, useGameTime);
|
|
||||||
}
|
|
||||||
@@ -37,30 +37,13 @@ public:
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Finalizes an instance of the <see cref="Object"/> class.
|
/// Finalizes an instance of the <see cref="Object"/> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
virtual ~Object()
|
virtual ~Object();
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the string representation of this object.
|
/// Gets the string representation of this object.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>The string.</returns>
|
/// <returns>The string.</returns>
|
||||||
virtual String ToString() const = 0;
|
virtual String ToString() const = 0;
|
||||||
};
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Interface for removable Engine objects.
|
|
||||||
/// </summary>
|
|
||||||
/// <seealso cref="Object" />
|
|
||||||
class FLAXENGINE_API RemovableObject : public Object
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Virtual destructor but protected. Removable objects should be deleted using `DeleteObject` which supports deferred delete.
|
|
||||||
/// Note: it's unsafe to delete object using destructor it it has been marked for deferred delete.
|
|
||||||
/// </summary>
|
|
||||||
virtual ~RemovableObject();
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Deletes the object without queueing it to the ObjectsRemovalService.
|
/// Deletes the object without queueing it to the ObjectsRemovalService.
|
||||||
@@ -68,7 +51,7 @@ public:
|
|||||||
void DeleteObjectNow();
|
void DeleteObjectNow();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Deletes the object.
|
/// Deletes the object (deferred).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="timeToLive">The time to live (in seconds). Use zero to kill it now.</param>
|
/// <param name="timeToLive">The time to live (in seconds). Use zero to kill it now.</param>
|
||||||
/// <param name="useGameTime">True if unscaled game time for the object life timeout, otherwise false to use absolute time.</param>
|
/// <param name="useGameTime">True if unscaled game time for the object life timeout, otherwise false to use absolute time.</param>
|
||||||
@@ -82,3 +65,6 @@ public:
|
|||||||
Delete(this);
|
Delete(this);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// [Deprecated on 5.01.2022, expires on 5.01.2024]
|
||||||
|
typedef Object RemovableObject;
|
||||||
|
|||||||
@@ -16,8 +16,8 @@ namespace ObjectsRemovalServiceImpl
|
|||||||
CriticalSection NewItemsLocker;
|
CriticalSection NewItemsLocker;
|
||||||
DateTime LastUpdate;
|
DateTime LastUpdate;
|
||||||
float LastUpdateGameTime;
|
float LastUpdateGameTime;
|
||||||
Dictionary<RemovableObject*, float> Pool(8192);
|
Dictionary<Object*, float> Pool(8192);
|
||||||
Dictionary<RemovableObject*, float> NewItemsPool(2048);
|
Dictionary<Object*, float> NewItemsPool(2048);
|
||||||
}
|
}
|
||||||
|
|
||||||
using namespace ObjectsRemovalServiceImpl;
|
using namespace ObjectsRemovalServiceImpl;
|
||||||
@@ -38,7 +38,7 @@ public:
|
|||||||
|
|
||||||
ObjectsRemovalServiceService ObjectsRemovalServiceServiceInstance;
|
ObjectsRemovalServiceService ObjectsRemovalServiceServiceInstance;
|
||||||
|
|
||||||
bool ObjectsRemovalService::IsInPool(RemovableObject* obj)
|
bool ObjectsRemovalService::IsInPool(Object* obj)
|
||||||
{
|
{
|
||||||
if (!IsReady)
|
if (!IsReady)
|
||||||
return false;
|
return false;
|
||||||
@@ -67,7 +67,7 @@ bool ObjectsRemovalService::HasNewItemsForFlush()
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ObjectsRemovalService::Dereference(RemovableObject* obj)
|
void ObjectsRemovalService::Dereference(Object* obj)
|
||||||
{
|
{
|
||||||
if (!IsReady)
|
if (!IsReady)
|
||||||
return;
|
return;
|
||||||
@@ -81,7 +81,7 @@ void ObjectsRemovalService::Dereference(RemovableObject* obj)
|
|||||||
PoolLocker.Unlock();
|
PoolLocker.Unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ObjectsRemovalService::Add(RemovableObject* obj, float timeToLive, bool useGameTime)
|
void ObjectsRemovalService::Add(Object* obj, float timeToLive, bool useGameTime)
|
||||||
{
|
{
|
||||||
ScopeLock lock(NewItemsLocker);
|
ScopeLock lock(NewItemsLocker);
|
||||||
|
|
||||||
@@ -213,3 +213,24 @@ void ObjectsRemovalServiceService::Dispose()
|
|||||||
|
|
||||||
IsReady = false;
|
IsReady = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Object::~Object()
|
||||||
|
{
|
||||||
|
#if BUILD_DEBUG
|
||||||
|
// Prevent removing object that is still reverenced by the removal service
|
||||||
|
ASSERT(!ObjectsRemovalService::IsInPool(this));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void Object::DeleteObjectNow()
|
||||||
|
{
|
||||||
|
ObjectsRemovalService::Dereference(this);
|
||||||
|
|
||||||
|
OnDeleteObject();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Object::DeleteObject(float timeToLive, bool useGameTime)
|
||||||
|
{
|
||||||
|
// Add to deferred remove (or just update timeout but don't remove object here)
|
||||||
|
ObjectsRemovalService::Add(this, timeToLive, useGameTime);
|
||||||
|
}
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ public:
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="obj">The object.</param>
|
/// <param name="obj">The object.</param>
|
||||||
/// <returns>True if object has been registered in the pool for the removing, otherwise false.</returns>
|
/// <returns>True if object has been registered in the pool for the removing, otherwise false.</returns>
|
||||||
static bool IsInPool(RemovableObject* obj);
|
static bool IsInPool(Object* obj);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Determines whether any object has been registered to be removed from pool (requests are flushed on Flush call).
|
/// Determines whether any object has been registered to be removed from pool (requests are flushed on Flush call).
|
||||||
@@ -28,7 +28,7 @@ public:
|
|||||||
/// Removes the specified object from the dead pool (clears the reference to it).
|
/// Removes the specified object from the dead pool (clears the reference to it).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="obj">The object.</param>
|
/// <param name="obj">The object.</param>
|
||||||
static void Dereference(RemovableObject* obj);
|
static void Dereference(Object* obj);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Adds the specified object to the dead pool.
|
/// Adds the specified object to the dead pool.
|
||||||
@@ -36,7 +36,7 @@ public:
|
|||||||
/// <param name="obj">The object.</param>
|
/// <param name="obj">The object.</param>
|
||||||
/// <param name="timeToLive">The time to live (in seconds).</param>
|
/// <param name="timeToLive">The time to live (in seconds).</param>
|
||||||
/// <param name="useGameTime">True if unscaled game time for the object life timeout, otherwise false to use absolute time.</param>
|
/// <param name="useGameTime">True if unscaled game time for the object life timeout, otherwise false to use absolute time.</param>
|
||||||
static void Add(RemovableObject* obj, float timeToLive = 1.0f, bool useGameTime = false);
|
static void Add(Object* obj, float timeToLive = 1.0f, bool useGameTime = false);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Flushes the objects pool removing objects marked to remove now (with negative or zero time to live).
|
/// Flushes the objects pool removing objects marked to remove now (with negative or zero time to live).
|
||||||
|
|||||||
@@ -337,6 +337,8 @@ public:
|
|||||||
void ReserveSpace(int32 length)
|
void ReserveSpace(int32 length)
|
||||||
{
|
{
|
||||||
ASSERT(length >= 0);
|
ASSERT(length >= 0);
|
||||||
|
if (length == _length)
|
||||||
|
return;
|
||||||
Platform::Free(_data);
|
Platform::Free(_data);
|
||||||
if (length != 0)
|
if (length != 0)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
#include "Foliage.h"
|
#include "Foliage.h"
|
||||||
|
|
||||||
FoliageType::FoliageType()
|
FoliageType::FoliageType()
|
||||||
: PersistentScriptingObject(SpawnParams(Guid::New(), TypeInitializer))
|
: ScriptingObject(SpawnParams(Guid::New(), TypeInitializer))
|
||||||
, Foliage(nullptr)
|
, Foliage(nullptr)
|
||||||
, Index(-1)
|
, Index(-1)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ API_ENUM() enum class FoliageScalingModes
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Foliage mesh instances type descriptor. Defines the shared properties of the spawned mesh instances.
|
/// Foliage mesh instances type descriptor. Defines the shared properties of the spawned mesh instances.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
API_CLASS(Sealed, NoSpawn) class FLAXENGINE_API FoliageType : public PersistentScriptingObject, public ISerializable
|
API_CLASS(Sealed, NoSpawn) class FLAXENGINE_API FoliageType : public ScriptingObject, public ISerializable
|
||||||
{
|
{
|
||||||
DECLARE_SCRIPTING_TYPE_NO_SPAWN(FoliageType);
|
DECLARE_SCRIPTING_TYPE_NO_SPAWN(FoliageType);
|
||||||
friend Foliage;
|
friend Foliage;
|
||||||
|
|||||||
@@ -14,13 +14,13 @@
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Interface for GPU device adapter.
|
/// Interface for GPU device adapter.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
API_CLASS(NoSpawn, Attributes="HideInEditor") class FLAXENGINE_API GPUAdapter : public PersistentScriptingObject
|
API_CLASS(NoSpawn, Attributes="HideInEditor") class FLAXENGINE_API GPUAdapter : public ScriptingObject
|
||||||
{
|
{
|
||||||
DECLARE_SCRIPTING_TYPE_NO_SPAWN(GPUDevice);
|
DECLARE_SCRIPTING_TYPE_NO_SPAWN(GPUDevice);
|
||||||
public:
|
public:
|
||||||
|
|
||||||
GPUAdapter()
|
GPUAdapter()
|
||||||
: PersistentScriptingObject(SpawnParams(Guid::New(), TypeInitializer))
|
: ScriptingObject(SpawnParams(Guid::New(), TypeInitializer))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
#include "Textures/GPUTexture.h"
|
#include "Textures/GPUTexture.h"
|
||||||
|
|
||||||
GPUContext::GPUContext(GPUDevice* device)
|
GPUContext::GPUContext(GPUDevice* device)
|
||||||
: PersistentScriptingObject(ScriptingObjectSpawnParams(Guid::New(), TypeInitializer))
|
: ScriptingObject(ScriptingObjectSpawnParams(Guid::New(), TypeInitializer))
|
||||||
, _device(device)
|
, _device(device)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -113,7 +113,7 @@ DECLARE_SCRIPTING_TYPE_NO_SPAWN(GPUDrawIndexedIndirectArgs);
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Interface for GPU device context that can record and send graphics commands to the GPU in a sequence.
|
/// Interface for GPU device context that can record and send graphics commands to the GPU in a sequence.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
API_CLASS(Sealed, NoSpawn) class FLAXENGINE_API GPUContext : public PersistentScriptingObject
|
API_CLASS(Sealed, NoSpawn) class FLAXENGINE_API GPUContext : public ScriptingObject
|
||||||
{
|
{
|
||||||
DECLARE_SCRIPTING_TYPE_NO_SPAWN(GPUContext);
|
DECLARE_SCRIPTING_TYPE_NO_SPAWN(GPUContext);
|
||||||
private:
|
private:
|
||||||
|
|||||||
@@ -173,12 +173,12 @@ GPUPipelineState::Description GPUPipelineState::Description::DefaultFullscreenTr
|
|||||||
};
|
};
|
||||||
|
|
||||||
GPUResource::GPUResource()
|
GPUResource::GPUResource()
|
||||||
: PersistentScriptingObject(SpawnParams(Guid::New(), GPUResource::TypeInitializer))
|
: ScriptingObject(SpawnParams(Guid::New(), GPUResource::TypeInitializer))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
GPUResource::GPUResource(const SpawnParams& params)
|
GPUResource::GPUResource(const SpawnParams& params)
|
||||||
: PersistentScriptingObject(params)
|
: ScriptingObject(params)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -241,7 +241,7 @@ void GPUResource::OnDeleteObject()
|
|||||||
{
|
{
|
||||||
ReleaseGPU();
|
ReleaseGPU();
|
||||||
|
|
||||||
PersistentScriptingObject::OnDeleteObject();
|
ScriptingObject::OnDeleteObject();
|
||||||
}
|
}
|
||||||
|
|
||||||
double GPUResourceView::DummyLastRenderTime = -1;
|
double GPUResourceView::DummyLastRenderTime = -1;
|
||||||
@@ -262,7 +262,7 @@ struct GPUDevice::PrivateData
|
|||||||
GPUDevice* GPUDevice::Instance = nullptr;
|
GPUDevice* GPUDevice::Instance = nullptr;
|
||||||
|
|
||||||
GPUDevice::GPUDevice(RendererType type, ShaderProfile profile)
|
GPUDevice::GPUDevice(RendererType type, ShaderProfile profile)
|
||||||
: PersistentScriptingObject(SpawnParams(Guid::New(), TypeInitializer))
|
: ScriptingObject(SpawnParams(Guid::New(), TypeInitializer))
|
||||||
, _state(DeviceState::Missing)
|
, _state(DeviceState::Missing)
|
||||||
, _isRendering(false)
|
, _isRendering(false)
|
||||||
, _wasVSyncUsed(false)
|
, _wasVSyncUsed(false)
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ class MaterialBase;
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Graphics device object for rendering on GPU.
|
/// Graphics device object for rendering on GPU.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
API_CLASS(Sealed, NoSpawn) class FLAXENGINE_API GPUDevice : public PersistentScriptingObject
|
API_CLASS(Sealed, NoSpawn) class FLAXENGINE_API GPUDevice : public ScriptingObject
|
||||||
{
|
{
|
||||||
DECLARE_SCRIPTING_TYPE_NO_SPAWN(GPUDevice);
|
DECLARE_SCRIPTING_TYPE_NO_SPAWN(GPUDevice);
|
||||||
public:
|
public:
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The base class for all GPU resources.
|
/// The base class for all GPU resources.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
API_CLASS(Abstract, NoSpawn) class FLAXENGINE_API GPUResource : public PersistentScriptingObject
|
API_CLASS(Abstract, NoSpawn) class FLAXENGINE_API GPUResource : public ScriptingObject
|
||||||
{
|
{
|
||||||
DECLARE_SCRIPTING_TYPE_NO_SPAWN(GPUResource);
|
DECLARE_SCRIPTING_TYPE_NO_SPAWN(GPUResource);
|
||||||
public:
|
public:
|
||||||
@@ -108,7 +108,7 @@ protected:
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
// [PersistentScriptingObject]
|
// [ScriptingObject]
|
||||||
String ToString() const override;
|
String ToString() const override;
|
||||||
void OnDeleteObject() override;
|
void OnDeleteObject() override;
|
||||||
};
|
};
|
||||||
@@ -186,14 +186,14 @@ public:
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Interface for GPU resources views. Shared base class for texture and buffer views.
|
/// Interface for GPU resources views. Shared base class for texture and buffer views.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
API_CLASS(Abstract, NoSpawn, Attributes="HideInEditor") class FLAXENGINE_API GPUResourceView : public PersistentScriptingObject
|
API_CLASS(Abstract, NoSpawn, Attributes="HideInEditor") class FLAXENGINE_API GPUResourceView : public ScriptingObject
|
||||||
{
|
{
|
||||||
DECLARE_SCRIPTING_TYPE_NO_SPAWN(GPUResourceView);
|
DECLARE_SCRIPTING_TYPE_NO_SPAWN(GPUResourceView);
|
||||||
protected:
|
protected:
|
||||||
static double DummyLastRenderTime;
|
static double DummyLastRenderTime;
|
||||||
|
|
||||||
explicit GPUResourceView(const SpawnParams& params)
|
explicit GPUResourceView(const SpawnParams& params)
|
||||||
: PersistentScriptingObject(params)
|
: ScriptingObject(params)
|
||||||
, LastRenderTime(&DummyLastRenderTime)
|
, LastRenderTime(&DummyLastRenderTime)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -167,9 +167,9 @@ struct SerializedMaterialParam
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Material variable object. Allows to modify material parameter value at runtime.
|
/// Material variable object. Allows to modify material parameter value at runtime.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
API_CLASS(NoSpawn) class FLAXENGINE_API MaterialParameter : public PersistentScriptingObject
|
API_CLASS(NoSpawn) class FLAXENGINE_API MaterialParameter : public ScriptingObject
|
||||||
{
|
{
|
||||||
DECLARE_SCRIPTING_TYPE_WITH_CONSTRUCTOR_IMPL(MaterialParameter, PersistentScriptingObject);
|
DECLARE_SCRIPTING_TYPE_WITH_CONSTRUCTOR_IMPL(MaterialParameter, ScriptingObject);
|
||||||
friend MaterialParams;
|
friend MaterialParams;
|
||||||
friend MaterialInstance;
|
friend MaterialInstance;
|
||||||
private:
|
private:
|
||||||
|
|||||||
@@ -10,9 +10,9 @@
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The material slot descriptor that specifies how to render geometry using it.
|
/// The material slot descriptor that specifies how to render geometry using it.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
API_CLASS(NoSpawn) class FLAXENGINE_API MaterialSlot : public PersistentScriptingObject
|
API_CLASS(NoSpawn) class FLAXENGINE_API MaterialSlot : public ScriptingObject
|
||||||
{
|
{
|
||||||
DECLARE_SCRIPTING_TYPE_WITH_CONSTRUCTOR_IMPL(MaterialSlot, PersistentScriptingObject);
|
DECLARE_SCRIPTING_TYPE_WITH_CONSTRUCTOR_IMPL(MaterialSlot, ScriptingObject);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The material to use for rendering.
|
/// The material to use for rendering.
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ class ModelBase;
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Base class for model resources meshes.
|
/// Base class for model resources meshes.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
API_CLASS(Abstract, NoSpawn) class FLAXENGINE_API MeshBase : public PersistentScriptingObject
|
API_CLASS(Abstract, NoSpawn) class FLAXENGINE_API MeshBase : public ScriptingObject
|
||||||
{
|
{
|
||||||
DECLARE_SCRIPTING_TYPE_MINIMAL(MeshBase);
|
DECLARE_SCRIPTING_TYPE_MINIMAL(MeshBase);
|
||||||
protected:
|
protected:
|
||||||
@@ -30,7 +30,7 @@ protected:
|
|||||||
bool _use16BitIndexBuffer;
|
bool _use16BitIndexBuffer;
|
||||||
|
|
||||||
explicit MeshBase(const SpawnParams& params)
|
explicit MeshBase(const SpawnParams& params)
|
||||||
: PersistentScriptingObject(params)
|
: ScriptingObject(params)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,9 +10,9 @@ class MemoryReadStream;
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents single Level Of Detail for the model. Contains a collection of the meshes.
|
/// Represents single Level Of Detail for the model. Contains a collection of the meshes.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
API_CLASS(NoSpawn) class FLAXENGINE_API ModelLOD : public PersistentScriptingObject
|
API_CLASS(NoSpawn) class FLAXENGINE_API ModelLOD : public ScriptingObject
|
||||||
{
|
{
|
||||||
DECLARE_SCRIPTING_TYPE_WITH_CONSTRUCTOR_IMPL(ModelLOD, PersistentScriptingObject);
|
DECLARE_SCRIPTING_TYPE_WITH_CONSTRUCTOR_IMPL(ModelLOD, ScriptingObject);
|
||||||
friend Model;
|
friend Model;
|
||||||
friend Mesh;
|
friend Mesh;
|
||||||
private:
|
private:
|
||||||
|
|||||||
@@ -10,9 +10,9 @@ class MemoryReadStream;
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents single Level Of Detail for the skinned model. Contains a collection of the meshes.
|
/// Represents single Level Of Detail for the skinned model. Contains a collection of the meshes.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
API_CLASS(NoSpawn) class FLAXENGINE_API SkinnedModelLOD : public PersistentScriptingObject
|
API_CLASS(NoSpawn) class FLAXENGINE_API SkinnedModelLOD : public ScriptingObject
|
||||||
{
|
{
|
||||||
DECLARE_SCRIPTING_TYPE_WITH_CONSTRUCTOR_IMPL(SkinnedModelLOD, PersistentScriptingObject);
|
DECLARE_SCRIPTING_TYPE_WITH_CONSTRUCTOR_IMPL(SkinnedModelLOD, ScriptingObject);
|
||||||
friend SkinnedModel;
|
friend SkinnedModel;
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
#include "Engine/Engine/Engine.h"
|
#include "Engine/Engine/Engine.h"
|
||||||
|
|
||||||
RenderBuffers::RenderBuffers(const SpawnParams& params)
|
RenderBuffers::RenderBuffers(const SpawnParams& params)
|
||||||
: PersistentScriptingObject(params)
|
: ScriptingObject(params)
|
||||||
{
|
{
|
||||||
#define CREATE_TEXTURE(name) name = GPUDevice::Instance->CreateTexture(TEXT(#name)); _resources.Add(name)
|
#define CREATE_TEXTURE(name) name = GPUDevice::Instance->CreateTexture(TEXT(#name)); _resources.Add(name)
|
||||||
CREATE_TEXTURE(DepthBuffer);
|
CREATE_TEXTURE(DepthBuffer);
|
||||||
@@ -183,8 +183,3 @@ void RenderBuffers::Release()
|
|||||||
UPDATE_LAZY_KEEP_RT(LuminanceMap);
|
UPDATE_LAZY_KEEP_RT(LuminanceMap);
|
||||||
#undef UPDATE_LAZY_KEEP_RT
|
#undef UPDATE_LAZY_KEEP_RT
|
||||||
}
|
}
|
||||||
|
|
||||||
String RenderBuffers::ToString() const
|
|
||||||
{
|
|
||||||
return TEXT("RenderBuffers");
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The scene rendering buffers container.
|
/// The scene rendering buffers container.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
API_CLASS() class FLAXENGINE_API RenderBuffers : public PersistentScriptingObject
|
API_CLASS() class FLAXENGINE_API RenderBuffers : public ScriptingObject
|
||||||
{
|
{
|
||||||
DECLARE_SCRIPTING_TYPE(RenderBuffers);
|
DECLARE_SCRIPTING_TYPE(RenderBuffers);
|
||||||
protected:
|
protected:
|
||||||
@@ -175,9 +175,4 @@ public:
|
|||||||
/// Release the buffers data.
|
/// Release the buffers data.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
API_FUNCTION() void Release();
|
API_FUNCTION() void Release();
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
// [PersistentScriptingObject]
|
|
||||||
String ToString() const override;
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -82,7 +82,7 @@ void RenderTask::DrawAll()
|
|||||||
}
|
}
|
||||||
|
|
||||||
RenderTask::RenderTask(const SpawnParams& params)
|
RenderTask::RenderTask(const SpawnParams& params)
|
||||||
: PersistentScriptingObject(params)
|
: ScriptingObject(params)
|
||||||
{
|
{
|
||||||
// Register
|
// Register
|
||||||
TasksLocker.Lock();
|
TasksLocker.Lock();
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ class Actor;
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Allows to perform custom rendering using graphics pipeline.
|
/// Allows to perform custom rendering using graphics pipeline.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
API_CLASS() class FLAXENGINE_API RenderTask : public PersistentScriptingObject
|
API_CLASS() class FLAXENGINE_API RenderTask : public ScriptingObject
|
||||||
{
|
{
|
||||||
DECLARE_SCRIPTING_TYPE(RenderTask);
|
DECLARE_SCRIPTING_TYPE(RenderTask);
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Base class for all input device objects.
|
/// Base class for all input device objects.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
API_CLASS(Abstract, NoSpawn) class FLAXENGINE_API InputDevice : public PersistentScriptingObject
|
API_CLASS(Abstract, NoSpawn) class FLAXENGINE_API InputDevice : public ScriptingObject
|
||||||
{
|
{
|
||||||
DECLARE_SCRIPTING_TYPE_NO_SPAWN(InputDevice);
|
DECLARE_SCRIPTING_TYPE_NO_SPAWN(InputDevice);
|
||||||
public:
|
public:
|
||||||
@@ -86,7 +86,7 @@ protected:
|
|||||||
EventQueue _queue;
|
EventQueue _queue;
|
||||||
|
|
||||||
explicit InputDevice(const SpawnParams& params, const StringView& name)
|
explicit InputDevice(const SpawnParams& params, const StringView& name)
|
||||||
: PersistentScriptingObject(params)
|
: ScriptingObject(params)
|
||||||
, _name(name)
|
, _name(name)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -971,7 +971,7 @@ private:
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
// [PersistentScriptingObject]
|
// [ScriptingObject]
|
||||||
String ToString() const override;
|
String ToString() const override;
|
||||||
void OnDeleteObject() override;
|
void OnDeleteObject() override;
|
||||||
|
|
||||||
|
|||||||
@@ -36,6 +36,7 @@
|
|||||||
#include "Editor/Editor.h"
|
#include "Editor/Editor.h"
|
||||||
#include "Engine/Platform/MessageBox.h"
|
#include "Engine/Platform/MessageBox.h"
|
||||||
#include "Engine/Engine/CommandLine.h"
|
#include "Engine/Engine/CommandLine.h"
|
||||||
|
#include "Engine/Serialization/JsonSerializer.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
bool LayersMask::HasLayer(const StringView& layerName) const
|
bool LayersMask::HasLayer(const StringView& layerName) const
|
||||||
@@ -74,11 +75,25 @@ public:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#if USE_EDITOR
|
||||||
|
|
||||||
|
struct ScriptsReloadObject
|
||||||
|
{
|
||||||
|
StringAnsi TypeName;
|
||||||
|
ScriptingObject** Object;
|
||||||
|
Array<byte> Data;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace LevelImpl
|
namespace LevelImpl
|
||||||
{
|
{
|
||||||
Array<SceneAction*> _sceneActions;
|
Array<SceneAction*> _sceneActions;
|
||||||
CriticalSection _sceneActionsLocker;
|
CriticalSection _sceneActionsLocker;
|
||||||
DateTime _lastSceneLoadTime(0);
|
DateTime _lastSceneLoadTime(0);
|
||||||
|
#if USE_EDITOR
|
||||||
|
Array<ScriptsReloadObject> ScriptsReloadObjects;
|
||||||
|
#endif
|
||||||
|
|
||||||
void CallSceneEvent(SceneEventType eventType, Scene* scene, Guid sceneId);
|
void CallSceneEvent(SceneEventType eventType, Scene* scene, Guid sceneId);
|
||||||
|
|
||||||
@@ -128,9 +143,11 @@ Delegate<Scene*, const Guid&> Level::SceneLoaded;
|
|||||||
Delegate<Scene*, const Guid&> Level::SceneLoadError;
|
Delegate<Scene*, const Guid&> Level::SceneLoadError;
|
||||||
Delegate<Scene*, const Guid&> Level::SceneUnloading;
|
Delegate<Scene*, const Guid&> Level::SceneUnloading;
|
||||||
Delegate<Scene*, const Guid&> Level::SceneUnloaded;
|
Delegate<Scene*, const Guid&> Level::SceneUnloaded;
|
||||||
|
#if USE_EDITOR
|
||||||
Action Level::ScriptsReloadStart;
|
Action Level::ScriptsReloadStart;
|
||||||
Action Level::ScriptsReload;
|
Action Level::ScriptsReload;
|
||||||
Action Level::ScriptsReloadEnd;
|
Action Level::ScriptsReloadEnd;
|
||||||
|
#endif
|
||||||
Array<String> Level::Tags;
|
Array<String> Level::Tags;
|
||||||
String Level::Layers[32];
|
String Level::Layers[32];
|
||||||
|
|
||||||
@@ -559,6 +576,24 @@ public:
|
|||||||
Level::ScriptsReload();
|
Level::ScriptsReload();
|
||||||
Scripting::Reload();
|
Scripting::Reload();
|
||||||
|
|
||||||
|
// Restore objects
|
||||||
|
for (auto& e : ScriptsReloadObjects)
|
||||||
|
{
|
||||||
|
const ScriptingTypeHandle typeHandle = Scripting::FindScriptingType(e.TypeName);
|
||||||
|
*e.Object = ScriptingObject::NewObject(typeHandle);
|
||||||
|
if (!*e.Object)
|
||||||
|
{
|
||||||
|
LOG(Warning, "Failed to restore hot-reloaded object of type {0}.", String(e.TypeName));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
auto* serializable = ScriptingObject::ToInterface<ISerializable>(*e.Object);
|
||||||
|
if (serializable && e.Data.HasItems())
|
||||||
|
{
|
||||||
|
JsonSerializer::LoadFromBytes(serializable, e.Data, FLAXENGINE_VERSION_BUILD);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ScriptsReloadObjects.Clear();
|
||||||
|
|
||||||
// Restore scenes (from memory)
|
// Restore scenes (from memory)
|
||||||
for (int32 i = 0; i < scenesCount; i++)
|
for (int32 i = 0; i < scenesCount; i++)
|
||||||
{
|
{
|
||||||
@@ -595,6 +630,20 @@ public:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
void Level::ScriptsReloadRegisterObject(ScriptingObject*& obj)
|
||||||
|
{
|
||||||
|
if (!obj)
|
||||||
|
return;
|
||||||
|
auto& e = ScriptsReloadObjects.AddOne();
|
||||||
|
e.Object = &obj;
|
||||||
|
e.TypeName = obj->GetType().Fullname;
|
||||||
|
if (auto* serializable = ScriptingObject::ToInterface<ISerializable>(obj))
|
||||||
|
e.Data = JsonSerializer::SaveToBytes(serializable);
|
||||||
|
ScriptingObject* o = obj;
|
||||||
|
obj = nullptr;
|
||||||
|
o->DeleteObjectNow();
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
class SpawnActorAction : public SceneAction
|
class SpawnActorAction : public SceneAction
|
||||||
@@ -605,9 +654,9 @@ public:
|
|||||||
ScriptingObjectReference<Actor> ParentActor;
|
ScriptingObjectReference<Actor> ParentActor;
|
||||||
|
|
||||||
SpawnActorAction(Actor* actor, Actor* parent)
|
SpawnActorAction(Actor* actor, Actor* parent)
|
||||||
|
: TargetActor(actor)
|
||||||
|
, ParentActor(parent)
|
||||||
{
|
{
|
||||||
TargetActor = actor;
|
|
||||||
ParentActor = parent;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Do() const override
|
bool Do() const override
|
||||||
@@ -623,8 +672,8 @@ public:
|
|||||||
ScriptingObjectReference<Actor> TargetActor;
|
ScriptingObjectReference<Actor> TargetActor;
|
||||||
|
|
||||||
DeleteActorAction(Actor* actor)
|
DeleteActorAction(Actor* actor)
|
||||||
|
: TargetActor(actor)
|
||||||
{
|
{
|
||||||
TargetActor = actor;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Do() const override
|
bool Do() const override
|
||||||
|
|||||||
@@ -215,6 +215,8 @@ public:
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
API_EVENT() static Delegate<Scene*, const Guid&> SceneUnloaded;
|
API_EVENT() static Delegate<Scene*, const Guid&> SceneUnloaded;
|
||||||
|
|
||||||
|
#if USE_EDITOR
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Fired when scene starts reloading scripts.
|
/// Fired when scene starts reloading scripts.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -230,6 +232,14 @@ public:
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
API_EVENT() static Action ScriptsReloadEnd;
|
API_EVENT() static Action ScriptsReloadEnd;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds object to preserve during scripts reload. Called during ScriptsReloadStart event to serialize and destroy the object that should be restored when scripts reload ends.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="obj">Reference to the object to preserve during the scripting reload.</param>
|
||||||
|
static void ScriptsReloadRegisterObject(ScriptingObject*& obj);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ typedef Dictionary<Guid, Actor*, HeapAllocation> ActorsLookup;
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Base class for objects that are parts of the scene (actors and scripts).
|
/// Base class for objects that are parts of the scene (actors and scripts).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
API_CLASS(Abstract, NoSpawn) class FLAXENGINE_API SceneObject : public PersistentScriptingObject, public ISerializable
|
API_CLASS(Abstract, NoSpawn) class FLAXENGINE_API SceneObject : public ScriptingObject, public ISerializable
|
||||||
{
|
{
|
||||||
DECLARE_SCRIPTING_TYPE_NO_SPAWN(SceneObject);
|
DECLARE_SCRIPTING_TYPE_NO_SPAWN(SceneObject);
|
||||||
friend PrefabInstanceData;
|
friend PrefabInstanceData;
|
||||||
@@ -64,7 +64,7 @@ DECLARE_SCRIPTING_TYPE_NO_SPAWN(SceneObject);
|
|||||||
friend ScriptsFactory;
|
friend ScriptsFactory;
|
||||||
friend SceneTicking;
|
friend SceneTicking;
|
||||||
public:
|
public:
|
||||||
typedef PersistentScriptingObject Base;
|
typedef ScriptingObject Base;
|
||||||
|
|
||||||
// Scene Object lifetime flow:
|
// Scene Object lifetime flow:
|
||||||
// - Create
|
// - Create
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ void SendPacketToPeer(ENetPeer* peer, const NetworkChannelType channelType, cons
|
|||||||
}
|
}
|
||||||
|
|
||||||
ENetDriver::ENetDriver(const SpawnParams& params)
|
ENetDriver::ENetDriver(const SpawnParams& params)
|
||||||
: PersistentScriptingObject(params)
|
: ScriptingObject(params)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Low-level network transport interface implementation based on ENet library.
|
/// Low-level network transport interface implementation based on ENet library.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
API_CLASS(Namespace="FlaxEngine.Networking", Sealed) class FLAXENGINE_API ENetDriver : public PersistentScriptingObject, public INetworkDriver
|
API_CLASS(Namespace="FlaxEngine.Networking", Sealed) class FLAXENGINE_API ENetDriver : public ScriptingObject, public INetworkDriver
|
||||||
{
|
{
|
||||||
DECLARE_SCRIPTING_TYPE(ENetDriver);
|
DECLARE_SCRIPTING_TYPE(ENetDriver);
|
||||||
public:
|
public:
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Low-level network peer class. Provides server-client communication functions, message processing and sending.
|
/// Low-level network peer class. Provides server-client communication functions, message processing and sending.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
API_CLASS(sealed, NoSpawn, Namespace = "FlaxEngine.Networking") class FLAXENGINE_API NetworkPeer final : public PersistentScriptingObject
|
API_CLASS(sealed, NoSpawn, Namespace = "FlaxEngine.Networking") class FLAXENGINE_API NetworkPeer final : public ScriptingObject
|
||||||
{
|
{
|
||||||
DECLARE_SCRIPTING_TYPE_NO_SPAWN(NetworkPeer);
|
DECLARE_SCRIPTING_TYPE_NO_SPAWN(NetworkPeer);
|
||||||
friend class NetworkManager;
|
friend class NetworkManager;
|
||||||
@@ -30,7 +30,7 @@ public:
|
|||||||
/// Initializes a new instance of the <see cref="NetworkPeer"/> class.
|
/// Initializes a new instance of the <see cref="NetworkPeer"/> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
NetworkPeer()
|
NetworkPeer()
|
||||||
: PersistentScriptingObject(SpawnParams(Guid::New(), TypeInitializer))
|
: ScriptingObject(SpawnParams(Guid::New(), TypeInitializer))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Particle system parameter.
|
/// Particle system parameter.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
API_CLASS(NoSpawn) class ParticleEffectParameter : public PersistentScriptingObject
|
API_CLASS(NoSpawn) class ParticleEffectParameter : public ScriptingObject
|
||||||
{
|
{
|
||||||
DECLARE_SCRIPTING_TYPE_NO_SPAWN(ParticleEffectParameter);
|
DECLARE_SCRIPTING_TYPE_NO_SPAWN(ParticleEffectParameter);
|
||||||
friend ParticleEffect;
|
friend ParticleEffect;
|
||||||
@@ -30,7 +30,7 @@ public:
|
|||||||
/// Initializes a new instance of the <see cref="ParticleEffectParameter"/> class.
|
/// Initializes a new instance of the <see cref="ParticleEffectParameter"/> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
ParticleEffectParameter()
|
ParticleEffectParameter()
|
||||||
: PersistentScriptingObject(SpawnParams(Guid::New(), TypeInitializer))
|
: ScriptingObject(SpawnParams(Guid::New(), TypeInitializer))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -173,6 +173,7 @@ CharacterController::CollisionFlags CharacterController::Move(const Vector3& dis
|
|||||||
filters.mFilterData = (PxFilterData*)&_filterData;
|
filters.mFilterData = (PxFilterData*)&_filterData;
|
||||||
filters.mFilterCallback = Physics::GetCharacterQueryFilterCallback();
|
filters.mFilterCallback = Physics::GetCharacterQueryFilterCallback();
|
||||||
filters.mFilterFlags = PxQueryFlag::eDYNAMIC | PxQueryFlag::eSTATIC | PxQueryFlag::ePREFILTER;
|
filters.mFilterFlags = PxQueryFlag::eDYNAMIC | PxQueryFlag::eSTATIC | PxQueryFlag::ePREFILTER;
|
||||||
|
filters.mCCTFilterCallback = Physics::GetCharacterControllerFilterCallback();
|
||||||
|
|
||||||
result = (CollisionFlags)(byte)_controller->move(C2P(displacement), _minMoveDistance, deltaTime, filters);
|
result = (CollisionFlags)(byte)_controller->move(C2P(displacement), _minMoveDistance, deltaTime, filters);
|
||||||
_lastFlags = result;
|
_lastFlags = result;
|
||||||
|
|||||||
@@ -6,6 +6,8 @@
|
|||||||
#include "Actors/PhysicsColliderActor.h"
|
#include "Actors/PhysicsColliderActor.h"
|
||||||
#include <ThirdParty/PhysX/PxScene.h>
|
#include <ThirdParty/PhysX/PxScene.h>
|
||||||
#include <ThirdParty/PhysX/PxQueryFiltering.h>
|
#include <ThirdParty/PhysX/PxQueryFiltering.h>
|
||||||
|
#include <ThirdParty/PhysX/PxRigidDynamic.h>
|
||||||
|
#include <ThirdParty/PhysX/characterkinematic/PxController.h>
|
||||||
|
|
||||||
// Temporary result buffer size
|
// Temporary result buffer size
|
||||||
#define HIT_BUFFER_SIZE 128
|
#define HIT_BUFFER_SIZE 128
|
||||||
@@ -217,6 +219,52 @@ public:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class CharacterControllerFilter : public PxControllerFilterCallback
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
|
||||||
|
PxShape* getShape(const PxController& controller)
|
||||||
|
{
|
||||||
|
PxRigidDynamic* actor = controller.getActor();
|
||||||
|
|
||||||
|
// Early out if no actor or no shapes
|
||||||
|
if (!actor || actor->getNbShapes() < 1)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
// Get first shape only.
|
||||||
|
PxShape* shape = nullptr;
|
||||||
|
actor->getShapes(&shape, 1);
|
||||||
|
|
||||||
|
return shape;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
bool filter(const PxController& a, const PxController& b) override
|
||||||
|
{
|
||||||
|
// Early out to avoid crashing
|
||||||
|
PxShape* shapeA = getShape(a);
|
||||||
|
if (!shapeA)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
PxShape* shapeB = getShape(b);
|
||||||
|
if (!shapeB)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Let triggers through
|
||||||
|
if (PxFilterObjectIsTrigger(shapeB->getFlags()))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Trigger the contact callback for pairs (A,B) where the filtermask of A contains the ID of B and vice versa
|
||||||
|
const PxFilterData shapeFilterA = shapeA->getQueryFilterData();
|
||||||
|
const PxFilterData shapeFilterB = shapeB->getQueryFilterData();
|
||||||
|
if (shapeFilterA.word0 & shapeFilterB.word1)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
PxQueryFilterCallback* Physics::GetQueryFilterCallback()
|
PxQueryFilterCallback* Physics::GetQueryFilterCallback()
|
||||||
{
|
{
|
||||||
static QueryFilter Filter;
|
static QueryFilter Filter;
|
||||||
@@ -229,6 +277,12 @@ PxQueryFilterCallback* Physics::GetCharacterQueryFilterCallback()
|
|||||||
return &Filter;
|
return &Filter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PxControllerFilterCallback* Physics::GetCharacterControllerFilterCallback()
|
||||||
|
{
|
||||||
|
static CharacterControllerFilter Filter;
|
||||||
|
return &Filter;
|
||||||
|
}
|
||||||
|
|
||||||
bool Physics::RayCast(const Vector3& origin, const Vector3& direction, const float maxDistance, uint32 layerMask, bool hitTriggers)
|
bool Physics::RayCast(const Vector3& origin, const Vector3& direction, const float maxDistance, uint32 layerMask, bool hitTriggers)
|
||||||
{
|
{
|
||||||
// Prepare data
|
// Prepare data
|
||||||
|
|||||||
@@ -112,6 +112,11 @@ DECLARE_SCRIPTING_TYPE_NO_SPAWN(Physics);
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
static PxQueryFilterCallback* GetCharacterQueryFilterCallback();
|
static PxQueryFilterCallback* GetCharacterQueryFilterCallback();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the default controller filter callback used for the character controller collisions detection.
|
||||||
|
/// </summary>
|
||||||
|
static PxControllerFilterCallback* GetCharacterControllerFilterCallback();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the default physical material.
|
/// Gets the default physical material.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ namespace physx
|
|||||||
class PxController;
|
class PxController;
|
||||||
class PxCapsuleController;
|
class PxCapsuleController;
|
||||||
class PxQueryFilterCallback;
|
class PxQueryFilterCallback;
|
||||||
|
class PxControllerFilterCallback;
|
||||||
class PxHeightField;
|
class PxHeightField;
|
||||||
struct PxFilterData;
|
struct PxFilterData;
|
||||||
struct PxRaycastHit;
|
struct PxRaycastHit;
|
||||||
|
|||||||
@@ -112,7 +112,7 @@ UserBase::UserBase(const String& name)
|
|||||||
}
|
}
|
||||||
|
|
||||||
UserBase::UserBase(const SpawnParams& params, const String& name)
|
UserBase::UserBase(const SpawnParams& params, const String& name)
|
||||||
: PersistentScriptingObject(params)
|
: ScriptingObject(params)
|
||||||
, _name(name)
|
, _name(name)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ API_INJECT_CPP_CODE("#include \"Engine/Platform/User.h\"");
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Native platform user object.
|
/// Native platform user object.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
API_CLASS(NoSpawn, NoConstructor, Sealed, Name="User") class FLAXENGINE_API UserBase : public PersistentScriptingObject
|
API_CLASS(NoSpawn, NoConstructor, Sealed, Name="User") class FLAXENGINE_API UserBase : public ScriptingObject
|
||||||
{
|
{
|
||||||
DECLARE_SCRIPTING_TYPE_NO_SPAWN(UserBase);
|
DECLARE_SCRIPTING_TYPE_NO_SPAWN(UserBase);
|
||||||
protected:
|
protected:
|
||||||
|
|||||||
@@ -87,7 +87,7 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
WindowBase::WindowBase(const CreateWindowSettings& settings)
|
WindowBase::WindowBase(const CreateWindowSettings& settings)
|
||||||
: PersistentScriptingObject(SpawnParams(Guid::New(), TypeInitializer))
|
: ScriptingObject(SpawnParams(Guid::New(), TypeInitializer))
|
||||||
, _visible(false)
|
, _visible(false)
|
||||||
, _minimized(false)
|
, _minimized(false)
|
||||||
, _maximized(false)
|
, _maximized(false)
|
||||||
@@ -190,7 +190,7 @@ void WindowBase::OnDeleteObject()
|
|||||||
SAFE_DELETE(_swapChain);
|
SAFE_DELETE(_swapChain);
|
||||||
|
|
||||||
// Base
|
// Base
|
||||||
PersistentScriptingObject::OnDeleteObject();
|
ScriptingObject::OnDeleteObject();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool WindowBase::GetRenderingEnabled() const
|
bool WindowBase::GetRenderingEnabled() const
|
||||||
|
|||||||
@@ -269,7 +269,7 @@ API_INJECT_CPP_CODE("#include \"Engine/Platform/Window.h\"");
|
|||||||
/// Native platform window object.
|
/// Native platform window object.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
API_CLASS(NoSpawn, NoConstructor, Sealed, Name="Window")
|
API_CLASS(NoSpawn, NoConstructor, Sealed, Name="Window")
|
||||||
class FLAXENGINE_API WindowBase : public PersistentScriptingObject
|
class FLAXENGINE_API WindowBase : public ScriptingObject
|
||||||
{
|
{
|
||||||
DECLARE_SCRIPTING_TYPE_NO_SPAWN(WindowBase);
|
DECLARE_SCRIPTING_TYPE_NO_SPAWN(WindowBase);
|
||||||
friend GPUSwapChain;
|
friend GPUSwapChain;
|
||||||
@@ -949,7 +949,7 @@ private:
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
// [PersistentScriptingObject]
|
// [ScriptingObject]
|
||||||
String ToString() const override;
|
String ToString() const override;
|
||||||
void OnDeleteObject() override;
|
void OnDeleteObject() override;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -369,7 +369,7 @@ bool DrawCallsList::IsEmpty() const
|
|||||||
}
|
}
|
||||||
|
|
||||||
RenderList::RenderList(const SpawnParams& params)
|
RenderList::RenderList(const SpawnParams& params)
|
||||||
: PersistentScriptingObject(params)
|
: ScriptingObject(params)
|
||||||
, DirectionalLights(4)
|
, DirectionalLights(4)
|
||||||
, PointLights(32)
|
, PointLights(32)
|
||||||
, SpotLights(32)
|
, SpotLights(32)
|
||||||
|
|||||||
@@ -322,7 +322,7 @@ struct DrawCallsList
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Rendering cache container object for the draw calls collecting, sorting and executing.
|
/// Rendering cache container object for the draw calls collecting, sorting and executing.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
API_CLASS(Sealed) class FLAXENGINE_API RenderList : public PersistentScriptingObject
|
API_CLASS(Sealed) class FLAXENGINE_API RenderList : public ScriptingObject
|
||||||
{
|
{
|
||||||
DECLARE_SCRIPTING_TYPE(RenderList);
|
DECLARE_SCRIPTING_TYPE(RenderList);
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ using System;
|
|||||||
namespace FlaxEngine
|
namespace FlaxEngine
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Specifies a options for an type reference picker in the editor. Allows to customize view or provide custom value assign policy (eg/ restrict types to inherit from a given type).
|
/// Specifies a options for an type reference picker in the editor. Allows to customize view or provide custom value assign policy (eg. restrict types to inherit from a given type).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <seealso cref="System.Attribute" />
|
/// <seealso cref="System.Attribute" />
|
||||||
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Parameter)]
|
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Parameter)]
|
||||||
|
|||||||
@@ -197,7 +197,7 @@ struct MConverter<T*, typename TEnableIf<TIsBaseOf<class ScriptingObject, T>::Va
|
|||||||
|
|
||||||
// Converter for Scripting Objects (collection of values).
|
// Converter for Scripting Objects (collection of values).
|
||||||
template<typename T>
|
template<typename T>
|
||||||
struct MConverter<T, typename TEnableIf<TIsBaseOf<class PersistentScriptingObject, T>::Value>::Type>
|
struct MConverter<T, typename TEnableIf<TIsBaseOf<class ScriptingObject, T>::Value>::Type>
|
||||||
{
|
{
|
||||||
MonoObject* Box(const T& data, MonoClass* klass)
|
MonoObject* Box(const T& data, MonoClass* klass)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -134,7 +134,7 @@ private:
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
// [PersistentScriptingObject]
|
// [ScriptingObject]
|
||||||
String ToString() const override;
|
String ToString() const override;
|
||||||
void OnDeleteObject() override;
|
void OnDeleteObject() override;
|
||||||
|
|
||||||
|
|||||||
@@ -38,20 +38,32 @@ ScriptingObject::ScriptingObject(const SpawnParams& params)
|
|||||||
|
|
||||||
ScriptingObject::~ScriptingObject()
|
ScriptingObject::~ScriptingObject()
|
||||||
{
|
{
|
||||||
// Ensure that GC handle is empty
|
|
||||||
ASSERT(_gcHandle == 0);
|
|
||||||
|
|
||||||
// Ensure that object has been already unregistered
|
|
||||||
if (IsRegistered())
|
|
||||||
UnregisterObject();
|
|
||||||
|
|
||||||
Deleted(this);
|
Deleted(this);
|
||||||
|
|
||||||
|
// Get rid of managed object
|
||||||
|
ScriptingObject::DestroyManaged();
|
||||||
|
ASSERT(_gcHandle == 0);
|
||||||
|
|
||||||
// Handle custom scripting objects removing
|
// Handle custom scripting objects removing
|
||||||
if (Flags & ObjectFlags::IsCustomScriptingType)
|
if (Flags & ObjectFlags::IsCustomScriptingType)
|
||||||
{
|
{
|
||||||
_type.Module->OnObjectDeleted(this);
|
_type.Module->OnObjectDeleted(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ensure that object has been already unregistered
|
||||||
|
if (IsRegistered())
|
||||||
|
UnregisterObject();
|
||||||
|
}
|
||||||
|
|
||||||
|
ScriptingObject* ScriptingObject::NewObject(const ScriptingTypeHandle& typeHandle)
|
||||||
|
{
|
||||||
|
if (!typeHandle)
|
||||||
|
return nullptr;
|
||||||
|
auto& type = typeHandle.GetType();
|
||||||
|
if (type.Type != ScriptingTypes::Script)
|
||||||
|
return nullptr;
|
||||||
|
const ScriptingObjectSpawnParams params(Guid::New(), typeHandle);
|
||||||
|
return type.Script.Spawn(params);
|
||||||
}
|
}
|
||||||
|
|
||||||
MObject* ScriptingObject::GetManagedInstance() const
|
MObject* ScriptingObject::GetManagedInstance() const
|
||||||
@@ -207,18 +219,49 @@ void ScriptingObject::OnManagedInstanceDeleted()
|
|||||||
// Unregister object
|
// Unregister object
|
||||||
if (IsRegistered())
|
if (IsRegistered())
|
||||||
UnregisterObject();
|
UnregisterObject();
|
||||||
|
|
||||||
// Self destruct
|
|
||||||
DeleteObject();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ScriptingObject::OnScriptingDispose()
|
void ScriptingObject::OnScriptingDispose()
|
||||||
{
|
{
|
||||||
// Delete C# object
|
// Delete C# object
|
||||||
|
if (IsRegistered())
|
||||||
|
UnregisterObject();
|
||||||
DestroyManaged();
|
DestroyManaged();
|
||||||
|
}
|
||||||
|
|
||||||
// Delete C++ object
|
bool ScriptingObject::CreateManaged()
|
||||||
DeleteObject();
|
{
|
||||||
|
#if USE_MONO
|
||||||
|
MonoObject* managedInstance = CreateManagedInternal();
|
||||||
|
if (!managedInstance)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// Prevent form object GC destruction
|
||||||
|
auto handle = mono_gchandle_new(managedInstance, false);
|
||||||
|
auto oldHandle = Platform::InterlockedCompareExchange((int32*)&_gcHandle, *(int32*)&handle, 0);
|
||||||
|
if (*(uint32*)&oldHandle != 0)
|
||||||
|
{
|
||||||
|
// Other thread already created the object before
|
||||||
|
if (const auto monoClass = GetClass())
|
||||||
|
{
|
||||||
|
// Reset managed to unmanaged pointer
|
||||||
|
const MField* monoUnmanagedPtrField = monoClass->GetField(ScriptingObject_unmanagedPtr);
|
||||||
|
if (monoUnmanagedPtrField)
|
||||||
|
{
|
||||||
|
void* param = nullptr;
|
||||||
|
monoUnmanagedPtrField->SetValue(managedInstance, ¶m);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mono_gchandle_free(handle);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Ensure to be registered
|
||||||
|
if (!IsRegistered())
|
||||||
|
RegisterObject();
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if USE_MONO
|
#if USE_MONO
|
||||||
@@ -233,8 +276,16 @@ MonoObject* ScriptingObject::CreateManagedInternal()
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ensure to have managed domain attached (this can be called from custom native thread, eg. content loader)
|
||||||
|
auto domain = mono_domain_get();
|
||||||
|
if (!domain)
|
||||||
|
{
|
||||||
|
MCore::AttachThread();
|
||||||
|
domain = mono_domain_get();
|
||||||
|
}
|
||||||
|
|
||||||
// Allocate managed instance
|
// Allocate managed instance
|
||||||
MonoObject* managedInstance = mono_object_new(mono_domain_get(), monoClass->GetNative());
|
MonoObject* managedInstance = mono_object_new(domain, monoClass->GetNative());
|
||||||
if (managedInstance == nullptr)
|
if (managedInstance == nullptr)
|
||||||
{
|
{
|
||||||
LOG(Warning, "Failed to create new instance of the object of type {0}", String(monoClass->GetFullName()));
|
LOG(Warning, "Failed to create new instance of the object of type {0}", String(monoClass->GetFullName()));
|
||||||
@@ -358,7 +409,7 @@ void ScriptingObject::OnDeleteObject()
|
|||||||
UnregisterObject();
|
UnregisterObject();
|
||||||
|
|
||||||
// Base
|
// Base
|
||||||
RemovableObject::OnDeleteObject();
|
Object::OnDeleteObject();
|
||||||
}
|
}
|
||||||
|
|
||||||
String ScriptingObject::ToString() const
|
String ScriptingObject::ToString() const
|
||||||
@@ -371,6 +422,24 @@ ManagedScriptingObject::ManagedScriptingObject(const SpawnParams& params)
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ManagedScriptingObject::OnManagedInstanceDeleted()
|
||||||
|
{
|
||||||
|
// Base
|
||||||
|
ScriptingObject::OnManagedInstanceDeleted();
|
||||||
|
|
||||||
|
// Self destruct
|
||||||
|
DeleteObject();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ManagedScriptingObject::OnScriptingDispose()
|
||||||
|
{
|
||||||
|
// Base
|
||||||
|
ScriptingObject::OnScriptingDispose();
|
||||||
|
|
||||||
|
// Self destruct
|
||||||
|
DeleteObject();
|
||||||
|
}
|
||||||
|
|
||||||
bool ManagedScriptingObject::CreateManaged()
|
bool ManagedScriptingObject::CreateManaged()
|
||||||
{
|
{
|
||||||
#if USE_MONO
|
#if USE_MONO
|
||||||
@@ -411,70 +480,6 @@ PersistentScriptingObject::PersistentScriptingObject(const SpawnParams& params)
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
PersistentScriptingObject::~PersistentScriptingObject()
|
|
||||||
{
|
|
||||||
PersistentScriptingObject::DestroyManaged();
|
|
||||||
}
|
|
||||||
|
|
||||||
void PersistentScriptingObject::OnManagedInstanceDeleted()
|
|
||||||
{
|
|
||||||
// Cleanup
|
|
||||||
if (_gcHandle)
|
|
||||||
{
|
|
||||||
#if USE_MONO
|
|
||||||
mono_gchandle_free(_gcHandle);
|
|
||||||
#endif
|
|
||||||
_gcHandle = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// But do not delete itself
|
|
||||||
}
|
|
||||||
|
|
||||||
void PersistentScriptingObject::OnScriptingDispose()
|
|
||||||
{
|
|
||||||
// Delete C# object
|
|
||||||
if (IsRegistered())
|
|
||||||
UnregisterObject();
|
|
||||||
DestroyManaged();
|
|
||||||
|
|
||||||
// Don't delete C++ object
|
|
||||||
}
|
|
||||||
|
|
||||||
bool PersistentScriptingObject::CreateManaged()
|
|
||||||
{
|
|
||||||
#if USE_MONO
|
|
||||||
MonoObject* managedInstance = CreateManagedInternal();
|
|
||||||
if (!managedInstance)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
// Prevent form object GC destruction
|
|
||||||
auto handle = mono_gchandle_new(managedInstance, false);
|
|
||||||
auto oldHandle = Platform::InterlockedCompareExchange((int32*)&_gcHandle, *(int32*)&handle, 0);
|
|
||||||
if (*(uint32*)&oldHandle != 0)
|
|
||||||
{
|
|
||||||
// Other thread already created the object before
|
|
||||||
if (const auto monoClass = GetClass())
|
|
||||||
{
|
|
||||||
// Reset managed to unmanaged pointer
|
|
||||||
const MField* monoUnmanagedPtrField = monoClass->GetField(ScriptingObject_unmanagedPtr);
|
|
||||||
if (monoUnmanagedPtrField)
|
|
||||||
{
|
|
||||||
void* param = nullptr;
|
|
||||||
monoUnmanagedPtrField->SetValue(managedInstance, ¶m);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
mono_gchandle_free(handle);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Ensure to be registered
|
|
||||||
if (!IsRegistered())
|
|
||||||
RegisterObject();
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
class ScriptingObjectInternal
|
class ScriptingObjectInternal
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -746,7 +751,7 @@ public:
|
|||||||
|
|
||||||
static ScriptingObject* Spawn(const ScriptingObjectSpawnParams& params)
|
static ScriptingObject* Spawn(const ScriptingObjectSpawnParams& params)
|
||||||
{
|
{
|
||||||
return New<PersistentScriptingObject>(params);
|
return New<ScriptingObject>(params);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -8,9 +8,9 @@
|
|||||||
#include "ManagedCLR/MTypes.h"
|
#include "ManagedCLR/MTypes.h"
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents object from unmanaged memory that can use accessed via C# scripting.
|
/// Represents object from unmanaged memory that can use accessed via scripting.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
API_CLASS(InBuild) class FLAXENGINE_API ScriptingObject : public RemovableObject
|
API_CLASS(InBuild) class FLAXENGINE_API ScriptingObject : public Object
|
||||||
{
|
{
|
||||||
friend class Scripting;
|
friend class Scripting;
|
||||||
friend class BinaryModule;
|
friend class BinaryModule;
|
||||||
@@ -40,14 +40,23 @@ public:
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
/// <summary>
|
// Spawns a new objects of the given type.
|
||||||
/// Spawns a new objects of the given type.
|
static ScriptingObject* NewObject(const ScriptingTypeHandle& typeHandle);
|
||||||
/// </summary>
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
static T* Spawn()
|
static T* NewObject()
|
||||||
{
|
{
|
||||||
const SpawnParams params(Guid::New(), T::TypeInitializer);
|
return (T*)NewObject(T::TypeInitializer);
|
||||||
return T::New(params);
|
}
|
||||||
|
template<typename T>
|
||||||
|
static T* NewObject(const ScriptingTypeHandle& typeHandle)
|
||||||
|
{
|
||||||
|
auto obj = NewObject(typeHandle);
|
||||||
|
if (obj && !obj->Is<T>())
|
||||||
|
{
|
||||||
|
Delete(obj);
|
||||||
|
obj = nullptr;
|
||||||
|
}
|
||||||
|
return (T*)obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@@ -182,7 +191,7 @@ public:
|
|||||||
virtual void OnManagedInstanceDeleted();
|
virtual void OnManagedInstanceDeleted();
|
||||||
virtual void OnScriptingDispose();
|
virtual void OnScriptingDispose();
|
||||||
|
|
||||||
virtual bool CreateManaged() = 0;
|
virtual bool CreateManaged();
|
||||||
virtual void DestroyManaged();
|
virtual void DestroyManaged();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@@ -216,7 +225,7 @@ protected:
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
// [RemovableObject]
|
// [Object]
|
||||||
void OnDeleteObject() override;
|
void OnDeleteObject() override;
|
||||||
String ToString() const override;
|
String ToString() const override;
|
||||||
};
|
};
|
||||||
@@ -239,32 +248,17 @@ public:
|
|||||||
public:
|
public:
|
||||||
|
|
||||||
// [ScriptingObject]
|
// [ScriptingObject]
|
||||||
bool CreateManaged() override;
|
|
||||||
};
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Managed object that uses pinned GC handle to prevent collecting and moving.
|
|
||||||
/// Used by the objects that lifetime is controlled by the C++ side.
|
|
||||||
/// </summary>
|
|
||||||
API_CLASS(InBuild) class FLAXENGINE_API PersistentScriptingObject : public ScriptingObject
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new instance of the <see cref="PersistentScriptingObject"/> class.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="params">The object initialization parameters.</param>
|
|
||||||
explicit PersistentScriptingObject(const SpawnParams& params);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Finalizes an instance of the <see cref="PersistentScriptingObject"/> class.
|
|
||||||
/// </summary>
|
|
||||||
~PersistentScriptingObject();
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
// [ManagedScriptingObject]
|
|
||||||
void OnManagedInstanceDeleted() override;
|
void OnManagedInstanceDeleted() override;
|
||||||
void OnScriptingDispose() override;
|
void OnScriptingDispose() override;
|
||||||
bool CreateManaged() override;
|
bool CreateManaged() override;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Use ScriptingObject instead.
|
||||||
|
/// [Deprecated on 5.01.2022, expires on 5.01.2024]
|
||||||
|
/// </summary>
|
||||||
|
API_CLASS(InBuild) class FLAXENGINE_API PersistentScriptingObject : public ScriptingObject
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
PersistentScriptingObject(const SpawnParams& params);
|
||||||
|
};
|
||||||
|
|||||||
@@ -15,10 +15,7 @@ using Newtonsoft.Json.Serialization;
|
|||||||
|
|
||||||
namespace FlaxEngine.Json
|
namespace FlaxEngine.Json
|
||||||
{
|
{
|
||||||
/// <summary>
|
partial class JsonSerializer
|
||||||
/// Objects serialization tool (json format).
|
|
||||||
/// </summary>
|
|
||||||
public static class JsonSerializer
|
|
||||||
{
|
{
|
||||||
internal class SerializerCache
|
internal class SerializerCache
|
||||||
{
|
{
|
||||||
|
|||||||
41
Source/Engine/Serialization/JsonSerializer.h
Normal file
41
Source/Engine/Serialization/JsonSerializer.h
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Engine/Core/ISerializable.h"
|
||||||
|
#include "Engine/Core/Types/Span.h"
|
||||||
|
#include "Engine/Core/Collections/Array.h"
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Objects serialization tool (json format).
|
||||||
|
/// </summary>
|
||||||
|
API_CLASS(Static, Namespace="FlaxEngine.Json") class FLAXENGINE_API JsonSerializer
|
||||||
|
{
|
||||||
|
DECLARE_SCRIPTING_TYPE_MINIMAL(JsonSerializer);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Performs object Json serialization to the raw bytes.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="obj">The object to serialize (can be null).</param>
|
||||||
|
/// <returns>The output data.</returns>
|
||||||
|
API_FUNCTION() static Array<byte> SaveToBytes(ISerializable* obj);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Performs object Json deserialization from the raw bytes.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="obj">The object to deserialize (can be null).</param>
|
||||||
|
/// <param name="data">The source data to read from.</param>
|
||||||
|
/// <param name="engineBuild">The engine build number of the saved data. Used to resolve old object formats when loading deprecated data.</param>
|
||||||
|
FORCE_INLINE static void LoadFromBytes(ISerializable* obj, const Array<byte>& data, int32 engineBuild)
|
||||||
|
{
|
||||||
|
LoadFromBytes(obj, Span<byte>(data.Get(), data.Count()), engineBuild);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Performs object Json deserialization from the raw bytes.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="obj">The object to deserialize (can be null).</param>
|
||||||
|
/// <param name="data">The source data to read from.</param>
|
||||||
|
/// <param name="engineBuild">The engine build number of the saved data. Used to resolve old object formats when loading deprecated data.</param>
|
||||||
|
API_FUNCTION() static void LoadFromBytes(ISerializable* obj, const Span<byte>& data, int32 engineBuild);
|
||||||
|
};
|
||||||
@@ -3,6 +3,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "ReadStream.h"
|
#include "ReadStream.h"
|
||||||
|
#include "Engine/Platform/Platform.h"
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Super fast advanced data reading from raw bytes without any overhead at all
|
/// Super fast advanced data reading from raw bytes without any overhead at all
|
||||||
@@ -33,8 +34,8 @@ public:
|
|||||||
/// Init
|
/// Init
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="data">Array with data to read from</param>
|
/// <param name="data">Array with data to read from</param>
|
||||||
template<typename T>
|
template<typename T, typename AllocationType = HeapAllocation>
|
||||||
MemoryReadStream(const Array<T>& data)
|
MemoryReadStream(const Array<T, AllocationType>& data)
|
||||||
: MemoryReadStream(data.Get(), data.Count() * sizeof(T))
|
: MemoryReadStream(data.Get(), data.Count() * sizeof(T))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@@ -52,8 +53,8 @@ public:
|
|||||||
/// Init stream to the custom buffer location
|
/// Init stream to the custom buffer location
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="data">Array with data to read from</param>
|
/// <param name="data">Array with data to read from</param>
|
||||||
template<typename T>
|
template<typename T, typename AllocationType = HeapAllocation>
|
||||||
FORCE_INLINE void Init(const Array<T>& data)
|
FORCE_INLINE void Init(const Array<T, AllocationType>& data)
|
||||||
{
|
{
|
||||||
Init(data.Get(), data.Count() * sizeof(T));
|
Init(data.Get(), data.Count() * sizeof(T));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,27 +2,19 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "Engine/Core/Collections/Array.h"
|
|
||||||
#include "Stream.h"
|
#include "Stream.h"
|
||||||
|
#include "Engine/Core/Templates.h"
|
||||||
|
|
||||||
struct CommonValue;
|
struct CommonValue;
|
||||||
struct Variant;
|
struct Variant;
|
||||||
struct VariantType;
|
struct VariantType;
|
||||||
|
class ISerializable;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Base class for all data read streams
|
/// Base class for all data read streams
|
||||||
/// </summary>
|
/// </summary>
|
||||||
class FLAXENGINE_API ReadStream : public Stream
|
class FLAXENGINE_API ReadStream : public Stream
|
||||||
{
|
{
|
||||||
public:
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Virtual destructor
|
|
||||||
/// </summary>
|
|
||||||
virtual ~ReadStream()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -32,8 +24,6 @@ public:
|
|||||||
/// <param name="bytes">Amount of bytes to read</param>
|
/// <param name="bytes">Amount of bytes to read</param>
|
||||||
virtual void ReadBytes(void* data, uint32 bytes) = 0;
|
virtual void ReadBytes(void* data, uint32 bytes) = 0;
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
FORCE_INLINE void Read(T* data)
|
FORCE_INLINE void Read(T* data)
|
||||||
{
|
{
|
||||||
@@ -193,9 +183,10 @@ public:
|
|||||||
/// Read data array
|
/// Read data array
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="data">Array to read</param>
|
/// <param name="data">Array to read</param>
|
||||||
template<typename T>
|
template<typename T, typename AllocationType = HeapAllocation>
|
||||||
void ReadArray(Array<T>* data)
|
void ReadArray(Array<T, AllocationType>* data)
|
||||||
{
|
{
|
||||||
|
static_assert(TIsPODType<T>::Value, "Only POD types are valid for ReadArray.");
|
||||||
int32 size;
|
int32 size;
|
||||||
ReadInt32(&size);
|
ReadInt32(&size);
|
||||||
data->Resize(size, false);
|
data->Resize(size, false);
|
||||||
@@ -203,6 +194,13 @@ public:
|
|||||||
ReadBytes(data->Get(), size * sizeof(T));
|
ReadBytes(data->Get(), size * sizeof(T));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Deserializes object from Json by reading it as a raw data (ver+length+bytes).
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>Reads version number, data length and actual data bytes from the stream.</remarks>
|
||||||
|
/// <param name="obj">The object to deserialize.</param>
|
||||||
|
void ReadJson(ISerializable* obj);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
// [Stream]
|
// [Stream]
|
||||||
|
|||||||
@@ -3,11 +3,16 @@
|
|||||||
#include "ReadStream.h"
|
#include "ReadStream.h"
|
||||||
#include "WriteStream.h"
|
#include "WriteStream.h"
|
||||||
#include "JsonWriters.h"
|
#include "JsonWriters.h"
|
||||||
|
#include "JsonSerializer.h"
|
||||||
|
#include "MemoryReadStream.h"
|
||||||
#include "Engine/Core/Types/CommonValue.h"
|
#include "Engine/Core/Types/CommonValue.h"
|
||||||
#include "Engine/Core/Types/Variant.h"
|
#include "Engine/Core/Types/Variant.h"
|
||||||
#include "Engine/Core/Collections/Dictionary.h"
|
#include "Engine/Core/Collections/Dictionary.h"
|
||||||
#include "Engine/Content/Asset.h"
|
#include "Engine/Content/Asset.h"
|
||||||
|
#include "Engine/Core/Cache.h"
|
||||||
#include "Engine/Debug/DebugLog.h"
|
#include "Engine/Debug/DebugLog.h"
|
||||||
|
#include "Engine/Debug/Exceptions/JsonParseException.h"
|
||||||
|
#include "Engine/Profiler/ProfilerCPU.h"
|
||||||
#include "Engine/Scripting/ManagedSerialization.h"
|
#include "Engine/Scripting/ManagedSerialization.h"
|
||||||
#include "Engine/Scripting/Scripting.h"
|
#include "Engine/Scripting/Scripting.h"
|
||||||
#include "Engine/Scripting/ScriptingObject.h"
|
#include "Engine/Scripting/ScriptingObject.h"
|
||||||
@@ -489,12 +494,41 @@ void ReadStream::ReadVariant(Variant* data)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ReadStream::ReadJson(ISerializable* obj)
|
||||||
|
{
|
||||||
|
int32 engineBuild, size;
|
||||||
|
ReadInt32(&engineBuild);
|
||||||
|
ReadInt32(&size);
|
||||||
|
if (obj)
|
||||||
|
{
|
||||||
|
if (const auto memoryStream = dynamic_cast<MemoryReadStream*>(this))
|
||||||
|
{
|
||||||
|
JsonSerializer::LoadFromBytes(obj, Span<byte>((byte*)memoryStream->Read(size), size), engineBuild);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
void* data = Allocator::Allocate(size);
|
||||||
|
ReadBytes(data, size);
|
||||||
|
JsonSerializer::LoadFromBytes(obj, Span<byte>((byte*)data, size), engineBuild);
|
||||||
|
Allocator::Free(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
SetPosition(GetPosition() + size);
|
||||||
|
}
|
||||||
|
|
||||||
void WriteStream::WriteText(const StringView& text)
|
void WriteStream::WriteText(const StringView& text)
|
||||||
{
|
{
|
||||||
for (int32 i = 0; i < text.Length(); i++)
|
for (int32 i = 0; i < text.Length(); i++)
|
||||||
WriteChar(text[i]);
|
WriteChar(text[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void WriteStream::WriteText(const StringAnsiView& text)
|
||||||
|
{
|
||||||
|
for (int32 i = 0; i < text.Length(); i++)
|
||||||
|
WriteChar(text[i]);
|
||||||
|
}
|
||||||
|
|
||||||
void WriteStream::WriteString(const StringView& data)
|
void WriteStream::WriteString(const StringView& data)
|
||||||
{
|
{
|
||||||
const int32 length = data.Length();
|
const int32 length = data.Length();
|
||||||
@@ -519,7 +553,7 @@ void WriteStream::WriteStringAnsi(const StringAnsiView& data)
|
|||||||
Write(data.Get(), length);
|
Write(data.Get(), length);
|
||||||
}
|
}
|
||||||
|
|
||||||
void WriteStream::WriteStringAnsi(const StringAnsiView& data, int16 lock)
|
void WriteStream::WriteStringAnsi(const StringAnsiView& data, int8 lock)
|
||||||
{
|
{
|
||||||
const int32 length = data.Length();
|
const int32 length = data.Length();
|
||||||
ASSERT(length < STREAM_MAX_STRING_LENGTH);
|
ASSERT(length < STREAM_MAX_STRING_LENGTH);
|
||||||
@@ -734,3 +768,57 @@ void WriteStream::WriteVariant(const Variant& data)
|
|||||||
CRASH;
|
CRASH;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void WriteStream::WriteJson(ISerializable* obj, const void* otherObj)
|
||||||
|
{
|
||||||
|
WriteInt32(FLAXENGINE_VERSION_BUILD);
|
||||||
|
if (obj)
|
||||||
|
{
|
||||||
|
rapidjson_flax::StringBuffer buffer;
|
||||||
|
CompactJsonWriter writer(buffer);
|
||||||
|
writer.StartObject();
|
||||||
|
obj->Serialize(writer, otherObj);
|
||||||
|
writer.EndObject();
|
||||||
|
|
||||||
|
WriteInt32((int32)buffer.GetSize());
|
||||||
|
WriteBytes((byte*)buffer.GetString(), (int32)buffer.GetSize());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
WriteInt32(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
Array<byte> JsonSerializer::SaveToBytes(ISerializable* obj)
|
||||||
|
{
|
||||||
|
Array<byte> result;
|
||||||
|
if (obj)
|
||||||
|
{
|
||||||
|
rapidjson_flax::StringBuffer buffer;
|
||||||
|
CompactJsonWriter writer(buffer);
|
||||||
|
writer.StartObject();
|
||||||
|
obj->Serialize(writer, nullptr);
|
||||||
|
writer.EndObject();
|
||||||
|
result.Set((byte*)buffer.GetString(), (int32)buffer.GetSize());
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void JsonSerializer::LoadFromBytes(ISerializable* obj, const Span<byte>& data, int32 engineBuild)
|
||||||
|
{
|
||||||
|
if (!obj || data.Length() == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
ISerializable::SerializeDocument document;
|
||||||
|
{
|
||||||
|
PROFILE_CPU_NAMED("Json.Parse");
|
||||||
|
document.Parse((const char*)data.Get(), data.Length());
|
||||||
|
}
|
||||||
|
if (document.HasParseError())
|
||||||
|
{
|
||||||
|
Log::JsonParseException(document.GetParseError(), document.GetErrorOffset());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto modifier = Cache::ISerializeModifier.Get();
|
||||||
|
modifier->EngineBuild = engineBuild;
|
||||||
|
obj->Deserialize(document, modifier.Value);
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "Engine/Core/Core.h"
|
||||||
#include "Engine/Core/Types/BaseTypes.h"
|
#include "Engine/Core/Types/BaseTypes.h"
|
||||||
|
|
||||||
#define FILESTREAM_BUFFER_SIZE 4096
|
#define FILESTREAM_BUFFER_SIZE 4096
|
||||||
|
|||||||
@@ -2,28 +2,19 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "Engine/Core/Collections/Array.h"
|
|
||||||
#include "Engine/Core/Formatting.h"
|
|
||||||
#include "Stream.h"
|
#include "Stream.h"
|
||||||
|
#include "Engine/Core/Templates.h"
|
||||||
|
|
||||||
struct CommonValue;
|
struct CommonValue;
|
||||||
struct Variant;
|
struct Variant;
|
||||||
struct VariantType;
|
struct VariantType;
|
||||||
|
class ISerializable;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Base class for all data write streams
|
/// Base class for all data write streams
|
||||||
/// </summary>
|
/// </summary>
|
||||||
class FLAXENGINE_API WriteStream : public Stream
|
class FLAXENGINE_API WriteStream : public Stream
|
||||||
{
|
{
|
||||||
public:
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Virtual destructor
|
|
||||||
/// </summary>
|
|
||||||
virtual ~WriteStream()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -33,8 +24,6 @@ public:
|
|||||||
/// <param name="bytes">Amount of bytes to write</param>
|
/// <param name="bytes">Amount of bytes to write</param>
|
||||||
virtual void WriteBytes(const void* data, uint32 bytes) = 0;
|
virtual void WriteBytes(const void* data, uint32 bytes) = 0;
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
FORCE_INLINE void Write(const T* data)
|
FORCE_INLINE void Write(const T* data)
|
||||||
{
|
{
|
||||||
@@ -165,24 +154,6 @@ public:
|
|||||||
WriteBytes((const void*)text, sizeof(Char) * length);
|
WriteBytes((const void*)text, sizeof(Char) * length);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename... Args>
|
|
||||||
void WriteTextFormatted(const char* format, const Args& ... args)
|
|
||||||
{
|
|
||||||
fmt_flax::allocator_ansi allocator;
|
|
||||||
fmt_flax::memory_buffer_ansi buffer(allocator);
|
|
||||||
fmt_flax::format(buffer, format, args...);
|
|
||||||
WriteText(buffer.data(), (int32)buffer.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename... Args>
|
|
||||||
void WriteTextFormatted(const Char* format, const Args& ... args)
|
|
||||||
{
|
|
||||||
fmt_flax::allocator allocator;
|
|
||||||
fmt_flax::memory_buffer buffer(allocator);
|
|
||||||
fmt_flax::format(buffer, format, args...);
|
|
||||||
WriteText(buffer.data(), (int32)buffer.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write UTF BOM character sequence
|
// Write UTF BOM character sequence
|
||||||
void WriteBOM()
|
void WriteBOM()
|
||||||
{
|
{
|
||||||
@@ -194,6 +165,7 @@ public:
|
|||||||
// Writes text to the stream
|
// Writes text to the stream
|
||||||
// @param data Text to write
|
// @param data Text to write
|
||||||
void WriteText(const StringView& text);
|
void WriteText(const StringView& text);
|
||||||
|
void WriteText(const StringAnsiView& text);
|
||||||
|
|
||||||
// Writes String to the stream
|
// Writes String to the stream
|
||||||
// @param data Data to write
|
// @param data Data to write
|
||||||
@@ -211,7 +183,7 @@ public:
|
|||||||
// Writes Ansi String to the stream
|
// Writes Ansi String to the stream
|
||||||
// @param data Data to write
|
// @param data Data to write
|
||||||
// @param lock Characters pass in the stream
|
// @param lock Characters pass in the stream
|
||||||
void WriteStringAnsi(const StringAnsiView& data, int16 lock);
|
void WriteStringAnsi(const StringAnsiView& data, int8 lock);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
@@ -231,8 +203,8 @@ public:
|
|||||||
/// Write data array
|
/// Write data array
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="data">Array to write</param>
|
/// <param name="data">Array to write</param>
|
||||||
template<typename T>
|
template<typename T, typename AllocationType = HeapAllocation>
|
||||||
void WriteArray(const Array<T>& data)
|
void WriteArray(const Array<T, AllocationType>& data)
|
||||||
{
|
{
|
||||||
static_assert(TIsPODType<T>::Value, "Only POD types are valid for WriteArray.");
|
static_assert(TIsPODType<T>::Value, "Only POD types are valid for WriteArray.");
|
||||||
const int32 size = data.Count();
|
const int32 size = data.Count();
|
||||||
@@ -241,6 +213,14 @@ public:
|
|||||||
WriteBytes(data.Get(), size * sizeof(T));
|
WriteBytes(data.Get(), size * sizeof(T));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Serializes object to Json and writes it as a raw data (ver+length+bytes).
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>Writes version number, data length and actual data bytes to the stream.</remarks>
|
||||||
|
/// <param name="obj">The object to serialize.</param>
|
||||||
|
/// <param name="otherObj">The instance of the object to compare with and serialize only the modified properties. If null, then serialize all properties.</param>
|
||||||
|
void WriteJson(ISerializable* obj, const void* otherObj = nullptr);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
// [Stream]
|
// [Stream]
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ DECLARE_ENUM_EX_6(TaskState, int64, 0, Created, Failed, Canceled, Queued, Runnin
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents an asynchronous operation.
|
/// Represents an asynchronous operation.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
class FLAXENGINE_API Task : public RemovableObject, public NonCopyable
|
class FLAXENGINE_API Task : public Object, public NonCopyable
|
||||||
{
|
{
|
||||||
//
|
//
|
||||||
// Tasks execution and states flow:
|
// Tasks execution and states flow:
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ namespace
|
|||||||
}
|
}
|
||||||
|
|
||||||
TaskGraphSystem::TaskGraphSystem(const SpawnParams& params)
|
TaskGraphSystem::TaskGraphSystem(const SpawnParams& params)
|
||||||
: PersistentScriptingObject(params)
|
: ScriptingObject(params)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -36,7 +36,7 @@ void TaskGraphSystem::PostExecute(TaskGraph* graph)
|
|||||||
}
|
}
|
||||||
|
|
||||||
TaskGraph::TaskGraph(const SpawnParams& params)
|
TaskGraph::TaskGraph(const SpawnParams& params)
|
||||||
: PersistentScriptingObject(params)
|
: ScriptingObject(params)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ class TaskGraph;
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// System that can generate work into Task Graph for asynchronous execution.
|
/// System that can generate work into Task Graph for asynchronous execution.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
API_CLASS(Abstract) class FLAXENGINE_API TaskGraphSystem : public PersistentScriptingObject
|
API_CLASS(Abstract) class FLAXENGINE_API TaskGraphSystem : public ScriptingObject
|
||||||
{
|
{
|
||||||
DECLARE_SCRIPTING_TYPE(TaskGraphSystem);
|
DECLARE_SCRIPTING_TYPE(TaskGraphSystem);
|
||||||
friend TaskGraph;
|
friend TaskGraph;
|
||||||
@@ -52,7 +52,7 @@ public:
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Graph-based asynchronous tasks scheduler for high-performance computing and processing.
|
/// Graph-based asynchronous tasks scheduler for high-performance computing and processing.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
API_CLASS() class FLAXENGINE_API TaskGraph : public PersistentScriptingObject
|
API_CLASS() class FLAXENGINE_API TaskGraph : public ScriptingObject
|
||||||
{
|
{
|
||||||
DECLARE_SCRIPTING_TYPE(TaskGraph);
|
DECLARE_SCRIPTING_TYPE(TaskGraph);
|
||||||
private:
|
private:
|
||||||
|
|||||||
@@ -720,9 +720,9 @@ bool ProcessMesh(ImportedModelData& result, OpenFbxImporterData& data, const ofb
|
|||||||
continue;
|
continue;
|
||||||
const ofbx::Shape* shape = channel->getShape(targetShapeCount - 1);
|
const ofbx::Shape* shape = channel->getShape(targetShapeCount - 1);
|
||||||
|
|
||||||
if (shape->getVertexCount() != vertexCount)
|
if (shape->getVertexCount() != aGeometry->getVertexCount())
|
||||||
{
|
{
|
||||||
LOG(Error, "Blend shape '{0}' in mesh '{1}' has different amount of vertices ({2}) than mesh ({3})", String(shape->name), mesh.Name, shape->getVertexCount(), vertexCount);
|
LOG(Error, "Blend shape '{0}' in mesh '{1}' has different amount of vertices ({2}) than mesh ({3})", String(shape->name), mesh.Name, shape->getVertexCount(), aGeometry->getVertexCount());
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -730,21 +730,21 @@ bool ProcessMesh(ImportedModelData& result, OpenFbxImporterData& data, const ofb
|
|||||||
blendShapeData.Name = shape->name;
|
blendShapeData.Name = shape->name;
|
||||||
blendShapeData.Weight = channel->getShapeCount() > 1 ? (float)(channel->getDeformPercent() / 100.0) : 1.0f;
|
blendShapeData.Weight = channel->getShapeCount() > 1 ? (float)(channel->getDeformPercent() / 100.0) : 1.0f;
|
||||||
|
|
||||||
blendShapeData.Vertices.Resize(shape->getVertexCount());
|
blendShapeData.Vertices.Resize(vertexCount);
|
||||||
for (int32 i = 0; i < blendShapeData.Vertices.Count(); i++)
|
for (int32 i = 0; i < blendShapeData.Vertices.Count(); i++)
|
||||||
blendShapeData.Vertices.Get()[i].VertexIndex = i;
|
blendShapeData.Vertices.Get()[i].VertexIndex = i;
|
||||||
|
|
||||||
auto shapeVertices = shape->getVertices();
|
auto shapeVertices = shape->getVertices();
|
||||||
for (int32 i = 0; i < blendShapeData.Vertices.Count(); i++)
|
for (int32 i = 0; i < blendShapeData.Vertices.Count(); i++)
|
||||||
{
|
{
|
||||||
auto delta = ToVector3(shapeVertices[i]) - mesh.Positions.Get()[i];
|
auto delta = ToVector3(shapeVertices[i + firstVertexOffset]) - mesh.Positions.Get()[i];
|
||||||
blendShapeData.Vertices.Get()[i].PositionDelta = delta;
|
blendShapeData.Vertices.Get()[i].PositionDelta = delta;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto shapeNormals = shape->getNormals();
|
auto shapeNormals = shape->getNormals();
|
||||||
for (int32 i = 0; i < blendShapeData.Vertices.Count(); i++)
|
for (int32 i = 0; i < blendShapeData.Vertices.Count(); i++)
|
||||||
{
|
{
|
||||||
/*auto delta = ToVector3(shapeNormals[i]) - mesh.Normals[i];
|
/*auto delta = ToVector3(shapeNormals[i + firstVertexOffset]) - mesh.Normals[i];
|
||||||
auto length = delta.Length();
|
auto length = delta.Length();
|
||||||
if (length > ZeroTolerance)
|
if (length > ZeroTolerance)
|
||||||
delta /= length;*/
|
delta /= length;*/
|
||||||
|
|||||||
@@ -357,6 +357,26 @@ namespace FlaxEngine
|
|||||||
return new Matrix(stream.ReadSingle(), stream.ReadSingle(), stream.ReadSingle(), stream.ReadSingle(), stream.ReadSingle(), stream.ReadSingle(), stream.ReadSingle(), stream.ReadSingle(), stream.ReadSingle(), stream.ReadSingle(), stream.ReadSingle(), stream.ReadSingle(), stream.ReadSingle(), stream.ReadSingle(), stream.ReadSingle(), stream.ReadSingle());
|
return new Matrix(stream.ReadSingle(), stream.ReadSingle(), stream.ReadSingle(), stream.ReadSingle(), stream.ReadSingle(), stream.ReadSingle(), stream.ReadSingle(), stream.ReadSingle(), stream.ReadSingle(), stream.ReadSingle(), stream.ReadSingle(), stream.ReadSingle(), stream.ReadSingle(), stream.ReadSingle(), stream.ReadSingle(), stream.ReadSingle());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Deserializes object from Json by reading it as a raw data (ver+length+bytes).
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>Reads version number, data length and actual data bytes from the stream.</remarks>
|
||||||
|
/// <param name="stream">The stream.</param>
|
||||||
|
/// <param name="obj">The object to deserialize.</param>
|
||||||
|
public static void ReadJson(this BinaryReader stream, ISerializable obj)
|
||||||
|
{
|
||||||
|
// ReadStream::ReadJson
|
||||||
|
var engineBuild = stream.ReadInt32();
|
||||||
|
var size = stream.ReadInt32();
|
||||||
|
if (obj != null)
|
||||||
|
{
|
||||||
|
var data = stream.ReadBytes(size);
|
||||||
|
Json.JsonSerializer.LoadFromBytes(obj, data, engineBuild);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
stream.BaseStream.Seek(size, SeekOrigin.Current);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Writes the color to the binary stream.
|
/// Writes the color to the binary stream.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -548,5 +568,25 @@ namespace FlaxEngine
|
|||||||
stream.Write(value.M43);
|
stream.Write(value.M43);
|
||||||
stream.Write(value.M44);
|
stream.Write(value.M44);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Serializes object to Json and writes it as a raw data (ver+length+bytes).
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="stream">The stream.</param>
|
||||||
|
/// <remarks>Writes version number, data length and actual data bytes to the stream.</remarks>
|
||||||
|
/// <param name="obj">The object to serialize.</param>
|
||||||
|
public static void WriteJson(this BinaryWriter stream, ISerializable obj)
|
||||||
|
{
|
||||||
|
// WriteStream::WriteJson
|
||||||
|
stream.Write(Globals.EngineBuildNumber);
|
||||||
|
if (obj != null)
|
||||||
|
{
|
||||||
|
var bytes = Json.JsonSerializer.SaveToBytes(obj);
|
||||||
|
stream.Write(bytes.Length);
|
||||||
|
stream.Write(bytes);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
stream.Write(0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,9 +39,9 @@ API_ENUM() enum class ChannelMask
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents a parameter in the Graph.
|
/// Represents a parameter in the Graph.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
API_CLASS() class FLAXENGINE_API GraphParameter : public PersistentScriptingObject
|
API_CLASS() class FLAXENGINE_API GraphParameter : public ScriptingObject
|
||||||
{
|
{
|
||||||
DECLARE_SCRIPTING_TYPE_WITH_CONSTRUCTOR_IMPL(GraphParameter, PersistentScriptingObject);
|
DECLARE_SCRIPTING_TYPE_WITH_CONSTRUCTOR_IMPL(GraphParameter, ScriptingObject);
|
||||||
public:
|
public:
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
Reference in New Issue
Block a user