Das Laden eines Modells mit GLTFLOADER durch Webarbeiter führt zu einer Netzverformung auf SkinnedMeshJavaScript

Javascript-Forum
Anonymous
 Das Laden eines Modells mit GLTFLOADER durch Webarbeiter führt zu einer Netzverformung auf SkinnedMesh

Post by Anonymous »

Meine Seite dauert lange, bis die Modelle geladen werden, sobald die Seite geladen wird, wenn die Seite geladen wird. Fast 0! minimal reproduzierbares Beispiel < /p>

Code: Select all

index.html

Code: Select all




 default loading 



  Web Worker loading 





{
"imports": {
"three": "https://esm.sh/three@0.168.0/build/three.module.js",
"three/addons/": "https://esm.sh/three@0.168.0/examples/jsm/"
}
}





// base libs
import * as THREE from 'three';
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'
import * as SkeletonUtils from 'three/addons/utils/SkeletonUtils.js';

// dependencies
let _glbLoader = new GLTFLoader();
// state
let _isSceneInstantiated = false;
// models
let _itemsContainer = undefined;
let _baseSkeleton = undefined;
// animation
let _clock = new THREE.Clock();
let _skeletonMixer = undefined;
// parts
let _scene = undefined;
let _camera = undefined;
let _renderer = undefined;

let InstantiateScene = (hasToLoadWithWebWorker = true) =>
{
if (!_isSceneInstantiated)
{
_isSceneInstantiated = true;

let canvas = document.getElementById('canvas-container');
canvas.classList.remove('hidden');
let rect = canvas.getBoundingClientRect();

_scene = new THREE.Scene();
_camera = new THREE.PerspectiveCamera(30, rect.width / rect.height, 0.1, 1000);
_renderer = new THREE.WebGLRenderer({ canvas: canvas, });
_renderer.setSize(rect.width, rect.height);
_renderer.setAnimationLoop(animate);
const geometry = new THREE.BoxGeometry(0.4, 0.4, 0.4);
const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
const cube = new THREE.Mesh(geometry, material);
_scene.add(cube);
_camera.position.z = 5;

function animate()
{
cube.rotation.x += 0.01;
cube.rotation.y += 0.01;

let deltaTime = (60.0 / 1000.0);

if (_clock)
deltaTime = _clock.getDelta();

if (_skeletonMixer)
_skeletonMixer.update(deltaTime);

if (_renderer)
_renderer.render(_scene, _camera);
}

_scene.background = new THREE.Color("rgb(10, 80, 10)");

SetupInitialState(hasToLoadWithWebWorker);
}
}

let DestroyScene = () =>
{
if (_isSceneInstantiated)
{
_isSceneInstantiated = false;

_scene = undefined;
_camera = undefined;
_renderer = undefined;

let canvas = document.getElementById('canvas-container');
canvas.classList.add('hidden');
}
}

let SetupInitialState = (hasToLoadWithWebWorker = true) =>
{
_itemsContainer = new THREE.Group();
_scene.add(_itemsContainer);

_itemsContainer.position.set(0, -1, 0);

InstantiateLights();

let onFinishCallback = () =>
{
let blueMaterial = new THREE.MeshStandardMaterial({ color: 0x0000ff });
let redMaterial = new THREE.MeshStandardMaterial({ color:  0xff0000 });

InstantiateDynamicBodyPart('default-male-body-01-model-01.glb', blueMaterial, hasToLoadWithWebWorker);
InstantiateDynamicBodyPart('default-male-shirt-01-model-01.glb', redMaterial, hasToLoadWithWebWorker);
}

InstantiateBaseSkeleton(onFinishCallback, hasToLoadWithWebWorker)
}

let InstantiateLights = () =>
{
const mainUpLight = new THREE.DirectionalLight(0xffffff, 2);
const secondaryUpLight = new THREE.DirectionalLight(0xffffff, 1);

mainUpLight.position.set(20, 30, 10);
mainUpLight.target.position.set(0, 0, 0);
mainUpLight.castShadow = true;

secondaryUpLight.position.set(-20, 30, -10);
secondaryUpLight.target.position.set(0, 0, 0);
secondaryUpLight.castShadow = true;

const reverseLight1 = new THREE.DirectionalLight(0xffffff, 1);
const reverseLight2 = new THREE.DirectionalLight(0xffffff, 1);

reverseLight1.position.set(20, -30, -10);
reverseLight1.target.position.set(0, 0, 0);
reverseLight1.castShadow = false;
reverseLight2.position.set(-20, -30, 10);
reverseLight2.target.position.set(0, 0, 0);
reverseLight2.castShadow = false;

const ambientLight = new THREE.AmbientLight(0xaaaaaa); // soft white light

_scene.add(ambientLight);
_scene.add(mainUpLight);
}

let InstantiateBaseSkeleton = async (onFinishCallback, hasToLoadWithWebWorker = true) =>
{
let onFinishCallback2 = (baseModel) =>
{
if (baseModel)
{
_itemsContainer.add(baseModel);

let skeletonHelper = new THREE.SkeletonHelper(_scene);
_scene.add(skeletonHelper)

let baseBones = [];

baseModel.traverse(
(object) =>
{
switch (object.type)
{
case 'Bone':
baseBones.push(object);
break;
}
});

_baseSkeleton = new THREE.Skeleton(baseBones);

let charAnimationGroup = new THREE.AnimationObjectGroup(baseModel);
_skeletonMixer = new THREE.AnimationMixer(charAnimationGroup);

if (baseModel.animations.length >  0)
{
let currentAnimation = _skeletonMixer.clipAction(baseModel.animations[1]);
currentAnimation.play();
}

let finalScale = 1;
let finalPositionX = 0;
let finalPositionY = 0;

baseModel.scale.set(finalScale, finalScale, finalScale);
baseModel.position.set(finalPositionX, 0, finalPositionY);
}

onFinishCallback();
};

let baseModel = await LoadObject3D('default-skeleton-01.glb', onFinishCallback2, hasToLoadWithWebWorker);
}

let InstantiateDynamicBodyPart = (
modelFileName,
baseMaterial,
hasToLoadWithWebWorker = true) =>
{
let onFinishCallback = (baseModel) =>
{
if (baseModel)
{
let skinnedMeshesList = [];

baseModel.traverse(
(object) =>
{
switch (object.type)
{
case 'SkinnedMesh':
skinnedMeshesList.push(object);
break;
}
});

InjectMaterial(baseModel, baseMaterial);

skinnedMeshesList.map(
(skinnedMesh) =>
{
_itemsContainer.add(skinnedMesh);
skinnedMesh.skeleton = _baseSkeleton;
});
}
}

LoadObject3D(modelFileName, onFinishCallback, hasToLoadWithWebWorker);
}

let InjectMaterial = (baseModel, material) =>
{
if (baseModel)
{
baseModel.traverse(
(object) =>
{
if (object.isMesh)
object.material = material;
});
}
}

let GetRandomId = (length) =>
{
let result = '';

const characters = 'abcdefghijklmnopqrstuvwxyz0123456789';
const charactersLength = characters.length;
let counter = 0;
while (counter < length)
{
result += characters.charAt(Math.floor(Math.random() * charactersLength));
counter += 1;
}

return result;
}

let LoadObject3D = (assetSubPath, onFinishCallback, hasToLoadWithWebWorker = true) =>
{
let value = undefined;

let assetsRootPath = 'https://y87vj9.csb.app/public/';
let assetPath = assetsRootPath + assetSubPath;

if (!hasToLoadWithWebWorker)
{
if (!value)
{
console.log('[default loading] > loading model...  | assetPath =', assetPath);

let onSuccessCallback =
(modelGLTF) =>
{
console.log('[default loading] >>> model loaded! | assetPath =', assetPath);

let moodelObject3D = SkeletonUtils.clone(modelGLTF.scene.clone());
moodelObject3D.animations = modelGLTF.animations;

onFinishCallback(moodelObject3D);
};

_glbLoader.load(assetPath, onSuccessCallback);
};
}
else
{
if (typeof Worker !== 'undefined')
{
let requestId = GetRandomId(16);
_webworkersCallbacksById[requestId] = onFinishCallback;

let webworkerRequest =
{
RequestId: requestId,
ProcessName: 'LoadModel',
Params: [assetPath],
}

_worker.postMessage(JSON.stringify(webworkerRequest));
}
else
{
// Web workers are not supported in this environment.
// You should add a fallback so that your program still executes correctly.
console.error('$$q> AssetsService.GetAsset | Web workers are not supported in this environment!');
}
}
}

