Reputation: 195
I'm trying to access elements in this array, but for some reason it's saying "undefined" and giving back a length of 0, but there's clearly a length of 5. The picture above is the result of this code.
console.log(scene.children[1].children);
The result of this code below is 0
console.log(scene.children[1].children.length);
The result of this code below is undefined
console.log(scene.children[1].children[0]);
Here's the previous paths if it helps with the context:
console.log(scene);
console.log(scene.children[1]);
Per comments: here is full code:
import "./styles.css";
import * as THREE from "three";
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
import gsap from "gsap";
import blueHouseBaked from "./static/Bluehouse.png";
import blueHouse from "./static/Bluehouse.glb";
import greenHouseBaked from "./static/Greenhouse.png";
import greenHouse from "./static/Greenhouse.glb";
import redHouseBaked from "./static/Redhouse.png";
import redHouse from "./static/Redhouse.glb";
import purpleHouseBaked from "./static/Purplehouse.png";
import purpleHouse from "./static/Purplehouse.glb";
import groundBaked from "./static/Ground.png";
import ground from "./static/Ground.glb";
import "regenerator-runtime/runtime";
const canvas = document.querySelector("canvas.experience");
// Scene
const scene = new THREE.Scene();
/**
* Sizes
*/
const sizes = {
width: window.innerWidth,
height: window.innerHeight,
};
window.addEventListener("resize", () => {
// Update sizes
sizes.width = window.innerWidth;
sizes.height = window.innerHeight;
// Update camera
camera.aspect = sizes.width / sizes.height;
camera.updateProjectionMatrix();
// Update renderer
renderer.setSize(sizes.width, sizes.height);
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
});
/**
* Camera
*/
// Base camera
const camera = new THREE.PerspectiveCamera(
75,
sizes.width / sizes.height,
0.1,
100
);
camera.position.x = 0;
camera.position.y = 17;
camera.position.z = 22;
camera.rotation.x = -Math.PI / 8;
scene.add(camera);
//Controls -------------------
//Three.js vector lerping
let cameraTargetPosition = new THREE.Vector3(0, 17, 22);
window.addEventListener("mousedown", () => {
window.addEventListener("mousemove", updateCamera);
});
window.addEventListener("mouseup", () => {
window.removeEventListener("mousemove", updateCamera);
});
// Controls
function updateCamera(ev) {
ev.preventDefault();
if (ev.movementX > 0) {
cameraTargetPosition.x -= ev.movementX * 0.015;
} else {
cameraTargetPosition.x -= ev.movementX * 0.015;
}
if (ev.movementY > 0) {
cameraTargetPosition.z -= ev.movementY * 0.015;
} else {
cameraTargetPosition.z -= ev.movementY * 0.015;
}
}
/**
* Everything load
*/
let group = new THREE.Group();
async function loadModels(model, texture) {
const gltfLoader = new GLTFLoader();
const textureLoader = new THREE.TextureLoader();
const mesh = await gltfLoader.loadAsync(model);
const bakedTexture = textureLoader.load(texture);
const bakedMaterial = new THREE.MeshBasicMaterial({
map: bakedTexture,
});
bakedTexture.flipY = false;
bakedTexture.encoding = THREE.sRGBEncoding;
mesh.scene.children[0].material = bakedMaterial;
group.add(mesh.scene.children[0]);
}
loadModels(blueHouse, blueHouseBaked);
loadModels(greenHouse, greenHouseBaked);
loadModels(redHouse, redHouseBaked);
loadModels(purpleHouse, purpleHouseBaked);
loadModels(ground, groundBaked);
scene.add(group);
/**
* Renderer
*/
const renderer = new THREE.WebGLRenderer({
canvas: canvas,
antialias: true,
});
renderer.setSize(sizes.width, sizes.height);
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
/**
* Animate
*/
const clock = new THREE.Clock();
let lastElapsedTime = 0;
// Ray Casting stuff
let raycaster = new THREE.Raycaster();
let mouse = new THREE.Vector2();
let currentIntersect = null;
window.addEventListener("mousemove", (event) => {
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
});
console.log(scene.children[1].children[0]);
const tick = () => {
camera.position.lerp(cameraTargetPosition, 0.1);
const elapsedTime = clock.getElapsedTime();
const deltaTime = elapsedTime - lastElapsedTime;
lastElapsedTime = elapsedTime;
// Raycaster stuff
raycaster.setFromCamera(mouse, camera);
const intersects = raycaster.intersectObjects([scene.children[1].children]);
if (intersects.length) {
if (!currentIntersect) {
console.log("mouse enter");
}
currentIntersect = intersects[0];
} else {
if (currentIntersect) {
console.log("mouse leave");
}
currentIntersect = null;
}
// Render
renderer.render(scene, camera);
// Call tick again on the next frame
window.requestAnimationFrame(tick);
};
tick();
Upvotes: 1
Views: 1227
Reputation: 1169
The problem you are facing is due to asynchronous code. The loadModels
function loads model asynchronously which means that the rest of the code finishes first before that asynchronous method is finished for all of its calls.
Here is a great video of the event loop and how it works to better understand why this happens - https://www.youtube.com/watch?v=8aGhZQkoFbQ.
Loading GLTF models can also take a bit of time depending on their size so it may be a few ms before they are available in the group.
You can try logging on every tick, so changing you tick method to -
const tick = () => {
camera.position.lerp(cameraTargetPosition, 0.1);
const elapsedTime = clock.getElapsedTime();
const deltaTime = elapsedTime - lastElapsedTime;
lastElapsedTime = elapsedTime;
// Raycaster stuff
raycaster.setFromCamera(mouse, camera);
const intersects = raycaster.intersectObjects([scene.children[1].children]);
if (intersects.length) {
if (!currentIntersect) {
console.log("mouse enter");
}
currentIntersect = intersects[0];
} else {
if (currentIntersect) {
console.log("mouse leave");
}
currentIntersect = null;
}
// Render
renderer.render(scene, camera);
console.log(scene.children[0].children[1])
// Call tick again on the next frame
window.requestAnimationFrame(tick);
};
However, when we worked with threejs, it was much easier to have the scene assigned to the global window for easy access in chrome.
Simply add the following lines in your code after creating the scene
window.scene = scene
import "./styles.css";
import * as THREE from "three";
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
import gsap from "gsap";
import blueHouseBaked from "./static/Bluehouse.png";
import blueHouse from "./static/Bluehouse.glb";
import greenHouseBaked from "./static/Greenhouse.png";
import greenHouse from "./static/Greenhouse.glb";
import redHouseBaked from "./static/Redhouse.png";
import redHouse from "./static/Redhouse.glb";
import purpleHouseBaked from "./static/Purplehouse.png";
import purpleHouse from "./static/Purplehouse.glb";
import groundBaked from "./static/Ground.png";
import ground from "./static/Ground.glb";
import "regenerator-runtime/runtime";
const canvas = document.querySelector("canvas.experience");
// Scene
const scene = new THREE.Scene();
window.scene = scene
.......
Then you can access the group from the chrome console once the GLTF models are loaded since scene is now accessible from the devtools. You can just write scene there and you would be able to access it, or you can write scene.children[0].children[1]
and you would see your group.
Edit: Meant this just for debugging.
Upvotes: 2
Reputation: 195
So I found a solution, but I still don't know why the fundamental problem still exists. If I put the console.log(scene.children[1].children[0]);
INSIDE of the tick It outputs this:
undefined
mesh
mesh
mesh
So the first loop through is undefined. Therefore the solution is just to check if it's undefined and now I get no errors and everything works:
// Raycaster stuff
raycaster.setFromCamera(mouse, camera);
if (
typeof scene.children[1].children[0] !== "undefined" &&
typeof scene.children[1].children[1] !== "undefined" &&
typeof scene.children[1].children[2] !== "undefined" &&
typeof scene.children[1].children[3] !== "undefined"
) {
const intersects = raycaster.intersectObjects([
scene.children[1].children[0],
scene.children[1].children[1],
scene.children[1].children[2],
scene.children[1].children[3],
]);
if (intersects.length) {
if (!currentIntersect) {
console.log("mouse enter");
}
currentIntersect = intersects[0];
} else {
if (currentIntersect) {
console.log("mouse leave");
}
currentIntersect = null;
}
}
But again, I do NOT understand why it's undefined in the first few loops.
Upvotes: 0