Skip to content

Commit 9bcfe04

Browse files
committed
Type raw texture payload variants
1 parent 4e7929b commit 9bcfe04

8 files changed

Lines changed: 226 additions & 75 deletions

File tree

src/PlateauResoniteLink/Application/Importing/TextureImportSource.cs

Lines changed: 169 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,78 @@
88

99
namespace PlateauResoniteLink.Application.Importing;
1010

11-
public enum RawTexturePayloadFormat
11+
public abstract class RawTexturePayload
1212
{
13-
Rgba32 = 0,
14-
RgbaFloat32 = 1,
13+
private protected RawTexturePayload(
14+
int width,
15+
int height,
16+
string? colorProfile,
17+
byte[] bytes)
18+
{
19+
ArgumentOutOfRangeException.ThrowIfNegativeOrZero(width);
20+
ArgumentOutOfRangeException.ThrowIfNegativeOrZero(height);
21+
ArgumentNullException.ThrowIfNull(bytes);
22+
23+
Width = width;
24+
Height = height;
25+
ColorProfile = colorProfile;
26+
Bytes = bytes;
27+
}
28+
29+
public int Width { get; }
30+
31+
public int Height { get; }
32+
33+
public string? ColorProfile { get; }
34+
35+
public byte[] Bytes { get; }
36+
37+
public abstract TResult Match<TResult>(
38+
Func<Rgba32RawTexturePayload, TResult> rgba32,
39+
Func<RgbaFloat32RawTexturePayload, TResult> rgbaFloat32);
1540
}
1641

17-
public sealed record RawTexturePayload(
18-
int Width,
19-
int Height,
20-
string? ColorProfile,
21-
byte[] Bytes,
22-
RawTexturePayloadFormat Format = RawTexturePayloadFormat.Rgba32);
42+
public sealed class Rgba32RawTexturePayload : RawTexturePayload
43+
{
44+
public Rgba32RawTexturePayload(
45+
int width,
46+
int height,
47+
string? colorProfile,
48+
byte[] bytes)
49+
: base(width, height, colorProfile, bytes)
50+
{
51+
}
52+
53+
public override TResult Match<TResult>(
54+
Func<Rgba32RawTexturePayload, TResult> rgba32,
55+
Func<RgbaFloat32RawTexturePayload, TResult> rgbaFloat32)
56+
{
57+
ArgumentNullException.ThrowIfNull(rgba32);
58+
ArgumentNullException.ThrowIfNull(rgbaFloat32);
59+
return rgba32(this);
60+
}
61+
}
62+
63+
public sealed class RgbaFloat32RawTexturePayload : RawTexturePayload
64+
{
65+
public RgbaFloat32RawTexturePayload(
66+
int width,
67+
int height,
68+
string? colorProfile,
69+
byte[] bytes)
70+
: base(width, height, colorProfile, bytes)
71+
{
72+
}
73+
74+
public override TResult Match<TResult>(
75+
Func<Rgba32RawTexturePayload, TResult> rgba32,
76+
Func<RgbaFloat32RawTexturePayload, TResult> rgbaFloat32)
77+
{
78+
ArgumentNullException.ThrowIfNull(rgba32);
79+
ArgumentNullException.ThrowIfNull(rgbaFloat32);
80+
return rgbaFloat32(this);
81+
}
82+
}
2383

2484
public interface ITextureImportSource
2585
{
@@ -32,9 +92,14 @@ public interface ITextureImportSource
3292
long? EstimatedByteLength { get; }
3393
}
3494

35-
internal interface IRawTexturePayloadSource : ITextureImportSource
95+
internal interface IRgba32RawTexturePayloadSource : ITextureImportSource
3696
{
37-
ValueTask<RawTexturePayload> MaterializeRawAsync(CancellationToken cancellationToken);
97+
ValueTask<Rgba32RawTexturePayload> MaterializeRgba32Async(CancellationToken cancellationToken);
98+
}
99+
100+
internal interface IRgbaFloat32RawTexturePayloadSource : ITextureImportSource
101+
{
102+
ValueTask<RgbaFloat32RawTexturePayload> MaterializeRgbaFloat32Async(CancellationToken cancellationToken);
38103
}
39104

