The difference is you are masking each object. I am masking the container that has the objects. I will try to mask each object.
The issue is the Blob. It won't clone it with the blob as the mask.
I have tried other stuff like fontloader, textgeometry, and find that they don't work under ZIM_THREE but if add threejs script tag, they work but now I have two imports of THREE. Is this the only way or how can we utilize other "addons" with ZIM's version of THREE and not have multiple imports of THREE?
Tell us what is missing and we may be able to add them in.
Use NPM to bundle any three.js stuff and ZIM stuff and the ZIM Three helper - @zimjs/three - npm
Or just use script tags for createjs, ZIM and three helper module rather than the single import.
<script src="https://zimjs.org/cdn/1.5.0/createjs.js"></script>
<script src="https://zimjs.org/cdn/017/zim_min.js"></script>
<!-- add your threejs libraries -->
<script src="https://zimjs.org/cdn/three_2.3.js"></script>
I get this. three_2.3.js:22 Uncaught ReferenceError: THREE is not defined
You need to add the script tag to the three.js file where it says to add your threejs libraries. I think that will automatically make a THREE reference.
I have tried a couple dozen variations of scripts and/or imports and nothing.
I keep getting the same thing and the other error is Three is not defined.
I am using the TWEEN, TextGeometry, and FontLoader files.
Ah... lost this thread. We are doing some work on three.js imports - so what is your current needs? Perhaps add them to a request. Thanks
KX2loader of threeJS for me and ml5js gestures for the mouth and hands
any idea how to add this script, not working when using `import ""https://cdnjs.cloudflare.com/ajax/libs/three.js/r132/examples/js/loaders/FBXLoader.js"
I have already head ok turning towards mouse,
ZAPP: MetaHuman T-pose: personbodyshorts (3D+Tshirt) mouth/talk+360(noVRbtn) | ZIM JavaScript Canvas Framework
but would like waving animation
I need fbx import of movements created with , to attach to metaHuman video 06:30 https://youtu.be/egQFAeu6Ihw?si=1PjrAfIVYcSS9Xy5&t=391
but get always

because new FBXloader() does not exists
you can find the waving at https://www.mixamo.com/#/?page=1&query=wave
saved at

the full code to test
import "https://zimjs.org/cdn/018/zim_pizzazz"
import "https://zimjs.org/cdn/018/zim_three"
import "https://zimjs.org/cdn/018/zim_cam"
import "https://cdnjs.cloudflare.com/ajax/libs/three.js/r132/examples/js/loaders/FBXLoader.js"
new Frame(FIT, 1280, 720, white, dark, ready);
function ready(F, stage, stageW, stageH) {
if (document.Blob) Blob = document.Blob;
const CAM_WIDTH = 400, CAM_HEIGHT = 300;
const AVATAR_MODEL_URL = "https://raw.githubusercontent.com/karelrosseel/model3D/master/personbody/personshort/boyshorts.glb";
const WAVE_ANIM_URL = "https://raw.githubusercontent.com/karelrosseel/model3D/master/personbody/personshort/waving.fbx";
const CORRESPONDING_VISEME = {
'A':'viseme_aa','B':'viseme_PP','C':'viseme_kk','D':'viseme_DD',
'E':'viseme_E','F':'viseme_FF','G':'viseme_kk','H':'viseme_CH',
'I':'viseme_I','J':'viseme_DD','K':'viseme_kk','L':'viseme_nn',
'M':'viseme_PP','N':'viseme_nn','O':'viseme_O','P':'viseme_PP',
'Q':'viseme_kk','R':'viseme_RR','S':'viseme_SS','T':'viseme_DD',
'U':'viseme_U','V':'viseme_FF','W':'viseme_PP','X':'viseme_kk',
'Y':'viseme_I','Z':'viseme_SS'
};
const MOUTH_VISEME = 'viseme_aa';
const TSHIRT_TEXTURES = {
'Blue': "https://i.imgur.com/bMOWMUK.png",
'Red': 'https://i.imgur.com/OoS5kmg.png',
'Black': 'https://i.imgur.com/nMl9krn.png',
'Green': 'https://i.imgur.com/fMBCzMj.png',
'Yellow': 'https://i.imgur.com/ITegOwJ.png',
'Purple': 'https://i.imgur.com/xpRNzD1.png'
};
let model, headMesh, teethMesh, headBone, bodyMesh;
let initialHeadRotation = null;
let currentViseme = null, typingInfluence = 0;
let typingTimer = null;
let isTyping = false;
let blinkInfluence = 0;
const mouse = new THREE.Vector2();
let headFollowEnabled = true;
let view, scene, camera, renderer, controls, sceneOrtho, cameraOrtho;
let speechPanel, tshirtPanel, cameraPanel;
let HUD_speech, HUD_tshirt, HUD_camera, HUD_full, HUD_toggle, HUD_arm;
let holder;
let walkMode = false;
// === FBX ANIMATION VARIABLES ===
let mixer, waveAction;
// ===============================
function changeTshirtTexture(textureName) {
if (!bodyMesh) return;
const url = TSHIRT_TEXTURES[textureName];
if (url) {
new THREE.TextureLoader().load(url, texture => {
texture.flipY = false;
if (bodyMesh.material.map) {
bodyMesh.material.map.dispose();
}
bodyMesh.material.map = texture;
bodyMesh.material.needsUpdate = true;
});
} else {
if (bodyMesh.material.map) {
bodyMesh.material.map.dispose();
}
bodyMesh.material.map = null;
bodyMesh.material.needsUpdate = true;
}
}
const fullscreenPanel = new TextureActive(300, 100, clear);
const restartAgainBtn = new Button(AUTO,70,"Restart").pos(60,0,RIGHT,CENTER,fullscreenPanel)
.tap(()=>{window.location.reload("Refresh")});
var fullscreen = new Button({
width: 60,
height: 60,
corner: 0,
backgroundColor: clear,
rollBackgroundColor: pink,
icon: makeIcon("maximize", white,1.2),
toggleIcon: makeIcon("close", white,1.2)
}).expand(40).addTo(fullscreenPanel).pos(0, 15, RIGHT, TOP);
fullscreen.on("click", () => {
F.fullscreen(!F.isFullscreen);
});
const hudTogglePanel = new TextureActive(220, 60, grey.toAlpha(.5));
new Button(AUTO,50,"Panels").pos(70,0,RIGHT,CENTER,hudTogglePanel);
const hudCheckBox = new CheckBox({ startChecked: true })
.sca(1).pos(0,0,RIGHT,CENTER,hudTogglePanel);
new Pic("https://i.imgur.com/6DYwfHd.png").siz(W,H).center();
new Label({
text:"Walk with WASD or Arrow Keys | Mouse to Look Around | Type and SPEAK",
size: 20,
font:"Roboto",
color:white,
align:CENTER,
backgroundColor:black.toAlpha(0.6)
}).expand(10,5).pos(0,10,CENTER,BOTTOM).ord(2000);
const waiter = new Waiter().show();
// Camera Position Panel
cameraPanel = new TextureActive(300, 120, grey.toAlpha(.5));
const cameraPositions = [
{ x: 0, y: 6.5, z: 0, label: "Close Face" },
{ x: 0, y: 6, z: -3, label: "Standing" },
{ x: 3, y: 6, z: 1, label: "Side View" },
{ x: 0, y: 8, z: 0.5, label: "Top View" }
]
new Label("CAMERA POSITION", 20, "Roboto", white).pos(0, 15, CENTER, TOP, cameraPanel);
const cameraTabs = new Tabs({
tabs: ["1","2","3","4"],
bgColor: black.toAlpha(.8),
color: white,
selectedBgColor: white.toAlpha(.8),
selectedColor: black,
rollBgColor: pink.toAlpha(.8),
width: 265,
height: 55,
spacing: 5,
currentEnabled: true
}).pos(0, 50, CENTER, TOP, cameraPanel);
speechPanel = new TextureActive(500,260, grey.toAlpha(0.7));
new Label("Type and watch the mouth move",24,"Roboto",white).pos(0,15,CENTER,TOP,speechPanel);
const textInput = new TextInput({
width:450,height:50,
text:"Hello, I'm a metaHuman avatar",
backgroundColor: light,color:black,size:30,padding:10
}).pos(0,50,CENTER,TOP,speechPanel);
textInput.on("focus", () => { isTyping = true; });
textInput.on("blur", () => { isTyping = false; });
textInput.on("input", () => {
typingInfluence = 1.5 + Math.random()*0.6;
if(typingTimer) clearTimeout(typingTimer);
typingTimer = setTimeout(()=>{typingInfluence=0;},150);
});
const speakButton = new Button({
width: AUTO, height: 40, label:"SPEAK",
backgroundColor: green, rollBackgroundColor:"darkgreen", corner:10
}).pos(-120,120,CENTER,TOP,speechPanel);
const walkToggle = new Toggle({
width: 130, height: 40,
label:"ORBIT VIEW",
labelLeft:"WALK MODE",
backgroundColor: blue, rollBackgroundColor:"darkblue", corner:10,
startToggled: false, // Start in Orbit Mode
vertical: false
}).pos(90,120,CENTER,TOP,speechPanel);
const headFollowButton = new Toggle({
label:"TURN",
labelLeft:"HEAD OFF",
color:white,
startToggled:true
}).pos(0,185,CENTER,TOP,speechPanel);
tshirtPanel = new TextureActive(300, 320, grey.toAlpha(0.7));
new Label("T-SHIRT TEXTURE",20,"Roboto",white).pos(0,15,CENTER,TOP,tshirtPanel);
let yPos = 55;
Object.keys(TSHIRT_TEXTURES).forEach(textureName => {
const btn = new Button({
width: 260, height: 35,
label: textureName,
backgroundColor: dark,
rollBackgroundColor: "#444",
corner: 5
}).pos(0, yPos, CENTER, TOP, tshirtPanel);
btn.on("click", () => {
changeTshirtTexture(textureName);
});
yPos += 40;
});
// ARM CONTROL PANEL (FBX Animation Control)
const armPnl = new TextureActive(300, 150, grey.toAlpha(0.7));
new Label("WAVE ANIMATION", 24, "Roboto", white).pos(0, 15, CENTER, TOP, armPnl);
const waveToggle = new Toggle({
width: 60, height: 50,
label: "WAVE",
labelLeft: "STOP",
backgroundColor: green, rollBackgroundColor: "darkgreen", corner: 10,
startToggled: false
}).sca(.8).pos(0, 70, CENTER, TOP, armPnl);
// This is where we control the animation playback
waveToggle.on("change", () => {
if (!waveAction) {
new Pane("Waving animation still loading...", orange).show();
waveToggle.toggled = false;
return;
}
if (waveToggle.toggled) {
// Play the wave animation
waveAction.reset().play();
new Pane("Avatar is waving!", green).show();
} else {
// Stop the wave animation and reset to default pose
waveAction.fadeOut(0.5);
// We leave the manual wave controls out to rely solely on the FBX animation
new Pane("Waving stopped.", red).show();
}
});
// --- End of ARM CONTROL PANEL ---
function setupHUD(viewInstance) {
speakButton.on("click", () => {
const text = textInput.text;
if (text.trim() && headMesh && teethMesh) {
speakText(text);
} else if (text.trim() && (!headMesh || !teethMesh)) {
new Pane("Avatar is still loading. Please wait...", red).show();
} else {
new Pane("Please type some text first!", orange).show();
}
});
walkToggle.on("change", () => {
walkMode = walkToggle.toggled;
if (walkMode) {
// Set up First Person Controls
controls = new FirstPersonControls(camera, view.canvas);
controls.lookSpeed = 0.08;
controls.movementSpeed = 5;
controls.lookVertical = true;
controls.noFly = true;
new Pane("Walk Mode: Use WASD/Arrows + Mouse", orange).show();
} else {
// Set up Orbit Controls
controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;
controls.dampingFactor = 0.05;
controls.target.set(0, 5.5, 3);
controls.maxDistance = 30;
controls.maxPolarAngle = Math.PI / 2 - 0.1;
controls.minDistance = 2;
camera.lookAt(0, 5.5, 3);
new Pane("Orbit View: Drag to rotate, scroll to zoom", green).show();
}
});
headFollowButton.on("click", () => {
headFollowEnabled = !headFollowEnabled;
if (!headFollowEnabled) {
if (headBone && initialHeadRotation) {
headBone.rotation.copy(initialHeadRotation);
}
new Pane("Head following disabled", orange).show();
}
});
cameraTabs.on("change", () => {
const pos = cameraPositions[cameraTabs.selectedIndex];
camera.position.set(pos.x, pos.y, pos.z);
camera.lookAt(0, 5.5, 3);
if (!walkMode && controls.target) {
controls.target.set(0, 5.5, 3);
controls.update();
}
});
const panelsArray = [speechPanel, tshirtPanel, cameraPanel, armPnl, fullscreenPanel, hudTogglePanel];
const textureActivesOrtho = new TextureActives(panelsArray, THREE, viewInstance, viewInstance.renderer, viewInstance.sceneOrtho, viewInstance.cameraOrtho, null, 1);
if (textureActivesOrtho && textureActivesOrtho.manager) {
textureActivesOrtho.manager.toggleKey = -1;
textureActivesOrtho.manager.activeLayer = 1;
}
HUD_speech = viewInstance.makePanel(speechPanel, textureActivesOrtho).pos(20,20,RIGHT,BOTTOM,500);
HUD_tshirt = viewInstance.makePanel(tshirtPanel, textureActivesOrtho).pos(20,10,LEFT,BOTTOM,500);
HUD_camera = viewInstance.makePanel(cameraPanel, textureActivesOrtho).pos(10, 10, LEFT, TOP, 500);
HUD_arm = viewInstance.makePanel(armPnl, textureActivesOrtho).pos(0, 10, CENTER, TOP, 500);
HUD_full = viewInstance.makePanel(fullscreenPanel, textureActivesOrtho).pos(25, 10, RIGHT, TOP, 500);
HUD_toggle = viewInstance.makePanel(hudTogglePanel, textureActivesOrtho).pos(20, 60, RIGHT, TOP, 500);
if (viewInstance.sceneOrtho) {
viewInstance.sceneOrtho.add(HUD_speech);
viewInstance.sceneOrtho.add(HUD_tshirt);
viewInstance.sceneOrtho.add(HUD_camera);
viewInstance.sceneOrtho.add(HUD_arm);
viewInstance.sceneOrtho.add(HUD_full);
viewInstance.sceneOrtho.add(HUD_toggle);
}
hudCheckBox.on("change", () => {
const isChecked = hudCheckBox.checked;
if (HUD_speech) HUD_speech.visible = isChecked;
if (HUD_tshirt) HUD_tshirt.visible = isChecked;
if (HUD_camera) HUD_camera.visible = isChecked;
if (HUD_arm) HUD_arm.visible = isChecked;
});
}
// Three.js VR Room Setup
view = new Three({
width: W,
height: H,
cameraPosition: new THREE.Vector3(0, 6, -3),
textureActive: true,
ortho: true
});
renderer = view.renderer;
scene = view.scene;
camera = view.camera;
sceneOrtho = view.sceneOrtho;
cameraOrtho = view.cameraOrtho;
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
// CONSOLIDATED AND CORRECTED INITIAL ORBIT CONTROLS SETUP
controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;
controls.dampingFactor = 0.05;
controls.target.set(0, 5.5, 3);
controls.maxDistance = 10;
controls.minDistance = 2;
controls.maxPolarAngle = Math.PI / 2 - 0.1;
camera.lookAt(0, 5.5, 3);
holder = new THREE.Group();
scene.add(holder);
setupHUD(view);
const loaderTex = new THREE.TextureLoader();
// Room textures, geometry, and lighting setup (omitted for brevity)
const texRight = loaderTex.load("https://i.imgur.com/VH8HCcB.png");
const texLeft = loaderTex.load("https://i.imgur.com/7QcSyKB.png");
const texFront = loaderTex.load("https://i.imgur.com/aipIj5s.png");
const texBack = loaderTex.load("https://i.imgur.com/Rhh8ad0.png");
const texTop = loaderTex.load("https://zimjs.com/studio/assets/stucco.jpg");
[texRight, texLeft, texFront, texBack, texTop].forEach(tex => {
tex.wrapS = tex.wrapT = THREE.RepeatWrapping;
tex.repeat.set(1, 1);
});
const roomMat = [
new THREE.MeshBasicMaterial({ map: texRight, side: THREE.BackSide }),
new THREE.MeshBasicMaterial({ map: texLeft, side: THREE.BackSide }),
new THREE.MeshLambertMaterial({ map: texTop, side: THREE.BackSide }),
new THREE.MeshBasicMaterial({ color: 0x111111, side: THREE.FrontSide }),
new THREE.MeshBasicMaterial({ map: texFront, side: THREE.BackSide }),
new THREE.MeshBasicMaterial({ map: texBack, side: THREE.BackSide })
];
const roomGeom = new THREE.BoxGeometry(25, 12, 25);
const room = new THREE.Mesh(roomGeom, roomMat);
room.receiveShadow = true;
holder.add(room);
room.position.y = 6;
// Floor and decoration setup (omitted for brevity)
const tileTexture = loaderTex.load("https://i.imgur.com/bqnXDQn.png");
tileTexture.flipY = false;
const tileMat = new THREE.MeshStandardMaterial({
map: tileTexture,
side: THREE.FrontSide,
roughness: 0.9,
metalness: 0.0
});
const tileSize = 1;
const tilesX = 26;
const tilesZ = 26;
const floorY = 0.01;
const tileGeom = new THREE.PlaneGeometry(tileSize, tileSize);
const tileCount = tilesX * tilesZ;
const floorTiles = new THREE.InstancedMesh(tileGeom, tileMat, tileCount);
// ... (instanced mesh matrix setup)
const startX = -(tilesX * tileSize) / 2 + tileSize / 2;
const startZ = -(tilesZ * tileSize) / 2 + tileSize / 2;
const dummy = new THREE.Object3D();
let i = 0;
for (let x = 0; x < tilesX; x++) {
for (let z = 0; z < tilesZ; z++) {
dummy.position.set(startX + x * tileSize, floorY, startZ + z * tileSize);
dummy.rotation.set(-Math.PI / 2, 0, 0);
dummy.updateMatrix();
floorTiles.setMatrixAt(i++, dummy.matrix);
}
}
floorTiles.frustumCulled = false;
holder.add(floorTiles);
// Lights
const light1 = new THREE.PointLight(0xffffff, 2 * Math.PI);
light1.position.set(0, 10, 0);
light1.castShadow = true;
holder.add(light1);
const light2 = new THREE.AmbientLight(0xffffff, 0.8 * Math.PI);
holder.add(light2);
const light3 = new THREE.DirectionalLight(0xffffff, .6 * Math.PI);
light3.position.set(0, 0, -2);
light3.castShadow = true;
holder.add(light3);
// Load Avatar Model (GLB)
const avatarLoader = new GLTFLoader();
avatarLoader.load(
AVATAR_MODEL_URL,
glb => {
model = glb.scene;
model.position.set(0, 0, 3);
model.scale.set(4, 4, 4);
model.rotation.y = Math.PI;
holder.add(model);
// --- Animation Mixer Setup ---
mixer = new THREE.AnimationMixer(model);
// -----------------------------
model.traverse(child => {
if (child.isMesh) {
const materialName = child.material?.name?.replace(/\s*\(Instance\)$/, '') || '';
if (materialName === 'Wolf3D_Skin') headMesh = child;
if (materialName === 'Wolf3D_Teeth') teethMesh = child;
if (materialName === 'Wolf3D_Outfit_top' || materialName === 'Wolf3D_Outfit_Top') {
bodyMesh = child;
}
}
if (child.isBone && child.name.toLowerCase().includes("head") && !headBone) {
headBone = child;
}
});
if (headBone) {
initialHeadRotation = headBone.rotation.clone();
}
if (headMesh && headMesh.morphTargetDictionary && !headMesh.morphTargetInfluences) {
headMesh.morphTargetInfluences = new Array(Object.keys(headMesh.morphTargetDictionary).length).fill(0);
}
if (teethMesh && teethMesh.morphTargetDictionary && !teethMesh.morphTargetInfluences) {
teethMesh.morphTargetInfluences = new Array(Object.keys(teethMesh.morphTargetDictionary).length).fill(0);
}
if (bodyMesh) {
const firstTextureName = Object.keys(TSHIRT_TEXTURES)[0];
if (firstTextureName) {
changeTshirtTexture(firstTextureName);
cameraTabs.selectedIndex = 0;
}
}
waiter.hide();
new Pane("Avatar loaded! Click START WAVING.", green).show();
startBlinking();
// --- Load Waving FBX Animation ---
const fbxLoader = new FBXLoader();
fbxLoader.load(WAVE_ANIM_URL, fbx => {
// Assuming the FBX contains the animation clip
const clip = fbx.animations[0];
if (clip) {
waveAction = mixer.clipAction(clip);
waveAction.loop = THREE.LoopRepeat;
waveAction.timeScale = 1.0;
// Start the animation faded out so it doesn't interfere with the idle pose
waveAction.setEffectiveWeight(0).play();
new Pane("Waving animation clip loaded.", yellow).show();
} else {
new Pane("FBX animation clip not found.", red).show();
}
}, undefined, err => {
console.error("Error loading FBX animation:", err);
new Pane("Error loading FBX animation.", red).show();
});
// ---------------------------------
},
undefined,
err => {
console.error("Error loading avatar:", err);
waiter.hide();
new Pane("Error loading avatar model.", red).show();
}
);
function speakText(text) {
if (!window.speechSynthesis) {
new Pane("Speech not supported", red).show();
return;
}
const utter = new SpeechSynthesisUtterance(text);
utter.onboundary = e => {
const idx = Math.max(0, Math.min(text.length - 1, e.charIndex || 0));
const char = text.charAt(idx).toUpperCase();
const viseme = CORRESPONDING_VISEME[char];
if (viseme) {
currentViseme = viseme;
setTimeout(() => {
if (currentViseme === viseme) currentViseme = null;
}, 150);
}
};
utter.onend = () => { currentViseme = null; };
window.speechSynthesis.speak(utter);
}
function startBlinking() {
function scheduleBlink() {
const waitTime = (2 + Math.random() * 2) * 1000;
setTimeout(() => {
blinkInfluence = 1.0;
const blinkDuration = (0.1 + Math.random() * 0.1) * 1000;
setTimeout(() => {
blinkInfluence = 0;
scheduleBlink();
}, blinkDuration);
}, waitTime);
}
scheduleBlink();
}
const roomBounds = {
minX: -11, maxX: 11,
minY: 1.5, maxY: 10,
minZ: -11, maxZ: 11
};
const avatarCollision = {
x: 0,
z: 3,
radius: 1.5,
minY: 0,
maxY: 8
};
function checkAvatarCollision(pos) {
const dx = pos.x - avatarCollision.x;
const dz = pos.z - avatarCollision.z;
const distance = Math.sqrt(dx * dx + dz * dz);
if (distance < avatarCollision.radius &&
pos.y >= avatarCollision.minY &&
pos.y <= avatarCollision.maxY) {
const angle = Math.atan2(dz, dx);
pos.x = avatarCollision.x + Math.cos(angle) * avatarCollision.radius;
pos.z = avatarCollision.z + Math.sin(angle) * avatarCollision.radius;
}
}
const clock = new THREE.Clock();
let pause = false;
window.addEventListener('mousemove', (e) => {
if (!headFollowEnabled || !headBone) return;
const rect = renderer.domElement.getBoundingClientRect();
mouse.x = ((e.clientX - rect.left) / rect.width) * 2 - 1;
mouse.y = -((e.clientY - rect.top) / rect.height) * 2 + 1;
let yaw = mouse.x * (Math.PI / 4);
yaw = THREE.MathUtils.clamp(yaw, -Math.PI / 4, Math.PI / 4);
let pitch = -mouse.y * (Math.PI / 6);
pitch = THREE.MathUtils.clamp(pitch, -Math.PI / 6, Math.PI / 6);
let targetRotation = new THREE.Euler(pitch, yaw, 0, 'XYZ');
if (initialHeadRotation) {
targetRotation.x += initialHeadRotation.x;
targetRotation.y += initialHeadRotation.y;
targetRotation.z += initialHeadRotation.z;
}
const currentRotation = headBone.rotation.clone();
const lerpFactor = 0.1;
headBone.rotation.x = THREE.MathUtils.lerp(currentRotation.x, targetRotation.x, lerpFactor);
headBone.rotation.y = THREE.MathUtils.lerp(currentRotation.y, targetRotation.y, lerpFactor);
headBone.rotation.z = THREE.MathUtils.lerp(currentRotation.z, targetRotation.z, lerpFactor);
});
// Animation Loop
function animate() {
requestAnimationFrame(animate);
const delta = clock.getDelta();
if (walkMode && !pause && !isTyping) {
controls.update(delta);
checkAvatarCollision(camera.position);
} else if (!walkMode) {
controls.update();
}
// --- Animation Mixer Update ---
if (mixer) mixer.update(delta);
// ------------------------------
// Update avatar mouth and eye morphs
if (headMesh && headMesh.morphTargetDictionary && headMesh.morphTargetInfluences) {
Object.keys(headMesh.morphTargetDictionary).forEach(key => {
const index = headMesh.morphTargetDictionary[key];
if (index === undefined) return;
let typingValue = (key === MOUTH_VISEME) ? typingInfluence : 0;
let ttsValue = (key === currentViseme) ? 1.5 : 0;
let blinkValue = 0;
if (key === 'eyeBlinkLeft' || key === 'eyeBlinkRight' ||
key === 'eyesClosed' || key === 'eyesBlink') {
blinkValue = blinkInfluence;
}
const targetInfluence = Math.max(typingValue, ttsValue, blinkValue);
const current = headMesh.morphTargetInfluences[index] || 0;
headMesh.morphTargetInfluences[index] = THREE.MathUtils.lerp(current, targetInfluence, 0.15);
if (teethMesh && teethMesh.morphTargetDictionary) {
const teethIndex = teethMesh.morphTargetDictionary[key];
if (teethIndex !== undefined && teethMesh.morphTargetInfluences) {
teethMesh.morphTargetInfluences[teethIndex] = THREE.MathUtils.lerp(
teethMesh.morphTargetInfluences[teethIndex],
targetInfluence,
0.15
);
}
}
});
}
// Render
renderer.autoClear = false;
renderer.clear();
renderer.render(scene, camera);
if (sceneOrtho && cameraOrtho) {
renderer.clearDepth();
renderer.render(sceneOrtho, cameraOrtho);
}
}
animate();
F.on("keydown", e => {
if (isTyping) return;
if (e.key === " ") {
pause = !pause;
e.preventDefault();
}
});
window.addEventListener('resize', () => {
if (view && view.resize) view.resize();
});
}
any idea to fix?
looked also to this post
so what is the solution @mathtechie ?



