» Dynamic Plugins Reference
This site relies heavily on Javascript. You should enable it if you want the full experience. Learn more.

Dynamic Plugins Reference

French | Italian

Cloning a Template

In order to create a dynamic c# plugin doubleclick in a patch to open the nodebrowser. Type "Template" to get a list of templates which you can choose to start from. Choose one of the templates prefixed with d and press CTRL+Enter or CTRL+Click to clone the template.

In the dialog that now shows up choose a unique Name and Category for your node. Specifying a Version is optional, see NodeAndPinNaming.

If the patch you're cloning in is not yet saved you'll also have to specify a path where to clone to. It is therefore always recommended to save the patch first because then the clone conveniently goes to a subdirectory of the patch.

Press Clone to see your ready plugin appear in the patch. Rightclick the node to open the code-editor.

Use the Project Explorer to add/remove documents or references to a plugin.

PluginInfo

[PluginInfo(Name = "Template",
            Category = "Value",
            Help = "A Template",
            Tags = "simple, basic",
            Author = "vvvv group",
            AutoEvaluate = false)]
The PluginInfo attribute written above your plugin-class is used to describe a vvvv plugin.

Inside the () brackets in the attribute statement there is a comma separated list of Name=Value pairs. Only the Name and Category ones are mandatory, all others are optional.

Here is a listing of all available PluginInfos.

Regarding the AutoEvaluate setting see the Evaluate() section below.

Defining Pins

[Config("My Config Value")]
public ISpread<double> FConfig;
 
[Input("My Input String")]
public ISpread<string> FInput;
 
[Output("My Output Color")]
public ISpread<RGBAColor> FOutput;
Attribute examples
//specify a default value for a pin
[Input("Input", DefaultValue = 255.0)]
 
//specify a not spreadable pin. 
//The pin has only one slice 
//and the user can't change that. 
[.., IsSingle = true)]
 
//specify the pins visibilty. 
[.., Visibility = PinVisibility.Hidden)]
 
//specify a pin to be of type filename 
//to show a file-dialog on rightclick.
[.., StringType = StringType.Filename)]
IDiffSpread example
[Input("Write", IsBang=true)]
IDiffSpread<bool> FDoWrite;
...
if (FDoWrite.IsChanged)
...
Spread of Spread example
//create a two-dimensional spread
//exposed in vvvv with a data and
//a bin-size pin.
[..]
ISpread<ISpread<double>> FInput;
PinGroup example
//create a dynamic pingroup, where 
//the user can specify the pin count 
//via the Inspektor. 
[.., IsPinGroup = true)]
ISpread<ISpread<double>> FInput;
A Pin is being defined by 2 lines of code:

  • a PinInfo attribute (Config, Input or Output)
  • a variable declaration
PinInfo attribute

The PinInfo attribute is used to describe a pin. The attribute immediately precedes the declaration of an input or output variable.

Inside the () brackets in the attribute statement the first string argument is mandatory and specifies the pin's name. A further comma separated list of Name=Value pairs (that is optional) allows you to describe a subtype of the pins value by setting a Default-, Min-, MaxValue and more.

Here is a listing of all available PinInfos.

Variable declarations

In most scenarios you'll want to declare your in- and outputs of the generic type ISpread<T> where for T you can specify any type like:

float, double, bool, int, ..
value pin
string
string pin
RGBAColor
color pin

If you want to ask from an input if it has changed then simply use IDiffSpread<T> instead of ISpread<T> in the declaration and call .IsChanged whenever you need to know.

Spread of Spread

If you declare a variable as ISpread<ISpread<T>> then next to the actual pin (that holds the data) you get a free BinSize pin that allows you to specify how that data is structured into bins.

If you still want to know if the input was changed, only the outer spread should be declared as IDiffSpread<T>.

Alternatively you can also make this pin a PinGroup where instead of the BinSize pin you get a free PinCount config pin, see example to the left.

Add/Remove pins

There are 2 ways to do this at runtime:

  • simple: use a PinGroup: This only works in combination with an ISpread<ISpread<T>> with any T (see codesnipped on the left).
  • flexible: have a look at the code of the Template (Value DynamicPins).
Connection callbacks

In the unlikely case that you need to react to pin connect/disconnect events use Pin<T> instead of ISpread<T>, see Pin(T) Events.

Evaluate

public void Evaluate(int SpreadMax)
{ 
  FOutput.SliceCount = SpreadMax;
 
  for (int i = 0; i < SpreadMax; i++)
    FOutput[i] = FInput[i] * 2;
}
This is the heart of your plugin. The Evaluate() function is run once every frame if:

  • something is connected downstream
  • or you have set AutoEvaluate=true (see PluginInfo above)

Here you do the main calculation of your plugin. If you're looking for a section to initialize some parts of your code (ie. run them only once), see the next section (Constructor).

