Thanks for the feedback regarding the scrnshots, I’ll have that in mind!
I feel like I haven’t presented my issue thoroughly enough and that may be why you have a hard time helping me. I’ll give this another shot and I’ll try to create a clearer post.
This is my modelLoader
module. It handles all the GLTF assets
import * as THREE from "https://cdn.jsdelivr.net/npm/three@0.117/build/three.module.js";
import { loadingManager, loadedJSONdata, scene, clock } from "../app.js";
import { GLTFLoader } from "https://cdn.jsdelivr.net/npm/three@0.117/examples/jsm/loaders/GLTFLoader.js";
import { DRACOLoader } from "https://cdn.jsdelivr.net/npm/three@0.117/examples/jsm/loaders/DRACOLoader.js";
export let modelAnimationMixers = [];
export let modelAnimations = [];
export let frontFoam;
//let video = document.getElementById("video"); // link video HMTL element to JS
let video = document.getElementById("video"); // link video HMTL element to JS
let launchBtn = document.getElementById("launch");
video.play();
launchBtn.addEventListener("launch", function () {
video.currentTime = 3;
});
let videoTexture = new THREE.VideoTexture(video); // create texture for video
export let FoamAnimMat = new THREE.MeshBasicMaterial({
alphaMap: videoTexture,
alphaTest: 0.45,
side: THREE.DoubleSide,
fog: false
});
export function loadModels() {
const gltfLoader = new GLTFLoader(loadingManager); // This comes from GLTFLoader.js.
const dracoLoader = new DRACOLoader(loadingManager);
// Specify path to a folder containing WASM/JS decoding libraries.
dracoLoader.setDecoderPath(
"https://cdn.8thwall.com/web/aframe/draco-decoder/"
);
//loader.setDecoderPath('https://unpkg.com/three/examples/js/libs/draco/');
//loader.setDecoderConfig( { type: 'js' } );
// Optional: Pre-fetch Draco WASM/JS module.
dracoLoader.preload();
gltfLoader.setDRACOLoader(dracoLoader);
let modelScale;
const modelFiles = loadedJSONdata.ModelFiles;
if (loadedJSONdata.ModelScale) {
modelScale = new THREE.Vector3(
loadedJSONdata.ModelScale,
loadedJSONdata.ModelScale,
loadedJSONdata.ModelScale
);
} else {
modelScale = new THREE.Vector3(1, 1, 1);
}
let i = 0;
function loadModelFile(url) {
gltfLoader.load(url, (gltf) => {
const loadedObject = gltf.scene;
//console.log("loggar loadedObject som " + loadedObject.scale);
// loop throught the object and see if there are any camera animations
loadedObject.traverse((obj) => {
//console.log(obj);
if (obj.isSkinnedMesh) obj.castShadow = true;
obj.frustumCulled = false;
if (
obj.name.includes("glas") ||
obj.name.includes("Glas") ||
obj.name.includes("Mesh_112")
) {
obj.material = new THREE.MeshBasicMaterial({
color: 0x000,
transparent: true,
opacity: 0.75,
side: THREE.DoubleSide,
depthWrite: false,
depthTest: true,
fog: false
//envMap: envMap
});
//console.log("obj is", obj);
obj.castShadow = false;
obj.receiveShadow = false;
obj.renderOrder = 900;
} else if (
obj.name.includes("VILJANtop") ||
obj.name.includes("VILJANflaglow") ||
obj.name.includes("VILJANgardiner") ||
obj.name.includes("VILJANinside") ||
obj.name.includes("VILJANpersienner") ||
obj.name.includes("VILJANsmall") ||
obj.name.includes("VILJANtyg")
) {
//obj.material.fog = false;
// MeshBasicMaterial behövs tillfälligt för att ha en fungerande lightmap. Fixa!
/* */
// console.log(obj);
let newMat = new THREE.MeshBasicMaterial({
map: obj.material.map,
lightMap: obj.material.emissiveMap,
fog: false,
side: THREE.DoubleSide
});
obj.material.dispose();
obj.material = newMat;
if (obj.name.includes("VILJANflaglow")) {
obj.visible = false;
}
//console.log(obj);
} else if (
obj.name.includes("VILJANbottom") ||
obj.name.includes("VILJANproppeller")
) {
//console.log(obj);
let newMat = new THREE.MeshBasicMaterial({
map: obj.material.map,
lightMap: obj.material.emissiveMap,
fog: true,
side: THREE.DoubleSide
});
obj.material.dispose();
obj.material = newMat;
//console.log(obj);
} else if (obj.name.includes("foam_front_short002")) {
obj.material.fog = false;
frontFoam = obj;
frontFoam.material.map.blending = 2;
frontFoam.material.emissive = { r: 1, g: 1, b: 1 };
frontFoam.material.emissiveIntensity = 2;
//console.log("console log 505", obj);
} else if (obj.name.includes("backfoam_plane")) {
// console.log("console.log from 507", obj);
obj.material = FoamAnimMat;
let objCopy = obj.clone();
objCopy.position.y = -0.06;
objCopy.material.flipY = false;
scene.add(objCopy);
//obj.fog = false;
//obj.alphaTest = 1;
obj.position.y = 0.06;
} else if (obj.name.includes("underwater")) {
//obj.position.y = 15;
} else if (obj.name.includes("Islands")) {
//obj.position.y = 15;
//console.log(obj);
/*
obj.material = new THREE.MeshBasicMaterial({
map: obj.material.map,
fog: false
}); */
obj.material.fog = false;
}
obj.castShadow = true;
obj.receiveShadow = true;
});
let skeleton = new THREE.SkeletonHelper(loadedObject);
let propellerAction, flagAction, actions;
skeleton.visible = true;
scene.add(skeleton);
//console.log("180", skeleton);
const mixer = new THREE.AnimationMixer(gltf.scene);
const clips = gltf.animations;
console.log(clips);
flagAction = mixer.clipAction(clips[1]);
propellerAction = mixer.clipAction(clips[8]);
//console.log(mixer.clipAction(clips[1]));
//console.log(mixer.clipAction(clips[8]));
actions = [flagAction, propellerAction];
function activateAllActions() {
actions.forEach(function (action) {
action.play();
// console.log("198", action);
});
}
function animateBoat() {
requestAnimationFrame(animateBoat);
let mixerUpdateDelta = clock.getDelta();
mixer.update(mixerUpdateDelta);
}
activateAllActions();
animateBoat();
loadedObject.scale.set(modelScale.x, modelScale.y, modelScale.z);
scene.add(loadedObject);
if (i < modelFiles.length - 1) {
// When the model has been loaded and added, load the next one
i++;
loadModelFile(modelFiles[i].src);
}
});
}
//Load first model in array
loadModelFile(modelFiles[i].src);
}
The actual animations code stems from line 169 - 202.
let skeleton = new THREE.SkeletonHelper(loadedObject);
let propellerAction, flagAction, actions;
skeleton.visible = true;
scene.add(skeleton);
//console.log("180", skeleton);
const mixer = new THREE.AnimationMixer(gltf.scene);
const clips = gltf.animations;
console.log(clips);
flagAction = mixer.clipAction(clips[1]);
propellerAction = mixer.clipAction(clips[8]);
//console.log(mixer.clipAction(clips[1]));
//console.log(mixer.clipAction(clips[8]));
actions = [flagAction, propellerAction];
function activateAllActions() {
actions.forEach(function (action) {
action.play();
// console.log("198", action);
});
}
function animateBoat() {
requestAnimationFrame(animateBoat);
let mixerUpdateDelta = clock.getDelta();
mixer.update(mixerUpdateDelta);
}
activateAllActions();
animateBoat();
When I rewrite my code I used this threejs example
This is the code from the example
skeleton = new THREE.SkeletonHelper( model );
skeleton.visible = false;
scene.add( skeleton );
//
createPanel();
//
const animations = gltf.animations;
mixer = new THREE.AnimationMixer( model );
idleAction = mixer.clipAction( animations[ 0 ] );
walkAction = mixer.clipAction( animations[ 3 ] );
runAction = mixer.clipAction( animations[ 1 ] );
actions = [ idleAction, walkAction, runAction ];
activateAllActions();
animate();
Only difference is(that I have noticed) is that the developers activateAllActions
and animate
function is outside of the gltfloader, whereas mine is inside. I tried moving it out before but I code some scope-errors.
This is the functions
function activateAllActions() {
setWeight( idleAction, settings[ 'modify idle weight' ] );
setWeight( walkAction, settings[ 'modify walk weight' ] );
setWeight( runAction, settings[ 'modify run weight' ] );
actions.forEach( function ( action ) {
action.play();
} );
}
function animate() {
// Render loop
requestAnimationFrame( animate );
idleWeight = idleAction.getEffectiveWeight();
walkWeight = walkAction.getEffectiveWeight();
runWeight = runAction.getEffectiveWeight();
// Update the panel values if weights are modified from "outside" (by crossfadings)
updateWeightSliders();
// Enable/disable crossfade controls according to current weight values
updateCrossFadeControls();
// Get the time elapsed since the last frame, used for mixer update (if not in single step mode)
let mixerUpdateDelta = clock.getDelta();
// If in single step mode, make one step and then do nothing (until the user clicks again)
if ( singleStepMode ) {
mixerUpdateDelta = sizeOfNextStep;
sizeOfNextStep = 0;
}
// Update the animation mixer, the stats panel, and render this frame
mixer.update( mixerUpdateDelta );
stats.update();
renderer.render( scene, camera );
}
As you can see, I(have tried to) declare(d) the variables containing the mixer.clipAction
the same way and I call it the same way. Our functions are also very much alike.
When I try to run this, I get the following error
sse-hooks.f742b80f43c5a2e0e619b0d97b5886cd.js:1 TypeError: Cannot read properties of undefined (reading 'uuid')
at AnimationMixer.clipAction (three.module.js:45818:48)
at modelLoader.js:181:26
at GLTFLoader.js:148:7
at GLTFLoader.js:1527:4
line 181
refers to flagAction = mixer.clipAction(clips[1]);
The working clip is just rotating object (the propeller) whilst the flagClip has bones.
Now… Question is, how do I solve this? I’ve been at it for a week now and I’ve developed a tunnel-vision, the solution might be easier than I think but I am so stuck and I can’t see it.
I hope this post was better, cheers!
Thanks again for sticking with me and giving great feedback.