diff options
Diffstat (limited to 'Scripts')
| -rw-r--r-- | Scripts/DataDecoder.asset | 118 | ||||
| -rw-r--r-- | Scripts/DataDecoder.cs | 327 |
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); - } - } |
