[SOLVED] Raycaster - MultiMaterial

Thanks to Mugen87 :+1: [solved]

PR https://github.com/mrdoob/three.js/pull/11860 viewed.

Then used
three.js/Mesh.js at 82e866be2ffa752969af6efe96f91b5baab19c33 · Mugen87/three.js · GitHub
and replaced in three.js rev 89:
function Mesh( geometry, material ) { … }
Mesh.prototype = Object.assign( … )

It works! :relaxed: Try here: http://sandboxthreef.threejs.hofk.de/modifyRaycastMultiMaterial.html

Update: Bugfix auxilary plane position

<!DOCTYPE html>
<!--  Recaster MultiMaterial 
/**
 * @author hofk / http://threejs.hofk.de/
*/
-->
<html lang="de" >
<head>
<meta charset="utf-8" />   
<title> Recaster MultiMaterial </title>
</head>
<body>OrbitControls & raycaster - drag faces, edges or vertices with the mouse</body>

<!-- three.89.mugen.js with workaround from mugen87 (raycast for multi material)
	https://github.com/Mugen87/three.js/blob/82e866be2ffa752969af6efe96f91b5baab19c33/src/objects/Mesh.js
	replaced in three.js rev 89:
	function Mesh( geometry, material ) { ... } 
	Mesh.prototype = Object.assign( ... )
-->
<script src="three.89.mugen.js"></script>

<script src="OrbitControls.js"></script>
<script src="THREEx.WindowResize.js"></script>

<script src="THREEh.js"></script> <!--  vertexFaceNumbersHelper( mesh, mode, size, color ) -->

<script src="geo.js"></script> <!-- defines a indexedBufferGeometry geo -->

<script>
'use strict'
var mouse 		= new THREE.Vector2();
var raycaster 	= new THREE.Raycaster();
var intersects 	= [];
var selection;
var object;
var handlePos;
var a, ax, ay, az, aPos;
var b, bx, by, bz, bPos; 
var c, cx, cy, cz, cPos;
var vertexPickedA = false;
var vertexPickedB = false;
var vertexPickedC = false;
var edgePickedAB = false;
var edgePickedBC = false;
var edgePickedCA = false;
var facePicked = false;
var abV = new THREE.Vector3( 0,0,0 );
var bcV = new THREE.Vector3( 0,0,0 );
var caV = new THREE.Vector3( 0,0,0 );
var abVn;
var bcVn;
var caVn;

var paD;
var pbD;
var pcD;
var abD;
var bcD;
var caD;

var abpD;
var bcpD;
var capD;

var paV = new THREE.Vector3( 0,0,0 );
var pbV = new THREE.Vector3( 0,0,0 );
var pcV = new THREE.Vector3( 0,0,0 );
var paVn = new THREE.Vector3( 0,0,0 );	
var pbVn = new THREE.Vector3( 0,0,0 );   
var pcVn = new THREE.Vector3( 0,0,0 );   

var scene = new THREE.Scene();

var camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight , 0.01, 2000);
scene.add(camera);
camera.position.set( 0, 1, 3);
camera.lookAt(new THREE.Vector3( 0, 0, 0 ) ); 
var renderer = new THREE.WebGLRenderer({ antialias:true });
renderer.setSize(window.innerWidth, window.innerHeight);  
renderer.setClearColor(0xdddddd, 1); 
var container = document.createElement('div');
document.body.appendChild(container);
container.appendChild(renderer.domElement);
THREEx.WindowResize(renderer, camera);

scene.add(new THREE.AmbientLight(0xcccccc));
var dirLight = new THREE.DirectionalLight(0xefefef);
dirLight.position.set(2, 6, 3).normalize();
camera.add(dirLight);
camera.add(dirLight.target);

container.addEventListener('mousedown', onContainerMouseDown );
container.addEventListener('mousemove', onContainerMouseMove );
container.addEventListener('mouseup', onContainerMouseUp );
var controls = new THREE.OrbitControls( camera, renderer.domElement );
controls.enableZoom = true;