40105
internal static class TextureImportSourceMaterializer
@@ -44,17 +109,50 @@ public static ValueTask<RawTexturePayload> MaterializeRawAsync(
44109
CancellationToken cancellationToken)
45110
{
46111
ArgumentNullException.ThrowIfNull(source);
47-
if (source is not IRawTexturePayloadSource rawSource)
112+
if (source is IRgba32RawTexturePayloadSource rgba32Source)
113+
{
114+
return new ValueTask<RawTexturePayload>(MaterializeRgba32AsRawAsync(rgba32Source, cancellationToken));
115+
}
116+
117+
if (source is IRgbaFloat32RawTexturePayloadSource rgbaFloat32Source)
118+
{
119+
return new ValueTask<RawTexturePayload>(MaterializeRgbaFloat32AsRawAsync(rgbaFloat32Source, cancellationToken));
120+
}
121+
122+
throw new InvalidOperationException(
123+
$"Texture import source '{source.GetType().Name}' cannot materialize a raw texture payload.");
124+
125+
static async Task<RawTexturePayload> MaterializeRgba32AsRawAsync(
126+
IRgba32RawTexturePayloadSource source,
127+
CancellationToken cancellationToken)
128+
{
129+
return await source.MaterializeRgba32Async(cancellationToken);
130+
}
131+
132+
static async Task<RawTexturePayload> MaterializeRgbaFloat32AsRawAsync(
133+
IRgbaFloat32RawTexturePayloadSource source,
134+
CancellationToken cancellationToken)
135+
{
136+
return await source.MaterializeRgbaFloat32Async(cancellationToken);
137+
}
138+
}
139+
140+
public static ValueTask<Rgba32RawTexturePayload> MaterializeRgba32Async(
141+
ITextureImportSource source,
142+
CancellationToken cancellationToken)
143+
{
144+
ArgumentNullException.ThrowIfNull(source);
145+
if (source is not IRgba32RawTexturePayloadSource rgba32Source)
48146
{
49147
throw new InvalidOperationException(
50-
$"Texture import source '{source.GetType().Name}' cannot materialize a raw texture payload.");
148+
$"Texture import source '{source.GetType().Name}' cannot materialize an RGBA32 texture payload.");
51149
}
52150

53-
return rawSource.MaterializeRawAsync(cancellationToken);
151+
return rgba32Source.MaterializeRgba32Async(cancellationToken);
54152
}
55153
}
56154

