IDC - Yeusepe/Yeusepes-Modules GitHub Wiki

IDC System Developer Guide

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.

Table of Contents

  1. Introduction
  2. System Overview
  3. Detailed Code Walkthrough
  4. Shader Integration and Prefab Setup
  5. Examples and Code Snippets
  6. Debugging and Images
  7. Conclusion

Introduction

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.

System Overview

Encoding and Sending Messages

  1. 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).
  2. 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.

Decoding and Receiving Messages

  1. 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).
  2. 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.

Detailed Code Walkthrough

StringEncoder

The StringEncoder class is responsible for converting a text string into OSC signals.

Key Functions:

  • 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.

Example Usage:

// 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);
});

StringDecoder

The StringDecoder class decodes the visual code from a captured screenshot.

Key Functions:

  • OnModuleStart
    Initializes screen capture utilities and creates a debug folder.

  • StartDecode
    Captures a screenshot, processes it through EmguCV, and calls DecodeText.

  • 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).

Example Usage:

// 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}");

EncodingUtilities

The EncodingUtilities class provides utility functions:

  • Logging functions (Log, LogDebug).
  • Functions to retrieve parameters.
  • Screen capture utilities to take screenshots.

Example:

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 */)
};

Shader Integration and Prefab Setup

Shader: "Yeusepe/Scannable"

The provided shader renders the visual code based on a set of uniform parameters.

Key Shader 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.

Prefab Setup

  • A prefab is provided with the scannable shader attached.
  • Steps:
    1. Attach the Prefab:
      • Add the prefab to your VRChat avatar or scene.
    2. Configure the Shader:
      • In the prefab’s inspector, adjust shader parameters as needed.
      • Ensure that OSC messages update the hidden integer properties (_Val0–_Val27).
    3. Link with the IDC System:
      • The SpotiOSC module sends OSC parameters to the shader, which renders the code that the StringDecoder decodes.

image


XAML Layout and Controls

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>

Code-Behind and Event Handling

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.

Extensibility for Future Modules

This settings window is built with generic principles:

  • Modularity:
    The ScreenUtilitySelector 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 and DisplaySelectionChanged) 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 the LiveCaptureProvider 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:

  1. Initializing the settings window during module startup.
  2. Binding module-specific settings (such as default GPU or display) to the dependency properties.
  3. Subscribing to selection-changed events to update your module’s configuration accordingly.

Examples and Code Snippets

Sending a Message (Encoding)

// 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);
});

Receiving a Message (Decoding)

// Example: Decoding the visual code into text.
StringDecoder decoder = new StringDecoder(encodingUtilities);
await decoder.OnModuleStart();
string receivedMessage = decoder.StartDecode();
Console.WriteLine($"Received Message: {receivedMessage}");

Integrating with the Shader

// 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);
}

Debugging and Images

  • Debug Logging:
    Both the encoder and decoder use encodingUtilities.LogDebug to output debug information. Use these logs to trace the encoding/decoding process.

  • Saved Debug Images:
    The StringDecoder 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) data_hits_debug_20250317_191703983

  • 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.

Where can I get help?

Reach out to the discord for support.

Happy coding!

⚠️ **GitHub.com Fallback** ⚠️