var axesHelper = new THREE.AxesHelper( 4 );
scene.add( axesHelper );

var gridHelperXY = new THREE.GridHelper( 2, 20, 0x333333, 0x888888 );
scene.add( gridHelperXY );
gridHelperXY.rotation.x = -Math.PI / 2;
gridHelperXY.position.set( 0, 0, -1 );
var gridHelperXZ = new THREE.GridHelper( 2, 20 );
scene.add( gridHelperXZ );
gridHelperXZ.position.set( 0, -1, 0 );
var gridHelperYZ = new THREE.GridHelper( 2, 20 );
scene.add( gridHelperYZ );
gridHelperYZ.rotation.z = -Math.PI / 2;
gridHelperYZ.position.set( -1, 0, 0 );

// Auxiliary layer for determining the mouse position and moving the clicked object in 3D
var auxilaryPlaneGeo = new THREE.PlaneBufferGeometry( 3, 3, 3, 3 );
var auxilaryPlaneMaterial = new THREE.MeshBasicMaterial({color: 0x888888, transparent: true, opacity:0.2, side: THREE.DoubleSide, wireframe: false} );
var auxilaryPlane = new THREE.Mesh( auxilaryPlaneGeo, auxilaryPlaneMaterial );  
auxilaryPlane.visible = true;
scene.add( auxilaryPlane );

arrowAuxilaryAxis( "x" );
arrowAuxilaryAxis( "y" );
arrowAuxilaryAxis( "z" );

// material
var uvTex			= new THREE.TextureLoader().load( "uvgrid01.png" );			
var waterlilyTex	= new THREE.TextureLoader().load( "waterlily.png" );
var specular = 0x333333;
var side = THREE.DoubleSide;
var wireframe = false;
var flatShading = true;

var materials = [
		
		new THREE.MeshBasicMaterial( {
			opacity: 0.15,	transparent: true, 	
			side: side, wireframe: wireframe } ),					//  0 transparent
			
		new THREE.MeshPhongMaterial( {
			color: 0x440033, emissive: 0x330033, specular: specular,
			side: side, wireframe: wireframe, flatShading: flatShading } ),	//  1 color
			
		new THREE.MeshPhongMaterial( {
			color: 0xff0000, emissive: 0xff0000, specular: specular, 
			side: side, wireframe: wireframe, flatShading: flatShading } ),	//  2 red
			
		new THREE.MeshPhongMaterial( {
			color: 0x00ff00, emissive: 0x00ff00, specular: specular, 
			side: side, wireframe: wireframe, flatShading: flatShading } ),	//  3 green
			
		new THREE.MeshPhongMaterial( {
			color: 0x0000ff, emissive: 0x0000ff, specular: specular, 
			side: side, wireframe: wireframe, flatShading: flatShading } ),	//  4 blue
			
		new THREE.MeshPhongMaterial( {
			color: 0xffff00, emissive: 0xffff00, specular: specular, 
			side: side, wireframe: wireframe, flatShading: flatShading } ),	//  5 yellow
			
		new THREE.MeshPhongMaterial( {
			color: 0xff00ff, emissive: 0xff00ff, specular: specular, 
			side: side, wireframe: wireframe, flatShading: flatShading } ),	//  6 mgenta
			
		new THREE.MeshPhongMaterial( {
			color: 0x00ffff, emissive: 0x00ffff, specular: specular, 
			side: side, wireframe: wireframe, flatShading: flatShading } ),	//  7 cyan
			
		new THREE.MeshBasicMaterial( {
			map: uvTex,			
			side: side, wireframe: wireframe, flatShading: flatShading } ),	//  8 uv grid
			
		new THREE.MeshBasicMaterial( {
			map: waterlilyTex,	
			side: side, wireframe: wireframe, flatShading: flatShading } ),	//  9 photo
			
		new THREE.MeshPhongMaterial( {
			color: 0x444444, emissive: 0x444444, specular: specular, 
			side: side, wireframe: wireframe, flatShading: flatShading } )	// 10 grey
		
	];

//....... skript geo.js defines geometry geo .........
var mesh = new THREE.Mesh( geo, materials );

if ( geo.quadLine !== undefined ) {
	
	mesh.add( geo.quadLine );
 	geo.quadLine.visible = false;
 
}

//object = mesh;
scene.add( mesh );

var handlePoint = new THREE.Object3D();
scene.add( handlePoint );
var handleBoxGeometry = new THREE.BoxBufferGeometry( 0.1, 0.1, 0.1 );
var handleBoxMaterial = new THREE.MeshBasicMaterial( {color: 0x00cc00, transparent: true, opacity: 0.6 } );
var handleBox = new THREE.Mesh( handleBoxGeometry , handleBoxMaterial );
handlePoint.add( handleBox );
var handleBoxEdges = new THREE.BoxHelper( handleBox, 0x000000 );
handleBox.add( handleBoxEdges );

var handleDirA = new THREE.Vector3( 0, 0, 0 );
var handleArrowA = new THREE.ArrowHelper( handleDirA, new THREE.Vector3( 0, 0, 0 ), 0, 0xff00cc);
handlePoint.add( handleArrowA );
var handleDirB = new THREE.Vector3( 0, 0, 0 );
var handleArrowB = new THREE.ArrowHelper( handleDirB, new THREE.Vector3( 0, 0, 0 ), 0, 0xffcc00);
handlePoint.add( handleArrowB );
var handleDirC = new THREE.Vector3( 0, 0, 0 );
var handleArrowC = new THREE.ArrowHelper( handleDirC, new THREE.Vector3( 0, 0, 0 ), 0, 0x00ffcc );
handlePoint.add( handleArrowC );

handlePoint.visible = false;

var vertexFaceNumbersHelper = new THREEh.vertexFaceNumbersHelper( mesh, 3, 0.04, 0x0000ff ); // from THREEh.js !!!
vertexFaceNumbersHelper.update( 3 );

animate();

//...........................................................................

function onContainerMouseDown( event ) {

	// event.preventDefault(); // ???

	selection =  null;
	
	mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
	mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
	
	raycaster.setFromCamera( mouse, camera );

	intersects = raycaster.intersectObject( mesh );

	if (intersects.length > 0) {        //  cutting object
	
		controls.enabled = false;
		
		vertexPickedA = false;
		vertexPickedB = false;
		vertexPickedC = false;
					
		edgePickedAB = false;
		edgePickedBC = false;
		edgePickedCA = false;
		
		facePicked = false;
		
		handleArrowA.visible = false;
		handleArrowB.visible = false;
		handleArrowC.visible = false;
		
		handlePoint.visible = true;
		
		// Selection - first cutting object
		selection = intersects[0].object;	
		handlePos = intersects[0].point;
		handlePoint.position.set( handlePos );
		//auxilaryPlane.position.copy( intersects[0].object.position );
		auxilaryPlane.position.copy( handlePos );
		auxilaryPlane.quaternion.copy( camera.quaternion )
		
		//faceIdx = intersects[0].faceIndex;
		
		a = intersects[0].face.a;
		b = intersects[0].face.b;
		c = intersects[0].face.c;
		
		ax = geo.vertices[ a * 3 ];
		ay = geo.vertices[ a * 3 + 1 ];
		az = geo.vertices[ a * 3 + 2 ];
		
		bx = geo.vertices[ b * 3 ];
		by = geo.vertices[ b * 3 + 1 ];
		bz = geo.vertices[ b * 3 + 2 ];
		
		cx = geo.vertices[ c * 3 ];
		cy = geo.vertices[ c * 3 + 1 ];
		cz = geo.vertices[ c * 3 + 2 ];
	
		aPos = new THREE.Vector3( ax, ay, az );
		bPos = new THREE.Vector3( bx, by, bz );
		cPos = new THREE.Vector3( cx, cy, cz );
		
		handlePos = intersects[0].point;
		handlePoint.position.set( handlePos );
		
		paD = handlePos.distanceTo( aPos );
		pbD = handlePos.distanceTo( bPos );
		pcD = handlePos.distanceTo( cPos );
				
		abV.subVectors( bPos, aPos );
		bcV.subVectors( cPos, bPos );
		caV.subVectors( aPos, cPos );
		
		abD = Math.sqrt( abV.dot( abV ) ); // a, b Distance
		bcD = Math.sqrt( bcV.dot( bcV ) ); // b, c Distance
		caD = Math.sqrt( caV.dot( caV ) ); // c, a Distance
		
		abVn = abV;
		bcVn = bcV;
		caVn = caV;
		
		abVn.normalize( );
		bcVn.normalize( );
		caVn.normalize( );
				
		paV.subVectors( aPos, handlePos );
		pbV.subVectors( bPos, handlePos );
		pcV.subVectors( cPos, handlePos );
		
		paVn.copy( paV ).normalize( );
		pbVn.copy( pbV ).normalize( );
		pcVn.copy( pcV ).normalize( );

		abpD = Math.sqrt( paV.dot( paV ) - abVn.dot( paV ) * abVn.dot( paV ) ); // ab, p Distance
		bcpD = Math.sqrt( pbV.dot( pbV ) - bcVn.dot( pbV ) * bcVn.dot( pbV ) ); // bc, p Distance
		capD = Math.sqrt( pcV.dot( pcV ) - caVn.dot( pcV ) * caVn.dot( pcV ) ); // ca, p Distance
				
		if ( paD < ( pbD + pcD ) / 8  ) { 
			
			handleArrowA.setDirection( paVn );
			handleArrowA.setLength( paD, paD / 2, paD / 16 );
			handleArrowA.visible = true;
			vertexPickedA = true;
							
		} else if ( pbD < ( paD + pcD ) / 8  ) {	
			
			handleArrowB.setDirection( pbVn );
			handleArrowB.setLength(  pbD, pbD / 2, pbD / 16);
			handleArrowB.visible = true;
			vertexPickedB = true;
						
		} else if ( pcD < ( paD + pbD ) / 8  ) {
			
			handleArrowC.setDirection( pcVn );
			handleArrowC.setLength( pcD, pcD / 2, pcD / 16 );
			handleArrowC.visible = true;
			vertexPickedC = true;
									
		} else {	
							
			// check for edges
			
			if ( abpD< bcpD  &&  abpD< capD && abpD < abD / 8 ) {
				
				handleArrowA.setDirection( paVn );
				handleArrowB.setDirection( pbVn );
				handleArrowA.setLength( paD, paD / 2, paD / 16 );
				handleArrowB.setLength( pbD, pbD / 2, pbD / 16 );
				handleArrowA.visible = true;
				handleArrowB.visible = true;
				edgePickedAB = true;
								
			} else if ( bcpD < capD  &&  bcpD < abpD&& bcpD < bcD / 8 ) {
					
				handleArrowB.setDirection( pbVn );
				handleArrowC.setDirection( pcVn );
				handleArrowB.setLength( pbD, pbD / 2, pbD / 16 );
				handleArrowC.setLength( pcD, pcD / 2, pcD / 16 );
				handleArrowB.visible = true;
				handleArrowC.visible = true;
				edgePickedBC = true;
								
			} else if ( capD < abpD &&  capD < bcpD && capD < caD / 8 ) {

				handleArrowC.setDirection( pcVn );
				handleArrowA.setDirection( paVn );
				handleArrowC.setLength( pcD, pcD / 2, pcD / 16 );
				handleArrowA.setLength( paD, paD / 2, paD / 16 );
				handleArrowC.visible = true;
				handleArrowA.visible = true;
				edgePickedCA = true;
								
			} else {
			
				// face is picked
										
				handleArrowA.setDirection( paVn );
				handleArrowB.setDirection( pbVn );
				handleArrowC.setDirection( pcVn );
				handleArrowA.setLength( paD, paD / 2, paD / 16 );
				handleArrowB.setLength( pbD, pbD / 2, pbD / 16 );
				handleArrowC.setLength( pcD, pcD / 2, pcD / 16 );
				handleArrowA.visible = true;
				handleArrowB.visible = true;
				handleArrowC.visible = true;
				facePicked = true;
				
			}
			
		}
		
	}
	
}
  
