summaryrefslogtreecommitdiffstats
path: root/Scripts
diff options
context:
space:
mode:
authoryum <yum.food.vr@gmail.com>2025-10-20 13:43:54 -0700
committeryum <yum.food.vr@gmail.com>2025-10-20 13:43:54 -0700
commit630573cb0b4db15cbe155d674bf2c0f61d0f86b6 (patch)
tree6bccc3b88eb67818d3d37f1d5155fbb92fd8d6b4 /Scripts
parentb0a200beca94c27388e4d2df0fff04332941329e (diff)
simplify decoder; note that you need to use an srgb texture with it
Diffstat (limited to 'Scripts')
-rw-r--r--Scripts/DataDecoder.asset118
-rw-r--r--Scripts/DataDecoder.cs327
2 files changed, 78 insertions, 367 deletions
diff --git a/Scripts/DataDecoder.asset b/Scripts/DataDecoder.asset
index 264a519..ddfcdfe 100644
--- a/Scripts/DataDecoder.asset
+++ b/Scripts/DataDecoder.asset
@@ -44,7 +44,7 @@ MonoBehaviour:
Data:
- Name:
Entry: 12
- Data: 8
+ Data: 7
- Name:
Entry: 7
Data:
@@ -158,25 +158,19 @@ MonoBehaviour:
Data:
- Name: $k
Entry: 1
- Data: isValid
+ Data: tileToCheck
- Name: $v
Entry: 7
Data: 8|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor
- Name: <Name>k__BackingField
Entry: 1
- Data: isValid
+ Data: tileToCheck
- Name: <UserType>k__BackingField
- Entry: 7
- Data: 9|System.RuntimeType, mscorlib
- - Name:
- Entry: 1
- Data: System.Boolean, mscorlib
- - Name:
- Entry: 8
- Data:
+ Entry: 9
+ Data: 6
- Name: <SystemType>k__BackingField
Entry: 9
- Data: 9
+ Data: 6
- Name: <SyncMode>k__BackingField
Entry: 7
Data: System.Nullable`1[[UdonSharp.UdonSyncMode, UdonSharp.Runtime]], mscorlib
@@ -188,10 +182,10 @@ MonoBehaviour:
Data:
- Name: <IsSerialized>k__BackingField
Entry: 5
- Data: false
+ Data: true
- Name: _fieldAttributes
Entry: 7
- Data: 10|System.Collections.Generic.List`1[[System.Attribute, mscorlib]], mscorlib
+ Data: 9|System.Collections.Generic.List`1[[System.Attribute, mscorlib]], mscorlib
- Name:
Entry: 12
Data: 0
@@ -212,25 +206,25 @@ MonoBehaviour:
Data:
- Name: $k
Entry: 1
- Data: pixelDataFloat
+ Data: pixelData
- Name: $v
Entry: 7
- Data: 11|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor
+ Data: 10|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor
- Name: <Name>k__BackingField
Entry: 1
- Data: pixelDataFloat
+ Data: pixelData
- Name: <UserType>k__BackingField
Entry: 7
- Data: 12|System.RuntimeType, mscorlib
+ Data: 11|System.RuntimeType, mscorlib
- Name:
Entry: 1
- Data: UnityEngine.Color[], UnityEngine.CoreModule
+ Data: UnityEngine.Color32[], UnityEngine.CoreModule
- Name:
Entry: 8
Data:
- Name: <SystemType>k__BackingField
Entry: 9
- Data: 12
+ Data: 11
- Name: <SyncMode>k__BackingField
Entry: 7
Data: System.Nullable`1[[UdonSharp.UdonSyncMode, UdonSharp.Runtime]], mscorlib
@@ -245,7 +239,7 @@ MonoBehaviour:
Data: false
- Name: _fieldAttributes
Entry: 7
- Data: 13|System.Collections.Generic.List`1[[System.Attribute, mscorlib]], mscorlib
+ Data: 12|System.Collections.Generic.List`1[[System.Attribute, mscorlib]], mscorlib
- Name:
Entry: 12
Data: 0
@@ -266,73 +260,25 @@ MonoBehaviour:
Data:
- Name: $k
Entry: 1
- Data: pixelDataBytes
+ Data: hasData
- Name: $v
Entry: 7
- Data: 14|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor
+ Data: 13|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor
- Name: <Name>k__BackingField
Entry: 1
- Data: pixelDataBytes
+ Data: hasData
- Name: <UserType>k__BackingField
Entry: 7
- Data: 15|System.RuntimeType, mscorlib
+ Data: 14|System.RuntimeType, mscorlib
- Name:
Entry: 1
- Data: UnityEngine.Color32[], UnityEngine.CoreModule
- - Name:
- Entry: 8
- Data:
- - Name: <SystemType>k__BackingField
- Entry: 9
- Data: 15
- - Name: <SyncMode>k__BackingField
- Entry: 7
- Data: System.Nullable`1[[UdonSharp.UdonSyncMode, UdonSharp.Runtime]], mscorlib
- - Name:
- Entry: 6
- Data:
- - Name:
- Entry: 8
- Data:
- - Name: <IsSerialized>k__BackingField
- Entry: 5
- Data: false
- - Name: _fieldAttributes
- Entry: 7
- Data: 16|System.Collections.Generic.List`1[[System.Attribute, mscorlib]], mscorlib
- - Name:
- Entry: 12
- Data: 0
- - Name:
- Entry: 13
- Data:
- - Name:
- Entry: 8
- Data:
- - Name:
- Entry: 8
- Data:
+ Data: System.Boolean, mscorlib
- Name:
Entry: 8
Data:
- - Name:
- Entry: 7
- Data:
- - Name: $k
- Entry: 1
- Data: hasData
- - Name: $v
- Entry: 7
- Data: 17|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor
- - Name: <Name>k__BackingField
- Entry: 1
- Data: hasData
- - Name: <UserType>k__BackingField
- Entry: 9
- Data: 9
- Name: <SystemType>k__BackingField
Entry: 9
- Data: 9
+ Data: 14
- Name: <SyncMode>k__BackingField
Entry: 7
Data: System.Nullable`1[[UdonSharp.UdonSyncMode, UdonSharp.Runtime]], mscorlib
@@ -347,7 +293,7 @@ MonoBehaviour:
Data: false
- Name: _fieldAttributes
Entry: 7
- Data: 18|System.Collections.Generic.List`1[[System.Attribute, mscorlib]], mscorlib
+ Data: 15|System.Collections.Generic.List`1[[System.Attribute, mscorlib]], mscorlib
- Name:
Entry: 12
Data: 0
@@ -368,19 +314,19 @@ MonoBehaviour:
Data:
- Name: $k
Entry: 1
- Data: hasReportedTileLimit
+ Data: readWidth
- Name: $v
Entry: 7
- Data: 19|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor
+ Data: 16|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor
- Name: <Name>k__BackingField
Entry: 1
- Data: hasReportedTileLimit
+ Data: readWidth
- Name: <UserType>k__BackingField
Entry: 9
- Data: 9
+ Data: 6
- Name: <SystemType>k__BackingField
Entry: 9
- Data: 9
+ Data: 6
- Name: <SyncMode>k__BackingField
Entry: 7
Data: System.Nullable`1[[UdonSharp.UdonSyncMode, UdonSharp.Runtime]], mscorlib
@@ -395,7 +341,7 @@ MonoBehaviour:
Data: false
- Name: _fieldAttributes
Entry: 7
- Data: 20|System.Collections.Generic.List`1[[System.Attribute, mscorlib]], mscorlib
+ Data: 17|System.Collections.Generic.List`1[[System.Attribute, mscorlib]], mscorlib
- Name:
Entry: 12
Data: 0
@@ -416,13 +362,13 @@ MonoBehaviour:
Data:
- Name: $k
Entry: 1
- Data: currentPixelFormat
+ Data: readHeight
- Name: $v
Entry: 7
- Data: 21|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor
+ Data: 18|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor
- Name: <Name>k__BackingField
Entry: 1
- Data: currentPixelFormat
+ Data: readHeight
- Name: <UserType>k__BackingField
Entry: 9
Data: 6
@@ -443,7 +389,7 @@ MonoBehaviour:
Data: false
- Name: _fieldAttributes
Entry: 7
- Data: 22|System.Collections.Generic.List`1[[System.Attribute, mscorlib]], mscorlib
+ Data: 19|System.Collections.Generic.List`1[[System.Attribute, mscorlib]], mscorlib
- Name:
Entry: 12
Data: 0
diff --git a/Scripts/DataDecoder.cs b/Scripts/DataDecoder.cs
index 52a7103..810c46a 100644
--- a/Scripts/DataDecoder.cs
+++ b/Scripts/DataDecoder.cs
@@ -1,7 +1,5 @@
using UdonSharp;
using UnityEngine;
-using VRC.SDKBase;
-using VRC.Udon;
using VRC.SDK3.Rendering;
using VRC.Udon.Common.Interfaces;
@@ -9,322 +7,89 @@ public class DataDecoder : UdonSharpBehaviour
{
public RenderTexture sourceTexture;
public int tileSize = 8;
- private const int MaxTiles = 8192;
+ public int tileToCheck = 0;
- private bool isValid;
- private Color[] pixelDataFloat;
- private Color32[] pixelDataBytes;
+ private Color32[] pixelData;
private bool hasData = false;
- private bool hasReportedTileLimit;
-
- private const int PixelFormatNone = 0;
- private const int PixelFormatColor32 = 1;
- private const int PixelFormatFloat = 2;
-
- private int currentPixelFormat = PixelFormatNone;
+ private int readWidth;
+ private int readHeight;
void Start() {}
- private bool IsValid()
- {
- if (sourceTexture == null) {
- Debug.LogWarning("DataDecoder: sourceTexture missing");
- return false;
- }
- if (tileSize <= 0) {
- Debug.LogWarning("DataDecoder: invalid tile size");
- return false;
- }
- return true;
- }
-
void Update()
{
- // Only call IsValid() until it succeeds, then cache.
- if (!isValid)
- {
- isValid = IsValid();
- if (!isValid) {
- return;
- }
- }
+ if (sourceTexture == null || tileSize <= 0) return;
- int pixelCount = sourceTexture.width * sourceTexture.height;
- if (pixelDataBytes == null || pixelCount != pixelDataBytes.Length)
- {
- Debug.Log($"DataDecoder: allocating Color32 buffer for {pixelCount} pixels");
- pixelDataBytes = new Color32[pixelCount];
- }
+ // TODO get more than one column
+ int requestWidth = Mathf.Min(tileSize, sourceTexture.width);
+ int requestHeight = sourceTexture.height;
+ int pixelCount = requestWidth * requestHeight;
+
+ if (pixelCount <= 0) return;
- if (pixelDataFloat == null || pixelCount != pixelDataFloat.Length)
+ if (pixelData == null || pixelCount != pixelData.Length)
{
- pixelDataFloat = new Color[pixelCount];
+ pixelData = new Color32[pixelCount];
+ hasData = false;
}
- // Request readback every frame (multiple requests can be in flight)
- VRCAsyncGPUReadback.Request(sourceTexture, 0, (IUdonEventReceiver)this);
+ readWidth = requestWidth;
+ readHeight = requestHeight;
+
+ VRCAsyncGPUReadback.Request(sourceTexture,
+ 0,
+ 0, readWidth,
+ 0, readHeight,
+ 0, 1,
+ (IUdonEventReceiver)this);
- // Process data if available
if (hasData)
{
- DecodeData(tileSize);
+ ProcessTiles();
hasData = false;
}
}
public override void OnAsyncGpuReadbackComplete(VRCAsyncGPUReadbackRequest request)
{
- if (request.hasError)
- {
- Debug.LogError("DataDecoder: GPU readback error");
- return;
- }
-
- // Store the pixel data (overwrites previous data with latest)
- if (pixelDataBytes != null && request.TryGetData(pixelDataBytes))
- {
- currentPixelFormat = PixelFormatColor32;
- hasData = true;
- return;
- }
+ if (request.hasError) return;
- if (pixelDataFloat != null && request.TryGetData(pixelDataFloat))
+ if (pixelData != null && request.TryGetData(pixelData))
{
- currentPixelFormat = PixelFormatFloat;
hasData = true;
- return;
}
-
- Debug.LogWarning("DataDecoder: Unable to read GPU data into any supported buffer format");
}
- /// <summary>
- /// Prints the RGB values of the first 4 tiles for debugging purposes.
- /// </summary>
- private void PrintFirst4Tiles(int tileSize, int tilesPerColumn)
+ private void ProcessTiles()
{
- int tilesPerRow = (sourceTexture.width + tileSize - 1) / tileSize;
- int totalTiles = Mathf.Max(Mathf.Min(tilesPerColumn * tilesPerRow, MaxTiles), 0);
-
- if (totalTiles == 0)
- {
- Debug.Log("DataDecoder: tiles=<none>");
- return;
- }
-
- int tilesToLog = Mathf.Min(4, totalTiles);
- string log = "DataDecoder: tiles=";
-
- for (int tileIdx = 0; tileIdx < tilesToLog; tileIdx++)
- {
- GetTileRGB(tileIdx, tileSize, tilesPerColumn, out byte r, out byte g, out byte b);
- log += $"[{tileIdx}:{r},{g},{b}]";
- if (tileIdx < tilesToLog - 1)
- {
- log += " ";
- }
- }
-
- if (totalTiles > tilesToLog)
- {
- log += " ...";
- }
-
- Debug.Log(log);
- }
-
- /// <summary>
- /// Decodes data from a texture where the data is encoded in RGB channels of tiles.
- /// The first tile contains the length (number of bytes to decode).
- /// Subsequent tiles contain the actual data.
- /// </summary>
- /// <param name="tileSize">The size of each tile (e.g., 8 for 8x8 tiles)</param>
- /// <returns>An array of decoded bytes</returns>
- public byte[] DecodeData(int tileSize)
- {
- if (!HasPixelData())
- {
- Debug.LogWarning("DataDecoder: No pixel data available");
- return new byte[0];
- }
-
- int tilesPerColumn = (sourceTexture.height + tileSize - 1) / tileSize;
- int tilesPerRow = (sourceTexture.width + tileSize - 1) / tileSize;
-
- PrintFirst4Tiles(tileSize, tilesPerColumn);
+ if (pixelData == null || readWidth <= 0 || readHeight <= 0) return;
- // Read the length from the first tile
- int length = ReadLength(tileSize, tilesPerColumn);
- //Debug.Log($"Parsed length: {length}");
- if (length <= 0) {
- Debug.LogWarning($"DataDecoder: Length is 0 or negative: {length}");
- return new byte[0];
- }
-
- // Calculate how many data tiles we need (excluding the length tile)
- int bytesPerTile = 3; // RGB from center pixel only
- int tilesNeeded = (length + bytesPerTile - 1) / bytesPerTile; // Ceiling division
-
- // Allocate result array
- byte[] data = new byte[length];
-
- // Calculate tiles per column
- // tilesPerColumn/Row calculated above
-
- if (tilesPerColumn <= 0 || tilesPerRow <= 0)
- {
- Debug.LogError("DataDecoder: Texture dimensions smaller than tile size");
- return new byte[0];
- }
-
- int totalTilesRaw = tilesPerColumn * tilesPerRow;
- int totalTiles = Mathf.Min(totalTilesRaw, MaxTiles);
-
- if (totalTilesRaw <= MaxTiles)
- {
- hasReportedTileLimit = false;
- }
- else if (!hasReportedTileLimit)
- {
- Debug.LogWarning($"DataDecoder: Limiting decoding to first {MaxTiles} tiles (of {totalTilesRaw})");
- hasReportedTileLimit = true;
- }
-
- // Read data from each tile (starting after the length tile)
- int byteIndex = 0;
- for (int tileIdx = 0; tileIdx < tilesNeeded && byteIndex < length; tileIdx++)
- {
- // Skip tile 0 (the length tile) by using actualTileIdx
- int actualTileIdx = tileIdx + 1;
+ int tilesPerColumn = (int) Mathf.Floor(readHeight / tileSize);
- if (actualTileIdx >= totalTiles)
- {
- Debug.LogWarning($"DataDecoder: Ran out of tiles at index {actualTileIdx} while decoding {length} bytes");
- break;
- }
-
- // Get RGB bytes from this tile
- GetTileRGB(actualTileIdx, tileSize, tilesPerColumn, out byte r, out byte g, out byte b);
-
- // Write bytes to output array
- if (byteIndex < length) data[byteIndex++] = r;
- if (byteIndex < length) data[byteIndex++] = g;
- if (byteIndex < length) data[byteIndex++] = b;
- }
-
- return data;
+ GetTileRGB(tileToCheck, out int r, out int g, out int b);
+ Debug.Log($"Tile {tileToCheck}: R={r}, G={g}, B={b}");
}
- /// <summary>
- /// Gets the RGB bytes from the center pixel of the nth tile.
- /// Tiles are indexed in column-major order (tile 0 is top-left).
- /// </summary>
- /// <param name="tileIndex">The index of the tile (0-based)</param>
- /// <param name="tileSize">The size of each tile</param>
- /// <param name="r">Output: Red channel as byte (0-255)</param>
- /// <param name="g">Output: Green channel as byte (0-255)</param>
- /// <param name="b">Output: Blue channel as byte (0-255)</param>
- /// <returns>The center pixel color, or Color.clear if invalid data detected</returns>
- private Color GetTileRGB(int tileIndex, int tileSize, int tilesPerColumn, out byte r, out byte g, out byte b)
+ private void GetTileRGB(int tileIndex, out int r, out int g, out int b)
{
- // Calculate tile position (column-major order)
- int columnIdx = tileIndex / tilesPerColumn;
- int rowIdx = tileIndex % tilesPerColumn;
+ r = 0;
+ g = 0;
+ b = 0;
- int tileX = columnIdx * tileSize;
- int tileY = rowIdx * tileSize;
+ int tileY = tileIndex * tileSize;
+ int centerY = tileY + tileSize / 2;
- int xStart = tileX;
- int xEnd = Mathf.Min(tileX + tileSize, sourceTexture.width);
- int yStart = tileY;
- int yEnd = Mathf.Min(tileY + tileSize, sourceTexture.height);
+ if (centerY >= readHeight) return;
- if (xStart >= xEnd || yStart >= yEnd)
- {
- r = 0;
- g = 0;
- b = 0;
- return Color.clear;
- }
-
- // Sample roughly from the tile center, clamped to the valid area.
- int centerX = Mathf.Clamp(tileX + tileSize / 2, xStart, xEnd - 1);
- int centerY = Mathf.Clamp(tileY + tileSize / 2, yStart, yEnd - 1);
- int sampleY = Mathf.Clamp(sourceTexture.height - 1 - centerY, 0, sourceTexture.height - 1);
- int index = sampleY * sourceTexture.width + centerX;
-
- switch (currentPixelFormat)
- {
- case PixelFormatColor32:
- {
- Color32 c32 = pixelDataBytes[index];
- r = c32.r;
- g = c32.g;
- b = c32.b;
- return new Color(c32.r / 255f, c32.g / 255f, c32.b / 255f, c32.a / 255f);
- }
- case PixelFormatFloat:
- {
- Color pixel = pixelDataFloat[index];
-
- // Check for invalid data
- if (float.IsNaN(pixel.r) || float.IsInfinity(pixel.r) ||
- float.IsNaN(pixel.g) || float.IsInfinity(pixel.g) ||
- float.IsNaN(pixel.b) || float.IsInfinity(pixel.b))
- {
- Debug.LogWarning($"DataDecoder: Invalid pixel data detected in tile {tileIndex}: {pixel}");
- r = 0;
- g = 0;
- b = 0;
- return Color.clear;
- }
-
- // Convert float (0-1) to bytes (0-255), clamping to prevent overflow
- r = FloatToByte(pixel.r);
- g = FloatToByte(pixel.g);
- b = FloatToByte(pixel.b);
- return pixel;
- }
- default:
- r = 0;
- g = 0;
- b = 0;
- return Color.clear;
- }
- }
-
- /// <summary>
- /// Reads the length value from the center pixel of the first tile (at position 0, 0).
- /// The length is stored as a 24-bit integer (3 bytes) in the RGB channels.
- /// </summary>
- private int ReadLength(int tileSize, int tilesPerColumn)
- {
- // Get RGB bytes from the first tile (tile 0)
- // GetTileRGB handles NaN/infinity checks internally
- GetTileRGB(0, tileSize, tilesPerColumn, out byte r, out byte g, out byte b);
+ int localX = readWidth / 2;
+ int localY = readHeight - 1 - centerY;
+ int index = localY * readWidth + localX;
- // Convert bytes to int (little-endian, 24-bit)
- int length = r | (g << 8) | (b << 16);
+ if (index < 0 || index >= pixelData.Length) return;
- return length;
+ Color32 c = pixelData[index];
+ r = c.r;
+ g = c.g;
+ b = c.b;
}
-
- private bool HasPixelData()
- {
- switch (currentPixelFormat)
- {
- case PixelFormatColor32:
- return pixelDataBytes != null && pixelDataBytes.Length > 0;
- case PixelFormatFloat:
- return pixelDataFloat != null && pixelDataFloat.Length > 0;
- default:
- return false;
- }
- }
-
- private byte FloatToByte(float value)
- {
- return (byte)Mathf.Clamp(Mathf.RoundToInt(value * 255f), 0, 255);
- }
-
}