summaryrefslogtreecommitdiffstats
path: root/Scripts
diff options
context:
space:
mode:
Diffstat (limited to 'Scripts')
-rw-r--r--Scripts/DataDecoder.asset467
-rw-r--r--Scripts/DataDecoder.asset.meta8
-rw-r--r--Scripts/DataDecoder.cs330
-rw-r--r--Scripts/DataDecoder.cs.meta11
4 files changed, 816 insertions, 0 deletions
diff --git a/Scripts/DataDecoder.asset b/Scripts/DataDecoder.asset
new file mode 100644
index 0000000..264a519
--- /dev/null
+++ b/Scripts/DataDecoder.asset
@@ -0,0 +1,467 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!114 &11400000
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 0}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: c333ccfdd0cbdbc4ca30cef2dd6e6b9b, type: 3}
+ m_Name: DataDecoder
+ m_EditorClassIdentifier:
+ serializedUdonProgramAsset: {fileID: 11400000, guid: b63b58ca1fad83a46a36891e4e44e019,
+ type: 2}
+ udonAssembly:
+ assemblyError:
+ sourceCsScript: {fileID: 11500000, guid: 708447eb5654d224286452750a06172e, type: 3}
+ scriptVersion: 2
+ compiledVersion: 2
+ behaviourSyncMode: 0
+ hasInteractEvent: 0
+ scriptID: -6195429949765824410
+ serializationData:
+ SerializedFormat: 2
+ SerializedBytes:
+ ReferencedUnityObjects: []
+ SerializedBytesString:
+ Prefab: {fileID: 0}
+ PrefabModificationsReferencedUnityObjects: []
+ PrefabModifications: []
+ SerializationNodes:
+ - Name: fieldDefinitions
+ Entry: 7
+ Data: 0|System.Collections.Generic.Dictionary`2[[System.String, mscorlib],[UdonSharp.Compiler.FieldDefinition,
+ UdonSharp.Editor]], mscorlib
+ - Name: comparer
+ Entry: 7
+ Data: 1|System.Collections.Generic.GenericEqualityComparer`1[[System.String,
+ mscorlib]], mscorlib
+ - Name:
+ Entry: 8
+ Data:
+ - Name:
+ Entry: 12
+ Data: 8
+ - Name:
+ Entry: 7
+ Data:
+ - Name: $k
+ Entry: 1
+ Data: sourceTexture
+ - Name: $v
+ Entry: 7
+ Data: 2|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor
+ - Name: <Name>k__BackingField
+ Entry: 1
+ Data: sourceTexture
+ - Name: <UserType>k__BackingField
+ Entry: 7
+ Data: 3|System.RuntimeType, mscorlib
+ - Name:
+ Entry: 1
+ Data: UnityEngine.RenderTexture, UnityEngine.CoreModule
+ - Name:
+ Entry: 8
+ Data:
+ - Name: <SystemType>k__BackingField
+ Entry: 9
+ Data: 3
+ - 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: true
+ - Name: _fieldAttributes
+ Entry: 7
+ Data: 4|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:
+ - Name:
+ Entry: 8
+ Data:
+ - Name:
+ Entry: 7
+ Data:
+ - Name: $k
+ Entry: 1
+ Data: tileSize
+ - Name: $v
+ Entry: 7
+ Data: 5|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor
+ - Name: <Name>k__BackingField
+ Entry: 1
+ Data: tileSize
+ - Name: <UserType>k__BackingField
+ Entry: 7
+ Data: 6|System.RuntimeType, mscorlib
+ - Name:
+ Entry: 1
+ Data: System.Int32, mscorlib
+ - Name:
+ Entry: 8
+ Data:
+ - Name: <SystemType>k__BackingField
+ Entry: 9
+ Data: 6
+ - 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: true
+ - Name: _fieldAttributes
+ Entry: 7
+ Data: 7|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:
+ - Name:
+ Entry: 8
+ Data:
+ - Name:
+ Entry: 7
+ Data:
+ - Name: $k
+ Entry: 1
+ Data: isValid
+ - Name: $v
+ Entry: 7
+ Data: 8|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor
+ - Name: <Name>k__BackingField
+ Entry: 1
+ Data: isValid
+ - Name: <UserType>k__BackingField
+ Entry: 7
+ Data: 9|System.RuntimeType, mscorlib
+ - Name:
+ Entry: 1
+ Data: System.Boolean, mscorlib
+ - Name:
+ Entry: 8
+ Data:
+ - Name: <SystemType>k__BackingField
+ Entry: 9
+ Data: 9
+ - 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: 10|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:
+ - Name:
+ Entry: 8
+ Data:
+ - Name:
+ Entry: 7
+ Data:
+ - Name: $k
+ Entry: 1
+ Data: pixelDataFloat
+ - Name: $v
+ Entry: 7
+ Data: 11|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor
+ - Name: <Name>k__BackingField
+ Entry: 1
+ Data: pixelDataFloat
+ - Name: <UserType>k__BackingField
+ Entry: 7
+ Data: 12|System.RuntimeType, mscorlib
+ - Name:
+ Entry: 1
+ Data: UnityEngine.Color[], UnityEngine.CoreModule
+ - Name:
+ Entry: 8
+ Data:
+ - Name: <SystemType>k__BackingField
+ Entry: 9
+ Data: 12
+ - 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: 13|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:
+ - Name:
+ Entry: 8
+ Data:
+ - Name:
+ Entry: 7
+ Data:
+ - Name: $k
+ Entry: 1
+ Data: pixelDataBytes
+ - Name: $v
+ Entry: 7
+ Data: 14|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor
+ - Name: <Name>k__BackingField
+ Entry: 1
+ Data: pixelDataBytes
+ - Name: <UserType>k__BackingField
+ Entry: 7
+ Data: 15|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:
+ - 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
+ - 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: 18|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:
+ - Name:
+ Entry: 8
+ Data:
+ - Name:
+ Entry: 7
+ Data:
+ - Name: $k
+ Entry: 1
+ Data: hasReportedTileLimit
+ - Name: $v
+ Entry: 7
+ Data: 19|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor
+ - Name: <Name>k__BackingField
+ Entry: 1
+ Data: hasReportedTileLimit
+ - Name: <UserType>k__BackingField
+ Entry: 9
+ Data: 9
+ - Name: <SystemType>k__BackingField
+ Entry: 9
+ Data: 9
+ - 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: 20|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:
+ - Name:
+ Entry: 8
+ Data:
+ - Name:
+ Entry: 7
+ Data:
+ - Name: $k
+ Entry: 1
+ Data: currentPixelFormat
+ - Name: $v
+ Entry: 7
+ Data: 21|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor
+ - Name: <Name>k__BackingField
+ Entry: 1
+ Data: currentPixelFormat
+ - Name: <UserType>k__BackingField
+ Entry: 9
+ Data: 6
+ - Name: <SystemType>k__BackingField
+ Entry: 9
+ Data: 6
+ - 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: 22|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:
+ - Name:
+ Entry: 8
+ Data:
+ - Name:
+ Entry: 13
+ Data:
+ - Name:
+ Entry: 8
+ Data:
diff --git a/Scripts/DataDecoder.asset.meta b/Scripts/DataDecoder.asset.meta
new file mode 100644
index 0000000..25606cf
--- /dev/null
+++ b/Scripts/DataDecoder.asset.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: b540c0dc6ec15744f82ff733ef9686c5
+NativeFormatImporter:
+ externalObjects: {}
+ mainObjectFileID: 11400000
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Scripts/DataDecoder.cs b/Scripts/DataDecoder.cs
new file mode 100644
index 0000000..52a7103
--- /dev/null
+++ b/Scripts/DataDecoder.cs
@@ -0,0 +1,330 @@
+using UdonSharp;
+using UnityEngine;
+using VRC.SDKBase;
+using VRC.Udon;
+using VRC.SDK3.Rendering;
+using VRC.Udon.Common.Interfaces;
+
+public class DataDecoder : UdonSharpBehaviour
+{
+ public RenderTexture sourceTexture;
+ public int tileSize = 8;
+ private const int MaxTiles = 8192;
+
+ private bool isValid;
+ private Color[] pixelDataFloat;
+ private Color32[] pixelDataBytes;
+ 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;
+
+ 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;
+ }
+ }
+
+ 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];
+ }
+
+ if (pixelDataFloat == null || pixelCount != pixelDataFloat.Length)
+ {
+ pixelDataFloat = new Color[pixelCount];
+ }
+
+ // Request readback every frame (multiple requests can be in flight)
+ VRCAsyncGPUReadback.Request(sourceTexture, 0, (IUdonEventReceiver)this);
+
+ // Process data if available
+ if (hasData)
+ {
+ DecodeData(tileSize);
+ 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 (pixelDataFloat != null && request.TryGetData(pixelDataFloat))
+ {
+ 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)
+ {
+ 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);
+
+ // 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;
+
+ 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;
+ }
+
+ /// <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)
+ {
+ // Calculate tile position (column-major order)
+ int columnIdx = tileIndex / tilesPerColumn;
+ int rowIdx = tileIndex % tilesPerColumn;
+
+ int tileX = columnIdx * tileSize;
+ int tileY = rowIdx * tileSize;
+
+ int xStart = tileX;
+ int xEnd = Mathf.Min(tileX + tileSize, sourceTexture.width);
+ int yStart = tileY;
+ int yEnd = Mathf.Min(tileY + tileSize, sourceTexture.height);
+
+ 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);
+
+ // Convert bytes to int (little-endian, 24-bit)
+ int length = r | (g << 8) | (b << 16);
+
+ return length;
+ }
+
+ 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);
+ }
+
+}
diff --git a/Scripts/DataDecoder.cs.meta b/Scripts/DataDecoder.cs.meta
new file mode 100644
index 0000000..38f5d72
--- /dev/null
+++ b/Scripts/DataDecoder.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 708447eb5654d224286452750a06172e
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant: