[SOLVED] Raycaster - MultiMaterial

materials
raycaster

#1

Starting from the example
http://threejs.live/#/webgl_interactive_buffergeometry
I created a simple example with THREE.CylinderBufferGeometry ().

However, this is indexed in contrast to the example given.
https://github.com/mrdoob/three.js/blob/master/src/geometries/CylinderGeometry.js
Then I still use OrbitControls.js.
I also used multi material, THREE.DoubleSide, THREE.FrontSide, THREE.BackSide.

Only the front side is detected. Even with material THREE.BackSide!
The script also contains a hidden custom geometry. The result is identical.

I’ve tried some things, no change - where’s my fault?

Try it out for yourself http://threejs.hofk.de/test/raycasterExample.html


<!DOCTYPE html>
<html lang="en">
<head>
<title> raycast BufferGeometry</title>
<meta charset="utf-8">	
</head>
<body>
OrbitControls & raycast / indexed BufferGeometry / based on http://threejs.live/#/webgl_interactive_buffergeometry
<div id="container"></div>		
<script src="three.min.89.js"></script>
<script src="OrbitControls.js"></script>
<script>
var container, camera, scene, renderer, raycaster, mouse, mesh, line;
init();
animate();
//................
function init() {
	container = document.getElementById( 'container' );
	camera = new THREE.PerspectiveCamera( 40, window.innerWidth / window.innerHeight, 0.1, 35000 );
	camera.position.set( -100, 250, 300 ); 
	scene = new THREE.Scene();
	scene.background = new THREE.Color( 0xdddddd );
	scene.add( new THREE.AmbientLight( 0x888888 ) );
	var light1 = new THREE.DirectionalLight( 0xffffff, 0.8 );
	light1.position.set( 1, 1, 1 );
	scene.add( light1 );
	var light2 = new THREE.DirectionalLight( 0xffffff, 0.6 );
	light2.position.set( 0, -1, 0 );
	scene.add( light2 );
	controls = new THREE.OrbitControls(camera);	
	var geometry = new THREE.CylinderBufferGeometry( 40, 20, 40, 5, 3, false, 0, 5.9);
	
/* // a custom indexed BufferGeometry 
	var geometry = new THREE.BufferGeometry();
	geometry.faceIndices = new Uint32Array( [ 0, 4, 5, 0, 5, 1, 1, 5, 6, 1, 6, 2, 2, 6, 7, 2, 7, 3, 4, 8, 9, 4, 9, 5, 5, 9, 10, 5, 10, 6, 6, 10, 11, 6, 11, 7, 8, 12, 13, 8, 13, 9, 9, 13, 14, 9, 14, 10, 10, 14, 15, 10, 15, 11 ] );
	geometry.vertices = new Float32Array( [ 0.1, -0.5, 0, 0.2, -0.2, 0, 0.2, 0.1, 0, 0.3, 0.5, 0, -0.1, -0.5, -0.2, -0.2, -0.2, -0.2, -0.2, 0.1, -0.3, -0.2, 0.5, -0.3, -0.1, -0.5, 0.1, -0.2, -0.2, 0.1, -0.2, 0.1, 0.2, -0.2, 0.5, 0.2, 0.1, -0.5, 0, 0.2, -0.2, 0, 0.2, 0.1, 0, 0.3, 0.5, 0 ] );
	geometry.normals = new Float32Array( [ 0.9, -0.2, -0.1, 0.9, -0.2, -0.1, 0.9, -0.2, 0, 0.9, -0.2, 0, -0.5, -0.2, -0.9, -0.5, -0.2, -0.9, -0.5, -0.2, -0.9, -0.5, -0.2, -0.9, -0.5, -0.2, 0.8, -0.5, -0.2, 0.8, -0.5, -0.2, 0.8, -0.5, -0.2, 0.8, 0.9, -0.2, -0.1, 0.9, -0.2, -0.1, 0.9, -0.2, 0, 0.9, -0.2, 0 ] );
	//geometry.uvs = new Float32Array( [ 0, 0, 0, 0.3, 0, 0.6, 0, 1, 0.3, 0, 0.3, 0.3, 0.3, 0.6, 0.3, 1, 0.6, 0, 0.6, 0.3, 0.6, 0.6, 0.6, 1, 1, 0, 1, 0.3, 1, 0.6, 1, 1 ] );
	//geometry.colors = new Float32Array( [101,  105,  200, 102,  102,  200, 102, 101,  200, 103, 105,  200,  101,  105,  102,  102,  102,  102,  102, 101,  103,  102, 105,  103,  101,  105, 101,  102,  102, 101,  102, 101, 102,  102, 105, 102, 101,  105,  200, 102,  102,  200, 102, 101,  200, 103, 105,  200 ] );
	geometry.setIndex( new THREE.BufferAttribute( geometry.faceIndices, 1 ) );
	//geometry.addAttribute( 'position', new THREE.BufferAttribute( geometry.vertices, 3 ).setDynamic( true ) );
	geometry.addAttribute( 'position', new THREE.BufferAttribute( geometry.vertices, 3 ) );
	//geometry.addAttribute( 'normal', new THREE.BufferAttribute( geometry.normals, 3 ).setDynamic( true ) );
	geometry.addAttribute( 'normal', new THREE.BufferAttribute( geometry.normals, 3 ) );
	//geometry.addAttribute( 'uv', new THREE.BufferAttribute( geometry.uvs, 2 ) );
	//geometry.addAttribute( 'color', new THREE.BufferAttribute( geometry.colors, 3 ) );	
	var geoGrp = [ 0, 3, 0, 3, 3, 0, 6, 3, 0, 9, 3, 0, 12, 3, 0, 15, 3, 0, 18, 3, 1, 21, 3, 1, 24, 3, 1, 27, 3, 1, 30, 3, 1, 33, 3, 1, 36, 3, 2, 39, 3, 2, 42, 3, 2, 45, 3, 2, 48, 3, 2, 51, 3, 2 ]; 	
	for ( var f = 0, p = 0; f < 18; f ++, p += 3 ) { geometry.addGroup( geoGrp[ p ], geoGrp[ p + 1 ], geoGrp[ p + 2 ] ); }				
	geometry.scale( 100, 100,100 ); //!!!!!!!!   scale   !!!!!!!!!!	
 */ ///////////////////////////////////////////////////////////////
	
	var material = [
		new THREE.MeshPhongMaterial( {				
			side: THREE.DoubleSide,	color: 0xff2222, specular: 0xee1111, shininess: 150, // vertexColors: THREE.VertexColors,		 
		} ),
		new THREE.MeshPhongMaterial( {				
			side: THREE.FrontSide, color: 0x22ff22, specular: 0x11ee11, shininess: 150, // vertexColors: THREE.VertexColors,			
		} ),
		new THREE.MeshPhongMaterial( {				
			side: THREE.BackSide, color: 0x2222ff, specular: 0x1111ee, shininess: 150, // vertexColors: THREE.VertexColors,			
		} )
	];	
	mesh = new THREE.Mesh( geometry, material );
	scene.add( mesh );
	raycaster = new THREE.Raycaster();
	mouse = new THREE.Vector2();
	var geometry = new THREE.BufferGeometry();
	geometry.addAttribute( 'position', new THREE.BufferAttribute( new Float32Array( 4 * 3 ), 3 ) );	
	var material = new THREE.LineBasicMaterial( { color: 0xffffff } );
	line = new THREE.Line( geometry, material );
	scene.add( line );	
	renderer = new THREE.WebGLRenderer( { antialias: false } );
	renderer.setPixelRatio( window.devicePixelRatio );
	renderer.setSize( window.innerWidth, window.innerHeight );
	container.appendChild( renderer.domElement );
	window.addEventListener( 'resize', onWindowResize, false );
	document.addEventListener( 'mousemove', onDocumentMouseMove, false );
}
function onWindowResize() {
	camera.aspect = window.innerWidth / window.innerHeight;
	camera.updateProjectionMatrix();
	renderer.setSize( window.innerWidth, window.innerHeight );
}
function onDocumentMouseMove( event ) {
	event.preventDefault();
	mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1;
	mouse.y = - ( event.clientY / window.innerHeight ) * 2 + 1;
}
function animate() {
	requestAnimationFrame( animate );
	render();	
}
function render() {
	var time = Date.now() * 0.001;
	raycaster.setFromCamera( mouse, camera );
	var intersects = raycaster.intersectObject( mesh );
	if ( intersects.length > 0 ) {
		var intersect = intersects[ 0 ];
		var face = intersect.face;
		var linePosition = line.geometry.attributes.position;
		var meshPosition = mesh.geometry.attributes.position;
		linePosition.copyAt( 0, meshPosition, face.a );
		linePosition.copyAt( 1, meshPosition, face.b );
		linePosition.copyAt( 2, meshPosition, face.c );
		linePosition.copyAt( 3, meshPosition, face.a );
		mesh.updateMatrix();
		line.geometry.applyMatrix( mesh.matrix );
		line.visible = true;
	} else {
		line.visible = false;
	}
	renderer.render( scene, camera );
}
</script>
</body>
</html>

Does three.js supports FK (forward kinematics) and IK (Inverse kinematics)?
Modify indexed BufferGeometry (mouse or input)
Modify indexed BufferGeometry (mouse or input)
Round-edged box
#2

Not sure, how actual it is, but take a look at this thread https://github.com/mrdoob/three.js/issues/9694

If to use just one material (not an array), then everything works fine with THREE.BackSide and THREE.DoubleSide as well.


#3

Tested it too, I guess that’s the reason.

THREE. MultiMaterial was changed some time ago, only array has to be specified. But I suppose the statement is still valid?

Too bad, for my geometry I need 11 materials or more.

I have to find a suitable standard material for manipulating the geometry. The manipulation :gear: is in progress.


#4

Yes. Some time ago, I’ve created a PR in order to solve this problem but the implementation was not satisfying. I hope we find a solution in the future.

If you need raycast with multiple materials as a feature in your project, you can pick my code of the mentioned PR as a workaround.


#5

Thanks to Mugen87 :+1: [solved]

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

Then used
https://github.com/Mugen87/three.js/blob/82e866be2ffa752969af6efe96f91b5baab19c33/src/objects/Mesh.js
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>

#6

A new PR was merged so raycasting with multiple materials and BufferGeometry does work now. :tada: