Thanks to Mugen87 [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! 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>