Skip to content

Commit

Permalink
DYN-4224 / DYN-4225 Watch3d Updates (Persistent camera and node size …
Browse files Browse the repository at this point in the history
…between saves / Ignore Node preview state) (#12150) (#13534)

* Use exisitng width, height, and camera during deserialization

* Allow Watch3D to tessellate directly connected nodes independent of preview state

* One more change for directly connected

* Fix bug with port connection and disconnect events

* pr comments

* Ignore certain cases

* PR comments

* update comments

* Update DefaultWatch3DViewModel.cs

* Added deserialization test for HelixWatch3D node

* Added the dyn file for the HelixWatch3D node deserialization test

* Added testing for Width deserialization

* Renamed test dyn file

* Added test for ignoring IsVisible parameter by Watch3d node

* Fixed wrong test geometry in dyn file

* Added another Assert to the IgnoreIsVisible test

* Fixed Assert statements

* Simplified a test

* Remove node_modules.

* Fix test.

* Simplify

Co-authored-by: Craig Long <[email protected]>
Co-authored-by: Long Nguyen <[email protected]>
Co-authored-by: Trygve Wastvedt <[email protected]>
(cherry picked from commit 903ca10)

Co-authored-by: Craig Long <[email protected]>
  • Loading branch information
twastvedt and saintentropy authored Nov 17, 2022
1 parent d9302ab commit 4988f41
Show file tree
Hide file tree
Showing 12 changed files with 482 additions and 61 deletions.
22 changes: 21 additions & 1 deletion src/DynamoCore/Graph/Nodes/NodeModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2709,6 +2709,25 @@ internal void RequestValueUpdate(EngineController engine)
/// <returns>Flag which indicates if geometry update has been scheduled</returns>
public virtual bool RequestVisualUpdateAsync(IScheduler scheduler,
EngineController engine, IRenderPackageFactory factory, bool forceUpdate = false)
{
return RequestVisualUpdateAsync(scheduler, engine, factory, forceUpdate, false);
}

/// <summary>
/// Call this method to asynchronously regenerate render package for
/// this node. This method accesses core properties of a NodeModel and
/// therefore is typically called on the main/UI thread.
/// </summary>
/// <param name="scheduler">An IScheduler on which the task will be scheduled.</param>
/// <param name="engine">The EngineController which will be used to get MirrorData for the node.</param>
/// <param name="factory">An IRenderPackageFactory which will be used to generate IRenderPackage objects.</param>
/// <param name="forceUpdate">Normally, render packages are only generated when the node's IsUpdated parameter is true.
/// By setting forceUpdate to true, the render packages will be updated.</param>
/// <param name="ignoreIsVisible">Normally, render packages are only generated when the node's IsVisible parameter is true.
/// By setting ignore to true, the render package will be updated.</param>
/// <returns>Flag which indicates if geometry update has been scheduled</returns>
public virtual bool RequestVisualUpdateAsync(IScheduler scheduler,
EngineController engine, IRenderPackageFactory factory, bool forceUpdate, bool ignoreIsVisible = false)
{
var initParams = new UpdateRenderPackageParams()
{
Expand All @@ -2717,7 +2736,8 @@ public virtual bool RequestVisualUpdateAsync(IScheduler scheduler,
EngineController = engine,
DrawableIdMap = GetDrawableIdMap(),
PreviewIdentifierName = AstIdentifierForPreview.Name,
ForceUpdate = forceUpdate
ForceUpdate = forceUpdate,
IgnoreIsVisible = ignoreIsVisible
};

var task = new UpdateRenderPackageAsyncTask(scheduler);
Expand Down
10 changes: 10 additions & 0 deletions src/DynamoCore/Graph/Nodes/NodeModelExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,16 @@ internal static void VisibleUpstreamNodes(this NodeModel node, List<NodeModel> g
}
}

/// <summary>
/// Provide the upstream nodes that are imediately connected.
/// </summary>
/// <param name="node"></param>
/// <returns></returns>
internal static HashSet<NodeModel> ImediateUpstreamNodes(this NodeModel node)
{
return node.InPorts.SelectMany(p => p.Connectors.Select(c => c.Start.Owner)).ToHashSet();
}

internal static IEnumerable<NodeModel> UpstreamNodesMatchingPredicate(this NodeModel node, List<NodeModel> gathered, Predicate<NodeModel> match)
{
var upstream = node.InPorts.SelectMany(p => p.Connectors.Select(c => c.Start.Owner)).
Expand Down
15 changes: 12 additions & 3 deletions src/DynamoCore/Scheduler/UpdateRenderPackageAsyncTask.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ class UpdateRenderPackageParams
internal IEnumerable<KeyValuePair<Guid,string>> DrawableIdMap { get; set; }

internal bool ForceUpdate { get; set; }
/// <summary>
/// Set to true to ignore the preview state of the node
/// </summary>
internal bool IgnoreIsVisible { get; set; }
}

/// <summary>
Expand Down Expand Up @@ -88,9 +92,14 @@ internal bool Initialize(UpdateRenderPackageParams initParams)
if (nodeModel.WasRenderPackageUpdatedAfterExecution && !initParams.ForceUpdate)
return false; // Not has not been updated at all.

// If a node is in either of the following states, then it will not
// produce any geometric output. Bail after clearing the render packages.
if (nodeModel.IsInErrorState || !nodeModel.IsVisible)
// If a node is in an error state it won't produce any geometric output.
// Bail after clearing the render packages.
if (nodeModel.IsInErrorState)
return false;

// If a node is not set as visible and the override is not set to true then
// bail after clearing the render packages.
if (!nodeModel.IsVisible && initParams.IgnoreIsVisible == false)
return false;

// Without AstIdentifierForPreview, a node cannot have MirrorData.
Expand Down
5 changes: 5 additions & 0 deletions src/DynamoCoreWpf/DynamoCoreWpf.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -1446,6 +1446,11 @@
<Project>{ccb6e56b-2da1-4eba-a1f9-e8510e129d12}</Project>
<Name>VMDataBridge</Name>
</ProjectReference>
<ProjectReference Include="..\Libraries\Watch3DNodeModels\Watch3DNodeModels.csproj">
<Project>{31183026-DE70-49CB-BC7C-0DFD0A088F62}</Project>
<Name>Watch3DNodeModels</Name>
<Private>False</Private>
</ProjectReference>
<ProjectReference Include="..\NodeServices\DynamoServices.csproj">
<Project>{ef879a10-041d-4c68-83e7-3192685f1bae}</Project>
<Name>DynamoServices</Name>
Expand Down
10 changes: 10 additions & 0 deletions src/DynamoCoreWpf/ViewModels/Watch3D/DefaultWatch3DViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using System.Windows.Media;
using System.Windows.Media.Media3D;
using System.Xml;
using CoreNodeModels;
using Dynamo.Core;
using Dynamo.Graph.Connectors;
using Dynamo.Graph.Nodes;
Expand Down Expand Up @@ -610,6 +611,15 @@ protected virtual void OnRenderPackagesUpdated(NodeModel node, RenderPackageCach
// If there is no attached model update for all render packages
if (watchModel == null)
{
//When Watch3D nodes are in canvas we need to ignore the Node.RenderPackageUpdate event in some cases.
//The background preview does not request tesselation for non-visible nodes or the Watch / Watch3D nodes.
//When these nodes are connected to a Watch3D, tesselation is requested and the Node.RenderPacakgeUpdate
//event will be raised. The background preview needs to filter out those tesselation events.
if (node.IsVisible == false || node is Watch || node is Watch3DNodeModels.Watch3D)
{
return;
}

AddGeometryForRenderPackages(packages);
return;
}
Expand Down
4 changes: 2 additions & 2 deletions src/DynamoCoreWpf/ViewModels/Watch3D/HelixWatch3DViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1092,7 +1092,7 @@ private void OnSceneItemsChanged()
OnRequestViewRefresh();
}

private KeyValuePair<string, Element3D>[] FindAllGeometryModel3DsForNode(NodeModel node)
internal KeyValuePair<string, Element3D>[] FindAllGeometryModel3DsForNode(NodeModel node)
{
KeyValuePair<string, Element3D>[] geometryModels;

Expand All @@ -1118,7 +1118,7 @@ private KeyValuePair<string, Element3D>[] FindAllGeometryModel3DsForNode(string
return geometryModels;
}

private void SetGeometryFrozen(HashSet<NodeModel> gathered)
internal void SetGeometryFrozen(HashSet<NodeModel> gathered)
{

foreach (var node in gathered)
Expand Down
9 changes: 3 additions & 6 deletions src/Libraries/Watch3DNodeModels/Watch3D.cs
Original file line number Diff line number Diff line change
Expand Up @@ -122,8 +122,8 @@ internal void OnSerialized(XmlElement element)
}
}

public double WatchWidth { get; private set; }
public double WatchHeight { get; private set; }
public double WatchWidth { get; set; }
public double WatchHeight { get; set; }
public bool WasExecuted { get; internal set; }

public delegate void VoidHandler();
Expand All @@ -134,10 +134,7 @@ internal void OnSerialized(XmlElement element)
private Watch3D(IEnumerable<PortModel> inPorts, IEnumerable<PortModel> outPorts) : base(inPorts, outPorts)
{
ArgumentLacing = LacingStrategy.Disabled;
WatchWidth = 200;
WatchHeight = 200;
ShouldDisplayPreviewCore = false;
Camera = new Watch3DCamera();
}

public Watch3D()
Expand Down Expand Up @@ -211,7 +208,7 @@ public override IEnumerable<AssociativeNode> BuildOutputAst(List<AssociativeNode
public Watch3DCamera Camera
{
get;
private set;
set;
}

#endregion
Expand Down
63 changes: 50 additions & 13 deletions src/Libraries/Watch3DNodeModelsWpf/HelixWatch3DNodeViewModel.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
Expand Down Expand Up @@ -50,18 +50,23 @@ void watchNode_Deserialized(XmlNode obj)

protected override void PortConnectedHandler(PortModel arg1, ConnectorModel arg2)
{
UpdateUpstream();
if (arg1.PortType == PortType.Input && watchModel == arg1.Owner)
{
UpdateUpstream();
}
}

protected internal override void UpdateUpstream()
{
OnClear();

var gathered = new List<NodeModel>();
watchModel.VisibleUpstreamNodes(gathered);
var connectedNodes = watchModel.ImediateUpstreamNodes();

gathered.ForEach(n => n.WasRenderPackageUpdatedAfterExecution = false);
gathered.ForEach(n => n.RequestVisualUpdateAsync(scheduler, engineManager.EngineController, renderPackageFactory));
foreach(var n in connectedNodes)
{
n.WasRenderPackageUpdatedAfterExecution = false;
n.RequestVisualUpdateAsync(scheduler, engineManager.EngineController, renderPackageFactory, false, true);
}
}

protected override void OnModelPropertyChanged(object sender, PropertyChangedEventArgs e)
Expand All @@ -74,22 +79,54 @@ protected override void OnModelPropertyChanged(object sender, PropertyChangedEve
}
}

protected override void OnNodePropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (!(sender is NodeModel node))
{
return;
}

if (e.PropertyName == nameof(node.CachedValue))
{
var connecteNodes = watchModel.ImediateUpstreamNodes();
if (!connecteNodes.Contains(node))
{
return;
}

node.RequestVisualUpdateAsync(scheduler, engineManager.EngineController, renderPackageFactory, true, true);
}

if (e.PropertyName == "IsFrozen")
{
if (!watchModel.UpstreamCache.Contains(node))
{
return;
}

var gathered = new HashSet<NodeModel>();
node.GetDownstreamNodes(node, gathered);
SetGeometryFrozen(gathered);
}

node.WasRenderPackageUpdatedAfterExecution = false;
}

protected override void PortDisconnectedHandler(PortModel obj)
{
OnClear();
if (obj.PortType == PortType.Input && watchModel == obj.Owner)
{
OnClear();
}
}


protected override void OnRenderPackagesUpdated(NodeModel node,
RenderPackageCache renderPackages)
{
var updatedNode = dynamoModel.CurrentWorkspace.Nodes.FirstOrDefault(n => n.GUID == node.GUID);
if (updatedNode == null) return;

var visibleUpstream = new List<NodeModel>();
watchModel.VisibleUpstreamNodes(visibleUpstream);
var connectedNodes = watchModel.ImediateUpstreamNodes();

if (!visibleUpstream.Contains(updatedNode))
if (!connectedNodes.Contains(node))
{
return;
}
Expand Down
23 changes: 17 additions & 6 deletions src/Libraries/Watch3DNodeModelsWpf/Watch3DNodeViewCustomization.cs
Original file line number Diff line number Diff line change
Expand Up @@ -56,15 +56,26 @@ public void CustomizeView(Watch3D model, NodeView nodeView)
watch3DViewModel.Setup(dynamoViewModel,
dynamoViewModel.RenderPackageFactoryViewModel.Factory);

if (model.initialCameraData != null)
if (model.Camera != null)
{
try
{
// The deserialization logic is unified between the view model and this node model.
// For the node model, we need to supply the deserialization method with the camera node.
var cameraNode = model.initialCameraData.ChildNodes.Cast<XmlNode>().FirstOrDefault(innerNode => innerNode.Name.Equals("camera", StringComparison.OrdinalIgnoreCase));
var cameraData = watch3DViewModel.DeserializeCamera(cameraNode);
watch3DViewModel.SetCameraData(cameraData);
var camera = model.Camera;
var camData = new CameraData
{
Name = camera.Name,
EyeX = camera.EyeX,
EyeY = camera.EyeY,
EyeZ = camera.EyeZ,
LookX = camera.LookX,
LookY = camera.LookY,
LookZ = camera.LookZ,
UpX = camera.UpX,
UpY = camera.UpY,
UpZ = camera.UpZ
};

watch3DViewModel.SetCameraData(camData);
}
catch
{
Expand Down
Loading

0 comments on commit 4988f41

Please sign in to comment.