sa2kit 1.6.9 → 1.6.11
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/ConfigService-BxK06xP6.d.mts +262 -0
- package/dist/ConfigService-BxK06xP6.d.ts +262 -0
- package/dist/UniversalFileService-BuHN-jrR.d.ts +515 -0
- package/dist/UniversalFileService-CGGzYeeF.d.mts +515 -0
- package/dist/analytics/index.d.mts +1084 -0
- package/dist/analytics/index.d.ts +1084 -0
- package/dist/analytics/server/index.d.mts +499 -0
- package/dist/analytics/server/index.d.ts +499 -0
- package/dist/api/index.d.mts +248 -0
- package/dist/api/index.d.ts +248 -0
- package/dist/audioDetection/index.d.mts +449 -0
- package/dist/audioDetection/index.d.ts +449 -0
- package/dist/auth/client/index.d.mts +32 -0
- package/dist/auth/client/index.d.ts +32 -0
- package/dist/auth/client/index.js +4 -4
- package/dist/auth/client/index.mjs +1 -1
- package/dist/auth/components/index.d.mts +227 -0
- package/dist/auth/components/index.d.ts +227 -0
- package/dist/auth/components/index.js +4 -4
- package/dist/auth/components/index.mjs +1 -1
- package/dist/auth/hooks/index.d.mts +31 -0
- package/dist/auth/hooks/index.d.ts +31 -0
- package/dist/auth/hooks/index.js +3 -3
- package/dist/auth/hooks/index.mjs +1 -1
- package/dist/auth/index.d.mts +41 -0
- package/dist/auth/index.d.ts +41 -0
- package/dist/auth/index.js +17 -36
- package/dist/auth/index.js.map +1 -1
- package/dist/auth/index.mjs +3 -33
- package/dist/auth/index.mjs.map +1 -1
- package/dist/auth/middleware/index.d.mts +75 -0
- package/dist/auth/middleware/index.d.ts +75 -0
- package/dist/auth/routes/index.d.mts +261 -0
- package/dist/auth/routes/index.d.ts +261 -0
- package/dist/auth/schema/index.d.mts +789 -0
- package/dist/auth/schema/index.d.ts +789 -0
- package/dist/auth/services/index.d.mts +48 -0
- package/dist/auth/services/index.d.ts +48 -0
- package/dist/base-api-client-B-yUCal3.d.ts +103 -0
- package/dist/base-api-client-BQ8ZPZjk.d.mts +103 -0
- package/dist/calendar/index.d.mts +1197 -0
- package/dist/calendar/index.d.ts +1197 -0
- package/dist/calendar/index.js +8 -8
- package/dist/calendar/index.js.map +1 -1
- package/dist/calendar/index.mjs +1 -1
- package/dist/calendar/index.mjs.map +1 -1
- package/dist/calendar/routes/index.d.mts +118 -0
- package/dist/calendar/routes/index.d.ts +118 -0
- package/dist/calendar/server.d.mts +1184 -0
- package/dist/calendar/server.d.ts +1184 -0
- package/dist/{chunk-ROEYW4A7.js → chunk-5QMBZP7S.js} +2 -2
- package/dist/chunk-5QMBZP7S.js.map +1 -0
- package/dist/chunk-6BZ3QFA5.mjs +33 -0
- package/dist/chunk-6BZ3QFA5.mjs.map +1 -0
- package/dist/{chunk-HEMA7SWK.mjs → chunk-6YKMCPQI.mjs} +2 -2
- package/dist/chunk-6YKMCPQI.mjs.map +1 -0
- package/dist/chunk-BH5TLVS5.mjs +1593 -0
- package/dist/chunk-BH5TLVS5.mjs.map +1 -0
- package/dist/chunk-E72D5KHY.js +1723 -0
- package/dist/chunk-E72D5KHY.js.map +1 -0
- package/dist/chunk-FAHLZIYQ.js +36 -0
- package/dist/chunk-FAHLZIYQ.js.map +1 -0
- package/dist/{chunk-KGRQNEIR.mjs → chunk-KW5JH6V6.mjs} +2 -2
- package/dist/chunk-KW5JH6V6.mjs.map +1 -0
- package/dist/{chunk-O26VCNS3.js → chunk-UOFTHYIH.js} +2 -2
- package/dist/chunk-UOFTHYIH.js.map +1 -0
- package/dist/config/index.d.mts +64 -0
- package/dist/config/index.d.ts +64 -0
- package/dist/config/server/index.d.mts +1533 -0
- package/dist/config/server/index.d.ts +1533 -0
- package/dist/drizzle-auth-service-Bxlovhv8.d.ts +145 -0
- package/dist/drizzle-auth-service-DZY2F1sv.d.mts +145 -0
- package/dist/drizzle-schema-BNhqj2AZ.d.mts +1114 -0
- package/dist/drizzle-schema-BNhqj2AZ.d.ts +1114 -0
- package/dist/enums-Dume-V5Y.d.mts +16 -0
- package/dist/enums-Dume-V5Y.d.ts +16 -0
- package/dist/i18n/index.d.mts +417 -0
- package/dist/i18n/index.d.ts +417 -0
- package/dist/imageCrop/index.d.mts +165 -0
- package/dist/imageCrop/index.d.ts +165 -0
- package/dist/index-DSel44Ke.d.mts +93 -0
- package/dist/index-DSel44Ke.d.ts +93 -0
- package/dist/index.d.mts +933 -0
- package/dist/index.d.ts +933 -0
- package/dist/index.js +1626 -2184
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +708 -1543
- package/dist/index.mjs.map +1 -1
- package/dist/logger/index.d.mts +125 -0
- package/dist/logger/index.d.ts +125 -0
- package/dist/mmd/admin/index.d.mts +487 -0
- package/dist/mmd/admin/index.d.ts +487 -0
- package/dist/mmd/index.d.mts +1412 -0
- package/dist/mmd/index.d.ts +1412 -0
- package/dist/mmd/index.js +2695 -625
- package/dist/mmd/index.js.map +1 -1
- package/dist/mmd/index.mjs +2501 -448
- package/dist/mmd/index.mjs.map +1 -1
- package/dist/mmd/server/index.d.mts +138 -0
- package/dist/mmd/server/index.d.ts +138 -0
- package/dist/music/index.d.mts +74 -0
- package/dist/music/index.d.ts +74 -0
- package/dist/music/server/index.d.mts +1 -0
- package/dist/music/server/index.d.ts +1 -0
- package/dist/request/index.d.mts +51 -0
- package/dist/request/index.d.ts +51 -0
- package/dist/storage/index.d.mts +75 -0
- package/dist/storage/index.d.ts +75 -0
- package/dist/testYourself/admin/index.d.mts +58 -0
- package/dist/testYourself/admin/index.d.ts +58 -0
- package/dist/testYourself/index.d.mts +53 -0
- package/dist/testYourself/index.d.ts +53 -0
- package/dist/testYourself/server/index.d.mts +1029 -0
- package/dist/testYourself/server/index.d.ts +1029 -0
- package/dist/types-BB-7_WtE.d.mts +253 -0
- package/dist/types-BB-7_WtE.d.ts +253 -0
- package/dist/types-BINlP9MK.d.mts +286 -0
- package/dist/types-BINlP9MK.d.ts +286 -0
- package/dist/types-BaZccpvk.d.mts +48 -0
- package/dist/types-BaZccpvk.d.ts +48 -0
- package/dist/types-CK4We_aI.d.mts +270 -0
- package/dist/types-CK4We_aI.d.ts +270 -0
- package/dist/types-CbTsi9CZ.d.mts +31 -0
- package/dist/types-CbTsi9CZ.d.ts +31 -0
- package/dist/types-CiYK5Klf.d.mts +99 -0
- package/dist/types-D3R6GzOw.d.mts +70 -0
- package/dist/types-Dlu52uDy.d.ts +99 -0
- package/dist/types-iFeyR443.d.ts +70 -0
- package/dist/universalExport/index.d.mts +235 -0
- package/dist/universalExport/index.d.ts +235 -0
- package/dist/universalExport/server/index.d.mts +1270 -0
- package/dist/universalExport/server/index.d.ts +1270 -0
- package/dist/universalFile/index.d.mts +480 -0
- package/dist/universalFile/index.d.ts +480 -0
- package/dist/universalFile/server/index.d.mts +4516 -0
- package/dist/universalFile/server/index.d.ts +4516 -0
- package/dist/useElectronStorage-Dj0rcorG.d.mts +65 -0
- package/dist/useElectronStorage-DwnNfIhl.d.ts +65 -0
- package/dist/utils/index.d.mts +192 -0
- package/dist/utils/index.d.ts +192 -0
- package/package.json +2 -1
- package/dist/chunk-4FOBBWXW.mjs +0 -179
- package/dist/chunk-4FOBBWXW.mjs.map +0 -1
- package/dist/chunk-G6WRJ2H2.js +0 -187
- package/dist/chunk-G6WRJ2H2.js.map +0 -1
- package/dist/chunk-HEMA7SWK.mjs.map +0 -1
- package/dist/chunk-KGRQNEIR.mjs.map +0 -1
- package/dist/chunk-O26VCNS3.js.map +0 -1
- package/dist/chunk-ROEYW4A7.js.map +0 -1
package/dist/mmd/index.mjs
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { useMusic } from '../chunk-CDK3DHKM.mjs';
|
|
2
2
|
import '../chunk-ZCLAF3XN.mjs';
|
|
3
3
|
import '../chunk-BJTO5JO5.mjs';
|
|
4
|
-
import
|
|
5
|
-
import * as
|
|
6
|
-
import {
|
|
4
|
+
import React21, { forwardRef, useRef, useEffect, useImperativeHandle, useState, useCallback } from 'react';
|
|
5
|
+
import * as THREE3 from 'three';
|
|
6
|
+
import { OutlineEffect, OrbitControls, MMDLoader, MMDAnimationHelper } from 'three-stdlib';
|
|
7
7
|
import { AlertCircle, Camera, Settings, RefreshCw, CameraOff, X, Check, SkipBack, Pause, Play, SkipForward, Repeat, Repeat1, Shuffle, ListMusic, Music, Search, Loader2, Grid3x3, Minimize, Maximize, Video, User, Image as Image$1 } from 'lucide-react';
|
|
8
8
|
import { createPortal } from 'react-dom';
|
|
9
9
|
|
|
@@ -48,19 +48,1561 @@ var loadAmmo = (path = "/libs/ammo.wasm.js") => {
|
|
|
48
48
|
return ammoPromise;
|
|
49
49
|
};
|
|
50
50
|
|
|
51
|
+
// src/mmd/fx/HLSLToGLSLConverter.ts
|
|
52
|
+
var HLSLToGLSLConverter = class {
|
|
53
|
+
constructor() {
|
|
54
|
+
this.warnings = [];
|
|
55
|
+
this.uniforms = /* @__PURE__ */ new Map();
|
|
56
|
+
this.attributes = /* @__PURE__ */ new Map();
|
|
57
|
+
this.varyings = /* @__PURE__ */ new Map();
|
|
58
|
+
/**
|
|
59
|
+
* HLSL类型到GLSL类型的映射
|
|
60
|
+
*/
|
|
61
|
+
this.typeMap = {
|
|
62
|
+
// 标量类型
|
|
63
|
+
"float": "float",
|
|
64
|
+
"int": "int",
|
|
65
|
+
"bool": "bool",
|
|
66
|
+
"half": "float",
|
|
67
|
+
// GLSL没有half,使用float
|
|
68
|
+
// 向量类型
|
|
69
|
+
"float2": "vec2",
|
|
70
|
+
"float3": "vec3",
|
|
71
|
+
"float4": "vec4",
|
|
72
|
+
"int2": "ivec2",
|
|
73
|
+
"int3": "ivec3",
|
|
74
|
+
"int4": "ivec4",
|
|
75
|
+
"bool2": "bvec2",
|
|
76
|
+
"bool3": "bvec3",
|
|
77
|
+
"bool4": "bvec4",
|
|
78
|
+
"half2": "vec2",
|
|
79
|
+
"half3": "vec3",
|
|
80
|
+
"half4": "vec4",
|
|
81
|
+
// 矩阵类型
|
|
82
|
+
"float2x2": "mat2",
|
|
83
|
+
"float3x3": "mat3",
|
|
84
|
+
"float4x4": "mat4",
|
|
85
|
+
"matrix": "mat4",
|
|
86
|
+
// 纹理类型
|
|
87
|
+
"texture": "sampler2D",
|
|
88
|
+
"texture2D": "sampler2D",
|
|
89
|
+
"sampler": "sampler2D",
|
|
90
|
+
"sampler2D": "sampler2D",
|
|
91
|
+
"samplerCUBE": "samplerCube"
|
|
92
|
+
};
|
|
93
|
+
/**
|
|
94
|
+
* HLSL函数到GLSL函数的映射
|
|
95
|
+
*/
|
|
96
|
+
this.functionMap = {
|
|
97
|
+
// 纹理采样
|
|
98
|
+
"tex2D": "texture2D",
|
|
99
|
+
"tex2Dlod": "texture2DLod",
|
|
100
|
+
"texCUBE": "textureCube",
|
|
101
|
+
// 数学函数
|
|
102
|
+
"mul": "matrixCompMult",
|
|
103
|
+
// 注意:mul可能需要特殊处理
|
|
104
|
+
"lerp": "mix",
|
|
105
|
+
"frac": "fract",
|
|
106
|
+
"saturate": "clamp01",
|
|
107
|
+
// 需要自定义函数
|
|
108
|
+
"ddx": "dFdx",
|
|
109
|
+
"ddy": "dFdy",
|
|
110
|
+
// 向量函数
|
|
111
|
+
"length": "length",
|
|
112
|
+
"normalize": "normalize",
|
|
113
|
+
"dot": "dot",
|
|
114
|
+
"cross": "cross",
|
|
115
|
+
"reflect": "reflect",
|
|
116
|
+
"refract": "refract",
|
|
117
|
+
// 数学运算
|
|
118
|
+
"pow": "pow",
|
|
119
|
+
"exp": "exp",
|
|
120
|
+
"log": "log",
|
|
121
|
+
"sqrt": "sqrt",
|
|
122
|
+
"abs": "abs",
|
|
123
|
+
"sin": "sin",
|
|
124
|
+
"cos": "cos",
|
|
125
|
+
"tan": "tan",
|
|
126
|
+
"asin": "asin",
|
|
127
|
+
"acos": "acos",
|
|
128
|
+
"atan": "atan",
|
|
129
|
+
"atan2": "atan",
|
|
130
|
+
"floor": "floor",
|
|
131
|
+
"ceil": "ceil",
|
|
132
|
+
"min": "min",
|
|
133
|
+
"max": "max",
|
|
134
|
+
"clamp": "clamp",
|
|
135
|
+
"step": "step",
|
|
136
|
+
"smoothstep": "smoothstep"
|
|
137
|
+
};
|
|
138
|
+
/**
|
|
139
|
+
* HLSL语义到GLSL的映射
|
|
140
|
+
*/
|
|
141
|
+
this.semanticMap = {
|
|
142
|
+
// 顶点着色器输入
|
|
143
|
+
"POSITION": "position",
|
|
144
|
+
"POSITION0": "position",
|
|
145
|
+
"NORMAL": "normal",
|
|
146
|
+
"NORMAL0": "normal",
|
|
147
|
+
"TEXCOORD": "uv",
|
|
148
|
+
"TEXCOORD0": "uv",
|
|
149
|
+
"TEXCOORD1": "uv2",
|
|
150
|
+
"COLOR": "color",
|
|
151
|
+
"COLOR0": "color",
|
|
152
|
+
"TANGENT": "tangent",
|
|
153
|
+
"BINORMAL": "binormal",
|
|
154
|
+
// 顶点着色器输出 / 像素着色器输入
|
|
155
|
+
"SV_POSITION": "gl_Position",
|
|
156
|
+
// 像素着色器输出
|
|
157
|
+
"SV_TARGET": "gl_FragColor",
|
|
158
|
+
"SV_TARGET0": "gl_FragColor"
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* 转换HLSL shader代码为GLSL
|
|
163
|
+
*/
|
|
164
|
+
convert(hlslCode, shaderType) {
|
|
165
|
+
this.warnings = [];
|
|
166
|
+
this.uniforms.clear();
|
|
167
|
+
this.attributes.clear();
|
|
168
|
+
this.varyings.clear();
|
|
169
|
+
let glslCode = hlslCode;
|
|
170
|
+
glslCode = this.preprocessCode(glslCode);
|
|
171
|
+
glslCode = this.convertTypes(glslCode);
|
|
172
|
+
glslCode = this.convertFunctions(glslCode);
|
|
173
|
+
glslCode = this.convertSemantics(glslCode, shaderType);
|
|
174
|
+
glslCode = this.convertIOStructs(glslCode, shaderType);
|
|
175
|
+
glslCode = this.addGLSLHeader(glslCode);
|
|
176
|
+
glslCode = this.fixSyntaxDifferences(glslCode);
|
|
177
|
+
return {
|
|
178
|
+
glslCode,
|
|
179
|
+
uniforms: this.uniforms,
|
|
180
|
+
attributes: this.attributes,
|
|
181
|
+
varyings: this.varyings,
|
|
182
|
+
warnings: this.warnings
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* 预处理代码
|
|
187
|
+
*/
|
|
188
|
+
preprocessCode(code) {
|
|
189
|
+
code = code.replace(/:\s*register\([^)]+\)/g, "");
|
|
190
|
+
code = code.replace(/:\s*packoffset\([^)]+\)/g, "");
|
|
191
|
+
return code;
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* 转换类型声明
|
|
195
|
+
*/
|
|
196
|
+
convertTypes(code) {
|
|
197
|
+
for (const [hlslType, glslType] of Object.entries(this.typeMap)) {
|
|
198
|
+
const regex = new RegExp(`\\b${hlslType}\\b`, "g");
|
|
199
|
+
code = code.replace(regex, glslType);
|
|
200
|
+
}
|
|
201
|
+
return code;
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* 转换函数调用
|
|
205
|
+
*/
|
|
206
|
+
convertFunctions(code) {
|
|
207
|
+
for (const [hlslFunc, glslFunc] of Object.entries(this.functionMap)) {
|
|
208
|
+
const regex = new RegExp(`\\b${hlslFunc}\\b`, "g");
|
|
209
|
+
code = code.replace(regex, glslFunc);
|
|
210
|
+
}
|
|
211
|
+
code = this.convertMulFunction(code);
|
|
212
|
+
code = code.replace(/\bclamp01\s*\(/g, "clamp(");
|
|
213
|
+
code = code.replace(/saturate\s*\(([^)]+)\)/g, "clamp($1, 0.0, 1.0)");
|
|
214
|
+
return code;
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* 转换mul()函数
|
|
218
|
+
* HLSL: mul(matrix, vector) 或 mul(vector, matrix)
|
|
219
|
+
* GLSL: matrix * vector 或 vector * matrix
|
|
220
|
+
*/
|
|
221
|
+
convertMulFunction(code) {
|
|
222
|
+
code = code.replace(/mul\s*\(\s*([^,]+)\s*,\s*([^)]+)\s*\)/g, "($1 * $2)");
|
|
223
|
+
this.warnings.push("mul() function converted to (*) operator - may need manual adjustment for matrix multiplication order");
|
|
224
|
+
return code;
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* 转换语义
|
|
228
|
+
*/
|
|
229
|
+
convertSemantics(code, shaderType) {
|
|
230
|
+
for (const [hlslSemantic, glslSemantic] of Object.entries(this.semanticMap)) {
|
|
231
|
+
const regex = new RegExp(`\\b${hlslSemantic}\\b`, "g");
|
|
232
|
+
code = code.replace(regex, glslSemantic);
|
|
233
|
+
}
|
|
234
|
+
return code;
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* 转换输入/输出结构
|
|
238
|
+
*/
|
|
239
|
+
convertIOStructs(code, shaderType) {
|
|
240
|
+
const inputStructRegex = /struct\s+(\w+)\s*{([^}]+)}/g;
|
|
241
|
+
let match;
|
|
242
|
+
while ((match = inputStructRegex.exec(code)) !== null) {
|
|
243
|
+
if (!match[1] || !match[2]) continue;
|
|
244
|
+
match[1];
|
|
245
|
+
const structBody = match[2];
|
|
246
|
+
const members = this.parseStructMembers(structBody);
|
|
247
|
+
if (shaderType === "vertex") {
|
|
248
|
+
members.forEach((member) => {
|
|
249
|
+
this.attributes.set(member.name, {
|
|
250
|
+
type: member.type,
|
|
251
|
+
glslType: this.typeMap[member.type] || member.type,
|
|
252
|
+
semantic: member.semantic || ""
|
|
253
|
+
});
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
return code;
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* 解析结构体成员
|
|
261
|
+
*/
|
|
262
|
+
parseStructMembers(structBody) {
|
|
263
|
+
const members = [];
|
|
264
|
+
const lines = structBody.split(";");
|
|
265
|
+
lines.forEach((line) => {
|
|
266
|
+
const trimmed = line.trim();
|
|
267
|
+
if (!trimmed) return;
|
|
268
|
+
const match = trimmed.match(/(\w+)\s+(\w+)\s*(?::\s*(\w+))?/);
|
|
269
|
+
if (match && match[1] && match[2]) {
|
|
270
|
+
members.push({
|
|
271
|
+
type: match[1],
|
|
272
|
+
name: match[2],
|
|
273
|
+
semantic: match[3]
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
});
|
|
277
|
+
return members;
|
|
278
|
+
}
|
|
279
|
+
/**
|
|
280
|
+
* 添加GLSL头部和辅助函数
|
|
281
|
+
*/
|
|
282
|
+
addGLSLHeader(code) {
|
|
283
|
+
const header = `
|
|
284
|
+
// Auto-converted from HLSL to GLSL
|
|
285
|
+
#ifdef GL_ES
|
|
286
|
+
precision highp float;
|
|
287
|
+
#endif
|
|
288
|
+
|
|
289
|
+
// Helper functions for HLSL compatibility
|
|
290
|
+
vec3 mul(mat3 m, vec3 v) { return m * v; }
|
|
291
|
+
vec4 mul(mat4 m, vec4 v) { return m * v; }
|
|
292
|
+
float saturate(float x) { return clamp(x, 0.0, 1.0); }
|
|
293
|
+
vec2 saturate(vec2 x) { return clamp(x, 0.0, 1.0); }
|
|
294
|
+
vec3 saturate(vec3 x) { return clamp(x, 0.0, 1.0); }
|
|
295
|
+
vec4 saturate(vec4 x) { return clamp(x, 0.0, 1.0); }
|
|
296
|
+
|
|
297
|
+
`;
|
|
298
|
+
return header + code;
|
|
299
|
+
}
|
|
300
|
+
/**
|
|
301
|
+
* 修复语法差异
|
|
302
|
+
*/
|
|
303
|
+
fixSyntaxDifferences(code) {
|
|
304
|
+
return code;
|
|
305
|
+
}
|
|
306
|
+
/**
|
|
307
|
+
* 从FX效果中提取并转换shader
|
|
308
|
+
*/
|
|
309
|
+
convertFromFXEffect(effect, vertexShaderName, fragmentShaderName) {
|
|
310
|
+
const vsFunc = effect.shaderFunctions?.find((f) => f.name === vertexShaderName);
|
|
311
|
+
const fsFunc = effect.shaderFunctions?.find((f) => f.name === fragmentShaderName);
|
|
312
|
+
if (!vsFunc || !fsFunc) {
|
|
313
|
+
this.warnings.push(`Shader functions not found: ${vertexShaderName} or ${fragmentShaderName}`);
|
|
314
|
+
return null;
|
|
315
|
+
}
|
|
316
|
+
const vertexShader = this.convert(vsFunc.body, "vertex");
|
|
317
|
+
const fragmentShader = this.convert(fsFunc.body, "fragment");
|
|
318
|
+
return {
|
|
319
|
+
vertexShader,
|
|
320
|
+
fragmentShader
|
|
321
|
+
};
|
|
322
|
+
}
|
|
323
|
+
};
|
|
324
|
+
|
|
325
|
+
// src/mmd/fx/FXParser.ts
|
|
326
|
+
var FXParser = class {
|
|
327
|
+
constructor(options = {}) {
|
|
328
|
+
this.options = {
|
|
329
|
+
keepComments: options.keepComments ?? true,
|
|
330
|
+
parseTechniques: options.parseTechniques ?? true,
|
|
331
|
+
parseShaderFunctions: options.parseShaderFunctions ?? true,
|
|
332
|
+
parseTextures: options.parseTextures ?? true,
|
|
333
|
+
parseControllers: options.parseControllers ?? true,
|
|
334
|
+
convertToGLSL: options.convertToGLSL ?? false,
|
|
335
|
+
vertexShaderFunction: options.vertexShaderFunction,
|
|
336
|
+
fragmentShaderFunction: options.fragmentShaderFunction
|
|
337
|
+
};
|
|
338
|
+
this.hlslConverter = new HLSLToGLSLConverter();
|
|
339
|
+
}
|
|
340
|
+
/**
|
|
341
|
+
* 从URL加载并解析FX文件
|
|
342
|
+
*/
|
|
343
|
+
async loadAndParse(url) {
|
|
344
|
+
const response = await fetch(url);
|
|
345
|
+
const content = await response.text();
|
|
346
|
+
const fileName = url.split("/").pop() || "unknown.fx";
|
|
347
|
+
return this.parse(content, fileName);
|
|
348
|
+
}
|
|
349
|
+
/**
|
|
350
|
+
* 解析FX文件内容
|
|
351
|
+
*/
|
|
352
|
+
parse(content, fileName = "unknown.fx") {
|
|
353
|
+
const lines = content.split("\n");
|
|
354
|
+
const effect = {
|
|
355
|
+
fileName,
|
|
356
|
+
rawContent: content,
|
|
357
|
+
defines: [],
|
|
358
|
+
parameters: [],
|
|
359
|
+
staticVariables: [],
|
|
360
|
+
textures: [],
|
|
361
|
+
controllers: [],
|
|
362
|
+
includes: [],
|
|
363
|
+
techniques: [],
|
|
364
|
+
shaderFunctions: [],
|
|
365
|
+
comments: []
|
|
366
|
+
};
|
|
367
|
+
effect.defines = this.parseDefines(lines);
|
|
368
|
+
effect.parameters = this.parseParameters(lines);
|
|
369
|
+
effect.staticVariables = this.parseStaticVariables(lines);
|
|
370
|
+
effect.includes = this.parseIncludes(lines);
|
|
371
|
+
if (this.options.parseTextures) {
|
|
372
|
+
effect.textures = this.parseTextures(effect.defines);
|
|
373
|
+
}
|
|
374
|
+
if (this.options.parseControllers) {
|
|
375
|
+
effect.controllers = this.parseControllers(lines);
|
|
376
|
+
}
|
|
377
|
+
if (this.options.parseTechniques) {
|
|
378
|
+
effect.techniques = this.parseTechniques(content);
|
|
379
|
+
}
|
|
380
|
+
if (this.options.parseShaderFunctions) {
|
|
381
|
+
effect.shaderFunctions = this.parseShaderFunctions(content);
|
|
382
|
+
}
|
|
383
|
+
if (this.options.keepComments) {
|
|
384
|
+
effect.comments = this.parseComments(lines);
|
|
385
|
+
}
|
|
386
|
+
if (this.options.convertToGLSL) {
|
|
387
|
+
effect.glslShaders = this.convertShadersToGLSL(effect);
|
|
388
|
+
}
|
|
389
|
+
return effect;
|
|
390
|
+
}
|
|
391
|
+
/**
|
|
392
|
+
* 转换shader到GLSL
|
|
393
|
+
*/
|
|
394
|
+
convertShadersToGLSL(effect) {
|
|
395
|
+
const warnings = [];
|
|
396
|
+
let vertexShaderName = this.options.vertexShaderFunction;
|
|
397
|
+
let fragmentShaderName = this.options.fragmentShaderFunction;
|
|
398
|
+
if (!vertexShaderName || !fragmentShaderName) {
|
|
399
|
+
const firstTechnique = effect.techniques[0];
|
|
400
|
+
if (firstTechnique?.passes && firstTechnique.passes.length > 0) {
|
|
401
|
+
const firstPass = firstTechnique.passes[0];
|
|
402
|
+
if (firstPass) {
|
|
403
|
+
vertexShaderName = vertexShaderName || firstPass.vertexShader?.function || void 0;
|
|
404
|
+
fragmentShaderName = fragmentShaderName || firstPass.pixelShader?.function || void 0;
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
if (!vertexShaderName || !fragmentShaderName) {
|
|
409
|
+
warnings.push("Could not determine shader function names. Skipping GLSL conversion.");
|
|
410
|
+
return {
|
|
411
|
+
warnings
|
|
412
|
+
};
|
|
413
|
+
}
|
|
414
|
+
const vsFunc = effect.shaderFunctions.find((f) => f.name === vertexShaderName);
|
|
415
|
+
const fsFunc = effect.shaderFunctions.find((f) => f.name === fragmentShaderName);
|
|
416
|
+
if (!vsFunc || !fsFunc) {
|
|
417
|
+
warnings.push(`Shader functions not found: ${vertexShaderName} or ${fragmentShaderName}`);
|
|
418
|
+
return {
|
|
419
|
+
warnings
|
|
420
|
+
};
|
|
421
|
+
}
|
|
422
|
+
const vertexResult = this.hlslConverter.convert(vsFunc.body, "vertex");
|
|
423
|
+
warnings.push(...vertexResult.warnings);
|
|
424
|
+
const fragmentResult = this.hlslConverter.convert(fsFunc.body, "fragment");
|
|
425
|
+
warnings.push(...fragmentResult.warnings);
|
|
426
|
+
return {
|
|
427
|
+
vertexShader: {
|
|
428
|
+
code: vertexResult.glslCode,
|
|
429
|
+
uniforms: vertexResult.uniforms,
|
|
430
|
+
attributes: vertexResult.attributes,
|
|
431
|
+
varyings: vertexResult.varyings
|
|
432
|
+
},
|
|
433
|
+
fragmentShader: {
|
|
434
|
+
code: fragmentResult.glslCode,
|
|
435
|
+
uniforms: fragmentResult.uniforms,
|
|
436
|
+
attributes: /* @__PURE__ */ new Map(),
|
|
437
|
+
varyings: fragmentResult.varyings
|
|
438
|
+
},
|
|
439
|
+
warnings
|
|
440
|
+
};
|
|
441
|
+
}
|
|
442
|
+
/**
|
|
443
|
+
* 解析宏定义 (#define)
|
|
444
|
+
*/
|
|
445
|
+
parseDefines(lines) {
|
|
446
|
+
const defines = [];
|
|
447
|
+
const defineRegex = /^\s*(\/\/)?\s*#define\s+(\w+)(?:\s+(.+))?\s*$/;
|
|
448
|
+
lines.forEach((line, index) => {
|
|
449
|
+
const match = line.match(defineRegex);
|
|
450
|
+
if (match && match[2]) {
|
|
451
|
+
const isCommented = !!match[1];
|
|
452
|
+
const name = match[2];
|
|
453
|
+
const value = match[3]?.trim();
|
|
454
|
+
let comment;
|
|
455
|
+
if (value) {
|
|
456
|
+
const commentMatch = value.match(/\/\/(.+)$/);
|
|
457
|
+
if (commentMatch && commentMatch[1]) {
|
|
458
|
+
comment = commentMatch[1].trim();
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
const cleanValue = value?.replace(/\/\/.*$/, "").trim();
|
|
462
|
+
defines.push({
|
|
463
|
+
name,
|
|
464
|
+
value: cleanValue || void 0,
|
|
465
|
+
isCommented,
|
|
466
|
+
lineNumber: index + 1,
|
|
467
|
+
comment
|
|
468
|
+
});
|
|
469
|
+
}
|
|
470
|
+
});
|
|
471
|
+
return defines;
|
|
472
|
+
}
|
|
473
|
+
/**
|
|
474
|
+
* 解析参数声明
|
|
475
|
+
*/
|
|
476
|
+
parseParameters(lines) {
|
|
477
|
+
const parameters = [];
|
|
478
|
+
const paramRegex = /^\s*(float|float2|float3|float4|float4x4|texture|sampler|sampler2D|bool|int)\s+(\w+)\s*(?::\s*(\w+))?\s*(?:<([^>]+)>)?\s*(?:=\s*([^;]+))?\s*;/;
|
|
479
|
+
lines.forEach((line, index) => {
|
|
480
|
+
const match = line.match(paramRegex);
|
|
481
|
+
if (match && match[1] && match[2]) {
|
|
482
|
+
const type = match[1];
|
|
483
|
+
const name = match[2];
|
|
484
|
+
const semantic = match[3];
|
|
485
|
+
const annotationsStr = match[4];
|
|
486
|
+
const defaultValue = match[5]?.trim();
|
|
487
|
+
let annotations;
|
|
488
|
+
if (annotationsStr) {
|
|
489
|
+
annotations = this.parseAnnotations(annotationsStr);
|
|
490
|
+
}
|
|
491
|
+
parameters.push({
|
|
492
|
+
type,
|
|
493
|
+
name,
|
|
494
|
+
semantic,
|
|
495
|
+
defaultValue,
|
|
496
|
+
annotations,
|
|
497
|
+
lineNumber: index + 1
|
|
498
|
+
});
|
|
499
|
+
}
|
|
500
|
+
});
|
|
501
|
+
return parameters;
|
|
502
|
+
}
|
|
503
|
+
/**
|
|
504
|
+
* 解析注解 (annotations)
|
|
505
|
+
*/
|
|
506
|
+
parseAnnotations(annotationsStr) {
|
|
507
|
+
const annotations = {};
|
|
508
|
+
const annotationRegex = /(string|float|int|bool)\s+(\w+)\s*=\s*"?([^;"]+)"?/g;
|
|
509
|
+
let match;
|
|
510
|
+
while ((match = annotationRegex.exec(annotationsStr)) !== null) {
|
|
511
|
+
if (!match[1] || !match[2] || !match[3]) continue;
|
|
512
|
+
const type = match[1];
|
|
513
|
+
const name = match[2];
|
|
514
|
+
const value = match[3].trim();
|
|
515
|
+
if (type === "float") {
|
|
516
|
+
annotations[name] = parseFloat(value);
|
|
517
|
+
} else if (type === "int") {
|
|
518
|
+
annotations[name] = parseInt(value, 10);
|
|
519
|
+
} else if (type === "bool") {
|
|
520
|
+
annotations[name] = value === "true";
|
|
521
|
+
} else {
|
|
522
|
+
annotations[name] = value;
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
return annotations;
|
|
526
|
+
}
|
|
527
|
+
/**
|
|
528
|
+
* 解析静态变量
|
|
529
|
+
*/
|
|
530
|
+
parseStaticVariables(lines) {
|
|
531
|
+
const variables = [];
|
|
532
|
+
const staticRegex = /^\s*static\s+(float|float2|float3|float4|bool|int)\s+(\w+)\s*=\s*(.+?);/;
|
|
533
|
+
lines.forEach((line, index) => {
|
|
534
|
+
const match = line.match(staticRegex);
|
|
535
|
+
if (match && match[1] && match[2] && match[3]) {
|
|
536
|
+
variables.push({
|
|
537
|
+
type: match[1],
|
|
538
|
+
name: match[2],
|
|
539
|
+
expression: match[3].trim(),
|
|
540
|
+
lineNumber: index + 1
|
|
541
|
+
});
|
|
542
|
+
}
|
|
543
|
+
});
|
|
544
|
+
return variables;
|
|
545
|
+
}
|
|
546
|
+
/**
|
|
547
|
+
* 解析纹理引用
|
|
548
|
+
*/
|
|
549
|
+
parseTextures(defines) {
|
|
550
|
+
const textures = [];
|
|
551
|
+
const textureDefineRegex = /^BLEND(\w+)TEXTURE$/;
|
|
552
|
+
defines.forEach((define) => {
|
|
553
|
+
const match = define.name.match(textureDefineRegex);
|
|
554
|
+
if (match && match[1] && define.value && !define.isCommented) {
|
|
555
|
+
const purpose = match[1].toLowerCase();
|
|
556
|
+
const path = define.value.replace(/"/g, "");
|
|
557
|
+
const widthDefine = defines.find((d) => d.name === `${define.name}_X`);
|
|
558
|
+
const heightDefine = defines.find((d) => d.name === `${define.name}_Y`);
|
|
559
|
+
textures.push({
|
|
560
|
+
name: define.name,
|
|
561
|
+
path,
|
|
562
|
+
width: widthDefine?.value ? parseInt(widthDefine.value, 10) : void 0,
|
|
563
|
+
height: heightDefine?.value ? parseInt(heightDefine.value, 10) : void 0,
|
|
564
|
+
purpose
|
|
565
|
+
});
|
|
566
|
+
}
|
|
567
|
+
});
|
|
568
|
+
return textures;
|
|
569
|
+
}
|
|
570
|
+
/**
|
|
571
|
+
* 解析控制器引用 (CONTROLOBJECT)
|
|
572
|
+
*/
|
|
573
|
+
parseControllers(lines) {
|
|
574
|
+
const controllers = [];
|
|
575
|
+
const controllerRegex = /(\w+)\s*:\s*CONTROLOBJECT\s*<\s*string\s+name\s*=\s*"([^"]+)"\s*;\s*string\s+item\s*=\s*"([^"]+)"\s*>/;
|
|
576
|
+
lines.forEach((line) => {
|
|
577
|
+
const match = line.match(controllerRegex);
|
|
578
|
+
if (match && match[1] && match[2] && match[3]) {
|
|
579
|
+
controllers.push({
|
|
580
|
+
name: match[1],
|
|
581
|
+
objectName: match[2],
|
|
582
|
+
itemName: match[3],
|
|
583
|
+
boundParameter: match[1]
|
|
584
|
+
});
|
|
585
|
+
}
|
|
586
|
+
});
|
|
587
|
+
return controllers;
|
|
588
|
+
}
|
|
589
|
+
/**
|
|
590
|
+
* 解析include指令
|
|
591
|
+
*/
|
|
592
|
+
parseIncludes(lines) {
|
|
593
|
+
const includes = [];
|
|
594
|
+
const includeRegex = /^\s*#include\s+"([^"]+)"/;
|
|
595
|
+
lines.forEach((line) => {
|
|
596
|
+
const match = line.match(includeRegex);
|
|
597
|
+
if (match && match[1]) {
|
|
598
|
+
includes.push(match[1]);
|
|
599
|
+
}
|
|
600
|
+
});
|
|
601
|
+
return includes;
|
|
602
|
+
}
|
|
603
|
+
/**
|
|
604
|
+
* 解析Technique定义
|
|
605
|
+
*/
|
|
606
|
+
parseTechniques(content) {
|
|
607
|
+
const techniques = [];
|
|
608
|
+
const techniqueRegex = /technique\s+(\w+)\s*\{([^}]+(?:\{[^}]*\}[^}]*)*)\}/g;
|
|
609
|
+
let match;
|
|
610
|
+
while ((match = techniqueRegex.exec(content)) !== null) {
|
|
611
|
+
if (!match[1] || !match[2]) continue;
|
|
612
|
+
const name = match[1];
|
|
613
|
+
const body = match[2];
|
|
614
|
+
const lineNumber = content.substring(0, match.index).split("\n").length;
|
|
615
|
+
techniques.push({
|
|
616
|
+
name,
|
|
617
|
+
passes: this.parsePasses(body),
|
|
618
|
+
lineNumber
|
|
619
|
+
});
|
|
620
|
+
}
|
|
621
|
+
return techniques;
|
|
622
|
+
}
|
|
623
|
+
/**
|
|
624
|
+
* 解析Pass定义
|
|
625
|
+
*/
|
|
626
|
+
parsePasses(techniqueBody) {
|
|
627
|
+
const passes = [];
|
|
628
|
+
const passRegex = /pass\s+(\w+)?\s*\{([^}]+)\}/g;
|
|
629
|
+
let match;
|
|
630
|
+
while ((match = passRegex.exec(techniqueBody)) !== null) {
|
|
631
|
+
if (!match[2]) continue;
|
|
632
|
+
const name = match[1] || void 0;
|
|
633
|
+
const body = match[2];
|
|
634
|
+
const pass = {
|
|
635
|
+
name,
|
|
636
|
+
renderStates: {}
|
|
637
|
+
};
|
|
638
|
+
const vsMatch = /VertexShader\s*=\s*compile\s+(\w+)\s+(\w+)/i.exec(body);
|
|
639
|
+
if (vsMatch && vsMatch[1] && vsMatch[2]) {
|
|
640
|
+
pass.vertexShader = {
|
|
641
|
+
profile: vsMatch[1],
|
|
642
|
+
function: vsMatch[2]
|
|
643
|
+
};
|
|
644
|
+
}
|
|
645
|
+
const psMatch = /PixelShader\s*=\s*compile\s+(\w+)\s+(\w+)/i.exec(body);
|
|
646
|
+
if (psMatch && psMatch[1] && psMatch[2]) {
|
|
647
|
+
pass.pixelShader = {
|
|
648
|
+
profile: psMatch[1],
|
|
649
|
+
function: psMatch[2]
|
|
650
|
+
};
|
|
651
|
+
}
|
|
652
|
+
const stateRegex = /(\w+)\s*=\s*([^;]+);/g;
|
|
653
|
+
let stateMatch;
|
|
654
|
+
while ((stateMatch = stateRegex.exec(body)) !== null) {
|
|
655
|
+
if (!stateMatch[1] || !stateMatch[2]) continue;
|
|
656
|
+
const stateName = stateMatch[1];
|
|
657
|
+
const stateValue = stateMatch[2].trim();
|
|
658
|
+
if (stateName !== "VertexShader" && stateName !== "PixelShader") {
|
|
659
|
+
pass.renderStates[stateName] = stateValue;
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
passes.push(pass);
|
|
663
|
+
}
|
|
664
|
+
return passes;
|
|
665
|
+
}
|
|
666
|
+
/**
|
|
667
|
+
* 解析着色器函数
|
|
668
|
+
*/
|
|
669
|
+
parseShaderFunctions(content) {
|
|
670
|
+
const functions = [];
|
|
671
|
+
const functionRegex = /(struct|void|float|float2|float3|float4)\s+(\w+)\s*\(([^)]*)\)\s*(?::\s*(\w+))?\s*\{/g;
|
|
672
|
+
let match;
|
|
673
|
+
while ((match = functionRegex.exec(content)) !== null) {
|
|
674
|
+
if (!match[1] || !match[2] || !match[3]) continue;
|
|
675
|
+
const returnType = match[1];
|
|
676
|
+
const name = match[2];
|
|
677
|
+
const parameters = match[3];
|
|
678
|
+
const outputSemantic = match[4] || void 0;
|
|
679
|
+
const lineNumber = content.substring(0, match.index).split("\n").length;
|
|
680
|
+
const bodyStart = match.index + match[0].length;
|
|
681
|
+
let braceCount = 1;
|
|
682
|
+
let bodyEnd = bodyStart;
|
|
683
|
+
for (let i = bodyStart; i < content.length && braceCount > 0; i++) {
|
|
684
|
+
if (content[i] === "{") braceCount++;
|
|
685
|
+
if (content[i] === "}") braceCount--;
|
|
686
|
+
bodyEnd = i;
|
|
687
|
+
}
|
|
688
|
+
const body = content.substring(bodyStart, bodyEnd);
|
|
689
|
+
functions.push({
|
|
690
|
+
name,
|
|
691
|
+
returnType,
|
|
692
|
+
parameters,
|
|
693
|
+
body,
|
|
694
|
+
outputSemantic,
|
|
695
|
+
lineNumber
|
|
696
|
+
});
|
|
697
|
+
}
|
|
698
|
+
return functions;
|
|
699
|
+
}
|
|
700
|
+
/**
|
|
701
|
+
* 解析注释
|
|
702
|
+
*/
|
|
703
|
+
parseComments(lines) {
|
|
704
|
+
const comments = [];
|
|
705
|
+
lines.forEach((line, index) => {
|
|
706
|
+
const lineCommentMatch = line.match(/^\s*\/\/(.*)$/);
|
|
707
|
+
if (lineCommentMatch && lineCommentMatch[1]) {
|
|
708
|
+
comments.push({
|
|
709
|
+
content: lineCommentMatch[1].trim(),
|
|
710
|
+
type: "line",
|
|
711
|
+
lineNumber: index + 1
|
|
712
|
+
});
|
|
713
|
+
}
|
|
714
|
+
const blockCommentMatch = line.match(/\/\*(.+?)\*\//);
|
|
715
|
+
if (blockCommentMatch && blockCommentMatch[1]) {
|
|
716
|
+
comments.push({
|
|
717
|
+
content: blockCommentMatch[1].trim(),
|
|
718
|
+
type: "block",
|
|
719
|
+
lineNumber: index + 1
|
|
720
|
+
});
|
|
721
|
+
}
|
|
722
|
+
});
|
|
723
|
+
return comments;
|
|
724
|
+
}
|
|
725
|
+
/**
|
|
726
|
+
* 生成FX文件摘要
|
|
727
|
+
*/
|
|
728
|
+
generateSummary(effect) {
|
|
729
|
+
const enabledDefines = effect.defines.filter((d) => !d.isCommented).map((d) => d.name);
|
|
730
|
+
const disabledDefines = effect.defines.filter((d) => d.isCommented).map((d) => d.name);
|
|
731
|
+
return {
|
|
732
|
+
fileName: effect.fileName,
|
|
733
|
+
defineCount: effect.defines.length,
|
|
734
|
+
parameterCount: effect.parameters.length,
|
|
735
|
+
textureCount: effect.textures.length,
|
|
736
|
+
techniqueCount: effect.techniques.length,
|
|
737
|
+
hasLocalShadow: enabledDefines.includes("USE_LOCALSHADOW"),
|
|
738
|
+
hasExcellentShadow: enabledDefines.includes("USE_EXCELLENTSHADOW"),
|
|
739
|
+
hasHgShadow: enabledDefines.includes("USE_HGSHADOW"),
|
|
740
|
+
enabledDefines,
|
|
741
|
+
disabledDefines
|
|
742
|
+
};
|
|
743
|
+
}
|
|
744
|
+
/**
|
|
745
|
+
* 提取特定着色器函数的代码
|
|
746
|
+
*/
|
|
747
|
+
extractShaderFunction(effect, functionName) {
|
|
748
|
+
const func = effect.shaderFunctions.find((f) => f.name === functionName);
|
|
749
|
+
if (!func) return null;
|
|
750
|
+
return `${func.returnType} ${func.name}(${func.parameters})${func.outputSemantic ? " : " + func.outputSemantic : ""}
|
|
751
|
+
{
|
|
752
|
+
${func.body}
|
|
753
|
+
}`;
|
|
754
|
+
}
|
|
755
|
+
/**
|
|
756
|
+
* 获取所有启用的功能标志
|
|
757
|
+
*/
|
|
758
|
+
getEnabledFeatures(effect) {
|
|
759
|
+
return effect.defines.filter((d) => !d.isCommented && !d.value).map((d) => d.name);
|
|
760
|
+
}
|
|
761
|
+
/**
|
|
762
|
+
* 获取配置参数
|
|
763
|
+
*/
|
|
764
|
+
getConfigParameters(effect) {
|
|
765
|
+
return effect.parameters.filter(
|
|
766
|
+
(p) => p.annotations?.UIName || p.annotations?.UIWidget
|
|
767
|
+
);
|
|
768
|
+
}
|
|
769
|
+
};
|
|
770
|
+
var FXToThreeAdapter = class {
|
|
771
|
+
constructor(effect, basePath = "") {
|
|
772
|
+
this.effect = effect;
|
|
773
|
+
this.basePath = basePath;
|
|
774
|
+
this.textureLoader = new THREE3.TextureLoader();
|
|
775
|
+
this.loadedTextures = /* @__PURE__ */ new Map();
|
|
776
|
+
}
|
|
777
|
+
/**
|
|
778
|
+
* 创建ShaderMaterial(如果有GLSL shader)
|
|
779
|
+
*/
|
|
780
|
+
createShaderMaterial() {
|
|
781
|
+
if (!this.effect.glslShaders?.vertexShader || !this.effect.glslShaders?.fragmentShader) {
|
|
782
|
+
console.warn("[FXToThreeAdapter] No GLSL shaders available. Use convertToGLSL option in FXParser.");
|
|
783
|
+
return null;
|
|
784
|
+
}
|
|
785
|
+
const vs = this.effect.glslShaders.vertexShader;
|
|
786
|
+
const fs = this.effect.glslShaders.fragmentShader;
|
|
787
|
+
const uniforms = {};
|
|
788
|
+
vs.uniforms.forEach((info, name) => {
|
|
789
|
+
uniforms[name] = { value: this.getDefaultUniformValue(info.glslType) };
|
|
790
|
+
});
|
|
791
|
+
fs.uniforms.forEach((info, name) => {
|
|
792
|
+
if (!uniforms[name]) {
|
|
793
|
+
uniforms[name] = { value: this.getDefaultUniformValue(info.glslType) };
|
|
794
|
+
}
|
|
795
|
+
});
|
|
796
|
+
uniforms.modelMatrix = { value: new THREE3.Matrix4() };
|
|
797
|
+
uniforms.viewMatrix = { value: new THREE3.Matrix4() };
|
|
798
|
+
uniforms.projectionMatrix = { value: new THREE3.Matrix4() };
|
|
799
|
+
uniforms.normalMatrix = { value: new THREE3.Matrix3() };
|
|
800
|
+
uniforms.cameraPosition = { value: new THREE3.Vector3() };
|
|
801
|
+
const material = new THREE3.ShaderMaterial({
|
|
802
|
+
vertexShader: vs.code,
|
|
803
|
+
fragmentShader: fs.code,
|
|
804
|
+
uniforms
|
|
805
|
+
});
|
|
806
|
+
console.log("[FXToThreeAdapter] Created ShaderMaterial with", Object.keys(uniforms).length, "uniforms");
|
|
807
|
+
console.log("[FXToThreeAdapter] Conversion warnings:", this.effect.glslShaders.warnings);
|
|
808
|
+
return material;
|
|
809
|
+
}
|
|
810
|
+
/**
|
|
811
|
+
* 获取uniform的默认值
|
|
812
|
+
*/
|
|
813
|
+
getDefaultUniformValue(glslType) {
|
|
814
|
+
switch (glslType) {
|
|
815
|
+
case "float":
|
|
816
|
+
return 0;
|
|
817
|
+
case "vec2":
|
|
818
|
+
return new THREE3.Vector2(0, 0);
|
|
819
|
+
case "vec3":
|
|
820
|
+
return new THREE3.Vector3(0, 0, 0);
|
|
821
|
+
case "vec4":
|
|
822
|
+
return new THREE3.Vector4(0, 0, 0, 0);
|
|
823
|
+
case "mat3":
|
|
824
|
+
return new THREE3.Matrix3();
|
|
825
|
+
case "mat4":
|
|
826
|
+
return new THREE3.Matrix4();
|
|
827
|
+
case "sampler2D":
|
|
828
|
+
return null;
|
|
829
|
+
case "samplerCube":
|
|
830
|
+
return null;
|
|
831
|
+
default:
|
|
832
|
+
return null;
|
|
833
|
+
}
|
|
834
|
+
}
|
|
835
|
+
/**
|
|
836
|
+
* 提取材质配置
|
|
837
|
+
*/
|
|
838
|
+
extractMaterialConfig() {
|
|
839
|
+
const config = {
|
|
840
|
+
uniforms: {}
|
|
841
|
+
};
|
|
842
|
+
console.log("[FXToThreeAdapter] Extracting material config from FX:", this.effect.fileName);
|
|
843
|
+
console.log("[FXToThreeAdapter] Total parameters:", this.effect.parameters.length);
|
|
844
|
+
this.effect.parameters.forEach((param) => {
|
|
845
|
+
const name = param.name.toLowerCase();
|
|
846
|
+
if (name.startsWith("add")) {
|
|
847
|
+
console.log(`[FXToThreeAdapter] Skipping additive param "${param.name}" (not an absolute value)`);
|
|
848
|
+
return;
|
|
849
|
+
}
|
|
850
|
+
if (name.includes("materialrgb") || name.includes("material")) {
|
|
851
|
+
const colorValue = this.parseFloat3(param.defaultValue);
|
|
852
|
+
if (colorValue) {
|
|
853
|
+
config.color = new THREE3.Color(
|
|
854
|
+
colorValue[0],
|
|
855
|
+
colorValue[1],
|
|
856
|
+
colorValue[2]
|
|
857
|
+
);
|
|
858
|
+
console.log(`[FXToThreeAdapter] Found color param "${param.name}":`, colorValue);
|
|
859
|
+
}
|
|
860
|
+
}
|
|
861
|
+
if (name.includes("emissive")) {
|
|
862
|
+
const emissiveValue = this.parseFloat3(param.defaultValue);
|
|
863
|
+
if (emissiveValue) {
|
|
864
|
+
config.emissive = new THREE3.Color(
|
|
865
|
+
emissiveValue[0],
|
|
866
|
+
emissiveValue[1],
|
|
867
|
+
emissiveValue[2]
|
|
868
|
+
);
|
|
869
|
+
console.log(`[FXToThreeAdapter] Found emissive param "${param.name}":`, emissiveValue);
|
|
870
|
+
}
|
|
871
|
+
}
|
|
872
|
+
if (name.includes("specular")) {
|
|
873
|
+
const specularValue = this.parseFloat3(param.defaultValue);
|
|
874
|
+
if (specularValue) {
|
|
875
|
+
config.specular = new THREE3.Color(
|
|
876
|
+
specularValue[0],
|
|
877
|
+
specularValue[1],
|
|
878
|
+
specularValue[2]
|
|
879
|
+
);
|
|
880
|
+
console.log(`[FXToThreeAdapter] Found specular param "${param.name}":`, specularValue);
|
|
881
|
+
}
|
|
882
|
+
}
|
|
883
|
+
if (name.includes("shininess") || name.includes("specularpower")) {
|
|
884
|
+
const shininessValue = this.parseFloat(param.defaultValue);
|
|
885
|
+
if (shininessValue !== null) {
|
|
886
|
+
config.shininess = shininessValue;
|
|
887
|
+
console.log(`[FXToThreeAdapter] Found shininess param "${param.name}":`, shininessValue);
|
|
888
|
+
}
|
|
889
|
+
}
|
|
890
|
+
if (config.uniforms && param.defaultValue) {
|
|
891
|
+
config.uniforms[param.name] = {
|
|
892
|
+
value: this.parseParameterValue(param)
|
|
893
|
+
};
|
|
894
|
+
}
|
|
895
|
+
});
|
|
896
|
+
console.log("[FXToThreeAdapter] Final material config:", {
|
|
897
|
+
color: config.color,
|
|
898
|
+
emissive: config.emissive,
|
|
899
|
+
specular: config.specular,
|
|
900
|
+
shininess: config.shininess
|
|
901
|
+
});
|
|
902
|
+
return config;
|
|
903
|
+
}
|
|
904
|
+
/**
|
|
905
|
+
* 提取渲染配置
|
|
906
|
+
*/
|
|
907
|
+
extractRenderConfig() {
|
|
908
|
+
const config = {};
|
|
909
|
+
const hasLocalShadow = this.effect.defines.some(
|
|
910
|
+
(d) => d.name === "USE_LOCALSHADOW" && !d.isCommented
|
|
911
|
+
);
|
|
912
|
+
const hasExcellentShadow = this.effect.defines.some(
|
|
913
|
+
(d) => d.name === "USE_EXCELLENTSHADOW" && !d.isCommented
|
|
914
|
+
);
|
|
915
|
+
const hasHgShadow = this.effect.defines.some(
|
|
916
|
+
(d) => d.name === "USE_HGSHADOW" && !d.isCommented
|
|
917
|
+
);
|
|
918
|
+
config.enableShadow = hasLocalShadow || hasExcellentShadow || hasHgShadow;
|
|
919
|
+
const shadowMapSizeDefine = this.effect.defines.find(
|
|
920
|
+
(d) => d.name === "LS_ShadowMapBuffSize"
|
|
921
|
+
);
|
|
922
|
+
if (shadowMapSizeDefine?.value) {
|
|
923
|
+
config.shadowMapSize = parseInt(shadowMapSizeDefine.value, 10);
|
|
924
|
+
}
|
|
925
|
+
const lightDirDefine = this.effect.defines.find(
|
|
926
|
+
(d) => d.name === "LS_InitDirection"
|
|
927
|
+
);
|
|
928
|
+
if (lightDirDefine?.value) {
|
|
929
|
+
const dir = this.parseFloat3(lightDirDefine.value);
|
|
930
|
+
if (dir) {
|
|
931
|
+
config.lightDirection = new THREE3.Vector3(dir[0], dir[1], dir[2]).normalize();
|
|
932
|
+
}
|
|
933
|
+
}
|
|
934
|
+
const hasToon = this.effect.defines.some(
|
|
935
|
+
(d) => d.name === "MODEL_TOON" && !d.isCommented
|
|
936
|
+
);
|
|
937
|
+
config.toneMapping = hasToon ? THREE3.NoToneMapping : THREE3.ACESFilmicToneMapping;
|
|
938
|
+
config.toneMappingExposure = 1;
|
|
939
|
+
console.log("[FXToThreeAdapter] Extracted render config:", config);
|
|
940
|
+
return config;
|
|
941
|
+
}
|
|
942
|
+
/**
|
|
943
|
+
* 加载纹理
|
|
944
|
+
*/
|
|
945
|
+
async loadTextures() {
|
|
946
|
+
const promises = this.effect.textures.map(async (fxTexture) => {
|
|
947
|
+
const path = this.basePath ? `${this.basePath}/${fxTexture.path}` : fxTexture.path;
|
|
948
|
+
try {
|
|
949
|
+
const texture = await this.loadTexture(path);
|
|
950
|
+
this.loadedTextures.set(fxTexture.name, texture);
|
|
951
|
+
return { name: fxTexture.name, texture };
|
|
952
|
+
} catch (error) {
|
|
953
|
+
console.warn(`Failed to load texture ${fxTexture.name}:`, error);
|
|
954
|
+
return null;
|
|
955
|
+
}
|
|
956
|
+
});
|
|
957
|
+
await Promise.all(promises);
|
|
958
|
+
return this.loadedTextures;
|
|
959
|
+
}
|
|
960
|
+
/**
|
|
961
|
+
* 加载单个纹理
|
|
962
|
+
*/
|
|
963
|
+
loadTexture(path) {
|
|
964
|
+
return new Promise((resolve, reject) => {
|
|
965
|
+
this.textureLoader.load(
|
|
966
|
+
path,
|
|
967
|
+
(texture) => {
|
|
968
|
+
texture.wrapS = THREE3.RepeatWrapping;
|
|
969
|
+
texture.wrapT = THREE3.RepeatWrapping;
|
|
970
|
+
texture.needsUpdate = true;
|
|
971
|
+
resolve(texture);
|
|
972
|
+
},
|
|
973
|
+
void 0,
|
|
974
|
+
(error) => reject(error)
|
|
975
|
+
);
|
|
976
|
+
});
|
|
977
|
+
}
|
|
978
|
+
/**
|
|
979
|
+
* 创建Three.js材质
|
|
980
|
+
*/
|
|
981
|
+
createMaterial() {
|
|
982
|
+
const config = this.extractMaterialConfig();
|
|
983
|
+
const material = new THREE3.MeshPhongMaterial({
|
|
984
|
+
color: config.color || 16777215,
|
|
985
|
+
emissive: config.emissive || 0,
|
|
986
|
+
specular: config.specular || 1118481,
|
|
987
|
+
shininess: config.shininess ?? 30,
|
|
988
|
+
transparent: config.transparent ?? false,
|
|
989
|
+
opacity: config.opacity ?? 1
|
|
990
|
+
});
|
|
991
|
+
const diffuseTexture = this.getTextureByPurpose("diffuse");
|
|
992
|
+
if (diffuseTexture) {
|
|
993
|
+
material.map = diffuseTexture;
|
|
994
|
+
}
|
|
995
|
+
const normalTexture = this.getTextureByPurpose("normal");
|
|
996
|
+
if (normalTexture) {
|
|
997
|
+
material.normalMap = normalTexture;
|
|
998
|
+
}
|
|
999
|
+
return material;
|
|
1000
|
+
}
|
|
1001
|
+
/**
|
|
1002
|
+
* 配置Three.js场景
|
|
1003
|
+
* 仅应用渲染器配置,不添加光源
|
|
1004
|
+
*/
|
|
1005
|
+
configureScene(scene, renderer) {
|
|
1006
|
+
const renderConfig = this.extractRenderConfig();
|
|
1007
|
+
if (renderConfig.enableShadow !== void 0) {
|
|
1008
|
+
renderer.shadowMap.enabled = renderConfig.enableShadow;
|
|
1009
|
+
if (renderConfig.enableShadow) {
|
|
1010
|
+
renderer.shadowMap.type = THREE3.PCFSoftShadowMap;
|
|
1011
|
+
}
|
|
1012
|
+
}
|
|
1013
|
+
if (renderConfig.toneMapping !== void 0) {
|
|
1014
|
+
renderer.toneMapping = renderConfig.toneMapping;
|
|
1015
|
+
}
|
|
1016
|
+
if (renderConfig.toneMappingExposure !== void 0) {
|
|
1017
|
+
renderer.toneMappingExposure = renderConfig.toneMappingExposure;
|
|
1018
|
+
}
|
|
1019
|
+
}
|
|
1020
|
+
/**
|
|
1021
|
+
* 获取指定用途的纹理
|
|
1022
|
+
*/
|
|
1023
|
+
getTextureByPurpose(purpose) {
|
|
1024
|
+
const fxTexture = this.effect.textures.find(
|
|
1025
|
+
(t) => t.purpose?.toLowerCase().includes(purpose.toLowerCase())
|
|
1026
|
+
);
|
|
1027
|
+
if (!fxTexture) return null;
|
|
1028
|
+
return this.loadedTextures.get(fxTexture.name) || null;
|
|
1029
|
+
}
|
|
1030
|
+
/**
|
|
1031
|
+
* 解析float3值
|
|
1032
|
+
*/
|
|
1033
|
+
parseFloat3(value) {
|
|
1034
|
+
if (!value) return null;
|
|
1035
|
+
const match = value.match(/float3\s*\(\s*([\d.]+)\s*,\s*([\d.]+)\s*,\s*([\d.]+)\s*\)/);
|
|
1036
|
+
if (!match || !match[1] || !match[2] || !match[3]) return null;
|
|
1037
|
+
return [
|
|
1038
|
+
parseFloat(match[1]),
|
|
1039
|
+
parseFloat(match[2]),
|
|
1040
|
+
parseFloat(match[3])
|
|
1041
|
+
];
|
|
1042
|
+
}
|
|
1043
|
+
/**
|
|
1044
|
+
* 解析float值
|
|
1045
|
+
*/
|
|
1046
|
+
parseFloat(value) {
|
|
1047
|
+
if (!value) return null;
|
|
1048
|
+
const num = parseFloat(value);
|
|
1049
|
+
return isNaN(num) ? null : num;
|
|
1050
|
+
}
|
|
1051
|
+
/**
|
|
1052
|
+
* 解析参数值
|
|
1053
|
+
*/
|
|
1054
|
+
parseParameterValue(param) {
|
|
1055
|
+
if (!param.defaultValue) return null;
|
|
1056
|
+
switch (param.type) {
|
|
1057
|
+
case "float":
|
|
1058
|
+
return this.parseFloat(param.defaultValue);
|
|
1059
|
+
case "float2": {
|
|
1060
|
+
const match = param.defaultValue.match(/float2\s*\(\s*([\d.]+)\s*,\s*([\d.]+)\s*\)/);
|
|
1061
|
+
return match && match[1] && match[2] ? new THREE3.Vector2(parseFloat(match[1]), parseFloat(match[2])) : null;
|
|
1062
|
+
}
|
|
1063
|
+
case "float3": {
|
|
1064
|
+
const values = this.parseFloat3(param.defaultValue);
|
|
1065
|
+
return values ? new THREE3.Vector3(values[0], values[1], values[2]) : null;
|
|
1066
|
+
}
|
|
1067
|
+
case "float4": {
|
|
1068
|
+
const match = param.defaultValue.match(/float4\s*\(\s*([\d.]+)\s*,\s*([\d.]+)\s*,\s*([\d.]+)\s*,\s*([\d.]+)\s*\)/);
|
|
1069
|
+
if (match && match[1] && match[2] && match[3] && match[4]) {
|
|
1070
|
+
return new THREE3.Vector4(
|
|
1071
|
+
parseFloat(match[1]),
|
|
1072
|
+
parseFloat(match[2]),
|
|
1073
|
+
parseFloat(match[3]),
|
|
1074
|
+
parseFloat(match[4])
|
|
1075
|
+
);
|
|
1076
|
+
}
|
|
1077
|
+
return null;
|
|
1078
|
+
}
|
|
1079
|
+
default:
|
|
1080
|
+
return param.defaultValue;
|
|
1081
|
+
}
|
|
1082
|
+
}
|
|
1083
|
+
/**
|
|
1084
|
+
* 获取所有自定义uniforms
|
|
1085
|
+
*/
|
|
1086
|
+
getUniforms() {
|
|
1087
|
+
const uniforms = {};
|
|
1088
|
+
this.effect.parameters.forEach((param) => {
|
|
1089
|
+
const value = this.parseParameterValue(param);
|
|
1090
|
+
if (value !== null) {
|
|
1091
|
+
uniforms[param.name] = { value };
|
|
1092
|
+
}
|
|
1093
|
+
});
|
|
1094
|
+
this.loadedTextures.forEach((texture, name) => {
|
|
1095
|
+
uniforms[name] = { value: texture };
|
|
1096
|
+
});
|
|
1097
|
+
return uniforms;
|
|
1098
|
+
}
|
|
1099
|
+
/**
|
|
1100
|
+
* 生成配置摘要
|
|
1101
|
+
*/
|
|
1102
|
+
getSummary() {
|
|
1103
|
+
return {
|
|
1104
|
+
materialParams: this.effect.parameters.map((p) => `${p.type} ${p.name}`),
|
|
1105
|
+
textures: this.effect.textures.map((t) => t.path),
|
|
1106
|
+
renderFeatures: this.effect.defines.filter((d) => !d.isCommented && (d.name.startsWith("USE_") || d.name.includes("SHADOW"))).map((d) => d.name)
|
|
1107
|
+
};
|
|
1108
|
+
}
|
|
1109
|
+
};
|
|
1110
|
+
var MultiFXAdapter = class {
|
|
1111
|
+
constructor(options = {}) {
|
|
1112
|
+
this.effects = /* @__PURE__ */ new Map();
|
|
1113
|
+
this.adapters = /* @__PURE__ */ new Map();
|
|
1114
|
+
this.configs = [];
|
|
1115
|
+
this.options = {
|
|
1116
|
+
mergeStrategy: options.mergeStrategy || "override",
|
|
1117
|
+
autoLoadTextures: options.autoLoadTextures ?? true
|
|
1118
|
+
};
|
|
1119
|
+
this.parser = new FXParser();
|
|
1120
|
+
}
|
|
1121
|
+
/**
|
|
1122
|
+
* 识别文件类型
|
|
1123
|
+
*/
|
|
1124
|
+
detectFileType(path, configType) {
|
|
1125
|
+
if (configType && configType !== "auto") {
|
|
1126
|
+
return configType;
|
|
1127
|
+
}
|
|
1128
|
+
const ext = path.toLowerCase().split(".").pop();
|
|
1129
|
+
if (ext === "x") return "x";
|
|
1130
|
+
if (ext === "fx") return "fx";
|
|
1131
|
+
return "fx";
|
|
1132
|
+
}
|
|
1133
|
+
/**
|
|
1134
|
+
* 添加效果文件(支持.fx和.x)
|
|
1135
|
+
*/
|
|
1136
|
+
async addFX(config) {
|
|
1137
|
+
if (!config.enabled && config.enabled !== void 0) {
|
|
1138
|
+
console.log("[MultiFXAdapter] Effect disabled:", config.path);
|
|
1139
|
+
return;
|
|
1140
|
+
}
|
|
1141
|
+
try {
|
|
1142
|
+
const fileType = this.detectFileType(config.path, config.type);
|
|
1143
|
+
const desc = config.description || config.path;
|
|
1144
|
+
console.log(`[MultiFXAdapter] Loading ${fileType.toUpperCase()} file:`, desc);
|
|
1145
|
+
const effect = await this.parser.loadAndParse(config.path);
|
|
1146
|
+
this.effects.set(config.path, effect);
|
|
1147
|
+
const adapter = new FXToThreeAdapter(effect, config.texturePath || "");
|
|
1148
|
+
this.adapters.set(config.path, adapter);
|
|
1149
|
+
if (this.options.autoLoadTextures) {
|
|
1150
|
+
console.log(`[MultiFXAdapter] Loading textures for ${fileType}:`, desc);
|
|
1151
|
+
await adapter.loadTextures();
|
|
1152
|
+
}
|
|
1153
|
+
const defaultPriority = fileType === "x" ? -10 : 0;
|
|
1154
|
+
const defaultTarget = fileType === "x" ? "all" : "model";
|
|
1155
|
+
this.configs.push({
|
|
1156
|
+
...config,
|
|
1157
|
+
type: fileType,
|
|
1158
|
+
priority: config.priority ?? defaultPriority,
|
|
1159
|
+
target: config.target ?? defaultTarget,
|
|
1160
|
+
enabled: true
|
|
1161
|
+
});
|
|
1162
|
+
this.configs.sort((a, b) => (a.priority ?? 0) - (b.priority ?? 0));
|
|
1163
|
+
const lastConfig = this.configs[this.configs.length - 1];
|
|
1164
|
+
console.log(`[MultiFXAdapter] ${fileType.toUpperCase()} loaded successfully:`, desc);
|
|
1165
|
+
console.log(`[MultiFXAdapter] - Priority: ${lastConfig?.priority ?? 0}`);
|
|
1166
|
+
console.log(`[MultiFXAdapter] - Target: ${lastConfig?.target ?? "all"}`);
|
|
1167
|
+
} catch (error) {
|
|
1168
|
+
console.error("[MultiFXAdapter] Failed to load effect file:", config.path, error);
|
|
1169
|
+
throw error;
|
|
1170
|
+
}
|
|
1171
|
+
}
|
|
1172
|
+
/**
|
|
1173
|
+
* 批量添加FX文件
|
|
1174
|
+
*/
|
|
1175
|
+
async addMultipleFX(configs) {
|
|
1176
|
+
await Promise.all(configs.map((config) => this.addFX(config)));
|
|
1177
|
+
}
|
|
1178
|
+
/**
|
|
1179
|
+
* 移除FX文件
|
|
1180
|
+
*/
|
|
1181
|
+
removeFX(path) {
|
|
1182
|
+
this.effects.delete(path);
|
|
1183
|
+
this.adapters.delete(path);
|
|
1184
|
+
this.configs = this.configs.filter((c) => c.path !== path);
|
|
1185
|
+
}
|
|
1186
|
+
/**
|
|
1187
|
+
* 清空所有FX
|
|
1188
|
+
*/
|
|
1189
|
+
clear() {
|
|
1190
|
+
this.effects.clear();
|
|
1191
|
+
this.adapters.clear();
|
|
1192
|
+
this.configs = [];
|
|
1193
|
+
}
|
|
1194
|
+
/**
|
|
1195
|
+
* 合并材质配置
|
|
1196
|
+
*/
|
|
1197
|
+
extractMergedMaterialConfig(target) {
|
|
1198
|
+
const merged = {
|
|
1199
|
+
uniforms: {}
|
|
1200
|
+
};
|
|
1201
|
+
console.log("[MultiFXAdapter] Extracting material config for target:", target);
|
|
1202
|
+
console.log("[MultiFXAdapter] Total configs:", this.configs.length);
|
|
1203
|
+
this.configs.forEach((config) => {
|
|
1204
|
+
let shouldApply = false;
|
|
1205
|
+
if (!target || config.target === "all") {
|
|
1206
|
+
shouldApply = true;
|
|
1207
|
+
} else if (Array.isArray(config.target)) {
|
|
1208
|
+
shouldApply = config.target.includes(target);
|
|
1209
|
+
} else {
|
|
1210
|
+
shouldApply = config.target === target;
|
|
1211
|
+
}
|
|
1212
|
+
if (!shouldApply) {
|
|
1213
|
+
console.log(`[MultiFXAdapter] - Skipping ${config.description || config.path} (target mismatch: ${config.target} !== ${target})`);
|
|
1214
|
+
return;
|
|
1215
|
+
}
|
|
1216
|
+
const adapter = this.adapters.get(config.path);
|
|
1217
|
+
if (!adapter) {
|
|
1218
|
+
console.log(`[MultiFXAdapter] - Skipping ${config.description || config.path} (adapter not found)`);
|
|
1219
|
+
return;
|
|
1220
|
+
}
|
|
1221
|
+
console.log(`[MultiFXAdapter] \u2705 Applying ${config.description || config.path} (priority: ${config.priority})`);
|
|
1222
|
+
const materialConfig = adapter.extractMaterialConfig();
|
|
1223
|
+
switch (this.options.mergeStrategy) {
|
|
1224
|
+
case "override":
|
|
1225
|
+
if (materialConfig.color) merged.color = materialConfig.color;
|
|
1226
|
+
if (materialConfig.emissive) merged.emissive = materialConfig.emissive;
|
|
1227
|
+
if (materialConfig.specular) merged.specular = materialConfig.specular;
|
|
1228
|
+
if (materialConfig.shininess !== void 0) merged.shininess = materialConfig.shininess;
|
|
1229
|
+
if (materialConfig.opacity !== void 0) merged.opacity = materialConfig.opacity;
|
|
1230
|
+
if (materialConfig.transparent !== void 0) merged.transparent = materialConfig.transparent;
|
|
1231
|
+
if (materialConfig.uniforms && merged.uniforms) {
|
|
1232
|
+
Object.assign(merged.uniforms, materialConfig.uniforms);
|
|
1233
|
+
}
|
|
1234
|
+
break;
|
|
1235
|
+
case "merge":
|
|
1236
|
+
merged.color = materialConfig.color || merged.color;
|
|
1237
|
+
merged.emissive = materialConfig.emissive || merged.emissive;
|
|
1238
|
+
merged.specular = materialConfig.specular || merged.specular;
|
|
1239
|
+
merged.shininess = materialConfig.shininess ?? merged.shininess;
|
|
1240
|
+
merged.opacity = materialConfig.opacity ?? merged.opacity;
|
|
1241
|
+
merged.transparent = materialConfig.transparent ?? merged.transparent;
|
|
1242
|
+
if (materialConfig.uniforms && merged.uniforms) {
|
|
1243
|
+
Object.assign(merged.uniforms, materialConfig.uniforms);
|
|
1244
|
+
}
|
|
1245
|
+
break;
|
|
1246
|
+
case "additive":
|
|
1247
|
+
if (materialConfig.color && merged.color) {
|
|
1248
|
+
merged.color.add(materialConfig.color);
|
|
1249
|
+
} else {
|
|
1250
|
+
merged.color = materialConfig.color;
|
|
1251
|
+
}
|
|
1252
|
+
if (materialConfig.emissive && merged.emissive) {
|
|
1253
|
+
merged.emissive.add(materialConfig.emissive);
|
|
1254
|
+
} else {
|
|
1255
|
+
merged.emissive = materialConfig.emissive;
|
|
1256
|
+
}
|
|
1257
|
+
if (materialConfig.shininess !== void 0) {
|
|
1258
|
+
merged.shininess = (merged.shininess ?? 0) + materialConfig.shininess;
|
|
1259
|
+
}
|
|
1260
|
+
if (materialConfig.uniforms && merged.uniforms) {
|
|
1261
|
+
Object.assign(merged.uniforms, materialConfig.uniforms);
|
|
1262
|
+
}
|
|
1263
|
+
break;
|
|
1264
|
+
}
|
|
1265
|
+
});
|
|
1266
|
+
return merged;
|
|
1267
|
+
}
|
|
1268
|
+
/**
|
|
1269
|
+
* 合并渲染配置
|
|
1270
|
+
*/
|
|
1271
|
+
extractMergedRenderConfig() {
|
|
1272
|
+
const merged = {};
|
|
1273
|
+
console.log("[MultiFXAdapter] Extracting merged render config from", this.configs.length, "configs");
|
|
1274
|
+
this.configs.forEach((config) => {
|
|
1275
|
+
const adapter = this.adapters.get(config.path);
|
|
1276
|
+
if (!adapter) return;
|
|
1277
|
+
const renderConfig = adapter.extractRenderConfig();
|
|
1278
|
+
console.log(`[MultiFXAdapter] Processing ${config.description || config.path}:`);
|
|
1279
|
+
console.log(" - enableShadow:", renderConfig.enableShadow);
|
|
1280
|
+
console.log(" - shadowMapSize:", renderConfig.shadowMapSize);
|
|
1281
|
+
console.log(" - toneMapping:", renderConfig.toneMapping);
|
|
1282
|
+
if (renderConfig.enableShadow !== void 0) {
|
|
1283
|
+
merged.enableShadow = renderConfig.enableShadow;
|
|
1284
|
+
}
|
|
1285
|
+
if (renderConfig.shadowMapSize !== void 0) {
|
|
1286
|
+
merged.shadowMapSize = renderConfig.shadowMapSize;
|
|
1287
|
+
}
|
|
1288
|
+
if (renderConfig.ambientLightIntensity !== void 0) {
|
|
1289
|
+
merged.ambientLightIntensity = renderConfig.ambientLightIntensity;
|
|
1290
|
+
}
|
|
1291
|
+
if (renderConfig.directionalLightIntensity !== void 0) {
|
|
1292
|
+
merged.directionalLightIntensity = renderConfig.directionalLightIntensity;
|
|
1293
|
+
}
|
|
1294
|
+
if (renderConfig.lightDirection) {
|
|
1295
|
+
merged.lightDirection = renderConfig.lightDirection;
|
|
1296
|
+
}
|
|
1297
|
+
if (renderConfig.toneMapping !== void 0) {
|
|
1298
|
+
merged.toneMapping = renderConfig.toneMapping;
|
|
1299
|
+
console.log(`[MultiFXAdapter] \u2705 ToneMapping set to:`, renderConfig.toneMapping === THREE3.NoToneMapping ? "NoToneMapping (Toon)" : "ACESFilmicToneMapping");
|
|
1300
|
+
}
|
|
1301
|
+
if (renderConfig.toneMappingExposure !== void 0) {
|
|
1302
|
+
merged.toneMappingExposure = renderConfig.toneMappingExposure;
|
|
1303
|
+
}
|
|
1304
|
+
});
|
|
1305
|
+
console.log("[MultiFXAdapter] Final merged render config:", merged);
|
|
1306
|
+
return merged;
|
|
1307
|
+
}
|
|
1308
|
+
/**
|
|
1309
|
+
* 应用到Three.js材质
|
|
1310
|
+
* 支持MeshPhongMaterial和MeshToonMaterial(MMD常用)
|
|
1311
|
+
*/
|
|
1312
|
+
applyToMaterial(material, target) {
|
|
1313
|
+
if (!(material instanceof THREE3.MeshPhongMaterial || material instanceof THREE3.MeshToonMaterial)) {
|
|
1314
|
+
console.warn("[MultiFXAdapter] Material type not supported:", material.type);
|
|
1315
|
+
return;
|
|
1316
|
+
}
|
|
1317
|
+
console.log("[MultiFXAdapter] Applying to material type:", material.type);
|
|
1318
|
+
const config = this.extractMergedMaterialConfig(target);
|
|
1319
|
+
console.log("[MultiFXAdapter] Extracted material config for target:", target);
|
|
1320
|
+
console.log(" - color:", config.color);
|
|
1321
|
+
console.log(" - emissive:", config.emissive);
|
|
1322
|
+
console.log(" - specular:", config.specular);
|
|
1323
|
+
console.log(" - shininess:", config.shininess);
|
|
1324
|
+
let applied = false;
|
|
1325
|
+
if (config.color) {
|
|
1326
|
+
const isBlack = config.color.r === 0 && config.color.g === 0 && config.color.b === 0;
|
|
1327
|
+
if (!isBlack) {
|
|
1328
|
+
material.color.copy(config.color);
|
|
1329
|
+
applied = true;
|
|
1330
|
+
} else {
|
|
1331
|
+
console.log("[MultiFXAdapter] Skipping black color (0,0,0) to preserve original material");
|
|
1332
|
+
}
|
|
1333
|
+
}
|
|
1334
|
+
if (config.emissive && material.emissive) {
|
|
1335
|
+
const isBlack = config.emissive.r === 0 && config.emissive.g === 0 && config.emissive.b === 0;
|
|
1336
|
+
if (!isBlack) {
|
|
1337
|
+
material.emissive.copy(config.emissive);
|
|
1338
|
+
applied = true;
|
|
1339
|
+
}
|
|
1340
|
+
}
|
|
1341
|
+
if (config.specular && material.specular) {
|
|
1342
|
+
material.specular.copy(config.specular);
|
|
1343
|
+
applied = true;
|
|
1344
|
+
console.log("[MultiFXAdapter] Applied specular");
|
|
1345
|
+
}
|
|
1346
|
+
if (config.shininess !== void 0 && material.shininess !== void 0) {
|
|
1347
|
+
material.shininess = config.shininess;
|
|
1348
|
+
applied = true;
|
|
1349
|
+
console.log("[MultiFXAdapter] Applied shininess:", config.shininess);
|
|
1350
|
+
}
|
|
1351
|
+
if (config.opacity !== void 0) {
|
|
1352
|
+
material.opacity = config.opacity;
|
|
1353
|
+
applied = true;
|
|
1354
|
+
}
|
|
1355
|
+
if (config.transparent !== void 0) {
|
|
1356
|
+
material.transparent = config.transparent;
|
|
1357
|
+
applied = true;
|
|
1358
|
+
}
|
|
1359
|
+
if (applied) {
|
|
1360
|
+
console.log("[MultiFXAdapter] \u2705 Material config applied to:", target || "default");
|
|
1361
|
+
} else {
|
|
1362
|
+
console.warn("[MultiFXAdapter] \u26A0\uFE0F No material config to apply (all values are undefined)");
|
|
1363
|
+
}
|
|
1364
|
+
}
|
|
1365
|
+
/**
|
|
1366
|
+
* 创建ShaderMaterial(如果配置了useShaderMaterial)
|
|
1367
|
+
* 返回第一个匹配target的ShaderMaterial,如果没有则返回null
|
|
1368
|
+
*/
|
|
1369
|
+
createShaderMaterial(target) {
|
|
1370
|
+
const shaderConfig = this.configs.find((config) => {
|
|
1371
|
+
if (!config.useShaderMaterial) return false;
|
|
1372
|
+
if (!target || config.target === "all") return true;
|
|
1373
|
+
if (Array.isArray(config.target)) return config.target.includes(target);
|
|
1374
|
+
return config.target === target;
|
|
1375
|
+
});
|
|
1376
|
+
if (!shaderConfig) {
|
|
1377
|
+
console.log(`[MultiFXAdapter] No shader material config found for target: ${target}`);
|
|
1378
|
+
return null;
|
|
1379
|
+
}
|
|
1380
|
+
const adapter = this.adapters.get(shaderConfig.path);
|
|
1381
|
+
if (!adapter) {
|
|
1382
|
+
console.warn(`[MultiFXAdapter] Adapter not found for: ${shaderConfig.path}`);
|
|
1383
|
+
return null;
|
|
1384
|
+
}
|
|
1385
|
+
const material = adapter.createShaderMaterial();
|
|
1386
|
+
if (material) {
|
|
1387
|
+
console.log(`[MultiFXAdapter] \u2705 Created ShaderMaterial for target: ${target} from ${shaderConfig.description || shaderConfig.path}`);
|
|
1388
|
+
}
|
|
1389
|
+
return material;
|
|
1390
|
+
}
|
|
1391
|
+
/**
|
|
1392
|
+
* 应用到Three.js场景
|
|
1393
|
+
*/
|
|
1394
|
+
applyToScene(scene, renderer) {
|
|
1395
|
+
const renderConfig = this.extractMergedRenderConfig();
|
|
1396
|
+
console.log("[MultiFXAdapter] Applying render config:");
|
|
1397
|
+
console.log(" - enableShadow:", renderConfig.enableShadow);
|
|
1398
|
+
console.log(" - shadowMapSize:", renderConfig.shadowMapSize);
|
|
1399
|
+
console.log(" - toneMapping:", renderConfig.toneMapping);
|
|
1400
|
+
console.log(" - ambientLightIntensity:", renderConfig.ambientLightIntensity);
|
|
1401
|
+
console.log(" - directionalLightIntensity:", renderConfig.directionalLightIntensity);
|
|
1402
|
+
if (renderConfig.enableShadow !== void 0) {
|
|
1403
|
+
renderer.shadowMap.enabled = renderConfig.enableShadow;
|
|
1404
|
+
if (renderConfig.enableShadow) {
|
|
1405
|
+
renderer.shadowMap.type = THREE3.PCFSoftShadowMap;
|
|
1406
|
+
}
|
|
1407
|
+
console.log("[MultiFXAdapter] \u2705 Shadow config applied:", renderConfig.enableShadow);
|
|
1408
|
+
}
|
|
1409
|
+
if (renderConfig.toneMapping !== void 0) {
|
|
1410
|
+
renderer.toneMapping = renderConfig.toneMapping;
|
|
1411
|
+
const toneMappingName = renderConfig.toneMapping === THREE3.NoToneMapping ? "NoToneMapping" : "ACESFilmicToneMapping";
|
|
1412
|
+
console.log("[MultiFXAdapter] \u2705 ToneMapping applied:", toneMappingName);
|
|
1413
|
+
}
|
|
1414
|
+
if (renderConfig.toneMappingExposure !== void 0) {
|
|
1415
|
+
renderer.toneMappingExposure = renderConfig.toneMappingExposure;
|
|
1416
|
+
}
|
|
1417
|
+
let ambientLight = scene.children.find((obj) => obj instanceof THREE3.AmbientLight);
|
|
1418
|
+
let directionalLight = scene.children.find((obj) => obj instanceof THREE3.DirectionalLight);
|
|
1419
|
+
console.log("[MultiFXAdapter] Found lights in scene:", {
|
|
1420
|
+
ambientLight: !!ambientLight,
|
|
1421
|
+
directionalLight: !!directionalLight
|
|
1422
|
+
});
|
|
1423
|
+
if (renderConfig.ambientLightIntensity !== void 0 && ambientLight) {
|
|
1424
|
+
console.log("[MultiFXAdapter] Updating ambient light intensity:", renderConfig.ambientLightIntensity);
|
|
1425
|
+
ambientLight.intensity = renderConfig.ambientLightIntensity;
|
|
1426
|
+
}
|
|
1427
|
+
if (renderConfig.directionalLightIntensity !== void 0 && directionalLight) {
|
|
1428
|
+
console.log("[MultiFXAdapter] Updating directional light intensity:", renderConfig.directionalLightIntensity);
|
|
1429
|
+
directionalLight.intensity = renderConfig.directionalLightIntensity;
|
|
1430
|
+
}
|
|
1431
|
+
if (renderConfig.lightDirection && directionalLight) {
|
|
1432
|
+
directionalLight.position.copy(renderConfig.lightDirection).multiplyScalar(10);
|
|
1433
|
+
console.log("[MultiFXAdapter] Updated light direction");
|
|
1434
|
+
}
|
|
1435
|
+
if (renderConfig.shadowMapSize && directionalLight?.shadow) {
|
|
1436
|
+
directionalLight.shadow.mapSize.width = renderConfig.shadowMapSize;
|
|
1437
|
+
directionalLight.shadow.mapSize.height = renderConfig.shadowMapSize;
|
|
1438
|
+
console.log("[MultiFXAdapter] Updated shadow map size:", renderConfig.shadowMapSize);
|
|
1439
|
+
}
|
|
1440
|
+
console.log("[MultiFXAdapter] \u2705 Scene config applied");
|
|
1441
|
+
}
|
|
1442
|
+
/**
|
|
1443
|
+
* 获取所有已加载的FX效果
|
|
1444
|
+
*/
|
|
1445
|
+
getLoadedEffects() {
|
|
1446
|
+
return Array.from(this.effects.values());
|
|
1447
|
+
}
|
|
1448
|
+
/**
|
|
1449
|
+
* 获取配置摘要
|
|
1450
|
+
*/
|
|
1451
|
+
getSummary() {
|
|
1452
|
+
const xCount = this.configs.filter((c) => c.type === "x").length;
|
|
1453
|
+
const fxCount = this.configs.filter((c) => c.type === "fx").length;
|
|
1454
|
+
return {
|
|
1455
|
+
totalFX: this.effects.size,
|
|
1456
|
+
enabledFX: this.configs.length,
|
|
1457
|
+
xFiles: xCount,
|
|
1458
|
+
fxFiles: fxCount,
|
|
1459
|
+
configs: this.configs.map((config) => {
|
|
1460
|
+
const adapter = this.adapters.get(config.path);
|
|
1461
|
+
const summary = adapter?.getSummary();
|
|
1462
|
+
return {
|
|
1463
|
+
path: config.path,
|
|
1464
|
+
type: config.type ?? "fx",
|
|
1465
|
+
priority: config.priority ?? 0,
|
|
1466
|
+
target: config.target ?? "all",
|
|
1467
|
+
features: summary?.renderFeatures || [],
|
|
1468
|
+
description: config.description
|
|
1469
|
+
};
|
|
1470
|
+
})
|
|
1471
|
+
};
|
|
1472
|
+
}
|
|
1473
|
+
/**
|
|
1474
|
+
* 获取指定类型的配置
|
|
1475
|
+
*/
|
|
1476
|
+
getConfigsByType(type) {
|
|
1477
|
+
return this.configs.filter((c) => c.type === type);
|
|
1478
|
+
}
|
|
1479
|
+
/**
|
|
1480
|
+
* 获取场景级效果(.x文件)
|
|
1481
|
+
*/
|
|
1482
|
+
getSceneEffects() {
|
|
1483
|
+
return this.getConfigsByType("x");
|
|
1484
|
+
}
|
|
1485
|
+
/**
|
|
1486
|
+
* 获取模型级效果(.fx文件)
|
|
1487
|
+
*/
|
|
1488
|
+
getModelEffects() {
|
|
1489
|
+
return this.getConfigsByType("fx");
|
|
1490
|
+
}
|
|
1491
|
+
/**
|
|
1492
|
+
* 获取合并后的Uniforms
|
|
1493
|
+
*/
|
|
1494
|
+
getMergedUniforms(target) {
|
|
1495
|
+
const merged = {};
|
|
1496
|
+
this.configs.forEach((config) => {
|
|
1497
|
+
if (target && config.target !== "all") {
|
|
1498
|
+
if (Array.isArray(config.target)) {
|
|
1499
|
+
if (!config.target.includes(target)) return;
|
|
1500
|
+
} else if (config.target !== target) {
|
|
1501
|
+
return;
|
|
1502
|
+
}
|
|
1503
|
+
}
|
|
1504
|
+
const adapter = this.adapters.get(config.path);
|
|
1505
|
+
if (!adapter) return;
|
|
1506
|
+
const uniforms = adapter.getUniforms();
|
|
1507
|
+
Object.assign(merged, uniforms);
|
|
1508
|
+
});
|
|
1509
|
+
return merged;
|
|
1510
|
+
}
|
|
1511
|
+
/**
|
|
1512
|
+
* 分层应用到场景对象
|
|
1513
|
+
* @param scene Three.js场景
|
|
1514
|
+
* @param renderer Three.js渲染器
|
|
1515
|
+
* @param modelMeshes 模型网格数组(可选,用于精确控制)
|
|
1516
|
+
* @param stageMeshes 舞台网格数组(可选,用于精确控制)
|
|
1517
|
+
*/
|
|
1518
|
+
applyLayered(scene, renderer, modelMeshes, stageMeshes) {
|
|
1519
|
+
console.log("[MultiFXAdapter] Applying layered effects...");
|
|
1520
|
+
const sceneEffects = this.getSceneEffects();
|
|
1521
|
+
console.log(`[MultiFXAdapter] Applying ${sceneEffects.length} scene-level effects (.x files)`);
|
|
1522
|
+
sceneEffects.forEach((config) => {
|
|
1523
|
+
const adapter = this.adapters.get(config.path);
|
|
1524
|
+
if (adapter) {
|
|
1525
|
+
console.log(`[MultiFXAdapter] - Applying: ${config.description || config.path}`);
|
|
1526
|
+
const renderConfig = adapter.extractRenderConfig();
|
|
1527
|
+
if (renderConfig.toneMapping !== void 0) {
|
|
1528
|
+
renderer.toneMapping = renderConfig.toneMapping;
|
|
1529
|
+
}
|
|
1530
|
+
if (renderConfig.toneMappingExposure !== void 0) {
|
|
1531
|
+
renderer.toneMappingExposure = renderConfig.toneMappingExposure;
|
|
1532
|
+
}
|
|
1533
|
+
if (renderConfig.enableShadow !== void 0) {
|
|
1534
|
+
renderer.shadowMap.enabled = renderConfig.enableShadow;
|
|
1535
|
+
}
|
|
1536
|
+
scene.traverse((obj) => {
|
|
1537
|
+
if (obj instanceof THREE3.Mesh && obj.material instanceof THREE3.MeshPhongMaterial) {
|
|
1538
|
+
this.applyToMaterial(obj.material, "scene");
|
|
1539
|
+
}
|
|
1540
|
+
});
|
|
1541
|
+
}
|
|
1542
|
+
});
|
|
1543
|
+
const modelEffects = this.getModelEffects();
|
|
1544
|
+
console.log(`[MultiFXAdapter] Applying ${modelEffects.length} model-level effects (.fx files)`);
|
|
1545
|
+
modelEffects.forEach((config) => {
|
|
1546
|
+
console.log(`[MultiFXAdapter] - Applying: ${config.description || config.path}`);
|
|
1547
|
+
if (modelMeshes) {
|
|
1548
|
+
modelMeshes.forEach((mesh) => {
|
|
1549
|
+
if (mesh instanceof THREE3.Mesh && mesh.material instanceof THREE3.MeshPhongMaterial) {
|
|
1550
|
+
this.applyToMaterial(mesh.material, "model");
|
|
1551
|
+
}
|
|
1552
|
+
mesh.traverse((obj) => {
|
|
1553
|
+
if (obj instanceof THREE3.Mesh && obj.material instanceof THREE3.MeshPhongMaterial) {
|
|
1554
|
+
this.applyToMaterial(obj.material, "model");
|
|
1555
|
+
}
|
|
1556
|
+
});
|
|
1557
|
+
});
|
|
1558
|
+
} else {
|
|
1559
|
+
const targetStr = typeof config.target === "string" ? config.target : "model";
|
|
1560
|
+
scene.traverse((obj) => {
|
|
1561
|
+
if (obj instanceof THREE3.Mesh && obj.material instanceof THREE3.MeshPhongMaterial) {
|
|
1562
|
+
const isModel = obj.isSkinnedMesh || obj.skeleton;
|
|
1563
|
+
if (targetStr === "model" && isModel || targetStr === "all") {
|
|
1564
|
+
this.applyToMaterial(obj.material, targetStr);
|
|
1565
|
+
}
|
|
1566
|
+
}
|
|
1567
|
+
});
|
|
1568
|
+
}
|
|
1569
|
+
});
|
|
1570
|
+
console.log("[MultiFXAdapter] Layered effects applied successfully");
|
|
1571
|
+
}
|
|
1572
|
+
/**
|
|
1573
|
+
* 打印当前配置(调试用)
|
|
1574
|
+
*/
|
|
1575
|
+
printConfig() {
|
|
1576
|
+
console.log("\n[MultiFXAdapter] Current Configuration:");
|
|
1577
|
+
console.log("\u2550".repeat(60));
|
|
1578
|
+
const summary = this.getSummary();
|
|
1579
|
+
console.log(`Total Effects: ${summary.totalFX}`);
|
|
1580
|
+
console.log(` - Scene-level (.x): ${summary.xFiles}`);
|
|
1581
|
+
console.log(` - Model-level (.fx): ${summary.fxFiles}`);
|
|
1582
|
+
console.log("\nLoad Order (by priority):");
|
|
1583
|
+
summary.configs.forEach((config, index) => {
|
|
1584
|
+
const icon = config.type === "x" ? "\u{1F30D}" : "\u{1F3A8}";
|
|
1585
|
+
console.log(`${index + 1}. ${icon} [${config.type.toUpperCase()}] ${config.description || config.path}`);
|
|
1586
|
+
console.log(` Priority: ${config.priority}, Target: ${config.target}`);
|
|
1587
|
+
if (config.features.length > 0) {
|
|
1588
|
+
console.log(` Features: ${config.features.join(", ")}`);
|
|
1589
|
+
}
|
|
1590
|
+
});
|
|
1591
|
+
console.log("\u2550".repeat(60) + "\n");
|
|
1592
|
+
}
|
|
1593
|
+
};
|
|
1594
|
+
|
|
51
1595
|
// src/mmd/components/MMDPlayerBase.tsx
|
|
52
1596
|
if (typeof window !== "undefined") {
|
|
53
|
-
|
|
1597
|
+
THREE3.Cache.enabled = true;
|
|
54
1598
|
}
|
|
55
1599
|
async function waitForMaterialsReady(object, renderer, scene, camera) {
|
|
56
1600
|
const textures = [];
|
|
57
|
-
let meshCount = 0;
|
|
58
1601
|
object.traverse((obj) => {
|
|
59
|
-
if (obj instanceof
|
|
60
|
-
meshCount++;
|
|
1602
|
+
if (obj instanceof THREE3.Mesh || obj instanceof THREE3.SkinnedMesh) {
|
|
61
1603
|
const materials = Array.isArray(obj.material) ? obj.material : [obj.material];
|
|
62
1604
|
materials.forEach((material) => {
|
|
63
|
-
if (material instanceof
|
|
1605
|
+
if (material instanceof THREE3.Material) {
|
|
64
1606
|
const textureProps = [
|
|
65
1607
|
"map",
|
|
66
1608
|
"lightMap",
|
|
@@ -82,7 +1624,7 @@ async function waitForMaterialsReady(object, renderer, scene, camera) {
|
|
|
82
1624
|
];
|
|
83
1625
|
textureProps.forEach((prop) => {
|
|
84
1626
|
const texture = material[prop];
|
|
85
|
-
if (texture instanceof
|
|
1627
|
+
if (texture instanceof THREE3.Texture && !textures.includes(texture)) {
|
|
86
1628
|
textures.push(texture);
|
|
87
1629
|
}
|
|
88
1630
|
});
|
|
@@ -90,22 +1632,18 @@ async function waitForMaterialsReady(object, renderer, scene, camera) {
|
|
|
90
1632
|
});
|
|
91
1633
|
}
|
|
92
1634
|
});
|
|
93
|
-
console.log(`[MMDPlayerBase] Found ${meshCount} meshes and ${textures.length} unique textures`);
|
|
94
1635
|
const texturePromises = textures.map((texture, index) => {
|
|
95
1636
|
return new Promise((resolve) => {
|
|
96
1637
|
const image = texture.image;
|
|
97
1638
|
if (!image) {
|
|
98
|
-
console.log(`[MMDPlayerBase] Texture ${index + 1}/${textures.length}: No image`);
|
|
99
1639
|
resolve();
|
|
100
1640
|
return;
|
|
101
1641
|
}
|
|
102
1642
|
if (image instanceof HTMLImageElement) {
|
|
103
1643
|
if (image.complete && image.naturalWidth > 0) {
|
|
104
|
-
console.log(`[MMDPlayerBase] Texture ${index + 1}/${textures.length}: Already loaded`);
|
|
105
1644
|
resolve();
|
|
106
1645
|
} else {
|
|
107
1646
|
const onLoad = () => {
|
|
108
|
-
console.log(`[MMDPlayerBase] Texture ${index + 1}/${textures.length}: Loaded`);
|
|
109
1647
|
image.removeEventListener("load", onLoad);
|
|
110
1648
|
image.removeEventListener("error", onError);
|
|
111
1649
|
resolve();
|
|
@@ -126,17 +1664,14 @@ async function waitForMaterialsReady(object, renderer, scene, camera) {
|
|
|
126
1664
|
}, 5e3);
|
|
127
1665
|
}
|
|
128
1666
|
} else {
|
|
129
|
-
console.log(`[MMDPlayerBase] Texture ${index + 1}/${textures.length}: Non-image type`);
|
|
130
1667
|
resolve();
|
|
131
1668
|
}
|
|
132
1669
|
});
|
|
133
1670
|
});
|
|
134
1671
|
await Promise.all(texturePromises);
|
|
135
|
-
console.log("[MMDPlayerBase] All texture images loaded");
|
|
136
1672
|
textures.forEach((texture) => {
|
|
137
1673
|
texture.needsUpdate = true;
|
|
138
1674
|
});
|
|
139
|
-
console.log("[MMDPlayerBase] Warming up renderer...");
|
|
140
1675
|
for (let i = 0; i < 3; i++) {
|
|
141
1676
|
await new Promise((resolve) => {
|
|
142
1677
|
requestAnimationFrame(() => {
|
|
@@ -167,7 +1702,6 @@ async function waitForMaterialsReady(object, renderer, scene, camera) {
|
|
|
167
1702
|
}
|
|
168
1703
|
});
|
|
169
1704
|
renderer.render(scene, camera);
|
|
170
|
-
console.log(`[MMDPlayerBase] Warmup render ${i + 1}/3`);
|
|
171
1705
|
} catch (renderError) {
|
|
172
1706
|
console.warn("[MMDPlayerBase] Warmup render failed (shader error?), skipping...", renderError);
|
|
173
1707
|
}
|
|
@@ -175,7 +1709,6 @@ async function waitForMaterialsReady(object, renderer, scene, camera) {
|
|
|
175
1709
|
});
|
|
176
1710
|
});
|
|
177
1711
|
}
|
|
178
|
-
console.log("[MMDPlayerBase] All materials and textures fully ready");
|
|
179
1712
|
}
|
|
180
1713
|
var MMDPlayerBase = forwardRef((props, ref) => {
|
|
181
1714
|
const {
|
|
@@ -200,20 +1733,23 @@ var MMDPlayerBase = forwardRef((props, ref) => {
|
|
|
200
1733
|
} = props;
|
|
201
1734
|
const renderEffect = props.renderEffect || stage.renderEffect || "default";
|
|
202
1735
|
const outlineOptions = { ...stage.outlineOptions, ...props.outlineOptions };
|
|
203
|
-
const
|
|
204
|
-
const
|
|
1736
|
+
const fxPath = props.fxPath || stage.fxPath;
|
|
1737
|
+
const fxTexturePath = props.fxTexturePath || stage.fxTexturePath;
|
|
1738
|
+
const fxConfigs = props.fxConfigs || stage.fxConfigs;
|
|
205
1739
|
const containerRef = useRef(null);
|
|
206
1740
|
const sceneRef = useRef(null);
|
|
207
1741
|
const cameraRef = useRef(null);
|
|
208
1742
|
const rendererRef = useRef(null);
|
|
209
1743
|
const outlineEffectRef = useRef(null);
|
|
210
|
-
const composerRef = useRef(null);
|
|
211
1744
|
const controlsRef = useRef(null);
|
|
212
1745
|
const helperRef = useRef(null);
|
|
213
1746
|
const axesHelperRef = useRef(null);
|
|
214
|
-
const clockRef = useRef(new
|
|
1747
|
+
const clockRef = useRef(new THREE3.Clock());
|
|
215
1748
|
const animationIdRef = useRef(null);
|
|
216
1749
|
const resizeObserverRef = useRef(null);
|
|
1750
|
+
const fxEffectRef = useRef(null);
|
|
1751
|
+
const fxAdapterRef = useRef(null);
|
|
1752
|
+
const multiFXAdapterRef = useRef(null);
|
|
217
1753
|
const isReadyRef = useRef(false);
|
|
218
1754
|
const isPlayingRef = useRef(false);
|
|
219
1755
|
const initIdRef = useRef(0);
|
|
@@ -222,7 +1758,7 @@ var MMDPlayerBase = forwardRef((props, ref) => {
|
|
|
222
1758
|
const loopRef = useRef(loop);
|
|
223
1759
|
const audioRef = useRef(null);
|
|
224
1760
|
const audioListenerRef = useRef(null);
|
|
225
|
-
const audioLoaderRef = useRef(new
|
|
1761
|
+
const audioLoaderRef = useRef(new THREE3.AudioLoader());
|
|
226
1762
|
const latestCallbacks = useRef({ onPlay, onPause, onEnded, onTimeUpdate });
|
|
227
1763
|
useEffect(() => {
|
|
228
1764
|
latestCallbacks.current = { onPlay, onPause, onEnded, onTimeUpdate };
|
|
@@ -313,23 +1849,15 @@ var MMDPlayerBase = forwardRef((props, ref) => {
|
|
|
313
1849
|
if (modelSwitchCountRef.current === 0) {
|
|
314
1850
|
startTimeRef.current = Date.now();
|
|
315
1851
|
modelSwitchCountRef.current = 1;
|
|
316
|
-
console.log("[MMDPlayerBase] \u{1F550} \u7CFB\u7EDF\u542F\u52A8\u65F6\u95F4:", new Date(startTimeRef.current).toLocaleString());
|
|
317
1852
|
} else {
|
|
318
1853
|
modelSwitchCountRef.current++;
|
|
319
|
-
const runningTime = Date.now() - startTimeRef.current;
|
|
320
|
-
const minutes = Math.floor(runningTime / 6e4);
|
|
321
|
-
const seconds = Math.floor(runningTime % 6e4 / 1e3);
|
|
322
|
-
console.log(`[MMDPlayerBase] \u{1F504} \u6A21\u578B\u5207\u6362 #${modelSwitchCountRef.current} (\u8FD0\u884C\u65F6\u95F4: ${minutes}\u5206${seconds}\u79D2)`);
|
|
323
1854
|
}
|
|
324
1855
|
try {
|
|
325
1856
|
if (stage.enablePhysics !== false && !mobileOptimization.disablePhysics) {
|
|
326
|
-
console.log("[MMDPlayerBase] Loading Ammo.js physics engine...");
|
|
327
1857
|
await loadAmmo(stage.physicsPath);
|
|
328
1858
|
if (checkCancelled()) return;
|
|
329
|
-
console.log("[MMDPlayerBase] Ammo.js loaded successfully");
|
|
330
1859
|
const Ammo = window.Ammo;
|
|
331
1860
|
if (Ammo) {
|
|
332
|
-
console.log("[MMDPlayerBase] Setting up physics component tracking...");
|
|
333
1861
|
const originalBtDefaultCollisionConfiguration = Ammo.btDefaultCollisionConfiguration;
|
|
334
1862
|
const originalBtCollisionDispatcher = Ammo.btCollisionDispatcher;
|
|
335
1863
|
const originalBtDbvtBroadphase = Ammo.btDbvtBroadphase;
|
|
@@ -339,52 +1867,44 @@ var MMDPlayerBase = forwardRef((props, ref) => {
|
|
|
339
1867
|
Ammo.btDefaultCollisionConfiguration = function(...args) {
|
|
340
1868
|
const obj = new originalBtDefaultCollisionConfiguration(...args);
|
|
341
1869
|
componentsRef.configs.push(obj);
|
|
342
|
-
console.log(`[MMDPlayerBase] \u{1F50D} Captured btDefaultCollisionConfiguration #${componentsRef.configs.length}`);
|
|
343
1870
|
return obj;
|
|
344
1871
|
};
|
|
345
1872
|
Ammo.btCollisionDispatcher = function(...args) {
|
|
346
1873
|
const obj = new originalBtCollisionDispatcher(...args);
|
|
347
1874
|
componentsRef.dispatchers.push(obj);
|
|
348
|
-
console.log(`[MMDPlayerBase] \u{1F50D} Captured btCollisionDispatcher #${componentsRef.dispatchers.length}`);
|
|
349
1875
|
return obj;
|
|
350
1876
|
};
|
|
351
1877
|
Ammo.btDbvtBroadphase = function(...args) {
|
|
352
1878
|
const obj = new originalBtDbvtBroadphase(...args);
|
|
353
1879
|
componentsRef.caches.push(obj);
|
|
354
|
-
console.log(`[MMDPlayerBase] \u{1F50D} Captured btDbvtBroadphase #${componentsRef.caches.length}`);
|
|
355
1880
|
return obj;
|
|
356
1881
|
};
|
|
357
1882
|
Ammo.btSequentialImpulseConstraintSolver = function(...args) {
|
|
358
1883
|
const obj = new originalBtSequentialImpulseConstraintSolver(...args);
|
|
359
1884
|
componentsRef.solvers.push(obj);
|
|
360
|
-
console.log(`[MMDPlayerBase] \u{1F50D} Captured btSequentialImpulseConstraintSolver #${componentsRef.solvers.length}`);
|
|
361
1885
|
return obj;
|
|
362
1886
|
};
|
|
363
1887
|
Ammo.btDiscreteDynamicsWorld = function(...args) {
|
|
364
1888
|
const obj = new originalBtDiscreteDynamicsWorld(...args);
|
|
365
1889
|
componentsRef.worlds.push(obj);
|
|
366
|
-
console.log(`[MMDPlayerBase] \u{1F50D} Captured btDiscreteDynamicsWorld #${componentsRef.worlds.length}`);
|
|
367
1890
|
return obj;
|
|
368
1891
|
};
|
|
369
|
-
console.log("[MMDPlayerBase] \u2705 Physics component tracking setup complete");
|
|
370
1892
|
}
|
|
371
|
-
} else {
|
|
372
|
-
console.log("[MMDPlayerBase] Physics disabled");
|
|
373
1893
|
}
|
|
374
1894
|
const container = containerRef.current;
|
|
375
1895
|
const width = container.clientWidth || 300;
|
|
376
1896
|
const height = container.clientHeight || 150;
|
|
377
|
-
const scene = new
|
|
1897
|
+
const scene = new THREE3.Scene();
|
|
378
1898
|
if (stage.backgroundColor && stage.backgroundColor !== "transparent") {
|
|
379
|
-
scene.background = new
|
|
1899
|
+
scene.background = new THREE3.Color(stage.backgroundColor);
|
|
380
1900
|
} else if (stage.backgroundImage) {
|
|
381
|
-
const textureLoader = new
|
|
1901
|
+
const textureLoader = new THREE3.TextureLoader();
|
|
382
1902
|
textureLoader.load(stage.backgroundImage, (texture) => {
|
|
383
1903
|
scene.background = texture;
|
|
384
1904
|
});
|
|
385
1905
|
}
|
|
386
1906
|
sceneRef.current = scene;
|
|
387
|
-
const camera = new
|
|
1907
|
+
const camera = new THREE3.PerspectiveCamera(45, width / height, 0.1, 2e3);
|
|
388
1908
|
if (stage.cameraPosition) {
|
|
389
1909
|
const pos = stage.cameraPosition;
|
|
390
1910
|
camera.position.set(pos.x, pos.y, pos.z);
|
|
@@ -392,10 +1912,10 @@ var MMDPlayerBase = forwardRef((props, ref) => {
|
|
|
392
1912
|
camera.position.set(0, 20, 30);
|
|
393
1913
|
}
|
|
394
1914
|
cameraRef.current = camera;
|
|
395
|
-
const listener = new
|
|
1915
|
+
const listener = new THREE3.AudioListener();
|
|
396
1916
|
camera.add(listener);
|
|
397
1917
|
audioListenerRef.current = listener;
|
|
398
|
-
const renderer = new
|
|
1918
|
+
const renderer = new THREE3.WebGLRenderer({
|
|
399
1919
|
antialias: !mobileOptimization.enabled,
|
|
400
1920
|
alpha: true,
|
|
401
1921
|
preserveDrawingBuffer: true
|
|
@@ -403,12 +1923,7 @@ var MMDPlayerBase = forwardRef((props, ref) => {
|
|
|
403
1923
|
renderer.setSize(width, height);
|
|
404
1924
|
const pixelRatio = mobileOptimization.enabled ? mobileOptimization.pixelRatio || Math.min(window.devicePixelRatio, 2) : window.devicePixelRatio;
|
|
405
1925
|
renderer.setPixelRatio(pixelRatio);
|
|
406
|
-
|
|
407
|
-
if (renderEffect.includes("outline") || toonOptions.enabled) {
|
|
408
|
-
renderer.toneMapping = THREE.NoToneMapping;
|
|
409
|
-
} else {
|
|
410
|
-
renderer.toneMapping = THREE.ACESFilmicToneMapping;
|
|
411
|
-
}
|
|
1926
|
+
renderer.toneMapping = THREE3.ACESFilmicToneMapping;
|
|
412
1927
|
if (checkCancelled()) {
|
|
413
1928
|
renderer.dispose();
|
|
414
1929
|
return;
|
|
@@ -421,31 +1936,20 @@ var MMDPlayerBase = forwardRef((props, ref) => {
|
|
|
421
1936
|
renderer.domElement.style.position = "relative";
|
|
422
1937
|
if (stage.enableShadow !== false && !mobileOptimization.reduceShadowQuality) {
|
|
423
1938
|
renderer.shadowMap.enabled = true;
|
|
424
|
-
renderer.shadowMap.type =
|
|
1939
|
+
renderer.shadowMap.type = THREE3.PCFSoftShadowMap;
|
|
425
1940
|
}
|
|
426
1941
|
container.appendChild(renderer.domElement);
|
|
427
1942
|
rendererRef.current = renderer;
|
|
428
1943
|
const effect = new OutlineEffect(renderer, {
|
|
429
1944
|
defaultThickness: outlineOptions.thickness ?? 3e-3,
|
|
430
|
-
defaultColor: new
|
|
1945
|
+
defaultColor: new THREE3.Color(outlineOptions.color ?? "#000000").toArray(),
|
|
431
1946
|
defaultAlpha: 1,
|
|
432
1947
|
defaultKeepAlive: true
|
|
433
1948
|
});
|
|
434
1949
|
outlineEffectRef.current = effect;
|
|
435
|
-
const
|
|
436
|
-
const renderPass = new RenderPass(scene, camera);
|
|
437
|
-
composer.addPass(renderPass);
|
|
438
|
-
const bloomPass = new UnrealBloomPass(
|
|
439
|
-
new THREE.Vector2(width, height),
|
|
440
|
-
bloomOptions.strength ?? 1,
|
|
441
|
-
bloomOptions.radius ?? 0.4,
|
|
442
|
-
bloomOptions.threshold ?? 0.8
|
|
443
|
-
);
|
|
444
|
-
composer.addPass(bloomPass);
|
|
445
|
-
composerRef.current = composer;
|
|
446
|
-
const ambientLight = new THREE.AmbientLight(16777215, stage.ambientLightIntensity ?? 0.5);
|
|
1950
|
+
const ambientLight = new THREE3.AmbientLight(16777215, stage.ambientLightIntensity ?? 0.5);
|
|
447
1951
|
scene.add(ambientLight);
|
|
448
|
-
const dirLight = new
|
|
1952
|
+
const dirLight = new THREE3.DirectionalLight(16777215, stage.directionalLightIntensity ?? 0.8);
|
|
449
1953
|
dirLight.position.set(0, 10, 0);
|
|
450
1954
|
if (stage.enableShadow !== false) {
|
|
451
1955
|
dirLight.castShadow = true;
|
|
@@ -454,6 +1958,62 @@ var MMDPlayerBase = forwardRef((props, ref) => {
|
|
|
454
1958
|
dirLight.shadow.bias = -1e-4;
|
|
455
1959
|
}
|
|
456
1960
|
scene.add(dirLight);
|
|
1961
|
+
if (fxConfigs && fxConfigs.length > 0) {
|
|
1962
|
+
try {
|
|
1963
|
+
console.log("[MMDPlayerBase] Loading multiple FX files:", fxConfigs.length);
|
|
1964
|
+
const multiFX = new MultiFXAdapter({
|
|
1965
|
+
mergeStrategy: "override",
|
|
1966
|
+
autoLoadTextures: true
|
|
1967
|
+
});
|
|
1968
|
+
await multiFX.addMultipleFX(fxConfigs);
|
|
1969
|
+
multiFXAdapterRef.current = multiFX;
|
|
1970
|
+
multiFX.printConfig();
|
|
1971
|
+
multiFX.applyToScene(scene, renderer);
|
|
1972
|
+
console.log("[MMDPlayerBase] Multiple FX files loaded successfully");
|
|
1973
|
+
} catch (error) {
|
|
1974
|
+
console.error("[MMDPlayerBase] Failed to load multiple FX files:", error);
|
|
1975
|
+
}
|
|
1976
|
+
} else if (fxPath) {
|
|
1977
|
+
try {
|
|
1978
|
+
console.log("[MMDPlayerBase] Loading single FX file:", fxPath);
|
|
1979
|
+
const parser = new FXParser();
|
|
1980
|
+
const fxEffect = await parser.loadAndParse(fxPath);
|
|
1981
|
+
fxEffectRef.current = fxEffect;
|
|
1982
|
+
const fxAdapter = new FXToThreeAdapter(fxEffect, fxTexturePath || "");
|
|
1983
|
+
fxAdapterRef.current = fxAdapter;
|
|
1984
|
+
console.log("[MMDPlayerBase] Loading FX textures...");
|
|
1985
|
+
await fxAdapter.loadTextures();
|
|
1986
|
+
console.log("[MMDPlayerBase] Applying FX render config (renderer only)...");
|
|
1987
|
+
const renderConfig = fxAdapter.extractRenderConfig();
|
|
1988
|
+
if (renderConfig.toneMapping !== void 0) {
|
|
1989
|
+
renderer.toneMapping = renderConfig.toneMapping;
|
|
1990
|
+
console.log("[MMDPlayerBase] Applied toneMapping from FX:", renderConfig.toneMapping === THREE3.NoToneMapping ? "NoToneMapping" : "ACESFilmicToneMapping");
|
|
1991
|
+
}
|
|
1992
|
+
if (renderConfig.toneMappingExposure !== void 0) {
|
|
1993
|
+
renderer.toneMappingExposure = renderConfig.toneMappingExposure;
|
|
1994
|
+
}
|
|
1995
|
+
if (renderConfig.enableShadow !== void 0) {
|
|
1996
|
+
renderer.shadowMap.enabled = renderConfig.enableShadow;
|
|
1997
|
+
if (renderConfig.enableShadow) {
|
|
1998
|
+
renderer.shadowMap.type = THREE3.PCFSoftShadowMap;
|
|
1999
|
+
}
|
|
2000
|
+
}
|
|
2001
|
+
if (renderConfig.ambientLightIntensity !== void 0) {
|
|
2002
|
+
ambientLight.intensity = renderConfig.ambientLightIntensity;
|
|
2003
|
+
console.log("[MMDPlayerBase] Updated ambient light from FX:", renderConfig.ambientLightIntensity);
|
|
2004
|
+
}
|
|
2005
|
+
if (renderConfig.directionalLightIntensity !== void 0) {
|
|
2006
|
+
dirLight.intensity = renderConfig.directionalLightIntensity;
|
|
2007
|
+
console.log("[MMDPlayerBase] Updated directional light from FX:", renderConfig.directionalLightIntensity);
|
|
2008
|
+
}
|
|
2009
|
+
if (renderConfig.lightDirection) {
|
|
2010
|
+
dirLight.position.copy(renderConfig.lightDirection).multiplyScalar(10);
|
|
2011
|
+
}
|
|
2012
|
+
console.log("[MMDPlayerBase] FX file loaded successfully");
|
|
2013
|
+
} catch (error) {
|
|
2014
|
+
console.error("[MMDPlayerBase] Failed to load FX file:", error);
|
|
2015
|
+
}
|
|
2016
|
+
}
|
|
457
2017
|
const controls = new OrbitControls(camera, renderer.domElement);
|
|
458
2018
|
controls.minDistance = 10;
|
|
459
2019
|
controls.maxDistance = 100;
|
|
@@ -469,7 +2029,7 @@ var MMDPlayerBase = forwardRef((props, ref) => {
|
|
|
469
2029
|
});
|
|
470
2030
|
controlsRef.current = controls;
|
|
471
2031
|
if (showAxes) {
|
|
472
|
-
const axesHelper = new
|
|
2032
|
+
const axesHelper = new THREE3.AxesHelper(20);
|
|
473
2033
|
scene.add(axesHelper);
|
|
474
2034
|
axesHelperRef.current = axesHelper;
|
|
475
2035
|
}
|
|
@@ -481,21 +2041,12 @@ var MMDPlayerBase = forwardRef((props, ref) => {
|
|
|
481
2041
|
cameraRef.current.aspect = w / h;
|
|
482
2042
|
cameraRef.current.updateProjectionMatrix();
|
|
483
2043
|
rendererRef.current.setSize(w, h);
|
|
484
|
-
if (composerRef.current) {
|
|
485
|
-
composerRef.current.setSize(w, h);
|
|
486
|
-
}
|
|
487
2044
|
};
|
|
488
2045
|
const resizeObserver = new ResizeObserver(onResize);
|
|
489
2046
|
resizeObserver.observe(container);
|
|
490
2047
|
resizeObserverRef.current = resizeObserver;
|
|
491
2048
|
onResize();
|
|
492
|
-
console.log("[MMDPlayerBase] Starting render loop (animation paused)");
|
|
493
2049
|
animate();
|
|
494
|
-
console.log("[MMDPlayerBase] Start loading resources...", {
|
|
495
|
-
model: resources.modelPath,
|
|
496
|
-
stage: resources.stageModelPath,
|
|
497
|
-
motion: resources.motionPath
|
|
498
|
-
});
|
|
499
2050
|
const loader = new MMDLoader();
|
|
500
2051
|
const helper = new MMDAnimationHelper({
|
|
501
2052
|
afterglow: 2
|
|
@@ -503,7 +2054,6 @@ var MMDPlayerBase = forwardRef((props, ref) => {
|
|
|
503
2054
|
helperRef.current = helper;
|
|
504
2055
|
const loadModelPromise = new Promise((resolve, reject) => {
|
|
505
2056
|
if (resources.motionPath) {
|
|
506
|
-
console.log("[MMDPlayerBase] Loading model with motion:", resources.motionPath);
|
|
507
2057
|
loader.loadWithAnimation(
|
|
508
2058
|
resources.modelPath,
|
|
509
2059
|
resources.motionPath,
|
|
@@ -519,7 +2069,6 @@ var MMDPlayerBase = forwardRef((props, ref) => {
|
|
|
519
2069
|
(err) => reject(err)
|
|
520
2070
|
);
|
|
521
2071
|
} else {
|
|
522
|
-
console.log("[MMDPlayerBase] Loading model only");
|
|
523
2072
|
loader.load(
|
|
524
2073
|
resources.modelPath,
|
|
525
2074
|
(mesh2) => {
|
|
@@ -537,26 +2086,21 @@ var MMDPlayerBase = forwardRef((props, ref) => {
|
|
|
537
2086
|
});
|
|
538
2087
|
const { mesh, animation } = await loadModelPromise;
|
|
539
2088
|
if (checkCancelled()) return;
|
|
540
|
-
console.log("[MMDPlayerBase] Model loaded:", mesh);
|
|
541
2089
|
if (animation) {
|
|
542
2090
|
animationClipRef.current = animation;
|
|
543
2091
|
durationRef.current = animation.duration;
|
|
544
|
-
console.log("[MMDPlayerBase] Animation duration:", animation.duration);
|
|
545
2092
|
}
|
|
546
2093
|
mesh.castShadow = true;
|
|
547
2094
|
mesh.receiveShadow = true;
|
|
548
|
-
|
|
549
|
-
const tempScene = new THREE.Scene();
|
|
2095
|
+
const tempScene = new THREE3.Scene();
|
|
550
2096
|
tempScene.add(mesh);
|
|
551
2097
|
await waitForMaterialsReady(mesh, renderer, tempScene, camera);
|
|
552
2098
|
if (checkCancelled()) return;
|
|
553
|
-
console.log("[MMDPlayerBase] \u2705 All materials and textures loaded");
|
|
554
2099
|
tempScene.remove(mesh);
|
|
555
|
-
const box = new
|
|
2100
|
+
const box = new THREE3.Box3().setFromObject(mesh);
|
|
556
2101
|
if (!box.isEmpty()) {
|
|
557
|
-
const center = box.getCenter(new
|
|
558
|
-
const size = box.getSize(new
|
|
559
|
-
console.log("[MMDPlayerBase] Model bounds:", { center, size });
|
|
2102
|
+
const center = box.getCenter(new THREE3.Vector3());
|
|
2103
|
+
const size = box.getSize(new THREE3.Vector3());
|
|
560
2104
|
if (!stage.cameraTarget) {
|
|
561
2105
|
controls.target.set(center.x, center.y + size.y * 0.35, center.z);
|
|
562
2106
|
if (!stage.cameraPosition) {
|
|
@@ -570,21 +2114,23 @@ var MMDPlayerBase = forwardRef((props, ref) => {
|
|
|
570
2114
|
center.z + dist
|
|
571
2115
|
// Z: 在模型正前方(+Z 方向)
|
|
572
2116
|
);
|
|
573
|
-
console.log("[MMDPlayerBase] Auto camera position:", camera.position);
|
|
574
2117
|
}
|
|
575
2118
|
controls.update();
|
|
576
2119
|
}
|
|
577
2120
|
}
|
|
578
2121
|
const enablePhysics = stage.enablePhysics !== false && !mobileOptimization.disablePhysics;
|
|
2122
|
+
console.log("[MMDPlayerBase] \u{1F3A8} Traversing model mesh to apply FX and outline, multiFX:", !!multiFXAdapterRef.current, "singleFX:", !!fxAdapterRef.current);
|
|
2123
|
+
let materialCount = 0;
|
|
579
2124
|
mesh.traverse((obj) => {
|
|
580
|
-
if (obj instanceof
|
|
2125
|
+
if (obj instanceof THREE3.Mesh || obj instanceof THREE3.SkinnedMesh) {
|
|
581
2126
|
const materials = Array.isArray(obj.material) ? obj.material : [obj.material];
|
|
2127
|
+
materialCount += materials.length;
|
|
582
2128
|
materials.forEach((m) => {
|
|
583
2129
|
if (!m.userData) m.userData = {};
|
|
584
2130
|
if (!m.userData.outlineParameters) {
|
|
585
2131
|
m.userData.outlineParameters = {
|
|
586
2132
|
thickness: outlineOptions.thickness ?? 3e-3,
|
|
587
|
-
color: new
|
|
2133
|
+
color: new THREE3.Color(outlineOptions.color ?? "#000000").toArray(),
|
|
588
2134
|
alpha: 1,
|
|
589
2135
|
visible: true,
|
|
590
2136
|
keepAlive: true
|
|
@@ -594,46 +2140,74 @@ var MMDPlayerBase = forwardRef((props, ref) => {
|
|
|
594
2140
|
m.userData.outlineParameters.thickness = outlineOptions.thickness;
|
|
595
2141
|
}
|
|
596
2142
|
if (outlineOptions.color !== void 0) {
|
|
597
|
-
m.userData.outlineParameters.color = new
|
|
2143
|
+
m.userData.outlineParameters.color = new THREE3.Color(outlineOptions.color).toArray();
|
|
598
2144
|
}
|
|
599
2145
|
}
|
|
600
|
-
if (m instanceof
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
2146
|
+
if (m instanceof THREE3.MeshPhongMaterial || m instanceof THREE3.MeshToonMaterial) {
|
|
2147
|
+
console.log("[MMDPlayerBase] \u{1F3A8} Applying FX to model material (type:", m.type, "), multiFX:", !!multiFXAdapterRef.current, "singleFX:", !!fxAdapterRef.current);
|
|
2148
|
+
if (multiFXAdapterRef.current) {
|
|
2149
|
+
console.log("[MMDPlayerBase] Using MultiFXAdapter");
|
|
2150
|
+
multiFXAdapterRef.current.applyToMaterial(m, "model");
|
|
2151
|
+
} else if (fxAdapterRef.current) {
|
|
2152
|
+
console.log("[MMDPlayerBase] Using single FXAdapter");
|
|
2153
|
+
const materialConfig = fxAdapterRef.current.extractMaterialConfig();
|
|
2154
|
+
console.log("[MMDPlayerBase] Extracted material config:");
|
|
2155
|
+
console.log(" - color:", materialConfig.color);
|
|
2156
|
+
console.log(" - emissive:", materialConfig.emissive);
|
|
2157
|
+
console.log(" - specular:", materialConfig.specular);
|
|
2158
|
+
console.log(" - shininess:", materialConfig.shininess);
|
|
2159
|
+
if (materialConfig.color) {
|
|
2160
|
+
const isBlack = materialConfig.color.r === 0 && materialConfig.color.g === 0 && materialConfig.color.b === 0;
|
|
2161
|
+
if (!isBlack) {
|
|
2162
|
+
m.color.copy(materialConfig.color);
|
|
2163
|
+
console.log("[MMDPlayerBase] Applied color:", materialConfig.color);
|
|
2164
|
+
} else {
|
|
2165
|
+
console.log("[MMDPlayerBase] Skipping black color (0,0,0) to preserve original material");
|
|
2166
|
+
}
|
|
2167
|
+
}
|
|
2168
|
+
if (materialConfig.emissive) {
|
|
2169
|
+
const isBlack = materialConfig.emissive.r === 0 && materialConfig.emissive.g === 0 && materialConfig.emissive.b === 0;
|
|
2170
|
+
if (!isBlack) {
|
|
2171
|
+
m.emissive.copy(materialConfig.emissive);
|
|
2172
|
+
console.log("[MMDPlayerBase] Applied emissive:", materialConfig.emissive);
|
|
2173
|
+
}
|
|
2174
|
+
}
|
|
2175
|
+
if (materialConfig.specular && m.specular) {
|
|
2176
|
+
m.specular.copy(materialConfig.specular);
|
|
2177
|
+
console.log("[MMDPlayerBase] Applied specular:", materialConfig.specular);
|
|
2178
|
+
}
|
|
2179
|
+
if (materialConfig.shininess !== void 0 && m.shininess !== void 0) {
|
|
2180
|
+
console.log("[MMDPlayerBase] Applying shininess:", materialConfig.shininess);
|
|
2181
|
+
m.shininess = materialConfig.shininess;
|
|
608
2182
|
}
|
|
609
2183
|
}
|
|
2184
|
+
} else {
|
|
2185
|
+
console.log("[MMDPlayerBase] Material type not supported for FX:", m.type);
|
|
610
2186
|
}
|
|
611
2187
|
});
|
|
612
2188
|
}
|
|
613
2189
|
});
|
|
2190
|
+
console.log("[MMDPlayerBase] \u{1F3A8} Model traverse complete, processed materials:", materialCount);
|
|
614
2191
|
helper.add(mesh, {
|
|
615
2192
|
animation,
|
|
616
2193
|
physics: enablePhysics
|
|
617
2194
|
});
|
|
618
2195
|
scene.add(mesh);
|
|
619
|
-
console.log("[MMDPlayerBase] \u2705 Model added to scene (fully loaded)");
|
|
620
2196
|
const isMobileDevice = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent) || window.innerWidth <= 768;
|
|
621
2197
|
if (isMobileDevice) {
|
|
622
2198
|
console.log("[MMDPlayerBase] \u{1F4F1} Mobile device detected, applying optimizations...");
|
|
623
|
-
if (renderer.capabilities.vertexTextures) {
|
|
624
|
-
console.log("[MMDPlayerBase] \u2705 Vertex textures supported");
|
|
625
|
-
} else {
|
|
2199
|
+
if (!renderer.capabilities.vertexTextures) {
|
|
626
2200
|
console.log("[MMDPlayerBase] \u26A0\uFE0F Vertex textures NOT supported");
|
|
627
2201
|
}
|
|
628
2202
|
let simplifiedMaterialCount = 0;
|
|
629
2203
|
mesh.traverse((child) => {
|
|
630
|
-
if (child instanceof
|
|
2204
|
+
if (child instanceof THREE3.Mesh || child instanceof THREE3.SkinnedMesh) {
|
|
631
2205
|
const materials = Array.isArray(child.material) ? child.material : [child.material];
|
|
632
2206
|
materials.forEach((material, idx) => {
|
|
633
|
-
if (material instanceof
|
|
2207
|
+
if (material instanceof THREE3.MeshPhongMaterial || material instanceof THREE3.MeshStandardMaterial) {
|
|
634
2208
|
const originalColor = material.color?.clone();
|
|
635
2209
|
const originalMap = material.map;
|
|
636
|
-
const basicMaterial = new
|
|
2210
|
+
const basicMaterial = new THREE3.MeshBasicMaterial({
|
|
637
2211
|
color: originalColor || 16777215,
|
|
638
2212
|
map: originalMap,
|
|
639
2213
|
transparent: material.transparent,
|
|
@@ -652,20 +2226,14 @@ var MMDPlayerBase = forwardRef((props, ref) => {
|
|
|
652
2226
|
});
|
|
653
2227
|
}
|
|
654
2228
|
});
|
|
655
|
-
if (simplifiedMaterialCount > 0) {
|
|
656
|
-
console.log(`[MMDPlayerBase] \u2705 Simplified ${simplifiedMaterialCount} materials to MeshBasicMaterial`);
|
|
657
|
-
}
|
|
658
2229
|
const MAX_BONES = 64;
|
|
659
2230
|
if (mesh.skeleton) {
|
|
660
2231
|
const boneCount = mesh.skeleton.bones.length;
|
|
661
2232
|
if (boneCount > MAX_BONES) {
|
|
662
2233
|
console.warn(`[MMDPlayerBase] \u26A0\uFE0F Model has ${boneCount} bones (max recommended: ${MAX_BONES})`);
|
|
663
2234
|
console.warn(`[MMDPlayerBase] This may cause performance issues on mobile devices`);
|
|
664
|
-
} else {
|
|
665
|
-
console.log(`[MMDPlayerBase] \u2705 Bone count: ${boneCount} (within limit)`);
|
|
666
2235
|
}
|
|
667
2236
|
}
|
|
668
|
-
console.log("[MMDPlayerBase] \u{1F4F1} Mobile optimizations applied");
|
|
669
2237
|
}
|
|
670
2238
|
if (resources.cameraPath) {
|
|
671
2239
|
loader.loadAnimation(
|
|
@@ -684,7 +2252,6 @@ var MMDPlayerBase = forwardRef((props, ref) => {
|
|
|
684
2252
|
const stagePaths = Array.isArray(resources.stageModelPath) ? resources.stageModelPath : resources.stageModelPath ? [resources.stageModelPath] : [];
|
|
685
2253
|
for (const stagePath of stagePaths) {
|
|
686
2254
|
try {
|
|
687
|
-
console.log(`[MMDPlayerBase] Loading stage from: ${stagePath}`);
|
|
688
2255
|
const stageMesh = await new Promise((resolve, reject) => {
|
|
689
2256
|
loader.load(
|
|
690
2257
|
stagePath,
|
|
@@ -700,12 +2267,15 @@ var MMDPlayerBase = forwardRef((props, ref) => {
|
|
|
700
2267
|
});
|
|
701
2268
|
if (checkCancelled()) return;
|
|
702
2269
|
console.log(`[MMDPlayerBase] Stage model loaded: ${stagePath}`, stageMesh);
|
|
2270
|
+
console.log("[MMDPlayerBase] \u{1F3A8} Traversing stage mesh to apply FX, multiFX:", !!multiFXAdapterRef.current, "singleFX:", !!fxAdapterRef.current);
|
|
2271
|
+
let stageMaterialCount = 0;
|
|
703
2272
|
stageMesh.traverse((child) => {
|
|
704
|
-
if (child instanceof
|
|
2273
|
+
if (child instanceof THREE3.Mesh) {
|
|
705
2274
|
child.castShadow = true;
|
|
706
2275
|
child.receiveShadow = true;
|
|
707
2276
|
const mesh2 = child;
|
|
708
2277
|
const materials = Array.isArray(mesh2.material) ? mesh2.material : [mesh2.material];
|
|
2278
|
+
stageMaterialCount += materials.length;
|
|
709
2279
|
materials.forEach((m, idx) => {
|
|
710
2280
|
if (m.morphTargets) {
|
|
711
2281
|
m.morphTargets = false;
|
|
@@ -722,20 +2292,56 @@ var MMDPlayerBase = forwardRef((props, ref) => {
|
|
|
722
2292
|
if (!m.userData) m.userData = {};
|
|
723
2293
|
m.userData.outlineParameters = {
|
|
724
2294
|
thickness: outlineOptions.thickness ?? 3e-3,
|
|
725
|
-
color: new
|
|
2295
|
+
color: new THREE3.Color(outlineOptions.color ?? "#000000").toArray(),
|
|
726
2296
|
alpha: 1,
|
|
727
2297
|
visible: true,
|
|
728
2298
|
keepAlive: true
|
|
729
2299
|
};
|
|
730
|
-
if (m instanceof
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
2300
|
+
if (m instanceof THREE3.MeshPhongMaterial || m instanceof THREE3.MeshToonMaterial) {
|
|
2301
|
+
console.log("[MMDPlayerBase] \u{1F3A8} Applying FX to stage material (type:", m.type, "), multiFX:", !!multiFXAdapterRef.current, "singleFX:", !!fxAdapterRef.current);
|
|
2302
|
+
if (multiFXAdapterRef.current) {
|
|
2303
|
+
console.log("[MMDPlayerBase] Using MultiFXAdapter for stage");
|
|
2304
|
+
multiFXAdapterRef.current.applyToMaterial(m, "stage");
|
|
2305
|
+
} else if (fxAdapterRef.current) {
|
|
2306
|
+
console.log("[MMDPlayerBase] Using single FXAdapter for stage");
|
|
2307
|
+
const materialConfig = fxAdapterRef.current.extractMaterialConfig();
|
|
2308
|
+
console.log("[MMDPlayerBase] Extracted material config for stage:");
|
|
2309
|
+
console.log(" - color:", materialConfig.color);
|
|
2310
|
+
console.log(" - emissive:", materialConfig.emissive);
|
|
2311
|
+
console.log(" - specular:", materialConfig.specular);
|
|
2312
|
+
console.log(" - shininess:", materialConfig.shininess);
|
|
2313
|
+
if (materialConfig.color) {
|
|
2314
|
+
const isBlack = materialConfig.color.r === 0 && materialConfig.color.g === 0 && materialConfig.color.b === 0;
|
|
2315
|
+
if (!isBlack) {
|
|
2316
|
+
m.color.copy(materialConfig.color);
|
|
2317
|
+
console.log("[MMDPlayerBase] Applied color to stage:", materialConfig.color);
|
|
2318
|
+
} else {
|
|
2319
|
+
console.log("[MMDPlayerBase] Skipping black color (0,0,0) for stage to preserve original material");
|
|
2320
|
+
}
|
|
2321
|
+
}
|
|
2322
|
+
if (materialConfig.emissive) {
|
|
2323
|
+
const isBlack = materialConfig.emissive.r === 0 && materialConfig.emissive.g === 0 && materialConfig.emissive.b === 0;
|
|
2324
|
+
if (!isBlack) {
|
|
2325
|
+
m.emissive.copy(materialConfig.emissive);
|
|
2326
|
+
console.log("[MMDPlayerBase] Applied emissive to stage:", materialConfig.emissive);
|
|
2327
|
+
}
|
|
2328
|
+
}
|
|
2329
|
+
if (materialConfig.specular && m.specular) {
|
|
2330
|
+
m.specular.copy(materialConfig.specular);
|
|
2331
|
+
console.log("[MMDPlayerBase] Applied specular to stage:", materialConfig.specular);
|
|
2332
|
+
}
|
|
2333
|
+
if (materialConfig.shininess !== void 0 && m.shininess !== void 0) {
|
|
2334
|
+
console.log("[MMDPlayerBase] Applying shininess to stage:", materialConfig.shininess);
|
|
2335
|
+
m.shininess = materialConfig.shininess;
|
|
2336
|
+
}
|
|
734
2337
|
}
|
|
2338
|
+
} else {
|
|
2339
|
+
console.log("[MMDPlayerBase] Stage material type not supported for FX:", m.type);
|
|
735
2340
|
}
|
|
736
2341
|
});
|
|
737
2342
|
}
|
|
738
2343
|
});
|
|
2344
|
+
console.log("[MMDPlayerBase] \u{1F3A8} Stage traverse complete, processed materials:", stageMaterialCount);
|
|
739
2345
|
try {
|
|
740
2346
|
await waitForMaterialsReady(stageMesh, renderer, scene, camera);
|
|
741
2347
|
} catch (e) {
|
|
@@ -743,14 +2349,13 @@ var MMDPlayerBase = forwardRef((props, ref) => {
|
|
|
743
2349
|
}
|
|
744
2350
|
if (checkCancelled()) return;
|
|
745
2351
|
scene.add(stageMesh);
|
|
746
|
-
const stageBox = new
|
|
747
|
-
const stageSize = stageBox.getSize(new
|
|
2352
|
+
const stageBox = new THREE3.Box3().setFromObject(stageMesh);
|
|
2353
|
+
const stageSize = stageBox.getSize(new THREE3.Vector3());
|
|
748
2354
|
if (stageSize.length() < 1) {
|
|
749
2355
|
stageMesh.scale.multiplyScalar(100);
|
|
750
2356
|
} else if (stageSize.y < 5) {
|
|
751
2357
|
stageMesh.scale.multiplyScalar(10);
|
|
752
2358
|
}
|
|
753
|
-
console.log(`[MMDPlayerBase] \u2705 Stage added: ${stagePath}`);
|
|
754
2359
|
if (resources.stageMotionPath) {
|
|
755
2360
|
loader.loadAnimation(resources.stageMotionPath, stageMesh, (anim) => {
|
|
756
2361
|
if (!checkCancelled()) helper.add(stageMesh, { animation: anim });
|
|
@@ -762,21 +2367,10 @@ var MMDPlayerBase = forwardRef((props, ref) => {
|
|
|
762
2367
|
}
|
|
763
2368
|
if (checkCancelled()) return;
|
|
764
2369
|
isReadyRef.current = true;
|
|
765
|
-
console.log("[MMDPlayerBase] \u{1F389} All resources fully loaded and ready!");
|
|
766
|
-
console.log("[MMDPlayerBase] \u{1F4CA} Summary:");
|
|
767
|
-
console.log(`[MMDPlayerBase] - Model: \u2705 Fully loaded with all textures`);
|
|
768
|
-
if (resources.stageModelPath) {
|
|
769
|
-
console.log(`[MMDPlayerBase] - Stage: \u2705 Fully loaded with all textures`);
|
|
770
|
-
}
|
|
771
|
-
if (animation) {
|
|
772
|
-
console.log(`[MMDPlayerBase] - Animation: \u2705 Ready (${animation.duration.toFixed(2)}s)`);
|
|
773
|
-
}
|
|
774
|
-
console.log("[MMDPlayerBase] \u{1F514} Triggering onLoad callback");
|
|
775
2370
|
onLoad?.();
|
|
776
2371
|
if (autoPlay) {
|
|
777
2372
|
setTimeout(() => {
|
|
778
2373
|
if (checkCancelled()) return;
|
|
779
|
-
console.log("[MMDPlayerBase] \u{1F3AC} Starting animation playback (after materials fully loaded)");
|
|
780
2374
|
isPlayingRef.current = true;
|
|
781
2375
|
if (!clockRef.current.running) clockRef.current.start();
|
|
782
2376
|
onPlay?.();
|
|
@@ -849,47 +2443,24 @@ ${errorMessage}
|
|
|
849
2443
|
console.log("[MMDPlayerBase] Found meshes count:", meshes.length);
|
|
850
2444
|
if (meshes && Array.isArray(meshes) && meshes.length > 0) {
|
|
851
2445
|
meshes.forEach((mesh, idx) => {
|
|
852
|
-
console.log(`[MMDPlayerBase] Cleaning mesh ${idx}:`, mesh.uuid);
|
|
853
2446
|
let meshData = null;
|
|
854
2447
|
if (helperObjects instanceof WeakMap) {
|
|
855
|
-
console.log("[MMDPlayerBase] Accessing WeakMap with mesh as key...");
|
|
856
2448
|
meshData = helperObjects.get(mesh);
|
|
857
|
-
if (meshData) {
|
|
858
|
-
const meshDataKeys = Object.keys(meshData);
|
|
859
|
-
console.log(`[MMDPlayerBase] \u2705 Got meshData from WeakMap, keys (${meshDataKeys.length}):`, meshDataKeys);
|
|
860
|
-
const physicsRelatedKeys = meshDataKeys.filter((k) => k.toLowerCase().includes("phys"));
|
|
861
|
-
if (physicsRelatedKeys.length > 0) {
|
|
862
|
-
console.log(`[MMDPlayerBase] Physics-related keys:`, physicsRelatedKeys);
|
|
863
|
-
physicsRelatedKeys.forEach((key) => {
|
|
864
|
-
const value = meshData[key];
|
|
865
|
-
console.log(`[MMDPlayerBase] ${key}:`, typeof value, value?.constructor?.name || value);
|
|
866
|
-
});
|
|
867
|
-
}
|
|
868
|
-
} else {
|
|
869
|
-
console.log("[MMDPlayerBase] \u26A0\uFE0F No meshData found in WeakMap for this mesh");
|
|
870
|
-
}
|
|
871
2449
|
}
|
|
872
2450
|
if (!meshData) {
|
|
873
|
-
console.log("[MMDPlayerBase] Using mesh itself as meshData");
|
|
874
2451
|
meshData = mesh;
|
|
875
2452
|
}
|
|
876
2453
|
const physics = meshData?.physics;
|
|
877
2454
|
if (physics) {
|
|
878
2455
|
try {
|
|
879
|
-
console.log("[MMDPlayerBase] \u{1F3AF} Starting physics cleanup for mesh", idx);
|
|
880
|
-
console.log("[MMDPlayerBase] Debug: physics object keys:", Object.keys(physics));
|
|
881
2456
|
if (typeof physics.dispose === "function") {
|
|
882
|
-
console.log("[MMDPlayerBase] Calling MMDPhysics.dispose()...");
|
|
883
2457
|
physics.dispose();
|
|
884
|
-
console.log("[MMDPlayerBase] \u2705 MMDPhysics.dispose() completed");
|
|
885
2458
|
} else {
|
|
886
|
-
console.log("[MMDPlayerBase] No dispose method, manually cleaning physics components...");
|
|
887
2459
|
const Ammo2 = window.Ammo;
|
|
888
2460
|
if (!Ammo2 || !Ammo2.destroy) {
|
|
889
2461
|
console.warn("[MMDPlayerBase] \u26A0\uFE0F Ammo.destroy not available");
|
|
890
2462
|
} else {
|
|
891
2463
|
if (physics.world && Array.isArray(physics.bodies) && physics.bodies.length > 0) {
|
|
892
|
-
console.log(`[MMDPlayerBase] Cleaning ${physics.bodies.length} rigid bodies...`);
|
|
893
2464
|
for (let i = physics.bodies.length - 1; i >= 0; i--) {
|
|
894
2465
|
try {
|
|
895
2466
|
const body = physics.bodies[i];
|
|
@@ -901,10 +2472,8 @@ ${errorMessage}
|
|
|
901
2472
|
}
|
|
902
2473
|
}
|
|
903
2474
|
physics.bodies.length = 0;
|
|
904
|
-
console.log("[MMDPlayerBase] \u2705 All rigid bodies removed");
|
|
905
2475
|
}
|
|
906
2476
|
if (physics.world && Array.isArray(physics.constraints) && physics.constraints.length > 0) {
|
|
907
|
-
console.log(`[MMDPlayerBase] Cleaning ${physics.constraints.length} constraints...`);
|
|
908
2477
|
for (let i = physics.constraints.length - 1; i >= 0; i--) {
|
|
909
2478
|
try {
|
|
910
2479
|
const constraint = physics.constraints[i];
|
|
@@ -916,18 +2485,14 @@ ${errorMessage}
|
|
|
916
2485
|
}
|
|
917
2486
|
}
|
|
918
2487
|
physics.constraints.length = 0;
|
|
919
|
-
console.log("[MMDPlayerBase] \u2705 All constraints removed");
|
|
920
2488
|
}
|
|
921
2489
|
}
|
|
922
2490
|
}
|
|
923
2491
|
meshData.physics = null;
|
|
924
|
-
console.log("[MMDPlayerBase] \u2705 Physics cleanup completed for mesh", idx);
|
|
925
2492
|
} catch (physicsError) {
|
|
926
2493
|
console.error("[MMDPlayerBase] \u274C Error cleaning up physics:", physicsError);
|
|
927
2494
|
console.error("[MMDPlayerBase] Physics error stack:", physicsError.stack);
|
|
928
2495
|
}
|
|
929
|
-
} else {
|
|
930
|
-
console.log("[MMDPlayerBase] \u26A0\uFE0F No physics object found for mesh", idx);
|
|
931
2496
|
}
|
|
932
2497
|
if (meshData?.mixer) {
|
|
933
2498
|
meshData.mixer.stopAllAction();
|
|
@@ -955,19 +2520,10 @@ ${errorMessage}
|
|
|
955
2520
|
});
|
|
956
2521
|
meshes.length = 0;
|
|
957
2522
|
}
|
|
958
|
-
console.log("[MMDPlayerBase] \u{1F525} Starting CRITICAL physics components cleanup...");
|
|
959
2523
|
const Ammo = window.Ammo;
|
|
960
2524
|
if (Ammo && Ammo.destroy) {
|
|
961
2525
|
const components = physicsComponentsRef.current;
|
|
962
|
-
console.log(`[MMDPlayerBase] \u{1F4CA} Physics components count:`, {
|
|
963
|
-
worlds: components.worlds.length,
|
|
964
|
-
solvers: components.solvers.length,
|
|
965
|
-
caches: components.caches.length,
|
|
966
|
-
dispatchers: components.dispatchers.length,
|
|
967
|
-
configs: components.configs.length
|
|
968
|
-
});
|
|
969
2526
|
if (components.worlds.length > 0) {
|
|
970
|
-
console.log(`[MMDPlayerBase] \u{1F5D1}\uFE0F Destroying ${components.worlds.length} btDiscreteDynamicsWorld(s)...`);
|
|
971
2527
|
for (let i = components.worlds.length - 1; i >= 0; i--) {
|
|
972
2528
|
try {
|
|
973
2529
|
Ammo.destroy(components.worlds[i]);
|
|
@@ -976,10 +2532,8 @@ ${errorMessage}
|
|
|
976
2532
|
}
|
|
977
2533
|
}
|
|
978
2534
|
components.worlds.length = 0;
|
|
979
|
-
console.log("[MMDPlayerBase] \u2705 All btDiscreteDynamicsWorld destroyed");
|
|
980
2535
|
}
|
|
981
2536
|
if (components.solvers.length > 0) {
|
|
982
|
-
console.log(`[MMDPlayerBase] \u{1F5D1}\uFE0F Destroying ${components.solvers.length} btSequentialImpulseConstraintSolver(s)...`);
|
|
983
2537
|
for (let i = components.solvers.length - 1; i >= 0; i--) {
|
|
984
2538
|
try {
|
|
985
2539
|
Ammo.destroy(components.solvers[i]);
|
|
@@ -988,10 +2542,8 @@ ${errorMessage}
|
|
|
988
2542
|
}
|
|
989
2543
|
}
|
|
990
2544
|
components.solvers.length = 0;
|
|
991
|
-
console.log("[MMDPlayerBase] \u2705 All btSequentialImpulseConstraintSolver destroyed");
|
|
992
2545
|
}
|
|
993
2546
|
if (components.caches.length > 0) {
|
|
994
|
-
console.log(`[MMDPlayerBase] \u{1F5D1}\uFE0F Destroying ${components.caches.length} btDbvtBroadphase(s)...`);
|
|
995
2547
|
for (let i = components.caches.length - 1; i >= 0; i--) {
|
|
996
2548
|
try {
|
|
997
2549
|
Ammo.destroy(components.caches[i]);
|
|
@@ -1000,10 +2552,8 @@ ${errorMessage}
|
|
|
1000
2552
|
}
|
|
1001
2553
|
}
|
|
1002
2554
|
components.caches.length = 0;
|
|
1003
|
-
console.log("[MMDPlayerBase] \u2705 All btDbvtBroadphase destroyed");
|
|
1004
2555
|
}
|
|
1005
2556
|
if (components.dispatchers.length > 0) {
|
|
1006
|
-
console.log(`[MMDPlayerBase] \u{1F5D1}\uFE0F Destroying ${components.dispatchers.length} btCollisionDispatcher(s)...`);
|
|
1007
2557
|
for (let i = components.dispatchers.length - 1; i >= 0; i--) {
|
|
1008
2558
|
try {
|
|
1009
2559
|
Ammo.destroy(components.dispatchers[i]);
|
|
@@ -1012,10 +2562,8 @@ ${errorMessage}
|
|
|
1012
2562
|
}
|
|
1013
2563
|
}
|
|
1014
2564
|
components.dispatchers.length = 0;
|
|
1015
|
-
console.log("[MMDPlayerBase] \u2705 All btCollisionDispatcher destroyed");
|
|
1016
2565
|
}
|
|
1017
2566
|
if (components.configs.length > 0) {
|
|
1018
|
-
console.log(`[MMDPlayerBase] \u{1F5D1}\uFE0F Destroying ${components.configs.length} btDefaultCollisionConfiguration(s)...`);
|
|
1019
2567
|
for (let i = components.configs.length - 1; i >= 0; i--) {
|
|
1020
2568
|
try {
|
|
1021
2569
|
Ammo.destroy(components.configs[i]);
|
|
@@ -1024,19 +2572,14 @@ ${errorMessage}
|
|
|
1024
2572
|
}
|
|
1025
2573
|
}
|
|
1026
2574
|
components.configs.length = 0;
|
|
1027
|
-
console.log("[MMDPlayerBase] \u2705 All btDefaultCollisionConfiguration destroyed");
|
|
1028
2575
|
}
|
|
1029
|
-
console.log("[MMDPlayerBase] \u{1F389} Physics components cleanup completed!");
|
|
1030
2576
|
} else {
|
|
1031
2577
|
console.warn("[MMDPlayerBase] \u26A0\uFE0F Ammo.destroy not available, skipping physics cleanup");
|
|
1032
2578
|
}
|
|
1033
|
-
console.log("[MMDPlayerBase] Checking helper-level physics...");
|
|
1034
2579
|
if (helperRef.current.sharedPhysics) {
|
|
1035
|
-
console.log("[MMDPlayerBase] Clearing sharedPhysics reference...");
|
|
1036
2580
|
helperRef.current.sharedPhysics = null;
|
|
1037
2581
|
}
|
|
1038
2582
|
if (helperRef.current.masterPhysics) {
|
|
1039
|
-
console.log("[MMDPlayerBase] Clearing masterPhysics reference...");
|
|
1040
2583
|
helperRef.current.masterPhysics = null;
|
|
1041
2584
|
}
|
|
1042
2585
|
if (helperRef.current.dispose) {
|
|
@@ -1057,8 +2600,8 @@ ${errorMessage}
|
|
|
1057
2600
|
}
|
|
1058
2601
|
if (sceneRef.current) {
|
|
1059
2602
|
sceneRef.current.traverse((object) => {
|
|
1060
|
-
if (object instanceof
|
|
1061
|
-
if (object instanceof
|
|
2603
|
+
if (object instanceof THREE3.Mesh || object instanceof THREE3.SkinnedMesh) {
|
|
2604
|
+
if (object instanceof THREE3.SkinnedMesh) {
|
|
1062
2605
|
if (object.skeleton) {
|
|
1063
2606
|
object.skeleton.dispose();
|
|
1064
2607
|
}
|
|
@@ -1110,7 +2653,7 @@ ${errorMessage}
|
|
|
1110
2653
|
object.material = null;
|
|
1111
2654
|
}
|
|
1112
2655
|
}
|
|
1113
|
-
if (object instanceof
|
|
2656
|
+
if (object instanceof THREE3.AudioListener) {
|
|
1114
2657
|
try {
|
|
1115
2658
|
if (object.context && object.context.state !== "closed") {
|
|
1116
2659
|
object.context.close?.();
|
|
@@ -1119,7 +2662,7 @@ ${errorMessage}
|
|
|
1119
2662
|
console.warn("[MMDPlayerBase] Error closing AudioContext:", e);
|
|
1120
2663
|
}
|
|
1121
2664
|
}
|
|
1122
|
-
if (object instanceof
|
|
2665
|
+
if (object instanceof THREE3.Light) {
|
|
1123
2666
|
if (object.shadow && object.shadow.map) {
|
|
1124
2667
|
object.shadow.map.dispose();
|
|
1125
2668
|
object.shadow.map = null;
|
|
@@ -1135,13 +2678,13 @@ ${errorMessage}
|
|
|
1135
2678
|
}
|
|
1136
2679
|
if (rendererRef.current) {
|
|
1137
2680
|
try {
|
|
1138
|
-
if (composerRef.current) {
|
|
1139
|
-
composerRef.current.passes.forEach((pass) => {
|
|
1140
|
-
if (pass.dispose) pass.dispose();
|
|
1141
|
-
});
|
|
1142
|
-
composerRef.current = null;
|
|
1143
|
-
}
|
|
1144
2681
|
outlineEffectRef.current = null;
|
|
2682
|
+
if (multiFXAdapterRef.current) {
|
|
2683
|
+
multiFXAdapterRef.current.clear();
|
|
2684
|
+
multiFXAdapterRef.current = null;
|
|
2685
|
+
}
|
|
2686
|
+
fxEffectRef.current = null;
|
|
2687
|
+
fxAdapterRef.current = null;
|
|
1145
2688
|
const renderer = rendererRef.current;
|
|
1146
2689
|
if (renderer.renderLists) {
|
|
1147
2690
|
renderer.renderLists.dispose();
|
|
@@ -1179,7 +2722,7 @@ ${errorMessage}
|
|
|
1179
2722
|
rendererRef.current = null;
|
|
1180
2723
|
}
|
|
1181
2724
|
cameraRef.current = null;
|
|
1182
|
-
clockRef.current = new
|
|
2725
|
+
clockRef.current = new THREE3.Clock();
|
|
1183
2726
|
durationRef.current = 0;
|
|
1184
2727
|
console.log("[MMDPlayerBase] Cleanup completed");
|
|
1185
2728
|
if (typeof window !== "undefined" && "gc" in window) {
|
|
@@ -1200,12 +2743,11 @@ ${errorMessage}
|
|
|
1200
2743
|
if (oldSound.parent) oldSound.parent.remove(oldSound);
|
|
1201
2744
|
audioRef.current = null;
|
|
1202
2745
|
}
|
|
1203
|
-
console.log("[MMDPlayerBase] Loading new audio track:", resources.audioPath);
|
|
1204
2746
|
audioLoaderRef.current.load(
|
|
1205
2747
|
resources.audioPath,
|
|
1206
2748
|
(buffer) => {
|
|
1207
2749
|
if (!audioListenerRef.current) return;
|
|
1208
|
-
const sound = new
|
|
2750
|
+
const sound = new THREE3.Audio(listener);
|
|
1209
2751
|
sound.setBuffer(buffer);
|
|
1210
2752
|
sound.setLoop(loopRef.current);
|
|
1211
2753
|
sound.setVolume(volume);
|
|
@@ -1226,7 +2768,7 @@ ${errorMessage}
|
|
|
1226
2768
|
useEffect(() => {
|
|
1227
2769
|
if (!sceneRef.current) return;
|
|
1228
2770
|
if (showAxes && !axesHelperRef.current) {
|
|
1229
|
-
const axesHelper = new
|
|
2771
|
+
const axesHelper = new THREE3.AxesHelper(20);
|
|
1230
2772
|
sceneRef.current.add(axesHelper);
|
|
1231
2773
|
axesHelperRef.current = axesHelper;
|
|
1232
2774
|
} else if (!showAxes && axesHelperRef.current) {
|
|
@@ -1244,10 +2786,10 @@ ${errorMessage}
|
|
|
1244
2786
|
useEffect(() => {
|
|
1245
2787
|
if (outlineEffectRef.current) {
|
|
1246
2788
|
outlineEffectRef.current.defaultThickness = outlineOptions.thickness ?? 3e-3;
|
|
1247
|
-
outlineEffectRef.current.defaultColor = new
|
|
2789
|
+
outlineEffectRef.current.defaultColor = new THREE3.Color(outlineOptions.color ?? "#000000").toArray();
|
|
1248
2790
|
if (sceneRef.current) {
|
|
1249
2791
|
sceneRef.current.traverse((obj) => {
|
|
1250
|
-
if (obj instanceof
|
|
2792
|
+
if (obj instanceof THREE3.Mesh || obj instanceof THREE3.SkinnedMesh) {
|
|
1251
2793
|
const materials = Array.isArray(obj.material) ? obj.material : [obj.material];
|
|
1252
2794
|
materials.forEach((m) => {
|
|
1253
2795
|
if (m.userData && m.userData.outlineParameters) {
|
|
@@ -1255,7 +2797,7 @@ ${errorMessage}
|
|
|
1255
2797
|
m.userData.outlineParameters.thickness = outlineOptions.thickness;
|
|
1256
2798
|
}
|
|
1257
2799
|
if (outlineOptions.color !== void 0) {
|
|
1258
|
-
m.userData.outlineParameters.color = new
|
|
2800
|
+
m.userData.outlineParameters.color = new THREE3.Color(outlineOptions.color).toArray();
|
|
1259
2801
|
}
|
|
1260
2802
|
}
|
|
1261
2803
|
});
|
|
@@ -1263,36 +2805,7 @@ ${errorMessage}
|
|
|
1263
2805
|
});
|
|
1264
2806
|
}
|
|
1265
2807
|
}
|
|
1266
|
-
|
|
1267
|
-
const bloomPass = composerRef.current.passes.find((p) => p instanceof UnrealBloomPass);
|
|
1268
|
-
if (bloomPass) {
|
|
1269
|
-
bloomPass.strength = bloomOptions.strength ?? 1;
|
|
1270
|
-
bloomPass.radius = bloomOptions.radius ?? 0.4;
|
|
1271
|
-
bloomPass.threshold = bloomOptions.threshold ?? 0.8;
|
|
1272
|
-
}
|
|
1273
|
-
}
|
|
1274
|
-
}, [outlineOptions.thickness, outlineOptions.color, bloomOptions.strength, bloomOptions.radius, bloomOptions.threshold]);
|
|
1275
|
-
useEffect(() => {
|
|
1276
|
-
if (!sceneRef.current) return;
|
|
1277
|
-
sceneRef.current.traverse((obj) => {
|
|
1278
|
-
if (obj instanceof THREE.Mesh || obj instanceof THREE.SkinnedMesh) {
|
|
1279
|
-
const materials = Array.isArray(obj.material) ? obj.material : [obj.material];
|
|
1280
|
-
materials.forEach((m) => {
|
|
1281
|
-
if (m instanceof THREE.MeshPhongMaterial) {
|
|
1282
|
-
if (toonOptions.enabled !== false && (toonOptions.enabled || renderEffect.includes("outline"))) {
|
|
1283
|
-
m.shininess = toonOptions.shininess ?? 0;
|
|
1284
|
-
m.specular.setScalar(0);
|
|
1285
|
-
if (toonOptions.forceHardShading && m.toonMap) {
|
|
1286
|
-
m.toonMap.magFilter = THREE.NearestFilter;
|
|
1287
|
-
m.toonMap.minFilter = THREE.NearestFilter;
|
|
1288
|
-
m.toonMap.needsUpdate = true;
|
|
1289
|
-
}
|
|
1290
|
-
}
|
|
1291
|
-
}
|
|
1292
|
-
});
|
|
1293
|
-
}
|
|
1294
|
-
});
|
|
1295
|
-
}, [toonOptions.enabled, toonOptions.shininess, toonOptions.forceHardShading, renderEffect]);
|
|
2808
|
+
}, [outlineOptions.thickness, outlineOptions.color]);
|
|
1296
2809
|
useEffect(() => {
|
|
1297
2810
|
if (!isReadyRef.current) return;
|
|
1298
2811
|
if (sceneRef.current) {
|
|
@@ -1300,10 +2813,10 @@ ${errorMessage}
|
|
|
1300
2813
|
if (stage.backgroundColor === "transparent") {
|
|
1301
2814
|
sceneRef.current.background = null;
|
|
1302
2815
|
} else {
|
|
1303
|
-
sceneRef.current.background = new
|
|
2816
|
+
sceneRef.current.background = new THREE3.Color(stage.backgroundColor);
|
|
1304
2817
|
}
|
|
1305
2818
|
} else if (stage.backgroundImage) {
|
|
1306
|
-
const textureLoader = new
|
|
2819
|
+
const textureLoader = new THREE3.TextureLoader();
|
|
1307
2820
|
textureLoader.load(stage.backgroundImage, (texture) => {
|
|
1308
2821
|
if (sceneRef.current) sceneRef.current.background = texture;
|
|
1309
2822
|
});
|
|
@@ -1311,10 +2824,10 @@ ${errorMessage}
|
|
|
1311
2824
|
}
|
|
1312
2825
|
if (sceneRef.current) {
|
|
1313
2826
|
sceneRef.current.traverse((obj) => {
|
|
1314
|
-
if (obj instanceof
|
|
2827
|
+
if (obj instanceof THREE3.AmbientLight && stage.ambientLightIntensity !== void 0) {
|
|
1315
2828
|
obj.intensity = stage.ambientLightIntensity;
|
|
1316
2829
|
}
|
|
1317
|
-
if (obj instanceof
|
|
2830
|
+
if (obj instanceof THREE3.DirectionalLight && stage.directionalLightIntensity !== void 0) {
|
|
1318
2831
|
obj.intensity = stage.directionalLightIntensity;
|
|
1319
2832
|
}
|
|
1320
2833
|
});
|
|
@@ -1345,18 +2858,14 @@ ${errorMessage}
|
|
|
1345
2858
|
latestCallbacks.current.onEnded?.();
|
|
1346
2859
|
}
|
|
1347
2860
|
}
|
|
1348
|
-
|
|
1349
|
-
const useBloom = renderEffect === "bloom" || renderEffect === "outline+bloom";
|
|
1350
|
-
if (useBloom && composerRef.current) {
|
|
1351
|
-
composerRef.current.render();
|
|
1352
|
-
} else if (useOutline && outlineEffectRef.current) {
|
|
2861
|
+
if (renderEffect === "outline" && outlineEffectRef.current) {
|
|
1353
2862
|
outlineEffectRef.current.render(sceneRef.current, cameraRef.current);
|
|
1354
2863
|
} else {
|
|
1355
2864
|
rendererRef.current.render(sceneRef.current, cameraRef.current);
|
|
1356
2865
|
}
|
|
1357
2866
|
}
|
|
1358
2867
|
};
|
|
1359
|
-
return /* @__PURE__ */
|
|
2868
|
+
return /* @__PURE__ */ React21.createElement(
|
|
1360
2869
|
"div",
|
|
1361
2870
|
{
|
|
1362
2871
|
ref: containerRef,
|
|
@@ -1395,78 +2904,78 @@ var ControlPanel = ({
|
|
|
1395
2904
|
onNext,
|
|
1396
2905
|
onResetCamera
|
|
1397
2906
|
}) => {
|
|
1398
|
-
return /* @__PURE__ */
|
|
2907
|
+
return /* @__PURE__ */ React21.createElement("div", { className: "absolute bottom-0 left-0 right-0 bg-gradient-to-t from-black/80 to-transparent p-4 transition-opacity duration-300 hover:opacity-100" }, /* @__PURE__ */ React21.createElement("div", { className: "flex items-center justify-between text-white" }, /* @__PURE__ */ React21.createElement("div", { className: "flex items-center gap-2" }, showPrevNext && onPrevious && /* @__PURE__ */ React21.createElement(
|
|
1399
2908
|
"button",
|
|
1400
2909
|
{
|
|
1401
2910
|
onClick: onPrevious,
|
|
1402
2911
|
className: "rounded-full p-2 hover:bg-white/20 transition-colors",
|
|
1403
2912
|
title: "\u4E0A\u4E00\u4E2A"
|
|
1404
2913
|
},
|
|
1405
|
-
/* @__PURE__ */
|
|
1406
|
-
), /* @__PURE__ */
|
|
2914
|
+
/* @__PURE__ */ React21.createElement(SkipBack, { size: 20 })
|
|
2915
|
+
), /* @__PURE__ */ React21.createElement(
|
|
1407
2916
|
"button",
|
|
1408
2917
|
{
|
|
1409
2918
|
onClick: onPlayPause,
|
|
1410
2919
|
className: "rounded-full p-2 hover:bg-white/20 transition-colors",
|
|
1411
2920
|
title: isPlaying ? "\u6682\u505C" : "\u64AD\u653E"
|
|
1412
2921
|
},
|
|
1413
|
-
isPlaying ? /* @__PURE__ */
|
|
1414
|
-
), showPrevNext && onNext && /* @__PURE__ */
|
|
2922
|
+
isPlaying ? /* @__PURE__ */ React21.createElement(Pause, { size: 24 }) : /* @__PURE__ */ React21.createElement(Play, { size: 24 })
|
|
2923
|
+
), showPrevNext && onNext && /* @__PURE__ */ React21.createElement(
|
|
1415
2924
|
"button",
|
|
1416
2925
|
{
|
|
1417
2926
|
onClick: onNext,
|
|
1418
2927
|
className: "rounded-full p-2 hover:bg-white/20 transition-colors",
|
|
1419
2928
|
title: "\u4E0B\u4E00\u4E2A"
|
|
1420
2929
|
},
|
|
1421
|
-
/* @__PURE__ */
|
|
1422
|
-
)), /* @__PURE__ */
|
|
2930
|
+
/* @__PURE__ */ React21.createElement(SkipForward, { size: 20 })
|
|
2931
|
+
)), /* @__PURE__ */ React21.createElement("div", { className: "flex items-center gap-4" }, (title || subtitle) && /* @__PURE__ */ React21.createElement("div", { className: "hidden text-sm font-medium opacity-80 md:block" }, title, subtitle && /* @__PURE__ */ React21.createElement("span", { className: "ml-2 text-xs opacity-60" }, subtitle)), onToggleListLoop && /* @__PURE__ */ React21.createElement(
|
|
1423
2932
|
"button",
|
|
1424
2933
|
{
|
|
1425
2934
|
onClick: onToggleListLoop,
|
|
1426
2935
|
className: `rounded-full p-2 transition-colors ${isListLooping ? "bg-green-500/30 hover:bg-green-500/50" : "hover:bg-white/20"}`,
|
|
1427
2936
|
title: isListLooping ? "\u5217\u8868\u5FAA\u73AF\uFF1A\u5F00\u542F" : "\u5217\u8868\u5FAA\u73AF\uFF1A\u5173\u95ED"
|
|
1428
2937
|
},
|
|
1429
|
-
/* @__PURE__ */
|
|
1430
|
-
), /* @__PURE__ */
|
|
2938
|
+
/* @__PURE__ */ React21.createElement(Repeat, { size: 20 })
|
|
2939
|
+
), /* @__PURE__ */ React21.createElement(
|
|
1431
2940
|
"button",
|
|
1432
2941
|
{
|
|
1433
2942
|
onClick: onToggleLoop,
|
|
1434
2943
|
className: `rounded-full p-2 transition-colors ${isLooping ? "bg-blue-500/30 hover:bg-blue-500/50" : "hover:bg-white/20"}`,
|
|
1435
2944
|
title: isLooping ? "\u5355\u66F2\u5FAA\u73AF\uFF1A\u5F00\u542F" : "\u5355\u66F2\u5FAA\u73AF\uFF1A\u5173\u95ED"
|
|
1436
2945
|
},
|
|
1437
|
-
/* @__PURE__ */
|
|
1438
|
-
), isCameraManual && onResetCamera && /* @__PURE__ */
|
|
2946
|
+
/* @__PURE__ */ React21.createElement(Repeat1, { size: 20 })
|
|
2947
|
+
), isCameraManual && onResetCamera && /* @__PURE__ */ React21.createElement(
|
|
1439
2948
|
"button",
|
|
1440
2949
|
{
|
|
1441
2950
|
onClick: onResetCamera,
|
|
1442
2951
|
className: "rounded-full p-2 bg-blue-500/30 text-blue-400 hover:bg-blue-500/50 hover:text-blue-300 transition-all animate-in zoom-in duration-300",
|
|
1443
2952
|
title: "\u6062\u590D\u521D\u59CB\u89C6\u89D2"
|
|
1444
2953
|
},
|
|
1445
|
-
/* @__PURE__ */
|
|
1446
|
-
), onToggleAxes && /* @__PURE__ */
|
|
2954
|
+
/* @__PURE__ */ React21.createElement(Camera, { size: 20 })
|
|
2955
|
+
), onToggleAxes && /* @__PURE__ */ React21.createElement(
|
|
1447
2956
|
"button",
|
|
1448
2957
|
{
|
|
1449
2958
|
onClick: onToggleAxes,
|
|
1450
2959
|
className: `rounded-full p-2 transition-colors ${showAxes ? "bg-blue-500/30 hover:bg-blue-500/50" : "hover:bg-white/20"}`,
|
|
1451
2960
|
title: "\u663E\u793A/\u9690\u85CF\u5750\u6807\u8F74"
|
|
1452
2961
|
},
|
|
1453
|
-
/* @__PURE__ */
|
|
1454
|
-
), showSettings && /* @__PURE__ */
|
|
2962
|
+
/* @__PURE__ */ React21.createElement(Grid3x3, { size: 20 })
|
|
2963
|
+
), showSettings && /* @__PURE__ */ React21.createElement(
|
|
1455
2964
|
"button",
|
|
1456
2965
|
{
|
|
1457
2966
|
onClick: onOpenSettings,
|
|
1458
2967
|
className: "rounded-full p-2 hover:bg-white/20 transition-colors",
|
|
1459
2968
|
title: "\u8D44\u6E90\u8BBE\u7F6E"
|
|
1460
2969
|
},
|
|
1461
|
-
/* @__PURE__ */
|
|
1462
|
-
), /* @__PURE__ */
|
|
2970
|
+
/* @__PURE__ */ React21.createElement(Settings, { size: 20 })
|
|
2971
|
+
), /* @__PURE__ */ React21.createElement(
|
|
1463
2972
|
"button",
|
|
1464
2973
|
{
|
|
1465
2974
|
onClick: onToggleFullscreen,
|
|
1466
2975
|
className: "rounded-full p-2 hover:bg-white/20 transition-colors",
|
|
1467
2976
|
title: isFullscreen ? "\u9000\u51FA\u5168\u5C4F" : "\u5168\u5C4F"
|
|
1468
2977
|
},
|
|
1469
|
-
isFullscreen ? /* @__PURE__ */
|
|
2978
|
+
isFullscreen ? /* @__PURE__ */ React21.createElement(Minimize, { size: 20 }) : /* @__PURE__ */ React21.createElement(Maximize, { size: 20 })
|
|
1470
2979
|
))));
|
|
1471
2980
|
};
|
|
1472
2981
|
var SettingsPanel = ({
|
|
@@ -1481,44 +2990,44 @@ var SettingsPanel = ({
|
|
|
1481
2990
|
}) => {
|
|
1482
2991
|
const renderListMode = () => {
|
|
1483
2992
|
if (!items) return null;
|
|
1484
|
-
return /* @__PURE__ */
|
|
2993
|
+
return /* @__PURE__ */ React21.createElement("div", { className: "grid grid-cols-1 gap-2 p-4 sm:grid-cols-2" }, items.map((item) => /* @__PURE__ */ React21.createElement(
|
|
1485
2994
|
"button",
|
|
1486
2995
|
{
|
|
1487
2996
|
key: item.id,
|
|
1488
2997
|
onClick: () => onSelectId?.(item.id),
|
|
1489
2998
|
className: `group flex items-center gap-3 rounded-lg p-3 transition-all ${currentId === item.id ? "bg-blue-500/20 ring-1 ring-blue-500" : "bg-white/5 hover:bg-white/10"}`
|
|
1490
2999
|
},
|
|
1491
|
-
/* @__PURE__ */
|
|
1492
|
-
/* @__PURE__ */
|
|
1493
|
-
currentId === item.id && /* @__PURE__ */
|
|
3000
|
+
/* @__PURE__ */ React21.createElement("div", { className: "flex h-12 w-12 flex-shrink-0 items-center justify-center rounded bg-black/20 overflow-hidden" }, item.thumbnail ? /* @__PURE__ */ React21.createElement("img", { src: item.thumbnail, alt: item.name, className: "h-full w-full object-cover" }) : /* @__PURE__ */ React21.createElement(Video, { size: 20, className: "opacity-50" })),
|
|
3001
|
+
/* @__PURE__ */ React21.createElement("div", { className: "flex-1 text-left" }, /* @__PURE__ */ React21.createElement("div", { className: `font-medium ${currentId === item.id ? "text-blue-400" : "text-white"}` }, item.name), item.description && /* @__PURE__ */ React21.createElement("div", { className: "text-xs text-white/50 truncate" }, item.description)),
|
|
3002
|
+
currentId === item.id && /* @__PURE__ */ React21.createElement(Check, { size: 16, className: "text-blue-400" })
|
|
1494
3003
|
)));
|
|
1495
3004
|
};
|
|
1496
3005
|
const renderOptionGroup = (title, icon, type, list = [], currentVal) => {
|
|
1497
3006
|
if (!list || list.length === 0) return null;
|
|
1498
|
-
return /* @__PURE__ */
|
|
3007
|
+
return /* @__PURE__ */ React21.createElement("div", { className: "mb-6" }, /* @__PURE__ */ React21.createElement("div", { className: "mb-3 flex items-center gap-2 text-sm font-medium text-white/70" }, icon, /* @__PURE__ */ React21.createElement("span", null, title)), /* @__PURE__ */ React21.createElement("div", { className: "grid grid-cols-2 gap-2 sm:grid-cols-3" }, list.map((opt) => /* @__PURE__ */ React21.createElement(
|
|
1499
3008
|
"button",
|
|
1500
3009
|
{
|
|
1501
3010
|
key: opt.id,
|
|
1502
3011
|
onClick: () => onSelectOption?.(type, opt.id),
|
|
1503
3012
|
className: `relative flex flex-col items-center gap-2 rounded-lg p-2 text-center transition-all ${currentVal === opt.id ? "bg-blue-500/20 ring-1 ring-blue-500" : "bg-white/5 hover:bg-white/10"}`
|
|
1504
3013
|
},
|
|
1505
|
-
opt.thumbnail ? /* @__PURE__ */
|
|
1506
|
-
/* @__PURE__ */
|
|
1507
|
-
currentVal === opt.id && /* @__PURE__ */
|
|
3014
|
+
opt.thumbnail ? /* @__PURE__ */ React21.createElement("img", { src: opt.thumbnail, alt: opt.name, className: "h-16 w-full rounded object-cover bg-black/20" }) : /* @__PURE__ */ React21.createElement("div", { className: "flex h-16 w-full items-center justify-center rounded bg-black/20" }, /* @__PURE__ */ React21.createElement("div", { className: "text-xs opacity-30" }, opt.name.slice(0, 2))),
|
|
3015
|
+
/* @__PURE__ */ React21.createElement("div", { className: `w-full truncate text-xs ${currentVal === opt.id ? "text-blue-400" : "text-white/80"}` }, opt.name),
|
|
3016
|
+
currentVal === opt.id && /* @__PURE__ */ React21.createElement("div", { className: "absolute top-1 right-1 rounded-full bg-blue-500 p-0.5" }, /* @__PURE__ */ React21.createElement(Check, { size: 10, className: "text-white" }))
|
|
1508
3017
|
))));
|
|
1509
3018
|
};
|
|
1510
3019
|
const renderOptionsMode = () => {
|
|
1511
3020
|
if (!options) return null;
|
|
1512
|
-
return /* @__PURE__ */
|
|
3021
|
+
return /* @__PURE__ */ React21.createElement("div", { className: "p-4" }, renderOptionGroup("\u6A21\u578B", /* @__PURE__ */ React21.createElement(User, { size: 16 }), "models", options.models, currentSelection?.modelId), renderOptionGroup("\u52A8\u4F5C", /* @__PURE__ */ React21.createElement(Video, { size: 16 }), "motions", options.motions, currentSelection?.motionId), renderOptionGroup("\u955C\u5934", /* @__PURE__ */ React21.createElement(Image$1, { size: 16 }), "cameras", options.cameras, currentSelection?.cameraId), renderOptionGroup("\u97F3\u9891", /* @__PURE__ */ React21.createElement(Music, { size: 16 }), "audios", options.audios, currentSelection?.audioId), renderOptionGroup("\u821E\u53F0", /* @__PURE__ */ React21.createElement(Image$1, { size: 16 }), "stages", options.stages, currentSelection?.stageId));
|
|
1513
3022
|
};
|
|
1514
|
-
return /* @__PURE__ */
|
|
3023
|
+
return /* @__PURE__ */ React21.createElement("div", { className: "absolute right-0 top-0 h-full w-full max-w-sm transform bg-[#1a1a1e]/95 backdrop-blur-md shadow-2xl transition-transform duration-300 ease-in-out overflow-y-auto z-20 border-l border-white/10" }, /* @__PURE__ */ React21.createElement("div", { className: "sticky top-0 z-10 flex items-center justify-between border-b border-white/10 bg-[#1a1a1e]/95 p-4 backdrop-blur-md" }, /* @__PURE__ */ React21.createElement("h2", { className: "text-lg font-semibold text-white" }, mode === "list" ? "\u64AD\u653E\u5217\u8868" : "\u81EA\u5B9A\u4E49\u573A\u666F"), /* @__PURE__ */ React21.createElement(
|
|
1515
3024
|
"button",
|
|
1516
3025
|
{
|
|
1517
3026
|
onClick: onClose,
|
|
1518
3027
|
className: "rounded-full p-2 text-white/50 hover:bg-white/10 hover:text-white"
|
|
1519
3028
|
},
|
|
1520
|
-
/* @__PURE__ */
|
|
1521
|
-
)), /* @__PURE__ */
|
|
3029
|
+
/* @__PURE__ */ React21.createElement(X, { size: 20 })
|
|
3030
|
+
)), /* @__PURE__ */ React21.createElement("div", { className: "pb-20" }, mode === "list" ? renderListMode() : renderOptionsMode()));
|
|
1522
3031
|
};
|
|
1523
3032
|
var MMDPlayerEnhancedDebugInfo = ({
|
|
1524
3033
|
isPlaying,
|
|
@@ -1543,7 +3052,7 @@ var MMDPlayerEnhancedDebugInfo = ({
|
|
|
1543
3052
|
}, 1e3);
|
|
1544
3053
|
return () => clearInterval(timer);
|
|
1545
3054
|
}, []);
|
|
1546
|
-
return /* @__PURE__ */
|
|
3055
|
+
return /* @__PURE__ */ React21.createElement("div", { className: "text-white text-xs font-mono" }, /* @__PURE__ */ React21.createElement("h3", { className: "text-sm font-bold mb-3 pb-2 border-b border-gray-700" }, "\u{1F3AE} MMDPlayerEnhanced Debug"), /* @__PURE__ */ React21.createElement("div", { className: "mb-4" }, /* @__PURE__ */ React21.createElement("h4", { className: "text-gray-400 mb-2" }, "\u64AD\u653E\u72B6\u6001"), /* @__PURE__ */ React21.createElement("div", { className: "space-y-1 pl-2" }, /* @__PURE__ */ React21.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ React21.createElement("span", { className: "text-gray-400" }, "\u64AD\u653E\u4E2D:"), /* @__PURE__ */ React21.createElement(StatusBadge, { active: isPlaying, label: isPlaying ? "Playing" : "Paused" })), /* @__PURE__ */ React21.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ React21.createElement("span", { className: "text-gray-400" }, "\u5FAA\u73AF:"), /* @__PURE__ */ React21.createElement(StatusBadge, { active: isLooping, label: isLooping ? "On" : "Off" })), /* @__PURE__ */ React21.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ React21.createElement("span", { className: "text-gray-400" }, "\u52A0\u8F7D\u4E2D:"), /* @__PURE__ */ React21.createElement(StatusBadge, { active: isLoading, label: isLoading ? "Loading" : "Ready" })))), /* @__PURE__ */ React21.createElement("div", { className: "mb-4" }, /* @__PURE__ */ React21.createElement("h4", { className: "text-gray-400 mb-2" }, "\u89C6\u56FE\u72B6\u6001"), /* @__PURE__ */ React21.createElement("div", { className: "space-y-1 pl-2" }, /* @__PURE__ */ React21.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ React21.createElement("span", { className: "text-gray-400" }, "\u5168\u5C4F:"), /* @__PURE__ */ React21.createElement(StatusBadge, { active: isFullscreen, label: isFullscreen ? "Yes" : "No" })), /* @__PURE__ */ React21.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ React21.createElement("span", { className: "text-gray-400" }, "\u5750\u6807\u8F74:"), /* @__PURE__ */ React21.createElement(StatusBadge, { active: showAxes, label: showAxes ? "Show" : "Hide" })))), /* @__PURE__ */ React21.createElement("div", { className: "mb-4" }, /* @__PURE__ */ React21.createElement("h4", { className: "text-gray-400 mb-2" }, "\u8D44\u6E90\u4FE1\u606F"), /* @__PURE__ */ React21.createElement("div", { className: "space-y-1 pl-2" }, /* @__PURE__ */ React21.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ React21.createElement("span", { className: "text-gray-400" }, "\u6A21\u5F0F:"), /* @__PURE__ */ React21.createElement("span", { className: "text-blue-400 uppercase" }, mode)), mode === "list" && /* @__PURE__ */ React21.createElement(React21.Fragment, null, /* @__PURE__ */ React21.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ React21.createElement("span", { className: "text-gray-400" }, "\u603B\u6570:"), /* @__PURE__ */ React21.createElement("span", { className: "text-green-400" }, totalResources)), currentResourceId && /* @__PURE__ */ React21.createElement("div", { className: "mt-2 p-2 bg-gray-800 rounded" }, /* @__PURE__ */ React21.createElement("div", { className: "text-gray-400 text-[10px]" }, "\u5F53\u524D\u8D44\u6E90"), /* @__PURE__ */ React21.createElement("div", { className: "text-white truncate" }, currentResourceName || currentResourceId), /* @__PURE__ */ React21.createElement("div", { className: "text-gray-500 text-[10px] mt-1 truncate" }, "ID: ", currentResourceId))))), memoryInfo && /* @__PURE__ */ React21.createElement("div", { className: "mb-4" }, /* @__PURE__ */ React21.createElement("h4", { className: "text-gray-400 mb-2" }, "\u5185\u5B58\u76D1\u63A7 (Chrome only)"), /* @__PURE__ */ React21.createElement("div", { className: "space-y-2 p-2 bg-gray-800 rounded" }, /* @__PURE__ */ React21.createElement("div", { className: "flex items-center justify-between text-[10px]" }, /* @__PURE__ */ React21.createElement("span", { className: "text-gray-400" }, "\u5DF2\u7528:"), /* @__PURE__ */ React21.createElement("span", { className: "text-yellow-400 font-bold" }, memoryInfo.used, " MB")), /* @__PURE__ */ React21.createElement("div", { className: "flex items-center justify-between text-[10px]" }, /* @__PURE__ */ React21.createElement("span", { className: "text-gray-400" }, "\u603B\u8BA1:"), /* @__PURE__ */ React21.createElement("span", { className: "text-blue-400" }, memoryInfo.total, " MB")), /* @__PURE__ */ React21.createElement("div", { className: "flex items-center justify-between text-[10px]" }, /* @__PURE__ */ React21.createElement("span", { className: "text-gray-400" }, "\u9650\u5236:"), /* @__PURE__ */ React21.createElement("span", { className: "text-gray-400" }, memoryInfo.limit, " MB")), /* @__PURE__ */ React21.createElement("div", { className: "mt-2" }, /* @__PURE__ */ React21.createElement("div", { className: "bg-gray-700 rounded-full h-2 overflow-hidden" }, /* @__PURE__ */ React21.createElement(
|
|
1547
3056
|
"div",
|
|
1548
3057
|
{
|
|
1549
3058
|
className: `h-full transition-all duration-300 ${parseFloat(memoryInfo.used) / parseFloat(memoryInfo.limit) * 100 > 80 ? "bg-red-500" : parseFloat(memoryInfo.used) / parseFloat(memoryInfo.limit) * 100 > 60 ? "bg-yellow-500" : "bg-green-500"}`,
|
|
@@ -1551,9 +3060,9 @@ var MMDPlayerEnhancedDebugInfo = ({
|
|
|
1551
3060
|
width: `${Math.min(100, parseFloat(memoryInfo.used) / parseFloat(memoryInfo.limit) * 100)}%`
|
|
1552
3061
|
}
|
|
1553
3062
|
}
|
|
1554
|
-
)), /* @__PURE__ */
|
|
3063
|
+
)), /* @__PURE__ */ React21.createElement("div", { className: "text-[9px] text-gray-500 mt-1 text-center" }, (parseFloat(memoryInfo.used) / parseFloat(memoryInfo.limit) * 100).toFixed(1), "%")))), /* @__PURE__ */ React21.createElement("div", { className: "mt-auto pt-4 border-t border-gray-700" }, /* @__PURE__ */ React21.createElement("div", { className: "text-gray-500 text-[10px]" }, "Last Update: ", (/* @__PURE__ */ new Date()).toLocaleTimeString())));
|
|
1555
3064
|
};
|
|
1556
|
-
var StatusBadge = ({ active, label }) => /* @__PURE__ */
|
|
3065
|
+
var StatusBadge = ({ active, label }) => /* @__PURE__ */ React21.createElement(
|
|
1557
3066
|
"span",
|
|
1558
3067
|
{
|
|
1559
3068
|
className: `px-2 py-0.5 rounded text-[10px] font-bold ${active ? "bg-green-600 text-white" : "bg-gray-700 text-gray-400"}`
|
|
@@ -1682,16 +3191,16 @@ var MMDPlayerEnhanced = ({
|
|
|
1682
3191
|
});
|
|
1683
3192
|
};
|
|
1684
3193
|
if (!currentResources) {
|
|
1685
|
-
return /* @__PURE__ */
|
|
3194
|
+
return /* @__PURE__ */ React21.createElement("div", { className: "flex h-full w-full items-center justify-center bg-black text-white" }, "No Resources Configured");
|
|
1686
3195
|
}
|
|
1687
|
-
return /* @__PURE__ */
|
|
3196
|
+
return /* @__PURE__ */ React21.createElement(
|
|
1688
3197
|
"div",
|
|
1689
3198
|
{
|
|
1690
3199
|
ref: containerRef,
|
|
1691
3200
|
className: `relative overflow-hidden bg-black group flex ${className}`,
|
|
1692
3201
|
style
|
|
1693
3202
|
},
|
|
1694
|
-
/* @__PURE__ */
|
|
3203
|
+
/* @__PURE__ */ React21.createElement("div", { className: "flex-1 relative" }, /* @__PURE__ */ React21.createElement(
|
|
1695
3204
|
MMDPlayerBase,
|
|
1696
3205
|
{
|
|
1697
3206
|
key: mode === "list" ? currentId : JSON.stringify(currentResources),
|
|
@@ -1723,7 +3232,7 @@ var MMDPlayerEnhanced = ({
|
|
|
1723
3232
|
},
|
|
1724
3233
|
...rest
|
|
1725
3234
|
}
|
|
1726
|
-
), isLoading && /* @__PURE__ */
|
|
3235
|
+
), isLoading && /* @__PURE__ */ React21.createElement("div", { className: "absolute inset-0 z-10 flex items-center justify-center bg-black/50 backdrop-blur-sm" }, /* @__PURE__ */ React21.createElement("div", { className: "h-10 w-10 animate-spin rounded-full border-4 border-white/20 border-t-blue-500" })), /* @__PURE__ */ React21.createElement("div", { className: `transition-opacity duration-300 ${isPlaying && !showSettings ? "opacity-0 group-hover:opacity-100" : "opacity-100"}` }, /* @__PURE__ */ React21.createElement(
|
|
1727
3236
|
ControlPanel,
|
|
1728
3237
|
{
|
|
1729
3238
|
isPlaying,
|
|
@@ -1738,7 +3247,7 @@ var MMDPlayerEnhanced = ({
|
|
|
1738
3247
|
onToggleAxes: () => setShowAxes(!showAxes),
|
|
1739
3248
|
onOpenSettings: () => setShowSettings(true)
|
|
1740
3249
|
}
|
|
1741
|
-
)), showSettings && (mode === "list" || mode === "options") && /* @__PURE__ */
|
|
3250
|
+
)), showSettings && (mode === "list" || mode === "options") && /* @__PURE__ */ React21.createElement(
|
|
1742
3251
|
SettingsPanel,
|
|
1743
3252
|
{
|
|
1744
3253
|
mode,
|
|
@@ -1751,7 +3260,7 @@ var MMDPlayerEnhanced = ({
|
|
|
1751
3260
|
onClose: () => setShowSettings(false)
|
|
1752
3261
|
}
|
|
1753
3262
|
)),
|
|
1754
|
-
showDebugInfo && /* @__PURE__ */
|
|
3263
|
+
showDebugInfo && /* @__PURE__ */ React21.createElement("div", { className: "w-96 bg-gray-900/95 border-l border-gray-700 p-4 overflow-y-auto" }, /* @__PURE__ */ React21.createElement(
|
|
1755
3264
|
MMDPlayerEnhancedDebugInfo,
|
|
1756
3265
|
{
|
|
1757
3266
|
isPlaying,
|
|
@@ -1793,14 +3302,14 @@ var MMDPlaylistDebugInfo = ({
|
|
|
1793
3302
|
}, 1e3);
|
|
1794
3303
|
return () => clearInterval(timer);
|
|
1795
3304
|
}, []);
|
|
1796
|
-
return /* @__PURE__ */
|
|
3305
|
+
return /* @__PURE__ */ React21.createElement("div", { className: "text-white text-xs font-mono" }, /* @__PURE__ */ React21.createElement("h3", { className: "text-sm font-bold mb-3 pb-2 border-b border-gray-700" }, "\u{1F3AD} MMDPlaylist Debug"), /* @__PURE__ */ React21.createElement("div", { className: "mb-4" }, /* @__PURE__ */ React21.createElement("h4", { className: "text-gray-400 mb-2" }, "\u64AD\u653E\u5217\u8868"), /* @__PURE__ */ React21.createElement("div", { className: "space-y-1 pl-2" }, /* @__PURE__ */ React21.createElement("div", { className: "text-white truncate" }, playlistName), /* @__PURE__ */ React21.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ React21.createElement("span", { className: "text-gray-400" }, "\u8FDB\u5EA6:"), /* @__PURE__ */ React21.createElement("span", { className: "text-blue-400" }, currentIndex + 1, " / ", totalNodes)), /* @__PURE__ */ React21.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ React21.createElement("span", { className: "text-gray-400" }, "\u5217\u8868\u5FAA\u73AF:"), /* @__PURE__ */ React21.createElement(StatusBadge2, { active: isListLooping, label: isListLooping ? "On" : "Off" })))), /* @__PURE__ */ React21.createElement("div", { className: "mb-4" }, /* @__PURE__ */ React21.createElement("h4", { className: "text-gray-400 mb-2" }, "\u5F53\u524D\u8282\u70B9"), /* @__PURE__ */ React21.createElement("div", { className: "p-2 bg-gray-800 rounded space-y-1" }, /* @__PURE__ */ React21.createElement("div", { className: "text-white font-semibold truncate" }, currentNode.name), /* @__PURE__ */ React21.createElement("div", { className: "text-gray-500 text-[10px] truncate" }, "ID: ", currentNode.id), currentNode.duration && /* @__PURE__ */ React21.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ React21.createElement("span", { className: "text-gray-400" }, "\u65F6\u957F:"), /* @__PURE__ */ React21.createElement("span", { className: "text-green-400" }, currentNode.duration, "s")), /* @__PURE__ */ React21.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ React21.createElement("span", { className: "text-gray-400" }, "\u8282\u70B9\u5FAA\u73AF:"), /* @__PURE__ */ React21.createElement(StatusBadge2, { active: isNodeLooping, label: isNodeLooping ? "On" : "Off" })))), /* @__PURE__ */ React21.createElement("div", { className: "mb-4" }, /* @__PURE__ */ React21.createElement("h4", { className: "text-gray-400 mb-2" }, "\u64AD\u653E\u72B6\u6001"), /* @__PURE__ */ React21.createElement("div", { className: "space-y-1 pl-2" }, /* @__PURE__ */ React21.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ React21.createElement("span", { className: "text-gray-400" }, "\u64AD\u653E\u4E2D:"), /* @__PURE__ */ React21.createElement(StatusBadge2, { active: isPlaying, label: isPlaying ? "Playing" : "Paused" })), /* @__PURE__ */ React21.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ React21.createElement("span", { className: "text-gray-400" }, "\u52A0\u8F7D\u4E2D:"), /* @__PURE__ */ React21.createElement(StatusBadge2, { active: isLoading, label: isLoading ? "Loading" : "Ready" })))), /* @__PURE__ */ React21.createElement("div", { className: "mb-4" }, /* @__PURE__ */ React21.createElement("h4", { className: "text-gray-400 mb-2" }, "\u9884\u52A0\u8F7D\u7B56\u7565"), /* @__PURE__ */ React21.createElement("div", { className: "space-y-1 pl-2" }, /* @__PURE__ */ React21.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ React21.createElement("span", { className: "text-gray-400" }, "\u7B56\u7565:"), /* @__PURE__ */ React21.createElement("span", { className: `px-2 py-0.5 rounded text-[10px] font-bold uppercase ${preloadStrategy === "all" ? "bg-red-600 text-white" : preloadStrategy === "next" ? "bg-yellow-600 text-white" : "bg-gray-700 text-gray-400"}` }, preloadStrategy)), /* @__PURE__ */ React21.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ React21.createElement("span", { className: "text-gray-400" }, "\u5DF2\u9884\u52A0\u8F7D:"), /* @__PURE__ */ React21.createElement("span", { className: "text-purple-400" }, preloadedNodes.length)), preloadedNodes.length > 0 && /* @__PURE__ */ React21.createElement("div", { className: "mt-2 p-2 bg-gray-800 rounded" }, /* @__PURE__ */ React21.createElement("div", { className: "text-gray-400 text-[10px] mb-1" }, "\u9884\u52A0\u8F7D\u8282\u70B9\u7D22\u5F15"), /* @__PURE__ */ React21.createElement("div", { className: "flex flex-wrap gap-1" }, preloadedNodes.map((idx) => /* @__PURE__ */ React21.createElement(
|
|
1797
3306
|
"span",
|
|
1798
3307
|
{
|
|
1799
3308
|
key: idx,
|
|
1800
3309
|
className: `px-1.5 py-0.5 rounded text-[10px] ${idx === currentIndex ? "bg-green-600 text-white font-bold" : "bg-gray-700 text-gray-300"}`
|
|
1801
3310
|
},
|
|
1802
3311
|
idx
|
|
1803
|
-
)))))), /* @__PURE__ */
|
|
3312
|
+
)))))), /* @__PURE__ */ React21.createElement("div", { className: "mb-4" }, /* @__PURE__ */ React21.createElement("h4", { className: "text-gray-400 mb-2" }, "\u89C6\u56FE\u72B6\u6001"), /* @__PURE__ */ React21.createElement("div", { className: "space-y-1 pl-2" }, /* @__PURE__ */ React21.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ React21.createElement("span", { className: "text-gray-400" }, "\u5168\u5C4F:"), /* @__PURE__ */ React21.createElement(StatusBadge2, { active: isFullscreen, label: isFullscreen ? "Yes" : "No" })), /* @__PURE__ */ React21.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ React21.createElement("span", { className: "text-gray-400" }, "\u5750\u6807\u8F74:"), /* @__PURE__ */ React21.createElement(StatusBadge2, { active: showAxes, label: showAxes ? "Show" : "Hide" })))), memoryInfo && /* @__PURE__ */ React21.createElement("div", { className: "mb-4" }, /* @__PURE__ */ React21.createElement("h4", { className: "text-gray-400 mb-2" }, "\u5185\u5B58\u76D1\u63A7 (Chrome only)"), /* @__PURE__ */ React21.createElement("div", { className: "space-y-2 p-2 bg-gray-800 rounded" }, /* @__PURE__ */ React21.createElement("div", { className: "flex items-center justify-between text-[10px]" }, /* @__PURE__ */ React21.createElement("span", { className: "text-gray-400" }, "\u5DF2\u7528:"), /* @__PURE__ */ React21.createElement("span", { className: "text-yellow-400 font-bold" }, memoryInfo.used, " MB")), /* @__PURE__ */ React21.createElement("div", { className: "flex items-center justify-between text-[10px]" }, /* @__PURE__ */ React21.createElement("span", { className: "text-gray-400" }, "\u603B\u8BA1:"), /* @__PURE__ */ React21.createElement("span", { className: "text-blue-400" }, memoryInfo.total, " MB")), /* @__PURE__ */ React21.createElement("div", { className: "flex items-center justify-between text-[10px]" }, /* @__PURE__ */ React21.createElement("span", { className: "text-gray-400" }, "\u9650\u5236:"), /* @__PURE__ */ React21.createElement("span", { className: "text-gray-400" }, memoryInfo.limit, " MB")), /* @__PURE__ */ React21.createElement("div", { className: "mt-2" }, /* @__PURE__ */ React21.createElement("div", { className: "bg-gray-700 rounded-full h-2 overflow-hidden" }, /* @__PURE__ */ React21.createElement(
|
|
1804
3313
|
"div",
|
|
1805
3314
|
{
|
|
1806
3315
|
className: `h-full transition-all duration-300 ${parseFloat(memoryInfo.used) / parseFloat(memoryInfo.limit) * 100 > 80 ? "bg-red-500" : parseFloat(memoryInfo.used) / parseFloat(memoryInfo.limit) * 100 > 60 ? "bg-yellow-500" : "bg-green-500"}`,
|
|
@@ -1808,18 +3317,18 @@ var MMDPlaylistDebugInfo = ({
|
|
|
1808
3317
|
width: `${Math.min(100, parseFloat(memoryInfo.used) / parseFloat(memoryInfo.limit) * 100)}%`
|
|
1809
3318
|
}
|
|
1810
3319
|
}
|
|
1811
|
-
)), /* @__PURE__ */
|
|
3320
|
+
)), /* @__PURE__ */ React21.createElement("div", { className: "text-[9px] text-gray-500 mt-1 text-center" }, (parseFloat(memoryInfo.used) / parseFloat(memoryInfo.limit) * 100).toFixed(1), "%")))), /* @__PURE__ */ React21.createElement("div", { className: "mb-4" }, /* @__PURE__ */ React21.createElement("h4", { className: "text-gray-400 mb-2" }, "\u8282\u70B9\u5217\u8868"), /* @__PURE__ */ React21.createElement("div", { className: "space-y-1 max-h-40 overflow-y-auto" }, Array.from({ length: totalNodes }).map((_, idx) => /* @__PURE__ */ React21.createElement(
|
|
1812
3321
|
"div",
|
|
1813
3322
|
{
|
|
1814
3323
|
key: idx,
|
|
1815
3324
|
className: `px-2 py-1 rounded text-[10px] flex items-center justify-between ${idx === currentIndex ? "bg-blue-600 text-white font-bold" : preloadedNodes.includes(idx) ? "bg-yellow-900/50 text-yellow-300" : "bg-gray-800 text-gray-400"}`
|
|
1816
3325
|
},
|
|
1817
|
-
/* @__PURE__ */
|
|
1818
|
-
idx === currentIndex && /* @__PURE__ */
|
|
1819
|
-
preloadedNodes.includes(idx) && idx !== currentIndex && /* @__PURE__ */
|
|
1820
|
-
)))), /* @__PURE__ */
|
|
3326
|
+
/* @__PURE__ */ React21.createElement("span", null, "\u8282\u70B9 ", idx),
|
|
3327
|
+
idx === currentIndex && /* @__PURE__ */ React21.createElement("span", null, "\u25B6"),
|
|
3328
|
+
preloadedNodes.includes(idx) && idx !== currentIndex && /* @__PURE__ */ React21.createElement("span", null, "\u23F3")
|
|
3329
|
+
)))), /* @__PURE__ */ React21.createElement("div", { className: "mt-auto pt-4 border-t border-gray-700" }, /* @__PURE__ */ React21.createElement("div", { className: "text-gray-500 text-[10px]" }, "Last Update: ", (/* @__PURE__ */ new Date()).toLocaleTimeString())));
|
|
1821
3330
|
};
|
|
1822
|
-
var StatusBadge2 = ({ active, label }) => /* @__PURE__ */
|
|
3331
|
+
var StatusBadge2 = ({ active, label }) => /* @__PURE__ */ React21.createElement(
|
|
1823
3332
|
"span",
|
|
1824
3333
|
{
|
|
1825
3334
|
className: `px-2 py-0.5 rounded text-[10px] font-bold ${active ? "bg-green-600 text-white" : "bg-gray-700 text-gray-400"}`
|
|
@@ -1985,17 +3494,17 @@ var MMDPlaylist = ({
|
|
|
1985
3494
|
};
|
|
1986
3495
|
}, []);
|
|
1987
3496
|
if (!currentNode) {
|
|
1988
|
-
return /* @__PURE__ */
|
|
3497
|
+
return /* @__PURE__ */ React21.createElement("div", { className: "flex h-full w-full items-center justify-center bg-black text-white" }, "\u64AD\u653E\u5217\u8868\u4E3A\u7A7A");
|
|
1989
3498
|
}
|
|
1990
3499
|
const showPrevNext = nodes.length > 1;
|
|
1991
|
-
return /* @__PURE__ */
|
|
3500
|
+
return /* @__PURE__ */ React21.createElement(
|
|
1992
3501
|
"div",
|
|
1993
3502
|
{
|
|
1994
3503
|
ref: containerRef,
|
|
1995
3504
|
className: `relative overflow-hidden bg-black group flex h-full ${className}`,
|
|
1996
3505
|
style
|
|
1997
3506
|
},
|
|
1998
|
-
/* @__PURE__ */
|
|
3507
|
+
/* @__PURE__ */ React21.createElement("div", { className: "flex-1 relative" }, !isTransitioning && /* @__PURE__ */ React21.createElement(
|
|
1999
3508
|
MMDPlayerBase,
|
|
2000
3509
|
{
|
|
2001
3510
|
key: currentNode.id,
|
|
@@ -2018,12 +3527,12 @@ var MMDPlaylist = ({
|
|
|
2018
3527
|
onEnded: handleEnded,
|
|
2019
3528
|
onError
|
|
2020
3529
|
}
|
|
2021
|
-
), (isLoading || isTransitioning) && /* @__PURE__ */
|
|
3530
|
+
), (isLoading || isTransitioning) && /* @__PURE__ */ React21.createElement("div", { className: "absolute inset-0 z-10 flex items-center justify-center bg-black/50 backdrop-blur-sm" }, /* @__PURE__ */ React21.createElement("div", { className: "flex flex-col items-center gap-3" }, /* @__PURE__ */ React21.createElement("div", { className: "h-10 w-10 animate-spin rounded-full border-4 border-white/20 border-t-blue-500" }), /* @__PURE__ */ React21.createElement("div", { className: "text-sm text-white/80" }, isTransitioning ? "\u5207\u6362\u4E2D..." : `\u6B63\u5728\u52A0\u8F7D ${currentIndex + 1} / ${nodes.length}`))), /* @__PURE__ */ React21.createElement(
|
|
2022
3531
|
"div",
|
|
2023
3532
|
{
|
|
2024
3533
|
className: `transition-opacity duration-300 ${isPlaying && !showPlaylist ? "opacity-0 group-hover:opacity-100" : "opacity-100"}`
|
|
2025
3534
|
},
|
|
2026
|
-
/* @__PURE__ */
|
|
3535
|
+
/* @__PURE__ */ React21.createElement(
|
|
2027
3536
|
ControlPanel,
|
|
2028
3537
|
{
|
|
2029
3538
|
isPlaying,
|
|
@@ -2050,15 +3559,15 @@ var MMDPlaylist = ({
|
|
|
2050
3559
|
}
|
|
2051
3560
|
}
|
|
2052
3561
|
)
|
|
2053
|
-
), showPlaylist && /* @__PURE__ */
|
|
3562
|
+
), showPlaylist && /* @__PURE__ */ React21.createElement("div", { className: "absolute inset-0 z-20 flex items-end bg-black/80 backdrop-blur-sm" }, /* @__PURE__ */ React21.createElement("div", { className: "w-full max-h-[60vh] overflow-y-auto bg-gray-900/95 rounded-t-xl" }, /* @__PURE__ */ React21.createElement("div", { className: "sticky top-0 flex items-center justify-between bg-gray-800 px-4 py-3 border-b border-gray-700" }, /* @__PURE__ */ React21.createElement("div", null, /* @__PURE__ */ React21.createElement("h3", { className: "text-white font-semibold" }, playlist.name), /* @__PURE__ */ React21.createElement("p", { className: "text-xs text-gray-400 mt-0.5" }, "\u5171 ", nodes.length, " \u4E2A\u8282\u70B9")), /* @__PURE__ */ React21.createElement(
|
|
2054
3563
|
"button",
|
|
2055
3564
|
{
|
|
2056
3565
|
onClick: () => setShowPlaylist(false),
|
|
2057
3566
|
className: "p-2 hover:bg-white/10 rounded-lg transition-colors",
|
|
2058
3567
|
"aria-label": "\u5173\u95ED\u64AD\u653E\u5217\u8868"
|
|
2059
3568
|
},
|
|
2060
|
-
/* @__PURE__ */
|
|
2061
|
-
)), /* @__PURE__ */
|
|
3569
|
+
/* @__PURE__ */ React21.createElement("svg", { className: "w-5 h-5 text-white", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor" }, /* @__PURE__ */ React21.createElement("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M6 18L18 6M6 6l12 12" }))
|
|
3570
|
+
)), /* @__PURE__ */ React21.createElement("div", { className: "p-2" }, nodes.map((node, index) => /* @__PURE__ */ React21.createElement(
|
|
2062
3571
|
"button",
|
|
2063
3572
|
{
|
|
2064
3573
|
key: node.id,
|
|
@@ -2068,17 +3577,17 @@ var MMDPlaylist = ({
|
|
|
2068
3577
|
},
|
|
2069
3578
|
className: `w-full flex items-center gap-3 p-3 rounded-lg mb-2 transition-all ${index === currentIndex ? "bg-blue-600 text-white" : "bg-gray-800 text-gray-300 hover:bg-gray-700"}`
|
|
2070
3579
|
},
|
|
2071
|
-
/* @__PURE__ */
|
|
3580
|
+
/* @__PURE__ */ React21.createElement(
|
|
2072
3581
|
"div",
|
|
2073
3582
|
{
|
|
2074
3583
|
className: `flex-shrink-0 w-8 h-8 rounded-full flex items-center justify-center text-sm font-semibold ${index === currentIndex ? "bg-white/20" : "bg-gray-700"}`
|
|
2075
3584
|
},
|
|
2076
3585
|
index + 1
|
|
2077
3586
|
),
|
|
2078
|
-
/* @__PURE__ */
|
|
2079
|
-
index === currentIndex && /* @__PURE__ */
|
|
3587
|
+
/* @__PURE__ */ React21.createElement("div", { className: "flex-1 text-left" }, /* @__PURE__ */ React21.createElement("div", { className: "font-medium" }, node.name), node.duration && /* @__PURE__ */ React21.createElement("div", { className: "text-xs opacity-75 mt-0.5" }, Math.floor(node.duration / 60), ":", String(Math.floor(node.duration % 60)).padStart(2, "0"))),
|
|
3588
|
+
index === currentIndex && /* @__PURE__ */ React21.createElement("div", { className: "flex-shrink-0" }, /* @__PURE__ */ React21.createElement("svg", { className: "w-5 h-5", fill: "currentColor", viewBox: "0 0 24 24" }, /* @__PURE__ */ React21.createElement("path", { d: "M8 5v14l11-7z" })))
|
|
2080
3589
|
)))))),
|
|
2081
|
-
showDebugInfo && /* @__PURE__ */
|
|
3590
|
+
showDebugInfo && /* @__PURE__ */ React21.createElement("div", { className: "w-96 flex-shrink-0 bg-gray-900/95 border-l border-gray-700 p-4 overflow-y-auto h-full" }, /* @__PURE__ */ React21.createElement(
|
|
2082
3591
|
MMDPlaylistDebugInfo,
|
|
2083
3592
|
{
|
|
2084
3593
|
playlistName: playlist.name,
|
|
@@ -2224,7 +3733,7 @@ var DialogueBox = ({
|
|
|
2224
3733
|
displayedText,
|
|
2225
3734
|
isComplete
|
|
2226
3735
|
});
|
|
2227
|
-
const dialogueContent = /* @__PURE__ */
|
|
3736
|
+
const dialogueContent = /* @__PURE__ */ React21.createElement(
|
|
2228
3737
|
"div",
|
|
2229
3738
|
{
|
|
2230
3739
|
className: `${className || ""}`,
|
|
@@ -2240,7 +3749,7 @@ var DialogueBox = ({
|
|
|
2240
3749
|
pointerEvents: "auto"
|
|
2241
3750
|
}
|
|
2242
3751
|
},
|
|
2243
|
-
/* @__PURE__ */
|
|
3752
|
+
/* @__PURE__ */ React21.createElement(
|
|
2244
3753
|
"div",
|
|
2245
3754
|
{
|
|
2246
3755
|
className: "w-full h-full rounded-t-3xl border cursor-pointer select-none transition-all hover:border-white/50 hover:shadow-2xl flex flex-col relative overflow-hidden",
|
|
@@ -2265,7 +3774,7 @@ var DialogueBox = ({
|
|
|
2265
3774
|
`
|
|
2266
3775
|
}
|
|
2267
3776
|
},
|
|
2268
|
-
/* @__PURE__ */
|
|
3777
|
+
/* @__PURE__ */ React21.createElement(
|
|
2269
3778
|
"div",
|
|
2270
3779
|
{
|
|
2271
3780
|
className: "absolute inset-0 opacity-20 pointer-events-none",
|
|
@@ -2281,7 +3790,7 @@ var DialogueBox = ({
|
|
|
2281
3790
|
}
|
|
2282
3791
|
}
|
|
2283
3792
|
),
|
|
2284
|
-
/* @__PURE__ */
|
|
3793
|
+
/* @__PURE__ */ React21.createElement(
|
|
2285
3794
|
"div",
|
|
2286
3795
|
{
|
|
2287
3796
|
className: "absolute top-0 left-0 right-0 h-1",
|
|
@@ -2290,7 +3799,7 @@ var DialogueBox = ({
|
|
|
2290
3799
|
}
|
|
2291
3800
|
}
|
|
2292
3801
|
),
|
|
2293
|
-
/* @__PURE__ */
|
|
3802
|
+
/* @__PURE__ */ React21.createElement(
|
|
2294
3803
|
"div",
|
|
2295
3804
|
{
|
|
2296
3805
|
className: "absolute bottom-0 left-0 right-0 h-px",
|
|
@@ -2299,7 +3808,7 @@ var DialogueBox = ({
|
|
|
2299
3808
|
}
|
|
2300
3809
|
}
|
|
2301
3810
|
),
|
|
2302
|
-
showControls && /* @__PURE__ */
|
|
3811
|
+
showControls && /* @__PURE__ */ React21.createElement("div", { className: "flex justify-end gap-3 px-6 pt-4 pb-2 flex-shrink-0 relative z-10" }, showHistoryButton && /* @__PURE__ */ React21.createElement(
|
|
2303
3812
|
"button",
|
|
2304
3813
|
{
|
|
2305
3814
|
onClick: (e) => {
|
|
@@ -2314,7 +3823,7 @@ var DialogueBox = ({
|
|
|
2314
3823
|
title: "\u5386\u53F2\u8BB0\u5F55"
|
|
2315
3824
|
},
|
|
2316
3825
|
"\u{1F4DC} \u5386\u53F2"
|
|
2317
|
-
), isCameraManual && /* @__PURE__ */
|
|
3826
|
+
), isCameraManual && /* @__PURE__ */ React21.createElement(
|
|
2318
3827
|
"button",
|
|
2319
3828
|
{
|
|
2320
3829
|
onClick: (e) => {
|
|
@@ -2329,7 +3838,7 @@ var DialogueBox = ({
|
|
|
2329
3838
|
title: "\u6062\u590D\u521D\u59CB\u89C6\u89D2"
|
|
2330
3839
|
},
|
|
2331
3840
|
"\u{1F3A5} \u6062\u590D\u89C6\u89D2"
|
|
2332
|
-
), showAutoButton && /* @__PURE__ */
|
|
3841
|
+
), showAutoButton && /* @__PURE__ */ React21.createElement(
|
|
2333
3842
|
"button",
|
|
2334
3843
|
{
|
|
2335
3844
|
onClick: (e) => {
|
|
@@ -2344,7 +3853,7 @@ var DialogueBox = ({
|
|
|
2344
3853
|
title: "\u81EA\u52A8\u64AD\u653E"
|
|
2345
3854
|
},
|
|
2346
3855
|
"\u25B6 \u81EA\u52A8"
|
|
2347
|
-
), showSkipButton && /* @__PURE__ */
|
|
3856
|
+
), showSkipButton && /* @__PURE__ */ React21.createElement(
|
|
2348
3857
|
"button",
|
|
2349
3858
|
{
|
|
2350
3859
|
onClick: (e) => {
|
|
@@ -2360,7 +3869,7 @@ var DialogueBox = ({
|
|
|
2360
3869
|
},
|
|
2361
3870
|
"\u23E9 \u5FEB\u8FDB"
|
|
2362
3871
|
)),
|
|
2363
|
-
/* @__PURE__ */
|
|
3872
|
+
/* @__PURE__ */ React21.createElement("div", { className: "px-8 pb-6 flex-1 flex flex-col justify-center overflow-y-auto relative z-10" }, dialogue.speaker && /* @__PURE__ */ React21.createElement(
|
|
2364
3873
|
"div",
|
|
2365
3874
|
{
|
|
2366
3875
|
className: "inline-block px-6 py-2.5 rounded-2xl mb-4 text-sm font-bold shadow-2xl self-start relative overflow-hidden transition-all hover:scale-105",
|
|
@@ -2375,7 +3884,7 @@ var DialogueBox = ({
|
|
|
2375
3884
|
border: "1px solid rgba(255, 255, 255, 0.3)"
|
|
2376
3885
|
}
|
|
2377
3886
|
},
|
|
2378
|
-
/* @__PURE__ */
|
|
3887
|
+
/* @__PURE__ */ React21.createElement(
|
|
2379
3888
|
"div",
|
|
2380
3889
|
{
|
|
2381
3890
|
className: "absolute inset-0 opacity-30",
|
|
@@ -2386,8 +3895,8 @@ var DialogueBox = ({
|
|
|
2386
3895
|
}
|
|
2387
3896
|
}
|
|
2388
3897
|
),
|
|
2389
|
-
/* @__PURE__ */
|
|
2390
|
-
), /* @__PURE__ */
|
|
3898
|
+
/* @__PURE__ */ React21.createElement("span", { className: "relative z-10 drop-shadow-lg" }, dialogue.speaker)
|
|
3899
|
+
), /* @__PURE__ */ React21.createElement(
|
|
2391
3900
|
"div",
|
|
2392
3901
|
{
|
|
2393
3902
|
className: "text-lg leading-relaxed relative",
|
|
@@ -2396,10 +3905,10 @@ var DialogueBox = ({
|
|
|
2396
3905
|
textShadow: "0 2px 8px rgba(0, 0, 0, 0.5)"
|
|
2397
3906
|
}
|
|
2398
3907
|
},
|
|
2399
|
-
/* @__PURE__ */
|
|
3908
|
+
/* @__PURE__ */ React21.createElement("span", { className: "inline-block", style: {
|
|
2400
3909
|
animation: !isComplete ? "textFadeIn 0.3s ease-out" : "none"
|
|
2401
3910
|
} }, displayedText),
|
|
2402
|
-
!isComplete && /* @__PURE__ */
|
|
3911
|
+
!isComplete && /* @__PURE__ */ React21.createElement(
|
|
2403
3912
|
"span",
|
|
2404
3913
|
{
|
|
2405
3914
|
className: "inline-block w-1 h-6 ml-1 align-middle",
|
|
@@ -2410,7 +3919,7 @@ var DialogueBox = ({
|
|
|
2410
3919
|
}
|
|
2411
3920
|
}
|
|
2412
3921
|
)
|
|
2413
|
-
), isComplete && theme.showContinueHint && /* @__PURE__ */
|
|
3922
|
+
), isComplete && theme.showContinueHint && /* @__PURE__ */ React21.createElement("div", { className: "flex justify-end mt-4" }, /* @__PURE__ */ React21.createElement(
|
|
2414
3923
|
"span",
|
|
2415
3924
|
{
|
|
2416
3925
|
className: "text-sm px-4 py-2 rounded-full backdrop-blur-lg font-medium",
|
|
@@ -2448,7 +3957,7 @@ var HistoryPanel = ({
|
|
|
2448
3957
|
useEffect(() => {
|
|
2449
3958
|
setIsMounted(true);
|
|
2450
3959
|
}, []);
|
|
2451
|
-
const historyContent = /* @__PURE__ */
|
|
3960
|
+
const historyContent = /* @__PURE__ */ React21.createElement(
|
|
2452
3961
|
"div",
|
|
2453
3962
|
{
|
|
2454
3963
|
className: `fixed inset-0 flex flex-col ${className}`,
|
|
@@ -2459,7 +3968,7 @@ var HistoryPanel = ({
|
|
|
2459
3968
|
},
|
|
2460
3969
|
onClick: onClose
|
|
2461
3970
|
},
|
|
2462
|
-
/* @__PURE__ */
|
|
3971
|
+
/* @__PURE__ */ React21.createElement(
|
|
2463
3972
|
"div",
|
|
2464
3973
|
{
|
|
2465
3974
|
className: "absolute inset-0 opacity-25 pointer-events-none",
|
|
@@ -2475,7 +3984,7 @@ var HistoryPanel = ({
|
|
|
2475
3984
|
}
|
|
2476
3985
|
}
|
|
2477
3986
|
),
|
|
2478
|
-
/* @__PURE__ */
|
|
3987
|
+
/* @__PURE__ */ React21.createElement(
|
|
2479
3988
|
"div",
|
|
2480
3989
|
{
|
|
2481
3990
|
className: "absolute inset-0 pointer-events-none",
|
|
@@ -2485,7 +3994,7 @@ var HistoryPanel = ({
|
|
|
2485
3994
|
}
|
|
2486
3995
|
}
|
|
2487
3996
|
),
|
|
2488
|
-
/* @__PURE__ */
|
|
3997
|
+
/* @__PURE__ */ React21.createElement("div", { className: "relative z-10 flex flex-col h-full", onClick: (e) => e.stopPropagation() }, /* @__PURE__ */ React21.createElement(
|
|
2489
3998
|
"div",
|
|
2490
3999
|
{
|
|
2491
4000
|
className: "flex items-center justify-between px-8 py-6 border-b relative",
|
|
@@ -2497,8 +4006,8 @@ var HistoryPanel = ({
|
|
|
2497
4006
|
boxShadow: "0 4px 24px rgba(255, 255, 255, 0.1), inset 0 1px 0 rgba(255, 255, 255, 0.3)"
|
|
2498
4007
|
}
|
|
2499
4008
|
},
|
|
2500
|
-
/* @__PURE__ */
|
|
2501
|
-
/* @__PURE__ */
|
|
4009
|
+
/* @__PURE__ */ React21.createElement("h2", { className: "text-2xl font-bold text-white flex items-center gap-3" }, /* @__PURE__ */ React21.createElement("span", { className: "text-3xl" }, "\u{1F4DC}"), /* @__PURE__ */ React21.createElement("span", { style: { textShadow: "0 2px 12px rgba(255, 255, 255, 0.3)" } }, "\u5BF9\u8BDD\u5386\u53F2")),
|
|
4010
|
+
/* @__PURE__ */ React21.createElement(
|
|
2502
4011
|
"button",
|
|
2503
4012
|
{
|
|
2504
4013
|
onClick: onClose,
|
|
@@ -2512,9 +4021,9 @@ var HistoryPanel = ({
|
|
|
2512
4021
|
},
|
|
2513
4022
|
"aria-label": "\u5173\u95ED"
|
|
2514
4023
|
},
|
|
2515
|
-
/* @__PURE__ */
|
|
4024
|
+
/* @__PURE__ */ React21.createElement("svg", { className: "w-6 h-6", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor" }, /* @__PURE__ */ React21.createElement("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M6 18L18 6M6 6l12 12" }))
|
|
2516
4025
|
)
|
|
2517
|
-
), /* @__PURE__ */
|
|
4026
|
+
), /* @__PURE__ */ React21.createElement("div", { className: "flex-1 overflow-y-auto p-6 space-y-4" }, history.length === 0 ? /* @__PURE__ */ React21.createElement("div", { className: "text-center text-white/70 py-20 text-lg font-medium", style: { textShadow: "0 2px 8px rgba(0, 0, 0, 0.3)" } }, "\u6682\u65E0\u5BF9\u8BDD\u5386\u53F2") : history.map((item, index) => /* @__PURE__ */ React21.createElement(
|
|
2518
4027
|
"div",
|
|
2519
4028
|
{
|
|
2520
4029
|
key: `${item.nodeIndex}-${item.dialogueIndex}-${index}`,
|
|
@@ -2527,7 +4036,7 @@ var HistoryPanel = ({
|
|
|
2527
4036
|
boxShadow: "0 8px 24px rgba(255, 255, 255, 0.08), inset 0 1px 0 rgba(255, 255, 255, 0.25)"
|
|
2528
4037
|
}
|
|
2529
4038
|
},
|
|
2530
|
-
/* @__PURE__ */
|
|
4039
|
+
/* @__PURE__ */ React21.createElement(
|
|
2531
4040
|
"div",
|
|
2532
4041
|
{
|
|
2533
4042
|
className: "absolute top-0 left-0 right-0 h-0.5",
|
|
@@ -2536,7 +4045,7 @@ var HistoryPanel = ({
|
|
|
2536
4045
|
}
|
|
2537
4046
|
}
|
|
2538
4047
|
),
|
|
2539
|
-
item.speaker && /* @__PURE__ */
|
|
4048
|
+
item.speaker && /* @__PURE__ */ React21.createElement(
|
|
2540
4049
|
"div",
|
|
2541
4050
|
{
|
|
2542
4051
|
className: "inline-block px-5 py-2 rounded-xl mb-3 text-sm font-bold shadow-lg relative overflow-hidden",
|
|
@@ -2549,7 +4058,7 @@ var HistoryPanel = ({
|
|
|
2549
4058
|
border: "1px solid rgba(255, 255, 255, 0.25)"
|
|
2550
4059
|
}
|
|
2551
4060
|
},
|
|
2552
|
-
/* @__PURE__ */
|
|
4061
|
+
/* @__PURE__ */ React21.createElement(
|
|
2553
4062
|
"div",
|
|
2554
4063
|
{
|
|
2555
4064
|
className: "absolute inset-0 opacity-30",
|
|
@@ -2560,9 +4069,9 @@ var HistoryPanel = ({
|
|
|
2560
4069
|
}
|
|
2561
4070
|
}
|
|
2562
4071
|
),
|
|
2563
|
-
/* @__PURE__ */
|
|
4072
|
+
/* @__PURE__ */ React21.createElement("span", { className: "relative z-10 drop-shadow-lg" }, item.speaker)
|
|
2564
4073
|
),
|
|
2565
|
-
/* @__PURE__ */
|
|
4074
|
+
/* @__PURE__ */ React21.createElement(
|
|
2566
4075
|
"p",
|
|
2567
4076
|
{
|
|
2568
4077
|
className: "text-base leading-relaxed",
|
|
@@ -2573,7 +4082,7 @@ var HistoryPanel = ({
|
|
|
2573
4082
|
},
|
|
2574
4083
|
item.text
|
|
2575
4084
|
)
|
|
2576
|
-
))), /* @__PURE__ */
|
|
4085
|
+
))), /* @__PURE__ */ React21.createElement(
|
|
2577
4086
|
"div",
|
|
2578
4087
|
{
|
|
2579
4088
|
className: "px-8 py-4 border-t text-center",
|
|
@@ -2585,7 +4094,7 @@ var HistoryPanel = ({
|
|
|
2585
4094
|
boxShadow: "inset 0 1px 0 rgba(255, 255, 255, 0.2)"
|
|
2586
4095
|
}
|
|
2587
4096
|
},
|
|
2588
|
-
/* @__PURE__ */
|
|
4097
|
+
/* @__PURE__ */ React21.createElement(
|
|
2589
4098
|
"span",
|
|
2590
4099
|
{
|
|
2591
4100
|
className: "text-sm px-5 py-2.5 rounded-full inline-block font-medium",
|
|
@@ -2635,7 +4144,7 @@ var LoadingScreen = ({
|
|
|
2635
4144
|
console.log("[LoadingScreen] Not showing, returning null");
|
|
2636
4145
|
return null;
|
|
2637
4146
|
}
|
|
2638
|
-
const content = /* @__PURE__ */
|
|
4147
|
+
const content = /* @__PURE__ */ React21.createElement(
|
|
2639
4148
|
"div",
|
|
2640
4149
|
{
|
|
2641
4150
|
className: `fixed inset-0 w-screen h-screen flex items-center justify-center ${className}`,
|
|
@@ -2647,7 +4156,7 @@ var LoadingScreen = ({
|
|
|
2647
4156
|
padding: 0
|
|
2648
4157
|
}
|
|
2649
4158
|
},
|
|
2650
|
-
/* @__PURE__ */
|
|
4159
|
+
/* @__PURE__ */ React21.createElement(
|
|
2651
4160
|
"div",
|
|
2652
4161
|
{
|
|
2653
4162
|
className: "absolute inset-0 w-full h-full pointer-events-none",
|
|
@@ -2657,7 +4166,7 @@ var LoadingScreen = ({
|
|
|
2657
4166
|
WebkitBackdropFilter: "blur(32px) saturate(200%)"
|
|
2658
4167
|
}
|
|
2659
4168
|
},
|
|
2660
|
-
/* @__PURE__ */
|
|
4169
|
+
/* @__PURE__ */ React21.createElement(
|
|
2661
4170
|
"div",
|
|
2662
4171
|
{
|
|
2663
4172
|
className: "flex items-center justify-center flex-col inset-0 w-full h-full pointer-events-none",
|
|
@@ -2672,7 +4181,7 @@ var LoadingScreen = ({
|
|
|
2672
4181
|
animation: "gradientShift 15s ease infinite"
|
|
2673
4182
|
}
|
|
2674
4183
|
},
|
|
2675
|
-
/* @__PURE__ */
|
|
4184
|
+
/* @__PURE__ */ React21.createElement(
|
|
2676
4185
|
"div",
|
|
2677
4186
|
{
|
|
2678
4187
|
className: "text-lg font-medium text-white text-center px-6 py-3 rounded-2xl"
|
|
@@ -2707,13 +4216,13 @@ var LoadingScreen = ({
|
|
|
2707
4216
|
LoadingScreen.displayName = "LoadingScreen";
|
|
2708
4217
|
var VNModal = ({ title, show, onClose, children }) => {
|
|
2709
4218
|
if (!show) return null;
|
|
2710
|
-
return /* @__PURE__ */
|
|
4219
|
+
return /* @__PURE__ */ React21.createElement(
|
|
2711
4220
|
"div",
|
|
2712
4221
|
{
|
|
2713
4222
|
className: "fixed inset-0 flex items-center justify-center bg-black/40 backdrop-blur-xl z-[1000000] pointer-events-auto transition-all animate-in fade-in zoom-in-95 duration-300",
|
|
2714
4223
|
onClick: onClose
|
|
2715
4224
|
},
|
|
2716
|
-
/* @__PURE__ */
|
|
4225
|
+
/* @__PURE__ */ React21.createElement(
|
|
2717
4226
|
"div",
|
|
2718
4227
|
{
|
|
2719
4228
|
className: "w-full max-w-lg mx-4 p-8 rounded-[2.5rem] border border-white/40 shadow-[0_20px_80px_rgba(0,0,0,0.4)] relative overflow-hidden",
|
|
@@ -2724,9 +4233,9 @@ var VNModal = ({ title, show, onClose, children }) => {
|
|
|
2724
4233
|
},
|
|
2725
4234
|
onClick: (e) => e.stopPropagation()
|
|
2726
4235
|
},
|
|
2727
|
-
/* @__PURE__ */
|
|
2728
|
-
/* @__PURE__ */
|
|
2729
|
-
/* @__PURE__ */
|
|
4236
|
+
/* @__PURE__ */ React21.createElement("div", { className: "absolute -top-24 -right-24 w-64 h-64 bg-cyan-400/20 rounded-full blur-3xl pointer-events-none" }),
|
|
4237
|
+
/* @__PURE__ */ React21.createElement("div", { className: "absolute -bottom-24 -left-24 w-64 h-64 bg-pink-400/20 rounded-full blur-3xl pointer-events-none" }),
|
|
4238
|
+
/* @__PURE__ */ React21.createElement("div", { className: "flex items-center justify-between mb-8 relative" }, /* @__PURE__ */ React21.createElement("div", { className: "flex flex-col" }, /* @__PURE__ */ React21.createElement("h2", { className: "text-2xl font-bold text-white tracking-wider drop-shadow-lg" }, title), /* @__PURE__ */ React21.createElement("div", { className: "h-1.5 w-16 bg-white/60 rounded-full mt-2 shadow-[0_0_10px_rgba(255,255,255,0.5)]" })), /* @__PURE__ */ React21.createElement(
|
|
2730
4239
|
"button",
|
|
2731
4240
|
{
|
|
2732
4241
|
onClick: onClose,
|
|
@@ -2734,8 +4243,8 @@ var VNModal = ({ title, show, onClose, children }) => {
|
|
|
2734
4243
|
},
|
|
2735
4244
|
"\u2715"
|
|
2736
4245
|
)),
|
|
2737
|
-
/* @__PURE__ */
|
|
2738
|
-
/* @__PURE__ */
|
|
4246
|
+
/* @__PURE__ */ React21.createElement("div", { className: "text-white leading-relaxed max-h-[50vh] overflow-y-auto pr-4 custom-scrollbar relative font-medium" }, children),
|
|
4247
|
+
/* @__PURE__ */ React21.createElement("div", { className: "mt-10 flex justify-center relative" }, /* @__PURE__ */ React21.createElement(
|
|
2739
4248
|
"button",
|
|
2740
4249
|
{
|
|
2741
4250
|
onClick: onClose,
|
|
@@ -2763,7 +4272,7 @@ var StartScreen = ({
|
|
|
2763
4272
|
}, []);
|
|
2764
4273
|
if (!isMounted) return null;
|
|
2765
4274
|
if (!showStartScreen) return null;
|
|
2766
|
-
const content = /* @__PURE__ */
|
|
4275
|
+
const content = /* @__PURE__ */ React21.createElement(
|
|
2767
4276
|
"div",
|
|
2768
4277
|
{
|
|
2769
4278
|
className: `fixed inset-0 w-screen h-screen flex items-center justify-center overflow-hidden ${className}`,
|
|
@@ -2775,7 +4284,7 @@ var StartScreen = ({
|
|
|
2775
4284
|
padding: 0
|
|
2776
4285
|
}
|
|
2777
4286
|
},
|
|
2778
|
-
/* @__PURE__ */
|
|
4287
|
+
/* @__PURE__ */ React21.createElement("div", { className: "absolute inset-0 w-full h-full overflow-hidden" }, /* @__PURE__ */ React21.createElement(
|
|
2779
4288
|
"div",
|
|
2780
4289
|
{
|
|
2781
4290
|
className: "absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-[150vw] h-[150vw] opacity-40 pointer-events-none",
|
|
@@ -2785,7 +4294,7 @@ var StartScreen = ({
|
|
|
2785
4294
|
animation: "pulse 10s ease-in-out infinite"
|
|
2786
4295
|
}
|
|
2787
4296
|
}
|
|
2788
|
-
), /* @__PURE__ */
|
|
4297
|
+
), /* @__PURE__ */ React21.createElement("div", { className: "absolute inset-0 transition-opacity duration-1000" }, /* @__PURE__ */ React21.createElement("div", { className: "absolute top-[-10%] left-[-10%] w-[60%] h-[60%] bg-cyan-500/10 rounded-full blur-[120px] animate-blob" }), /* @__PURE__ */ React21.createElement("div", { className: "absolute bottom-[-10%] right-[-10%] w-[60%] h-[60%] bg-purple-500/10 rounded-full blur-[120px] animate-blob animation-delay-2000" }), /* @__PURE__ */ React21.createElement("div", { className: "absolute top-[20%] right-[10%] w-[40%] h-[40%] bg-pink-500/10 rounded-full blur-[100px] animate-blob animation-delay-4000" })), /* @__PURE__ */ React21.createElement("div", { className: "absolute inset-0 opacity-60" }, [...Array(40)].map((_, i) => /* @__PURE__ */ React21.createElement(
|
|
2789
4298
|
"div",
|
|
2790
4299
|
{
|
|
2791
4300
|
key: i,
|
|
@@ -2801,7 +4310,7 @@ var StartScreen = ({
|
|
|
2801
4310
|
}
|
|
2802
4311
|
}
|
|
2803
4312
|
)))),
|
|
2804
|
-
/* @__PURE__ */
|
|
4313
|
+
/* @__PURE__ */ React21.createElement("div", { className: "relative z-10 w-full max-w-5xl mx-auto px-6 flex flex-col items-center" }, /* @__PURE__ */ React21.createElement("div", { className: "text-center mb-20 md:mb-32 group flex flex-col items-center" }, /* @__PURE__ */ React21.createElement("div", { className: "w-40 h-px bg-gradient-to-r from-transparent via-white/60 to-transparent mb-10 shadow-[0_0_15px_rgba(255,255,255,0.5)]" }), /* @__PURE__ */ React21.createElement(
|
|
2805
4314
|
"h1",
|
|
2806
4315
|
{
|
|
2807
4316
|
className: "text-6xl md:text-8xl font-black text-white tracking-[0.15em] leading-tight select-none",
|
|
@@ -2811,49 +4320,49 @@ var StartScreen = ({
|
|
|
2811
4320
|
}
|
|
2812
4321
|
},
|
|
2813
4322
|
scriptName || "VISUAL NOVEL"
|
|
2814
|
-
), /* @__PURE__ */
|
|
4323
|
+
), /* @__PURE__ */ React21.createElement("div", { className: "mt-10 flex flex-col items-center gap-4" }, /* @__PURE__ */ React21.createElement("div", { className: "h-1 w-32 bg-white rounded-full opacity-80 shadow-[0_0_20px_rgba(255,255,255,0.8)]" }), /* @__PURE__ */ React21.createElement("span", { className: "text-sm md:text-base tracking-[0.8em] text-white/60 font-medium uppercase translate-x-[0.4em]" }, "Adventure System"))), /* @__PURE__ */ React21.createElement("div", { className: "flex flex-col gap-10 items-center w-full max-w-sm" }, /* @__PURE__ */ React21.createElement(
|
|
2815
4324
|
"button",
|
|
2816
4325
|
{
|
|
2817
4326
|
onClick: onStart,
|
|
2818
4327
|
className: "group relative w-full h-20 flex items-center justify-center transition-all duration-500 active:scale-95"
|
|
2819
4328
|
},
|
|
2820
|
-
/* @__PURE__ */
|
|
4329
|
+
/* @__PURE__ */ React21.createElement(
|
|
2821
4330
|
"div",
|
|
2822
4331
|
{
|
|
2823
4332
|
className: "absolute inset-0 bg-white/10 backdrop-blur-2xl border-2 border-white/40 rounded-3xl transition-all duration-500 group-hover:bg-white/25 group-hover:border-white/70 group-hover:shadow-[0_0_50px_rgba(255,255,255,0.3)] group-hover:-inset-1"
|
|
2824
4333
|
}
|
|
2825
4334
|
),
|
|
2826
|
-
/* @__PURE__ */
|
|
2827
|
-
/* @__PURE__ */
|
|
2828
|
-
), /* @__PURE__ */
|
|
4335
|
+
/* @__PURE__ */ React21.createElement("div", { className: "absolute inset-0 overflow-hidden rounded-3xl pointer-events-none" }, /* @__PURE__ */ React21.createElement("div", { className: "absolute inset-0 opacity-0 group-hover:opacity-100 transition-opacity duration-700 bg-gradient-to-r from-transparent via-white/20 to-transparent -skew-x-[25deg] -translate-x-[200%] group-hover:translate-x-[200%] transition-transform duration-1000 ease-in-out" })),
|
|
4336
|
+
/* @__PURE__ */ React21.createElement("div", { className: "relative flex items-center gap-6" }, /* @__PURE__ */ React21.createElement("div", { className: "w-3 h-3 rounded-full bg-cyan-400 animate-ping opacity-75" }), /* @__PURE__ */ React21.createElement("span", { className: "text-2xl font-black text-white tracking-[0.4em] drop-shadow-md" }, startText), /* @__PURE__ */ React21.createElement("div", { className: "w-3 h-3 rounded-full bg-pink-400 animate-ping opacity-75" }))
|
|
4337
|
+
), /* @__PURE__ */ React21.createElement("div", { className: "flex gap-8 w-full justify-center" }, [
|
|
2829
4338
|
{ text: settingsText, onClick: () => setShowSettings(true), color: "rgba(57, 197, 187, 0.2)" },
|
|
2830
4339
|
{ text: aboutText, onClick: () => setShowAbout(true), color: "rgba(255, 182, 193, 0.2)" }
|
|
2831
|
-
].map((btn, idx) => /* @__PURE__ */
|
|
4340
|
+
].map((btn, idx) => /* @__PURE__ */ React21.createElement(
|
|
2832
4341
|
"button",
|
|
2833
4342
|
{
|
|
2834
4343
|
key: idx,
|
|
2835
4344
|
onClick: btn.onClick,
|
|
2836
4345
|
className: "group relative flex-1 h-14 flex items-center justify-center transition-all duration-300 active:scale-95 overflow-hidden rounded-2xl"
|
|
2837
4346
|
},
|
|
2838
|
-
/* @__PURE__ */
|
|
4347
|
+
/* @__PURE__ */ React21.createElement(
|
|
2839
4348
|
"div",
|
|
2840
4349
|
{
|
|
2841
4350
|
className: "absolute inset-0 bg-white/5 backdrop-blur-xl border border-white/20 transition-all duration-300 group-hover:bg-white/15 group-hover:border-white/40"
|
|
2842
4351
|
}
|
|
2843
4352
|
),
|
|
2844
|
-
/* @__PURE__ */
|
|
4353
|
+
/* @__PURE__ */ React21.createElement(
|
|
2845
4354
|
"div",
|
|
2846
4355
|
{
|
|
2847
4356
|
className: "absolute inset-0 opacity-0 group-hover:opacity-100 transition-opacity",
|
|
2848
4357
|
style: { background: btn.color }
|
|
2849
4358
|
}
|
|
2850
4359
|
),
|
|
2851
|
-
/* @__PURE__ */
|
|
4360
|
+
/* @__PURE__ */ React21.createElement("span", { className: "relative text-sm md:text-base font-bold text-white/80 group-hover:text-white tracking-[0.25em] transition-colors uppercase" }, btn.text)
|
|
2852
4361
|
))))),
|
|
2853
|
-
/* @__PURE__ */
|
|
2854
|
-
/* @__PURE__ */
|
|
2855
|
-
/* @__PURE__ */
|
|
2856
|
-
/* @__PURE__ */
|
|
4362
|
+
/* @__PURE__ */ React21.createElement("div", { className: "fixed bottom-10 left-0 right-0 text-center pointer-events-none select-none" }, /* @__PURE__ */ React21.createElement("div", { className: "inline-block px-6 py-2 rounded-full bg-white/5 backdrop-blur-md border border-white/10" }, /* @__PURE__ */ React21.createElement("span", { className: "text-[10px] md:text-xs text-white/30 tracking-[0.5em] font-light uppercase" }, "Ver 1.6.2 \u2014 ENGINE POWERED BY SA2KIT"))),
|
|
4363
|
+
/* @__PURE__ */ React21.createElement(VNModal, { title: settingsText, show: showSettings, onClose: () => setShowSettings(false) }, /* @__PURE__ */ React21.createElement("div", { className: "space-y-8 py-4" }, /* @__PURE__ */ React21.createElement("div", { className: "space-y-4" }, /* @__PURE__ */ React21.createElement("div", { className: "flex justify-between items-center text-sm font-bold tracking-widest text-white/60" }, /* @__PURE__ */ React21.createElement("span", null, "MUSIC VOLUME"), /* @__PURE__ */ React21.createElement("span", null, "80%")), /* @__PURE__ */ React21.createElement("div", { className: "h-3 bg-white/10 rounded-full p-0.5 border border-white/10" }, /* @__PURE__ */ React21.createElement("div", { className: "h-full w-[80%] bg-gradient-to-r from-cyan-400 to-blue-500 rounded-full shadow-[0_0_15px_rgba(34,211,238,0.5)]" }))), /* @__PURE__ */ React21.createElement("div", { className: "space-y-4" }, /* @__PURE__ */ React21.createElement("div", { className: "flex justify-between items-center text-sm font-bold tracking-widest text-white/60" }, /* @__PURE__ */ React21.createElement("span", null, "TEXT SPEED"), /* @__PURE__ */ React21.createElement("span", null, "NORMAL")), /* @__PURE__ */ React21.createElement("div", { className: "flex gap-3" }, ["SLOW", "NORMAL", "FAST"].map((s, i) => /* @__PURE__ */ React21.createElement("div", { key: s, className: `flex-1 py-3 rounded-xl border text-center text-xs font-bold transition-all cursor-pointer ${i === 1 ? "bg-white/20 border-white/60 text-white" : "bg-white/5 border-white/10 text-white/40 hover:bg-white/10 hover:border-white/30"}` }, s)))), /* @__PURE__ */ React21.createElement("div", { className: "pt-4 flex items-center justify-between opacity-50 italic text-xs border-t border-white/10" }, /* @__PURE__ */ React21.createElement("span", null, "Auto Save Enabled"), /* @__PURE__ */ React21.createElement("span", null, "Cloud Sync Active")))),
|
|
4364
|
+
/* @__PURE__ */ React21.createElement(VNModal, { title: aboutText, show: showAbout, onClose: () => setShowAbout(false) }, /* @__PURE__ */ React21.createElement("div", { className: "space-y-8 py-4" }, /* @__PURE__ */ React21.createElement("div", { className: "flex items-center gap-6 p-6 rounded-3xl bg-white/5 border border-white/10" }, /* @__PURE__ */ React21.createElement("div", { className: "w-20 h-20 rounded-2xl bg-gradient-to-br from-cyan-400 via-blue-500 to-purple-600 flex items-center justify-center text-3xl font-black text-white shadow-[0_10px_30px_rgba(0,0,0,0.3)]" }, "S2"), /* @__PURE__ */ React21.createElement("div", null, /* @__PURE__ */ React21.createElement("h3", { className: "text-2xl font-black text-white tracking-tight" }, scriptName || "Project SA2"), /* @__PURE__ */ React21.createElement("p", { className: "text-xs font-bold text-white/40 tracking-widest mt-1 uppercase" }, "Visual Novel Experience"))), /* @__PURE__ */ React21.createElement("div", { className: "space-y-4 px-2" }, /* @__PURE__ */ React21.createElement("p", { className: "text-white/80 font-medium leading-relaxed" }, "\u91C7\u7528 sa2kit \u5F15\u64CE\u6784\u5EFA\u7684\u65B0\u4E00\u4EE3\u5B9E\u65F6 3D \u89C6\u89C9\u5C0F\u8BF4\u3002\u7ED3\u5408\u4E86 MMD \u5B9E\u65F6\u6E32\u67D3\u6280\u672F\u4E0E\u4EA4\u4E92\u5F0F\u5267\u60C5\u5206\u652F\u7CFB\u7EDF\uFF0C\u81F4\u529B\u4E8E\u6253\u9020\u6781\u81F4\u7684\u6C89\u6D78\u5F0F\u53D9\u4E8B\u4F53\u9A8C\u3002")), /* @__PURE__ */ React21.createElement("div", { className: "grid grid-cols-2 gap-4 pt-6 border-t border-white/10" }, /* @__PURE__ */ React21.createElement("div", { className: "flex flex-col gap-1" }, /* @__PURE__ */ React21.createElement("span", { className: "text-[10px] font-bold text-white/30 tracking-widest" }, "DEVELOPER"), /* @__PURE__ */ React21.createElement("span", { className: "text-xs font-bold text-white/80" }, "SA2KIT TEAM")), /* @__PURE__ */ React21.createElement("div", { className: "flex flex-col gap-1 text-right" }, /* @__PURE__ */ React21.createElement("span", { className: "text-[10px] font-bold text-white/30 tracking-widest" }, "ENGINE"), /* @__PURE__ */ React21.createElement("span", { className: "text-xs font-bold text-white/80" }, "THREE.JS / REACT"))))),
|
|
4365
|
+
/* @__PURE__ */ React21.createElement("style", null, `
|
|
2857
4366
|
@keyframes floatParticle {
|
|
2858
4367
|
from { transform: translateY(0) translateX(0); opacity: 0; }
|
|
2859
4368
|
20% { opacity: 1; }
|
|
@@ -2938,14 +4447,14 @@ var LoadingOverlay = ({
|
|
|
2938
4447
|
onStart,
|
|
2939
4448
|
className = ""
|
|
2940
4449
|
}) => {
|
|
2941
|
-
return /* @__PURE__ */
|
|
4450
|
+
return /* @__PURE__ */ React21.createElement(React21.Fragment, null, /* @__PURE__ */ React21.createElement(
|
|
2942
4451
|
LoadingScreen,
|
|
2943
4452
|
{
|
|
2944
4453
|
isLoading,
|
|
2945
4454
|
loadingText,
|
|
2946
4455
|
className
|
|
2947
4456
|
}
|
|
2948
|
-
), /* @__PURE__ */
|
|
4457
|
+
), /* @__PURE__ */ React21.createElement(
|
|
2949
4458
|
StartScreen,
|
|
2950
4459
|
{
|
|
2951
4460
|
showStartScreen,
|
|
@@ -2963,18 +4472,18 @@ var SkipConfirmDialog = ({
|
|
|
2963
4472
|
onConfirm,
|
|
2964
4473
|
onCancel
|
|
2965
4474
|
}) => {
|
|
2966
|
-
const [isMounted, setIsMounted] =
|
|
2967
|
-
|
|
4475
|
+
const [isMounted, setIsMounted] = React21.useState(false);
|
|
4476
|
+
React21.useEffect(() => {
|
|
2968
4477
|
setIsMounted(true);
|
|
2969
4478
|
}, []);
|
|
2970
4479
|
if (!isMounted) return null;
|
|
2971
|
-
const content = /* @__PURE__ */
|
|
4480
|
+
const content = /* @__PURE__ */ React21.createElement(
|
|
2972
4481
|
"div",
|
|
2973
4482
|
{
|
|
2974
4483
|
className: "fixed inset-0 flex items-center justify-center bg-black/40 backdrop-blur-sm pointer-events-auto",
|
|
2975
4484
|
style: { zIndex: 999999 }
|
|
2976
4485
|
},
|
|
2977
|
-
/* @__PURE__ */
|
|
4486
|
+
/* @__PURE__ */ React21.createElement(
|
|
2978
4487
|
"div",
|
|
2979
4488
|
{
|
|
2980
4489
|
className: "p-8 rounded-3xl border border-white/30 shadow-2xl max-w-sm w-full mx-4 overflow-hidden relative",
|
|
@@ -2986,16 +4495,16 @@ var SkipConfirmDialog = ({
|
|
|
2986
4495
|
WebkitBackdropFilter: "blur(32px) saturate(200%)"
|
|
2987
4496
|
}
|
|
2988
4497
|
},
|
|
2989
|
-
/* @__PURE__ */
|
|
2990
|
-
/* @__PURE__ */
|
|
2991
|
-
/* @__PURE__ */
|
|
4498
|
+
/* @__PURE__ */ React21.createElement("h3", { className: "text-xl font-bold text-white mb-4 drop-shadow-md" }, "\u52A8\u753B\u5C1A\u672A\u64AD\u653E\u5B8C\u6210"),
|
|
4499
|
+
/* @__PURE__ */ React21.createElement("p", { className: "text-white/90 mb-8 leading-relaxed drop-shadow-sm" }, "\u5F53\u524D\u573A\u666F\u7684\u52A8\u753B\u8FD8\u6CA1\u6709\u5B8C\u6574\u64AD\u653E\u4E00\u904D\uFF0C\u662F\u5426\u76F4\u63A5\u8DF3\u8F6C\u5230\u4E0B\u4E00\u4E2A\u573A\u666F\uFF1F"),
|
|
4500
|
+
/* @__PURE__ */ React21.createElement("div", { className: "flex justify-end gap-4" }, /* @__PURE__ */ React21.createElement(
|
|
2992
4501
|
"button",
|
|
2993
4502
|
{
|
|
2994
4503
|
onClick: onCancel,
|
|
2995
4504
|
className: "px-6 py-2.5 rounded-2xl text-white/80 font-medium hover:text-white hover:bg-white/10 transition-all border border-white/10 hover:border-white/30"
|
|
2996
4505
|
},
|
|
2997
4506
|
"\u53D6\u6D88"
|
|
2998
|
-
), /* @__PURE__ */
|
|
4507
|
+
), /* @__PURE__ */ React21.createElement(
|
|
2999
4508
|
"button",
|
|
3000
4509
|
{
|
|
3001
4510
|
onClick: onConfirm,
|
|
@@ -3019,18 +4528,18 @@ var ChoiceMenu = ({
|
|
|
3019
4528
|
onSelect,
|
|
3020
4529
|
theme
|
|
3021
4530
|
}) => {
|
|
3022
|
-
const [isMounted, setIsMounted] =
|
|
3023
|
-
|
|
4531
|
+
const [isMounted, setIsMounted] = React21.useState(false);
|
|
4532
|
+
React21.useEffect(() => {
|
|
3024
4533
|
setIsMounted(true);
|
|
3025
4534
|
}, []);
|
|
3026
4535
|
if (!isMounted) return null;
|
|
3027
|
-
const content = /* @__PURE__ */
|
|
4536
|
+
const content = /* @__PURE__ */ React21.createElement(
|
|
3028
4537
|
"div",
|
|
3029
4538
|
{
|
|
3030
4539
|
className: "fixed inset-0 flex flex-col items-center justify-center bg-black/20 backdrop-blur-sm pointer-events-auto transition-all animate-in fade-in duration-500",
|
|
3031
4540
|
style: { zIndex: 1e6 }
|
|
3032
4541
|
},
|
|
3033
|
-
/* @__PURE__ */
|
|
4542
|
+
/* @__PURE__ */ React21.createElement("div", { className: "flex flex-col gap-4 w-full max-w-md px-6" }, choices.map((choice, index) => /* @__PURE__ */ React21.createElement(
|
|
3034
4543
|
"button",
|
|
3035
4544
|
{
|
|
3036
4545
|
key: index,
|
|
@@ -3044,9 +4553,9 @@ var ChoiceMenu = ({
|
|
|
3044
4553
|
WebkitBackdropFilter: "blur(32px) saturate(200%)"
|
|
3045
4554
|
}
|
|
3046
4555
|
},
|
|
3047
|
-
/* @__PURE__ */
|
|
3048
|
-
/* @__PURE__ */
|
|
3049
|
-
/* @__PURE__ */
|
|
4556
|
+
/* @__PURE__ */ React21.createElement("div", { className: "absolute inset-0 bg-white/10 opacity-0 group-hover:opacity-100 transition-opacity" }),
|
|
4557
|
+
/* @__PURE__ */ React21.createElement("div", { className: "absolute inset-0 bg-gradient-to-r from-transparent via-white/5 to-transparent -translate-x-full group-hover:translate-x-full transition-transform duration-1000 ease-in-out" }),
|
|
4558
|
+
/* @__PURE__ */ React21.createElement("span", { className: "relative z-10 drop-shadow-md" }, choice.text)
|
|
3050
4559
|
)))
|
|
3051
4560
|
);
|
|
3052
4561
|
let portalContainer = document.getElementById("dialogue-portal-root");
|
|
@@ -3328,16 +4837,16 @@ var MMDVisualNovel = forwardRef(
|
|
|
3328
4837
|
};
|
|
3329
4838
|
}, []);
|
|
3330
4839
|
if (!currentNode) {
|
|
3331
|
-
return /* @__PURE__ */
|
|
4840
|
+
return /* @__PURE__ */ React21.createElement("div", { className: "flex h-full w-full items-center justify-center bg-black text-white" }, "\u5267\u672C\u4E3A\u7A7A");
|
|
3332
4841
|
}
|
|
3333
|
-
return /* @__PURE__ */
|
|
4842
|
+
return /* @__PURE__ */ React21.createElement(
|
|
3334
4843
|
"div",
|
|
3335
4844
|
{
|
|
3336
4845
|
ref: containerRef,
|
|
3337
4846
|
className: `relative bg-black ${className}`,
|
|
3338
4847
|
style: { width: "100%", height: "100%", overflow: "hidden", ...style }
|
|
3339
4848
|
},
|
|
3340
|
-
/* @__PURE__ */
|
|
4849
|
+
/* @__PURE__ */ React21.createElement(
|
|
3341
4850
|
"div",
|
|
3342
4851
|
{
|
|
3343
4852
|
className: "absolute inset-0 w-full h-full",
|
|
@@ -3348,7 +4857,7 @@ var MMDVisualNovel = forwardRef(
|
|
|
3348
4857
|
transition: "opacity 0.3s ease-in-out"
|
|
3349
4858
|
}
|
|
3350
4859
|
},
|
|
3351
|
-
!isTransitioning && /* @__PURE__ */
|
|
4860
|
+
!isTransitioning && /* @__PURE__ */ React21.createElement(
|
|
3352
4861
|
MMDPlayerBase,
|
|
3353
4862
|
{
|
|
3354
4863
|
key: currentNode.id,
|
|
@@ -3397,13 +4906,13 @@ var MMDVisualNovel = forwardRef(
|
|
|
3397
4906
|
}
|
|
3398
4907
|
)
|
|
3399
4908
|
),
|
|
3400
|
-
activeEffect && /* @__PURE__ */
|
|
4909
|
+
activeEffect && /* @__PURE__ */ React21.createElement(
|
|
3401
4910
|
"div",
|
|
3402
4911
|
{
|
|
3403
4912
|
className: "pointer-events-none absolute inset-0 flex items-center justify-center",
|
|
3404
4913
|
style: { zIndex: 999 }
|
|
3405
4914
|
},
|
|
3406
|
-
activeEffect.type === "flash" && /* @__PURE__ */
|
|
4915
|
+
activeEffect.type === "flash" && /* @__PURE__ */ React21.createElement(
|
|
3407
4916
|
"div",
|
|
3408
4917
|
{
|
|
3409
4918
|
className: "h-full w-full",
|
|
@@ -3413,7 +4922,7 @@ var MMDVisualNovel = forwardRef(
|
|
|
3413
4922
|
}
|
|
3414
4923
|
}
|
|
3415
4924
|
),
|
|
3416
|
-
activeEffect.type === "gif" && activeEffect.url && /* @__PURE__ */
|
|
4925
|
+
activeEffect.type === "gif" && activeEffect.url && /* @__PURE__ */ React21.createElement(
|
|
3417
4926
|
"img",
|
|
3418
4927
|
{
|
|
3419
4928
|
src: activeEffect.url,
|
|
@@ -3421,7 +4930,7 @@ var MMDVisualNovel = forwardRef(
|
|
|
3421
4930
|
className: activeEffect.position === "full" ? "h-full w-full object-cover" : "max-h-full max-w-full"
|
|
3422
4931
|
}
|
|
3423
4932
|
),
|
|
3424
|
-
/* @__PURE__ */
|
|
4933
|
+
/* @__PURE__ */ React21.createElement("style", null, `
|
|
3425
4934
|
@keyframes flash-anim {
|
|
3426
4935
|
0% { opacity: 0; }
|
|
3427
4936
|
25% { opacity: 1; }
|
|
@@ -3429,7 +4938,7 @@ var MMDVisualNovel = forwardRef(
|
|
|
3429
4938
|
}
|
|
3430
4939
|
`)
|
|
3431
4940
|
),
|
|
3432
|
-
/* @__PURE__ */
|
|
4941
|
+
/* @__PURE__ */ React21.createElement(
|
|
3433
4942
|
LoadingOverlay,
|
|
3434
4943
|
{
|
|
3435
4944
|
isLoading: (() => {
|
|
@@ -3463,7 +4972,7 @@ var MMDVisualNovel = forwardRef(
|
|
|
3463
4972
|
shouldShow,
|
|
3464
4973
|
dialogue: currentDialogue
|
|
3465
4974
|
});
|
|
3466
|
-
return shouldShow ? /* @__PURE__ */
|
|
4975
|
+
return shouldShow ? /* @__PURE__ */ React21.createElement(
|
|
3467
4976
|
DialogueBox,
|
|
3468
4977
|
{
|
|
3469
4978
|
dialogue: currentDialogue,
|
|
@@ -3489,7 +4998,7 @@ var MMDVisualNovel = forwardRef(
|
|
|
3489
4998
|
}
|
|
3490
4999
|
) : null;
|
|
3491
5000
|
})(),
|
|
3492
|
-
pendingNodeIndex !== null && /* @__PURE__ */
|
|
5001
|
+
pendingNodeIndex !== null && /* @__PURE__ */ React21.createElement(
|
|
3493
5002
|
SkipConfirmDialog,
|
|
3494
5003
|
{
|
|
3495
5004
|
onConfirm: () => {
|
|
@@ -3502,7 +5011,7 @@ var MMDVisualNovel = forwardRef(
|
|
|
3502
5011
|
}
|
|
3503
5012
|
}
|
|
3504
5013
|
),
|
|
3505
|
-
showChoices && (currentDialogue?.choices || currentNode.choices) && /* @__PURE__ */
|
|
5014
|
+
showChoices && (currentDialogue?.choices || currentNode.choices) && /* @__PURE__ */ React21.createElement(
|
|
3506
5015
|
ChoiceMenu,
|
|
3507
5016
|
{
|
|
3508
5017
|
choices: currentDialogue?.choices || currentNode.choices,
|
|
@@ -3541,7 +5050,7 @@ var MMDVisualNovel = forwardRef(
|
|
|
3541
5050
|
}
|
|
3542
5051
|
}
|
|
3543
5052
|
),
|
|
3544
|
-
showHistory && /* @__PURE__ */
|
|
5053
|
+
showHistory && /* @__PURE__ */ React21.createElement(
|
|
3545
5054
|
HistoryPanel,
|
|
3546
5055
|
{
|
|
3547
5056
|
history,
|
|
@@ -3574,18 +5083,18 @@ var MusicControls = ({
|
|
|
3574
5083
|
return `${mins}:${secs.toString().padStart(2, "0")}`;
|
|
3575
5084
|
};
|
|
3576
5085
|
const progress = duration > 0 ? currentTime / duration * 100 : 0;
|
|
3577
|
-
return /* @__PURE__ */
|
|
5086
|
+
return /* @__PURE__ */ React21.createElement(
|
|
3578
5087
|
"div",
|
|
3579
5088
|
{
|
|
3580
5089
|
className: `w-full max-w-4xl mx-auto px-6 py-4 bg-white/10 backdrop-blur-xl border border-white/20 rounded-2xl shadow-2xl pointer-events-auto transition-all group ${className}`
|
|
3581
5090
|
},
|
|
3582
|
-
/* @__PURE__ */
|
|
5091
|
+
/* @__PURE__ */ React21.createElement("div", { className: "relative w-full h-1.5 bg-white/20 rounded-full mb-4 cursor-pointer group/progress overflow-hidden" }, /* @__PURE__ */ React21.createElement(
|
|
3583
5092
|
"div",
|
|
3584
5093
|
{
|
|
3585
5094
|
className: "absolute h-full bg-blue-500 rounded-full transition-all duration-300",
|
|
3586
5095
|
style: { width: `${progress}%` }
|
|
3587
5096
|
}
|
|
3588
|
-
), /* @__PURE__ */
|
|
5097
|
+
), /* @__PURE__ */ React21.createElement(
|
|
3589
5098
|
"input",
|
|
3590
5099
|
{
|
|
3591
5100
|
type: "range",
|
|
@@ -3596,52 +5105,52 @@ var MusicControls = ({
|
|
|
3596
5105
|
className: "absolute inset-0 w-full h-full opacity-0 cursor-pointer"
|
|
3597
5106
|
}
|
|
3598
5107
|
)),
|
|
3599
|
-
/* @__PURE__ */
|
|
5108
|
+
/* @__PURE__ */ React21.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ React21.createElement("div", { className: "flex items-center gap-2 text-xs font-mono text-white/60 w-32" }, /* @__PURE__ */ React21.createElement("span", null, formatTime(currentTime)), /* @__PURE__ */ React21.createElement("span", null, "/"), /* @__PURE__ */ React21.createElement("span", null, formatTime(duration))), /* @__PURE__ */ React21.createElement("div", { className: "flex items-center gap-6" }, /* @__PURE__ */ React21.createElement(
|
|
3600
5109
|
"button",
|
|
3601
5110
|
{
|
|
3602
5111
|
onClick: onPrevious,
|
|
3603
5112
|
className: "text-white/80 hover:text-white transition-colors"
|
|
3604
5113
|
},
|
|
3605
|
-
/* @__PURE__ */
|
|
3606
|
-
), /* @__PURE__ */
|
|
5114
|
+
/* @__PURE__ */ React21.createElement(SkipBack, { className: "w-6 h-6 fill-current" })
|
|
5115
|
+
), /* @__PURE__ */ React21.createElement(
|
|
3607
5116
|
"button",
|
|
3608
5117
|
{
|
|
3609
5118
|
onClick: onPlayPause,
|
|
3610
5119
|
className: "w-12 h-12 flex items-center justify-center bg-blue-500 hover:bg-blue-400 text-white rounded-full shadow-lg shadow-blue-500/20 transition-all hover:scale-105 active:scale-95"
|
|
3611
5120
|
},
|
|
3612
|
-
isPlaying ? /* @__PURE__ */
|
|
3613
|
-
), /* @__PURE__ */
|
|
5121
|
+
isPlaying ? /* @__PURE__ */ React21.createElement(Pause, { className: "w-6 h-6 fill-current" }) : /* @__PURE__ */ React21.createElement(Play, { className: "w-6 h-6 fill-current ml-1" })
|
|
5122
|
+
), /* @__PURE__ */ React21.createElement(
|
|
3614
5123
|
"button",
|
|
3615
5124
|
{
|
|
3616
5125
|
onClick: onNext,
|
|
3617
5126
|
className: "text-white/80 hover:text-white transition-colors"
|
|
3618
5127
|
},
|
|
3619
|
-
/* @__PURE__ */
|
|
3620
|
-
)), /* @__PURE__ */
|
|
5128
|
+
/* @__PURE__ */ React21.createElement(SkipForward, { className: "w-6 h-6 fill-current" })
|
|
5129
|
+
)), /* @__PURE__ */ React21.createElement("div", { className: "flex items-center gap-4 w-32 justify-end" }, isCameraManual && /* @__PURE__ */ React21.createElement(
|
|
3621
5130
|
"button",
|
|
3622
5131
|
{
|
|
3623
5132
|
onClick: onResetCamera,
|
|
3624
5133
|
className: "text-blue-400 hover:text-blue-300 transition-colors animate-in zoom-in duration-300",
|
|
3625
5134
|
title: "\u6062\u590D\u521D\u59CB\u89C6\u89D2"
|
|
3626
5135
|
},
|
|
3627
|
-
/* @__PURE__ */
|
|
3628
|
-
), /* @__PURE__ */
|
|
5136
|
+
/* @__PURE__ */ React21.createElement(Camera, { className: "w-5 h-5" })
|
|
5137
|
+
), /* @__PURE__ */ React21.createElement(
|
|
3629
5138
|
"button",
|
|
3630
5139
|
{
|
|
3631
5140
|
onClick: onToggleLoop,
|
|
3632
5141
|
className: "text-white/60 hover:text-white transition-colors",
|
|
3633
5142
|
title: loopMode
|
|
3634
5143
|
},
|
|
3635
|
-
loopMode === "list" && /* @__PURE__ */
|
|
3636
|
-
loopMode === "single" && /* @__PURE__ */
|
|
3637
|
-
loopMode === "shuffle" && /* @__PURE__ */
|
|
3638
|
-
), /* @__PURE__ */
|
|
5144
|
+
loopMode === "list" && /* @__PURE__ */ React21.createElement(Repeat, { className: "w-5 h-5" }),
|
|
5145
|
+
loopMode === "single" && /* @__PURE__ */ React21.createElement(Repeat1, { className: "w-5 h-5 text-blue-400" }),
|
|
5146
|
+
loopMode === "shuffle" && /* @__PURE__ */ React21.createElement(Shuffle, { className: "w-5 h-5 text-orange-400" })
|
|
5147
|
+
), /* @__PURE__ */ React21.createElement(
|
|
3639
5148
|
"button",
|
|
3640
5149
|
{
|
|
3641
5150
|
onClick: onTogglePlaylist,
|
|
3642
5151
|
className: "text-white/60 hover:text-white transition-colors"
|
|
3643
5152
|
},
|
|
3644
|
-
/* @__PURE__ */
|
|
5153
|
+
/* @__PURE__ */ React21.createElement(ListMusic, { className: "w-5 h-5" })
|
|
3645
5154
|
)))
|
|
3646
5155
|
);
|
|
3647
5156
|
};
|
|
@@ -3656,25 +5165,25 @@ var PlaylistPanel = ({
|
|
|
3656
5165
|
onSearch,
|
|
3657
5166
|
isSearching
|
|
3658
5167
|
}) => {
|
|
3659
|
-
const [searchValue, setSearchValue] =
|
|
5168
|
+
const [searchValue, setSearchValue] = React21.useState("");
|
|
3660
5169
|
if (!isOpen) return null;
|
|
3661
5170
|
const handleSearchSubmit = (e) => {
|
|
3662
5171
|
e.preventDefault();
|
|
3663
5172
|
onSearch?.(searchValue);
|
|
3664
5173
|
};
|
|
3665
|
-
return /* @__PURE__ */
|
|
5174
|
+
return /* @__PURE__ */ React21.createElement(
|
|
3666
5175
|
"div",
|
|
3667
5176
|
{
|
|
3668
5177
|
className: `fixed inset-y-0 right-0 w-80 bg-gray-900/90 backdrop-blur-2xl border-l border-white/10 shadow-2xl z-50 flex flex-col pointer-events-auto transform transition-transform duration-500 ease-out ${isOpen ? "translate-x-0" : "translate-x-full"} ${className}`
|
|
3669
5178
|
},
|
|
3670
|
-
/* @__PURE__ */
|
|
5179
|
+
/* @__PURE__ */ React21.createElement("div", { className: "flex flex-col p-6 border-b border-white/10 gap-4" }, /* @__PURE__ */ React21.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ React21.createElement("div", { className: "flex items-center gap-3" }, /* @__PURE__ */ React21.createElement(Music, { className: "w-5 h-5 text-blue-400" }), /* @__PURE__ */ React21.createElement("h3", { className: "text-lg font-bold text-white" }, mikuMode ? "Miku \u6B4C\u66F2\u5E93" : "\u64AD\u653E\u5217\u8868")), /* @__PURE__ */ React21.createElement(
|
|
3671
5180
|
"button",
|
|
3672
5181
|
{
|
|
3673
5182
|
onClick: onClose,
|
|
3674
5183
|
className: "p-2 text-white/60 hover:text-white transition-colors"
|
|
3675
5184
|
},
|
|
3676
|
-
/* @__PURE__ */
|
|
3677
|
-
)), mikuMode && /* @__PURE__ */
|
|
5185
|
+
/* @__PURE__ */ React21.createElement(X, { className: "w-5 h-5" })
|
|
5186
|
+
)), mikuMode && /* @__PURE__ */ React21.createElement("form", { onSubmit: handleSearchSubmit, className: "relative" }, /* @__PURE__ */ React21.createElement(
|
|
3678
5187
|
"input",
|
|
3679
5188
|
{
|
|
3680
5189
|
type: "text",
|
|
@@ -3683,23 +5192,23 @@ var PlaylistPanel = ({
|
|
|
3683
5192
|
placeholder: "\u641C\u7D22 Miku \u6B4C\u66F2...",
|
|
3684
5193
|
className: "w-full bg-white/5 border border-white/10 rounded-lg py-2 pl-10 pr-4 text-sm text-white placeholder:text-white/20 focus:outline-none focus:border-blue-500/50 transition-colors"
|
|
3685
5194
|
}
|
|
3686
|
-
), /* @__PURE__ */
|
|
3687
|
-
/* @__PURE__ */
|
|
5195
|
+
), /* @__PURE__ */ React21.createElement(Search, { className: "absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-white/20" }), isSearching && /* @__PURE__ */ React21.createElement(Loader2, { className: "absolute right-3 top-1/2 -translate-y-1/2 w-4 h-4 text-blue-400 animate-spin" }))),
|
|
5196
|
+
/* @__PURE__ */ React21.createElement("div", { className: "flex-1 overflow-y-auto p-4 space-y-2 custom-scrollbar" }, tracks.map((track, index) => {
|
|
3688
5197
|
const isActive = index === currentIndex;
|
|
3689
|
-
return /* @__PURE__ */
|
|
5198
|
+
return /* @__PURE__ */ React21.createElement(
|
|
3690
5199
|
"button",
|
|
3691
5200
|
{
|
|
3692
5201
|
key: track.id,
|
|
3693
5202
|
onClick: () => onSelectTrack(index),
|
|
3694
5203
|
className: `w-full flex items-center gap-4 p-3 rounded-xl transition-all group ${isActive ? "bg-blue-500/20 border border-blue-500/30" : "hover:bg-white/5 border border-transparent"}`
|
|
3695
5204
|
},
|
|
3696
|
-
/* @__PURE__ */
|
|
3697
|
-
/* @__PURE__ */
|
|
3698
|
-
!isActive && /* @__PURE__ */
|
|
5205
|
+
/* @__PURE__ */ React21.createElement("div", { className: "relative w-12 h-12 flex-shrink-0 rounded-lg overflow-hidden bg-gray-800" }, track.coverUrl ? /* @__PURE__ */ React21.createElement("img", { src: track.coverUrl, alt: track.title, className: "w-full h-full object-cover" }) : /* @__PURE__ */ React21.createElement("div", { className: "w-full h-full flex items-center justify-center text-white/20" }, /* @__PURE__ */ React21.createElement(Music, { className: "w-6 h-6" })), isActive && /* @__PURE__ */ React21.createElement("div", { className: "absolute inset-0 bg-blue-500/40 flex items-center justify-center" }, /* @__PURE__ */ React21.createElement("div", { className: "flex gap-1 items-end h-4" }, /* @__PURE__ */ React21.createElement("div", { className: "w-1 bg-white animate-music-bar-1" }), /* @__PURE__ */ React21.createElement("div", { className: "w-1 bg-white animate-music-bar-2" }), /* @__PURE__ */ React21.createElement("div", { className: "w-1 bg-white animate-music-bar-3" })))),
|
|
5206
|
+
/* @__PURE__ */ React21.createElement("div", { className: "flex-1 text-left min-w-0" }, /* @__PURE__ */ React21.createElement("h4", { className: `text-sm font-bold truncate ${isActive ? "text-blue-400" : "text-white/90"}` }, track.title), /* @__PURE__ */ React21.createElement("p", { className: "text-xs text-white/40 truncate mt-0.5" }, track.artist || "\u672A\u77E5\u827A\u672F\u5BB6")),
|
|
5207
|
+
!isActive && /* @__PURE__ */ React21.createElement("div", { className: "opacity-0 group-hover:opacity-100 transition-opacity" }, /* @__PURE__ */ React21.createElement(Play, { className: "w-4 h-4 text-white/40 fill-current" }))
|
|
3699
5208
|
);
|
|
3700
5209
|
})),
|
|
3701
|
-
/* @__PURE__ */
|
|
3702
|
-
/* @__PURE__ */
|
|
5210
|
+
/* @__PURE__ */ React21.createElement("div", { className: "p-6 border-t border-white/10" }, /* @__PURE__ */ React21.createElement("p", { className: "text-xs text-gray-500 text-center" }, "\u5171 ", tracks.length, " \u9996\u66F2\u76EE")),
|
|
5211
|
+
/* @__PURE__ */ React21.createElement("style", { dangerouslySetInnerHTML: { __html: `
|
|
3703
5212
|
.custom-scrollbar::-webkit-scrollbar {
|
|
3704
5213
|
width: 4px;
|
|
3705
5214
|
}
|
|
@@ -3732,7 +5241,7 @@ var PlaylistPanel = ({
|
|
|
3732
5241
|
);
|
|
3733
5242
|
};
|
|
3734
5243
|
var TrackInfo = ({ track, className = "" }) => {
|
|
3735
|
-
return /* @__PURE__ */
|
|
5244
|
+
return /* @__PURE__ */ React21.createElement("div", { className: `flex flex-col items-center text-center gap-2 ${className}` }, /* @__PURE__ */ React21.createElement("div", { className: "px-4 py-1.5 bg-black/40 backdrop-blur-md rounded-full border border-white/10 shadow-lg" }, /* @__PURE__ */ React21.createElement("h2", { className: "text-lg font-bold text-white tracking-wider truncate max-w-md" }, track.title)), /* @__PURE__ */ React21.createElement("p", { className: "text-sm font-medium text-white/60 drop-shadow-md" }, track.artist || "\u672A\u77E5\u827A\u672F\u5BB6"));
|
|
3736
5245
|
};
|
|
3737
5246
|
|
|
3738
5247
|
// src/mmd/music-player/MMDMusicPlayer.tsx
|
|
@@ -3934,9 +5443,9 @@ var MMDMusicPlayer = forwardRef(
|
|
|
3934
5443
|
};
|
|
3935
5444
|
}, [resetUITimeout]);
|
|
3936
5445
|
if (!currentTrack) {
|
|
3937
|
-
return /* @__PURE__ */
|
|
5446
|
+
return /* @__PURE__ */ React21.createElement("div", { className: "flex h-full w-full items-center justify-center bg-black text-white" }, "\u64AD\u653E\u5217\u8868\u4E3A\u7A7A");
|
|
3938
5447
|
}
|
|
3939
|
-
return /* @__PURE__ */
|
|
5448
|
+
return /* @__PURE__ */ React21.createElement(
|
|
3940
5449
|
"div",
|
|
3941
5450
|
{
|
|
3942
5451
|
ref: containerRef,
|
|
@@ -3945,7 +5454,7 @@ var MMDMusicPlayer = forwardRef(
|
|
|
3945
5454
|
onMouseMove: resetUITimeout,
|
|
3946
5455
|
onClick: resetUITimeout
|
|
3947
5456
|
},
|
|
3948
|
-
/* @__PURE__ */
|
|
5457
|
+
/* @__PURE__ */ React21.createElement(
|
|
3949
5458
|
"div",
|
|
3950
5459
|
{
|
|
3951
5460
|
className: "absolute inset-0 w-full h-full",
|
|
@@ -3955,7 +5464,7 @@ var MMDMusicPlayer = forwardRef(
|
|
|
3955
5464
|
transition: "opacity 0.5s ease-in-out"
|
|
3956
5465
|
}
|
|
3957
5466
|
},
|
|
3958
|
-
!isTransitioning && /* @__PURE__ */
|
|
5467
|
+
!isTransitioning && /* @__PURE__ */ React21.createElement(
|
|
3959
5468
|
MMDPlayerBase,
|
|
3960
5469
|
{
|
|
3961
5470
|
ref: playerRef,
|
|
@@ -3989,14 +5498,14 @@ var MMDMusicPlayer = forwardRef(
|
|
|
3989
5498
|
}
|
|
3990
5499
|
)
|
|
3991
5500
|
),
|
|
3992
|
-
(isLoading || isTransitioning) && /* @__PURE__ */
|
|
3993
|
-
/* @__PURE__ */
|
|
5501
|
+
(isLoading || isTransitioning) && /* @__PURE__ */ React21.createElement("div", { className: "absolute inset-0 z-50 flex items-center justify-center bg-black/60 backdrop-blur-md" }, /* @__PURE__ */ React21.createElement("div", { className: "flex flex-col items-center gap-4" }, /* @__PURE__ */ React21.createElement("div", { className: "h-12 w-12 animate-spin rounded-full border-4 border-white/20 border-t-blue-500" }), /* @__PURE__ */ React21.createElement("div", { className: "text-white font-medium" }, isTransitioning ? "\u51C6\u5907\u4E0B\u4E00\u9996..." : "\u6B63\u5728\u52A0\u8F7D\u821E\u53F0..."))),
|
|
5502
|
+
/* @__PURE__ */ React21.createElement(
|
|
3994
5503
|
"div",
|
|
3995
5504
|
{
|
|
3996
5505
|
className: `absolute inset-0 z-10 flex flex-col justify-between transition-opacity duration-700 pointer-events-none ${isUIVisible ? "opacity-100" : "opacity-0"}`
|
|
3997
5506
|
},
|
|
3998
|
-
/* @__PURE__ */
|
|
3999
|
-
/* @__PURE__ */
|
|
5507
|
+
/* @__PURE__ */ React21.createElement("div", { className: "pt-12 px-8 flex justify-center" }, /* @__PURE__ */ React21.createElement(TrackInfo, { track: currentTrack })),
|
|
5508
|
+
/* @__PURE__ */ React21.createElement("div", { className: "pb-12 px-8" }, /* @__PURE__ */ React21.createElement(
|
|
4000
5509
|
MusicControls,
|
|
4001
5510
|
{
|
|
4002
5511
|
isPlaying,
|
|
@@ -4021,7 +5530,7 @@ var MMDMusicPlayer = forwardRef(
|
|
|
4021
5530
|
}
|
|
4022
5531
|
))
|
|
4023
5532
|
),
|
|
4024
|
-
/* @__PURE__ */
|
|
5533
|
+
/* @__PURE__ */ React21.createElement(
|
|
4025
5534
|
PlaylistPanel,
|
|
4026
5535
|
{
|
|
4027
5536
|
tracks,
|
|
@@ -4159,13 +5668,13 @@ var MMDARPlayer = forwardRef((props, ref) => {
|
|
|
4159
5668
|
}
|
|
4160
5669
|
return () => stopCamera();
|
|
4161
5670
|
}, [autoPlay, startCamera, stopCamera]);
|
|
4162
|
-
return /* @__PURE__ */
|
|
5671
|
+
return /* @__PURE__ */ React21.createElement(
|
|
4163
5672
|
"div",
|
|
4164
5673
|
{
|
|
4165
5674
|
className: `relative w-full h-full bg-black overflow-hidden ${className}`,
|
|
4166
5675
|
style
|
|
4167
5676
|
},
|
|
4168
|
-
/* @__PURE__ */
|
|
5677
|
+
/* @__PURE__ */ React21.createElement(
|
|
4169
5678
|
"video",
|
|
4170
5679
|
{
|
|
4171
5680
|
ref: videoRef,
|
|
@@ -4176,13 +5685,13 @@ var MMDARPlayer = forwardRef((props, ref) => {
|
|
|
4176
5685
|
style: { zIndex: 0 }
|
|
4177
5686
|
}
|
|
4178
5687
|
),
|
|
4179
|
-
/* @__PURE__ */
|
|
5688
|
+
/* @__PURE__ */ React21.createElement(
|
|
4180
5689
|
"div",
|
|
4181
5690
|
{
|
|
4182
5691
|
className: "absolute inset-0 w-full h-full",
|
|
4183
5692
|
style: { zIndex: 1 }
|
|
4184
5693
|
},
|
|
4185
|
-
/* @__PURE__ */
|
|
5694
|
+
/* @__PURE__ */ React21.createElement(
|
|
4186
5695
|
MMDPlayerBase,
|
|
4187
5696
|
{
|
|
4188
5697
|
ref: playerRef,
|
|
@@ -4205,45 +5714,45 @@ var MMDARPlayer = forwardRef((props, ref) => {
|
|
|
4205
5714
|
}
|
|
4206
5715
|
)
|
|
4207
5716
|
),
|
|
4208
|
-
/* @__PURE__ */
|
|
5717
|
+
/* @__PURE__ */ React21.createElement("div", { className: "absolute inset-0 z-10 pointer-events-none flex flex-col justify-between p-6" }, /* @__PURE__ */ React21.createElement("div", { className: "flex justify-between items-start pointer-events-auto" }, cameraError ? /* @__PURE__ */ React21.createElement("div", { className: "bg-red-500/80 backdrop-blur-md text-white px-4 py-2 rounded-full flex items-center gap-2 text-sm" }, /* @__PURE__ */ React21.createElement(AlertCircle, { className: "w-4 h-4" }), cameraError, /* @__PURE__ */ React21.createElement(
|
|
4209
5718
|
"button",
|
|
4210
5719
|
{
|
|
4211
5720
|
onClick: () => startCamera(),
|
|
4212
5721
|
className: "ml-2 underline"
|
|
4213
5722
|
},
|
|
4214
5723
|
"\u91CD\u8BD5"
|
|
4215
|
-
)) : /* @__PURE__ */
|
|
5724
|
+
)) : /* @__PURE__ */ React21.createElement("div", { className: "bg-black/40 backdrop-blur-md text-white px-4 py-2 rounded-full flex items-center gap-2 text-sm" }, /* @__PURE__ */ React21.createElement(Camera, { className: "w-4 h-4 text-green-400" }), isCameraStarted ? "\u5B9E\u666F AR \u6A21\u5F0F\u5DF2\u5F00\u542F" : "\u7B49\u5F85\u6444\u50CF\u5934..."), /* @__PURE__ */ React21.createElement("div", { className: "flex flex-col gap-2" }, showSettings && /* @__PURE__ */ React21.createElement(
|
|
4216
5725
|
"button",
|
|
4217
5726
|
{
|
|
4218
5727
|
onClick: () => setIsSettingsOpen(!isSettingsOpen),
|
|
4219
5728
|
className: `p-3 backdrop-blur-md rounded-full text-white transition-all active:scale-95 pointer-events-auto ${isSettingsOpen ? "bg-blue-500" : "bg-white/10 hover:bg-white/20"}`,
|
|
4220
5729
|
title: "\u8BBE\u7F6E\u6A21\u578B\u4E0E\u52A8\u4F5C"
|
|
4221
5730
|
},
|
|
4222
|
-
/* @__PURE__ */
|
|
4223
|
-
), /* @__PURE__ */
|
|
5731
|
+
/* @__PURE__ */ React21.createElement(Settings, { className: "w-5 h-5" })
|
|
5732
|
+
), /* @__PURE__ */ React21.createElement(
|
|
4224
5733
|
"button",
|
|
4225
5734
|
{
|
|
4226
5735
|
onClick: switchCamera,
|
|
4227
5736
|
className: "p-3 bg-white/10 hover:bg-white/20 backdrop-blur-md rounded-full text-white transition-all active:scale-95 pointer-events-auto",
|
|
4228
5737
|
title: "\u5207\u6362\u524D\u540E\u6444\u50CF\u5934"
|
|
4229
5738
|
},
|
|
4230
|
-
/* @__PURE__ */
|
|
4231
|
-
), /* @__PURE__ */
|
|
5739
|
+
/* @__PURE__ */ React21.createElement(RefreshCw, { className: "w-5 h-5" })
|
|
5740
|
+
), /* @__PURE__ */ React21.createElement(
|
|
4232
5741
|
"button",
|
|
4233
5742
|
{
|
|
4234
5743
|
onClick: isCameraStarted ? stopCamera : () => startCamera(),
|
|
4235
5744
|
className: `p-3 backdrop-blur-md rounded-full text-white transition-all active:scale-95 pointer-events-auto ${isCameraStarted ? "bg-red-500/20 hover:bg-red-500/40" : "bg-green-500/20 hover:bg-green-500/40"}`,
|
|
4236
5745
|
title: isCameraStarted ? "\u5173\u95ED\u6444\u50CF\u5934" : "\u5F00\u542F\u6444\u50CF\u5934"
|
|
4237
5746
|
},
|
|
4238
|
-
isCameraStarted ? /* @__PURE__ */
|
|
4239
|
-
))), isSettingsOpen && /* @__PURE__ */
|
|
5747
|
+
isCameraStarted ? /* @__PURE__ */ React21.createElement(CameraOff, { className: "w-5 h-5" }) : /* @__PURE__ */ React21.createElement(Camera, { className: "w-5 h-5" })
|
|
5748
|
+
))), isSettingsOpen && /* @__PURE__ */ React21.createElement("div", { className: "absolute top-20 right-6 w-80 bg-black/80 backdrop-blur-xl border border-white/10 rounded-2xl p-6 pointer-events-auto shadow-2xl animate-in slide-in-from-right-4 duration-300" }, /* @__PURE__ */ React21.createElement("div", { className: "flex items-center justify-between mb-6" }, /* @__PURE__ */ React21.createElement("h3", { className: "text-white font-bold flex items-center gap-2" }, /* @__PURE__ */ React21.createElement(Settings, { className: "w-4 h-4" }), "\u8D44\u6E90\u8BBE\u7F6E"), /* @__PURE__ */ React21.createElement(
|
|
4240
5749
|
"button",
|
|
4241
5750
|
{
|
|
4242
5751
|
onClick: () => setIsSettingsOpen(false),
|
|
4243
5752
|
className: "p-1 hover:bg-white/10 rounded-full transition-colors text-white/60"
|
|
4244
5753
|
},
|
|
4245
|
-
/* @__PURE__ */
|
|
4246
|
-
)), /* @__PURE__ */
|
|
5754
|
+
/* @__PURE__ */ React21.createElement(X, { className: "w-4 h-4" })
|
|
5755
|
+
)), /* @__PURE__ */ React21.createElement("div", { className: "space-y-4" }, /* @__PURE__ */ React21.createElement("div", null, /* @__PURE__ */ React21.createElement("label", { className: "block text-xs font-medium text-white/50 mb-1.5 ml-1 uppercase tracking-wider" }, "\u6A21\u578B\u8DEF\u5F84 (.pmx)"), /* @__PURE__ */ React21.createElement(
|
|
4247
5756
|
"input",
|
|
4248
5757
|
{
|
|
4249
5758
|
type: "text",
|
|
@@ -4252,7 +5761,7 @@ var MMDARPlayer = forwardRef((props, ref) => {
|
|
|
4252
5761
|
placeholder: "\u8BF7\u8F93\u5165\u6A21\u578B URL...",
|
|
4253
5762
|
className: "w-full bg-white/5 border border-white/10 rounded-xl px-4 py-2.5 text-sm text-white placeholder:text-white/10 focus:outline-none focus:border-blue-500/50 transition-colors"
|
|
4254
5763
|
}
|
|
4255
|
-
)), /* @__PURE__ */
|
|
5764
|
+
)), /* @__PURE__ */ React21.createElement("div", null, /* @__PURE__ */ React21.createElement("label", { className: "block text-xs font-medium text-white/50 mb-1.5 ml-1 uppercase tracking-wider" }, "\u52A8\u4F5C\u8DEF\u5F84 (.vmd)"), /* @__PURE__ */ React21.createElement(
|
|
4256
5765
|
"input",
|
|
4257
5766
|
{
|
|
4258
5767
|
type: "text",
|
|
@@ -4261,7 +5770,7 @@ var MMDARPlayer = forwardRef((props, ref) => {
|
|
|
4261
5770
|
placeholder: "\u8BF7\u8F93\u5165\u52A8\u4F5C URL...",
|
|
4262
5771
|
className: "w-full bg-white/5 border border-white/10 rounded-xl px-4 py-2.5 text-sm text-white placeholder:text-white/10 focus:outline-none focus:border-blue-500/50 transition-colors"
|
|
4263
5772
|
}
|
|
4264
|
-
)), /* @__PURE__ */
|
|
5773
|
+
)), /* @__PURE__ */ React21.createElement("div", { className: "pt-2" }, /* @__PURE__ */ React21.createElement(
|
|
4265
5774
|
"button",
|
|
4266
5775
|
{
|
|
4267
5776
|
onClick: () => {
|
|
@@ -4272,13 +5781,557 @@ var MMDARPlayer = forwardRef((props, ref) => {
|
|
|
4272
5781
|
},
|
|
4273
5782
|
className: "w-full bg-blue-500 hover:bg-blue-600 text-white font-bold py-3 rounded-xl flex items-center justify-center gap-2 transition-all active:scale-95 shadow-lg shadow-blue-500/20"
|
|
4274
5783
|
},
|
|
4275
|
-
/* @__PURE__ */
|
|
5784
|
+
/* @__PURE__ */ React21.createElement(Check, { className: "w-4 h-4" }),
|
|
4276
5785
|
"\u5E94\u7528\u66F4\u6539"
|
|
4277
|
-
)))), isLoading && /* @__PURE__ */
|
|
5786
|
+
)))), isLoading && /* @__PURE__ */ React21.createElement("div", { className: "absolute inset-0 flex items-center justify-center pointer-events-none" }, /* @__PURE__ */ React21.createElement("div", { className: "flex flex-col items-center gap-3" }, /* @__PURE__ */ React21.createElement("div", { className: "w-12 h-12 border-4 border-blue-500/30 border-t-blue-500 rounded-full animate-spin" }), /* @__PURE__ */ React21.createElement("div", { className: "text-white text-sm font-medium bg-black/40 px-3 py-1 rounded-full backdrop-blur-sm" }, "\u6B63\u5728\u52A0\u8F7D Miku \u8D44\u6E90..."))))
|
|
4278
5787
|
);
|
|
4279
5788
|
});
|
|
4280
5789
|
MMDARPlayer.displayName = "MMDARPlayer";
|
|
4281
5790
|
|
|
4282
|
-
|
|
5791
|
+
// src/mmd/fx/utils.ts
|
|
5792
|
+
function exportFXToJSON(effect) {
|
|
5793
|
+
return JSON.stringify(effect, null, 2);
|
|
5794
|
+
}
|
|
5795
|
+
function exportFXToMarkdown(effect) {
|
|
5796
|
+
const lines = [];
|
|
5797
|
+
lines.push(`# ${effect.fileName}`);
|
|
5798
|
+
lines.push("");
|
|
5799
|
+
if (effect.defines.length > 0) {
|
|
5800
|
+
lines.push("## \u5B8F\u5B9A\u4E49 (Defines)");
|
|
5801
|
+
lines.push("");
|
|
5802
|
+
lines.push("| \u540D\u79F0 | \u503C | \u72B6\u6001 | \u8BF4\u660E |");
|
|
5803
|
+
lines.push("|------|-----|------|------|");
|
|
5804
|
+
effect.defines.forEach((d) => {
|
|
5805
|
+
const status = d.isCommented ? "\u7981\u7528" : "\u542F\u7528";
|
|
5806
|
+
lines.push(`| ${d.name} | ${d.value || "-"} | ${status} | ${d.comment || "-"} |`);
|
|
5807
|
+
});
|
|
5808
|
+
lines.push("");
|
|
5809
|
+
}
|
|
5810
|
+
if (effect.textures.length > 0) {
|
|
5811
|
+
lines.push("## \u7EB9\u7406 (Textures)");
|
|
5812
|
+
lines.push("");
|
|
5813
|
+
lines.push("| \u540D\u79F0 | \u8DEF\u5F84 | \u5C3A\u5BF8 | \u7528\u9014 |");
|
|
5814
|
+
lines.push("|------|------|------|------|");
|
|
5815
|
+
effect.textures.forEach((t) => {
|
|
5816
|
+
const size = t.width && t.height ? `${t.width}x${t.height}` : "-";
|
|
5817
|
+
lines.push(`| ${t.name} | ${t.path} | ${size} | ${t.purpose || "-"} |`);
|
|
5818
|
+
});
|
|
5819
|
+
lines.push("");
|
|
5820
|
+
}
|
|
5821
|
+
if (effect.parameters.length > 0) {
|
|
5822
|
+
lines.push("## \u53C2\u6570 (Parameters)");
|
|
5823
|
+
lines.push("");
|
|
5824
|
+
lines.push("| \u540D\u79F0 | \u7C7B\u578B | \u8BED\u4E49 | \u9ED8\u8BA4\u503C |");
|
|
5825
|
+
lines.push("|------|------|------|--------|");
|
|
5826
|
+
effect.parameters.forEach((p) => {
|
|
5827
|
+
lines.push(`| ${p.name} | ${p.type} | ${p.semantic || "-"} | ${p.defaultValue || "-"} |`);
|
|
5828
|
+
});
|
|
5829
|
+
lines.push("");
|
|
5830
|
+
}
|
|
5831
|
+
if (effect.controllers.length > 0) {
|
|
5832
|
+
lines.push("## \u63A7\u5236\u5668 (Controllers)");
|
|
5833
|
+
lines.push("");
|
|
5834
|
+
lines.push("| \u53C2\u6570 | \u5BF9\u8C61\u540D | \u63A7\u5236\u9879 |");
|
|
5835
|
+
lines.push("|------|--------|--------|");
|
|
5836
|
+
effect.controllers.forEach((c) => {
|
|
5837
|
+
lines.push(`| ${c.name} | ${c.objectName} | ${c.itemName} |`);
|
|
5838
|
+
});
|
|
5839
|
+
lines.push("");
|
|
5840
|
+
}
|
|
5841
|
+
if (effect.includes.length > 0) {
|
|
5842
|
+
lines.push("## \u5305\u542B\u6587\u4EF6 (Includes)");
|
|
5843
|
+
lines.push("");
|
|
5844
|
+
effect.includes.forEach((inc) => {
|
|
5845
|
+
lines.push(`- ${inc}`);
|
|
5846
|
+
});
|
|
5847
|
+
lines.push("");
|
|
5848
|
+
}
|
|
5849
|
+
if (effect.techniques.length > 0) {
|
|
5850
|
+
lines.push("## \u6280\u672F (Techniques)");
|
|
5851
|
+
lines.push("");
|
|
5852
|
+
effect.techniques.forEach((t) => {
|
|
5853
|
+
lines.push(`### ${t.name}`);
|
|
5854
|
+
lines.push("");
|
|
5855
|
+
t.passes.forEach((p, idx) => {
|
|
5856
|
+
lines.push(`#### Pass ${p.name || idx + 1}`);
|
|
5857
|
+
if (p.vertexShader) {
|
|
5858
|
+
lines.push(`- **\u9876\u70B9\u7740\u8272\u5668**: ${p.vertexShader.function} (${p.vertexShader.profile})`);
|
|
5859
|
+
}
|
|
5860
|
+
if (p.pixelShader) {
|
|
5861
|
+
lines.push(`- **\u50CF\u7D20\u7740\u8272\u5668**: ${p.pixelShader.function} (${p.pixelShader.profile})`);
|
|
5862
|
+
}
|
|
5863
|
+
lines.push("");
|
|
5864
|
+
});
|
|
5865
|
+
});
|
|
5866
|
+
}
|
|
5867
|
+
return lines.join("\n");
|
|
5868
|
+
}
|
|
5869
|
+
function compareFXEffects(effect1, effect2) {
|
|
5870
|
+
const diff = {
|
|
5871
|
+
addedDefines: [],
|
|
5872
|
+
removedDefines: [],
|
|
5873
|
+
changedDefines: [],
|
|
5874
|
+
addedTextures: [],
|
|
5875
|
+
removedTextures: [],
|
|
5876
|
+
addedParameters: [],
|
|
5877
|
+
removedParameters: []
|
|
5878
|
+
};
|
|
5879
|
+
const defines1Map = new Map(effect1.defines.map((d) => [d.name, d]));
|
|
5880
|
+
const defines2Map = new Map(effect2.defines.map((d) => [d.name, d]));
|
|
5881
|
+
defines2Map.forEach((define, name) => {
|
|
5882
|
+
if (!defines1Map.has(name)) {
|
|
5883
|
+
diff.addedDefines.push(name);
|
|
5884
|
+
} else {
|
|
5885
|
+
const oldDefine = defines1Map.get(name);
|
|
5886
|
+
if (oldDefine.value !== define.value || oldDefine.isCommented !== define.isCommented) {
|
|
5887
|
+
diff.changedDefines.push({
|
|
5888
|
+
name,
|
|
5889
|
+
oldValue: oldDefine.value,
|
|
5890
|
+
newValue: define.value
|
|
5891
|
+
});
|
|
5892
|
+
}
|
|
5893
|
+
}
|
|
5894
|
+
});
|
|
5895
|
+
defines1Map.forEach((define, name) => {
|
|
5896
|
+
if (!defines2Map.has(name)) {
|
|
5897
|
+
diff.removedDefines.push(name);
|
|
5898
|
+
}
|
|
5899
|
+
});
|
|
5900
|
+
const textures1Set = new Set(effect1.textures.map((t) => t.name));
|
|
5901
|
+
const textures2Set = new Set(effect2.textures.map((t) => t.name));
|
|
5902
|
+
textures2Set.forEach((name) => {
|
|
5903
|
+
if (!textures1Set.has(name)) {
|
|
5904
|
+
diff.addedTextures.push(name);
|
|
5905
|
+
}
|
|
5906
|
+
});
|
|
5907
|
+
textures1Set.forEach((name) => {
|
|
5908
|
+
if (!textures2Set.has(name)) {
|
|
5909
|
+
diff.removedTextures.push(name);
|
|
5910
|
+
}
|
|
5911
|
+
});
|
|
5912
|
+
const params1Set = new Set(effect1.parameters.map((p) => p.name));
|
|
5913
|
+
const params2Set = new Set(effect2.parameters.map((p) => p.name));
|
|
5914
|
+
params2Set.forEach((name) => {
|
|
5915
|
+
if (!params1Set.has(name)) {
|
|
5916
|
+
diff.addedParameters.push(name);
|
|
5917
|
+
}
|
|
5918
|
+
});
|
|
5919
|
+
params1Set.forEach((name) => {
|
|
5920
|
+
if (!params2Set.has(name)) {
|
|
5921
|
+
diff.removedParameters.push(name);
|
|
5922
|
+
}
|
|
5923
|
+
});
|
|
5924
|
+
return diff;
|
|
5925
|
+
}
|
|
5926
|
+
function filterDefinesByPrefix(defines, prefix) {
|
|
5927
|
+
return defines.filter((d) => d.name.startsWith(prefix));
|
|
5928
|
+
}
|
|
5929
|
+
function getTextureDefines(defines) {
|
|
5930
|
+
return filterDefinesByPrefix(defines, "BLEND");
|
|
5931
|
+
}
|
|
5932
|
+
function getFeatureFlags(defines) {
|
|
5933
|
+
return defines.filter(
|
|
5934
|
+
(d) => d.name.startsWith("USE_") || d.name.includes("FLAG") || d.name === "HANDLE_EDGE" || d.name === "MODEL_TOON"
|
|
5935
|
+
);
|
|
5936
|
+
}
|
|
5937
|
+
function getColorParameters(parameters) {
|
|
5938
|
+
return parameters.filter(
|
|
5939
|
+
(p) => p.name.toLowerCase().includes("color") || p.name.toLowerCase().includes("rgb") || p.name.toLowerCase().includes("hsv") || p.type === "float3" || p.type === "float4"
|
|
5940
|
+
);
|
|
5941
|
+
}
|
|
5942
|
+
function hasFeature(effect, featureName) {
|
|
5943
|
+
return effect.defines.some(
|
|
5944
|
+
(d) => d.name === featureName && !d.isCommented
|
|
5945
|
+
);
|
|
5946
|
+
}
|
|
5947
|
+
function getConfigSummaryText(effect) {
|
|
5948
|
+
const features = [];
|
|
5949
|
+
if (hasFeature(effect, "USE_LOCALSHADOW")) {
|
|
5950
|
+
features.push("LocalShadow");
|
|
5951
|
+
}
|
|
5952
|
+
if (hasFeature(effect, "USE_EXCELLENTSHADOW")) {
|
|
5953
|
+
features.push("ExcellentShadow");
|
|
5954
|
+
}
|
|
5955
|
+
if (hasFeature(effect, "USE_HGSHADOW")) {
|
|
5956
|
+
features.push("HgShadow");
|
|
5957
|
+
}
|
|
5958
|
+
if (hasFeature(effect, "HANDLE_EDGE")) {
|
|
5959
|
+
features.push("HandleEdge");
|
|
5960
|
+
}
|
|
5961
|
+
if (hasFeature(effect, "MODEL_TOON")) {
|
|
5962
|
+
features.push("ModelToon");
|
|
5963
|
+
}
|
|
5964
|
+
if (hasFeature(effect, "USE_ROUNDNORMAL")) {
|
|
5965
|
+
features.push("RoundNormal");
|
|
5966
|
+
}
|
|
5967
|
+
return features.length > 0 ? `\u542F\u7528\u529F\u80FD: ${features.join(", ")}` : "\u65E0\u7279\u6B8A\u529F\u80FD\u542F\u7528";
|
|
5968
|
+
}
|
|
5969
|
+
function extractTexturePaths(effect) {
|
|
5970
|
+
return effect.textures.map((t) => t.path);
|
|
5971
|
+
}
|
|
5972
|
+
function validateFXEffect(effect) {
|
|
5973
|
+
const errors = [];
|
|
5974
|
+
const warnings = [];
|
|
5975
|
+
if (effect.includes.length === 0) {
|
|
5976
|
+
warnings.push("\u6CA1\u6709\u627E\u5230include\u6307\u4EE4");
|
|
5977
|
+
}
|
|
5978
|
+
effect.textures.forEach((texture) => {
|
|
5979
|
+
if (!texture.path || texture.path === "") {
|
|
5980
|
+
errors.push(`\u7EB9\u7406 ${texture.name} \u7F3A\u5C11\u8DEF\u5F84`);
|
|
5981
|
+
}
|
|
5982
|
+
});
|
|
5983
|
+
effect.parameters.forEach((param) => {
|
|
5984
|
+
if (param.type.includes("float") && param.defaultValue) {
|
|
5985
|
+
const floatRegex = /^float[234]?\s*\(/;
|
|
5986
|
+
if (!floatRegex.test(param.defaultValue) && isNaN(parseFloat(param.defaultValue))) {
|
|
5987
|
+
warnings.push(`\u53C2\u6570 ${param.name} \u7684\u9ED8\u8BA4\u503C\u53EF\u80FD\u4E0D\u5408\u6CD5: ${param.defaultValue}`);
|
|
5988
|
+
}
|
|
5989
|
+
}
|
|
5990
|
+
});
|
|
5991
|
+
if (effect.techniques.length === 0 && effect.includes.length === 0) {
|
|
5992
|
+
warnings.push("\u6CA1\u6709\u627E\u5230Technique\u5B9A\u4E49\uFF0C\u4E5F\u6CA1\u6709include\u6587\u4EF6");
|
|
5993
|
+
}
|
|
5994
|
+
return {
|
|
5995
|
+
isValid: errors.length === 0,
|
|
5996
|
+
errors,
|
|
5997
|
+
warnings
|
|
5998
|
+
};
|
|
5999
|
+
}
|
|
6000
|
+
var FXViewer = ({
|
|
6001
|
+
source,
|
|
6002
|
+
isContent = false,
|
|
6003
|
+
fileName = "effect.fx",
|
|
6004
|
+
onParsed,
|
|
6005
|
+
onError,
|
|
6006
|
+
className = ""
|
|
6007
|
+
}) => {
|
|
6008
|
+
const [effect, setEffect] = useState(null);
|
|
6009
|
+
const [summary, setSummary] = useState(null);
|
|
6010
|
+
const [loading, setLoading] = useState(true);
|
|
6011
|
+
const [error, setError] = useState(null);
|
|
6012
|
+
const [activeTab, setActiveTab] = useState("summary");
|
|
6013
|
+
useEffect(() => {
|
|
6014
|
+
const parse = async () => {
|
|
6015
|
+
try {
|
|
6016
|
+
setLoading(true);
|
|
6017
|
+
setError(null);
|
|
6018
|
+
const parser = new FXParser();
|
|
6019
|
+
let parsedEffect;
|
|
6020
|
+
if (isContent) {
|
|
6021
|
+
parsedEffect = parser.parse(source, fileName);
|
|
6022
|
+
} else {
|
|
6023
|
+
parsedEffect = await parser.loadAndParse(source);
|
|
6024
|
+
}
|
|
6025
|
+
setEffect(parsedEffect);
|
|
6026
|
+
setSummary(parser.generateSummary(parsedEffect));
|
|
6027
|
+
onParsed?.(parsedEffect);
|
|
6028
|
+
} catch (err) {
|
|
6029
|
+
const errorMsg = err instanceof Error ? err.message : "\u89E3\u6790\u5931\u8D25";
|
|
6030
|
+
setError(errorMsg);
|
|
6031
|
+
onError?.(err instanceof Error ? err : new Error(errorMsg));
|
|
6032
|
+
} finally {
|
|
6033
|
+
setLoading(false);
|
|
6034
|
+
}
|
|
6035
|
+
};
|
|
6036
|
+
parse();
|
|
6037
|
+
}, [source, isContent, fileName, onParsed, onError]);
|
|
6038
|
+
if (loading) {
|
|
6039
|
+
return /* @__PURE__ */ React21.createElement("div", { className: `fx-viewer loading ${className}` }, /* @__PURE__ */ React21.createElement("div", { className: "fx-viewer-spinner" }, "\u52A0\u8F7D\u4E2D..."));
|
|
6040
|
+
}
|
|
6041
|
+
if (error || !effect || !summary) {
|
|
6042
|
+
return /* @__PURE__ */ React21.createElement("div", { className: `fx-viewer error ${className}` }, /* @__PURE__ */ React21.createElement("div", { className: "fx-viewer-error" }, /* @__PURE__ */ React21.createElement("h3", null, "\u274C \u89E3\u6790\u9519\u8BEF"), /* @__PURE__ */ React21.createElement("p", null, error || "\u672A\u77E5\u9519\u8BEF")));
|
|
6043
|
+
}
|
|
6044
|
+
const validation = validateFXEffect(effect);
|
|
6045
|
+
return /* @__PURE__ */ React21.createElement("div", { className: `fx-viewer ${className}` }, /* @__PURE__ */ React21.createElement("div", { className: "fx-viewer-header" }, /* @__PURE__ */ React21.createElement("h2", null, effect.fileName), /* @__PURE__ */ React21.createElement("p", { className: "fx-viewer-config-summary" }, getConfigSummaryText(effect))), /* @__PURE__ */ React21.createElement("div", { className: "fx-viewer-tabs" }, /* @__PURE__ */ React21.createElement(
|
|
6046
|
+
"button",
|
|
6047
|
+
{
|
|
6048
|
+
className: activeTab === "summary" ? "active" : "",
|
|
6049
|
+
onClick: () => setActiveTab("summary")
|
|
6050
|
+
},
|
|
6051
|
+
"\u6458\u8981"
|
|
6052
|
+
), /* @__PURE__ */ React21.createElement(
|
|
6053
|
+
"button",
|
|
6054
|
+
{
|
|
6055
|
+
className: activeTab === "defines" ? "active" : "",
|
|
6056
|
+
onClick: () => setActiveTab("defines")
|
|
6057
|
+
},
|
|
6058
|
+
"\u5B8F\u5B9A\u4E49 (",
|
|
6059
|
+
summary.defineCount,
|
|
6060
|
+
")"
|
|
6061
|
+
), /* @__PURE__ */ React21.createElement(
|
|
6062
|
+
"button",
|
|
6063
|
+
{
|
|
6064
|
+
className: activeTab === "textures" ? "active" : "",
|
|
6065
|
+
onClick: () => setActiveTab("textures")
|
|
6066
|
+
},
|
|
6067
|
+
"\u7EB9\u7406 (",
|
|
6068
|
+
summary.textureCount,
|
|
6069
|
+
")"
|
|
6070
|
+
), /* @__PURE__ */ React21.createElement(
|
|
6071
|
+
"button",
|
|
6072
|
+
{
|
|
6073
|
+
className: activeTab === "parameters" ? "active" : "",
|
|
6074
|
+
onClick: () => setActiveTab("parameters")
|
|
6075
|
+
},
|
|
6076
|
+
"\u53C2\u6570 (",
|
|
6077
|
+
summary.parameterCount,
|
|
6078
|
+
")"
|
|
6079
|
+
), /* @__PURE__ */ React21.createElement(
|
|
6080
|
+
"button",
|
|
6081
|
+
{
|
|
6082
|
+
className: activeTab === "validation" ? "active" : "",
|
|
6083
|
+
onClick: () => setActiveTab("validation")
|
|
6084
|
+
},
|
|
6085
|
+
"\u9A8C\u8BC1"
|
|
6086
|
+
)), /* @__PURE__ */ React21.createElement("div", { className: "fx-viewer-content" }, activeTab === "summary" && /* @__PURE__ */ React21.createElement(SummaryTab, { summary, effect }), activeTab === "defines" && /* @__PURE__ */ React21.createElement(DefinesTab, { effect }), activeTab === "textures" && /* @__PURE__ */ React21.createElement(TexturesTab, { effect }), activeTab === "parameters" && /* @__PURE__ */ React21.createElement(ParametersTab, { effect }), activeTab === "validation" && /* @__PURE__ */ React21.createElement(ValidationTab, { validation })));
|
|
6087
|
+
};
|
|
6088
|
+
var SummaryTab = ({ summary, effect }) => /* @__PURE__ */ React21.createElement("div", { className: "fx-tab-summary" }, /* @__PURE__ */ React21.createElement("div", { className: "fx-stats-grid" }, /* @__PURE__ */ React21.createElement("div", { className: "fx-stat-card" }, /* @__PURE__ */ React21.createElement("div", { className: "fx-stat-label" }, "\u5B8F\u5B9A\u4E49"), /* @__PURE__ */ React21.createElement("div", { className: "fx-stat-value" }, summary.defineCount)), /* @__PURE__ */ React21.createElement("div", { className: "fx-stat-card" }, /* @__PURE__ */ React21.createElement("div", { className: "fx-stat-label" }, "\u53C2\u6570"), /* @__PURE__ */ React21.createElement("div", { className: "fx-stat-value" }, summary.parameterCount)), /* @__PURE__ */ React21.createElement("div", { className: "fx-stat-card" }, /* @__PURE__ */ React21.createElement("div", { className: "fx-stat-label" }, "\u7EB9\u7406"), /* @__PURE__ */ React21.createElement("div", { className: "fx-stat-value" }, summary.textureCount)), /* @__PURE__ */ React21.createElement("div", { className: "fx-stat-card" }, /* @__PURE__ */ React21.createElement("div", { className: "fx-stat-label" }, "Technique"), /* @__PURE__ */ React21.createElement("div", { className: "fx-stat-value" }, summary.techniqueCount))), /* @__PURE__ */ React21.createElement("div", { className: "fx-features" }, /* @__PURE__ */ React21.createElement("h3", null, "\u529F\u80FD\u7279\u6027"), /* @__PURE__ */ React21.createElement("div", { className: "fx-feature-list" }, /* @__PURE__ */ React21.createElement("div", { className: `fx-feature ${summary.hasLocalShadow ? "enabled" : "disabled"}` }, summary.hasLocalShadow ? "\u2713" : "\u2717", " LocalShadow"), /* @__PURE__ */ React21.createElement("div", { className: `fx-feature ${summary.hasExcellentShadow ? "enabled" : "disabled"}` }, summary.hasExcellentShadow ? "\u2713" : "\u2717", " ExcellentShadow"), /* @__PURE__ */ React21.createElement("div", { className: `fx-feature ${summary.hasHgShadow ? "enabled" : "disabled"}` }, summary.hasHgShadow ? "\u2713" : "\u2717", " HgShadow"))), effect.includes.length > 0 && /* @__PURE__ */ React21.createElement("div", { className: "fx-includes" }, /* @__PURE__ */ React21.createElement("h3", null, "\u5305\u542B\u6587\u4EF6"), /* @__PURE__ */ React21.createElement("ul", null, effect.includes.map((inc, idx) => /* @__PURE__ */ React21.createElement("li", { key: idx }, /* @__PURE__ */ React21.createElement("code", null, inc))))), effect.controllers.length > 0 && /* @__PURE__ */ React21.createElement("div", { className: "fx-controllers" }, /* @__PURE__ */ React21.createElement("h3", null, "\u63A7\u5236\u5668"), /* @__PURE__ */ React21.createElement("ul", null, effect.controllers.map((ctrl, idx) => /* @__PURE__ */ React21.createElement("li", { key: idx }, /* @__PURE__ */ React21.createElement("strong", null, ctrl.name), ": ", ctrl.objectName, " / ", ctrl.itemName)))));
|
|
6089
|
+
var DefinesTab = ({ effect }) => /* @__PURE__ */ React21.createElement("div", { className: "fx-tab-defines" }, /* @__PURE__ */ React21.createElement("table", { className: "fx-table" }, /* @__PURE__ */ React21.createElement("thead", null, /* @__PURE__ */ React21.createElement("tr", null, /* @__PURE__ */ React21.createElement("th", null, "\u540D\u79F0"), /* @__PURE__ */ React21.createElement("th", null, "\u503C"), /* @__PURE__ */ React21.createElement("th", null, "\u72B6\u6001"), /* @__PURE__ */ React21.createElement("th", null, "\u884C\u53F7"))), /* @__PURE__ */ React21.createElement("tbody", null, effect.defines.map((define, idx) => /* @__PURE__ */ React21.createElement("tr", { key: idx, className: define.isCommented ? "disabled" : "enabled" }, /* @__PURE__ */ React21.createElement("td", null, /* @__PURE__ */ React21.createElement("code", null, define.name)), /* @__PURE__ */ React21.createElement("td", null, define.value || "-"), /* @__PURE__ */ React21.createElement("td", null, /* @__PURE__ */ React21.createElement("span", { className: `status-badge ${define.isCommented ? "disabled" : "enabled"}` }, define.isCommented ? "\u7981\u7528" : "\u542F\u7528")), /* @__PURE__ */ React21.createElement("td", null, define.lineNumber))))));
|
|
6090
|
+
var TexturesTab = ({ effect }) => /* @__PURE__ */ React21.createElement("div", { className: "fx-tab-textures" }, effect.textures.length === 0 ? /* @__PURE__ */ React21.createElement("p", { className: "fx-empty-message" }, "\u672A\u627E\u5230\u7EB9\u7406\u5B9A\u4E49") : /* @__PURE__ */ React21.createElement("table", { className: "fx-table" }, /* @__PURE__ */ React21.createElement("thead", null, /* @__PURE__ */ React21.createElement("tr", null, /* @__PURE__ */ React21.createElement("th", null, "\u540D\u79F0"), /* @__PURE__ */ React21.createElement("th", null, "\u8DEF\u5F84"), /* @__PURE__ */ React21.createElement("th", null, "\u5C3A\u5BF8"), /* @__PURE__ */ React21.createElement("th", null, "\u7528\u9014"))), /* @__PURE__ */ React21.createElement("tbody", null, effect.textures.map((texture, idx) => /* @__PURE__ */ React21.createElement("tr", { key: idx }, /* @__PURE__ */ React21.createElement("td", null, /* @__PURE__ */ React21.createElement("code", null, texture.name)), /* @__PURE__ */ React21.createElement("td", null, texture.path), /* @__PURE__ */ React21.createElement("td", null, texture.width && texture.height ? `${texture.width}\xD7${texture.height}` : "-"), /* @__PURE__ */ React21.createElement("td", null, texture.purpose || "-"))))));
|
|
6091
|
+
var ParametersTab = ({ effect }) => /* @__PURE__ */ React21.createElement("div", { className: "fx-tab-parameters" }, /* @__PURE__ */ React21.createElement("table", { className: "fx-table" }, /* @__PURE__ */ React21.createElement("thead", null, /* @__PURE__ */ React21.createElement("tr", null, /* @__PURE__ */ React21.createElement("th", null, "\u540D\u79F0"), /* @__PURE__ */ React21.createElement("th", null, "\u7C7B\u578B"), /* @__PURE__ */ React21.createElement("th", null, "\u8BED\u4E49"), /* @__PURE__ */ React21.createElement("th", null, "\u9ED8\u8BA4\u503C"), /* @__PURE__ */ React21.createElement("th", null, "\u884C\u53F7"))), /* @__PURE__ */ React21.createElement("tbody", null, effect.parameters.map((param, idx) => /* @__PURE__ */ React21.createElement("tr", { key: idx }, /* @__PURE__ */ React21.createElement("td", null, /* @__PURE__ */ React21.createElement("code", null, param.name)), /* @__PURE__ */ React21.createElement("td", null, /* @__PURE__ */ React21.createElement("code", null, param.type)), /* @__PURE__ */ React21.createElement("td", null, param.semantic || "-"), /* @__PURE__ */ React21.createElement("td", null, param.defaultValue || "-"), /* @__PURE__ */ React21.createElement("td", null, param.lineNumber))))));
|
|
6092
|
+
var ValidationTab = ({ validation }) => /* @__PURE__ */ React21.createElement("div", { className: "fx-tab-validation" }, /* @__PURE__ */ React21.createElement("div", { className: `fx-validation-status ${validation.isValid ? "valid" : "invalid"}` }, /* @__PURE__ */ React21.createElement("h3", null, validation.isValid ? "\u2713 \u9A8C\u8BC1\u901A\u8FC7" : "\u2717 \u9A8C\u8BC1\u5931\u8D25")), validation.errors.length > 0 && /* @__PURE__ */ React21.createElement("div", { className: "fx-validation-section fx-errors" }, /* @__PURE__ */ React21.createElement("h4", null, "\u9519\u8BEF (", validation.errors.length, ")"), /* @__PURE__ */ React21.createElement("ul", null, validation.errors.map((error, idx) => /* @__PURE__ */ React21.createElement("li", { key: idx, className: "fx-error-item" }, error)))), validation.warnings.length > 0 && /* @__PURE__ */ React21.createElement("div", { className: "fx-validation-section fx-warnings" }, /* @__PURE__ */ React21.createElement("h4", null, "\u8B66\u544A (", validation.warnings.length, ")"), /* @__PURE__ */ React21.createElement("ul", null, validation.warnings.map((warning, idx) => /* @__PURE__ */ React21.createElement("li", { key: idx, className: "fx-warning-item" }, warning)))), validation.isValid && validation.warnings.length === 0 && /* @__PURE__ */ React21.createElement("p", { className: "fx-success-message" }, "FX\u6587\u4EF6\u7ED3\u6784\u5B8C\u6574\uFF0C\u6CA1\u6709\u53D1\u73B0\u95EE\u9898\u3002"));
|
|
6093
|
+
FXViewer.displayName = "FXViewer";
|
|
6094
|
+
var FXThreePreview = ({
|
|
6095
|
+
effect,
|
|
6096
|
+
texturePath = "",
|
|
6097
|
+
objectType = "sphere",
|
|
6098
|
+
className = "",
|
|
6099
|
+
showInfo = true
|
|
6100
|
+
}) => {
|
|
6101
|
+
const containerRef = useRef(null);
|
|
6102
|
+
const sceneRef = useRef(null);
|
|
6103
|
+
const rendererRef = useRef(null);
|
|
6104
|
+
const cameraRef = useRef(null);
|
|
6105
|
+
const controlsRef = useRef(null);
|
|
6106
|
+
const animationIdRef = useRef(null);
|
|
6107
|
+
const meshRef = useRef(null);
|
|
6108
|
+
const [loading, setLoading] = useState(true);
|
|
6109
|
+
const [error, setError] = useState(null);
|
|
6110
|
+
const [adapterInfo, setAdapterInfo] = useState(null);
|
|
6111
|
+
useEffect(() => {
|
|
6112
|
+
if (!containerRef.current) return;
|
|
6113
|
+
const init = async () => {
|
|
6114
|
+
try {
|
|
6115
|
+
setLoading(true);
|
|
6116
|
+
setError(null);
|
|
6117
|
+
const container = containerRef.current;
|
|
6118
|
+
const width = container.clientWidth || 600;
|
|
6119
|
+
const height = container.clientHeight || 400;
|
|
6120
|
+
const scene = new THREE3.Scene();
|
|
6121
|
+
scene.background = new THREE3.Color(1710638);
|
|
6122
|
+
sceneRef.current = scene;
|
|
6123
|
+
const camera = new THREE3.PerspectiveCamera(45, width / height, 0.1, 1e3);
|
|
6124
|
+
camera.position.set(0, 2, 5);
|
|
6125
|
+
cameraRef.current = camera;
|
|
6126
|
+
const renderer = new THREE3.WebGLRenderer({ antialias: true });
|
|
6127
|
+
renderer.setSize(width, height);
|
|
6128
|
+
renderer.setPixelRatio(window.devicePixelRatio);
|
|
6129
|
+
container.appendChild(renderer.domElement);
|
|
6130
|
+
rendererRef.current = renderer;
|
|
6131
|
+
const controls = new OrbitControls(camera, renderer.domElement);
|
|
6132
|
+
controls.enableDamping = true;
|
|
6133
|
+
controls.dampingFactor = 0.05;
|
|
6134
|
+
controlsRef.current = controls;
|
|
6135
|
+
const adapter = new FXToThreeAdapter(effect, texturePath);
|
|
6136
|
+
console.log("Loading textures...");
|
|
6137
|
+
await adapter.loadTextures();
|
|
6138
|
+
adapter.configureScene(scene, renderer);
|
|
6139
|
+
let geometry;
|
|
6140
|
+
switch (objectType) {
|
|
6141
|
+
case "box":
|
|
6142
|
+
geometry = new THREE3.BoxGeometry(2, 2, 2);
|
|
6143
|
+
break;
|
|
6144
|
+
case "torus":
|
|
6145
|
+
geometry = new THREE3.TorusGeometry(1, 0.4, 16, 100);
|
|
6146
|
+
break;
|
|
6147
|
+
case "plane":
|
|
6148
|
+
geometry = new THREE3.PlaneGeometry(3, 3);
|
|
6149
|
+
break;
|
|
6150
|
+
default:
|
|
6151
|
+
geometry = new THREE3.SphereGeometry(1.5, 32, 32);
|
|
6152
|
+
}
|
|
6153
|
+
const material = adapter.createMaterial();
|
|
6154
|
+
const mesh = new THREE3.Mesh(geometry, material);
|
|
6155
|
+
mesh.castShadow = true;
|
|
6156
|
+
mesh.receiveShadow = true;
|
|
6157
|
+
scene.add(mesh);
|
|
6158
|
+
meshRef.current = mesh;
|
|
6159
|
+
const renderConfig = adapter.extractRenderConfig();
|
|
6160
|
+
if (renderConfig.enableShadow) {
|
|
6161
|
+
const groundGeometry = new THREE3.PlaneGeometry(10, 10);
|
|
6162
|
+
const groundMaterial = new THREE3.MeshStandardMaterial({ color: 2763326 });
|
|
6163
|
+
const ground = new THREE3.Mesh(groundGeometry, groundMaterial);
|
|
6164
|
+
ground.rotation.x = -Math.PI / 2;
|
|
6165
|
+
ground.position.y = -2;
|
|
6166
|
+
ground.receiveShadow = true;
|
|
6167
|
+
scene.add(ground);
|
|
6168
|
+
}
|
|
6169
|
+
setAdapterInfo(adapter.getSummary());
|
|
6170
|
+
const animate = () => {
|
|
6171
|
+
animationIdRef.current = requestAnimationFrame(animate);
|
|
6172
|
+
if (meshRef.current) {
|
|
6173
|
+
meshRef.current.rotation.y += 5e-3;
|
|
6174
|
+
}
|
|
6175
|
+
if (controlsRef.current) {
|
|
6176
|
+
controlsRef.current.update();
|
|
6177
|
+
}
|
|
6178
|
+
if (rendererRef.current && sceneRef.current && cameraRef.current) {
|
|
6179
|
+
rendererRef.current.render(sceneRef.current, cameraRef.current);
|
|
6180
|
+
}
|
|
6181
|
+
};
|
|
6182
|
+
animate();
|
|
6183
|
+
setLoading(false);
|
|
6184
|
+
} catch (err) {
|
|
6185
|
+
console.error("FX Preview initialization error:", err);
|
|
6186
|
+
setError(err instanceof Error ? err.message : "\u521D\u59CB\u5316\u5931\u8D25");
|
|
6187
|
+
setLoading(false);
|
|
6188
|
+
}
|
|
6189
|
+
};
|
|
6190
|
+
init();
|
|
6191
|
+
return () => {
|
|
6192
|
+
if (animationIdRef.current) {
|
|
6193
|
+
cancelAnimationFrame(animationIdRef.current);
|
|
6194
|
+
}
|
|
6195
|
+
if (rendererRef.current) {
|
|
6196
|
+
rendererRef.current.dispose();
|
|
6197
|
+
if (containerRef.current && rendererRef.current.domElement) {
|
|
6198
|
+
containerRef.current.removeChild(rendererRef.current.domElement);
|
|
6199
|
+
}
|
|
6200
|
+
}
|
|
6201
|
+
if (sceneRef.current) {
|
|
6202
|
+
sceneRef.current.traverse((obj) => {
|
|
6203
|
+
if (obj instanceof THREE3.Mesh) {
|
|
6204
|
+
obj.geometry?.dispose();
|
|
6205
|
+
if (obj.material) {
|
|
6206
|
+
if (Array.isArray(obj.material)) {
|
|
6207
|
+
obj.material.forEach((m) => m.dispose());
|
|
6208
|
+
} else {
|
|
6209
|
+
obj.material.dispose();
|
|
6210
|
+
}
|
|
6211
|
+
}
|
|
6212
|
+
}
|
|
6213
|
+
});
|
|
6214
|
+
}
|
|
6215
|
+
};
|
|
6216
|
+
}, [effect, texturePath, objectType]);
|
|
6217
|
+
if (loading) {
|
|
6218
|
+
return /* @__PURE__ */ React21.createElement("div", { className: `fx-three-preview loading ${className}` }, /* @__PURE__ */ React21.createElement("div", { className: "preview-loading" }, "\u52A0\u8F7D\u4E2D..."));
|
|
6219
|
+
}
|
|
6220
|
+
if (error) {
|
|
6221
|
+
return /* @__PURE__ */ React21.createElement("div", { className: `fx-three-preview error ${className}` }, /* @__PURE__ */ React21.createElement("div", { className: "preview-error" }, /* @__PURE__ */ React21.createElement("h3", null, "\u274C \u6E32\u67D3\u5931\u8D25"), /* @__PURE__ */ React21.createElement("p", null, error)));
|
|
6222
|
+
}
|
|
6223
|
+
return /* @__PURE__ */ React21.createElement("div", { className: `fx-three-preview ${className}` }, /* @__PURE__ */ React21.createElement(
|
|
6224
|
+
"div",
|
|
6225
|
+
{
|
|
6226
|
+
ref: containerRef,
|
|
6227
|
+
className: "preview-container",
|
|
6228
|
+
style: {
|
|
6229
|
+
width: "100%",
|
|
6230
|
+
height: "100%",
|
|
6231
|
+
minHeight: "400px",
|
|
6232
|
+
position: "relative"
|
|
6233
|
+
}
|
|
6234
|
+
}
|
|
6235
|
+
), showInfo && adapterInfo && /* @__PURE__ */ React21.createElement("div", { className: "preview-info" }, /* @__PURE__ */ React21.createElement("div", { className: "info-section" }, /* @__PURE__ */ React21.createElement("h4", null, "\u{1F3A8} \u5E94\u7528\u7684FX\u53C2\u6570"), /* @__PURE__ */ React21.createElement("div", { className: "info-badges" }, /* @__PURE__ */ React21.createElement("span", { className: "badge" }, adapterInfo.materialParams.length, " \u4E2A\u6750\u8D28\u53C2\u6570"), /* @__PURE__ */ React21.createElement("span", { className: "badge" }, adapterInfo.textures.length, " \u4E2A\u7EB9\u7406"), /* @__PURE__ */ React21.createElement("span", { className: "badge" }, adapterInfo.renderFeatures.length, " \u4E2A\u6E32\u67D3\u7279\u6027"))), adapterInfo.renderFeatures.length > 0 && /* @__PURE__ */ React21.createElement("div", { className: "info-section" }, /* @__PURE__ */ React21.createElement("h5", null, "\u542F\u7528\u7684\u7279\u6027:"), /* @__PURE__ */ React21.createElement("div", { className: "feature-list" }, adapterInfo.renderFeatures.map((feature, idx) => /* @__PURE__ */ React21.createElement("span", { key: idx, className: "feature-tag" }, feature))))), /* @__PURE__ */ React21.createElement("style", null, `
|
|
6236
|
+
.fx-three-preview {
|
|
6237
|
+
display: flex;
|
|
6238
|
+
flex-direction: column;
|
|
6239
|
+
gap: 1rem;
|
|
6240
|
+
}
|
|
6241
|
+
|
|
6242
|
+
.fx-three-preview.loading,
|
|
6243
|
+
.fx-three-preview.error {
|
|
6244
|
+
min-height: 400px;
|
|
6245
|
+
display: flex;
|
|
6246
|
+
align-items: center;
|
|
6247
|
+
justify-content: center;
|
|
6248
|
+
background: #f5f5f5;
|
|
6249
|
+
border-radius: 8px;
|
|
6250
|
+
}
|
|
6251
|
+
|
|
6252
|
+
.preview-loading {
|
|
6253
|
+
font-size: 1.1rem;
|
|
6254
|
+
color: #666;
|
|
6255
|
+
}
|
|
6256
|
+
|
|
6257
|
+
.preview-error {
|
|
6258
|
+
text-align: center;
|
|
6259
|
+
color: #d32f2f;
|
|
6260
|
+
padding: 2rem;
|
|
6261
|
+
}
|
|
6262
|
+
|
|
6263
|
+
.preview-error h3 {
|
|
6264
|
+
margin: 0 0 1rem;
|
|
6265
|
+
}
|
|
6266
|
+
|
|
6267
|
+
.preview-container {
|
|
6268
|
+
border-radius: 8px;
|
|
6269
|
+
overflow: hidden;
|
|
6270
|
+
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
|
6271
|
+
}
|
|
6272
|
+
|
|
6273
|
+
.preview-info {
|
|
6274
|
+
background: white;
|
|
6275
|
+
border-radius: 8px;
|
|
6276
|
+
padding: 1.5rem;
|
|
6277
|
+
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
|
6278
|
+
}
|
|
6279
|
+
|
|
6280
|
+
.info-section {
|
|
6281
|
+
margin-bottom: 1rem;
|
|
6282
|
+
}
|
|
6283
|
+
|
|
6284
|
+
.info-section:last-child {
|
|
6285
|
+
margin-bottom: 0;
|
|
6286
|
+
}
|
|
6287
|
+
|
|
6288
|
+
.info-section h4 {
|
|
6289
|
+
margin: 0 0 0.75rem;
|
|
6290
|
+
font-size: 1rem;
|
|
6291
|
+
color: #333;
|
|
6292
|
+
}
|
|
6293
|
+
|
|
6294
|
+
.info-section h5 {
|
|
6295
|
+
margin: 0 0 0.5rem;
|
|
6296
|
+
font-size: 0.9rem;
|
|
6297
|
+
color: #666;
|
|
6298
|
+
}
|
|
6299
|
+
|
|
6300
|
+
.info-badges {
|
|
6301
|
+
display: flex;
|
|
6302
|
+
flex-wrap: wrap;
|
|
6303
|
+
gap: 0.5rem;
|
|
6304
|
+
}
|
|
6305
|
+
|
|
6306
|
+
.badge {
|
|
6307
|
+
display: inline-block;
|
|
6308
|
+
padding: 0.4rem 0.8rem;
|
|
6309
|
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
6310
|
+
color: white;
|
|
6311
|
+
border-radius: 16px;
|
|
6312
|
+
font-size: 0.85rem;
|
|
6313
|
+
font-weight: 500;
|
|
6314
|
+
}
|
|
6315
|
+
|
|
6316
|
+
.feature-list {
|
|
6317
|
+
display: flex;
|
|
6318
|
+
flex-wrap: wrap;
|
|
6319
|
+
gap: 0.5rem;
|
|
6320
|
+
}
|
|
6321
|
+
|
|
6322
|
+
.feature-tag {
|
|
6323
|
+
display: inline-block;
|
|
6324
|
+
padding: 0.3rem 0.7rem;
|
|
6325
|
+
background: #e8f5e9;
|
|
6326
|
+
color: #2e7d32;
|
|
6327
|
+
border-radius: 12px;
|
|
6328
|
+
font-size: 0.8rem;
|
|
6329
|
+
font-weight: 500;
|
|
6330
|
+
}
|
|
6331
|
+
`));
|
|
6332
|
+
};
|
|
6333
|
+
FXThreePreview.displayName = "FXThreePreview";
|
|
6334
|
+
|
|
6335
|
+
export { ChoiceMenu, DialogueBox, FXParser, FXThreePreview, FXToThreeAdapter, FXViewer, HLSLToGLSLConverter, HistoryPanel, LoadingOverlay, LoadingScreen, MMDARPlayer, MMDMusicPlayer, MMDPlayerBase, MMDPlayerEnhanced, MMDPlayerEnhancedDebugInfo, MMDPlaylist, MMDPlaylistDebugInfo, MMDVisualNovel, MultiFXAdapter, MusicControls, PlaylistPanel, StartScreen, TrackInfo, compareFXEffects, exportFXToJSON, exportFXToMarkdown, extractTexturePaths, filterDefinesByPrefix, getColorParameters, getConfigSummaryText, getFeatureFlags, getTextureDefines, hasFeature, loadAmmo, validateFXEffect };
|
|
4283
6336
|
//# sourceMappingURL=index.mjs.map
|
|
4284
6337
|
//# sourceMappingURL=index.mjs.map
|