57-
internal sealed class InMemoryRawTextureImportSource : IRawTexturePayloadSource
155+
internal sealed class InMemoryRawTextureImportSource : IRgba32RawTexturePayloadSource
58156
{
59157
private readonly byte[] bytes;
60158

@@ -88,18 +186,18 @@ public InMemoryRawTextureImportSource(
88186

89187
public long? EstimatedByteLength => bytes.Length;
90188

91-
public ValueTask<RawTexturePayload> MaterializeRawAsync(CancellationToken cancellationToken)
189+
public ValueTask<Rgba32RawTexturePayload> MaterializeRgba32Async(CancellationToken cancellationToken)
92190
{
93191
cancellationToken.ThrowIfCancellationRequested();
94-
return ValueTask.FromResult(new RawTexturePayload(
192+
return ValueTask.FromResult(new Rgba32RawTexturePayload(
95193
Width,
96194
Height,
97195
ColorProfile,
98196
(byte[])bytes.Clone()));
99197
}
100198
}
101199

102-
internal sealed class InMemoryEncodedTextureImportSource : IRawTexturePayloadSource
200+
internal sealed class InMemoryEncodedTextureImportSource : IRgba32RawTexturePayloadSource
103201
{
104202
private readonly byte[] bytes;
105203

@@ -123,7 +221,7 @@ public InMemoryEncodedTextureImportSource(
123221

124222
public long? EstimatedByteLength => bytes.Length;
125223

126-
public async ValueTask<RawTexturePayload> MaterializeRawAsync(CancellationToken cancellationToken)
224+
public async ValueTask<Rgba32RawTexturePayload> MaterializeRgba32Async(CancellationToken cancellationToken)
127225
{
128226
cancellationToken.ThrowIfCancellationRequested();
129227
using MemoryStream stream = new(bytes, writable: false);
@@ -138,7 +236,7 @@ internal sealed class DatasetTextureImportSource(
138236
IPlateauDatasetContentSource datasetSource,
139237
string relativePath,
140238
string? colorProfile,
141-
string identity) : IRawTexturePayloadSource
239+
string identity) : IRgba32RawTexturePayloadSource
142240
{
143241
public string Identity { get; } = identity;
144242

@@ -150,7 +248,7 @@ internal sealed class DatasetTextureImportSource(
150248
? lengthSource.TryGetFileLength(relativePath)
151249
: null;
152250

153-
public async ValueTask<RawTexturePayload> MaterializeRawAsync(CancellationToken cancellationToken)
251+
public async ValueTask<Rgba32RawTexturePayload> MaterializeRgba32Async(CancellationToken cancellationToken)
154252
{
155253
await using Stream stream = await datasetSource.OpenReadAsync(relativePath, cancellationToken);
156254
using Image<Rgba32> image = await Image.LoadAsync<Rgba32>(stream, cancellationToken);
@@ -161,7 +259,7 @@ public async ValueTask<RawTexturePayload> MaterializeRawAsync(CancellationToken
161259
internal sealed class FileTextureImportSource(
162260
string absolutePath,
163261
string colorProfile,
164-
string identity) : IRawTexturePayloadSource
262+
string identity) : IRgba32RawTexturePayloadSource
165263
{
166264
public string Identity { get; } = identity;
167265

@@ -188,19 +286,40 @@ public long? EstimatedByteLength
188286
}
189287
}
190288

191-
public async ValueTask<RawTexturePayload> MaterializeRawAsync(CancellationToken cancellationToken)
289+
public async ValueTask<Rgba32RawTexturePayload> MaterializeRgba32Async(CancellationToken cancellationToken)
192290
{
193291
using Image<Rgba32> image = await Image.LoadAsync<Rgba32>(absolutePath, cancellationToken);
194292
return TextureImportSourceFactory.CreateRawPayloadFromImage(image, ColorProfile);
195293
}
196294
}
197295

198-
internal sealed class GeneratedTextureImportSource(
199-
Func<CancellationToken, ValueTask<RawTexturePayload>> materializeRawAsync,
296+
internal sealed class GeneratedRgba32TextureImportSource(
297+
Func<CancellationToken, ValueTask<Rgba32RawTexturePayload>> materializeRgba32Async,
298+
string identity,
299+
string description,
300+
string? colorProfile,
301+
long? estimatedByteLength = null) : IRgba32RawTexturePayloadSource
302+
{
303+
public string Identity { get; } = identity;
304+
305+
public string Description { get; } = description;
306+
307+
public string? ColorProfile { get; } = colorProfile;
308+
309+
public long? EstimatedByteLength { get; } = estimatedByteLength;
310+
311+
public ValueTask<Rgba32RawTexturePayload> MaterializeRgba32Async(CancellationToken cancellationToken)
312+
{
313+
return materializeRgba32Async(cancellationToken);
314+
}
315+
}
316+
317+
internal sealed class GeneratedRgbaFloat32TextureImportSource(
318+
Func<CancellationToken, ValueTask<RgbaFloat32RawTexturePayload>> materializeRgbaFloat32Async,
200319
string identity,
201320
string description,
202321
string? colorProfile,
203-
long? estimatedByteLength = null) : IRawTexturePayloadSource
322+
long? estimatedByteLength = null) : IRgbaFloat32RawTexturePayloadSource
204323
{
205324
public string Identity { get; } = identity;
206325

@@ -210,9 +329,9 @@ internal sealed class GeneratedTextureImportSource(
210329

211330
public long? EstimatedByteLength { get; } = estimatedByteLength;
212331

213-
public ValueTask<RawTexturePayload> MaterializeRawAsync(CancellationToken cancellationToken)
332+
public ValueTask<RgbaFloat32RawTexturePayload> MaterializeRgbaFloat32Async(CancellationToken cancellationToken)
214333
{
215-
return materializeRawAsync(cancellationToken);
334+
return materializeRgbaFloat32Async(cancellationToken);
216335
}
217336
}
218337

@@ -266,15 +385,30 @@ public static ITextureImportSource CreateFileImage(
266385
identity ?? $"file:{Path.GetFullPath(absolutePath)}:{colorProfile}");
267386
}
268387

269-
public static ITextureImportSource CreateGeneratedImage(
270-
Func<CancellationToken, ValueTask<RawTexturePayload>> materializeRawAsync,
388+
public static ITextureImportSource CreateGeneratedRgba32Image(
389+
Func<CancellationToken, ValueTask<Rgba32RawTexturePayload>> materializeRgba32Async,
390+
string identity,
391+
string description,
392+
string? colorProfile,
393+
long? estimatedByteLength = null)
394+
{
395+
return new GeneratedRgba32TextureImportSource(
396+
materializeRgba32Async,
397+
identity,
398+
description,
399+
colorProfile,
400+
estimatedByteLength);
401+
}
402+
403+
public static ITextureImportSource CreateGeneratedRgbaFloat32Image(
404+
Func<CancellationToken, ValueTask<RgbaFloat32RawTexturePayload>> materializeRgbaFloat32Async,
271405
string identity,
272406
string description,
273407
string? colorProfile,
274408
long? estimatedByteLength = null)
275409
{
276-
return new GeneratedTextureImportSource(
277-
materializeRawAsync,
410+
return new GeneratedRgbaFloat32TextureImportSource(
411+
materializeRgbaFloat32Async,
278412
identity,
279413
description,
280414
colorProfile,
@@ -290,7 +424,7 @@ public static ITextureImportSource CreateGeneratedImageFromClone(
290424
ArgumentNullException.ThrowIfNull(image);
291425
Image<Rgba32> retainedImage = image.Clone();
292426
object gate = new();
293-
return CreateGeneratedImage(
427+
return CreateGeneratedRgba32Image(
294428
cancellationToken =>
295429
{
296430
cancellationToken.ThrowIfCancellationRequested();
@@ -305,14 +439,14 @@ public static ITextureImportSource CreateGeneratedImageFromClone(
305439
(long)image.Width * image.Height * 4);
306440
}
307441

308-
public static RawTexturePayload CreateRawPayloadFromImage(
442+
public static Rgba32RawTexturePayload CreateRawPayloadFromImage(
309443
Image<Rgba32> image,
310444
string? colorProfile)
311445
{
312446
ArgumentNullException.ThrowIfNull(image);
313447
byte[] rawBytes = new byte[image.Width * image.Height * 4];
314448
image.CopyPixelDataTo(rawBytes);
315-
return new RawTexturePayload(
449+
return new Rgba32RawTexturePayload(
316450
image.Width,
317451
image.Height,
318452
colorProfile,

src/PlateauResoniteLink/Targets/Resonite/Diagnostics/SceneSinkRecordingClientCanonicalDump.cs

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -139,8 +139,7 @@ private static JsonObject CreateImportNode(SceneSinkRecordingClient client)
139139
List<JsonObject> textureNodes = [];
140140
for (int index = 0; index < client.ImportedTexturePayloads.Count; index++)
141141
{
142-
RawTexturePayload texture = client.ImportedTexturePayloads[index];
143-
if (texture.Format != RawTexturePayloadFormat.Rgba32)
142+
if (client.ImportedTexturePayloads[index] is not Rgba32RawTexturePayload texture)
144143
{
145144
continue;
146145
}
@@ -164,8 +163,7 @@ private static JsonObject CreateImportNode(SceneSinkRecordingClient client)
164163
List<JsonObject> hdrTextureNodes = [];
165164
for (int index = 0; index < client.ImportedTexturePayloads.Count; index++)
166165
{
167-
RawTexturePayload texture = client.ImportedTexturePayloads[index];
168-
if (texture.Format != RawTexturePayloadFormat.RgbaFloat32)
166+
if (client.ImportedTexturePayloads[index] is not RgbaFloat32RawTexturePayload texture)
169167
{
170168
continue;
171169
}
@@ -398,16 +396,16 @@ private static string CreateMeshToken(ImportMeshRawData mesh)
398396

399397
private static string CreateTextureToken(RawTexturePayload texture)
400398
{
401-
return texture.Format == RawTexturePayloadFormat.RgbaFloat32
402-
? string.Create(
399+
return texture.Match(
400+
rgba32 => string.Create(
403401
CultureInfo.InvariantCulture,
404-
$"hdr-texture:{texture.Width}x{texture.Height}:{HashBytes(texture.Bytes)}")
405-
: string.Create(
402+
$"texture:{rgba32.Width}x{rgba32.Height}:{rgba32.ColorProfile}:{HashBytes(rgba32.Bytes)}"),
403+
rgbaFloat32 => string.Create(
406404
CultureInfo.InvariantCulture,
407-
$"texture:{texture.Width}x{texture.Height}:{texture.ColorProfile}:{HashBytes(texture.Bytes)}");
405+
$"hdr-texture:{rgbaFloat32.Width}x{rgbaFloat32.Height}:{HashBytes(rgbaFloat32.Bytes)}"));
408406
}
409407

410-
private static string CreateHdrTextureToken(RawTexturePayload texture)
408+
private static string CreateHdrTextureToken(RgbaFloat32RawTexturePayload texture)
411409
{
412410
return string.Create(
413411
CultureInfo.InvariantCulture,

0 commit comments

Comments
 (0)