Modify indexed BufferGeometry (mouse or input)

I wrote a HTML/Script. :gear:

modifyGeometry

A basic version led to a question that was solved thanks to mugen87.
https://discourse.threejs.org/t/solved-raycaster-multimaterial/1633

Therefore, it is essential to use three-version three.89.mugen.js.
(workaround from mugen87, raycast for multi material)

The tool makes it possible to modify indexed BufferGeometry with the mouse or by input.
It requires the output of the :gear: modifyCreateGeo.html.
See https://discourse.threejs.org/t/addon-produces-almost-infinite-many-time-varying-geometries-with-functions/262/31
The code must be stored in the geo.js file.

Of course, the indexed BufferGeometry can also be generated in other ways.

Make sure that the arrays are named correctly. You can “abuse” the quadline to draw special lines. You can leave it out, it’s not necessary.

When moving vertex, edge or face with the mouse, the handling point (box) normally moves in the camera-orthogonal auxiliary plane. If the x, y or z keys are pressed, movement takes place in the plane orthogonal to the axis. The respective coordinate remains constant.

It is a bit difficult to use the mouse for camera control (OrbitControls.js) and selecting and moving vertex, edge or face. Then x, y or z are also selectable. Maybe there’s a better solution than mine.
Just let me know.

Note that the code is also copied to the clipboard immediately when the button “export changed code” is pressed.

You can find it on my site threejs. hofk. de. Or directly http://threejs.hofk.de/modifyGeo/modifyGeo.html .

At GitHub I added it as well. https://github.com/hofk/THREEf.js/tree/master/THREEf_89
There is also the script THREEh.js for vertexFaceNumbersHelper (mesh, mode, size, color)
and three-version three.89.mugen.js
HTML can’t show here - limit of 32000.

'use strict'

var out;

var mouse = new THREE.Vector2();
var keyCode = null;
var sqrt2_2 = Math.sqrt( 2 ) / 2; 
var raycaster = new THREE.Raycaster();

var intersects 	= [];
var selection;
var handlePos = new THREE.Vector3( 0,0,0 );
var hdlSize = 1;

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 edgeV;

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 quad = false;

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);

var light1 = new THREE.PointLight();
light1.position.set( 1.5, 4, 7 );
scene.add( light1 );

var light2 = new THREE.PointLight();
light2.position.set( -1, -8, -8 );
scene.add( light2 );

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

var btnChangeInput = document.getElementById( "changeInput" );
btnChangeInput.onclick = changeInput;
var btnClr = document.getElementById( "clr" );
btnClr.onclick = clearCode;
var btnGrids = document.getElementById( "grids" );
btnGrids.onclick = showGrids;
var btnWireframe = document.getElementById( "wirefr" );
btnWireframe.onclick = showWireframe;
var btnQuad = document.getElementById( "quad" );
btnQuad.onclick = showQuadline;
var btnPlus = document.getElementById( "plus" );
btnPlus.onclick = handleSizePlus;
var btnMinus = document.getElementById( "minus" );
btnMinus.onclick = handleSizeMinus;

nDec.onchange = changeDecimals;

document.getElementById("export").onclick  = outputJavaScript;

document.addEventListener('keydown', onDocumentKeyDown );
document.addEventListener('keyup', onDocumentKeyUp );

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 );

var grids = true;

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

arrowAuxiliaryAxis( "x" );
arrowAuxiliaryAxis( "y" );
arrowAuxiliaryAxis( "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 def. geometry geo .........
var mesh = new THREE.Mesh( geo, materials );

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

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 faceIdx = 0;
faceIndex.value = 0;
faceIndex.max = geo.faceIndices.length / 3 - 1;
var currentFaceIdx = 0;

var statusInp = InpChoosed.checked;

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

String.prototype.replaceAll = function( search, replacement ) {
	
	var target = this;
	return target.split( search ).join( replacement );
	
};

animate();

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

function enableDisableInp( ) {
	
	if ( InpChoosed.checked ) {
		
		faceIndex.disabled = false;
		
		rangeAx.disabled = numberAx.disabled = rangeAy.disabled = numberAy.disabled = rangeAz.disabled = numberAz.disabled = false;
		rangeBx.disabled = numberBx.disabled = rangeBy.disabled = numberBy.disabled = rangeBz.disabled = numberBz.disabled = false;
		rangeCx.disabled = numberCx.disabled = rangeCy.disabled = numberCy.disabled = rangeCz.disabled = numberCz.disabled = false;

		faceIdx = faceIndex.value;
		
		auxiliaryPlane.visible = false;
		handleBox.visible = false;
		handleArrowA.visible = false;
		handleArrowB.visible = false;
		handleArrowC.visible = false;
				
	} else {
		
		faceIndex.disabled = true;
		
		rangeAx.disabled = numberAx.disabled = rangeAy.disabled = numberAy.disabled = rangeAz.disabled = numberAz.disabled = true;
		rangeBx.disabled = numberBx.disabled = rangeBy.disabled = numberBy.disabled = rangeBz.disabled = numberBz.disabled = true;
		rangeCx.disabled = numberCx.disabled = rangeCy.disabled = numberCy.disabled = rangeCz.disabled = numberCz.disabled = true;
		
		auxiliaryPlane.visible = true;
		handleBox.visible = true;
		handleArrowA.visible = true;
		handleArrowB.visible = true;
		handleArrowC.visible = true;
		
	}
	
	statusInp = InpChoosed.checked;
	
}

function changeDecimals( ) {
	
	var d =  Math.pow( 10, -nDec.value );
	
	rangeAx.step = rangeAy.step = rangeAz.step = d;
	rangeBx.step = rangeBy.step = rangeBz.step = d;
	rangeCx.step = rangeCy.step = rangeCz.step = d;
	
	numberAx.step = numberAy.step = numberAz.step = d;
	numberBx.step = numberBy.step = numberBz.step = d;
	numberCx.step = numberCy.step = numberCz.step = d;
	
}

function handleSizePlus( ) {
	
	hdlSize = ( hdlSize + 0.2 ) / hdlSize;
	handleBoxGeometry.scale( hdlSize, hdlSize, hdlSize);
	handleBoxEdges.geometry.scale( hdlSize, hdlSize, hdlSize );
	
}

function handleSizeMinus( ) {
	
	hdlSize = hdlSize / ( hdlSize + 0.2 );
	handleBoxGeometry.scale( hdlSize, hdlSize, hdlSize);
	handleBoxEdges.geometry.scale( hdlSize, hdlSize, hdlSize );
	
}

function changeInput( ) {
	
	if ( InpChoosed.checked ) { 
		
		InpChoosed.checked = false;
		
	} else { 
		
		InpChoosed.checked = true;	
	}
	
}

function clearCode( ) {
	
	 output.innerHTML = "";
	
}

function showGrids( ) {
	
	grids = !grids;
	
	if ( grids ) {
		
		gridHelperXY.visible = true;
		gridHelperXZ.visible = true;
		gridHelperYZ.visible = true;
		btnGrids.innerHTML = "hide grids";
		
	} else {
		
		gridHelperXY.visible = false;
		gridHelperXZ.visible = false;
		gridHelperYZ.visible = false;
		btnGrids.innerHTML = "show grids";
		
	}
	
}

function showWireframe( ) {
	
	wireframe = !wireframe;
	btnWireframe.innerHTML = wireframe ? "no wirefr." : "wireframe";
	for ( var m = 0; m < materials.length; m ++ ) materials[ m ].wireframe = wireframe;
	
}

function showQuadline( ) {
	
	quad = !quad;
	
	btnQuad.innerHTML = quad === true ? "no quad" : "quadline";
	if ( geo.quadLine !== undefined ) geo.quadLine.visible = quad ? true : false;
	
}


function onDocumentKeyDown( event ) {
	
	keyCode = event.keyCode;
	
}

function onDocumentKeyUp( event ) {
	
	keyCode = null;	
	
}

function onContainerMouseDown( event ) {
	
	event.preventDefault(); 
	
	if ( !InpChoosed.checked ) {
		
		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 );
			auxiliaryPlane.position.copy( handlePos );
			
			if ( keyCode === 88 ) {
				
				auxiliaryPlane.quaternion.set( 0, -sqrt2_2, 0, sqrt2_2); // auxiliary plane orthogonal to x axis
				
			} else  if ( keyCode === 89 ) { 
				
				auxiliaryPlane.quaternion.set( sqrt2_2, 0, 0, sqrt2_2 ); // auxiliary plane orthogonal to y axis
				
			} else  if ( keyCode === 90 ) {
				
				auxiliaryPlane.quaternion.set( 0, 0, 0, 1 ); // auxiliary plane orthogonal to z axis
				
			} else {
				
				auxiliaryPlane.quaternion.copy( camera.quaternion );
				
			}
			
			faceIdx = intersects[0].faceIndex;
			currentFaceIdx = faceIdx;
			
			faceIndex.value = faceIdx;
			
			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 );
			
			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 move() {
	
	if ( vertexPickedA || edgePickedAB || edgePickedCA || facePicked ) {
		
		btnIndexA.innerHTML = a;
		ax = handlePos.x + paV.x;
		ay = handlePos.y + paV.y;
		az = handlePos.z + paV.z;
		
	}
	
	if ( vertexPickedB || edgePickedAB || edgePickedBC || facePicked ) {
		
		btnIndexB.innerHTML = b;
		bx = handlePos.x + pbV.x;
		by = handlePos.y + pbV.y;
		bz = handlePos.z + pbV.z;
		
	}
	
	if ( vertexPickedC || edgePickedBC || edgePickedCA || facePicked ) {
		
		btnIndexC.innerHTML = c;
		cx = handlePos.x + pcV.x;
		cy = handlePos.y + pcV.y;
		cz = handlePos.z + pcV.z;
		
	}

}

function onContainerMouseMove( event ) {
	
	event.preventDefault( );
	
	if ( !InpChoosed.checked ) {
		
		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( auxiliaryPlane );
			handlePos = intersects[0].point;
			
			handlePoint.position.copy( handlePos );
			move();
			
		} else {
			
			// new position auxiliary layer (if necessary)
			intersects = raycaster.intersectObject( mesh );
			
			if ( intersects.length > 0) {
				
				auxiliaryPlane.position.copy( handlePos );
				auxiliaryPlane.quaternion.copy( camera.quaternion )
				
			}
			
		}
		
	}

}

