Reputation: 791
I am currently working on a .stl viewer using Three.js. The goal is select and calculate certain areas. For this area calculation I need to be able to select (e.g. change the color) faces.
I have found something similar, but from what I have seen in my own research this only seems to be working with ready made meshes (like the cube in the example).
I am looking to implement this example in my own code.
It is evident that something like this has been done before, but I just can't seem to implement any kind of working way in my own code:
My current code has a fully functional .stl loader and viewer. The raycaster is there, but doesn't seem to be working properly so I have commented it out for the moment. Mugen87, thank you for the fix!
You can download my code and an example .stl file from github. Which only requires a Live Server environment which can easily be ran using VSCode (see readme).
My current code:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>3d viewer tjalle</title>
<link rel="stylesheet" type="text/css" href="../style/render.css">
</head>
<body>
<script src="https://rawcdn.githack.com/mrdoob/three.js/r117/build/three.min.js"></script>
<script src="https://rawcdn.githack.com/mrdoob/three.js/r117/examples/js/loaders/STLLoader.js"></script>
<script src="https://rawcdn.githack.com/mrdoob/three.js/r117/examples/js/controls/OrbitControls.js"></script>
<script>
function init() {
var raycaster = new THREE.Raycaster();
var mouse = new THREE.Vector2();
document.addEventListener( 'mousemove', onMouseMove, false );
function onMouseMove(event) {
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
}
// Setup some basic stuff
scene = new THREE.Scene();
scene.background = new THREE.Color(0xdddddd);
// Setup Camera
camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 1, 5000);
// Setup renerer and add to page
renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
window.addEventListener('resize', onWindowResize, false);
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
// Setup Camera Position
camera.rotation.y = 45 / 180 * Math.PI;
camera.position.x = 800;
camera.position.y = 100;
camera.position.z = 1000;
// Add Camera Control through orbit.js
let controls = new THREE.OrbitControls(camera, renderer.domElement);
// Add some basic ambient lighting (Does light all parts equally and does not cast shadows)
hlight = new THREE.AmbientLight(0xffffff, 5.3);
scene.add(hlight);
//Add some point lights to simulate real lights
light = new THREE.PointLight(0xffffff, 1, 10000);
light.position.set(0, 300, 500);
scene.add(light);
controls.update();
// Animation Script
function animate() {
raycaster.setFromCamera(mouse, camera);
scene.children[2].material.color.set(0x1313)
// calculate objects intersecting the picking ray
var intersects = raycaster.intersectObjects(scene.children);
for (var i = 0; i < intersects.length; i++) {
intersects[i].object.material.color.set(0xff0000);
}
renderer.render(scene, camera);
requestAnimationFrame(animate);
}
// Setup GLTF Loader and load in car
let loader = new THREE.STLLoader();
loader.load('../converter/output/output.stl', function (geometry) {
// console.log(gltf);
var material = new THREE.MeshLambertMaterial({
color: 0x1313,
wireframe: false
});
var mesh = new THREE.Mesh(geometry, material);
mesh.castShadow = true;
mesh.receiveShadow = true;
mesh.position.set(0, 0, 0);
scene.add(mesh);
renderer.render(scene, camera)
animate();
console.log("SCene: ", )
});
}
// Call method for starting init
init();
</script>
</body>
</html>
Upvotes: 2
Views: 762
Reputation: 791
Only selecting faces with use of a raycaster (which was my question), can be done by reading out the intersects
data from the raycaster. Which gives you the ability to manipulate the face, thus 'select' it.
e.g.
for (var i = 0; i < intersects.length; i++) {
console.log(intersects[i].face)
}
face
has many built-in methods and gives you the ability to manipulate the face and get info from it. To give you an idea, this is directly copy pasted from my console.log(intersects[i].face)
output, and is the related to only one face (note: these are only the top/parental level methods, many of them have more children.).
Ac {a: 5307, b: 5308, c: 5309, normal: p, vertexNormals: Array(0), …}
a: 5307
b: 5308
c: 5309
color: D
__proto__:
add: ƒ (a)
addColors: ƒ (a,b)
addScalar: ƒ (a)
b: 1
clone: ƒ ()
convertGammaToLinear: ƒ (a)
convertLinearToGamma: ƒ (a)
convertLinearToSRGB: ƒ ()
convertSRGBToLinear: ƒ ()
copy: ƒ (a)
copyGammaToLinear: ƒ (a,b)
copyLinearToGamma: ƒ (a,b)
copyLinearToSRGB: ƒ (a)
copySRGBToLinear: ƒ (a)
equals: ƒ (a)
fromArray: ƒ (a,b)
g: 1
getHSL: ƒ (a)
getHex: ƒ ()
getHexString: ƒ ()
getStyle: ƒ ()
isColor: true
lerp: ƒ (a,b)
lerpHSL: ƒ (a,b)
multiply: ƒ (a)
multiplyScalar: ƒ (a)
offsetHSL: ƒ (a,b,c)
r: 1
set: ƒ (a)
setColorName: ƒ (a)
setHSL: ƒ (a,b,c)
setHex: ƒ (a)
setRGB: ƒ (a,b,c)
setScalar: ƒ (a)
setStyle: ƒ (a)
sub: ƒ (a)
toArray: ƒ (a,b)
toJSON: ƒ ()
constructor: ƒ D(a,b,c)
__proto__: Object
materialIndex: 0
normal: p
x: 0.9839296339696827
y: 0
z: 0.17855664478334757
__proto__:
add: ƒ (a,b)
addScalar: ƒ (a)
addScaledVector: ƒ (a,b)
addVectors: ƒ (a,b)
angleTo: ƒ (a)
applyAxisAngle: ƒ (a,b)
applyEuler: ƒ (a)
applyMatrix3: ƒ (a)
applyMatrix4: ƒ (a)
applyNormalMatrix: ƒ (a)
applyProjection: ƒ (a)
applyQuaternion: ƒ (a)
ceil: ƒ ()
clamp: ƒ (a,b)
clampLength: ƒ (a,b)
clampScalar: ƒ (a,b)
clone: ƒ ()
copy: ƒ (a)
cross: ƒ (a,b)
crossVectors: ƒ (a,b)
distanceTo: ƒ (a)
distanceToManhattan: ƒ (a)
distanceToSquared: ƒ (a)
divide: ƒ (a)
divideScalar: ƒ (a)
dot: ƒ (a)
equals: ƒ (a)
floor: ƒ ()
fromArray: ƒ (a,b)
fromAttribute: ƒ (a,b,c)
fromBufferAttribute: ƒ (a,b,c)
getColumnFromMatrix: ƒ (a,b)
getComponent: ƒ (a)
getPositionFromMatrix: ƒ (a)
getScaleFromMatrix: ƒ (a)
isVector3: true
length: ƒ ()
lengthManhattan: ƒ ()
lengthSq: ƒ ()
lerp: ƒ (a,b)
lerpVectors: ƒ (a,b,c)
manhattanDistanceTo: ƒ (a)
manhattanLength: ƒ ()
max: ƒ (a)
min: ƒ (a)
multiply: ƒ (a,b)
multiplyScalar: ƒ (a)
multiplyVectors: ƒ (a,b)
negate: ƒ ()
normalize: ƒ ()
project: ƒ (a)
projectOnPlane: ƒ (a)
projectOnVector: ƒ (a)
random: ƒ ()
reflect: ƒ (a)
round: ƒ ()
roundToZero: ƒ ()
set: ƒ (a,b,c)
setComponent: ƒ (a,b)
setEulerFromQuaternion: ƒ ()
setEulerFromRotationMatrix: ƒ ()
setFromCylindrical: ƒ (a)
setFromCylindricalCoords: ƒ (a,b,c)
setFromMatrix3Column: ƒ (a, b)
setFromMatrixColumn: ƒ (a,b)
setFromMatrixPosition: ƒ (a)
setFromMatrixScale: ƒ (a)
setFromSpherical: ƒ (a)
setFromSphericalCoords: ƒ (a,b,c)
setLength: ƒ (a)
setScalar: ƒ (a)
setX: ƒ (a)
setY: ƒ (a)
setZ: ƒ (a)
sub: ƒ (a,b)
subScalar: ƒ (a)
subVectors: ƒ (a,b)
toArray: ƒ (a,b)
transformDirection: ƒ (a)
unproject: ƒ (a)
constructor: ƒ p(a,b, c)
__proto__: Object
vertexColors: Array(0)
length: 0
__proto__: Array(0)
vertexNormals: Array(0)
length: 0
__proto__: Array(0)
__proto__:
clone: ƒ ()
copy: ƒ (a)
constructor: ƒ Ac(a,b,c,d,e,f)
__proto__: Object
Upvotes: 0
Reputation: 31076
The raycaster is there, but doesn't seem to be working properly so I have commented it out for the moment.
I have debugged your code locally. Raycasting does not work since onMouseMove()
is not yet called. You have to register it as an event listener first. So try to add the following line to your example:
document.addEventListener( 'mousemove', onMouseMove, false );
If you then comment in your raycasting code inside the animation loop, the model should become red when hovering with the mouse.
Upvotes: 1