As an input parameter the function hands you the SpreadMax which is the maximum slicecount of all connected spreads on all inputs of the plugin. You will typically use this to set the slicecount of your output pins as shown in the code to the left.

Please note: if ANY of your input pins is an ISpread<ISpread<...>>, you should evaluate SpreadMax yourself as you can see below. Like this a 2d-spread will only contribute to the spreadmax with its "bin count" which is what you typically want.

SpreadMax = SpreadUtils.SpreadMax(FInput1, FInput2 /* state ALL your inputs here*/);

The Constructor: Initializing stuff

public MyNodeClassname()
{ 
  //initialize stuff
}
public class MyNodeClassname: 
IPluginEvaluate, 
IPartImportsSatisfiedNotification
{
...
 
public void OnImportsSatisfied()
{
  //access your in-/outputs here
}
You'll use a constructor if you have parts of your code that only need to run once. Note that a constructor is optional and therefore most of the templates come without one.

If you need to access any of the variables you defined as in- or outputs you'll have to take extra care and implement IPartImportsSatisfiedNotification as shown in the example to the left. For an actual usecase see the code of Template (Raw).

The Destructor: Disposing stuff

public class MyNodeClassname: 
IPluginEvaluate, 
IDisposable
{
...
//called when the plugin gets 
//deleted
public void Dispose()
{
  //unsubscribe from events
  //and call dispose on resources
  //your plugin has created.
}
In case your plugin makes use of resources which need to be released when the plugin gets deleted you need to implement the IDisposable interface. In its Dispose method you can do all the necessary clean-up tasks.

Accessing Internals: The HDEHost

[Import()]
public IPluginHost2 FPluginHost;
 
[Import()]
public IHDEHost FHDEHost;
There are two entry points if you want to access some of the internals of vvvv:

  • want to get/set information on the plugin you're working at, have a look at: IPluginHost2
  • want access to the whole of vvvv, have a look at IHDEHost

Usings

using System;
..
using VVVV.PluginInterfaces.V2;
All entities in .NET can be reached via a global path, which is a combination of the namespace in which the entity is defined and the name of the entity. So e.g. VVVV.PluginInterfaces.V2.ISpread<bool> is the full path to the type ISpread<bool> which is defined in the namespace VVVV.PluginInterfaces.V2.

The using statements are there to be able to shorten your code by only saying ISpread<bool>. For that to work it is necessary to use the namespace (VVVV.PluginInterfaces.V2) once at the beginning of the code.

Debugging

[Import()]
public ILogger Flogger;
...
 
Flogger.Log(LogType.Debug, "foo");
The most basic way of debugging parts of your code is writing out log-messages that you can view in Renderer (TTY). You can do so by importing the ILogger and calling .Log() on it as shown in the example to the left.

Breakpoints

If you want to set breakpoints and step through your code line by line you'll need an IDE like SharpDevelop or VisualStudio and do the following:

  • load the .csproj of your plugin
  • set a breakpoint in your code
  • attach the IDE to the running instance of vvvv.

Now when a breakpoint is hit the IDE stops vvvv and you can step through your lines of code.

Further optional interfaces to implement

  • IBackgroundColor allows a GUI plugin to specify the background color for the window it is hosted in (mostly to prevent flickering when the window goes fullscreen).
  • IQueryDelete provides a plugin with the possibility to prohibit its own deletion.
  • IMainLoop allows to subscribe to mainloop events.
  • IDXDeviceService provides access to Direct3D9 devices created by vvvv.
  • IStartable allows some code to be executed on startup.

anonymous user login

Shoutbox

~7d ago

joreg: vvvvTv S02E03 is out: Logging: https://youtube.com/live/OpUrJjTXBxM

~9d ago

~11d ago

joreg: Follow TobyK on his Advent of Code: https://www.twitch.tv/tobyklight

~14d ago

joreg: vvvvTv S02E02 is out: Saving & Loading UI State: https://www.youtube.com/live/GJQGVxA1pIQ

~14d ago

joreg: We now have a presence on LinkedIn: https://www.linkedin.com/company/vvvv-group

~21d ago

joreg: vvvvTv S02E01 is out: Buttons & Sliders with Dear ImGui: https://www.youtube.com/live/PuuTilbqd9w

~28d ago

joreg: vvvvTv S02E00 is out: Sensors & Servos with Arduino: https://visualprogramming.net/blog/2024/vvvvtv-is-back-with-season-2/

~28d ago

~29d ago

fleg: hey there! What's the best tool for remote work? Teamviewer feels terrible. Thanks!

~1mth ago

joreg: Last call: 6-session vvvv beginner course starting November 4: https://thenodeinstitute.org/courses/ws24-5-vvvv-beginners-part-i/