Implemented the ability to load a node externally. This XML data could also
eventually be copy/pasted between instances of Olive alongside XML data for
other nodes and their connections to allow copying entire node trees.
While the copied graph doesn't pick up InvalidateCache signals, some node
processing relies on NodeInput update signals to update their internals
correctly.
Previous iteration used some "magic code" that added clips automatically to the
timeline. This was functional but ultimately outside of the undo commands'
control meaning nodes could be infinitely added and abandoned. This makes the
add process part of the undo command which means it's all undoable as the user
would expect.
This was many changes that were largely fundamentally related. They included:
- More const modifiers to enforce read only node graphs
- Support for fragment and vertex shaders from the nodes
- Support for node code loaded externally (embedded into the binary)
- Fixed issue preventing two textures from being used in a shader
- Removed several unused functions and cleaned up code
- Fixed video media node misreading its matrix input
Like the base "Block" type, the "Transition" type will not be a node used on its
own, it will have to be derived. This type defines some basic behavior for a
transition (e.g. how much it overlaps its "from" and "to" blocks).
If the nodes are now stateless, there's nothing stopping the renderer from
rendering multiple frames at once. Earlier since the nodes held some of their
input/output data (and that data could change per frame), it was not possible
to render multiple frames at once without conflicts. Now that the node state is
held in render threads, they can do whatever they want at any time.
Since all Nodes have an output that provides all output values now, we now
never need to add an output from a Node derivative. These changes remove the
last of them and disable the ability to add more outputs from a derivative.
If Nodes only have the one output, we don't need to do so much differentiation
between them. Previous iteration used outputs as like a distinct function
within a Node (e.g. length output would return one result, buffer output would
produce a different result - each run different code to produce their results).
Now in this iteration, it's more accurate to say a Node is just one function
(which seems more appropriate for a node system anyway).
Largely conforming renderers to new NodeValue system. Code seems a lot cleaner
this way which is a nice advantage. Likely non-functional as this won't
compile just yet and still needs probably another day or two of testing to get
it back to where it was before.
We use references to the node directly rather than references supplied by the
object. This should be more robust anyway and will fit better in the new node
structure.
Since the nodes won't be holding any rendering data themselves in this system,
we may as well enforce some level of non-write access by setting all the
functions to const. They were already const-friendly, they just weren't
labelled as such.
Fixes a "pure virtual method" crash. The inputs will try to send invalidate
cache signals through the node while it's being destroyed if these are left
connected. There's no purpose to sending invalidate cache signals since any
nodes affected by this one will automatically invalidate the cache from its
outputs being disconnected and the node being destroyed so this shouldn't
cause issues.
Fixes recurring design issue that the Blocks were a frequent exception to the
DAG concept. The Blocks connecting to each others inputs/outputs while not
necessarily being "dependent" on each other to produce an image continually
causes issues while trying to create a rendering code path. This redesign
should provide a more "directed" approach to the directed acyclic graph.
NodeParams used to be destroyed automatically through the QObject system, but
now that NodeParams perform actions that access the parent Node object in
their destructor, the NodeParam destructor needs to be called before the Node
has been fully destroyed. By destroying explicitly in the Node destructor we
accomplish this.
If a NodeParam is destroyed (presumably as the result of its parent Node being
destroyed), any edge objects connecting to another param should be destroyed
or else the edge will be floating referencing a now destroyed object.