@xmov/avatar 2.0.0-alpha.38
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/README.md +139 -0
- package/dist/agent/__tests__/agent.test.d.ts +1 -0
- package/dist/agent/audio-debug.d.ts +3 -0
- package/dist/agent/audio-uplink.d.ts +62 -0
- package/dist/agent/avatar.cjs +2 -0
- package/dist/agent/avatar.cjs.map +1 -0
- package/dist/agent/avatar.modern.js +2 -0
- package/dist/agent/avatar.modern.js.map +1 -0
- package/dist/agent/avatar.module.js +2 -0
- package/dist/agent/avatar.module.js.map +1 -0
- package/dist/agent/avatar.umd.js +2 -0
- package/dist/agent/avatar.umd.js.map +1 -0
- package/dist/agent/e2e-client.d.ts +32 -0
- package/dist/agent/fixed-audio-track.d.ts +48 -0
- package/dist/agent/index.cjs +5 -0
- package/dist/agent/index.d.ts +104 -0
- package/dist/agent/index.umd.js +10 -0
- package/dist/agent/microphone.d.ts +49 -0
- package/dist/agent/types.d.ts +153 -0
- package/dist/baseRender/AudioRenderer.d.ts +46 -0
- package/dist/baseRender/AudioWorklet.d.ts +78 -0
- package/dist/baseRender/AvatarRenderer.d.ts +226 -0
- package/dist/baseRender/MSEAudioPlayer.d.ts +51 -0
- package/dist/baseRender/UIRenderer.d.ts +39 -0
- package/dist/baseRender/pcm-audio-processor.d.ts +63 -0
- package/dist/control/APIForwarder.d.ts +23 -0
- package/dist/control/DataCacheQueue.d.ts +69 -0
- package/dist/control/EventDispatcher.d.ts +35 -0
- package/dist/control/RenderScheduler.d.ts +162 -0
- package/dist/control/SaveAndDownload.d.ts +14 -0
- package/dist/control/StreamingClient.d.ts +33 -0
- package/dist/control/VideoPlayer.d.ts +47 -0
- package/dist/control/ttsa.d.ts +111 -0
- package/dist/encoding/media-recorder-encoder.d.ts +33 -0
- package/dist/encoding/pcm-webm-encoder.d.ts +13 -0
- package/dist/encoding/webcodec-opus-encoder.d.ts +41 -0
- package/dist/encoding/webm-muxer.d.ts +90 -0
- package/dist/index.cjs +2 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +231 -0
- package/dist/index.modern.js +2 -0
- package/dist/index.modern.js.map +1 -0
- package/dist/index.module.js +2 -0
- package/dist/index.module.js.map +1 -0
- package/dist/index.umd.js +2 -0
- package/dist/index.umd.js.map +1 -0
- package/dist/modules/Composition.d.ts +28 -0
- package/dist/modules/ResourceManager.d.ts +295 -0
- package/dist/modules/TrackRenderer/base-track.d.ts +4 -0
- package/dist/modules/TrackRenderer/index.d.ts +12 -0
- package/dist/modules/TrackRenderer/render-implements.d.ts +19 -0
- package/dist/modules/TrackRenderer/track-pic.d.ts +8 -0
- package/dist/modules/TrackRenderer/track-subtitle.d.ts +8 -0
- package/dist/modules/cache-manager.d.ts +5 -0
- package/dist/modules/decoder.d.ts +84 -0
- package/dist/modules/error-handle.d.ts +17 -0
- package/dist/modules/network.d.ts +21 -0
- package/dist/proto/face_data_pb.d.ts +0 -0
- package/dist/types/capability.d.ts +15 -0
- package/dist/types/error.d.ts +70 -0
- package/dist/types/event.d.ts +25 -0
- package/dist/types/frame-data.d.ts +107 -0
- package/dist/types/index.d.ts +187 -0
- package/dist/types/render.d.ts +18 -0
- package/dist/utils/DataInterface.d.ts +113 -0
- package/dist/utils/GLDevice.d.ts +46 -0
- package/dist/utils/GLPipeline.d.ts +84 -0
- package/dist/utils/GLPipelineDebugTools.d.ts +5 -0
- package/dist/utils/Math.d.ts +21 -0
- package/dist/utils/__tests__/capability-checker.test.d.ts +1 -0
- package/dist/utils/audio-session.d.ts +11 -0
- package/dist/utils/audio.d.ts +1 -0
- package/dist/utils/capability-checker.d.ts +23 -0
- package/dist/utils/encodeToken.d.ts +7 -0
- package/dist/utils/face.d.ts +1 -0
- package/dist/utils/float32-decoder.d.ts +26 -0
- package/dist/utils/fpsTracker.d.ts +27 -0
- package/dist/utils/index.d.ts +22 -0
- package/dist/utils/logger.d.ts +9 -0
- package/dist/utils/media-recorder-timestamp.d.ts +15 -0
- package/dist/utils/pcmAnalyzer.d.ts +40 -0
- package/dist/utils/perfermance.d.ts +15 -0
- package/dist/utils/request.d.ts +19 -0
- package/dist/utils/requestAnimateFrames.d.ts +31 -0
- package/dist/utils/time.d.ts +9 -0
- package/dist/view/DebugOverlay.d.ts +54 -0
- package/dist/worker/streaming-video.d.ts +1 -0
- package/package.json +82 -0
- package/src/agent/__tests__/agent.test.ts +3787 -0
- package/src/agent/audio-debug.ts +36 -0
- package/src/agent/audio-uplink.ts +392 -0
- package/src/agent/e2e-client.ts +215 -0
- package/src/agent/fixed-audio-track.ts +547 -0
- package/src/agent/index.ts +1393 -0
- package/src/agent/microphone.ts +534 -0
- package/src/agent/types.ts +197 -0
- package/src/baseRender/AudioRenderer.ts +317 -0
- package/src/baseRender/AudioWorklet.ts +776 -0
- package/src/baseRender/AvatarRenderer.ts +1559 -0
- package/src/baseRender/MSEAudioPlayer.ts +243 -0
- package/src/baseRender/UIRenderer.ts +195 -0
- package/src/baseRender/pcm-audio-processor.js +267 -0
- package/src/control/APIForwarder.ts +67 -0
- package/src/control/DataCacheQueue.ts +372 -0
- package/src/control/EventDispatcher.ts +106 -0
- package/src/control/RenderScheduler.ts +945 -0
- package/src/control/SaveAndDownload.js +77 -0
- package/src/control/StreamingClient.ts +138 -0
- package/src/control/VideoPlayer.ts +306 -0
- package/src/control/ttsa.ts +676 -0
- package/src/encoding/media-recorder-encoder.ts +175 -0
- package/src/encoding/pcm-webm-encoder.ts +72 -0
- package/src/encoding/webcodec-opus-encoder.ts +258 -0
- package/src/encoding/webm-muxer.ts +337 -0
- package/src/global.d.ts +105 -0
- package/src/index.ts +1920 -0
- package/src/modules/Composition.ts +79 -0
- package/src/modules/ResourceManager.ts +899 -0
- package/src/modules/TrackRenderer/base-track.ts +7 -0
- package/src/modules/TrackRenderer/index.ts +40 -0
- package/src/modules/TrackRenderer/render-implements.ts +269 -0
- package/src/modules/TrackRenderer/track-pic.ts +19 -0
- package/src/modules/TrackRenderer/track-subtitle.ts +16 -0
- package/src/modules/cache-manager.ts +10 -0
- package/src/modules/decoder.ts +515 -0
- package/src/modules/error-handle.ts +20 -0
- package/src/modules/network.ts +71 -0
- package/src/proto/face_data.proto +45 -0
- package/src/proto/face_data_pb.js +1297 -0
- package/src/proto/protobuf.min.js +8 -0
- package/src/types/capability.ts +23 -0
- package/src/types/error.ts +211 -0
- package/src/types/event.ts +38 -0
- package/src/types/frame-data.ts +121 -0
- package/src/types/index.ts +207 -0
- package/src/types/render.ts +18 -0
- package/src/utils/DataInterface.ts +746 -0
- package/src/utils/GLDevice.ts +286 -0
- package/src/utils/GLPipeline.ts +1293 -0
- package/src/utils/GLPipelineDebugTools.ts +174 -0
- package/src/utils/Math.ts +266 -0
- package/src/utils/__tests__/capability-checker.test.ts +159 -0
- package/src/utils/audio-session.ts +144 -0
- package/src/utils/audio.ts +44 -0
- package/src/utils/blueimp-md5.d.ts +3 -0
- package/src/utils/capability-checker.ts +379 -0
- package/src/utils/encodeToken.ts +100 -0
- package/src/utils/face.ts +29 -0
- package/src/utils/float32-decoder.js +130 -0
- package/src/utils/fpsTracker.ts +100 -0
- package/src/utils/index.ts +182 -0
- package/src/utils/logger.js +36 -0
- package/src/utils/media-recorder-timestamp.ts +79 -0
- package/src/utils/pcmAnalyzer.ts +154 -0
- package/src/utils/perfermance.ts +141 -0
- package/src/utils/request.ts +95 -0
- package/src/utils/requestAnimateFrames.ts +195 -0
- package/src/utils/time.ts +32 -0
- package/src/view/DebugOverlay.ts +404 -0
- package/src/worker/streaming-video.ts +4647 -0
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import { GLPipelineCharData } from './GLPipeline'
|
|
2
|
+
import { mvmul } from './Math'
|
|
3
|
+
import { TensorDataType, RigidTransform, IBRAnimationFrameData_NN } from './DataInterface'
|
|
4
|
+
|
|
5
|
+
function sampleUniformTensor(data: TensorDataType, index: number): number {
|
|
6
|
+
// @ts-ignore
|
|
7
|
+
if(Float16Array === data.constructor || Float32Array === data.constructor || Float64Array === data.constructor)return data[index];
|
|
8
|
+
else if(Uint8Array === data.constructor)return (data[index] / 0xFF);
|
|
9
|
+
else if(Int8Array === data.constructor)return ((data[index] + 0x80) / 0xFF) * 2.0 - 1.0;
|
|
10
|
+
else if(Uint16Array === data.constructor)return (data[index] / 0xFFFF);
|
|
11
|
+
else if(Int16Array === data.constructor)return ((data[index] + 0x8000) / 0xFFFF) * 2.0 - 1.0;
|
|
12
|
+
else if(Uint32Array === data.constructor)return (data[index] / 0xFFFFFFFF);
|
|
13
|
+
else if(Int32Array === data.constructor)return ((data[index] + 0x80000000) / 0xFFFFFFFF) * 2.0 - 1.0;
|
|
14
|
+
else if(BigUint64Array === data.constructor)return (Number(data[index] >> 32n) / 0xFFFFFFFF);
|
|
15
|
+
else if(BigInt64Array === data.constructor)return ((Number(data[index] >> 32n) + 0x80000000) / 0xFFFFFFFF) * 2.0 - 1.0;
|
|
16
|
+
else return 0.0;
|
|
17
|
+
}
|
|
18
|
+
function sampleUniformPCATensor(data: TensorDataType, index: number): number {
|
|
19
|
+
// @ts-ignore
|
|
20
|
+
if(Float16Array === data.constructor || Float32Array === data.constructor || Float64Array === data.constructor)return data[index];
|
|
21
|
+
else if(Uint8Array === data.constructor)return (data[index] / 0xFF) * 2.0 - 1.0;
|
|
22
|
+
else if(Int8Array === data.constructor)return ((data[index] + 0x80) / 0xFF) * 2.0 - 1.0;
|
|
23
|
+
else if(Uint16Array === data.constructor)return (data[index] / 0xFFFF) * 2.0 - 1.0;
|
|
24
|
+
else if(Int16Array === data.constructor)return ((data[index] + 0x8000) / 0xFFFF) * 2.0 - 1.0;
|
|
25
|
+
else if(Uint32Array === data.constructor)return (data[index] / 0xFFFFFFFF) * 2.0 - 1.0;
|
|
26
|
+
else if(Int32Array === data.constructor)return ((data[index] + 0x80000000) / 0xFFFFFFFF) * 2.0 - 1.0;
|
|
27
|
+
else if(BigUint64Array === data.constructor)return (Number(data[index] >> 32n) / 0xFFFFFFFF) * 2.0 - 1.0;
|
|
28
|
+
else if(BigInt64Array === data.constructor)return ((Number(data[index] >> 32n) + 0x80000000) / 0xFFFFFFFF) * 2.0 - 1.0;
|
|
29
|
+
else return 0.0;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function getVertices(data: GLPipelineCharData, frame_data: IBRAnimationFrameData_NN): number[][][] {
|
|
33
|
+
let char = data.char!;
|
|
34
|
+
|
|
35
|
+
let initSkeletonStatus = char.evalSkeleton();
|
|
36
|
+
let currentSkeletonStatus = char.evalSkeletonFromMovable(frame_data.movableJointTransforms);
|
|
37
|
+
let joint_matrices = Array(char.skeleton.length);
|
|
38
|
+
for (let i = 0; i < char.skeleton.length; i++) {
|
|
39
|
+
const joint_transform = currentSkeletonStatus[i].apply(initSkeletonStatus[i].inv()) as RigidTransform;
|
|
40
|
+
joint_matrices[i] = joint_transform.homogeneous_matrix();
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
let result: number[][][] = [];
|
|
44
|
+
for (let mesh_index = 0; mesh_index < char.mesh.length; mesh_index++) {
|
|
45
|
+
let geometry: number[][] = [];
|
|
46
|
+
for (let i = 0; i < char.mesh[mesh_index].blendshapes.size[0]; i++) {
|
|
47
|
+
let vertex: number[] = [];
|
|
48
|
+
let neutral_vertices = char.mesh[mesh_index].vertices;
|
|
49
|
+
let blendshapes = char.mesh[mesh_index].blendshapes;
|
|
50
|
+
let blendshapeWeights = frame_data.blendshapeWeights;
|
|
51
|
+
for (let k = 0; k < 3; k++)vertex.push(neutral_vertices.data[i * neutral_vertices.size[1] + k] as number);
|
|
52
|
+
for (let j = 0; j < char.mesh[mesh_index].blendshapeIndices.length; j++) {
|
|
53
|
+
for (let k = 0; k < 3; k++)vertex[k] += blendshapeWeights[char.mesh[mesh_index].blendshapeIndices[j]] * Number(blendshapes.data[(i * blendshapes.size[1] + k) * blendshapes.size[2] + j]);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
vertex.push(1.0);
|
|
57
|
+
let vertex_lbs = [0.0, 0.0, 0.0, 0.0];
|
|
58
|
+
let jointIndex = char.mesh[mesh_index].jointIndex;
|
|
59
|
+
let jointWeight = char.mesh[mesh_index].jointWeight;
|
|
60
|
+
for (let j = 0; j < char.maxJointsPerVertex; j++) {
|
|
61
|
+
let curJointIndex = jointIndex[i * char.maxJointsPerVertex + j];
|
|
62
|
+
if(curJointIndex < char.skeleton.length){
|
|
63
|
+
let vertex_transformed = mvmul(joint_matrices[curJointIndex], vertex);
|
|
64
|
+
for (let k = 0; k < 4; k++)vertex_lbs[k] += jointWeight[i * char.maxJointsPerVertex + j] * vertex_transformed[k];
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
for (let k = 0; k < 3; k++)vertex_lbs[k] /= vertex_lbs[3];
|
|
69
|
+
geometry.push([vertex_lbs[0], vertex_lbs[1], vertex_lbs[2]]);
|
|
70
|
+
}
|
|
71
|
+
result.push(geometry);
|
|
72
|
+
}
|
|
73
|
+
return result;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export function getPCATextures(data: GLPipelineCharData, frame_data: IBRAnimationFrameData_NN): ImageData[] {
|
|
77
|
+
let char = data.char!;
|
|
78
|
+
let result: ImageData[] = [];
|
|
79
|
+
|
|
80
|
+
for (let mesh_index = 0; mesh_index < char.mesh.length; mesh_index++) {
|
|
81
|
+
let texture_model = char.mesh[mesh_index].textureModels[frame_data.mesh[mesh_index].textureModelIndex];
|
|
82
|
+
let texture: number[][][] = [];
|
|
83
|
+
let texture_data = new Uint8ClampedArray(texture_model.data.size[1] * texture_model.data.size[2] * 4);
|
|
84
|
+
|
|
85
|
+
let mean_weight = 1.0;
|
|
86
|
+
if(texture_model.scalingFactor)mean_weight *= Number(texture_model.scalingFactor.data[0]);
|
|
87
|
+
for (let j = 0; j < texture_model.data.size[1]; j++){
|
|
88
|
+
let row: number[][] = [];
|
|
89
|
+
for (let k = 0; k < texture_model.data.size[2]; k++){
|
|
90
|
+
let pixel: number[] = [];
|
|
91
|
+
for (let l = 0; l < 3; l++)pixel.push(mean_weight * sampleUniformPCATensor(texture_model.data.data, (j * texture_model.data.size[2] + k) * 3 + l));
|
|
92
|
+
row.push(pixel);
|
|
93
|
+
}
|
|
94
|
+
texture.push(row);
|
|
95
|
+
}
|
|
96
|
+
for (let i = 1; i < texture_model.data.size[0]; i++) {
|
|
97
|
+
let weight = frame_data.mesh[mesh_index].texturePCAWeights[i - 1];
|
|
98
|
+
if(texture_model.scalingFactor)weight *= Number(texture_model.scalingFactor.data[i]);
|
|
99
|
+
for (let j = 0; j < texture_model.data.size[1]; j++){
|
|
100
|
+
for (let k = 0; k < texture_model.data.size[2]; k++){
|
|
101
|
+
for (let l = 0; l < 3; l++)texture[j][k][l] += weight * sampleUniformPCATensor(texture_model.data.data, ((i * texture_model.data.size[1] + j) * texture_model.data.size[2] + k) * 3 + l);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
for (let j = 0; j < texture_model.data.size[1]; j++){
|
|
106
|
+
let row: number[][] = [];
|
|
107
|
+
for (let k = 0; k < texture_model.data.size[2]; k++){
|
|
108
|
+
let pixel: number[] = [];
|
|
109
|
+
for (let l = 0; l < 3; l++)texture_data[(j * texture_model.data.size[2] + k) * 4 + l] = Math.round(texture[texture_model.data.size[1] - 1 - j][k][2 - l] * 255);
|
|
110
|
+
texture_data[(j * texture_model.data.size[2] + k) * 4 + 3] = 0xFF;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
result.push(new ImageData(texture_data, texture_model.data.size[1], texture_model.data.size[2]));
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return result;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
interface PolygonMeshInfo {
|
|
120
|
+
vertices: number[][],
|
|
121
|
+
uv: number[][],
|
|
122
|
+
polygons: number[][]
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function exportAsWavefrontObj(geometries: PolygonMeshInfo[]): string {
|
|
126
|
+
let vertex_string = "";
|
|
127
|
+
let polygon_string = "";
|
|
128
|
+
let vertex_index_offset = 1;
|
|
129
|
+
let geometry_offset = 0;
|
|
130
|
+
for(const geometry of geometries){
|
|
131
|
+
for(const vertex of geometry.vertices)vertex_string += `v ${vertex[0]} ${vertex[1]} ${vertex[2]}\n`
|
|
132
|
+
for(const uv of geometry.uv)vertex_string += `vt ${uv[0]} ${uv[1]}}\n`
|
|
133
|
+
|
|
134
|
+
polygon_string += `g Obj${geometry_offset}\n`
|
|
135
|
+
for(const poly of geometry.polygons){
|
|
136
|
+
let poly_str = poly.map(x => (x + vertex_index_offset).toString()).map(x => `${x}/${x}`).join(" ");
|
|
137
|
+
polygon_string += `f ${poly_str}\n`;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
vertex_index_offset += geometry.vertices.length;
|
|
141
|
+
geometry_offset += 1;
|
|
142
|
+
}
|
|
143
|
+
return vertex_string + polygon_string;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export function getWavefrontObjFromVertices(data: GLPipelineCharData, vertices: number[][][]): string {
|
|
147
|
+
function unflattenUniform(data: TensorDataType, new_axis_size: number): number[][] {
|
|
148
|
+
let result: number[][] = [];
|
|
149
|
+
for (let i = 0; i < data.length; i++) {
|
|
150
|
+
if (0 == i % new_axis_size) result.push([]);
|
|
151
|
+
result[result.length - 1].push(sampleUniformTensor(data, i));
|
|
152
|
+
}
|
|
153
|
+
return result;
|
|
154
|
+
}
|
|
155
|
+
function unflatten(data: TensorDataType, new_axis_size: number): number[][] {
|
|
156
|
+
let result: number[][] = [];
|
|
157
|
+
for (let i = 0; i < data.length; i++) {
|
|
158
|
+
if (0 == i % new_axis_size) result.push([]);
|
|
159
|
+
result[result.length - 1].push(Number(data[i]));
|
|
160
|
+
}
|
|
161
|
+
return result;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
let char = data.char!;
|
|
165
|
+
let geometries: PolygonMeshInfo[] = [];
|
|
166
|
+
for (let mesh_index = 0; mesh_index < char.mesh.length; mesh_index++) {
|
|
167
|
+
geometries.push({
|
|
168
|
+
vertices: vertices[mesh_index],
|
|
169
|
+
uv: unflattenUniform(char.mesh[mesh_index].UVCoord.data, 2),
|
|
170
|
+
polygons: unflatten(char.mesh[mesh_index].triangles.data, 3)
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
return exportAsWavefrontObj(geometries);
|
|
174
|
+
}
|
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
export type vec3 = [number, number, number];
|
|
2
|
+
export type vec4 = [number, number, number, number];
|
|
3
|
+
export type mat3 = [vec3, vec3, vec3];
|
|
4
|
+
export type mat4 = [vec4, vec4, vec4, vec4];
|
|
5
|
+
|
|
6
|
+
export function length(vec: number[]): number { return Math.sqrt(vec.reduce((sum, val) => (sum + val * val), 0.0)); }
|
|
7
|
+
export function normalize(vec: number[]): number[] {
|
|
8
|
+
let vec_len = length(vec);
|
|
9
|
+
return vec.map((x) => (x / vec_len));
|
|
10
|
+
}
|
|
11
|
+
export function mvmul(lhs: number[][], rhs: number[]): number[] {
|
|
12
|
+
// lhs is row major.
|
|
13
|
+
const size_row = lhs.length;
|
|
14
|
+
const size_col = rhs.length;
|
|
15
|
+
let result: number[] = Array(size_row);
|
|
16
|
+
for (let i = 0; i < size_row; i++) {
|
|
17
|
+
result[i] = 0.0;
|
|
18
|
+
for (let j = 0; j < size_col; j++)result[i] += lhs[i][j] * rhs[j];
|
|
19
|
+
}
|
|
20
|
+
return result;
|
|
21
|
+
}
|
|
22
|
+
export function mmul(lhs: number[][], rhs: number[][]): number[][] {
|
|
23
|
+
// lhs and rhs is row major.
|
|
24
|
+
const size_row = lhs.length;
|
|
25
|
+
const size_mid = rhs.length;
|
|
26
|
+
const size_col = rhs[0].length;
|
|
27
|
+
let result: number[][] = Array(size_row);
|
|
28
|
+
for (let i = 0; i < size_row; i++) {
|
|
29
|
+
let result_row: number[] = Array(size_col);
|
|
30
|
+
for (let j = 0; j < size_col; j++){
|
|
31
|
+
result_row[j] = 0.0;
|
|
32
|
+
for (let k = 0; k < size_mid; k++)result_row[j] += lhs[i][k] * rhs[k][j];
|
|
33
|
+
}
|
|
34
|
+
result[i] = result_row;
|
|
35
|
+
}
|
|
36
|
+
return result;
|
|
37
|
+
}
|
|
38
|
+
export function transpose(mat: number[][]): number[][] {
|
|
39
|
+
// lhs and rhs is row major.
|
|
40
|
+
const size_row = mat.length;
|
|
41
|
+
const size_col = mat[0].length;
|
|
42
|
+
let result: number[][] = Array(size_col);
|
|
43
|
+
for (let i = 0; i < size_col; i++) {
|
|
44
|
+
let result_row: number[] = Array(size_row);
|
|
45
|
+
for (let j = 0; j < size_row; j++) {
|
|
46
|
+
result_row[j] = mat[j][i];
|
|
47
|
+
}
|
|
48
|
+
result[i] = result_row;
|
|
49
|
+
}
|
|
50
|
+
return result;
|
|
51
|
+
}
|
|
52
|
+
export function identity(size: number): number[][] {
|
|
53
|
+
let result: number[][] = Array(size);
|
|
54
|
+
for (let i = 0; i < size; i++) {
|
|
55
|
+
let result_row: number[] = Array(size);
|
|
56
|
+
for (let j = 0; j < size; j++)result_row[j] = (i == j ? 1.0 : 0.0);
|
|
57
|
+
result[i] = result_row;
|
|
58
|
+
}
|
|
59
|
+
return result;
|
|
60
|
+
}
|
|
61
|
+
export function det3(mat: mat3): number {
|
|
62
|
+
let result = 0.0;
|
|
63
|
+
result += mat[0][0] * mat[1][1] * mat[2][2];
|
|
64
|
+
result += mat[0][1] * mat[1][2] * mat[2][0];
|
|
65
|
+
result += mat[0][2] * mat[1][0] * mat[2][1];
|
|
66
|
+
result -= mat[0][2] * mat[1][1] * mat[2][0];
|
|
67
|
+
result -= mat[0][1] * mat[1][0] * mat[2][2];
|
|
68
|
+
result -= mat[0][0] * mat[1][2] * mat[2][1];
|
|
69
|
+
return result;
|
|
70
|
+
}
|
|
71
|
+
export function inv3(mat: mat3): mat3 {
|
|
72
|
+
const invdet = 1.0 / det3(mat);
|
|
73
|
+
return [
|
|
74
|
+
[(mat[1][1] * mat[2][2] - mat[2][1] * mat[1][2]) * invdet, (mat[0][2] * mat[2][1] - mat[0][1] * mat[2][2]) * invdet, (mat[0][1] * mat[1][2] - mat[0][2] * mat[1][1]) * invdet],
|
|
75
|
+
[(mat[1][2] * mat[2][0] - mat[1][0] * mat[2][2]) * invdet, (mat[0][0] * mat[2][2] - mat[0][2] * mat[2][0]) * invdet, (mat[1][0] * mat[0][2] - mat[0][0] * mat[1][2]) * invdet],
|
|
76
|
+
[(mat[1][0] * mat[2][1] - mat[2][0] * mat[1][1]) * invdet, (mat[2][0] * mat[0][1] - mat[0][0] * mat[2][1]) * invdet, (mat[0][0] * mat[1][1] - mat[1][0] * mat[0][1]) * invdet]
|
|
77
|
+
];
|
|
78
|
+
}
|
|
79
|
+
export function inv4(mat: mat4): mat4 {
|
|
80
|
+
//Gauss-Jordan inverse from http://dev.gameres.com/Program/Visual/3D/Mnquick.htm
|
|
81
|
+
let result: mat4 = [[...mat[0]], [...mat[1]], [...mat[2]], [...mat[3]]];
|
|
82
|
+
let xPos = [0, 0, 0, 0], yPos = [0, 0, 0, 0];
|
|
83
|
+
let flip = 1;
|
|
84
|
+
let det = 1, tmp = 0;
|
|
85
|
+
|
|
86
|
+
for (let k = 0; k < 4; k++) {
|
|
87
|
+
//Choose pivot element
|
|
88
|
+
let pivot = 0;
|
|
89
|
+
for (let i = k; i < 4; i++) {
|
|
90
|
+
for (let j = k; j < 4; j++) {
|
|
91
|
+
if (Math.abs(result[i][j]) > pivot) { pivot = Math.abs(result[i][j]); xPos[k] = i; yPos[k] = j; }
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
if (pivot < Number.EPSILON) return new Array(4).fill(new Array(4).fill(NaN)) as mat4;
|
|
95
|
+
//Swap pivot element to lead element
|
|
96
|
+
if (xPos[k] != k) {
|
|
97
|
+
flip = -flip;
|
|
98
|
+
for (let i = 0; i < 4; i++) {
|
|
99
|
+
tmp = result[k][i];
|
|
100
|
+
result[k][i] = result[xPos[k]][i];
|
|
101
|
+
result[xPos[k]][i] = tmp;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
if (yPos[k] != k) {
|
|
105
|
+
flip = -flip;
|
|
106
|
+
for (let i = 0; i < 4; i++) {
|
|
107
|
+
tmp = result[i][k];
|
|
108
|
+
result[i][k] = result[i][yPos[k]];
|
|
109
|
+
result[i][yPos[k]] = tmp;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
det *= result[k][k];
|
|
113
|
+
|
|
114
|
+
//elimination
|
|
115
|
+
result[k][k] = 1 / result[k][k];
|
|
116
|
+
for (let j = 0; j < 4; j++) {
|
|
117
|
+
if (j != k) result[k][j] *= result[k][k];
|
|
118
|
+
}
|
|
119
|
+
for (let i = 0; i < 4; i++) {
|
|
120
|
+
if (i != k) {
|
|
121
|
+
for (let j = 0; j < 4; j++) {
|
|
122
|
+
if (j != k) result[i][j] -= result[i][k] * result[k][j];
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
for (let i = 0; i < 4; i++) {
|
|
127
|
+
if (i != k) result[i][k] *= -result[k][k];
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
for (let k = 3; k >= 0; k--) {
|
|
132
|
+
if (yPos[k] != k) {
|
|
133
|
+
for (let i = 0; i < 4; i++) {
|
|
134
|
+
tmp = result[k][i];
|
|
135
|
+
result[k][i] = result[yPos[k]][i];
|
|
136
|
+
result[yPos[k]][i] = tmp;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
if (xPos[k] != k) {
|
|
140
|
+
for (let i = 0; i < 4; i++) {
|
|
141
|
+
tmp = result[i][k];
|
|
142
|
+
result[i][k] = result[i][xPos[k]];
|
|
143
|
+
result[i][xPos[k]] = tmp;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
return result;
|
|
148
|
+
}
|
|
149
|
+
export function quaternion_conj(q: vec4): vec4 {
|
|
150
|
+
return [q[0], -q[1], -q[2], -q[3]];
|
|
151
|
+
}
|
|
152
|
+
export function quaternion_mul(lhs: vec4, rhs: vec4): vec4 {
|
|
153
|
+
return [
|
|
154
|
+
lhs[0] * rhs[0] - lhs[1] * rhs[1] - lhs[2] * rhs[2] - lhs[3] * rhs[3],
|
|
155
|
+
lhs[0] * rhs[1] + lhs[1] * rhs[0] + lhs[2] * rhs[3] - lhs[3] * rhs[2],
|
|
156
|
+
lhs[0] * rhs[2] - lhs[1] * rhs[3] + lhs[2] * rhs[0] + lhs[3] * rhs[1],
|
|
157
|
+
lhs[0] * rhs[3] + lhs[1] * rhs[2] - lhs[2] * rhs[1] + lhs[3] * rhs[0],
|
|
158
|
+
];
|
|
159
|
+
}
|
|
160
|
+
export function quaternion_to_mat(q: vec4): mat3 {
|
|
161
|
+
const [r, i, j, k] = q;
|
|
162
|
+
return [
|
|
163
|
+
[r * r + i * i - j * j - k * k, 2 * (i * j - k * r), 2 * (i * k + j * r)],
|
|
164
|
+
[2 * (i * j + k * r), r * r - i * i + j * j - k * k, 2 * (j * k - i * r)],
|
|
165
|
+
[2 * (i * k - j * r), 2 * (j * k + i * r), r * r - i * i - j * j + k * k]
|
|
166
|
+
];
|
|
167
|
+
}
|
|
168
|
+
export function mat_to_quaternion(mat: mat3 | mat4): vec4 {
|
|
169
|
+
let trace: number;
|
|
170
|
+
let result: vec4;
|
|
171
|
+
if (mat[2][2] < 0.0) {
|
|
172
|
+
if (mat[0][0] > mat[1][1]) {
|
|
173
|
+
trace = 1.0 + mat[0][0] - mat[1][1] - mat[2][2];
|
|
174
|
+
result = [mat[2][1] - mat[1][2], trace, mat[1][0] + mat[0][1], mat[0][2] + mat[2][0]];
|
|
175
|
+
}
|
|
176
|
+
else {
|
|
177
|
+
trace = 1.0 - mat[0][0] + mat[1][1] - mat[2][2];
|
|
178
|
+
result = [mat[0][2] - mat[2][0], mat[1][0] + mat[0][1], trace, mat[2][1] + mat[1][2]];
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
else {
|
|
182
|
+
if (mat[0][0] < -mat[1][1]) {
|
|
183
|
+
trace = 1.0 - mat[0][0] - mat[1][1] + mat[2][2];
|
|
184
|
+
result = [mat[1][0] - mat[0][1], mat[0][2] + mat[2][0], mat[2][1] + mat[1][2], trace];
|
|
185
|
+
}
|
|
186
|
+
else {
|
|
187
|
+
trace = 1.0 + mat[0][0] + mat[1][1] + mat[2][2];
|
|
188
|
+
result = [trace, mat[2][1] - mat[1][2], mat[0][2] - mat[2][0], mat[1][0] - mat[0][1]];
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
let trace_2sqrt = 2.0 * Math.sqrt(trace); // need further check
|
|
192
|
+
for (let i = 0; i < 4; i++)result[i] /= trace_2sqrt;
|
|
193
|
+
return result;
|
|
194
|
+
}
|
|
195
|
+
export function GivensTransform(mat: number[][], row0: number, row1: number, angle: number): void {
|
|
196
|
+
// The angle should be in radian.
|
|
197
|
+
|
|
198
|
+
const s = Math.sin(angle);
|
|
199
|
+
const c = Math.cos(angle);
|
|
200
|
+
const size_col = mat[0].length;
|
|
201
|
+
for (let i = 0; i < size_col; i++) {
|
|
202
|
+
const a1 = c * mat[row0][i] - s * mat[row1][i];
|
|
203
|
+
const a2 = s * mat[row0][i] + c * mat[row1][i];
|
|
204
|
+
mat[row0][i] = a1;
|
|
205
|
+
mat[row1][i] = a2;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
export function Euler_to_mat(angles: [number, number, number], order: string = "xyz"): mat3 {
|
|
209
|
+
// The angle should be in radian.
|
|
210
|
+
let result = identity(3) as mat3;
|
|
211
|
+
|
|
212
|
+
switch(order){
|
|
213
|
+
case "xyz":
|
|
214
|
+
GivensTransform(result, 1, 2, angles[0]);
|
|
215
|
+
GivensTransform(result, 2, 0, angles[1]);
|
|
216
|
+
GivensTransform(result, 0, 1, angles[2]);
|
|
217
|
+
break
|
|
218
|
+
case "yzx":
|
|
219
|
+
GivensTransform(result, 0, 1, angles[2]);
|
|
220
|
+
GivensTransform(result, 1, 2, angles[0]);
|
|
221
|
+
GivensTransform(result, 2, 0, angles[1]);
|
|
222
|
+
break
|
|
223
|
+
case "zxy":
|
|
224
|
+
GivensTransform(result, 1, 2, angles[0]);
|
|
225
|
+
GivensTransform(result, 2, 0, angles[1]);
|
|
226
|
+
GivensTransform(result, 0, 1, angles[2]);
|
|
227
|
+
break
|
|
228
|
+
case "xzy":
|
|
229
|
+
GivensTransform(result, 1, 2, angles[0]);
|
|
230
|
+
GivensTransform(result, 0, 1, angles[2]);
|
|
231
|
+
GivensTransform(result, 2, 0, angles[1]);
|
|
232
|
+
break
|
|
233
|
+
case "yxz":
|
|
234
|
+
GivensTransform(result, 2, 0, angles[1]);
|
|
235
|
+
GivensTransform(result, 1, 2, angles[0]);
|
|
236
|
+
GivensTransform(result, 0, 1, angles[2]);
|
|
237
|
+
break
|
|
238
|
+
case "zyx":
|
|
239
|
+
GivensTransform(result, 0, 1, angles[2]);
|
|
240
|
+
GivensTransform(result, 2, 0, angles[1]);
|
|
241
|
+
GivensTransform(result, 1, 2, angles[0]);
|
|
242
|
+
break
|
|
243
|
+
default:
|
|
244
|
+
throw Error("Unrecognizable rotation order.");
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
return result;
|
|
248
|
+
}
|
|
249
|
+
export function make_homogeneous(mat: mat3, translation: vec3): mat4 {
|
|
250
|
+
// lhs and rhs is row major.
|
|
251
|
+
return [
|
|
252
|
+
[mat[0][0], mat[0][1], mat[0][2], translation[0]],
|
|
253
|
+
[mat[1][0], mat[1][1], mat[1][2], translation[1]],
|
|
254
|
+
[mat[2][0], mat[2][1], mat[2][2], translation[2]],
|
|
255
|
+
[0.0, 0.0, 0.0, 1.0]
|
|
256
|
+
]
|
|
257
|
+
}
|
|
258
|
+
export function flatten(mat: number[][]): number[] {
|
|
259
|
+
const size_row = mat.length;
|
|
260
|
+
const size_col = mat[0].length;
|
|
261
|
+
let result: number[] = Array(size_row * size_col);
|
|
262
|
+
for (let i = 0; i < size_row; i++) {
|
|
263
|
+
for (let j = 0; j < size_col; j++)result[i * size_col + j] = mat[i][j];
|
|
264
|
+
}
|
|
265
|
+
return result;
|
|
266
|
+
}
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
import test from 'node:test';
|
|
3
|
+
import assert from 'node:assert/strict';
|
|
4
|
+
import { checkCapability } from '../capability-checker';
|
|
5
|
+
|
|
6
|
+
type WorkerConstructor = new (url: string) => {
|
|
7
|
+
onmessage: ((event: { data: unknown }) => void) | null;
|
|
8
|
+
onerror: ((event: unknown) => void) | null;
|
|
9
|
+
terminate(): void;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
function installCapabilityBrowser(WorkerClass?: WorkerConstructor) {
|
|
13
|
+
const globals = globalThis as typeof globalThis & Record<string, unknown>;
|
|
14
|
+
const descriptors = new Map<string, PropertyDescriptor | undefined>();
|
|
15
|
+
const remember = (name: string) => descriptors.set(
|
|
16
|
+
name,
|
|
17
|
+
Object.getOwnPropertyDescriptor(globalThis, name),
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
for (const name of [
|
|
21
|
+
'window',
|
|
22
|
+
'Worker',
|
|
23
|
+
'AudioContext',
|
|
24
|
+
'AudioWorkletNode',
|
|
25
|
+
'setTimeout',
|
|
26
|
+
'clearTimeout',
|
|
27
|
+
]) {
|
|
28
|
+
remember(name);
|
|
29
|
+
}
|
|
30
|
+
const createObjectURL = URL.createObjectURL;
|
|
31
|
+
const revokeObjectURL = URL.revokeObjectURL;
|
|
32
|
+
|
|
33
|
+
Object.defineProperty(globalThis, 'window', { configurable: true, value: globalThis });
|
|
34
|
+
Object.defineProperty(globalThis, 'AudioContext', { configurable: true, value: class {} });
|
|
35
|
+
Object.defineProperty(globalThis, 'AudioWorkletNode', { configurable: true, value: class {} });
|
|
36
|
+
Object.defineProperty(globalThis, 'Worker', {
|
|
37
|
+
configurable: true,
|
|
38
|
+
value: WorkerClass,
|
|
39
|
+
});
|
|
40
|
+
Object.defineProperty(globalThis, 'setTimeout', {
|
|
41
|
+
configurable: true,
|
|
42
|
+
value: (callback: () => void) => {
|
|
43
|
+
queueMicrotask(callback);
|
|
44
|
+
return 1;
|
|
45
|
+
},
|
|
46
|
+
});
|
|
47
|
+
Object.defineProperty(globalThis, 'clearTimeout', {
|
|
48
|
+
configurable: true,
|
|
49
|
+
value: () => {},
|
|
50
|
+
});
|
|
51
|
+
Object.defineProperty(globalThis, 'isSecureContext', {
|
|
52
|
+
configurable: true,
|
|
53
|
+
value: true,
|
|
54
|
+
});
|
|
55
|
+
URL.createObjectURL = () => 'blob:test-worker';
|
|
56
|
+
URL.revokeObjectURL = () => {};
|
|
57
|
+
|
|
58
|
+
return () => {
|
|
59
|
+
for (const [name, descriptor] of descriptors) {
|
|
60
|
+
if (descriptor) {
|
|
61
|
+
Object.defineProperty(globalThis, name, descriptor);
|
|
62
|
+
} else {
|
|
63
|
+
delete globals[name];
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
delete globals.isSecureContext;
|
|
67
|
+
URL.createObjectURL = createObjectURL;
|
|
68
|
+
URL.revokeObjectURL = revokeObjectURL;
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
test('Worker probe timeout is retried and downgraded to a warning', async () => {
|
|
73
|
+
const workers: Array<{ terminated: boolean }> = [];
|
|
74
|
+
class SlowWorker {
|
|
75
|
+
onmessage = null;
|
|
76
|
+
onerror = null;
|
|
77
|
+
terminated = false;
|
|
78
|
+
|
|
79
|
+
constructor() {
|
|
80
|
+
workers.push(this);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
terminate() {
|
|
84
|
+
this.terminated = true;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
const restore = installCapabilityBrowser(SlowWorker);
|
|
88
|
+
|
|
89
|
+
try {
|
|
90
|
+
const report = await checkCapability(true);
|
|
91
|
+
const worker = report.items.find(item => item.name === 'worker');
|
|
92
|
+
|
|
93
|
+
assert.equal(report.supported, true);
|
|
94
|
+
assert.equal(report.fatalCount, 0);
|
|
95
|
+
assert.equal(report.warnCount, 1);
|
|
96
|
+
assert.equal(worker?.supported, false);
|
|
97
|
+
assert.equal(worker?.severity, 'warn');
|
|
98
|
+
assert.equal(workers.length, 2);
|
|
99
|
+
assert.ok(workers.every(instance => instance.terminated));
|
|
100
|
+
} finally {
|
|
101
|
+
restore();
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
test('Worker probe accepts a successful retry after an initial timeout', async () => {
|
|
106
|
+
let attempts = 0;
|
|
107
|
+
class RetriedWorker {
|
|
108
|
+
onmessage: ((event: { data: unknown }) => void) | null = null;
|
|
109
|
+
onerror = null;
|
|
110
|
+
|
|
111
|
+
constructor() {
|
|
112
|
+
attempts += 1;
|
|
113
|
+
if (attempts === 2) {
|
|
114
|
+
queueMicrotask(() => this.onmessage?.({ data: 'ok' }));
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
terminate() {}
|
|
119
|
+
}
|
|
120
|
+
const restore = installCapabilityBrowser(RetriedWorker);
|
|
121
|
+
|
|
122
|
+
try {
|
|
123
|
+
const report = await checkCapability(true);
|
|
124
|
+
const worker = report.items.find(item => item.name === 'worker');
|
|
125
|
+
|
|
126
|
+
assert.equal(report.supported, true);
|
|
127
|
+
assert.equal(report.warnCount, 0);
|
|
128
|
+
assert.equal(worker?.supported, true);
|
|
129
|
+
assert.deepEqual(worker?.detail, { attempts: 2 });
|
|
130
|
+
} finally {
|
|
131
|
+
restore();
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
test('Worker probe keeps explicit creation errors fatal', async () => {
|
|
136
|
+
class FailedWorker {
|
|
137
|
+
onmessage = null;
|
|
138
|
+
onerror: ((event: unknown) => void) | null = null;
|
|
139
|
+
|
|
140
|
+
constructor() {
|
|
141
|
+
queueMicrotask(() => this.onerror?.(new Error('worker failed')));
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
terminate() {}
|
|
145
|
+
}
|
|
146
|
+
const restore = installCapabilityBrowser(FailedWorker);
|
|
147
|
+
|
|
148
|
+
try {
|
|
149
|
+
const report = await checkCapability(true);
|
|
150
|
+
const worker = report.items.find(item => item.name === 'worker');
|
|
151
|
+
|
|
152
|
+
assert.equal(report.supported, false);
|
|
153
|
+
assert.equal(report.fatalCount, 1);
|
|
154
|
+
assert.equal(worker?.severity, 'fatal');
|
|
155
|
+
assert.equal(worker?.reason, 'Web Worker 创建失败');
|
|
156
|
+
} finally {
|
|
157
|
+
restore();
|
|
158
|
+
}
|
|
159
|
+
});
|