function onContainerMouseUp( event ) {
	
	// new position auxiliary layer (if necessary)
	intersects = raycaster.intersectObject( mesh );
	
	if (intersects.length > 0) {
		
		auxiliaryPlane.position.copy( intersects[0].object.position );
		auxiliaryPlane.quaternion.copy( camera.quaternion )
		
	}
	
	controls.enabled = true;
	selection = null;
	handlePoint.visible = false;
	keyCode = null;
	
}

function arrowAuxiliaryAxis( 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 );
	auxiliaryPlane.add( arrow );
	
}

function outputJavaScript( ) {
	
	var vc3 = geo.vertices.length;
	var fic = geo.faceIndices.length;
	var fc = geo.faceIndices.length / 3;
	var uvc2 = geo.uvs.length;
	var out = "";
	var ffd = Math.pow( 10, nDec.value );
	
	// round for output
	function round( x ) {
		
		return Math.floor( x * ffd ) / ffd;
		
	}
	
	// --- generate JavaScript code ---
	
	output.innerHTML  = "/*... BufferGeometry generated with addon THREEf<br />and modifyGeo.html ...*/<br />";
	
	out += "var geo = new THREE.BufferGeometry();<br />";
	
	out += "geo.faceIndices = new Uint32Array( [ ";
	
	for ( var i = 0; i < fic; i ++ ) {
		
		out += geo.faceIndices[ i ];
		out += i < fic - 1  ? ", " : "";
		
	}
	
	out += " ] );<br />";
	
	out += "geo.vertices = new Float32Array( [ ";
	
	for ( var v = 0; v < vc3 ; v ++ ) {
		
		out += round( geo.vertices[ v ] );
		out += v < vc3 - 1  ? ", " : "";
		
	}
	
	out += " ] );<br />";
	
	out += "geo.normals = new Float32Array( [ ";
	
	for ( var v = 0; v < vc3 ; v ++ ) {
		
		out +=  round( geo.normals[ v ] );
		out += v < vc3 - 1  ? ", " : "";
		
	}
	
	out += " ] );<br />"
	
	out += "geo.uvs = new Float32Array( [ ";
	
	for ( var v = 0; v < uvc2 ; v ++ ) {
		
		out +=  round( geo.uvs[ v ] );
		out += v < uvc2 - 1  ? ", " : "";
		
	}
	
	out += " ] );<br />"
	
	out += "geo.setIndex( new THREE.BufferAttribute( geo.faceIndices, 1 ) );<br />geo.addAttribute( 'position', new THREE.BufferAttribute( geo.vertices, 3 ).setDynamic( true ) );<br />geo.addAttribute( 'normal', new THREE.BufferAttribute( geo.normals, 3 ).setDynamic( true ) );<br />geo.addAttribute( 'uv', new THREE.BufferAttribute( geo.uvs, 2 ) );<br />";
	
	out += "var geoGrp = [ ";
	
	for ( var f = 0, p = 0; f < fc ; f ++, p += 3 ) { 
		
		out += geo.groups[ f ].start + ", " + geo.groups[ f ].count + ", " + geo.groups[ f ].materialIndex ;
		out += f < fc - 1  ? ", " : "";
		
	}
	
	out += " ];<br />";
	
	out += "for ( var f = 0, p = 0; f < " + fc + "; f ++, p += 3 ) { geo.addGroup( geoGrp[ p ], geoGrp[ p + 1 ], geoGrp[ p + 2 ] ); }<br />";
	
	if ( geo.quadLine && quad ) {
		
		out += "geo.lineGeometry = new THREE.BufferGeometry();<br />";
		out += "geo.quadColor = 0x" + geo.quadColor.toString(16) + ";<br />";
		out += "geo.quadLine = new THREE.Line( geo.lineGeometry, new THREE.LineBasicMaterial( { color: geo.quadColor } ) );<br />";
		
		out += "geo.linePositions = new Float32Array( [ ";
		
		var glc = geo.linePositions.length;
		
		for ( var v = 0; v < glc; v ++ ) {
			
			out +=  round( geo.linePositions[ v ] );
			out += v < glc - 1  ? ", " : "";
			
		}
		
		out += " ] ); <br /> ";
		
		out += "geo.lineGeometry.addAttribute( 'position', new THREE.BufferAttribute( geo.linePositions, 3 ) );<br />";
		
	}
	
	
	output.innerHTML += out;
	
	code = document.createElement( "textarea" );
	code.value = output.innerHTML.replaceAll( '<br>', '\r\n' ).replaceAll( '&lt;', '<' ).replaceAll( '&gt;', '>' );
	document.body.appendChild( code );
	code.select();
	document.execCommand( "Copy" );
	document.body.removeChild( code );
	
}

