@flighthq/texture 0.2.0 → 0.2.1-next.410.a23f189
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/texture.d.ts +3 -2
- package/dist/texture.d.ts.map +1 -1
- package/dist/texture.js +27 -11
- package/dist/texture.js.map +1 -1
- package/dist/videoTexture.d.ts +13 -0
- package/dist/videoTexture.d.ts.map +1 -0
- package/dist/videoTexture.js +119 -0
- package/dist/videoTexture.js.map +1 -0
- package/package.json +4 -4
- package/src/texture.test.ts +50 -11
- package/src/videoTexture.test.ts +157 -0
package/dist/index.d.ts
CHANGED
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAC;AAC9B,cAAc,WAAW,CAAC;AAC1B,cAAc,WAAW,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAC;AAC9B,cAAc,WAAW,CAAC;AAC1B,cAAc,WAAW,CAAC;AAC1B,cAAc,gBAAgB,CAAC"}
|
package/dist/index.js
CHANGED
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAC;AAC9B,cAAc,WAAW,CAAC;AAC1B,cAAc,WAAW,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAC;AAC9B,cAAc,WAAW,CAAC;AAC1B,cAAc,WAAW,CAAC;AAC1B,cAAc,gBAAgB,CAAC"}
|
package/dist/texture.d.ts
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
import type { ImageResource, Matrix3Like, Texture, TextureLike, Vector2Like } from '@flighthq/types';
|
|
1
|
+
import type { ImageResource, Matrix3Like, Texture, TextureLike, TextureUvTransform, Vector2Like } from '@flighthq/types';
|
|
2
2
|
export declare function cloneTexture(source: Readonly<TextureLike>): Texture;
|
|
3
3
|
export declare function copyTexture(out: TextureLike, source: Readonly<TextureLike>): void;
|
|
4
4
|
export declare function createTexture(opts?: Readonly<Partial<TextureLike>>): Texture;
|
|
5
5
|
export declare function equalsTexture(a: Readonly<TextureLike> | null | undefined, b: Readonly<TextureLike> | null | undefined): boolean;
|
|
6
6
|
export declare function getTextureHeight(texture: Readonly<TextureLike>): number;
|
|
7
7
|
export declare function getTextureInverseUvMatrix(out: Matrix3Like, texture: Readonly<TextureLike>): void;
|
|
8
|
-
export declare function getTextureUvMatrix(out: Matrix3Like, texture: Readonly<
|
|
8
|
+
export declare function getTextureUvMatrix(out: Matrix3Like, texture: Readonly<TextureUvTransform>): void;
|
|
9
9
|
export declare function getTextureWidth(texture: Readonly<TextureLike>): number;
|
|
10
|
+
export declare function hasTextureUvTransform(texture: Readonly<TextureUvTransform>): boolean;
|
|
10
11
|
export declare function isTextureReady(texture: Readonly<TextureLike>): boolean;
|
|
11
12
|
export declare function resetTextureUvTransform(texture: TextureLike): void;
|
|
12
13
|
export declare function setTextureImage(texture: TextureLike, image: ImageResource | null): void;
|
package/dist/texture.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"texture.d.ts","sourceRoot":"","sources":["../src/texture.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"texture.d.ts","sourceRoot":"","sources":["../src/texture.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,aAAa,EACb,WAAW,EACX,OAAO,EACP,WAAW,EACX,kBAAkB,EAClB,WAAW,EACZ,MAAM,iBAAiB,CAAC;AAOzB,wBAAgB,YAAY,CAAC,MAAM,EAAE,QAAQ,CAAC,WAAW,CAAC,GAAG,OAAO,CAUnE;AAKD,wBAAgB,WAAW,CAAC,GAAG,EAAE,WAAW,EAAE,MAAM,EAAE,QAAQ,CAAC,WAAW,CAAC,GAAG,IAAI,CAYjF;AAKD,wBAAgB,aAAa,CAAC,IAAI,CAAC,EAAE,QAAQ,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC,GAAG,OAAO,CAU5E;AAID,wBAAgB,aAAa,CAC3B,CAAC,EAAE,QAAQ,CAAC,WAAW,CAAC,GAAG,IAAI,GAAG,SAAS,EAC3C,CAAC,EAAE,QAAQ,CAAC,WAAW,CAAC,GAAG,IAAI,GAAG,SAAS,GAC1C,OAAO,CAaT;AAGD,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,QAAQ,CAAC,WAAW,CAAC,GAAG,MAAM,CAEvE;AAQD,wBAAgB,yBAAyB,CAAC,GAAG,EAAE,WAAW,EAAE,OAAO,EAAE,QAAQ,CAAC,WAAW,CAAC,GAAG,IAAI,CAGhG;AASD,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,WAAW,EAAE,OAAO,EAAE,QAAQ,CAAC,kBAAkB,CAAC,GAAG,IAAI,CAkBhG;AAGD,wBAAgB,eAAe,CAAC,OAAO,EAAE,QAAQ,CAAC,WAAW,CAAC,GAAG,MAAM,CAEtE;AAMD,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,QAAQ,CAAC,kBAAkB,CAAC,GAAG,OAAO,CAQpF;AAID,wBAAgB,cAAc,CAAC,OAAO,EAAE,QAAQ,CAAC,WAAW,CAAC,GAAG,OAAO,CAEtE;AAID,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,WAAW,GAAG,IAAI,CAMlE;AAID,wBAAgB,eAAe,CAAC,OAAO,EAAE,WAAW,EAAE,KAAK,EAAE,aAAa,GAAG,IAAI,GAAG,IAAI,CAEvF;AAID,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,WAAW,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,IAAI,CAGnF;AAGD,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAEhF;AAGD,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,WAAW,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,IAAI,CAGlF;AAMD,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,WAAW,EAAE,OAAO,EAAE,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,IAAI,CAU/G"}
|
package/dist/texture.js
CHANGED
|
@@ -8,6 +8,7 @@ export function cloneTexture(source) {
|
|
|
8
8
|
return createEntity({
|
|
9
9
|
colorSpace: source.colorSpace,
|
|
10
10
|
image: source.image,
|
|
11
|
+
resource: source.resource ?? null,
|
|
11
12
|
sampler: cloneSampler(source.sampler),
|
|
12
13
|
uvOffset: cloneVector2(source.uvOffset),
|
|
13
14
|
uvRotation: source.uvRotation,
|
|
@@ -20,12 +21,14 @@ export function cloneTexture(source) {
|
|
|
20
21
|
export function copyTexture(out, source) {
|
|
21
22
|
const colorSpace = source.colorSpace;
|
|
22
23
|
const image = source.image;
|
|
24
|
+
const resource = source.resource ?? null;
|
|
23
25
|
const uvRotation = source.uvRotation;
|
|
24
26
|
copySampler(out.sampler, source.sampler);
|
|
25
27
|
copyVector2(out.uvOffset, source.uvOffset);
|
|
26
28
|
copyVector2(out.uvScale, source.uvScale);
|
|
27
29
|
out.colorSpace = colorSpace;
|
|
28
30
|
out.image = image;
|
|
31
|
+
out.resource = resource;
|
|
29
32
|
out.uvRotation = uvRotation;
|
|
30
33
|
}
|
|
31
34
|
// Builds a Texture: an unbound image slot (null), a default Sampler, 'srgb' color space (the
|
|
@@ -35,6 +38,7 @@ export function createTexture(opts) {
|
|
|
35
38
|
return createEntity({
|
|
36
39
|
colorSpace: opts?.colorSpace ?? 'srgb',
|
|
37
40
|
image: opts?.image ?? null,
|
|
41
|
+
resource: opts?.resource ?? null,
|
|
38
42
|
sampler: opts?.sampler ? cloneSampler(opts.sampler) : createSampler(),
|
|
39
43
|
uvOffset: opts?.uvOffset ? cloneVector2(opts.uvOffset) : createVector2(0, 0),
|
|
40
44
|
uvRotation: opts?.uvRotation ?? 0,
|
|
@@ -72,8 +76,9 @@ export function getTextureInverseUvMatrix(out, texture) {
|
|
|
72
76
|
inverseMatrix3(out, out);
|
|
73
77
|
}
|
|
74
78
|
// Composes the KHR_texture_transform fields (uvOffset, uvRotation, uvScale) into the 3×3 matrix
|
|
75
|
-
// a shader consumes at sample time.
|
|
76
|
-
//
|
|
79
|
+
// a shader consumes at sample time. Column-major layout matching @flighthq/geometry Matrix3, so it
|
|
80
|
+
// uploads to a GL/WGSL mat3 uniform with no transpose. The resulting transform applies:
|
|
81
|
+
// scale → rotate → translate, per the KHR_texture_transform spec:
|
|
77
82
|
// row 0 = [sx*cos(r), -sy*sin(r), tx]; row 1 = [sx*sin(r), sy*cos(r), ty]; row 2 = [0, 0, 1].
|
|
78
83
|
// Out-param form — write result into a pre-allocated Matrix3 to avoid per-call allocation.
|
|
79
84
|
// Safe when out is an unrelated scratch; not intended for aliased input (no in-param here).
|
|
@@ -86,20 +91,31 @@ export function getTextureUvMatrix(out, texture) {
|
|
|
86
91
|
const cosR = Math.cos(r);
|
|
87
92
|
const sinR = Math.sin(r);
|
|
88
93
|
const m = out.m;
|
|
89
|
-
m[0] = sx * cosR;
|
|
90
|
-
m[1] =
|
|
91
|
-
m[2] =
|
|
92
|
-
m[3] =
|
|
93
|
-
m[4] = sy * cosR;
|
|
94
|
-
m[5] =
|
|
95
|
-
m[6] = 0
|
|
96
|
-
m[7] =
|
|
97
|
-
m[8] = 1;
|
|
94
|
+
m[0] = sx * cosR; // (0,0)
|
|
95
|
+
m[1] = sx * sinR; // (1,0)
|
|
96
|
+
m[2] = 0; // (2,0)
|
|
97
|
+
m[3] = -sy * sinR; // (0,1)
|
|
98
|
+
m[4] = sy * cosR; // (1,1)
|
|
99
|
+
m[5] = 0; // (2,1)
|
|
100
|
+
m[6] = tx; // (0,2)
|
|
101
|
+
m[7] = ty; // (1,2)
|
|
102
|
+
m[8] = 1; // (2,2)
|
|
98
103
|
}
|
|
99
104
|
// Returns the pixel width of the texture's bound image, or -1 when no image is bound.
|
|
100
105
|
export function getTextureWidth(texture) {
|
|
101
106
|
return texture.image !== null ? texture.image.width : -1;
|
|
102
107
|
}
|
|
108
|
+
// True when the texture carries a non-identity KHR_texture_transform — any non-unit scale, non-zero
|
|
109
|
+
// offset, or non-zero rotation. GPU material renderers gate the HAS_UV_TRANSFORM shader variant on
|
|
110
|
+
// this so an untiled surface pays nothing for the uv-transform uniform or the extra vertex multiply;
|
|
111
|
+
// only a texture that actually remaps its uv compiles the transforming path.
|
|
112
|
+
export function hasTextureUvTransform(texture) {
|
|
113
|
+
return (texture.uvScale.x !== 1 ||
|
|
114
|
+
texture.uvScale.y !== 1 ||
|
|
115
|
+
texture.uvOffset.x !== 0 ||
|
|
116
|
+
texture.uvOffset.y !== 0 ||
|
|
117
|
+
texture.uvRotation !== 0);
|
|
118
|
+
}
|
|
103
119
|
// True once the texture references a pixel source. A texture with a null image is treated as an
|
|
104
120
|
// absent slot by materials, so this is the gate a material samples behind.
|
|
105
121
|
export function isTextureReady(texture) {
|
package/dist/texture.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"texture.js","sourceRoot":"","sources":["../src/texture.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAChD,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"texture.js","sourceRoot":"","sources":["../src/texture.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { Matrix3Like, VideoResource, VideoTexture, VideoTextureLike } from '@flighthq/types';
|
|
2
|
+
export declare function advanceVideoTexture(videoTexture: VideoTextureLike): number;
|
|
3
|
+
export declare function cloneVideoTexture(source: Readonly<VideoTextureLike>): VideoTexture;
|
|
4
|
+
export declare function copyVideoTexture(out: VideoTextureLike, source: Readonly<VideoTextureLike>): void;
|
|
5
|
+
export declare function createVideoTexture(source: VideoResource, opts?: Readonly<Partial<VideoTextureLike>>): VideoTexture;
|
|
6
|
+
export declare function getVideoTextureHeight(videoTexture: Readonly<VideoTextureLike>): number;
|
|
7
|
+
export declare function getVideoTextureInverseUvMatrix(out: Matrix3Like, videoTexture: Readonly<VideoTextureLike>): void;
|
|
8
|
+
export declare function getVideoTextureUvMatrix(out: Matrix3Like, videoTexture: Readonly<VideoTextureLike>): void;
|
|
9
|
+
export declare function getVideoTextureWidth(videoTexture: Readonly<VideoTextureLike>): number;
|
|
10
|
+
export declare function isVideoTextureFrameReady(videoTexture: Readonly<VideoTextureLike>): boolean;
|
|
11
|
+
export declare function resetVideoTextureFrame(videoTexture: VideoTextureLike): void;
|
|
12
|
+
export declare function setVideoTextureSource(videoTexture: VideoTextureLike, source: VideoResource): void;
|
|
13
|
+
//# sourceMappingURL=videoTexture.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"videoTexture.d.ts","sourceRoot":"","sources":["../src/videoTexture.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,aAAa,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAQlG,wBAAgB,mBAAmB,CAAC,YAAY,EAAE,gBAAgB,GAAG,MAAM,CAG1E;AAMD,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,QAAQ,CAAC,gBAAgB,CAAC,GAAG,YAAY,CAUlF;AAKD,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,gBAAgB,EAAE,MAAM,EAAE,QAAQ,CAAC,gBAAgB,CAAC,GAAG,IAAI,CAYhG;AAKD,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,aAAa,EAAE,IAAI,CAAC,EAAE,QAAQ,CAAC,OAAO,CAAC,gBAAgB,CAAC,CAAC,GAAG,YAAY,CAUlH;AAKD,wBAAgB,qBAAqB,CAAC,YAAY,EAAE,QAAQ,CAAC,gBAAgB,CAAC,GAAG,MAAM,CAGtF;AAKD,wBAAgB,8BAA8B,CAAC,GAAG,EAAE,WAAW,EAAE,YAAY,EAAE,QAAQ,CAAC,gBAAgB,CAAC,GAAG,IAAI,CAG/G;AAKD,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,WAAW,EAAE,YAAY,EAAE,QAAQ,CAAC,gBAAgB,CAAC,GAAG,IAAI,CAkBxG;AAID,wBAAgB,oBAAoB,CAAC,YAAY,EAAE,QAAQ,CAAC,gBAAgB,CAAC,GAAG,MAAM,CAGrF;AAKD,wBAAgB,wBAAwB,CAAC,YAAY,EAAE,QAAQ,CAAC,gBAAgB,CAAC,GAAG,OAAO,CAK1F;AAID,wBAAgB,sBAAsB,CAAC,YAAY,EAAE,gBAAgB,GAAG,IAAI,CAE3E;AAID,wBAAgB,qBAAqB,CAAC,YAAY,EAAE,gBAAgB,EAAE,MAAM,EAAE,aAAa,GAAG,IAAI,CAGjG"}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { createEntity } from '@flighthq/entity';
|
|
2
|
+
import { cloneVector2, copyVector2, createVector2, inverseMatrix3 } from '@flighthq/geometry';
|
|
3
|
+
import { cloneSampler, copySampler, createSampler } from './sampler';
|
|
4
|
+
// Marks a fresh decoded frame as available for upload by bumping `frameId`. A driver calls this once
|
|
5
|
+
// per rendered frame when the backing element reports new pixels (readyState advanced, or a
|
|
6
|
+
// requestVideoFrameCallback fired); the GPU uploader compares the id against the one it last uploaded
|
|
7
|
+
// and re-uploads only on a change, so a paused stream costs no upload. Returns the new id.
|
|
8
|
+
export function advanceVideoTexture(videoTexture) {
|
|
9
|
+
videoTexture.frameId += 1;
|
|
10
|
+
return videoTexture.frameId;
|
|
11
|
+
}
|
|
12
|
+
// Allocates an independent VideoTexture over the SAME video stream: the VideoResource reference is
|
|
13
|
+
// shared (an HTMLVideoElement cannot be duplicated), while the Sampler and uv-transform vectors are
|
|
14
|
+
// deep-cloned so two textures over one stream sample independently. `frameId` resets to -1 so the
|
|
15
|
+
// clone forces its own first upload.
|
|
16
|
+
export function cloneVideoTexture(source) {
|
|
17
|
+
return createEntity({
|
|
18
|
+
colorSpace: source.colorSpace,
|
|
19
|
+
frameId: -1,
|
|
20
|
+
sampler: cloneSampler(source.sampler),
|
|
21
|
+
source: source.source,
|
|
22
|
+
uvOffset: cloneVector2(source.uvOffset),
|
|
23
|
+
uvRotation: source.uvRotation,
|
|
24
|
+
uvScale: cloneVector2(source.uvScale),
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
// Copies every VideoTexture field from source into out in place. The VideoResource reference is
|
|
28
|
+
// shared; the Sampler and uv-transform vectors are copied into out's existing entities. Safe when out
|
|
29
|
+
// aliases source.
|
|
30
|
+
export function copyVideoTexture(out, source) {
|
|
31
|
+
const colorSpace = source.colorSpace;
|
|
32
|
+
const frameId = source.frameId;
|
|
33
|
+
const src = source.source;
|
|
34
|
+
const uvRotation = source.uvRotation;
|
|
35
|
+
copySampler(out.sampler, source.sampler);
|
|
36
|
+
copyVector2(out.uvOffset, source.uvOffset);
|
|
37
|
+
copyVector2(out.uvScale, source.uvScale);
|
|
38
|
+
out.colorSpace = colorSpace;
|
|
39
|
+
out.frameId = frameId;
|
|
40
|
+
out.source = src;
|
|
41
|
+
out.uvRotation = uvRotation;
|
|
42
|
+
}
|
|
43
|
+
// Builds a VideoTexture over a VideoResource: a default Sampler, 'srgb' color space (video is
|
|
44
|
+
// display-referred), an identity KHR_texture_transform, and `frameId` -1 (no frame uploaded yet).
|
|
45
|
+
// Pass VideoTextureLike fields to override any of these.
|
|
46
|
+
export function createVideoTexture(source, opts) {
|
|
47
|
+
return createEntity({
|
|
48
|
+
colorSpace: opts?.colorSpace ?? 'srgb',
|
|
49
|
+
frameId: opts?.frameId ?? -1,
|
|
50
|
+
sampler: opts?.sampler ? cloneSampler(opts.sampler) : createSampler(),
|
|
51
|
+
source: opts?.source ?? source,
|
|
52
|
+
uvOffset: opts?.uvOffset ? cloneVector2(opts.uvOffset) : createVector2(0, 0),
|
|
53
|
+
uvRotation: opts?.uvRotation ?? 0,
|
|
54
|
+
uvScale: opts?.uvScale ? cloneVector2(opts.uvScale) : createVector2(1, 1),
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
// Returns the pixel height of the current video frame, or -1 when no element is attached or no frame
|
|
58
|
+
// has decoded yet (videoHeight is 0). Reads the element directly, so the value tracks a resolution
|
|
59
|
+
// change mid-stream.
|
|
60
|
+
export function getVideoTextureHeight(videoTexture) {
|
|
61
|
+
const element = videoTexture.source.element;
|
|
62
|
+
return element !== null && element.videoHeight > 0 ? element.videoHeight : -1;
|
|
63
|
+
}
|
|
64
|
+
// Composes the KHR_texture_transform fields and inverts the result, producing the matrix that maps
|
|
65
|
+
// already-transformed uv back to the unit-square source uv. A zero scale is singular, so inverseMatrix3
|
|
66
|
+
// fills the matrix with NaN. Out-param form — write into a pre-allocated Matrix3.
|
|
67
|
+
export function getVideoTextureInverseUvMatrix(out, videoTexture) {
|
|
68
|
+
getVideoTextureUvMatrix(out, videoTexture);
|
|
69
|
+
inverseMatrix3(out, out);
|
|
70
|
+
}
|
|
71
|
+
// Composes the KHR_texture_transform fields (uvOffset, uvRotation, uvScale) into the column-major 3×3
|
|
72
|
+
// matrix a shader consumes at sample time — the same layout getTextureUvMatrix produces, so a material
|
|
73
|
+
// samples a VideoTexture through one uv-transform path. scale → rotate → translate. Out-param form.
|
|
74
|
+
export function getVideoTextureUvMatrix(out, videoTexture) {
|
|
75
|
+
const r = videoTexture.uvRotation;
|
|
76
|
+
const sx = videoTexture.uvScale.x;
|
|
77
|
+
const sy = videoTexture.uvScale.y;
|
|
78
|
+
const tx = videoTexture.uvOffset.x;
|
|
79
|
+
const ty = videoTexture.uvOffset.y;
|
|
80
|
+
const cosR = Math.cos(r);
|
|
81
|
+
const sinR = Math.sin(r);
|
|
82
|
+
const m = out.m;
|
|
83
|
+
m[0] = sx * cosR;
|
|
84
|
+
m[1] = sx * sinR;
|
|
85
|
+
m[2] = 0;
|
|
86
|
+
m[3] = -sy * sinR;
|
|
87
|
+
m[4] = sy * cosR;
|
|
88
|
+
m[5] = 0;
|
|
89
|
+
m[6] = tx;
|
|
90
|
+
m[7] = ty;
|
|
91
|
+
m[8] = 1;
|
|
92
|
+
}
|
|
93
|
+
// Returns the pixel width of the current video frame, or -1 when no element is attached or no frame
|
|
94
|
+
// has decoded yet.
|
|
95
|
+
export function getVideoTextureWidth(videoTexture) {
|
|
96
|
+
const element = videoTexture.source.element;
|
|
97
|
+
return element !== null && element.videoWidth > 0 ? element.videoWidth : -1;
|
|
98
|
+
}
|
|
99
|
+
// True when the backing element has decoded at least the current frame (readyState >=
|
|
100
|
+
// HAVE_CURRENT_DATA) and its dimensions are known — the gate a material samples behind and a driver
|
|
101
|
+
// checks before calling advanceVideoTexture. False while metadata/first-frame is still buffering.
|
|
102
|
+
export function isVideoTextureFrameReady(videoTexture) {
|
|
103
|
+
const element = videoTexture.source.element;
|
|
104
|
+
return (element !== null && element.readyState >= HAVE_CURRENT_DATA && element.videoWidth > 0 && element.videoHeight > 0);
|
|
105
|
+
}
|
|
106
|
+
// Resets `frameId` to -1 so the next upload re-sends the current frame regardless of history. Used
|
|
107
|
+
// after a context loss (the GPU texture is gone) or a source swap to force a re-upload.
|
|
108
|
+
export function resetVideoTextureFrame(videoTexture) {
|
|
109
|
+
videoTexture.frameId = -1;
|
|
110
|
+
}
|
|
111
|
+
// Binds a different video stream and resets `frameId` to -1 so the new stream's first frame uploads.
|
|
112
|
+
// Does not touch sampling state or the uv-transform.
|
|
113
|
+
export function setVideoTextureSource(videoTexture, source) {
|
|
114
|
+
videoTexture.source = source;
|
|
115
|
+
videoTexture.frameId = -1;
|
|
116
|
+
}
|
|
117
|
+
// HTMLMediaElement.HAVE_CURRENT_DATA — data for the current playback position is available.
|
|
118
|
+
const HAVE_CURRENT_DATA = 2;
|
|
119
|
+
//# sourceMappingURL=videoTexture.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"videoTexture.js","sourceRoot":"","sources":["../src/videoTexture.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@flighthq/texture",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.1-next.410.a23f189",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "https://github.com/flighthq/flight.git",
|
|
@@ -32,9 +32,9 @@
|
|
|
32
32
|
"clean:dist": "tsx ../../scripts/clean-package-dist.ts"
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
|
-
"@flighthq/entity": "0.2.
|
|
36
|
-
"@flighthq/geometry": "0.2.
|
|
37
|
-
"@flighthq/types": "0.2.
|
|
35
|
+
"@flighthq/entity": "0.2.1-next.410.a23f189",
|
|
36
|
+
"@flighthq/geometry": "0.2.1-next.410.a23f189",
|
|
37
|
+
"@flighthq/types": "0.2.1-next.410.a23f189"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
40
40
|
"typescript": "^5.3.0"
|
package/src/texture.test.ts
CHANGED
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
getTextureInverseUvMatrix,
|
|
12
12
|
getTextureUvMatrix,
|
|
13
13
|
getTextureWidth,
|
|
14
|
+
hasTextureUvTransform,
|
|
14
15
|
isTextureReady,
|
|
15
16
|
resetTextureUvTransform,
|
|
16
17
|
setTextureImage,
|
|
@@ -222,7 +223,7 @@ describe('getTextureUvMatrix', () => {
|
|
|
222
223
|
|
|
223
224
|
getTextureUvMatrix(out, texture);
|
|
224
225
|
|
|
225
|
-
//
|
|
226
|
+
// Identity = [1,0,0; 0,1,0; 0,0,1]
|
|
226
227
|
expect(out.m[0]).toBeCloseTo(1);
|
|
227
228
|
expect(out.m[1]).toBeCloseTo(0);
|
|
228
229
|
expect(out.m[2]).toBeCloseTo(0);
|
|
@@ -241,8 +242,9 @@ describe('getTextureUvMatrix', () => {
|
|
|
241
242
|
|
|
242
243
|
getTextureUvMatrix(out, texture);
|
|
243
244
|
|
|
244
|
-
|
|
245
|
-
expect(out.m[
|
|
245
|
+
// Column-major: the translation column is m[6], m[7].
|
|
246
|
+
expect(out.m[6]).toBeCloseTo(0.25); // tx
|
|
247
|
+
expect(out.m[7]).toBeCloseTo(0.75); // ty
|
|
246
248
|
});
|
|
247
249
|
|
|
248
250
|
it('encodes scale in the diagonal', () => {
|
|
@@ -267,12 +269,13 @@ describe('getTextureUvMatrix', () => {
|
|
|
267
269
|
|
|
268
270
|
const cosR = Math.cos(r);
|
|
269
271
|
const sinR = Math.sin(r);
|
|
270
|
-
|
|
271
|
-
expect(out.m[
|
|
272
|
-
expect(out.m[
|
|
273
|
-
expect(out.m[3]).toBeCloseTo(2 * sinR); //
|
|
274
|
-
expect(out.m[4]).toBeCloseTo(2 * cosR); // sy*cos(r)
|
|
275
|
-
expect(out.m[
|
|
272
|
+
// Column-major storage of rows [sx·cos, -sy·sin, tx], [sx·sin, sy·cos, ty], [0,0,1].
|
|
273
|
+
expect(out.m[0]).toBeCloseTo(2 * cosR); // (0,0) sx*cos(r)
|
|
274
|
+
expect(out.m[1]).toBeCloseTo(2 * sinR); // (1,0) sx*sin(r)
|
|
275
|
+
expect(out.m[3]).toBeCloseTo(-2 * sinR); // (0,1) -sy*sin(r)
|
|
276
|
+
expect(out.m[4]).toBeCloseTo(2 * cosR); // (1,1) sy*cos(r)
|
|
277
|
+
expect(out.m[6]).toBeCloseTo(0.1); // (0,2) tx
|
|
278
|
+
expect(out.m[7]).toBeCloseTo(0.2); // (1,2) ty
|
|
276
279
|
});
|
|
277
280
|
});
|
|
278
281
|
|
|
@@ -290,6 +293,41 @@ describe('getTextureWidth', () => {
|
|
|
290
293
|
});
|
|
291
294
|
});
|
|
292
295
|
|
|
296
|
+
describe('hasTextureUvTransform', () => {
|
|
297
|
+
it('is false for a freshly created identity-transform texture', () => {
|
|
298
|
+
expect(hasTextureUvTransform(createTexture())).toBe(false);
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
it('is true when the scale is non-unit', () => {
|
|
302
|
+
const texture = createTexture();
|
|
303
|
+
setTextureUvScale(texture, 2, 2);
|
|
304
|
+
|
|
305
|
+
expect(hasTextureUvTransform(texture)).toBe(true);
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
it('is true when the offset is non-zero', () => {
|
|
309
|
+
const texture = createTexture();
|
|
310
|
+
setTextureUvOffset(texture, 0.25, 0);
|
|
311
|
+
|
|
312
|
+
expect(hasTextureUvTransform(texture)).toBe(true);
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
it('is true when the rotation is non-zero', () => {
|
|
316
|
+
const texture = createTexture();
|
|
317
|
+
setTextureUvRotation(texture, 0.5);
|
|
318
|
+
|
|
319
|
+
expect(hasTextureUvTransform(texture)).toBe(true);
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
it('is false again after resetTextureUvTransform', () => {
|
|
323
|
+
const texture = createTexture();
|
|
324
|
+
setTextureUvScale(texture, 3, 4);
|
|
325
|
+
resetTextureUvTransform(texture);
|
|
326
|
+
|
|
327
|
+
expect(hasTextureUvTransform(texture)).toBe(false);
|
|
328
|
+
});
|
|
329
|
+
});
|
|
330
|
+
|
|
293
331
|
describe('isTextureReady', () => {
|
|
294
332
|
it('is false with a null image and true once bound', () => {
|
|
295
333
|
const texture = createTexture();
|
|
@@ -398,8 +436,9 @@ describe('transformTextureUv', () => {
|
|
|
398
436
|
const u = 0.3;
|
|
399
437
|
const v = 0.8;
|
|
400
438
|
const m = matrix.m;
|
|
401
|
-
|
|
402
|
-
const
|
|
439
|
+
// Column-major: row 0 = m[0], m[3], m[6]; row 1 = m[1], m[4], m[7].
|
|
440
|
+
const expectedX = m[0] * u + m[3] * v + m[6];
|
|
441
|
+
const expectedY = m[1] * u + m[4] * v + m[7];
|
|
403
442
|
const out = createVector2(0, 0);
|
|
404
443
|
|
|
405
444
|
transformTextureUv(out, texture, u, v);
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import { createMatrix3, createVector2 } from '@flighthq/geometry';
|
|
2
|
+
import type { VideoResource } from '@flighthq/types';
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
advanceVideoTexture,
|
|
6
|
+
cloneVideoTexture,
|
|
7
|
+
copyVideoTexture,
|
|
8
|
+
createVideoTexture,
|
|
9
|
+
getVideoTextureHeight,
|
|
10
|
+
getVideoTextureInverseUvMatrix,
|
|
11
|
+
getVideoTextureUvMatrix,
|
|
12
|
+
getVideoTextureWidth,
|
|
13
|
+
isVideoTextureFrameReady,
|
|
14
|
+
resetVideoTextureFrame,
|
|
15
|
+
setVideoTextureSource,
|
|
16
|
+
} from './videoTexture';
|
|
17
|
+
|
|
18
|
+
// A minimal element carrier: only the fields the VideoTexture accessors read.
|
|
19
|
+
function makeVideoResource(readyState = 4, videoWidth = 320, videoHeight = 240): VideoResource {
|
|
20
|
+
return {
|
|
21
|
+
element: { readyState, videoWidth, videoHeight } as unknown as HTMLVideoElement,
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
describe('advanceVideoTexture', () => {
|
|
26
|
+
it('bumps frameId and returns the new value', () => {
|
|
27
|
+
const vt = createVideoTexture(makeVideoResource());
|
|
28
|
+
expect(vt.frameId).toBe(-1);
|
|
29
|
+
expect(advanceVideoTexture(vt)).toBe(0);
|
|
30
|
+
expect(advanceVideoTexture(vt)).toBe(1);
|
|
31
|
+
expect(vt.frameId).toBe(1);
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
describe('cloneVideoTexture', () => {
|
|
36
|
+
it('shares the source but deep-clones sampler and uv vectors and resets frameId', () => {
|
|
37
|
+
const source = makeVideoResource();
|
|
38
|
+
const vt = createVideoTexture(source, { uvOffset: createVector2(2, 3) });
|
|
39
|
+
advanceVideoTexture(vt);
|
|
40
|
+
const clone = cloneVideoTexture(vt);
|
|
41
|
+
expect(clone.source).toBe(source);
|
|
42
|
+
expect(clone.sampler).not.toBe(vt.sampler);
|
|
43
|
+
expect(clone.uvOffset).not.toBe(vt.uvOffset);
|
|
44
|
+
expect(clone.uvOffset.x).toBe(2);
|
|
45
|
+
expect(clone.frameId).toBe(-1);
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
describe('copyVideoTexture', () => {
|
|
50
|
+
it('copies fields into out and is safe when out aliases source', () => {
|
|
51
|
+
const a = createVideoTexture(makeVideoResource(), { colorSpace: 'linear', uvScale: createVector2(4, 5) });
|
|
52
|
+
advanceVideoTexture(a);
|
|
53
|
+
const b = createVideoTexture(makeVideoResource());
|
|
54
|
+
copyVideoTexture(b, a);
|
|
55
|
+
expect(b.colorSpace).toBe('linear');
|
|
56
|
+
expect(b.frameId).toBe(0);
|
|
57
|
+
expect(b.source).toBe(a.source);
|
|
58
|
+
expect(b.uvScale.x).toBe(4);
|
|
59
|
+
copyVideoTexture(a, a);
|
|
60
|
+
expect(a.uvScale.x).toBe(4);
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
describe('createVideoTexture', () => {
|
|
65
|
+
it('defaults to srgb, identity uv-transform, and frameId -1', () => {
|
|
66
|
+
const source = makeVideoResource();
|
|
67
|
+
const vt = createVideoTexture(source);
|
|
68
|
+
expect(vt.colorSpace).toBe('srgb');
|
|
69
|
+
expect(vt.source).toBe(source);
|
|
70
|
+
expect(vt.frameId).toBe(-1);
|
|
71
|
+
expect(vt.uvOffset.x).toBe(0);
|
|
72
|
+
expect(vt.uvScale.x).toBe(1);
|
|
73
|
+
expect(vt.uvRotation).toBe(0);
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
describe('getVideoTextureHeight', () => {
|
|
78
|
+
it('returns the element videoHeight when a frame is decoded, else -1', () => {
|
|
79
|
+
expect(getVideoTextureHeight(createVideoTexture(makeVideoResource(4, 320, 240)))).toBe(240);
|
|
80
|
+
expect(getVideoTextureHeight(createVideoTexture(makeVideoResource(0, 0, 0)))).toBe(-1);
|
|
81
|
+
expect(getVideoTextureHeight(createVideoTexture({ element: null }))).toBe(-1);
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
describe('getVideoTextureInverseUvMatrix', () => {
|
|
86
|
+
it('inverts the forward uv-transform', () => {
|
|
87
|
+
const vt = createVideoTexture(makeVideoResource(), { uvScale: createVector2(2, 2), uvOffset: createVector2(1, 0) });
|
|
88
|
+
const forward = createMatrix3();
|
|
89
|
+
const inverse = createMatrix3();
|
|
90
|
+
getVideoTextureUvMatrix(forward, vt);
|
|
91
|
+
getVideoTextureInverseUvMatrix(inverse, vt);
|
|
92
|
+
// forward * inverse should be identity: a point mapped forward then back returns to itself.
|
|
93
|
+
const m = forward.m;
|
|
94
|
+
const i = inverse.m;
|
|
95
|
+
// (forward maps (0,0) -> (1,0); inverse must map (1,0) -> (0,0))
|
|
96
|
+
const px = i[0] * 1 + i[3] * 0 + i[6];
|
|
97
|
+
const py = i[1] * 1 + i[4] * 0 + i[7];
|
|
98
|
+
expect(px).toBeCloseTo(0);
|
|
99
|
+
expect(py).toBeCloseTo(0);
|
|
100
|
+
expect(m[0]).toBe(2);
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
describe('getVideoTextureUvMatrix', () => {
|
|
105
|
+
it('composes scale, rotation, and offset in column-major layout', () => {
|
|
106
|
+
const vt = createVideoTexture(makeVideoResource(), {
|
|
107
|
+
uvScale: createVector2(2, 3),
|
|
108
|
+
uvOffset: createVector2(5, 7),
|
|
109
|
+
});
|
|
110
|
+
const out = createMatrix3();
|
|
111
|
+
getVideoTextureUvMatrix(out, vt);
|
|
112
|
+
expect(out.m[0]).toBe(2);
|
|
113
|
+
expect(out.m[4]).toBe(3);
|
|
114
|
+
expect(out.m[6]).toBe(5);
|
|
115
|
+
expect(out.m[7]).toBe(7);
|
|
116
|
+
expect(out.m[8]).toBe(1);
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
describe('getVideoTextureWidth', () => {
|
|
121
|
+
it('returns the element videoWidth when a frame is decoded, else -1', () => {
|
|
122
|
+
expect(getVideoTextureWidth(createVideoTexture(makeVideoResource(4, 320, 240)))).toBe(320);
|
|
123
|
+
expect(getVideoTextureWidth(createVideoTexture(makeVideoResource(0, 0, 0)))).toBe(-1);
|
|
124
|
+
expect(getVideoTextureWidth(createVideoTexture({ element: null }))).toBe(-1);
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
describe('isVideoTextureFrameReady', () => {
|
|
129
|
+
it('is true only when the element has a decoded frame with known dimensions', () => {
|
|
130
|
+
expect(isVideoTextureFrameReady(createVideoTexture(makeVideoResource(4, 320, 240)))).toBe(true);
|
|
131
|
+
expect(isVideoTextureFrameReady(createVideoTexture(makeVideoResource(1, 320, 240)))).toBe(false);
|
|
132
|
+
expect(isVideoTextureFrameReady(createVideoTexture(makeVideoResource(4, 0, 0)))).toBe(false);
|
|
133
|
+
expect(isVideoTextureFrameReady(createVideoTexture({ element: null }))).toBe(false);
|
|
134
|
+
});
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
describe('resetVideoTextureFrame', () => {
|
|
138
|
+
it('sets frameId back to -1', () => {
|
|
139
|
+
const vt = createVideoTexture(makeVideoResource());
|
|
140
|
+
advanceVideoTexture(vt);
|
|
141
|
+
advanceVideoTexture(vt);
|
|
142
|
+
resetVideoTextureFrame(vt);
|
|
143
|
+
expect(vt.frameId).toBe(-1);
|
|
144
|
+
});
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
describe('setVideoTextureSource', () => {
|
|
148
|
+
it('swaps the source and resets frameId', () => {
|
|
149
|
+
const vt = createVideoTexture(makeVideoResource());
|
|
150
|
+
advanceVideoTexture(vt);
|
|
151
|
+
const next = makeVideoResource(4, 640, 480);
|
|
152
|
+
setVideoTextureSource(vt, next);
|
|
153
|
+
expect(vt.source).toBe(next);
|
|
154
|
+
expect(vt.frameId).toBe(-1);
|
|
155
|
+
expect(getVideoTextureWidth(vt)).toBe(640);
|
|
156
|
+
});
|
|
157
|
+
});
|