// region Worker
// state
let _webworkersCallbacksById = {};

let HandleWebworkerMessage = (data) =>
{
// let webworkerResponse = JSON.parse(data);
let webworkerResponse = JSON.parse(data.data);
let requestId = webworkerResponse.RequestId;
let baseModel = undefined;

if (webworkerResponse.RawResponse)
{
let object3DJSON = webworkerResponse.RawResponse;

const loader = new THREE.ObjectLoader();
baseModel = loader.parse(object3DJSON);
}
else
console.error('$> webworkerResponse.RawResponse is undefined! ');

let isCallbackRegistered = requestId in _webworkersCallbacksById;

if (isCallbackRegistered)
{
let callback = _webworkersCallbacksById[requestId];

callback(baseModel);
delete _webworkersCallbacksById[requestId];
}
else
console.error('$> Unexpected requestId! requestId = ', requestId);
}

const _worker = new Worker("/model-loading-worker.js", { type: 'module' });
_worker.onmessage = HandleWebworkerMessage;
//end region Worker

let DefaultIntantiatingButton = () =>
{
DestroyScene();
InstantiateScene(false);
}

let WebWorkerIntantiatingButton = () =>
{
DestroyScene();
InstantiateScene(true);
}

let SetupButtons = () =>
{
let defaultIntantiatingButton = document.querySelector('#default-intantiating-button');
let webWorkerIntantiatingButton = document.querySelector('#web-worker-intantiating-button');

defaultIntantiatingButton.onclick = DefaultIntantiatingButton;
webWorkerIntantiatingButton.onclick = WebWorkerIntantiatingButton;
}

SetupButtons();
DefaultIntantiatingButton();


< /code>
model-loading-worker.js
import { GLTFLoader } from 'https://esm.sh/three@0.168.0/examples/j ... FLoader.js';

// dependencies
let _glbLoader = new GLTFLoader();

let HandleMessage = async (request) =>
{
let response = {};
response.RequestId = request.RequestId;

switch (request.ProcessName)
{
case 'LoadModel':
{
let assetPath = request.Params[0];
console.log('[web worker] > loading model... | assetPath =', assetPath);

let onSuccessCallback =
(modelGLTF) =>
{
console.log('[web worker] >>> model loaded! | assetPath =', assetPath);

let finalAssetValue = modelGLTF.scene;

finalAssetValue.animations = modelGLTF.animations;

let finalAssetValueObject3DJSON = finalAssetValue.toJSON()
response.RawResponse = finalAssetValueObject3DJSON;
}

await _glbLoader.loadAsync(assetPath).then(onSuccessCallback);

break
}

default:
{
console.error('Unexpected request.ProcessName! | request.ProcessName =', request.ProcessName);
break;
}
}

return response;
}

addEventListener(
'message',
async ({ data }) =>
{
let webworkerRequest = JSON.parse(data, function (key, value)
{
return value;
});

let response = await HandleMessage(webworkerRequest);
data = JSON.stringify(response);

postMessage(data);
});

< /code>
I'll leave a link below to a practical example on codesandbox with reproduction (change between loading modes by changing the state of the checkbox in the lower right corner and then press "Instatiate Scene". The buttons have a yellow hover to test with the mouse if the page is really stuck during loading, as well as the cube animation)
https://codesandbox.io/p/sandbox/y87vj9
Any help or guidance will be very welcome, I've been trying to find a way to improve the optimization of this page for about 2 weeks, and this was the only thing that had a good result in performance, however, it's not working.

Quick Reply

Change Text Case: 
   
  • Similar Topics
    Replies
    Views
    Last post