@daidr/minecraft-skin-renderer 0.1.0
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 +247 -0
- package/dist/animation/AnimationController.d.mts +15 -0
- package/dist/animation/AnimationController.d.mts.map +1 -0
- package/dist/animation/AnimationController.mjs +146 -0
- package/dist/animation/AnimationController.mjs.map +1 -0
- package/dist/animation/easing.mjs +9 -0
- package/dist/animation/easing.mjs.map +1 -0
- package/dist/animation/presets/fly.mjs +176 -0
- package/dist/animation/presets/fly.mjs.map +1 -0
- package/dist/animation/presets/idle.mjs +114 -0
- package/dist/animation/presets/idle.mjs.map +1 -0
- package/dist/animation/presets/index.mjs +7 -0
- package/dist/animation/presets/run.mjs +126 -0
- package/dist/animation/presets/run.mjs.map +1 -0
- package/dist/animation/presets/utils.mjs +166 -0
- package/dist/animation/presets/utils.mjs.map +1 -0
- package/dist/animation/presets/walk.mjs +76 -0
- package/dist/animation/presets/walk.mjs.map +1 -0
- package/dist/animation/types.d.mts +50 -0
- package/dist/animation/types.d.mts.map +1 -0
- package/dist/animation/types.mjs +22 -0
- package/dist/animation/types.mjs.map +1 -0
- package/dist/core/camera/Camera.d.mts +33 -0
- package/dist/core/camera/Camera.d.mts.map +1 -0
- package/dist/core/camera/Camera.mjs +77 -0
- package/dist/core/camera/Camera.mjs.map +1 -0
- package/dist/core/camera/OrbitControls.d.mts +50 -0
- package/dist/core/camera/OrbitControls.d.mts.map +1 -0
- package/dist/core/camera/OrbitControls.mjs +170 -0
- package/dist/core/camera/OrbitControls.mjs.map +1 -0
- package/dist/core/math/index.mjs +6 -0
- package/dist/core/math/mat4.d.mts +61 -0
- package/dist/core/math/mat4.d.mts.map +1 -0
- package/dist/core/math/mat4.mjs +513 -0
- package/dist/core/math/mat4.mjs.map +1 -0
- package/dist/core/math/quat.d.mts +61 -0
- package/dist/core/math/quat.d.mts.map +1 -0
- package/dist/core/math/quat.mjs +360 -0
- package/dist/core/math/quat.mjs.map +1 -0
- package/dist/core/math/utils.d.mts +43 -0
- package/dist/core/math/utils.d.mts.map +1 -0
- package/dist/core/math/utils.mjs +85 -0
- package/dist/core/math/utils.mjs.map +1 -0
- package/dist/core/math/vec3.d.mts +61 -0
- package/dist/core/math/vec3.d.mts.map +1 -0
- package/dist/core/math/vec3.mjs +198 -0
- package/dist/core/math/vec3.mjs.map +1 -0
- package/dist/core/plugins/registry.mjs +29 -0
- package/dist/core/plugins/registry.mjs.map +1 -0
- package/dist/core/plugins/types.d.mts +34 -0
- package/dist/core/plugins/types.d.mts.map +1 -0
- package/dist/core/renderer/registry.d.mts +46 -0
- package/dist/core/renderer/registry.d.mts.map +1 -0
- package/dist/core/renderer/registry.mjs +46 -0
- package/dist/core/renderer/registry.mjs.map +1 -0
- package/dist/core/renderer/shader-composer.mjs +30 -0
- package/dist/core/renderer/shader-composer.mjs.map +1 -0
- package/dist/core/renderer/types.d.mts +172 -0
- package/dist/core/renderer/types.d.mts.map +1 -0
- package/dist/core/renderer/types.mjs +90 -0
- package/dist/core/renderer/types.mjs.map +1 -0
- package/dist/core/renderer/utils.mjs +20 -0
- package/dist/core/renderer/utils.mjs.map +1 -0
- package/dist/core/renderer/webgl/WebGLBuffer.mjs +57 -0
- package/dist/core/renderer/webgl/WebGLBuffer.mjs.map +1 -0
- package/dist/core/renderer/webgl/WebGLPipeline.mjs +259 -0
- package/dist/core/renderer/webgl/WebGLPipeline.mjs.map +1 -0
- package/dist/core/renderer/webgl/WebGLRenderer.mjs +140 -0
- package/dist/core/renderer/webgl/WebGLRenderer.mjs.map +1 -0
- package/dist/core/renderer/webgl/WebGLTexture.mjs +87 -0
- package/dist/core/renderer/webgl/WebGLTexture.mjs.map +1 -0
- package/dist/core/renderer/webgl/plugin.d.mts +8 -0
- package/dist/core/renderer/webgl/plugin.d.mts.map +1 -0
- package/dist/core/renderer/webgl/plugin.mjs +24 -0
- package/dist/core/renderer/webgl/plugin.mjs.map +1 -0
- package/dist/core/renderer/webgl/shaders/index.mjs +81 -0
- package/dist/core/renderer/webgl/shaders/index.mjs.map +1 -0
- package/dist/core/renderer/webgpu/WebGPUBuffer.mjs +52 -0
- package/dist/core/renderer/webgpu/WebGPUBuffer.mjs.map +1 -0
- package/dist/core/renderer/webgpu/WebGPUPipeline.mjs +167 -0
- package/dist/core/renderer/webgpu/WebGPUPipeline.mjs.map +1 -0
- package/dist/core/renderer/webgpu/WebGPURenderer.mjs +299 -0
- package/dist/core/renderer/webgpu/WebGPURenderer.mjs.map +1 -0
- package/dist/core/renderer/webgpu/WebGPUTexture.mjs +126 -0
- package/dist/core/renderer/webgpu/WebGPUTexture.mjs.map +1 -0
- package/dist/core/renderer/webgpu/plugin.d.mts +8 -0
- package/dist/core/renderer/webgpu/plugin.d.mts.map +1 -0
- package/dist/core/renderer/webgpu/plugin.mjs +35 -0
- package/dist/core/renderer/webgpu/plugin.mjs.map +1 -0
- package/dist/core/renderer/webgpu/shaders/index.mjs +84 -0
- package/dist/core/renderer/webgpu/shaders/index.mjs.map +1 -0
- package/dist/index.d.mts +16 -0
- package/dist/index.mjs +19 -0
- package/dist/model/PlayerModel.d.mts +8 -0
- package/dist/model/PlayerModel.d.mts.map +1 -0
- package/dist/model/PlayerModel.mjs +253 -0
- package/dist/model/PlayerModel.mjs.map +1 -0
- package/dist/model/bone-utils.mjs +3 -0
- package/dist/model/constants.mjs +20 -0
- package/dist/model/constants.mjs.map +1 -0
- package/dist/model/geometry/BoxGeometry.mjs +316 -0
- package/dist/model/geometry/BoxGeometry.mjs.map +1 -0
- package/dist/model/geometry/index.mjs +3 -0
- package/dist/model/index.mjs +10 -0
- package/dist/model/types.d.mts +64 -0
- package/dist/model/types.d.mts.map +1 -0
- package/dist/model/types.mjs +67 -0
- package/dist/model/types.mjs.map +1 -0
- package/dist/model/uv/CapeUV.mjs +25 -0
- package/dist/model/uv/CapeUV.mjs.map +1 -0
- package/dist/model/uv/SkinUV.mjs +104 -0
- package/dist/model/uv/SkinUV.mjs.map +1 -0
- package/dist/model/uv/common.mjs +147 -0
- package/dist/model/uv/common.mjs.map +1 -0
- package/dist/panorama.d.mts +2 -0
- package/dist/panorama.mjs +3 -0
- package/dist/plugins/panorama/PanoramaRenderer.mjs +94 -0
- package/dist/plugins/panorama/PanoramaRenderer.mjs.map +1 -0
- package/dist/plugins/panorama/SkyboxGeometry.mjs +132 -0
- package/dist/plugins/panorama/SkyboxGeometry.mjs.map +1 -0
- package/dist/plugins/panorama/plugin.d.mts +8 -0
- package/dist/plugins/panorama/plugin.d.mts.map +1 -0
- package/dist/plugins/panorama/plugin.mjs +13 -0
- package/dist/plugins/panorama/plugin.mjs.map +1 -0
- package/dist/plugins/panorama/shaders/webgl.mjs +59 -0
- package/dist/plugins/panorama/shaders/webgl.mjs.map +1 -0
- package/dist/plugins/panorama/shaders/webgpu.mjs +70 -0
- package/dist/plugins/panorama/shaders/webgpu.mjs.map +1 -0
- package/dist/texture/TextureLoader.d.mts +22 -0
- package/dist/texture/TextureLoader.d.mts.map +1 -0
- package/dist/texture/TextureLoader.mjs +114 -0
- package/dist/texture/TextureLoader.mjs.map +1 -0
- package/dist/viewer/BoneMatrixComputer.mjs +82 -0
- package/dist/viewer/BoneMatrixComputer.mjs.map +1 -0
- package/dist/viewer/RenderLoop.mjs +45 -0
- package/dist/viewer/RenderLoop.mjs.map +1 -0
- package/dist/viewer/RenderState.mjs +44 -0
- package/dist/viewer/RenderState.mjs.map +1 -0
- package/dist/viewer/ResourceManager.mjs +278 -0
- package/dist/viewer/ResourceManager.mjs.map +1 -0
- package/dist/viewer/SkinViewer.d.mts +98 -0
- package/dist/viewer/SkinViewer.d.mts.map +1 -0
- package/dist/viewer/SkinViewer.mjs +379 -0
- package/dist/viewer/SkinViewer.mjs.map +1 -0
- package/dist/viewer/index.mjs +7 -0
- package/dist/webgl.d.mts +2 -0
- package/dist/webgl.mjs +3 -0
- package/dist/webgpu.d.mts +2 -0
- package/dist/webgpu.mjs +3 -0
- package/package.json +75 -0
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
//#region src/plugins/panorama/SkyboxGeometry.ts
|
|
2
|
+
/**
|
|
3
|
+
* Create a skybox cube geometry.
|
|
4
|
+
* The cube is centered at origin with size 2 (from -1 to 1 on each axis).
|
|
5
|
+
* Faces are wound to point inward (for rendering from inside the cube).
|
|
6
|
+
*
|
|
7
|
+
* @param size - Size of the cube (default 2.0)
|
|
8
|
+
*/
|
|
9
|
+
function createSkyboxGeometry(size = 2) {
|
|
10
|
+
const s = size / 2;
|
|
11
|
+
const vertices = new Float32Array([
|
|
12
|
+
-s,
|
|
13
|
+
-s,
|
|
14
|
+
s,
|
|
15
|
+
s,
|
|
16
|
+
-s,
|
|
17
|
+
s,
|
|
18
|
+
s,
|
|
19
|
+
s,
|
|
20
|
+
s,
|
|
21
|
+
-s,
|
|
22
|
+
s,
|
|
23
|
+
s,
|
|
24
|
+
s,
|
|
25
|
+
-s,
|
|
26
|
+
-s,
|
|
27
|
+
-s,
|
|
28
|
+
-s,
|
|
29
|
+
-s,
|
|
30
|
+
-s,
|
|
31
|
+
s,
|
|
32
|
+
-s,
|
|
33
|
+
s,
|
|
34
|
+
s,
|
|
35
|
+
-s,
|
|
36
|
+
-s,
|
|
37
|
+
s,
|
|
38
|
+
s,
|
|
39
|
+
s,
|
|
40
|
+
s,
|
|
41
|
+
s,
|
|
42
|
+
s,
|
|
43
|
+
s,
|
|
44
|
+
-s,
|
|
45
|
+
-s,
|
|
46
|
+
s,
|
|
47
|
+
-s,
|
|
48
|
+
-s,
|
|
49
|
+
-s,
|
|
50
|
+
-s,
|
|
51
|
+
s,
|
|
52
|
+
-s,
|
|
53
|
+
-s,
|
|
54
|
+
s,
|
|
55
|
+
-s,
|
|
56
|
+
s,
|
|
57
|
+
-s,
|
|
58
|
+
-s,
|
|
59
|
+
s,
|
|
60
|
+
s,
|
|
61
|
+
-s,
|
|
62
|
+
s,
|
|
63
|
+
s,
|
|
64
|
+
-s,
|
|
65
|
+
-s,
|
|
66
|
+
s,
|
|
67
|
+
s,
|
|
68
|
+
-s,
|
|
69
|
+
s,
|
|
70
|
+
s,
|
|
71
|
+
s,
|
|
72
|
+
-s,
|
|
73
|
+
-s,
|
|
74
|
+
-s,
|
|
75
|
+
-s,
|
|
76
|
+
-s,
|
|
77
|
+
s,
|
|
78
|
+
-s,
|
|
79
|
+
s,
|
|
80
|
+
s,
|
|
81
|
+
-s,
|
|
82
|
+
s,
|
|
83
|
+
-s
|
|
84
|
+
]);
|
|
85
|
+
const indices = new Uint16Array([
|
|
86
|
+
0,
|
|
87
|
+
1,
|
|
88
|
+
2,
|
|
89
|
+
0,
|
|
90
|
+
2,
|
|
91
|
+
3,
|
|
92
|
+
4,
|
|
93
|
+
5,
|
|
94
|
+
6,
|
|
95
|
+
4,
|
|
96
|
+
6,
|
|
97
|
+
7,
|
|
98
|
+
8,
|
|
99
|
+
9,
|
|
100
|
+
10,
|
|
101
|
+
8,
|
|
102
|
+
10,
|
|
103
|
+
11,
|
|
104
|
+
12,
|
|
105
|
+
13,
|
|
106
|
+
14,
|
|
107
|
+
12,
|
|
108
|
+
14,
|
|
109
|
+
15,
|
|
110
|
+
16,
|
|
111
|
+
17,
|
|
112
|
+
18,
|
|
113
|
+
16,
|
|
114
|
+
18,
|
|
115
|
+
19,
|
|
116
|
+
20,
|
|
117
|
+
21,
|
|
118
|
+
22,
|
|
119
|
+
20,
|
|
120
|
+
22,
|
|
121
|
+
23
|
|
122
|
+
]);
|
|
123
|
+
return {
|
|
124
|
+
vertices,
|
|
125
|
+
indices,
|
|
126
|
+
indexCount: indices.length
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
//#endregion
|
|
131
|
+
export { createSkyboxGeometry };
|
|
132
|
+
//# sourceMappingURL=SkyboxGeometry.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SkyboxGeometry.mjs","names":[],"sources":["../../../src/plugins/panorama/SkyboxGeometry.ts"],"sourcesContent":["/**\n * Skybox Geometry\n *\n * Creates a cube with faces pointing inward for panorama rendering.\n */\n\n/** Skybox geometry data */\nexport interface SkyboxGeometry {\n /** Vertex positions (vec3 per vertex) */\n vertices: Float32Array;\n /** Triangle indices */\n indices: Uint16Array;\n /** Number of indices */\n indexCount: number;\n}\n\n/**\n * Create a skybox cube geometry.\n * The cube is centered at origin with size 2 (from -1 to 1 on each axis).\n * Faces are wound to point inward (for rendering from inside the cube).\n *\n * @param size - Size of the cube (default 2.0)\n */\nexport function createSkyboxGeometry(size = 2.0): SkyboxGeometry {\n const s = size / 2;\n\n // 8 corners of the cube\n // prettier-ignore\n const vertices = new Float32Array([\n // Front face (z = +s)\n -s, -s, s, // 0: bottom-left\n s, -s, s, // 1: bottom-right\n s, s, s, // 2: top-right\n -s, s, s, // 3: top-left\n\n // Back face (z = -s)\n s, -s, -s, // 4: bottom-left (viewed from inside)\n -s, -s, -s, // 5: bottom-right\n -s, s, -s, // 6: top-right\n s, s, -s, // 7: top-left\n\n // Top face (y = +s)\n -s, s, s, // 8: front-left\n s, s, s, // 9: front-right\n s, s, -s, // 10: back-right\n -s, s, -s, // 11: back-left\n\n // Bottom face (y = -s)\n -s, -s, -s, // 12: back-left\n s, -s, -s, // 13: back-right\n s, -s, s, // 14: front-right\n -s, -s, s, // 15: front-left\n\n // Right face (x = +s)\n s, -s, s, // 16: front-bottom\n s, -s, -s, // 17: back-bottom\n s, s, -s, // 18: back-top\n s, s, s, // 19: front-top\n\n // Left face (x = -s)\n -s, -s, -s, // 20: back-bottom\n -s, -s, s, // 21: front-bottom\n -s, s, s, // 22: front-top\n -s, s, -s, // 23: back-top\n ]);\n\n // Indices for triangles (wound for inward-facing normals)\n // Each face: 2 triangles, 6 indices\n // prettier-ignore\n const indices = new Uint16Array([\n // Front face\n 0, 1, 2, 0, 2, 3,\n // Back face\n 4, 5, 6, 4, 6, 7,\n // Top face\n 8, 9, 10, 8, 10, 11,\n // Bottom face\n 12, 13, 14, 12, 14, 15,\n // Right face\n 16, 17, 18, 16, 18, 19,\n // Left face\n 20, 21, 22, 20, 22, 23,\n ]);\n\n return {\n vertices,\n indices,\n indexCount: indices.length,\n };\n}\n"],"mappings":";;;;;;;;AAuBA,SAAgB,qBAAqB,OAAO,GAAqB;CAC/D,MAAM,IAAI,OAAO;CAIjB,MAAM,WAAW,IAAI,aAAa;EAEhC,CAAC;EAAG,CAAC;EAAI;EACR;EAAG,CAAC;EAAI;EACR;EAAI;EAAI;EACT,CAAC;EAAI;EAAI;EAGR;EAAG,CAAC;EAAG,CAAC;EACT,CAAC;EAAG,CAAC;EAAG,CAAC;EACT,CAAC;EAAI;EAAG,CAAC;EACR;EAAI;EAAG,CAAC;EAGT,CAAC;EAAI;EAAI;EACR;EAAI;EAAI;EACR;EAAI;EAAG,CAAC;EACT,CAAC;EAAI;EAAG,CAAC;EAGT,CAAC;EAAG,CAAC;EAAG,CAAC;EACR;EAAG,CAAC;EAAG,CAAC;EACR;EAAG,CAAC;EAAI;EACT,CAAC;EAAG,CAAC;EAAI;EAGR;EAAG,CAAC;EAAI;EACR;EAAG,CAAC;EAAG,CAAC;EACR;EAAI;EAAG,CAAC;EACR;EAAI;EAAI;EAGT,CAAC;EAAG,CAAC;EAAG,CAAC;EACT,CAAC;EAAG,CAAC;EAAI;EACT,CAAC;EAAI;EAAI;EACT,CAAC;EAAI;EAAG,CAAC;EACV,CAAC;CAKF,MAAM,UAAU,IAAI,YAAY;EAE7B;EAAI;EAAI;EAAK;EAAI;EAAI;EAErB;EAAI;EAAI;EAAK;EAAI;EAAI;EAErB;EAAI;EAAG;EAAM;EAAG;EAAI;EAErB;EAAI;EAAI;EAAK;EAAI;EAAI;EAErB;EAAI;EAAI;EAAK;EAAI;EAAI;EAErB;EAAI;EAAI;EAAK;EAAI;EAAI;EACtB,CAAC;AAEF,QAAO;EACL;EACA;EACA,YAAY,QAAQ;EACrB"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { BackgroundPlugin } from "../../core/plugins/types.mjs";
|
|
2
|
+
|
|
3
|
+
//#region src/plugins/panorama/plugin.d.ts
|
|
4
|
+
/** Panorama plugin instance */
|
|
5
|
+
declare const PanoramaPlugin: BackgroundPlugin;
|
|
6
|
+
//#endregion
|
|
7
|
+
export { PanoramaPlugin };
|
|
8
|
+
//# sourceMappingURL=plugin.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin.d.mts","names":[],"sources":["../../../src/plugins/panorama/plugin.ts"],"mappings":";;;;cAwBa,cAAA,EAAgB,gBAAA"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { createPanoramaRenderer } from "./PanoramaRenderer.mjs";
|
|
2
|
+
|
|
3
|
+
//#region src/plugins/panorama/plugin.ts
|
|
4
|
+
/** Panorama plugin instance */
|
|
5
|
+
const PanoramaPlugin = {
|
|
6
|
+
type: "background",
|
|
7
|
+
name: "panorama",
|
|
8
|
+
createRenderer: createPanoramaRenderer
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
//#endregion
|
|
12
|
+
export { PanoramaPlugin };
|
|
13
|
+
//# sourceMappingURL=plugin.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin.mjs","names":[],"sources":["../../../src/plugins/panorama/plugin.ts"],"sourcesContent":["/**\n * Panorama Plugin\n *\n * Adds equirectangular panorama background support to the skin viewer.\n *\n * @example\n * ```ts\n * import { use } from 'minecraft-skin-renderer'\n * import { PanoramaPlugin } from 'minecraft-skin-renderer/panorama'\n *\n * use(PanoramaPlugin)\n *\n * const viewer = await createSkinViewer({\n * canvas,\n * skin: '...',\n * panorama: 'https://example.com/panorama.jpg',\n * })\n * ```\n */\n\nimport type { BackgroundPlugin } from \"../../core/plugins/types\";\nimport { createPanoramaRenderer } from \"./PanoramaRenderer\";\n\n/** Panorama plugin instance */\nexport const PanoramaPlugin: BackgroundPlugin = {\n type: \"background\",\n name: \"panorama\",\n\n createRenderer: createPanoramaRenderer,\n};\n"],"mappings":";;;;AAwBA,MAAa,iBAAmC;CAC9C,MAAM;CACN,MAAM;CAEN,gBAAgB;CACjB"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
//#region src/plugins/panorama/shaders/webgl.ts
|
|
2
|
+
/**
|
|
3
|
+
* WebGL panorama shaders
|
|
4
|
+
*
|
|
5
|
+
* Renders an equirectangular panorama texture on a skybox.
|
|
6
|
+
*/
|
|
7
|
+
const PANORAMA_VERTEX_SHADER = `#version 300 es
|
|
8
|
+
|
|
9
|
+
precision highp float;
|
|
10
|
+
|
|
11
|
+
layout(location = 0) in vec3 a_position;
|
|
12
|
+
|
|
13
|
+
uniform mat4 u_viewMatrix;
|
|
14
|
+
uniform mat4 u_projectionMatrix;
|
|
15
|
+
|
|
16
|
+
out vec3 v_direction;
|
|
17
|
+
|
|
18
|
+
void main() {
|
|
19
|
+
v_direction = a_position;
|
|
20
|
+
|
|
21
|
+
// Remove translation from view matrix (only rotation)
|
|
22
|
+
mat4 viewRotation = u_viewMatrix;
|
|
23
|
+
viewRotation[3] = vec4(0.0, 0.0, 0.0, 1.0);
|
|
24
|
+
|
|
25
|
+
vec4 pos = u_projectionMatrix * viewRotation * vec4(a_position, 1.0);
|
|
26
|
+
|
|
27
|
+
// Set z = w so depth is always at far plane
|
|
28
|
+
gl_Position = pos.xyww;
|
|
29
|
+
}
|
|
30
|
+
`;
|
|
31
|
+
const PANORAMA_FRAGMENT_SHADER = `#version 300 es
|
|
32
|
+
|
|
33
|
+
precision highp float;
|
|
34
|
+
|
|
35
|
+
in vec3 v_direction;
|
|
36
|
+
|
|
37
|
+
uniform sampler2D u_panorama;
|
|
38
|
+
|
|
39
|
+
out vec4 fragColor;
|
|
40
|
+
|
|
41
|
+
const float PI = 3.14159265359;
|
|
42
|
+
|
|
43
|
+
void main() {
|
|
44
|
+
vec3 dir = normalize(v_direction);
|
|
45
|
+
|
|
46
|
+
// Convert direction to equirectangular UV coordinates
|
|
47
|
+
// atan(z, x) gives horizontal angle (-PI to PI)
|
|
48
|
+
// asin(y) gives vertical angle (-PI/2 to PI/2)
|
|
49
|
+
float u = atan(dir.z, dir.x) / (2.0 * PI) + 0.5;
|
|
50
|
+
float v = asin(clamp(dir.y, -1.0, 1.0)) / PI + 0.5;
|
|
51
|
+
|
|
52
|
+
// Flip V to match standard equirectangular orientation
|
|
53
|
+
fragColor = texture(u_panorama, vec2(u, 1.0 - v));
|
|
54
|
+
}
|
|
55
|
+
`;
|
|
56
|
+
|
|
57
|
+
//#endregion
|
|
58
|
+
export { PANORAMA_FRAGMENT_SHADER, PANORAMA_VERTEX_SHADER };
|
|
59
|
+
//# sourceMappingURL=webgl.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webgl.mjs","names":[],"sources":["../../../../src/plugins/panorama/shaders/webgl.ts"],"sourcesContent":["/**\n * WebGL panorama shaders\n *\n * Renders an equirectangular panorama texture on a skybox.\n */\n\nexport const PANORAMA_VERTEX_SHADER = `#version 300 es\n\nprecision highp float;\n\nlayout(location = 0) in vec3 a_position;\n\nuniform mat4 u_viewMatrix;\nuniform mat4 u_projectionMatrix;\n\nout vec3 v_direction;\n\nvoid main() {\n v_direction = a_position;\n\n // Remove translation from view matrix (only rotation)\n mat4 viewRotation = u_viewMatrix;\n viewRotation[3] = vec4(0.0, 0.0, 0.0, 1.0);\n\n vec4 pos = u_projectionMatrix * viewRotation * vec4(a_position, 1.0);\n\n // Set z = w so depth is always at far plane\n gl_Position = pos.xyww;\n}\n`;\n\nexport const PANORAMA_FRAGMENT_SHADER = `#version 300 es\n\nprecision highp float;\n\nin vec3 v_direction;\n\nuniform sampler2D u_panorama;\n\nout vec4 fragColor;\n\nconst float PI = 3.14159265359;\n\nvoid main() {\n vec3 dir = normalize(v_direction);\n\n // Convert direction to equirectangular UV coordinates\n // atan(z, x) gives horizontal angle (-PI to PI)\n // asin(y) gives vertical angle (-PI/2 to PI/2)\n float u = atan(dir.z, dir.x) / (2.0 * PI) + 0.5;\n float v = asin(clamp(dir.y, -1.0, 1.0)) / PI + 0.5;\n\n // Flip V to match standard equirectangular orientation\n fragColor = texture(u_panorama, vec2(u, 1.0 - v));\n}\n`;\n"],"mappings":";;;;;;AAMA,MAAa,yBAAyB;;;;;;;;;;;;;;;;;;;;;;;;AAyBtC,MAAa,2BAA2B"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
//#region src/plugins/panorama/shaders/webgpu.ts
|
|
2
|
+
/**
|
|
3
|
+
* WebGPU panorama shaders
|
|
4
|
+
*
|
|
5
|
+
* Renders an equirectangular panorama texture on a skybox.
|
|
6
|
+
* Uses the same bind group layout as the skin shader for compatibility.
|
|
7
|
+
*/
|
|
8
|
+
const PANORAMA_WGSL_SHADER = `
|
|
9
|
+
// Uniform buffer matching the WebGPU renderer's layout
|
|
10
|
+
// We only use viewMatrix and projectionMatrix
|
|
11
|
+
struct Uniforms {
|
|
12
|
+
modelMatrix: mat4x4<f32>, // offset 0
|
|
13
|
+
viewMatrix: mat4x4<f32>, // offset 64
|
|
14
|
+
projectionMatrix: mat4x4<f32>, // offset 128
|
|
15
|
+
boneMatrices: array<mat4x4<f32>, 24>, // offset 192
|
|
16
|
+
alphaTest: f32, // offset 1728
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// Group 0: Uniforms (matches WebGPU renderer)
|
|
20
|
+
@group(0) @binding(0) var<uniform> uniforms: Uniforms;
|
|
21
|
+
|
|
22
|
+
// Group 1: Textures (matches WebGPU renderer)
|
|
23
|
+
@group(1) @binding(0) var panoramaSampler: sampler;
|
|
24
|
+
@group(1) @binding(1) var panoramaTexture: texture_2d<f32>;
|
|
25
|
+
|
|
26
|
+
struct VertexInput {
|
|
27
|
+
@location(0) position: vec3<f32>,
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
struct VertexOutput {
|
|
31
|
+
@builtin(position) position: vec4<f32>,
|
|
32
|
+
@location(0) direction: vec3<f32>,
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const PI: f32 = 3.14159265359;
|
|
36
|
+
|
|
37
|
+
@vertex
|
|
38
|
+
fn vs_main(input: VertexInput) -> VertexOutput {
|
|
39
|
+
var output: VertexOutput;
|
|
40
|
+
|
|
41
|
+
output.direction = input.position;
|
|
42
|
+
|
|
43
|
+
// Remove translation from view matrix (only rotation)
|
|
44
|
+
var viewRotation = uniforms.viewMatrix;
|
|
45
|
+
viewRotation[3] = vec4<f32>(0.0, 0.0, 0.0, 1.0);
|
|
46
|
+
|
|
47
|
+
let pos = uniforms.projectionMatrix * viewRotation * vec4<f32>(input.position, 1.0);
|
|
48
|
+
|
|
49
|
+
// Set z = w so depth is always at far plane
|
|
50
|
+
output.position = vec4<f32>(pos.xy, pos.w, pos.w);
|
|
51
|
+
|
|
52
|
+
return output;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
@fragment
|
|
56
|
+
fn fs_main(input: VertexOutput) -> @location(0) vec4<f32> {
|
|
57
|
+
let dir = normalize(input.direction);
|
|
58
|
+
|
|
59
|
+
// Convert direction to equirectangular UV coordinates
|
|
60
|
+
let u = atan2(dir.z, dir.x) / (2.0 * PI) + 0.5;
|
|
61
|
+
let v = asin(clamp(dir.y, -1.0, 1.0)) / PI + 0.5;
|
|
62
|
+
|
|
63
|
+
// Flip V to match standard equirectangular orientation
|
|
64
|
+
return textureSample(panoramaTexture, panoramaSampler, vec2<f32>(u, 1.0 - v));
|
|
65
|
+
}
|
|
66
|
+
`;
|
|
67
|
+
|
|
68
|
+
//#endregion
|
|
69
|
+
export { PANORAMA_WGSL_SHADER };
|
|
70
|
+
//# sourceMappingURL=webgpu.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webgpu.mjs","names":[],"sources":["../../../../src/plugins/panorama/shaders/webgpu.ts"],"sourcesContent":["/**\n * WebGPU panorama shaders\n *\n * Renders an equirectangular panorama texture on a skybox.\n * Uses the same bind group layout as the skin shader for compatibility.\n */\n\nexport const PANORAMA_WGSL_SHADER = `\n// Uniform buffer matching the WebGPU renderer's layout\n// We only use viewMatrix and projectionMatrix\nstruct Uniforms {\n modelMatrix: mat4x4<f32>, // offset 0\n viewMatrix: mat4x4<f32>, // offset 64\n projectionMatrix: mat4x4<f32>, // offset 128\n boneMatrices: array<mat4x4<f32>, 24>, // offset 192\n alphaTest: f32, // offset 1728\n}\n\n// Group 0: Uniforms (matches WebGPU renderer)\n@group(0) @binding(0) var<uniform> uniforms: Uniforms;\n\n// Group 1: Textures (matches WebGPU renderer)\n@group(1) @binding(0) var panoramaSampler: sampler;\n@group(1) @binding(1) var panoramaTexture: texture_2d<f32>;\n\nstruct VertexInput {\n @location(0) position: vec3<f32>,\n}\n\nstruct VertexOutput {\n @builtin(position) position: vec4<f32>,\n @location(0) direction: vec3<f32>,\n}\n\nconst PI: f32 = 3.14159265359;\n\n@vertex\nfn vs_main(input: VertexInput) -> VertexOutput {\n var output: VertexOutput;\n\n output.direction = input.position;\n\n // Remove translation from view matrix (only rotation)\n var viewRotation = uniforms.viewMatrix;\n viewRotation[3] = vec4<f32>(0.0, 0.0, 0.0, 1.0);\n\n let pos = uniforms.projectionMatrix * viewRotation * vec4<f32>(input.position, 1.0);\n\n // Set z = w so depth is always at far plane\n output.position = vec4<f32>(pos.xy, pos.w, pos.w);\n\n return output;\n}\n\n@fragment\nfn fs_main(input: VertexOutput) -> @location(0) vec4<f32> {\n let dir = normalize(input.direction);\n\n // Convert direction to equirectangular UV coordinates\n let u = atan2(dir.z, dir.x) / (2.0 * PI) + 0.5;\n let v = asin(clamp(dir.y, -1.0, 1.0)) / PI + 0.5;\n\n // Flip V to match standard equirectangular orientation\n return textureSample(panoramaTexture, panoramaSampler, vec2<f32>(u, 1.0 - v));\n}\n`;\n"],"mappings":";;;;;;;AAOA,MAAa,uBAAuB"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
//#region src/texture/TextureLoader.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Texture loading utilities
|
|
4
|
+
*/
|
|
5
|
+
/** Texture source types */
|
|
6
|
+
type TextureSource = string | Blob | HTMLImageElement | ImageBitmap;
|
|
7
|
+
/**
|
|
8
|
+
* Load and process a skin texture
|
|
9
|
+
* Handles old 64x32 format conversion
|
|
10
|
+
*/
|
|
11
|
+
declare function loadSkinTexture(source: TextureSource): Promise<ImageBitmap>;
|
|
12
|
+
/**
|
|
13
|
+
* Load a cape texture
|
|
14
|
+
*/
|
|
15
|
+
declare function loadCapeTexture(source: TextureSource): Promise<ImageBitmap>;
|
|
16
|
+
/**
|
|
17
|
+
* Load an elytra texture (uses cape texture format)
|
|
18
|
+
*/
|
|
19
|
+
declare function loadElytraTexture(source: TextureSource): Promise<ImageBitmap>;
|
|
20
|
+
//#endregion
|
|
21
|
+
export { TextureSource, loadCapeTexture, loadElytraTexture, loadSkinTexture };
|
|
22
|
+
//# sourceMappingURL=TextureLoader.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TextureLoader.d.mts","names":[],"sources":["../../src/texture/TextureLoader.ts"],"mappings":";;AAOA;;;KAAY,aAAA,YAAyB,IAAA,GAAO,gBAAA,GAAmB,WAAA;;;;;iBAiGzC,eAAA,CAAgB,MAAA,EAAQ,aAAA,GAAgB,OAAA,CAAQ,WAAA;;;AA0BtE;iBAAsB,eAAA,CAAgB,MAAA,EAAQ,aAAA,GAAgB,OAAA,CAAQ,WAAA;;;;iBAOhD,iBAAA,CAAkB,MAAA,EAAQ,aAAA,GAAgB,OAAA,CAAQ,WAAA"}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { convertOldSkinFormat, isOldSkinFormat } from "../model/uv/SkinUV.mjs";
|
|
2
|
+
|
|
3
|
+
//#region src/texture/TextureLoader.ts
|
|
4
|
+
/**
|
|
5
|
+
* Texture loading utilities
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Load an image from various sources
|
|
9
|
+
*/
|
|
10
|
+
async function loadImage(source) {
|
|
11
|
+
if (source instanceof HTMLImageElement) {
|
|
12
|
+
if (source.complete) return source;
|
|
13
|
+
return new Promise((resolve, reject) => {
|
|
14
|
+
source.addEventListener("load", () => resolve(source));
|
|
15
|
+
source.addEventListener("error", reject);
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
if (source instanceof ImageBitmap) {
|
|
19
|
+
const canvas = document.createElement("canvas");
|
|
20
|
+
canvas.width = source.width;
|
|
21
|
+
canvas.height = source.height;
|
|
22
|
+
canvas.getContext("2d").drawImage(source, 0, 0);
|
|
23
|
+
return loadImage(canvas.toDataURL());
|
|
24
|
+
}
|
|
25
|
+
const url = source instanceof Blob ? URL.createObjectURL(source) : source;
|
|
26
|
+
const image = new Image();
|
|
27
|
+
if (typeof source === "string" && !source.startsWith("data:")) image.crossOrigin = "anonymous";
|
|
28
|
+
return new Promise((resolve, reject) => {
|
|
29
|
+
image.addEventListener("load", () => {
|
|
30
|
+
if (source instanceof Blob) URL.revokeObjectURL(url);
|
|
31
|
+
resolve(image);
|
|
32
|
+
});
|
|
33
|
+
image.addEventListener("error", (e) => {
|
|
34
|
+
if (source instanceof Blob) URL.revokeObjectURL(url);
|
|
35
|
+
reject(/* @__PURE__ */ new Error(`Failed to load image: ${e.type}`));
|
|
36
|
+
});
|
|
37
|
+
image.src = url;
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Load an image and return as ImageBitmap
|
|
42
|
+
*/
|
|
43
|
+
async function loadImageBitmap(source) {
|
|
44
|
+
if (source instanceof ImageBitmap) return source;
|
|
45
|
+
if (source instanceof Blob) return createImageBitmap(source);
|
|
46
|
+
const image = await loadImage(source);
|
|
47
|
+
return createImageBitmap(image);
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Generic texture loader (alias for loadImageBitmap)
|
|
51
|
+
*/
|
|
52
|
+
const loadTexture = loadImageBitmap;
|
|
53
|
+
/**
|
|
54
|
+
* Get image data from an image source
|
|
55
|
+
*/
|
|
56
|
+
function getImageData(source) {
|
|
57
|
+
const canvas = document.createElement("canvas");
|
|
58
|
+
const width = source instanceof HTMLCanvasElement ? source.width : source.width;
|
|
59
|
+
const height = source instanceof HTMLCanvasElement ? source.height : source.height;
|
|
60
|
+
canvas.width = width;
|
|
61
|
+
canvas.height = height;
|
|
62
|
+
const ctx = canvas.getContext("2d");
|
|
63
|
+
ctx.drawImage(source, 0, 0);
|
|
64
|
+
return ctx.getImageData(0, 0, width, height);
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Load and process a skin texture
|
|
68
|
+
* Handles old 64x32 format conversion
|
|
69
|
+
*/
|
|
70
|
+
async function loadSkinTexture(source) {
|
|
71
|
+
const image = await loadImage(source);
|
|
72
|
+
const width = image.width;
|
|
73
|
+
const height = image.height;
|
|
74
|
+
if (isOldSkinFormat(width, height)) {
|
|
75
|
+
const convertedData = convertOldSkinFormat(getImageData(image));
|
|
76
|
+
const canvas = document.createElement("canvas");
|
|
77
|
+
canvas.width = 64;
|
|
78
|
+
canvas.height = 64;
|
|
79
|
+
canvas.getContext("2d").putImageData(convertedData, 0, 0);
|
|
80
|
+
return createImageBitmap(canvas);
|
|
81
|
+
}
|
|
82
|
+
return createImageBitmap(image);
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Load a cape texture
|
|
86
|
+
*/
|
|
87
|
+
async function loadCapeTexture(source) {
|
|
88
|
+
return loadImageBitmap(source);
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Load an elytra texture (uses cape texture format)
|
|
92
|
+
*/
|
|
93
|
+
async function loadElytraTexture(source) {
|
|
94
|
+
return loadImageBitmap(source);
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Create a placeholder texture (magenta/black checkerboard)
|
|
98
|
+
*/
|
|
99
|
+
async function createPlaceholderTexture(width = 64, height = 64) {
|
|
100
|
+
const canvas = document.createElement("canvas");
|
|
101
|
+
canvas.width = width;
|
|
102
|
+
canvas.height = height;
|
|
103
|
+
const ctx = canvas.getContext("2d");
|
|
104
|
+
const size = 8;
|
|
105
|
+
for (let y = 0; y < height; y += size) for (let x = 0; x < width; x += size) {
|
|
106
|
+
ctx.fillStyle = (x / size + y / size) % 2 === 0 ? "#ff00ff" : "#000000";
|
|
107
|
+
ctx.fillRect(x, y, size, size);
|
|
108
|
+
}
|
|
109
|
+
return createImageBitmap(canvas);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
//#endregion
|
|
113
|
+
export { createPlaceholderTexture, loadCapeTexture, loadElytraTexture, loadSkinTexture, loadTexture };
|
|
114
|
+
//# sourceMappingURL=TextureLoader.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TextureLoader.mjs","names":[],"sources":["../../src/texture/TextureLoader.ts"],"sourcesContent":["/**\n * Texture loading utilities\n */\n\nimport { isOldSkinFormat, convertOldSkinFormat } from \"../model/uv/SkinUV\";\n\n/** Texture source types */\nexport type TextureSource = string | Blob | HTMLImageElement | ImageBitmap;\n\n/**\n * Load an image from various sources\n */\nexport async function loadImage(source: TextureSource): Promise<HTMLImageElement> {\n if (source instanceof HTMLImageElement) {\n if (source.complete) {\n return source;\n }\n return new Promise((resolve, reject) => {\n source.addEventListener(\"load\", () => resolve(source));\n source.addEventListener(\"error\", reject);\n });\n }\n\n if (source instanceof ImageBitmap) {\n // Convert ImageBitmap to HTMLImageElement via canvas\n const canvas = document.createElement(\"canvas\");\n canvas.width = source.width;\n canvas.height = source.height;\n const ctx = canvas.getContext(\"2d\")!;\n ctx.drawImage(source, 0, 0);\n const dataUrl = canvas.toDataURL();\n return loadImage(dataUrl);\n }\n\n const url = source instanceof Blob ? URL.createObjectURL(source) : source;\n const image = new Image();\n\n // Handle CORS for external URLs\n if (typeof source === \"string\" && !source.startsWith(\"data:\")) {\n image.crossOrigin = \"anonymous\";\n }\n\n return new Promise((resolve, reject) => {\n image.addEventListener(\"load\", () => {\n // Revoke blob URL after loading\n if (source instanceof Blob) {\n URL.revokeObjectURL(url);\n }\n resolve(image);\n });\n image.addEventListener(\"error\", (e) => {\n if (source instanceof Blob) {\n URL.revokeObjectURL(url);\n }\n reject(new Error(`Failed to load image: ${e.type}`));\n });\n image.src = url;\n });\n}\n\n/**\n * Load an image and return as ImageBitmap\n */\nexport async function loadImageBitmap(source: TextureSource): Promise<ImageBitmap> {\n if (source instanceof ImageBitmap) {\n return source;\n }\n\n if (source instanceof Blob) {\n return createImageBitmap(source);\n }\n\n const image = await loadImage(source);\n return createImageBitmap(image);\n}\n\n/**\n * Generic texture loader (alias for loadImageBitmap)\n */\nexport const loadTexture = loadImageBitmap;\n\n/**\n * Get image data from an image source\n */\nexport function getImageData(\n source: HTMLImageElement | ImageBitmap | HTMLCanvasElement,\n): ImageData {\n const canvas = document.createElement(\"canvas\");\n const width = source instanceof HTMLCanvasElement ? source.width : source.width;\n const height = source instanceof HTMLCanvasElement ? source.height : source.height;\n\n canvas.width = width;\n canvas.height = height;\n\n const ctx = canvas.getContext(\"2d\")!;\n ctx.drawImage(source, 0, 0);\n\n return ctx.getImageData(0, 0, width, height);\n}\n\n/**\n * Load and process a skin texture\n * Handles old 64x32 format conversion\n */\nexport async function loadSkinTexture(source: TextureSource): Promise<ImageBitmap> {\n const image = await loadImage(source);\n const width = image.width;\n const height = image.height;\n\n // Check if it's old format and needs conversion\n if (isOldSkinFormat(width, height)) {\n const imageData = getImageData(image);\n const convertedData = convertOldSkinFormat(imageData);\n\n // Create canvas with converted data\n const canvas = document.createElement(\"canvas\");\n canvas.width = 64;\n canvas.height = 64;\n const ctx = canvas.getContext(\"2d\")!;\n ctx.putImageData(convertedData, 0, 0);\n\n return createImageBitmap(canvas);\n }\n\n return createImageBitmap(image);\n}\n\n/**\n * Load a cape texture\n */\nexport async function loadCapeTexture(source: TextureSource): Promise<ImageBitmap> {\n return loadImageBitmap(source);\n}\n\n/**\n * Load an elytra texture (uses cape texture format)\n */\nexport async function loadElytraTexture(source: TextureSource): Promise<ImageBitmap> {\n return loadImageBitmap(source);\n}\n\n/**\n * Detect if a skin uses slim (3-pixel) arms\n * This checks if the specific pixel areas for slim arms are transparent\n */\nexport function detectSlimModel(imageData: ImageData): boolean {\n // Check the rightmost column of the right arm area\n // If it's fully transparent, it's likely a slim skin\n const data = imageData.data;\n\n // Check pixels at x=46, y=52 (right arm overlay area)\n // In classic skins, this would have content; in slim, it's transparent\n for (let y = 52; y < 64; y++) {\n const idx = (y * 64 + 46) * 4;\n if (data[idx + 3] > 0) {\n return false; // Has content, likely classic\n }\n }\n\n return true; // Transparent, likely slim\n}\n\n/**\n * Create a placeholder texture (magenta/black checkerboard)\n */\nexport async function createPlaceholderTexture(width = 64, height = 64): Promise<ImageBitmap> {\n const canvas = document.createElement(\"canvas\");\n canvas.width = width;\n canvas.height = height;\n\n const ctx = canvas.getContext(\"2d\")!;\n const size = 8;\n\n for (let y = 0; y < height; y += size) {\n for (let x = 0; x < width; x += size) {\n const isEven = (x / size + y / size) % 2 === 0;\n ctx.fillStyle = isEven ? \"#ff00ff\" : \"#000000\";\n ctx.fillRect(x, y, size, size);\n }\n }\n\n return createImageBitmap(canvas);\n}\n\n/**\n * Create a solid color texture\n */\nexport async function createSolidTexture(\n color: string,\n width = 64,\n height = 64,\n): Promise<ImageBitmap> {\n const canvas = document.createElement(\"canvas\");\n canvas.width = width;\n canvas.height = height;\n\n const ctx = canvas.getContext(\"2d\")!;\n ctx.fillStyle = color;\n ctx.fillRect(0, 0, width, height);\n\n return createImageBitmap(canvas);\n}\n"],"mappings":";;;;;;;;;AAYA,eAAsB,UAAU,QAAkD;AAChF,KAAI,kBAAkB,kBAAkB;AACtC,MAAI,OAAO,SACT,QAAO;AAET,SAAO,IAAI,SAAS,SAAS,WAAW;AACtC,UAAO,iBAAiB,cAAc,QAAQ,OAAO,CAAC;AACtD,UAAO,iBAAiB,SAAS,OAAO;IACxC;;AAGJ,KAAI,kBAAkB,aAAa;EAEjC,MAAM,SAAS,SAAS,cAAc,SAAS;AAC/C,SAAO,QAAQ,OAAO;AACtB,SAAO,SAAS,OAAO;AAEvB,EADY,OAAO,WAAW,KAAK,CAC/B,UAAU,QAAQ,GAAG,EAAE;AAE3B,SAAO,UADS,OAAO,WAAW,CACT;;CAG3B,MAAM,MAAM,kBAAkB,OAAO,IAAI,gBAAgB,OAAO,GAAG;CACnE,MAAM,QAAQ,IAAI,OAAO;AAGzB,KAAI,OAAO,WAAW,YAAY,CAAC,OAAO,WAAW,QAAQ,CAC3D,OAAM,cAAc;AAGtB,QAAO,IAAI,SAAS,SAAS,WAAW;AACtC,QAAM,iBAAiB,cAAc;AAEnC,OAAI,kBAAkB,KACpB,KAAI,gBAAgB,IAAI;AAE1B,WAAQ,MAAM;IACd;AACF,QAAM,iBAAiB,UAAU,MAAM;AACrC,OAAI,kBAAkB,KACpB,KAAI,gBAAgB,IAAI;AAE1B,0BAAO,IAAI,MAAM,yBAAyB,EAAE,OAAO,CAAC;IACpD;AACF,QAAM,MAAM;GACZ;;;;;AAMJ,eAAsB,gBAAgB,QAA6C;AACjF,KAAI,kBAAkB,YACpB,QAAO;AAGT,KAAI,kBAAkB,KACpB,QAAO,kBAAkB,OAAO;CAGlC,MAAM,QAAQ,MAAM,UAAU,OAAO;AACrC,QAAO,kBAAkB,MAAM;;;;;AAMjC,MAAa,cAAc;;;;AAK3B,SAAgB,aACd,QACW;CACX,MAAM,SAAS,SAAS,cAAc,SAAS;CAC/C,MAAM,QAAQ,kBAAkB,oBAAoB,OAAO,QAAQ,OAAO;CAC1E,MAAM,SAAS,kBAAkB,oBAAoB,OAAO,SAAS,OAAO;AAE5E,QAAO,QAAQ;AACf,QAAO,SAAS;CAEhB,MAAM,MAAM,OAAO,WAAW,KAAK;AACnC,KAAI,UAAU,QAAQ,GAAG,EAAE;AAE3B,QAAO,IAAI,aAAa,GAAG,GAAG,OAAO,OAAO;;;;;;AAO9C,eAAsB,gBAAgB,QAA6C;CACjF,MAAM,QAAQ,MAAM,UAAU,OAAO;CACrC,MAAM,QAAQ,MAAM;CACpB,MAAM,SAAS,MAAM;AAGrB,KAAI,gBAAgB,OAAO,OAAO,EAAE;EAElC,MAAM,gBAAgB,qBADJ,aAAa,MAAM,CACgB;EAGrD,MAAM,SAAS,SAAS,cAAc,SAAS;AAC/C,SAAO,QAAQ;AACf,SAAO,SAAS;AAEhB,EADY,OAAO,WAAW,KAAK,CAC/B,aAAa,eAAe,GAAG,EAAE;AAErC,SAAO,kBAAkB,OAAO;;AAGlC,QAAO,kBAAkB,MAAM;;;;;AAMjC,eAAsB,gBAAgB,QAA6C;AACjF,QAAO,gBAAgB,OAAO;;;;;AAMhC,eAAsB,kBAAkB,QAA6C;AACnF,QAAO,gBAAgB,OAAO;;;;;AA2BhC,eAAsB,yBAAyB,QAAQ,IAAI,SAAS,IAA0B;CAC5F,MAAM,SAAS,SAAS,cAAc,SAAS;AAC/C,QAAO,QAAQ;AACf,QAAO,SAAS;CAEhB,MAAM,MAAM,OAAO,WAAW,KAAK;CACnC,MAAM,OAAO;AAEb,MAAK,IAAI,IAAI,GAAG,IAAI,QAAQ,KAAK,KAC/B,MAAK,IAAI,IAAI,GAAG,IAAI,OAAO,KAAK,MAAM;AAEpC,MAAI,aADY,IAAI,OAAO,IAAI,QAAQ,MAAM,IACpB,YAAY;AACrC,MAAI,SAAS,GAAG,GAAG,MAAM,KAAK;;AAIlC,QAAO,kBAAkB,OAAO"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { mat4Identity, mat4Multiply, mat4Translate } from "../core/math/mat4.mjs";
|
|
2
|
+
import { quatToMat4 } from "../core/math/quat.mjs";
|
|
3
|
+
import "../core/math/index.mjs";
|
|
4
|
+
import { BoneIndex } from "../model/types.mjs";
|
|
5
|
+
|
|
6
|
+
//#region src/viewer/BoneMatrixComputer.ts
|
|
7
|
+
/**
|
|
8
|
+
* Bone Matrix Computer
|
|
9
|
+
*
|
|
10
|
+
* Computes world matrices for all bones in the skeleton hierarchy.
|
|
11
|
+
* Separated from SkinViewer for better modularity and testability.
|
|
12
|
+
*/
|
|
13
|
+
/** Bone processing order (parent-first) */
|
|
14
|
+
const BONE_ORDER = [
|
|
15
|
+
BoneIndex.Root,
|
|
16
|
+
BoneIndex.Body,
|
|
17
|
+
BoneIndex.Head,
|
|
18
|
+
BoneIndex.RightArm,
|
|
19
|
+
BoneIndex.LeftArm,
|
|
20
|
+
BoneIndex.RightLeg,
|
|
21
|
+
BoneIndex.LeftLeg,
|
|
22
|
+
BoneIndex.HeadOverlay,
|
|
23
|
+
BoneIndex.BodyOverlay,
|
|
24
|
+
BoneIndex.RightArmOverlay,
|
|
25
|
+
BoneIndex.LeftArmOverlay,
|
|
26
|
+
BoneIndex.RightLegOverlay,
|
|
27
|
+
BoneIndex.LeftLegOverlay,
|
|
28
|
+
BoneIndex.Cape,
|
|
29
|
+
BoneIndex.LeftWing,
|
|
30
|
+
BoneIndex.RightWing
|
|
31
|
+
];
|
|
32
|
+
/** Number of bones in the skeleton */
|
|
33
|
+
const BONE_COUNT = 24;
|
|
34
|
+
/** Size of bone matrix data in floats */
|
|
35
|
+
const BONE_MATRICES_SIZE = BONE_COUNT * 16;
|
|
36
|
+
/**
|
|
37
|
+
* Compute bone matrices for the skeleton.
|
|
38
|
+
*
|
|
39
|
+
* This computes the world-space transformation matrix for each bone,
|
|
40
|
+
* taking into account the bone hierarchy (parent transforms propagate to children).
|
|
41
|
+
*
|
|
42
|
+
* @param skeleton - The player skeleton with bone transforms
|
|
43
|
+
* @returns Float32Array containing all bone matrices (24 bones * 16 floats each)
|
|
44
|
+
*/
|
|
45
|
+
function computeBoneMatrices(skeleton) {
|
|
46
|
+
const matrices = new Float32Array(BONE_MATRICES_SIZE);
|
|
47
|
+
const setMatrix = (index, matrix) => {
|
|
48
|
+
matrices.set(matrix, index * 16);
|
|
49
|
+
};
|
|
50
|
+
for (let i = 0; i < BONE_COUNT; i++) setMatrix(i, mat4Identity());
|
|
51
|
+
const worldMatrices = /* @__PURE__ */ new Map();
|
|
52
|
+
for (const boneIndex of BONE_ORDER) {
|
|
53
|
+
const bone = skeleton.bones.get(boneIndex);
|
|
54
|
+
if (!bone) continue;
|
|
55
|
+
let parentMatrix = mat4Identity();
|
|
56
|
+
if (bone.parentIndex !== null) parentMatrix = worldMatrices.get(bone.parentIndex) ?? mat4Identity();
|
|
57
|
+
const pos = bone.position;
|
|
58
|
+
const offset = bone.positionOffset;
|
|
59
|
+
const pivot = bone.pivot;
|
|
60
|
+
let localMatrix = mat4Identity();
|
|
61
|
+
localMatrix = mat4Translate(localMatrix, [
|
|
62
|
+
pos[0] + offset[0],
|
|
63
|
+
pos[1] + offset[1],
|
|
64
|
+
pos[2] + offset[2]
|
|
65
|
+
]);
|
|
66
|
+
localMatrix = mat4Translate(localMatrix, pivot);
|
|
67
|
+
localMatrix = mat4Multiply(localMatrix, quatToMat4(bone.rotation));
|
|
68
|
+
localMatrix = mat4Translate(localMatrix, [
|
|
69
|
+
-pivot[0],
|
|
70
|
+
-pivot[1],
|
|
71
|
+
-pivot[2]
|
|
72
|
+
]);
|
|
73
|
+
const worldMatrix = mat4Multiply(parentMatrix, localMatrix);
|
|
74
|
+
worldMatrices.set(boneIndex, worldMatrix);
|
|
75
|
+
setMatrix(boneIndex, worldMatrix);
|
|
76
|
+
}
|
|
77
|
+
return matrices;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
//#endregion
|
|
81
|
+
export { BONE_COUNT, BONE_MATRICES_SIZE, computeBoneMatrices };
|
|
82
|
+
//# sourceMappingURL=BoneMatrixComputer.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BoneMatrixComputer.mjs","names":[],"sources":["../../src/viewer/BoneMatrixComputer.ts"],"sourcesContent":["/**\n * Bone Matrix Computer\n *\n * Computes world matrices for all bones in the skeleton hierarchy.\n * Separated from SkinViewer for better modularity and testability.\n */\n\nimport { mat4Identity, mat4Multiply, mat4Translate, quatToMat4 } from \"../core/math\";\nimport type { Mat4 } from \"../core/math\";\nimport { BoneIndex } from \"../model/types\";\nimport type { PlayerSkeleton } from \"../model/types\";\n\n/** Bone processing order (parent-first) */\nconst BONE_ORDER = [\n BoneIndex.Root,\n BoneIndex.Body,\n BoneIndex.Head,\n BoneIndex.RightArm,\n BoneIndex.LeftArm,\n BoneIndex.RightLeg,\n BoneIndex.LeftLeg,\n BoneIndex.HeadOverlay,\n BoneIndex.BodyOverlay,\n BoneIndex.RightArmOverlay,\n BoneIndex.LeftArmOverlay,\n BoneIndex.RightLegOverlay,\n BoneIndex.LeftLegOverlay,\n BoneIndex.Cape,\n BoneIndex.LeftWing,\n BoneIndex.RightWing,\n];\n\n/** Number of bones in the skeleton */\nexport const BONE_COUNT = 24;\n\n/** Size of bone matrix data in floats */\nexport const BONE_MATRICES_SIZE = BONE_COUNT * 16;\n\n/**\n * Bone matrix cache for efficient updates.\n * Maintains a dirty flag to avoid unnecessary recalculations.\n */\nexport interface BoneMatrixCache {\n /** Pre-allocated Float32Array for all bone matrices */\n matrices: Float32Array;\n /** Whether the matrices need to be recomputed */\n dirty: boolean;\n}\n\n/**\n * Create a new bone matrix cache\n */\nexport function createBoneMatrixCache(): BoneMatrixCache {\n return {\n matrices: new Float32Array(BONE_MATRICES_SIZE),\n dirty: true,\n };\n}\n\n/**\n * Compute bone matrices for the skeleton.\n *\n * This computes the world-space transformation matrix for each bone,\n * taking into account the bone hierarchy (parent transforms propagate to children).\n *\n * @param skeleton - The player skeleton with bone transforms\n * @returns Float32Array containing all bone matrices (24 bones * 16 floats each)\n */\nexport function computeBoneMatrices(skeleton: PlayerSkeleton): Float32Array {\n const matrices = new Float32Array(BONE_MATRICES_SIZE);\n\n // Helper to set matrix for bone index\n const setMatrix = (index: number, matrix: Mat4) => {\n matrices.set(matrix, index * 16);\n };\n\n // Initialize all matrices to identity\n for (let i = 0; i < BONE_COUNT; i++) {\n setMatrix(i, mat4Identity());\n }\n\n // Compute world matrices for each bone\n const worldMatrices = new Map<BoneIndex, Mat4>();\n\n // Process bones in parent-first order\n for (const boneIndex of BONE_ORDER) {\n const bone = skeleton.bones.get(boneIndex);\n if (!bone) continue;\n\n // Get parent matrix\n let parentMatrix = mat4Identity();\n if (bone.parentIndex !== null) {\n parentMatrix = worldMatrices.get(bone.parentIndex) ?? mat4Identity();\n }\n\n // Compute local matrix:\n // 1. Translate to bone position (relative to parent) + animation offset\n // 2. Apply rotation around pivot point\n const pos = bone.position;\n const offset = bone.positionOffset;\n const pivot = bone.pivot;\n\n // Local transform: translate to position, then rotate around pivot\n let localMatrix = mat4Identity();\n\n // Translate to bone position + animation offset\n localMatrix = mat4Translate(localMatrix, [\n pos[0] + offset[0],\n pos[1] + offset[1],\n pos[2] + offset[2],\n ]);\n\n // Translate to pivot, rotate, translate back\n localMatrix = mat4Translate(localMatrix, pivot);\n localMatrix = mat4Multiply(localMatrix, quatToMat4(bone.rotation));\n localMatrix = mat4Translate(localMatrix, [-pivot[0], -pivot[1], -pivot[2]]);\n\n // Compute world matrix\n const worldMatrix = mat4Multiply(parentMatrix, localMatrix);\n worldMatrices.set(boneIndex, worldMatrix);\n\n // Store in uniform buffer\n setMatrix(boneIndex, worldMatrix);\n }\n\n return matrices;\n}\n\n/**\n * Update bone matrix cache if dirty.\n *\n * @param cache - The bone matrix cache to update\n * @param skeleton - The player skeleton\n * @returns true if matrices were recomputed, false if cache was valid\n */\nexport function updateBoneMatrixCache(cache: BoneMatrixCache, skeleton: PlayerSkeleton): boolean {\n if (!cache.dirty) {\n return false;\n }\n\n const newMatrices = computeBoneMatrices(skeleton);\n cache.matrices.set(newMatrices);\n cache.dirty = false;\n\n return true;\n}\n\n/**\n * Mark the bone matrix cache as dirty (needs recomputation).\n */\nexport function markBoneMatricesDirty(cache: BoneMatrixCache): void {\n cache.dirty = true;\n}\n"],"mappings":";;;;;;;;;;;;;AAaA,MAAM,aAAa;CACjB,UAAU;CACV,UAAU;CACV,UAAU;CACV,UAAU;CACV,UAAU;CACV,UAAU;CACV,UAAU;CACV,UAAU;CACV,UAAU;CACV,UAAU;CACV,UAAU;CACV,UAAU;CACV,UAAU;CACV,UAAU;CACV,UAAU;CACV,UAAU;CACX;;AAGD,MAAa,aAAa;;AAG1B,MAAa,qBAAqB,aAAa;;;;;;;;;;AAgC/C,SAAgB,oBAAoB,UAAwC;CAC1E,MAAM,WAAW,IAAI,aAAa,mBAAmB;CAGrD,MAAM,aAAa,OAAe,WAAiB;AACjD,WAAS,IAAI,QAAQ,QAAQ,GAAG;;AAIlC,MAAK,IAAI,IAAI,GAAG,IAAI,YAAY,IAC9B,WAAU,GAAG,cAAc,CAAC;CAI9B,MAAM,gCAAgB,IAAI,KAAsB;AAGhD,MAAK,MAAM,aAAa,YAAY;EAClC,MAAM,OAAO,SAAS,MAAM,IAAI,UAAU;AAC1C,MAAI,CAAC,KAAM;EAGX,IAAI,eAAe,cAAc;AACjC,MAAI,KAAK,gBAAgB,KACvB,gBAAe,cAAc,IAAI,KAAK,YAAY,IAAI,cAAc;EAMtE,MAAM,MAAM,KAAK;EACjB,MAAM,SAAS,KAAK;EACpB,MAAM,QAAQ,KAAK;EAGnB,IAAI,cAAc,cAAc;AAGhC,gBAAc,cAAc,aAAa;GACvC,IAAI,KAAK,OAAO;GAChB,IAAI,KAAK,OAAO;GAChB,IAAI,KAAK,OAAO;GACjB,CAAC;AAGF,gBAAc,cAAc,aAAa,MAAM;AAC/C,gBAAc,aAAa,aAAa,WAAW,KAAK,SAAS,CAAC;AAClE,gBAAc,cAAc,aAAa;GAAC,CAAC,MAAM;GAAI,CAAC,MAAM;GAAI,CAAC,MAAM;GAAG,CAAC;EAG3E,MAAM,cAAc,aAAa,cAAc,YAAY;AAC3D,gBAAc,IAAI,WAAW,YAAY;AAGzC,YAAU,WAAW,YAAY;;AAGnC,QAAO"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
//#region src/viewer/RenderLoop.ts
|
|
2
|
+
/**
|
|
3
|
+
* Create a render loop
|
|
4
|
+
*/
|
|
5
|
+
function createRenderLoop(onUpdate, onRender) {
|
|
6
|
+
return {
|
|
7
|
+
isRunning: false,
|
|
8
|
+
lastTime: 0,
|
|
9
|
+
frameId: null,
|
|
10
|
+
onUpdate,
|
|
11
|
+
onRender
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Start the render loop
|
|
16
|
+
*/
|
|
17
|
+
function startRenderLoop(loop) {
|
|
18
|
+
if (loop.isRunning) return;
|
|
19
|
+
loop.isRunning = true;
|
|
20
|
+
loop.lastTime = performance.now();
|
|
21
|
+
const tick = (currentTime) => {
|
|
22
|
+
if (!loop.isRunning) return;
|
|
23
|
+
const deltaTime = (currentTime - loop.lastTime) / 1e3;
|
|
24
|
+
loop.lastTime = currentTime;
|
|
25
|
+
const cappedDelta = Math.min(deltaTime, .1);
|
|
26
|
+
loop.onUpdate(cappedDelta);
|
|
27
|
+
loop.onRender();
|
|
28
|
+
loop.frameId = requestAnimationFrame(tick);
|
|
29
|
+
};
|
|
30
|
+
loop.frameId = requestAnimationFrame(tick);
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Stop the render loop
|
|
34
|
+
*/
|
|
35
|
+
function stopRenderLoop(loop) {
|
|
36
|
+
loop.isRunning = false;
|
|
37
|
+
if (loop.frameId !== null) {
|
|
38
|
+
cancelAnimationFrame(loop.frameId);
|
|
39
|
+
loop.frameId = null;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
//#endregion
|
|
44
|
+
export { createRenderLoop, startRenderLoop, stopRenderLoop };
|
|
45
|
+
//# sourceMappingURL=RenderLoop.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RenderLoop.mjs","names":[],"sources":["../../src/viewer/RenderLoop.ts"],"sourcesContent":["/**\n * Render loop management\n */\n\n/** Render loop state */\nexport interface RenderLoop {\n isRunning: boolean;\n lastTime: number;\n frameId: number | null;\n onUpdate: (deltaTime: number) => void;\n onRender: () => void;\n}\n\n/**\n * Create a render loop\n */\nexport function createRenderLoop(\n onUpdate: (deltaTime: number) => void,\n onRender: () => void,\n): RenderLoop {\n return {\n isRunning: false,\n lastTime: 0,\n frameId: null,\n onUpdate,\n onRender,\n };\n}\n\n/**\n * Start the render loop\n */\nexport function startRenderLoop(loop: RenderLoop): void {\n if (loop.isRunning) return;\n\n loop.isRunning = true;\n loop.lastTime = performance.now();\n\n const tick = (currentTime: number) => {\n if (!loop.isRunning) return;\n\n const deltaTime = (currentTime - loop.lastTime) / 1000; // Convert to seconds\n loop.lastTime = currentTime;\n\n // Cap delta time to prevent huge jumps\n const cappedDelta = Math.min(deltaTime, 0.1);\n\n loop.onUpdate(cappedDelta);\n loop.onRender();\n\n loop.frameId = requestAnimationFrame(tick);\n };\n\n loop.frameId = requestAnimationFrame(tick);\n}\n\n/**\n * Stop the render loop\n */\nexport function stopRenderLoop(loop: RenderLoop): void {\n loop.isRunning = false;\n\n if (loop.frameId !== null) {\n cancelAnimationFrame(loop.frameId);\n loop.frameId = null;\n }\n}\n\n/**\n * Check if render loop is running\n */\nexport function isRenderLoopRunning(loop: RenderLoop): boolean {\n return loop.isRunning;\n}\n"],"mappings":";;;;AAgBA,SAAgB,iBACd,UACA,UACY;AACZ,QAAO;EACL,WAAW;EACX,UAAU;EACV,SAAS;EACT;EACA;EACD;;;;;AAMH,SAAgB,gBAAgB,MAAwB;AACtD,KAAI,KAAK,UAAW;AAEpB,MAAK,YAAY;AACjB,MAAK,WAAW,YAAY,KAAK;CAEjC,MAAM,QAAQ,gBAAwB;AACpC,MAAI,CAAC,KAAK,UAAW;EAErB,MAAM,aAAa,cAAc,KAAK,YAAY;AAClD,OAAK,WAAW;EAGhB,MAAM,cAAc,KAAK,IAAI,WAAW,GAAI;AAE5C,OAAK,SAAS,YAAY;AAC1B,OAAK,UAAU;AAEf,OAAK,UAAU,sBAAsB,KAAK;;AAG5C,MAAK,UAAU,sBAAsB,KAAK;;;;;AAM5C,SAAgB,eAAe,MAAwB;AACrD,MAAK,YAAY;AAEjB,KAAI,KAAK,YAAY,MAAM;AACzB,uBAAqB,KAAK,QAAQ;AAClC,OAAK,UAAU"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { mat4Identity } from "../core/math/mat4.mjs";
|
|
2
|
+
import "../core/math/index.mjs";
|
|
3
|
+
|
|
4
|
+
//#region src/viewer/RenderState.ts
|
|
5
|
+
/**
|
|
6
|
+
* Render State Management
|
|
7
|
+
*
|
|
8
|
+
* Manages pre-allocated render objects to avoid GC pressure during rendering.
|
|
9
|
+
* Also handles bind group caching for efficient draw calls.
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* Create pre-allocated render bind groups.
|
|
13
|
+
* These objects are reused each frame to avoid garbage collection.
|
|
14
|
+
*/
|
|
15
|
+
function createRenderBindGroups() {
|
|
16
|
+
const modelMatrix = mat4Identity();
|
|
17
|
+
const uniforms = {
|
|
18
|
+
u_modelMatrix: modelMatrix,
|
|
19
|
+
u_viewMatrix: null,
|
|
20
|
+
u_projectionMatrix: null,
|
|
21
|
+
"u_boneMatrices[0]": null,
|
|
22
|
+
u_alphaTest: .01
|
|
23
|
+
};
|
|
24
|
+
const skinTextures = { u_skinTexture: null };
|
|
25
|
+
const capeTextures = { u_skinTexture: null };
|
|
26
|
+
return {
|
|
27
|
+
modelMatrix,
|
|
28
|
+
uniforms,
|
|
29
|
+
skinTextures,
|
|
30
|
+
capeTextures,
|
|
31
|
+
skinBindGroup: {
|
|
32
|
+
uniforms,
|
|
33
|
+
textures: skinTextures
|
|
34
|
+
},
|
|
35
|
+
capeBindGroup: {
|
|
36
|
+
uniforms,
|
|
37
|
+
textures: capeTextures
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
//#endregion
|
|
43
|
+
export { createRenderBindGroups };
|
|
44
|
+
//# sourceMappingURL=RenderState.mjs.map
|