function onContainerMouseMove( event ) {

   // event.preventDefault( ); // ???
		
	mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
	mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
	
	raycaster.setFromCamera( mouse, camera );
	
	if ( selection ) {
	
		// Position section with auxiliary plane
		intersects = raycaster.intersectObject( auxilaryPlane );
		handlePos = intersects[0].point;
				
		handlePoint.position.copy( handlePos );
		
		if ( vertexPickedA || edgePickedAB || edgePickedCA || facePicked ) {
							
			ax = handlePos.x + paV.x;
			ay = handlePos.y + paV.y;
			az = handlePos.z + paV.z;
			
		}
		
		if ( vertexPickedB || edgePickedAB || edgePickedBC || facePicked ) {
		
			bx = handlePos.x + pbV.x;
			by = handlePos.y + pbV.y;
			bz = handlePos.z + pbV.z;
			
		}	

		if ( vertexPickedC || edgePickedBC || edgePickedCA || facePicked ) {
		
			cx = handlePos.x + pcV.x;
			cy = handlePos.y + pcV.y;
			cz = handlePos.z + pcV.z;
			
		}	

	} else {
		
		// new position auxiliary layer (if necessary)
		intersects = raycaster.intersectObject( mesh );
		
		if (intersects.length > 0) {
		
			//auxilaryPlane.position.copy(intersects[0].object.position);
			auxilaryPlane.position.copy( handlePos );
			// auxilaryPlane.lookAt(camera.position);
			auxilaryPlane.quaternion.copy( camera.quaternion )

		}
		
	}
		
}

function onContainerMouseUp( event ) {
		
	// new position auxiliary layer (if necessary)
		intersects = raycaster.intersectObject( mesh );
		
		if (intersects.length > 0) {
		
			auxilaryPlane.position.copy(intersects[0].object.position);
			// auxilaryPlane.lookAt(camera.position);
			auxilaryPlane.quaternion.copy( camera.quaternion )

		}
			
		controls.enabled = true;
		selection = null;
		handlePoint.visible = false;
			
}

function arrowAuxilaryAxis( axes ) {
	
	var dir;
	var origin = new THREE.Vector3( 0, 0, 0 );
	var len = 1;
	var col = 0xffffff;
	var headLength = len / 32;
	var headWidth = headLength;
	if ( axes === "x" ) dir = new THREE.Vector3( 1, 0, 0 );
	if ( axes === "y" ) dir = new THREE.Vector3( 0, 1, 0 ); 
	if ( axes === "z" ) dir = new THREE.Vector3( 0, 0, 1 );
	var arrow = new THREE.ArrowHelper( dir, origin, len, col, headLength, headWidth );
	auxilaryPlane.add( arrow );
	
}

function round(x) { return x }

function animate( ) {

	requestAnimationFrame( animate );
		
	geo.vertices[ a * 3 ] = round( ax );
	geo.vertices[ a * 3 + 1 ] = round( ay );
	geo.vertices[ a * 3 + 2 ] = round( az );
	
	geo.vertices[ b * 3 ] = round( bx );
	geo.vertices[ b * 3 + 1 ] = round( by );
	geo.vertices[ b * 3 + 2 ] = round( bz );
	
	geo.vertices[ c * 3 ] = round( cx );
	geo.vertices[ c * 3 + 1 ] = round( cy );
	geo.vertices[ c * 3 + 2 ] = round( cz );

	geo.attributes.position.needsUpdate = true;
	geo.attributes.normal.needsUpdate = true;
		
	vertexFaceNumbersHelper.update( 3 );
	
	renderer.render( scene, camera );
 
}

</script> 
</html>
3 Likes