function round( x ) {
	
	var d = Math.pow( 10, nDec.value );
	return Math.floor( x * d ) / d;
	
}

function animate( ) {
	
	requestAnimationFrame( animate );
	
	if ( statusInp !== InpChoosed.checked ) enableDisableInp( );
	
	if ( InpChoosed.checked ) {
		
		faceIdx = faceIndex.value < geo.faceIndices.length / 3 ? faceIndex.value : geo.faceIndices.length / 3;
		
		a = geo.faceIndices[ faceIdx * 3 ];
		b = geo.faceIndices[ faceIdx * 3 + 1 ];
		c = geo.faceIndices[ faceIdx * 3 + 2 ];
		
		if ( currentFaceIdx !== faceIdx ) {
			
			btnIndexA.innerHTML = a;
			numberAx.value = rangeAx.value = round( geo.vertices[ a * 3 ] );
			numberAy.value = rangeAy.value = round( geo.vertices[ a * 3 + 1 ] );
			numberAz.value = rangeAz.value = round( geo.vertices[ a * 3 + 2 ] );
			
			btnIndexB.innerHTML = b;
			numberBx.value = rangeBx.value = round( geo.vertices[ b * 3 ] );
			numberBy.value = rangeBy.value = round( geo.vertices[ b * 3 + 1 ] );
			numberBz.value = rangeBz.value = round( geo.vertices[ b * 3 + 2 ] );
			
			btnIndexC.innerHTML = c;
			numberCx.value = rangeCx.value = round( geo.vertices[ c * 3 ] );
			numberCy.value = rangeCy.value = round( geo.vertices[ c * 3 + 1 ] );
			numberCz.value = rangeCz.value = round( geo.vertices[ c * 3 + 2 ] );
			
			currentFaceIdx = faceIdx;
			
		}
		
		if ( useRangeAx.checked ) ax = numberAx.value = round( rangeAx.value );
		if ( useRangeAy.checked ) ay = numberAy.value = round( rangeAy.value );
		if ( useRangeAz.checked ) az = numberAz.value = round( rangeAz.value );
		
		if ( useRangeBx.checked ) bx = numberBx.value = round( rangeBx.value );
		if ( useRangeBy.checked ) by = numberBy.value = round( rangeBy.value );
		if ( useRangeBz.checked ) bz = numberBz.value = round( rangeBz.value );
		
		if ( useRangeCx.checked ) cx = numberCx.value = round( rangeCx.value );
		if ( useRangeCy.checked ) cy = numberCy.value = round( rangeCy.value );
		if ( useRangeCz.checked ) cz = numberCz.value = round( rangeCz.value );
		
		if ( useNumberAx.checked ) ax = rangeAx.value = round( numberAx.value );
		if ( useNumberAy.checked ) ay = rangeAy.value = round( numberAy.value );
		if ( useNumberAz.checked ) az = rangeAz.value = round( numberAz.value );
		
		if ( useNumberBx.checked ) bx = rangeBx.value = round( numberBx.value );
		if ( useNumberBy.checked ) by = rangeBy.value = round( numberBy.value );
		if ( useNumberBz.checked ) bz = rangeBz.value = round( numberBz.value );
		
		if ( useNumberCx.checked ) cx = rangeCx.value = round( numberCx.value );
		if ( useNumberCy.checked ) cy = rangeCy.value = round( numberCy.value );
		if ( useNumberCz.checked ) cy = rangeCy.value = round( numberCy.value );
		
	}
	
	if ( !InpChoosed.checked ) {
		
		numberAx.value = rangeAx.value = round( ax );
		numberAy.value = rangeAy.value = round( ay );
		numberAz.value = rangeAz.value = round( az );
		
		numberBx.value = rangeBx.value = round( bx );
		numberBy.value = rangeBy.value = round( by );
		numberBz.value = rangeBz.value = round( bz );
		
		numberCx.value = rangeCx.value = round( cx );
		numberCy.value = rangeCy.value = round( cy );
		numberCz.value = rangeCz.value = round( cz );
		
	}
	
	btnIndexA.innerHTML = a;
	btnIndexB.innerHTML = b;
	btnIndexC.innerHTML = c;
	
	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 );
	
}

@hofk BTW: If you upvote the mentioned PR at github, we maybe find a way to merge this into the core. Or just leave a comment and ask for a fix :innocent:. I am still the opinion that raycasting with multiple materials should be supported.

1 Like

Since multimaterial raycast is supported in the core of three.js from revision 94, the workaround of mugen87 is no longer needed.

see also
[SOLVED] Raycaster - MultiMaterial
https://github.com/mrdoob/three.js/pull/14227

On Github corresponding note. https://github.com/hofk/THREEf.js README.md

There is still a bug in R94 but it was fixed via https://github.com/mrdoob/three.js/pull/14392

So the upcoming R95 will be the release you want to use :wink:

1 Like