daydreamer-json commited on
Commit
c47613b
·
verified ·
1 Parent(s): 9804fae

Update script.js

Browse files
Files changed (1) hide show
  1. script.js +399 -399
script.js CHANGED
@@ -1,399 +1,399 @@
1
- const mathFunc = {
2
- 'arrayMax': (array) => {
3
- const maxfct = (a, b) => {return Math.max(a, b)};
4
- return array.reduce(maxfct);
5
- },
6
- 'arrayMin': (array) => {
7
- const minfct = (a, b) => {return Math.min(a, b)};
8
- return array.reduce(minfct);
9
- },
10
- 'arrayTotal': (array) => {
11
- return array.reduce((acc, f) => acc + f, 0);
12
- },
13
- 'arrayAvg': (array) => {
14
- return array.reduce((acc, f) => acc + f, 0) / array.length;
15
- },
16
- 'rounder': (method, num, n, zeroPadding = false) => {
17
- const pow = Math.pow(10, n);
18
- let result;
19
- switch (method) {
20
- case 'floor':
21
- result = Math.floor(num * pow) / pow;
22
- break;
23
- case 'ceil':
24
- result = Math.ceil(num * pow) / pow;
25
- break;
26
- case 'round':
27
- result = Math.round(num * pow) / pow;
28
- break;
29
- default:
30
- throw new Error('Invalid rounding method specified.');
31
- }
32
- if (zeroPadding) {
33
- return result.toFixed(n);
34
- } else {
35
- return result;
36
- }
37
- },
38
- 'formatFileSize': (bytes, decimals = 2) => {
39
- if (bytes === 0) return '0 byte';
40
- const k = 1024;
41
- const dm = decimals < 0 ? 0 : decimals;
42
- const sizes = ['bytes', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB'];
43
- const i = Math.floor(Math.log(bytes) / Math.log(k));
44
- return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
45
- }
46
- };
47
-
48
- const resourcePathSetsDefault = {
49
- 'track': 'melancholy_night',
50
- 'mainModel': 'alicia'
51
- };
52
- const resourcePathSets = {
53
- 'track': {
54
- 'apdh': {
55
- 'mainMotion': './res/track/apdh/hime_motion_-8f.bvmd',
56
- 'audio': './res/track/apdh/audio.m4a',
57
- 'bakeMotion': {
58
- // 'hutao': './res/track/apdh/hime_motion_-8f_bake_hutao.bvmd'
59
- }
60
- },
61
- 'conqueror': {
62
- 'mainMotion': './res/track/conqueror/merged.bvmd',
63
- 'audio': './res/track/conqueror/audio.m4a',
64
- 'bakeMotion': {}
65
- },
66
- 'melancholy_night': {
67
- 'mainMotion': './res/track/melancholy_night/motion.bvmd',
68
- 'audio': './res/track/melancholy_night/melancholy_night.m4a',
69
- 'bakeMotion': {}
70
- }
71
- },
72
- 'mainModel': {
73
- 'alicia': './res/model/Alicia.bpmx',
74
- 'higuchiKaede': './res/model/higuchi_kaede.bpmx',
75
- 'higuchiKaedeHair': './res/model/higuchi_kaede_alt.bpmx',
76
- 'higuchiKaedeSummer': './res/model/higuchi_kaede_summer.bpmx',
77
- 'higuchiKaedeSummerHair': './res/model/higuchi_kaede_summer_alt.bpmx',
78
- 'higuchiKaedeCasual': './res/model/higuchi_kaede_casual.bpmx',
79
- 'higuchiKaedeOni': './res/model/higuchi_kaede_oni.bpmx',
80
- 'higuchiKaedeArmy': './res/model/higuchi_kaede_army.bpmx',
81
- 'higuchiKaedeArmyHair': './res/model/higuchi_kaede_army_alt.bpmx',
82
- 'hutao': './res/model/HuTao/HuTao.pmx',
83
- 'inuiToko': './res/model/inui_toko.bpmx',
84
- 'lumine': './res/model/Lumine/LumineModified.pmx',
85
- 'miraiAkari': './res/model/MiraiAkari_v1.0.bpmx',
86
- 'raidenShogun': './res/model/RaidenShogun.bpmx',
87
- 'tokinoSora': './res/model/tokino_sora.bpmx',
88
- 'yyb_miku_10th': './res/model/yyb_hatsune_miku_10th_v1.02.bpmx'
89
- }
90
- };
91
- let isEnablePhysicsEngine = true;
92
- const resourcePaths = {
93
- 'mainModel': resourcePathSets.mainModel[resourcePathSetsDefault.mainModel],
94
- 'mainMotion': resourcePathSets.track[resourcePathSetsDefault.track].mainMotion,
95
- 'audio': resourcePathSets.track[resourcePathSetsDefault.track].audio,
96
- };
97
- const resourceNames = {
98
- 'track': resourcePathSetsDefault.track,
99
- 'mainModel': resourcePathSetsDefault.mainModel
100
- };
101
- const urlQueryObject = Object.fromEntries(new URLSearchParams(window.location.search));
102
- if (Object.keys(urlQueryObject).some(el => el === 'track')) {
103
- for (let i = 0; i < Object.keys(resourcePathSets.track).length; i++) {
104
- if (urlQueryObject.track === Object.keys(resourcePathSets.track)[i]) {
105
- resourcePaths.mainMotion = resourcePathSets.track[Object.keys(resourcePathSets.track)[i]].mainMotion;
106
- resourcePaths.audio = resourcePathSets.track[Object.keys(resourcePathSets.track)[i]].audio;
107
- resourceNames.track = Object.keys(resourcePathSets.track)[i];
108
- }
109
- }
110
- }
111
- if (Object.keys(urlQueryObject).some(el => el === 'mainModel')) {
112
- for (let i = 0; i < Object.keys(resourcePathSets.mainModel).length; i++) {
113
- if (urlQueryObject.mainModel === Object.keys(resourcePathSets.mainModel)[i]) {
114
- resourcePaths.mainModel = resourcePathSets.mainModel[Object.keys(resourcePathSets.mainModel)[i]];
115
- resourceNames.mainModel = Object.keys(resourcePathSets.mainModel)[i];
116
- }
117
- }
118
- }
119
- for (let i = 0; i < Object.keys(resourcePathSets.track[resourceNames.track].bakeMotion).length; i++) {
120
- if (Object.keys(resourcePathSets.track[resourceNames.track].bakeMotion)[i] === resourceNames.mainModel) {
121
- resourcePaths.mainMotion = resourcePathSets.track[resourceNames.track].bakeMotion[resourceNames.mainModel];
122
- isEnablePhysicsEngine = false;
123
- }
124
- }
125
- console.log(resourcePaths)
126
-
127
- var canvas = document.getElementById("renderCanvas");
128
- var startRenderLoop = function (engine, canvas) {
129
- engine.runRenderLoop(function () {
130
- if (sceneToRender && sceneToRender.activeCamera) {
131
- sceneToRender.render();
132
- }
133
- });
134
- };
135
- var engine = null;
136
- var scene = null;
137
- var sceneToRender = null;
138
- var createDefaultEngine = function () {
139
- return new BABYLON.Engine(canvas, true, { preserveDrawingBuffer: true, stencil: true, disableWebGL2Support: false });
140
- };
141
- class Playground {
142
- static async CreateScene(engine, canvas) {
143
- await new Promise((resolve) => {
144
- const babylonMmdScript = document.createElement("script");
145
- babylonMmdScript.src = "https://www.unpkg.com/babylon-mmd@0.37.2/umd/babylon.mmd.min.js";
146
- document.head.appendChild(babylonMmdScript);
147
- babylonMmdScript.onload = resolve;
148
- });
149
- if (engine.hostInformation.isMobile) {engine.setHardwareScalingLevel(0.85)} else {engine.setHardwareScalingLevel(0.75)}
150
- if (Object.keys(urlQueryObject).some(el => el === 'scale')) {
151
- engine.setHardwareScalingLevel(parseFloat(urlQueryObject.scale));
152
- }
153
- const pmxLoader = BABYLON.SceneLoader.GetPluginForExtension(".pmx");
154
- const materialBuilder = pmxLoader.materialBuilder;
155
- materialBuilder.useAlphaEvaluation = false;
156
- // materialBuilder.loadOutlineRenderingProperties;
157
- // const alphaBlendMaterials = ["face02", "Facial02", "HL", "Hairshadow", "q302"];
158
- // const alphaTestMaterials = ["q301"];
159
- // materialBuilder.afterBuildSingleMaterial = (material) => {
160
- // if (!alphaBlendMaterials.includes(material.name) && !alphaTestMaterials.includes(material.name)) return;
161
- // material.transparencyMode = alphaBlendMaterials.includes(material.name) ? BABYLON.Material.MATERIAL_ALPHABLEND : BABYLON.Material.MATERIAL_ALPHATEST;
162
- // material.useAlphaFromDiffuseTexture = true;
163
- // material.diffuseTexture.hasAlpha = true;
164
- // };
165
- const scene = new BABYLON.Scene(engine);
166
- // BABYLON.SceneOptimizer.OptimizeAsync(scene);
167
- // scene.clearColor = new BABYLON.Color4(0.95, 0.95, 0.95, 1.0);
168
- scene.clearColor = new BABYLON.Color4(0, 0, 0, 1.0);
169
- const mmdCamera = new BABYLONMMD.MmdCamera("MmdCamera", new BABYLON.Vector3(0, 10, 0), scene);
170
- mmdCamera.maxZ = 5000;
171
- const camera = new BABYLON.ArcRotateCamera("ArcRotateCamera", 0, 0, 45, new BABYLON.Vector3(0, 10, 0), scene);
172
- camera.maxZ = 5000;
173
- camera.setPosition(new BABYLON.Vector3(0, 10, -45));
174
- camera.attachControl(canvas, false);
175
- camera.inertia = 0.8;
176
- camera.speed = 10;
177
- const hemisphericLight = new BABYLON.HemisphericLight("HemisphericLight", new BABYLON.Vector3(0, 1, 0), scene);
178
- hemisphericLight.intensity = 0.4;
179
- hemisphericLight.specular = new BABYLON.Color3(0, 0, 0);
180
- hemisphericLight.groundColor = new BABYLON.Color3(1, 0.9, 0.9);
181
- const directionalLight = new BABYLON.DirectionalLight("DirectionalLight", new BABYLON.Vector3(0.5, -1, 1), scene);
182
- directionalLight.intensity = 0.8;
183
- directionalLight.autoCalcShadowZBounds = false;
184
- directionalLight.autoUpdateExtends = false;
185
- directionalLight.shadowMaxZ = 20;
186
- directionalLight.shadowMinZ = -15;
187
- directionalLight.orthoTop = 18;
188
- directionalLight.orthoBottom = -1;
189
- directionalLight.orthoLeft = -10;
190
- directionalLight.orthoRight = 10;
191
- directionalLight.shadowOrthoScale = 0;
192
- const shadowGenerator = new BABYLON.ShadowGenerator(1024, directionalLight, true);
193
- shadowGenerator.usePercentageCloserFiltering = true;
194
- shadowGenerator.forceBackFacesOnly = true;
195
- shadowGenerator.filteringQuality = BABYLON.ShadowGenerator.QUALITY_MEDIUM;
196
- shadowGenerator.frustumEdgeFalloff = 0.1;
197
- const mmdRuntime = isEnablePhysicsEngine ? new BABYLONMMD.MmdRuntime(scene, new BABYLONMMD.MmdPhysics(scene)) : new BABYLONMMD.MmdRuntime(scene)
198
- mmdRuntime.register(scene);
199
- const audioPlayer = new BABYLONMMD.StreamAudioPlayer(scene);
200
- audioPlayer.preservesPitch = true;
201
- audioPlayer.source = resourcePaths.audio;
202
- mmdRuntime.setAudioPlayer(audioPlayer);
203
- const mmdPlayerControl = new BABYLONMMD.MmdPlayerControl(scene, mmdRuntime, audioPlayer);
204
- mmdPlayerControl.showPlayerControl();
205
- engine.displayLoadingUI();
206
- let loadingTexts = [];
207
- const updateLoadingText = (updateIndex, text) => {
208
- loadingTexts[updateIndex] = text;
209
- engine.loadingUIText = "<br/><br/><br/><br/>" + loadingTexts.join("<br/><br/>");
210
- };
211
- const promises = [];
212
- const bvmdLoader = new BABYLONMMD.BvmdLoader(scene);
213
- promises.push(bvmdLoader.loadAsync("motion", resourcePaths.mainMotion, (event) => updateLoadingText(0, `Loading motion... ${event.loaded}/${event.total} (${Math.floor((event.loaded * 100) / event.total)}%)`)));
214
- promises.push(BABYLON.SceneLoader.ImportMeshAsync(undefined, resourcePaths.mainModel, undefined, scene, (event) => updateLoadingText(1, `Loading model... ${event.loaded}/${event.total} (${Math.floor((event.loaded * 100) / event.total)}%)`)));
215
- if (isEnablePhysicsEngine === true) {
216
- promises.push(
217
- (async () => {
218
- updateLoadingText(2, "Loading physics engine...");
219
- const havokPlugin = new BABYLON.HavokPlugin();
220
- // scene.enablePhysics(new BABYLON.Vector3(0, -9.8, 0), havokPlugin);
221
- scene.enablePhysics(new BABYLON.Vector3(0, -9.8, 0), havokPlugin);
222
- updateLoadingText(2, "Loading physics engine... Done");
223
- })()
224
- );
225
- }
226
- loadingTexts = new Array(promises.length).fill("");
227
- const loadResults = await Promise.all(promises);
228
- scene.onAfterRenderObservable.addOnce(() => engine.hideLoadingUI());
229
- mmdRuntime.setCamera(mmdCamera);
230
- mmdCamera.addAnimation(loadResults[0]);
231
- mmdCamera.setAnimation("motion");
232
- const modelMesh = loadResults[1].meshes[0];
233
- modelMesh.receiveShadows = true;
234
- shadowGenerator.addShadowCaster(modelMesh);
235
- const mmdModel = mmdRuntime.createMmdModel(modelMesh);
236
- mmdModel.addAnimation(loadResults[0]);
237
- mmdModel.setAnimation("motion");
238
- // mmdModel.mesh.outlineWidth = 10
239
- const bodyBone = loadResults[1].skeletons[0].bones.find((bone) => bone.name === "センター");
240
- scene.onBeforeRenderObservable.add(() => {
241
- bodyBone.getFinalMatrix().getTranslationToRef(directionalLight.position);
242
- directionalLight.position.y -= 10;
243
- });
244
- const ground = BABYLON.MeshBuilder.CreateGround("Ground", { width: 100, height: 100, subdivisions: 2, updatable: false }, scene);
245
- ground.receiveShadows = true;
246
- const groundMaterial = (ground.material = new BABYLON.StandardMaterial("GroundMaterial", scene));
247
- groundMaterial.diffuseColor = new BABYLON.Color3(0.25, 0.25, 0.25);
248
- groundMaterial.specularPower = 128;
249
- const groundReflectionTexture = (groundMaterial.reflectionTexture = new BABYLON.MirrorTexture("MirrorTexture", 1024, scene, true));
250
- groundReflectionTexture.mirrorPlane = BABYLON.Plane.FromPositionAndNormal(ground.position, ground.getFacetNormal(0).scale(-1));
251
- groundReflectionTexture.renderList = [modelMesh];
252
- groundReflectionTexture.level = 0.25;
253
- const defaultPipeline = new BABYLON.DefaultRenderingPipeline("default", true, scene, [mmdCamera, camera]);
254
- defaultPipeline.samples = 4;
255
- defaultPipeline.bloomEnabled = true;
256
- defaultPipeline.bloomThreshold = 0;
257
- defaultPipeline.bloomWeight = 0.1;
258
- defaultPipeline.bloomKernel = 38;
259
- defaultPipeline.bloomScale = 0.5;
260
- defaultPipeline.chromaticAberrationEnabled = true;
261
- defaultPipeline.chromaticAberration.aberrationAmount = 0;
262
- defaultPipeline.fxaaEnabled = true;
263
- defaultPipeline.imageProcessingEnabled = true;
264
- defaultPipeline.imageProcessing.toneMappingEnabled = false;
265
- defaultPipeline.imageProcessing.toneMappingType = BABYLON.ImageProcessingConfiguration.TONEMAPPING_ACES;
266
- defaultPipeline.imageProcessing.vignetteWeight = 0.5;
267
- defaultPipeline.imageProcessing.vignetteStretch = 0.5;
268
- defaultPipeline.imageProcessing.vignetteColor = new BABYLON.Color4(0, 0, 0, 0);
269
- defaultPipeline.imageProcessing.vignetteEnabled = true;
270
- const lensEffect = new BABYLON.LensRenderingPipeline('lensEffects', {edge_blur: engine.hostInformation.isMobile ? 0.0 : 1.0, chromatic_aberration: engine.hostInformation.isMobile ? 0.5 : 1.0, distortion: 0.2}, scene, 1.0);
271
- scene.postProcessRenderPipelineManager.attachCamerasToRenderPipeline('lensEffects', camera);
272
- scene.postProcessRenderPipelineManager.attachCamerasToRenderPipeline('lensEffects', mmdCamera);
273
- // BABYLON.ParticleHelper.CreateAsync("rain", scene, false).then((set) => {set.start()});
274
- const guiCamera = new BABYLON.ArcRotateCamera("GUICamera", Math.PI / 2 + Math.PI / 7, Math.PI / 2, 100, new BABYLON.Vector3(0, 20, 0), scene);
275
- guiCamera.layerMask = 0x10000000;
276
- scene.activeCameras = [mmdCamera, guiCamera];
277
- let lastClickTime = -Infinity;
278
- canvas.onclick = () => {
279
- const currentTime = performance.now();
280
- if (500 < currentTime - lastClickTime) {
281
- lastClickTime = currentTime;
282
- return;
283
- }
284
- lastClickTime = -Infinity;
285
- if (scene.activeCameras[0] === mmdCamera) scene.activeCameras = [camera, guiCamera];
286
- else scene.activeCameras = [mmdCamera, guiCamera];
287
- };
288
- let engineInstrumentation = new BABYLON.EngineInstrumentation(engine);
289
- engineInstrumentation.captureGPUFrameTime = true;
290
- engineInstrumentation.captureShaderCompilationTime = true;
291
- let sceneInstrumentation = new BABYLON.SceneInstrumentation(scene);
292
- sceneInstrumentation.captureActiveMeshesEvaluationTime = true;
293
- sceneInstrumentation.captureFrameTime = true;
294
- sceneInstrumentation.captureParticlesRenderTime = true;
295
- sceneInstrumentation.captureRenderTime = true;
296
- sceneInstrumentation.captureCameraRenderTime = true;
297
- sceneInstrumentation.captureRenderTargetsRenderTime = true;
298
- sceneInstrumentation.captureInterFrameTime = true;
299
- let isLimitFPS = false;
300
- let targetFPS = null;
301
- if (Object.keys(urlQueryObject).some(el => el === 'fps')) {
302
- isLimitFPS = true;
303
- targetFPS = parseFloat(urlQueryObject.fps) + 0.35;
304
- engine.customAnimationFrameRequester = {
305
- requestAnimationFrame: (func) => {setTimeout(func, 1000 / targetFPS - sceneInstrumentation.frameTimeCounter.current - sceneInstrumentation.activeMeshesEvaluationTimeCounter.current - sceneInstrumentation.particlesRenderTimeCounter.lastSecAverage - sceneInstrumentation.renderTimeCounter.current - sceneInstrumentation.cameraRenderTimeCounter.current - sceneInstrumentation.renderTargetsRenderTimeCounter.current)}
306
- };
307
- }
308
- const advancedTexture = BABYLON.GUI.AdvancedDynamicTexture.CreateFullscreenUI("UI");
309
- advancedTexture.layer.layerMask = 0x10000000;
310
- advancedTexture.renderScale = 1.5;
311
- const textblock = new BABYLON.GUI.TextBlock();
312
- // textblock.widthInPixels = 800;
313
- // textblock.left = 10;
314
- textblock.text = `${engine._glRenderer}\n${engine._glVersion}`;
315
- textblock.fontSize = 32;
316
- textblock.fontFamily = "'SF Mono', 'SF Pro Text', 'Inter', 'Noto Sans JP', 'system-ui'";
317
- textblock.textHorizontalAlignment = BABYLON.GUI.Control.HORIZONTAL_ALIGNMENT_LEFT;
318
- textblock.textVerticalAlignment = BABYLON.GUI.Control.VERTICAL_ALIGNMENT_TOP;
319
- textblock.horizontalAlignment = BABYLON.GUI.Control.HORIZONTAL_ALIGNMENT_LEFT;
320
- textblock.verticalAlignment = BABYLON.GUI.Control.VERTICAL_ALIGNMENT_TOP;
321
- textblock.color = "#ffffff";
322
- advancedTexture.addControl(textblock);
323
- const textBlockUpdateDisp = setInterval(() => {
324
- if (engine.hostInformation.isMobile) {
325
- textblock.text = `${engine._glRenderer}
326
- ${engine._glVersion}
327
-
328
- ${engine.frameId} f
329
- ${mathFunc.rounder('floor', engine.performanceMonitor.averageFPS, 2)} fps
330
- ${mathFunc.rounder('floor', engine.performanceMonitor.averageFrameTime, 2)} ms`;
331
- } else {
332
- textblock.text = `${engine._glRenderer}
333
- ${engine._glVersion}
334
-
335
- Main Model Path: ${resourcePaths.mainModel}
336
- Main Motion Path: ${resourcePaths.mainMotion}
337
- Main Audio Path: ${resourcePaths.audio}
338
-
339
- FPS Disp: ${mathFunc.rounder('floor', engine.performanceMonitor.averageFPS, 2, true)} fps avg (${mathFunc.rounder('floor', engine.performanceMonitor.instantaneousFPS, 2, true)} fps)
340
- FPS Limit: ${isLimitFPS ? mathFunc.rounder('floor', parseFloat(urlQueryObject.fps), 2, true) + ' fps' : 'unlimited'}
341
- FPS Real: ${isLimitFPS ? mathFunc.rounder('floor', 1000 / sceneInstrumentation.frameTimeCounter.lastSecAverage, 2, true) : mathFunc.rounder('floor', 1000 / sceneInstrumentation.frameTimeCounter.lastSecAverage, 2, true)} fps
342
-
343
- Active Meshes: ${scene.getActiveMeshes().length} / ${scene.meshes.length}
344
- Total Vertices: ${scene.totalVerticesPerfCounter.current}
345
- Active Indices: ${scene.totalActiveIndicesPerfCounter.current}
346
- Total Objects: ${scene.materials.length} mat, ${scene.textures.length} tex, ${scene.animatables.length} anm, ${scene.lights.length} lit
347
- Draw Calls Count: ${sceneInstrumentation.drawCallsCounter.current}
348
-
349
- Frame Count: ${engine.frameId} frame
350
- Actually Frame: ${mathFunc.rounder('floor', engine.performanceMonitor.averageFrameTime, 2, true)} ms avg (${mathFunc.rounder('floor', engine.performanceMonitor.instantaneousFrameTime, 2, true)} ms)
351
- Scene Frame: ${mathFunc.rounder('ceil', sceneInstrumentation.frameTimeCounter.lastSecAverage, 2, true)} ms avg (${mathFunc.rounder('ceil', sceneInstrumentation.frameTimeCounter.current, 2, true)} ms)
352
- Active Meshes Eval: ${mathFunc.rounder('ceil', sceneInstrumentation.activeMeshesEvaluationTimeCounter.lastSecAverage, 2, true)} ms avg (${mathFunc.rounder('ceil', sceneInstrumentation.activeMeshesEvaluationTimeCounter.current, 2, true)} ms)
353
- Particles Render: ${mathFunc.rounder('ceil', sceneInstrumentation.particlesRenderTimeCounter.lastSecAverage, 2, true)} ms avg (${mathFunc.rounder('ceil', sceneInstrumentation.particlesRenderTimeCounter.current, 2, true)} ms)
354
- Inter Frame: ${mathFunc.rounder('ceil', sceneInstrumentation.interFrameTimeCounter.lastSecAverage, 2, true)} ms avg (${mathFunc.rounder('ceil', sceneInstrumentation.interFrameTimeCounter.current, 2, true)} ms)
355
- GPU Frame: ${mathFunc.rounder('ceil', engineInstrumentation.gpuFrameTimeCounter.lastSecAverage * 0.000001, 2, true)} ms avg (${mathFunc.rounder('ceil', engineInstrumentation.gpuFrameTimeCounter.current * 0.000001, 2, true)} ms)
356
- Shader Comp Total: ${mathFunc.rounder('ceil', engineInstrumentation.shaderCompilationTimeCounter.count, 2, true)} ms
357
- Scene Render: ${mathFunc.rounder('ceil', sceneInstrumentation.renderTimeCounter.lastSecAverage, 2, true)} ms avg (${mathFunc.rounder('ceil', sceneInstrumentation.renderTimeCounter.current, 2, true)} ms)
358
- Camera Render: ${mathFunc.rounder('ceil', sceneInstrumentation.cameraRenderTimeCounter.lastSecAverage, 2, true)} ms avg (${mathFunc.rounder('ceil', sceneInstrumentation.cameraRenderTimeCounter.current, 2, true)} ms)
359
- Targets Render: ${mathFunc.rounder('ceil', sceneInstrumentation.renderTargetsRenderTimeCounter.lastSecAverage, 2, true)} ms avg (${mathFunc.rounder('ceil', sceneInstrumentation.renderTargetsRenderTimeCounter.current, 2, true)} ms)
360
-
361
- Heap: ${!performance.memory ? 'Unavailable' : (mathFunc.rounder('ceil', performance.memory.usedJSHeapSize / 1024 / 1024, 2, true))} MB / ${!performance.memory ? 'Unavailable' : (mathFunc.rounder('ceil', performance.memory.totalJSHeapSize / 1024 / 1024, 2, true))} MB (Limit ${!performance.memory ? 'Unavailable' : (mathFunc.rounder('ceil', performance.memory.jsHeapSizeLimit / 1024 / 1024, 2, true))} MB)
362
- `;
363
- }
364
- }, 10);
365
- await new Promise(resolve => setTimeout(resolve, 1000));
366
- mmdRuntime.playAnimation();
367
- return scene;
368
- }
369
- }
370
- createScene = function () {
371
- return Playground.CreateScene(engine, engine.getRenderingCanvas());
372
- };
373
- window.initFunction = async function () {
374
- globalThis.HK = await HavokPhysics();
375
-
376
- var asyncEngineCreation = async function () {
377
- try {
378
- return createDefaultEngine();
379
- } catch (e) {
380
- console.log("the available createEngine function failed. Creating the default engine instead");
381
- return createDefaultEngine();
382
- }
383
- };
384
-
385
- window.engine = await asyncEngineCreation();
386
- if (!engine) throw "engine should not be null.";
387
- startRenderLoop(engine, canvas);
388
- window.scene = createScene();
389
- };
390
- initFunction().then(() => {
391
- scene.then((returnedScene) => {
392
- sceneToRender = returnedScene;
393
- });
394
- });
395
-
396
- // Resize
397
- window.addEventListener("resize", function () {
398
- engine.resize();
399
- });
 
1
+ const mathFunc = {
2
+ 'arrayMax': (array) => {
3
+ const maxfct = (a, b) => {return Math.max(a, b)};
4
+ return array.reduce(maxfct);
5
+ },
6
+ 'arrayMin': (array) => {
7
+ const minfct = (a, b) => {return Math.min(a, b)};
8
+ return array.reduce(minfct);
9
+ },
10
+ 'arrayTotal': (array) => {
11
+ return array.reduce((acc, f) => acc + f, 0);
12
+ },
13
+ 'arrayAvg': (array) => {
14
+ return array.reduce((acc, f) => acc + f, 0) / array.length;
15
+ },
16
+ 'rounder': (method, num, n, zeroPadding = false) => {
17
+ const pow = Math.pow(10, n);
18
+ let result;
19
+ switch (method) {
20
+ case 'floor':
21
+ result = Math.floor(num * pow) / pow;
22
+ break;
23
+ case 'ceil':
24
+ result = Math.ceil(num * pow) / pow;
25
+ break;
26
+ case 'round':
27
+ result = Math.round(num * pow) / pow;
28
+ break;
29
+ default:
30
+ throw new Error('Invalid rounding method specified.');
31
+ }
32
+ if (zeroPadding) {
33
+ return result.toFixed(n);
34
+ } else {
35
+ return result;
36
+ }
37
+ },
38
+ 'formatFileSize': (bytes, decimals = 2) => {
39
+ if (bytes === 0) return '0 byte';
40
+ const k = 1024;
41
+ const dm = decimals < 0 ? 0 : decimals;
42
+ const sizes = ['bytes', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB'];
43
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
44
+ return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
45
+ }
46
+ };
47
+
48
+ const resourcePathSetsDefault = {
49
+ 'track': 'melancholy_night',
50
+ 'mainModel': 'alicia'
51
+ };
52
+ const resourcePathSets = {
53
+ 'track': {
54
+ 'apdh': {
55
+ 'mainMotion': './res/track/apdh/hime_motion_-8f.bvmd',
56
+ 'audio': './res/track/apdh/audio.m4a',
57
+ 'bakeMotion': {
58
+ // 'hutao': './res/track/apdh/hime_motion_-8f_bake_hutao.bvmd'
59
+ }
60
+ },
61
+ 'conqueror': {
62
+ 'mainMotion': './res/track/conqueror/merged.bvmd',
63
+ 'audio': './res/track/conqueror/audio.m4a',
64
+ 'bakeMotion': {}
65
+ },
66
+ 'melancholy_night': {
67
+ 'mainMotion': './res/track/melancholy_night/motion.bvmd',
68
+ 'audio': './res/track/melancholy_night/melancholy_night.m4a',
69
+ 'bakeMotion': {}
70
+ }
71
+ },
72
+ 'mainModel': {
73
+ 'alicia': './res/model/Alicia.bpmx',
74
+ 'higuchiKaede': './res/model/higuchi_kaede.bpmx',
75
+ 'higuchiKaedeHair': './res/model/higuchi_kaede_alt.bpmx',
76
+ 'higuchiKaedeSummer': './res/model/higuchi_kaede_summer.bpmx',
77
+ 'higuchiKaedeSummerHair': './res/model/higuchi_kaede_summer_alt.bpmx',
78
+ 'higuchiKaedeCasual': './res/model/higuchi_kaede_casual.bpmx',
79
+ 'higuchiKaedeOni': './res/model/higuchi_kaede_oni.bpmx',
80
+ 'higuchiKaedeArmy': './res/model/higuchi_kaede_army.bpmx',
81
+ 'higuchiKaedeArmyHair': './res/model/higuchi_kaede_army_alt.bpmx',
82
+ 'hutao': './res/model/HuTao/HuTao.pmx',
83
+ 'inuiToko': './res/model/inui_toko.bpmx',
84
+ 'lumine': './res/model/Lumine/LumineModified.pmx',
85
+ 'miraiAkari': './res/model/MiraiAkari_v1.0.bpmx',
86
+ 'raidenShogun': './res/model/RaidenShogun.bpmx',
87
+ 'tokinoSora': './res/model/tokino_sora.bpmx',
88
+ 'yyb_miku_10th': './res/model/yyb_hatsune_miku_10th_v1.02.bpmx'
89
+ }
90
+ };
91
+ let isEnablePhysicsEngine = true;
92
+ const resourcePaths = {
93
+ 'mainModel': resourcePathSets.mainModel[resourcePathSetsDefault.mainModel],
94
+ 'mainMotion': resourcePathSets.track[resourcePathSetsDefault.track].mainMotion,
95
+ 'audio': resourcePathSets.track[resourcePathSetsDefault.track].audio,
96
+ };
97
+ const resourceNames = {
98
+ 'track': resourcePathSetsDefault.track,
99
+ 'mainModel': resourcePathSetsDefault.mainModel
100
+ };
101
+ const urlQueryObject = Object.fromEntries(new URLSearchParams(window.location.search));
102
+ if (Object.keys(urlQueryObject).some(el => el === 'track')) {
103
+ for (let i = 0; i < Object.keys(resourcePathSets.track).length; i++) {
104
+ if (urlQueryObject.track === Object.keys(resourcePathSets.track)[i]) {
105
+ resourcePaths.mainMotion = resourcePathSets.track[Object.keys(resourcePathSets.track)[i]].mainMotion;
106
+ resourcePaths.audio = resourcePathSets.track[Object.keys(resourcePathSets.track)[i]].audio;
107
+ resourceNames.track = Object.keys(resourcePathSets.track)[i];
108
+ }
109
+ }
110
+ }
111
+ if (Object.keys(urlQueryObject).some(el => el === 'mainModel')) {
112
+ for (let i = 0; i < Object.keys(resourcePathSets.mainModel).length; i++) {
113
+ if (urlQueryObject.mainModel === Object.keys(resourcePathSets.mainModel)[i]) {
114
+ resourcePaths.mainModel = resourcePathSets.mainModel[Object.keys(resourcePathSets.mainModel)[i]];
115
+ resourceNames.mainModel = Object.keys(resourcePathSets.mainModel)[i];
116
+ }
117
+ }
118
+ }
119
+ for (let i = 0; i < Object.keys(resourcePathSets.track[resourceNames.track].bakeMotion).length; i++) {
120
+ if (Object.keys(resourcePathSets.track[resourceNames.track].bakeMotion)[i] === resourceNames.mainModel) {
121
+ resourcePaths.mainMotion = resourcePathSets.track[resourceNames.track].bakeMotion[resourceNames.mainModel];
122
+ isEnablePhysicsEngine = false;
123
+ }
124
+ }
125
+ console.log(resourcePaths)
126
+
127
+ var canvas = document.getElementById("renderCanvas");
128
+ var startRenderLoop = function (engine, canvas) {
129
+ engine.runRenderLoop(function () {
130
+ if (sceneToRender && sceneToRender.activeCamera) {
131
+ sceneToRender.render();
132
+ }
133
+ });
134
+ };
135
+ var engine = null;
136
+ var scene = null;
137
+ var sceneToRender = null;
138
+ var createDefaultEngine = function () {
139
+ return new BABYLON.Engine(canvas, true, { preserveDrawingBuffer: true, stencil: true, disableWebGL2Support: false });
140
+ };
141
+ class Playground {
142
+ static async CreateScene(engine, canvas) {
143
+ await new Promise((resolve) => {
144
+ const babylonMmdScript = document.createElement("script");
145
+ babylonMmdScript.src = "https://www.unpkg.com/babylon-mmd@0.54.3/umd/babylon.mmd.min.js";
146
+ document.head.appendChild(babylonMmdScript);
147
+ babylonMmdScript.onload = resolve;
148
+ });
149
+ if (engine.hostInformation.isMobile) {engine.setHardwareScalingLevel(0.85)} else {engine.setHardwareScalingLevel(0.75)}
150
+ if (Object.keys(urlQueryObject).some(el => el === 'scale')) {
151
+ engine.setHardwareScalingLevel(parseFloat(urlQueryObject.scale));
152
+ }
153
+ const pmxLoader = BABYLON.SceneLoader.GetPluginForExtension(".pmx");
154
+ const materialBuilder = pmxLoader.materialBuilder;
155
+ materialBuilder.useAlphaEvaluation = false;
156
+ // materialBuilder.loadOutlineRenderingProperties;
157
+ // const alphaBlendMaterials = ["face02", "Facial02", "HL", "Hairshadow", "q302"];
158
+ // const alphaTestMaterials = ["q301"];
159
+ // materialBuilder.afterBuildSingleMaterial = (material) => {
160
+ // if (!alphaBlendMaterials.includes(material.name) && !alphaTestMaterials.includes(material.name)) return;
161
+ // material.transparencyMode = alphaBlendMaterials.includes(material.name) ? BABYLON.Material.MATERIAL_ALPHABLEND : BABYLON.Material.MATERIAL_ALPHATEST;
162
+ // material.useAlphaFromDiffuseTexture = true;
163
+ // material.diffuseTexture.hasAlpha = true;
164
+ // };
165
+ const scene = new BABYLON.Scene(engine);
166
+ // BABYLON.SceneOptimizer.OptimizeAsync(scene);
167
+ // scene.clearColor = new BABYLON.Color4(0.95, 0.95, 0.95, 1.0);
168
+ scene.clearColor = new BABYLON.Color4(0, 0, 0, 1.0);
169
+ const mmdCamera = new BABYLONMMD.MmdCamera("MmdCamera", new BABYLON.Vector3(0, 10, 0), scene);
170
+ mmdCamera.maxZ = 5000;
171
+ const camera = new BABYLON.ArcRotateCamera("ArcRotateCamera", 0, 0, 45, new BABYLON.Vector3(0, 10, 0), scene);
172
+ camera.maxZ = 5000;
173
+ camera.setPosition(new BABYLON.Vector3(0, 10, -45));
174
+ camera.attachControl(canvas, false);
175
+ camera.inertia = 0.8;
176
+ camera.speed = 10;
177
+ const hemisphericLight = new BABYLON.HemisphericLight("HemisphericLight", new BABYLON.Vector3(0, 1, 0), scene);
178
+ hemisphericLight.intensity = 0.4;
179
+ hemisphericLight.specular = new BABYLON.Color3(0, 0, 0);
180
+ hemisphericLight.groundColor = new BABYLON.Color3(1, 0.9, 0.9);
181
+ const directionalLight = new BABYLON.DirectionalLight("DirectionalLight", new BABYLON.Vector3(0.5, -1, 1), scene);
182
+ directionalLight.intensity = 0.8;
183
+ directionalLight.autoCalcShadowZBounds = false;
184
+ directionalLight.autoUpdateExtends = false;
185
+ directionalLight.shadowMaxZ = 20;
186
+ directionalLight.shadowMinZ = -15;
187
+ directionalLight.orthoTop = 18;
188
+ directionalLight.orthoBottom = -1;
189
+ directionalLight.orthoLeft = -10;
190
+ directionalLight.orthoRight = 10;
191
+ directionalLight.shadowOrthoScale = 0;
192
+ const shadowGenerator = new BABYLON.ShadowGenerator(1024, directionalLight, true);
193
+ shadowGenerator.usePercentageCloserFiltering = true;
194
+ shadowGenerator.forceBackFacesOnly = true;
195
+ shadowGenerator.filteringQuality = BABYLON.ShadowGenerator.QUALITY_MEDIUM;
196
+ shadowGenerator.frustumEdgeFalloff = 0.1;
197
+ const mmdRuntime = isEnablePhysicsEngine ? new BABYLONMMD.MmdRuntime(scene, new BABYLONMMD.MmdPhysics(scene)) : new BABYLONMMD.MmdRuntime(scene)
198
+ mmdRuntime.register(scene);
199
+ const audioPlayer = new BABYLONMMD.StreamAudioPlayer(scene);
200
+ audioPlayer.preservesPitch = true;
201
+ audioPlayer.source = resourcePaths.audio;
202
+ mmdRuntime.setAudioPlayer(audioPlayer);
203
+ const mmdPlayerControl = new BABYLONMMD.MmdPlayerControl(scene, mmdRuntime, audioPlayer);
204
+ mmdPlayerControl.showPlayerControl();
205
+ engine.displayLoadingUI();
206
+ let loadingTexts = [];
207
+ const updateLoadingText = (updateIndex, text) => {
208
+ loadingTexts[updateIndex] = text;
209
+ engine.loadingUIText = "<br/><br/><br/><br/>" + loadingTexts.join("<br/><br/>");
210
+ };
211
+ const promises = [];
212
+ const bvmdLoader = new BABYLONMMD.BvmdLoader(scene);
213
+ promises.push(bvmdLoader.loadAsync("motion", resourcePaths.mainMotion, (event) => updateLoadingText(0, `Loading motion... ${event.loaded}/${event.total} (${Math.floor((event.loaded * 100) / event.total)}%)`)));
214
+ promises.push(BABYLON.SceneLoader.ImportMeshAsync(undefined, resourcePaths.mainModel, undefined, scene, (event) => updateLoadingText(1, `Loading model... ${event.loaded}/${event.total} (${Math.floor((event.loaded * 100) / event.total)}%)`)));
215
+ if (isEnablePhysicsEngine === true) {
216
+ promises.push(
217
+ (async () => {
218
+ updateLoadingText(2, "Loading physics engine...");
219
+ const havokPlugin = new BABYLON.HavokPlugin();
220
+ // scene.enablePhysics(new BABYLON.Vector3(0, -9.8, 0), havokPlugin);
221
+ scene.enablePhysics(new BABYLON.Vector3(0, -9.8, 0), havokPlugin);
222
+ updateLoadingText(2, "Loading physics engine... Done");
223
+ })()
224
+ );
225
+ }
226
+ loadingTexts = new Array(promises.length).fill("");
227
+ const loadResults = await Promise.all(promises);
228
+ scene.onAfterRenderObservable.addOnce(() => engine.hideLoadingUI());
229
+ mmdRuntime.setCamera(mmdCamera);
230
+ mmdCamera.addAnimation(loadResults[0]);
231
+ mmdCamera.setAnimation("motion");
232
+ const modelMesh = loadResults[1].meshes[0];
233
+ modelMesh.receiveShadows = true;
234
+ shadowGenerator.addShadowCaster(modelMesh);
235
+ const mmdModel = mmdRuntime.createMmdModel(modelMesh);
236
+ mmdModel.addAnimation(loadResults[0]);
237
+ mmdModel.setAnimation("motion");
238
+ // mmdModel.mesh.outlineWidth = 10
239
+ const bodyBone = loadResults[1].skeletons[0].bones.find((bone) => bone.name === "センター");
240
+ scene.onBeforeRenderObservable.add(() => {
241
+ bodyBone.getFinalMatrix().getTranslationToRef(directionalLight.position);
242
+ directionalLight.position.y -= 10;
243
+ });
244
+ const ground = BABYLON.MeshBuilder.CreateGround("Ground", { width: 100, height: 100, subdivisions: 2, updatable: false }, scene);
245
+ ground.receiveShadows = true;
246
+ const groundMaterial = (ground.material = new BABYLON.StandardMaterial("GroundMaterial", scene));
247
+ groundMaterial.diffuseColor = new BABYLON.Color3(0.25, 0.25, 0.25);
248
+ groundMaterial.specularPower = 128;
249
+ const groundReflectionTexture = (groundMaterial.reflectionTexture = new BABYLON.MirrorTexture("MirrorTexture", 1024, scene, true));
250
+ groundReflectionTexture.mirrorPlane = BABYLON.Plane.FromPositionAndNormal(ground.position, ground.getFacetNormal(0).scale(-1));
251
+ groundReflectionTexture.renderList = [modelMesh];
252
+ groundReflectionTexture.level = 0.25;
253
+ const defaultPipeline = new BABYLON.DefaultRenderingPipeline("default", true, scene, [mmdCamera, camera]);
254
+ defaultPipeline.samples = 4;
255
+ defaultPipeline.bloomEnabled = true;
256
+ defaultPipeline.bloomThreshold = 0;
257
+ defaultPipeline.bloomWeight = 0.1;
258
+ defaultPipeline.bloomKernel = 38;
259
+ defaultPipeline.bloomScale = 0.5;
260
+ defaultPipeline.chromaticAberrationEnabled = true;
261
+ defaultPipeline.chromaticAberration.aberrationAmount = 0;
262
+ defaultPipeline.fxaaEnabled = true;
263
+ defaultPipeline.imageProcessingEnabled = true;
264
+ defaultPipeline.imageProcessing.toneMappingEnabled = false;
265
+ defaultPipeline.imageProcessing.toneMappingType = BABYLON.ImageProcessingConfiguration.TONEMAPPING_ACES;
266
+ defaultPipeline.imageProcessing.vignetteWeight = 0.5;
267
+ defaultPipeline.imageProcessing.vignetteStretch = 0.5;
268
+ defaultPipeline.imageProcessing.vignetteColor = new BABYLON.Color4(0, 0, 0, 0);
269
+ defaultPipeline.imageProcessing.vignetteEnabled = true;
270
+ const lensEffect = new BABYLON.LensRenderingPipeline('lensEffects', {edge_blur: engine.hostInformation.isMobile ? 0.0 : 1.0, chromatic_aberration: engine.hostInformation.isMobile ? 0.5 : 1.0, distortion: 0.2}, scene, 1.0);
271
+ scene.postProcessRenderPipelineManager.attachCamerasToRenderPipeline('lensEffects', camera);
272
+ scene.postProcessRenderPipelineManager.attachCamerasToRenderPipeline('lensEffects', mmdCamera);
273
+ // BABYLON.ParticleHelper.CreateAsync("rain", scene, false).then((set) => {set.start()});
274
+ const guiCamera = new BABYLON.ArcRotateCamera("GUICamera", Math.PI / 2 + Math.PI / 7, Math.PI / 2, 100, new BABYLON.Vector3(0, 20, 0), scene);
275
+ guiCamera.layerMask = 0x10000000;
276
+ scene.activeCameras = [mmdCamera, guiCamera];
277
+ let lastClickTime = -Infinity;
278
+ canvas.onclick = () => {
279
+ const currentTime = performance.now();
280
+ if (500 < currentTime - lastClickTime) {
281
+ lastClickTime = currentTime;
282
+ return;
283
+ }
284
+ lastClickTime = -Infinity;
285
+ if (scene.activeCameras[0] === mmdCamera) scene.activeCameras = [camera, guiCamera];
286
+ else scene.activeCameras = [mmdCamera, guiCamera];
287
+ };
288
+ let engineInstrumentation = new BABYLON.EngineInstrumentation(engine);
289
+ engineInstrumentation.captureGPUFrameTime = true;
290
+ engineInstrumentation.captureShaderCompilationTime = true;
291
+ let sceneInstrumentation = new BABYLON.SceneInstrumentation(scene);
292
+ sceneInstrumentation.captureActiveMeshesEvaluationTime = true;
293
+ sceneInstrumentation.captureFrameTime = true;
294
+ sceneInstrumentation.captureParticlesRenderTime = true;
295
+ sceneInstrumentation.captureRenderTime = true;
296
+ sceneInstrumentation.captureCameraRenderTime = true;
297
+ sceneInstrumentation.captureRenderTargetsRenderTime = true;
298
+ sceneInstrumentation.captureInterFrameTime = true;
299
+ let isLimitFPS = false;
300
+ let targetFPS = null;
301
+ if (Object.keys(urlQueryObject).some(el => el === 'fps')) {
302
+ isLimitFPS = true;
303
+ targetFPS = parseFloat(urlQueryObject.fps) + 0.35;
304
+ engine.customAnimationFrameRequester = {
305
+ requestAnimationFrame: (func) => {setTimeout(func, 1000 / targetFPS - sceneInstrumentation.frameTimeCounter.current - sceneInstrumentation.activeMeshesEvaluationTimeCounter.current - sceneInstrumentation.particlesRenderTimeCounter.lastSecAverage - sceneInstrumentation.renderTimeCounter.current - sceneInstrumentation.cameraRenderTimeCounter.current - sceneInstrumentation.renderTargetsRenderTimeCounter.current)}
306
+ };
307
+ }
308
+ const advancedTexture = BABYLON.GUI.AdvancedDynamicTexture.CreateFullscreenUI("UI");
309
+ advancedTexture.layer.layerMask = 0x10000000;
310
+ advancedTexture.renderScale = 1.5;
311
+ const textblock = new BABYLON.GUI.TextBlock();
312
+ // textblock.widthInPixels = 800;
313
+ // textblock.left = 10;
314
+ textblock.text = `${engine._glRenderer}\n${engine._glVersion}`;
315
+ textblock.fontSize = 32;
316
+ textblock.fontFamily = "'SF Mono', 'SF Pro Text', 'Inter', 'Noto Sans JP', 'system-ui'";
317
+ textblock.textHorizontalAlignment = BABYLON.GUI.Control.HORIZONTAL_ALIGNMENT_LEFT;
318
+ textblock.textVerticalAlignment = BABYLON.GUI.Control.VERTICAL_ALIGNMENT_TOP;
319
+ textblock.horizontalAlignment = BABYLON.GUI.Control.HORIZONTAL_ALIGNMENT_LEFT;
320
+ textblock.verticalAlignment = BABYLON.GUI.Control.VERTICAL_ALIGNMENT_TOP;
321
+ textblock.color = "#ffffff";
322
+ advancedTexture.addControl(textblock);
323
+ const textBlockUpdateDisp = setInterval(() => {
324
+ if (engine.hostInformation.isMobile) {
325
+ textblock.text = `${engine._glRenderer}
326
+ ${engine._glVersion}
327
+
328
+ ${engine.frameId} f
329
+ ${mathFunc.rounder('floor', engine.performanceMonitor.averageFPS, 2)} fps
330
+ ${mathFunc.rounder('floor', engine.performanceMonitor.averageFrameTime, 2)} ms`;
331
+ } else {
332
+ textblock.text = `${engine._glRenderer}
333
+ ${engine._glVersion}
334
+
335
+ Main Model Path: ${resourcePaths.mainModel}
336
+ Main Motion Path: ${resourcePaths.mainMotion}
337
+ Main Audio Path: ${resourcePaths.audio}
338
+
339
+ FPS Disp: ${mathFunc.rounder('floor', engine.performanceMonitor.averageFPS, 2, true)} fps avg (${mathFunc.rounder('floor', engine.performanceMonitor.instantaneousFPS, 2, true)} fps)
340
+ FPS Limit: ${isLimitFPS ? mathFunc.rounder('floor', parseFloat(urlQueryObject.fps), 2, true) + ' fps' : 'unlimited'}
341
+ FPS Real: ${isLimitFPS ? mathFunc.rounder('floor', 1000 / sceneInstrumentation.frameTimeCounter.lastSecAverage, 2, true) : mathFunc.rounder('floor', 1000 / sceneInstrumentation.frameTimeCounter.lastSecAverage, 2, true)} fps
342
+
343
+ Active Meshes: ${scene.getActiveMeshes().length} / ${scene.meshes.length}
344
+ Total Vertices: ${scene.totalVerticesPerfCounter.current}
345
+ Active Indices: ${scene.totalActiveIndicesPerfCounter.current}
346
+ Total Objects: ${scene.materials.length} mat, ${scene.textures.length} tex, ${scene.animatables.length} anm, ${scene.lights.length} lit
347
+ Draw Calls Count: ${sceneInstrumentation.drawCallsCounter.current}
348
+
349
+ Frame Count: ${engine.frameId} frame
350
+ Actually Frame: ${mathFunc.rounder('floor', engine.performanceMonitor.averageFrameTime, 2, true)} ms avg (${mathFunc.rounder('floor', engine.performanceMonitor.instantaneousFrameTime, 2, true)} ms)
351
+ Scene Frame: ${mathFunc.rounder('ceil', sceneInstrumentation.frameTimeCounter.lastSecAverage, 2, true)} ms avg (${mathFunc.rounder('ceil', sceneInstrumentation.frameTimeCounter.current, 2, true)} ms)
352
+ Active Meshes Eval: ${mathFunc.rounder('ceil', sceneInstrumentation.activeMeshesEvaluationTimeCounter.lastSecAverage, 2, true)} ms avg (${mathFunc.rounder('ceil', sceneInstrumentation.activeMeshesEvaluationTimeCounter.current, 2, true)} ms)
353
+ Particles Render: ${mathFunc.rounder('ceil', sceneInstrumentation.particlesRenderTimeCounter.lastSecAverage, 2, true)} ms avg (${mathFunc.rounder('ceil', sceneInstrumentation.particlesRenderTimeCounter.current, 2, true)} ms)
354
+ Inter Frame: ${mathFunc.rounder('ceil', sceneInstrumentation.interFrameTimeCounter.lastSecAverage, 2, true)} ms avg (${mathFunc.rounder('ceil', sceneInstrumentation.interFrameTimeCounter.current, 2, true)} ms)
355
+ GPU Frame: ${mathFunc.rounder('ceil', engineInstrumentation.gpuFrameTimeCounter.lastSecAverage * 0.000001, 2, true)} ms avg (${mathFunc.rounder('ceil', engineInstrumentation.gpuFrameTimeCounter.current * 0.000001, 2, true)} ms)
356
+ Shader Comp Total: ${mathFunc.rounder('ceil', engineInstrumentation.shaderCompilationTimeCounter.count, 2, true)} ms
357
+ Scene Render: ${mathFunc.rounder('ceil', sceneInstrumentation.renderTimeCounter.lastSecAverage, 2, true)} ms avg (${mathFunc.rounder('ceil', sceneInstrumentation.renderTimeCounter.current, 2, true)} ms)
358
+ Camera Render: ${mathFunc.rounder('ceil', sceneInstrumentation.cameraRenderTimeCounter.lastSecAverage, 2, true)} ms avg (${mathFunc.rounder('ceil', sceneInstrumentation.cameraRenderTimeCounter.current, 2, true)} ms)
359
+ Targets Render: ${mathFunc.rounder('ceil', sceneInstrumentation.renderTargetsRenderTimeCounter.lastSecAverage, 2, true)} ms avg (${mathFunc.rounder('ceil', sceneInstrumentation.renderTargetsRenderTimeCounter.current, 2, true)} ms)
360
+
361
+ Heap: ${!performance.memory ? 'Unavailable' : (mathFunc.rounder('ceil', performance.memory.usedJSHeapSize / 1024 / 1024, 2, true))} MB / ${!performance.memory ? 'Unavailable' : (mathFunc.rounder('ceil', performance.memory.totalJSHeapSize / 1024 / 1024, 2, true))} MB (Limit ${!performance.memory ? 'Unavailable' : (mathFunc.rounder('ceil', performance.memory.jsHeapSizeLimit / 1024 / 1024, 2, true))} MB)
362
+ `;
363
+ }
364
+ }, 10);
365
+ await new Promise(resolve => setTimeout(resolve, 1000));
366
+ mmdRuntime.playAnimation();
367
+ return scene;
368
+ }
369
+ }
370
+ createScene = function () {
371
+ return Playground.CreateScene(engine, engine.getRenderingCanvas());
372
+ };
373
+ window.initFunction = async function () {
374
+ globalThis.HK = await HavokPhysics();
375
+
376
+ var asyncEngineCreation = async function () {
377
+ try {
378
+ return createDefaultEngine();
379
+ } catch (e) {
380
+ console.log("the available createEngine function failed. Creating the default engine instead");
381
+ return createDefaultEngine();
382
+ }
383
+ };
384
+
385
+ window.engine = await asyncEngineCreation();
386
+ if (!engine) throw "engine should not be null.";
387
+ startRenderLoop(engine, canvas);
388
+ window.scene = createScene();
389
+ };
390
+ initFunction().then(() => {
391
+ scene.then((returnedScene) => {
392
+ sceneToRender = returnedScene;
393
+ });
394
+ });
395
+
396
+ // Resize
397
+ window.addEventListener("resize", function () {
398
+ engine.resize();
399
+ });