summaryrefslogtreecommitdiffstats
path: root/Scripts/Fold/Editor/KeyframeNodeView.cs
blob: 225b5b6a04a2d3596e01dcb243cadcd394081105 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
using GraphProcessor;
using UnityEngine.UIElements;
using UnityEngine;
using System.Linq;
using System.Collections.Generic;

[NodeCustomEditor(typeof(KeyframeNode))]
public class KeyframeNodeView : BaseNodeView
{
    Button generateButton;
    const string ERROR_TYPE = "GameObject input has wrong type";
    const string ERROR_REND = "GameObject must have a MeshRenderer component";

    public override void Enable()
    {
        generateButton = new Button(OnGenerateClick) { text = "Generate Keyframe" };
        controlsContainer.Add(generateButton);

        onPortConnected += _ => Validate();
        onPortDisconnected += _ => Validate();
        schedule.Execute(Validate).Every(200);

        Validate();
    }

    void OnGenerateClick()
    {
        var go = GetConnectedGameObject();
        if (go == null) return;
        var rend = go.GetComponent<MeshRenderer>();
        if (rend == null) return;

        var foldNode = GetConnectedFoldNode();

        // Accumulate nodes. We are going backwards from the output node
        // (KeyframeNode) to the root node.
        var foldNodes = new Stack<BaseFoldNode>();
        var cur = foldNode;
        while (cur != null) {
          foldNodes.Push(cur);
          cur = GetInputFoldNode(cur);
        }

        var mpb = new MaterialPropertyBlock();
        rend.GetPropertyBlock(mpb);

        // Set material properties for all 16 slots.
        for (int i = 0; i < 16; i++) {
            string slotPrefix = $"_Vertex_Deformation_Slot_{i}_";
            BaseFoldNode node = (foldNodes.Count > 0) ? foldNodes.Pop() : null;
            FoldNodeSerialized data = node?.Serialize();

            bool active = data != null;
            mpb.SetFloat(slotPrefix + "Enabled", active ? 1.0f : 0.0f);
            mpb.SetInt(slotPrefix + "Opcode", active ? data.opcode : 0);

            mpb.SetFloat(slotPrefix + "Float_0", active ? data.float0 : 0.0f);
            mpb.SetFloat(slotPrefix + "Float_1", active ? data.float1 : 0.0f);
            mpb.SetFloat(slotPrefix + "Float_2", active ? data.float2 : 0.0f);
            mpb.SetFloat(slotPrefix + "Float_3", active ? data.float3 : 0.0f);

            mpb.SetVector(slotPrefix + "Vector_0", active ? data.vec0 : Vector4.zero);
            mpb.SetVector(slotPrefix + "Vector_1", active ? data.vec1 : Vector4.zero);
            mpb.SetVector(slotPrefix + "Vector_2", active ? data.vec2 : Vector4.zero);
            mpb.SetVector(slotPrefix + "Vector_3", active ? data.vec3 : Vector4.zero);
        }

        rend.SetPropertyBlock(mpb);
        Debug.Log($"Generated Keyframe for '{go.name}' with properties applied to all 16 slots.");
    }

    BaseFoldNode GetInputFoldNode(BaseFoldNode node)
    {
        // Traverse via edges in the graph model, as 'input' field might not be populated in Editor
        var port = node.GetPort(nameof(BaseFoldNode.input), null);
        if (port == null) return null;

        // Find the edge connected to this input port
        var edge = node.graph.edges.FirstOrDefault(e => e.inputPort == port);
        return edge?.outputNode as BaseFoldNode;
    }

    T GetConnectedNode<T>(string fieldName) where T : class
    {
        var edge = GetFirstPortViewFromFieldName(fieldName)?.GetEdges().FirstOrDefault();
        return (edge?.output.node as BaseNodeView)?.nodeTarget as T;
    }

    GameObject GetConnectedGameObject() => GetConnectedNode<GameObjectNode>(nameof(KeyframeNode.targetObject))?.output;

    BaseFoldNode GetConnectedFoldNode() => GetConnectedNode<BaseFoldNode>(nameof(KeyframeNode.foldData));

    void Validate()
    {
        var go = GetConnectedGameObject();
        bool hasGoConn = GetFirstPortViewFromFieldName(nameof(KeyframeNode.targetObject))?.GetEdges().Any() ?? false;
        bool hasFoldConn = GetFirstPortViewFromFieldName(nameof(KeyframeNode.foldData))?.GetEdges().Any() ?? false;

        RemoveMessageView(ERROR_TYPE);
        RemoveMessageView(ERROR_REND);

        if (hasGoConn)
        {
            if (go == null) AddMessageView(ERROR_TYPE, NodeMessageType.Error);
            else if (go.GetComponent<MeshRenderer>() == null) AddMessageView(ERROR_REND, NodeMessageType.Error);
        }

        generateButton.SetEnabled(hasGoConn && hasFoldConn && go?.GetComponent<MeshRenderer>() != null);
    }
}