IDC - Yeusepe/Yeusepes-Modules GitHub Wiki
This guide is intended for developers who wish to understand and extend the Inter-Device Communication (IDC) system integrated in SpotiOSC. It covers the encoding and decoding process, the integration with a custom shader prefab, and examples of how to send and receive messages.
You can find the prefab here.
- Introduction
- System Overview
- Detailed Code Walkthrough
- Shader Integration and Prefab Setup
- Examples and Code Snippets
- Debugging and Images
- Conclusion
The IDC system in SpotiOSC allows avatars in VRChat to exchange data by converting text messages into a visual code and then decoding them back into text. This guide explains:
- How messages are encoded using OSC parameters.
- How the visual code is rendered using a custom shader.
- How the visual code is decoded back into text using image processing.
-
StringEncoder:
- Converts text (e.g., a session ID) into a series of OSC signals.
- Each character is converted into its ASCII integer value.
- A zero is used as a separator between characters.
- These values are transmitted to the shader, updating hidden integer parameters (_Val0 to _Val27).
-
Transmission Flow:
- The encoder sends a start signal (the length of the string).
- Each character’s ASCII value is sent with a delay, ensuring the receiver has enough time to process each value.
- After the complete string is sent, the encoding process stops.
-
StringDecoder:
- Captures a screenshot of the rendered code (using screen capture utilities).
- Processes the image to detect candidate circles representing code rings.
- Selects the best candidate circle based on color and geometric features.
- Calculates the dimensions (scale, dot size, ring margins) required for decoding.
- Extracts the binary string from the detected rings.
- Converts the binary string into text (8 bits per character).
-
Decoding Flow:
- A screenshot is taken and saved (for debugging purposes).
- The image is filtered (by color) to isolate the code.
- Image processing functions (such as Canny edge detection and contour finding) are used to identify the code.
- The binary bits are assembled and finally converted back into a text string.
The StringEncoder
class is responsible for converting a text string into OSC signals.
-
RegisterParameters
Registers parameters for encoding:-
EncodingParameter.CharIn
(integer) – Sends ASCII values. -
EncodingParameter.Touching
(boolean) – Indicates if encoding is active. -
EncodingParameter.Ready
(boolean) – Indicates readiness to receive.
-
-
SendString
Sends a string by:- Transmitting the string length.
- Sending each character's ASCII value followed by a zero.
- Introducing delays (
MillisecondsDelay
) between transmissions.
// Initialize the encoder with your EncodingUtilities instance.
StringEncoder encoder = new StringEncoder(encodingUtilities, CreateCustomSetting, CreateToggle);
// Register required parameters.
encoder.RegisterParameters(RegisterIntParameter, RegisterBoolParameter);
// Send a message.
encoder.SendString("Hello, VRChat!", false, (param, value) => {
// Implement your OSC parameter update logic here.
// For example: OSCClient.Send(param.ToString(), value);
});
The StringDecoder
class decodes the visual code from a captured screenshot.
-
OnModuleStart
Initializes screen capture utilities and creates a debug folder. -
StartDecode
Captures a screenshot, processes it through EmguCV, and callsDecodeText
. -
DecodeText / ProcessImage
Processes the image:- Detects candidate circles (using
GetCandidateCirclesContour
). - Selects the best candidate (using
SelectBestCandidate
). - Computes dimensions (using
ComputeDimensions
). - Decodes the sync ring and data rings to produce a binary string.
- Converts the binary string into text (via
BitsToText
).
- Detects candidate circles (using
// Initialize the decoder.
StringDecoder decoder = new StringDecoder(encodingUtilities);
await decoder.OnModuleStart();
// Start decoding process when ready.
string decodedMessage = decoder.StartDecode();
Console.WriteLine($"Decoded Message: {decodedMessage}");
The EncodingUtilities
class provides utility functions:
- Logging functions (
Log
,LogDebug
). - Functions to retrieve parameters.
- Screen capture utilities to take screenshots.
EncodingUtilities encodingUtilities = new EncodingUtilities
{
IsDebug = true,
Log = Console.WriteLine,
LogDebug = message => Console.WriteLine($"DEBUG: {message}"),
FindParameter = async (paramEnum) => { /* Implement parameter lookup */ return null; },
FindParameterByString = async (paramStr) => { /* Implement parameter lookup */ return null; },
GetSettingValue = (param) => "default",
ScreenUtilities = new ScreenUtilities(/* configuration parameters */)
};
The provided shader renders the visual code based on a set of uniform parameters.
-
Encoded Data:
-
_Val0
to_Val27
: 28 integer inputs holding the encoded ASCII values.
-
-
Layout and Design:
-
_Size
: Internal image size in pixels. -
_DotSize
: Size of each code dot. -
_LogoRadius
,_RingMargin
,_CircleMargin
: Control the geometry of the code. -
_NumRings
: Number of concentric rings.
-
-
Appearance:
-
_CodeColor
,_BackgroundColor
,_GapColor
,_ToggleDotColor
: Colors used in rendering. - Other parameters such as
_BackgroundShape
,_CornerRadius
, etc., control the shape and style of the rendered code.
-
- A prefab is provided with the scannable shader attached.
-
Steps:
-
Attach the Prefab:
- Add the prefab to your VRChat avatar or scene.
-
Configure the Shader:
- In the prefab’s inspector, adjust shader parameters as needed.
- Ensure that OSC messages update the hidden integer properties (_Val0–_Val27).
-
Link with the IDC System:
- The SpotiOSC module sends OSC parameters to the shader, which renders the code that the
StringDecoder
decodes.
- The SpotiOSC module sends OSC parameters to the shader, which renders the code that the
-
Attach the Prefab:
The Sign In window (SignInWindow.xaml
) includes a scrollable content area containing a ScreenUtilitySelector
control. This control has:
- A button to toggle advanced settings.
- A wrap panel that shows display previews.
- A ComboBox for selecting the GPU with a dark-themed dropdown.
Example XAML Snippet:
<Window x:Class="YourModuleNameSpace"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:su="clr-namespace:YeusepesModules.Common.ScreenUtilities"
Title="Sign In"
Height="800"
Width="700"
Background="#121212">
<!-- Make everything scrollable -->
<ScrollViewer VerticalScrollBarVisibility="Auto"
HorizontalScrollBarVisibility="Disabled">
<StackPanel Name="MainGrid"
Margin="20, 50, 20, 20"
Orientation="Vertical">
<su:ScreenUtilitySelector x:Name="ScreenSelector" />
</StackPanel>
</ScrollViewer>
</Window>
The ScreenUtilitySelector
user control (written in XAML and C#) handles:
- Toggling the advanced settings panel.
- Refreshing available GPUs and displays.
- Updating live display previews via a delegate (
LiveCaptureProvider
). - Raising events when the GPU or display selection changes.
Key Methods in the Code-Behind:
-
AdvancedSettingsButton_Click:
Toggles the visibility of the advanced settings panel and refreshes the display and GPU lists. -
RefreshLists:
Updates the GPU ComboBox and display WrapPanel with current options. -
SetSelectedDisplay / SetSelectedGPU:
Updates dependency properties so that external modules can bind to these settings.
This settings window is built with generic principles:
-
Modularity:
TheScreenUtilitySelector
is self-contained and can be reused in any module requiring display or GPU selection. -
Event-Driven Design:
External code can subscribe to events (e.g.,GPUSelectionChanged
andDisplaySelectionChanged
) to react to user changes. -
Customizable Layout:
The use of dependency properties allows for easy integration with various module settings frameworks. -
Live and Dummy Data:
With theLiveCaptureProvider
delegate, you can provide real-time previews or fallback to dummy data if needed.
Example Code for Refreshing Settings:
public void RefreshLists(IEnumerable<string> availableGPUs, IEnumerable<string> availableDisplays)
{
UpdateUI(() =>
{
GPUComboBox.ItemsSource = availableGPUs;
LoadDisplays(availableDisplays.ToList());
UpdateDisplayPreviews();
});
}
Integrating these settings into your module involves:
- Initializing the settings window during module startup.
- Binding module-specific settings (such as default GPU or display) to the dependency properties.
- Subscribing to selection-changed events to update your module’s configuration accordingly.
// Example: Sending "Test123" via the StringEncoder.
StringEncoder encoder = new StringEncoder(encodingUtilities, CreateCustomSetting, CreateToggle);
encoder.RegisterParameters(RegisterIntParameter, RegisterBoolParameter);
encoder.SendString("Test123", false, (param, value) => {
OSCClient.Send(param.ToString(), value);
});
// Example: Decoding the visual code into text.
StringDecoder decoder = new StringDecoder(encodingUtilities);
await decoder.OnModuleStart();
string receivedMessage = decoder.StartDecode();
Console.WriteLine($"Received Message: {receivedMessage}");
// Example: Updating the shader parameters with encoded data.
for (int i = 0; i < 28; i++) {
string paramName = $"_Val{i}";
int value = GetEncodedValueForByte(i); // Your method to retrieve the encoded byte.
UpdateShaderParameter(paramName, value);
}
-
Debug Logging:
Both the encoder and decoder useencodingUtilities.LogDebug
to output debug information. Use these logs to trace the encoding/decoding process. -
Saved Debug Images:
TheStringDecoder
saves screenshots and processed images (e.g., candidate circles, final distance maps) in a debug folder located in the user's Pictures directory. (IF AND ONLY IF THE SETTING Debug->Enable Debugging IS ON) -
Troubleshooting Tips:
- Ensure that screen capture utilities work correctly.
- Check if the candidate circles are being detected properly.
- Adjust thresholds in
GetThresholdValue
if the binary conversion is inaccurate.
Reach out to the discord for support.
Happy coding!