Hello guys,
As I am relatively new to Threejs my question might be a bit stupid, if so, please do tell (and point towards obvious answer ).
What I am trying to achieve: I have a 3D buildingplan and i want to make the parts āhoverableā by mouse. They should turn red and back to their original color afterwards.
(Ultimately I want to connect the parts with hoverable divs in my website, but for now I would be happy to take a first step)
For now the colorchange works with the geometry I put in the scene (sphere, cube) but with the mesh itself, it does not change the color back.
Where am I going wrong?
import * as THREE from 'three';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
const c41aUnten = new URL('https://uploads-ssl.webflow.com/64ef0d16e7e0c9e4853f735d/663a84e1909b5bc1fabc7627_240122_Lageplan_Sigma-Technopark-Dresden_41a_unten.glb.txt');
const c41EG = new URL('https://uploads-ssl.webflow.com/64ef0d16e7e0c9e4853f735d/663a84e0761f0fddb2aa5fb0_240122_Lageplan_Sigma-Technopark-Dresden_41_EG.glb.txt');
const c39AUG = new URL('https://uploads-ssl.webflow.com/64ef0d16e7e0c9e4853f735d/663a84df345aae95b00e18c6_240122_Lageplan_Sigma-Technopark-Dresden_39A_UG.glb.txt');
const c412OG = new URL('https://uploads-ssl.webflow.com/64ef0d16e7e0c9e4853f735d/663a84dfb78a02c5fd907f37_240122_Lageplan_Sigma-Technopark-Dresden_41_2OG.glb.txt');
const c391OG = new URL('https://uploads-ssl.webflow.com/64ef0d16e7e0c9e4853f735d/663a84df23e35a1b0fdadec4_240122_Lageplan_Sigma-Technopark-Dresden_39_1OG.glb.txt');
const c395OGTurm = new URL('https://uploads-ssl.webflow.com/64ef0d16e7e0c9e4853f735d/663a84df6bc0077c66c701a8_240122_Lageplan_Sigma-Technopark-Dresden_39_5OG_Turm.glb.txt');
const c411OG = new URL('https://uploads-ssl.webflow.com/64ef0d16e7e0c9e4853f735d/663a84df712a3e2b7fd96052_240122_Lageplan_Sigma-Technopark-Dresden_41_1OG.glb.txt');
const c394OGTurm = new URL('https://uploads-ssl.webflow.com/64ef0d16e7e0c9e4853f735d/663a84df893a588c77b18b28_240122_Lageplan_Sigma-Technopark-Dresden_39_4OG_Turm.glb.txt');
const c39A2OG = new URL('https://uploads-ssl.webflow.com/64ef0d16e7e0c9e4853f735d/663a84dfa9defb609f4def6b_240122_Lageplan_Sigma-Technopark-Dresden_39A_2OG.glb.txt');
const c39EG = new URL('https://uploads-ssl.webflow.com/64ef0d16e7e0c9e4853f735d/663a84dfbd2ec3d434924d4b_240122_Lageplan_Sigma-Technopark-Dresden_39_EG.glb.txt');
const c39AEG = new URL('https://uploads-ssl.webflow.com/64ef0d16e7e0c9e4853f735d/663a84df5903f5c7ca20ee54_240122_Lageplan_Sigma-Technopark-Dresden_39A_EG.glb.txt');
const c41aVorne = new URL('https://uploads-ssl.webflow.com/64ef0d16e7e0c9e4853f735d/663a84dfcbc9e98287998256_240122_Lageplan_Sigma-Technopark-Dresden_41a_vorne.glb.txt');
const cParkplatz = new URL('https://uploads-ssl.webflow.com/64ef0d16e7e0c9e4853f735d/663a84dfdea68e744a9ac490_240122_Lageplan_Sigma-Technopark-Dresden_Parkplatz.glb.txt');
const c41aOben = new URL('https://uploads-ssl.webflow.com/64ef0d16e7e0c9e4853f735d/663a84df7541ac5601bf288e_240122_Lageplan_Sigma-Technopark-Dresden_41a_oben.glb.txt');
const c392OG = new URL('https://uploads-ssl.webflow.com/64ef0d16e7e0c9e4853f735d/663a84dff689d32d332eafaa_240122_Lageplan_Sigma-Technopark-Dresden_39_2OG.glb.txt');
const cDaecher = new URL('https://uploads-ssl.webflow.com/64ef0d16e7e0c9e4853f735d/663a84df5f3e7d109765f81a_240122_Lageplan_Sigma-Technopark-Dresden_Daecher.glb.txt');
const c39A1OG = new URL('https://uploads-ssl.webflow.com/64ef0d16e7e0c9e4853f735d/663a84dfa9defb609f4def56_240122_Lageplan_Sigma-Technopark-Dresden_39A_1OG.glb.txt');
const c39UG = new URL('https://uploads-ssl.webflow.com/64ef0d16e7e0c9e4853f735d/663a84df01080f1bc3860fe9_240122_Lageplan_Sigma-Technopark-Dresden_39_UG.glb.txt');
const c44SE = new URL('https://uploads-ssl.webflow.com/64ef0d16e7e0c9e4853f735d/663a84df01080f1bc3860fe4_240122_Lageplan_Sigma-Technopark-Dresden_44_SE.glb.txt');
const c393OGTurm = new URL('https://uploads-ssl.webflow.com/64ef0d16e7e0c9e4853f735d/663a84df7c712df11f778eaa_240122_Lageplan_Sigma-Technopark-Dresden_39_3OG_Turm.glb.txt');
const cStrassen = new URL('https://uploads-ssl.webflow.com/64ef0d16e7e0c9e4853f735d/662f899e52ec4a035f10cf88_240122_Lageplan_Sigma-Technopark-Dresden_Strassen.glb.txt');
const cBoden = new URL('https://uploads-ssl.webflow.com/64ef0d16e7e0c9e4853f735d/662f89984072b4f57c8b9833_240122_Lageplan_Sigma-Technopark-Dresden_Boden.glb.txt');
window.Webflow ||= [];
window.Webflow.push(() => {
init3D();
});
function init3D() {
// GET TRUE CANVAS SIZE THROUGH DIV ========
// Select the div class "scene-3d" and store values
var sigmaApp = document.getElementById('scene-3d');
// select container
const viewport = document.querySelector('[data-3d="c"]');
console.log(viewport);
// Initialize Three.js scene
var scene = new THREE.Scene();
var renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setClearColor(0x000000, 0); //Scene Background
renderer.setSize(sigmaApp.offsetWidth, sigmaApp.offsetHeight); //Container Size renderer
renderer.shadowMap.enabled = true; //Enable Shadows
viewport.appendChild(renderer.domElement);
// ======== SCENE LIGHTING ========
// Ambient Light
scene.add(new THREE.AmbientLight(0xFFFFFF, 2));
// Directional Light
const directionalLight = new THREE.DirectionalLight(0xFFFFFF);
directionalLight.intensity = 2;
directionalLight.position.set(70, 70, 45);
directionalLight.castShadow = true;
directionalLight.shadow.camera.top = 30;
directionalLight.shadow.camera.right = 45;
directionalLight.shadow.camera.left = -45;
directionalLight.shadow.camera.bottom = -35;
scene.add(directionalLight);
// ======== CAMERA AND CONTROLS ========
// Camera and Mousecontrols
const camera = new THREE.PerspectiveCamera(35, sigmaApp.offsetWidth / sigmaApp.offsetHeight, 0.1, 1000);
const controls = new OrbitControls(camera, renderer.domElement); //set Orbitcontrols
camera.position.set(-47, 25, 37); //Set Cameraposition set(x,y,z)
controls.target.set(0, -3, 0); //First Look Target on load
controls.minAzimuthAngle = Math.PI / 1;
controls.maxAzimuthAngle = Math.PI / 6;
//controls.minPolarAngle = Math.PI / 3.2;
//controls.maxPolarAngle = Math.PI / 2.4;
controls.enableDamping = true;
controls.dampingFactor = 0.07;
controls.update();
// Create a cube
var geometry = new THREE.BoxGeometry();
var material = new THREE.MeshBasicMaterial({ color: 0xC0D12C }); // Green color
var cube = new THREE.Mesh(geometry, material);
cube.name = 'cube'; // Set name for identification
cube.position.set(1, 12, 3);
scene.add(cube);
// Create SPhere mesh for intersection testing
var sphereGeometry = new THREE.SphereGeometry(1, 32, 32);
var sphereMaterial = new THREE.MeshBasicMaterial({ color: 0xAF75E9 }); // Red color
var sphere = new THREE.Mesh(sphereGeometry, sphereMaterial);
sphere.position.set(2, 0, 0);
sphere.name = 'sphere'; // Set name for identification
sphere.position.set(0, 12, 0);
scene.add(sphere);
// ======================================
const assetLoader = new GLTFLoader();
assetLoader.load(c41aUnten.href, function (gltf) { const m41aUnten = gltf.scene; scene.add(m41aUnten); gltf.scene.traverse(function (node) { if (node.isMesh) { node.castShadow = true; } }) });
assetLoader.load(c41EG.href, function (gltf) { const m41EG = gltf.scene; scene.add(m41EG); gltf.scene.traverse(function (node) { if (node.isMesh) { node.castShadow = true; } }) });
assetLoader.load(c39AUG.href, function (gltf) { const m39AUG = gltf.scene; scene.add(m39AUG); gltf.scene.traverse(function (node) { if (node.isMesh) { node.castShadow = true; } }) });
assetLoader.load(c412OG.href, function (gltf) { const m412OG = gltf.scene; scene.add(m412OG); gltf.scene.traverse(function (node) { if (node.isMesh) { node.castShadow = true; } }) });
assetLoader.load(c391OG.href, function (gltf) { const m391OG = gltf.scene; scene.add(m391OG); gltf.scene.traverse(function (node) { if (node.isMesh) { node.castShadow = true; } }) });
assetLoader.load(c395OGTurm.href, function (gltf) { const m395OGTurm = gltf.scene; scene.add(m395OGTurm); gltf.scene.traverse(function (node) { if (node.isMesh) { node.castShadow = true; } }) });
assetLoader.load(c411OG.href, function (gltf) { const m411OG = gltf.scene; scene.add(m411OG); gltf.scene.traverse(function (node) { if (node.isMesh) { node.castShadow = true; } }) });
assetLoader.load(c394OGTurm.href, function (gltf) { const m394OGTurm = gltf.scene; scene.add(m394OGTurm); gltf.scene.traverse(function (node) { if (node.isMesh) { node.castShadow = true; } }) });
assetLoader.load(c39A2OG.href, function (gltf) { const m39A2OG = gltf.scene; scene.add(m39A2OG); gltf.scene.traverse(function (node) { if (node.isMesh) { node.castShadow = true; } }) });
assetLoader.load(c39EG.href, function (gltf) { const m39EG = gltf.scene; scene.add(m39EG); gltf.scene.traverse(function (node) { if (node.isMesh) { node.castShadow = true; } }) });
assetLoader.load(c39AEG.href, function (gltf) { const m39AEG = gltf.scene; scene.add(m39AEG); gltf.scene.traverse(function (node) { if (node.isMesh) { node.castShadow = true; } }) });
assetLoader.load(c41aVorne.href, function (gltf) { const m41aVorne = gltf.scene; scene.add(m41aVorne); gltf.scene.traverse(function (node) { if (node.isMesh) { node.castShadow = true; } }) });
//assetLoader.load(cParkplatz.href, function (gltf) { const mParkplatz = gltf.scene; scene.add(mParkplatz); gltf.scene.traverse(function (node) { if (node.isMesh) { node.receiveShadow = true; } }) });
assetLoader.load(c41aOben.href, function (gltf) { const m41aOben = gltf.scene; scene.add(m41aOben); gltf.scene.traverse(function (node) { if (node.isMesh) { node.castShadow = true; } }) });
assetLoader.load(c392OG.href, function (gltf) { const m392OG = gltf.scene; scene.add(m392OG); gltf.scene.traverse(function (node) { if (node.isMesh) { node.castShadow = true; } }) });
assetLoader.load(cDaecher.href, function (gltf) { const mDaecher = gltf.scene; scene.add(mDaecher); gltf.scene.traverse(function (node) { if (node.isMesh) { node.castShadow = true; } }) });
assetLoader.load(c39A1OG.href, function (gltf) { const m39A1OG = gltf.scene; scene.add(m39A1OG); gltf.scene.traverse(function (node) { if (node.isMesh) { node.castShadow = true; } }) });
assetLoader.load(c39UG.href, function (gltf) { const m39UG = gltf.scene; scene.add(m39UG); gltf.scene.traverse(function (node) { if (node.isMesh) { node.castShadow = true; } }) });
assetLoader.load(c44SE.href, function (gltf) { const m44SE = gltf.scene; scene.add(m44SE); gltf.scene.traverse(function (node) { if (node.isMesh) { node.castShadow = true; } }) });
assetLoader.load(c393OGTurm.href, function (gltf) { const m393OGTurm = gltf.scene; scene.add(m393OGTurm); gltf.scene.traverse(function (node) { if (node.isMesh) { node.castShadow = true; } }) });
assetLoader.load(cStrassen.href, function (gltf) { const mStrassen = gltf.scene; scene.add(mStrassen); gltf.scene.traverse(function (node) { if (node.isMesh) { node.receiveShadow = true; } }) });
assetLoader.load(cBoden.href, function (gltf) { const mBoden = gltf.scene; scene.add(mBoden); gltf.scene.traverse(function (node) { if (node.isMesh) { node.receiveShadow = true; } }) });
// ======================================
// Set up raycaster
var raycaster = new THREE.Raycaster();
var mouse = new THREE.Vector2();
// Store original material color for each mesh
var originalColors = {};
// Function to change mesh color to red
function changeColor(mesh) {
mesh.material.color.set(0xff0000); // Red color
}
// Function to revert mesh color to original
function revertColor(mesh) {
if (originalColors.hasOwnProperty(mesh.name)) {
mesh.material.color.set(originalColors[mesh.name]);
delete originalColors[mesh.name]; // Remove stored color after reverting
console.log(originalColors.data)
}
}
// Event listener for mouse move
function onMouseMove(event) {
// Calculate mouse position in normalized device coordinates
const viewportRect = renderer.domElement.getBoundingClientRect();
const x = event.clientX - viewportRect.left;
const y = event.clientY - viewportRect.top;
mouse.x = (x / sigmaApp.offsetWidth) * 2 - 1;
mouse.y = (y / sigmaApp.offsetHeight) * -2 + 1;
// Update the picking ray with the camera and mouse position
raycaster.setFromCamera(mouse, camera);
// Calculate objects intersecting the picking ray
var intersects = raycaster.intersectObjects(scene.children);
if (intersects.length > 0) {
onIntersection(intersects[0].object);
} else {
// Revert color if not intersecting with any object
Object.values(scene.children).forEach(mesh => {
revertColor(mesh);
});
}
}
// Event listener for raycaster intersection
function onIntersection(mesh) {
if (mesh !== undefined) {
// Check if original color is not stored
if (!originalColors.hasOwnProperty(mesh.name)) {
// Store original color
originalColors[mesh.name] = mesh.material.color.clone();
// Change color to red
changeColor(mesh);
}
} else {
// Revert color if intersection with any mesh stops
Object.values(scene.children).forEach(mesh => {
revertColor(mesh);
});
}
}
// Add event listener
window.addEventListener('mousemove', onMouseMove, false);
// Camera position
camera.position.z = 5;
// Render loop
function animate() {
requestAnimationFrame(animate);
renderer.render(scene, camera);
}
animate();
//window resize function
window.addEventListener('resize', onWindowResize);
function onWindowResize() {
camera.aspect = sigmaApp.offsetWidth / sigmaApp.offsetHeight;
camera.updateProjectionMatrix();
renderer.setSize(sigmaApp.offsetWidth, sigmaApp.offsetHeight);
animate();
}
}
The website is setup on webflow. I load the scene in a div on the Page.
I really appreciate any help you guys can provide me!