raycaster Intersection isn't accurate after moving vertices - three.js

I'm having issue with raycaster.intersectObjects() after moving vertices of a geometry.. It doesn't intersect with the new geometry, it only intersects with the part that overlaps the original area. The object looks correctly modified in the view. I've been trying to search for an answer but none of the solutions seem to be working. I'm not sure what is wrong.
//move parts/vertices
for(var p=0; p<latticeModel.selected.length; p++){
var partNum = latticeModel.selected[p];
if(latticeModel.part[partNum].children[1].visible==true){
latticeModel.part[partNum].position.add(vector);
latticeCloud.tiles.part[partNum].position.add(vector);
}else{
var localVector = vector.clone();
localVector.add(latticeCloud.tiles.part[partNum].position);
latticeCloud.tiles.part[partNum].worldToLocal(localVector);
for(var v=0; v<4; v++){
if( latticeCloud.tiles.colors[partNum][v].getHex() == 0xfe0000 ||
latticeCloud.tiles.colors[partNum][v].getHex() == 0xff00ff ){
latticeCloud.tiles.part[partNum].geometry.vertices[v].add(localVector);
latticeCloud.tiles.part[partNum].geometry.verticesNeedUpdate = true;
latticeModel.part[partNum].children[0].geometry.vertices[v].add(localVector);
latticeModel.part[partNum].children[0].geometry.verticesNeedUpdate = true;
//added these below to try and debug, but still won't help
latticeModel.part[partNum].children[0].geometry.normalsNeedUpdate = true;
latticeModel.part[partNum].children[0].geometry.elementsNeedUpdate = true;
latticeModel.part[partNum].children[0].geometry.tangentsNeedUpdate = true;
latticeModel.part[partNum].children[0].geometry.computeFaceNormals = true;
latticeModel.part[partNum].children[0].geometry.computeVertexNormals = true;
latticeModel.part[partNum].children[0].geometry.computeBoundingBox = true;
latticeModel.part[partNum].children[0].geometry.computeBoundingSphere = true;
}
}
}
}
Here is the intersect part:
mouse.x = 2 * (e.clientX / window.innerWidth) - 1;
mouse.y = 1 - 2 * (e.clientY / window.innerHeight);
var vector = new THREE.Vector3(mouse.x, mouse.y, 0.5).unproject(camera);
var raycaster = new THREE.Raycaster(camera.position, vector.sub(camera.position).normalize());
scene.updateMatrixWorld();
//find the part mouse is overlapping and highlight it.
mouse.intersects = raycaster.intersectObjects(latticeModel.part, true);
if(mouse.intersects.length){
var foundPart = latticeModel.part.indexOf(mouse.intersects[0].object.parent);
if(foundPart!=mouse.selectedPart){
if(latticeModel.part.length>mouse.selectedPart && mouse.selectedPart!=-1){
latticeModel.part[mouse.selectedPart].children[2].visible = false;
}
mouse.selectedPart = foundPart;
latticeModel.part[mouse.selectedPart].children[2].visible = true;
}
}else{
if(latticeModel.part.length>mouse.selectedPart && mouse.selectedPart!=-1){
latticeModel.part[mouse.selectedPart].children[2].visible = false;
mouse.selectedPart = -1;
}
}

If you modify the vertices of a geometry, the geometry's bounding sphere and bounding box may become invalid.
The raycasting code will recompute them for you, but you need to set the following:
geometry.boundingSphere = null;
geometry.boundingBox = null;
three.js r.71

Related

Draw a 2D line with width in three.js

I'm looking to draw a continuous line with a given thickness showing only the edges using three.js. I have achieved it. I'm trying to add thickness to the line but it is not getting reflected in the scene due to some angle in three.js. Can anyone help me out with the issue.
Here's the fiddle https://jsfiddle.net/16vhjm0y/1/
var renderer, scene, camera;
var line;
var count = 0;
var mouse = new THREE.Vector3();
var mesh3D;
var maxPoint = 6;
var height = window.innerHeight * .99;
var plane = new THREE.Plane(new THREE.Vector3(0, 0, 1), 0); // facing us for mouse intersection
var raycaster = new THREE.Raycaster();
var point3ds = [];
var usePerspectiveCamera = false; // toggles back and forth
var perspOrbit;
var perspCam;
var orthoOrbit;
var orthoCam;
var labelRenderer, labelAjay;
var testBoolean = false;
var mouseDownBoolean = false;
var distanceData, showDistanceData;
var ajay;
var arrAjay = [];
var arrAjayFinal = [];
var mouseUpBoolean = false;
init();
animate();
function init() {
// renderer
renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, height);
document.body.appendChild(renderer.domElement);
// scene
scene = new THREE.Scene();
scene.background = new THREE.Color(0xffffff);
// camera perspective
perspCam = new THREE.PerspectiveCamera(45, window.innerWidth / height, 1, 10000);
perspCam.position.set(0, 0, 200);
// camera ortho
var width = window.innerWidth;
//var height = window.innerHeight;
orthoCam = new THREE.OrthographicCamera(-width / 2, width / 2, height / 2, -height / 2, 0, 1200);
// assign cam
camera = perspCam;
someMaterial = new THREE.MeshBasicMaterial({ color: 0xA9A9A9, side: THREE.DoubleSide, transparent: true, opacity: 0.3 });
// grid
var grid = new THREE.GridHelper(1024, 56);
grid.rotateX(Math.PI / 2);
// scene.add(grid);
// geometry
var geometry = new THREE.BufferGeometry();
var MAX_POINTS = 500;
positions = new Float32Array(MAX_POINTS * 3);
geometry.addAttribute('position', new THREE.BufferAttribute(positions, 3));
// material
var material = new THREE.LineBasicMaterial({
color: 0xff0000,
linewidth: 10
});
// line
line = new THREE.Line(geometry, material);
// line.position.z = 20;
scene.add(line);
// var geometry = new THREE.BoxBufferGeometry( 10, 2, 20 );
// var edgesPavement = new THREE.EdgesGeometry( geomPavement );
// var lineGeometry = new THREE.LineSegmentsGeometry().setPositions( edgesPavement.attributes.position.array );
// var lineMaterial = new THREE.LineMaterial( { color: 0xff0000, linewidth: 10 } );
// lineMaterial.resolution.set( window.innerWidth, window.innerHeight ); // important, for now...
// var line = new THREE.LineSegments2( lineGeometry, lineMaterial );
// scene.add( line );
document.addEventListener("mousemove", onMouseMove, false);
document.addEventListener('mousedown', onMouseDown, false);
document.addEventListener('mouseup', onMouseUp, false);
createUI();
labelRenderer = new THREE.CSS2DRenderer();
ajay = document.createElement('div');
ajay.className = 'ajay';
ajay.style.color = "black";
ajayInsert = document.createElement('div');
ajayInsert.className = 'ajay';
ajayInsert.style.color = "black";
// ajay.style.color = "black";
// console.log(ajay)
labelAjay = new THREE.CSS2DObject(ajay);
labelAjayFinal = new THREE.CSS2DObject(ajayInsert);
labelRenderer.setSize(window.innerWidth, window.innerHeight);
labelRenderer.domElement.style.position = 'absolute';
labelRenderer.domElement.style.top = '0';
labelRenderer.domElement.style.pointerEvents = 'none';
ajay.style.display = "none";
ajayInsert.style.display = "none";
}
// update line
function updateLine() {
positions[count * 3 - 3] = mouse.x;
positions[count * 3 - 2] = mouse.y;
positions[count * 3 - 1] = mouse.z;
line.geometry.attributes.position.needsUpdate = true;
}
// mouse move handler
function onMouseMove(event) {
var rect = renderer.domElement.getBoundingClientRect();
mouse.x = (event.clientX - rect.left) / (rect.right - rect.left) * 2 - 1;
mouse.y = - ((event.clientY - rect.top) / (rect.bottom - rect.top)) * 2 + 1;
raycaster.setFromCamera(mouse, camera);
mouse = raycaster.ray.intersectPlane(plane, mouse);
if (count !== 0 && count < maxPoint) {
updateLine();
}
testBoolean = true;
if (testBoolean == true) {
// scene.remove(labelAjay);
var geometry = line.geometry;
geometry.computeBoundingBox();
center = geometry.boundingBox.getCenter();
// line.localToWorld(center);
// console.log(center);
if (mouseDownBoolean == true) {
labelAjay.position.set(mouse.x, mouse.y, mouse.z);
// console.log(line.position)
scene.add(labelAjay);
document.body.appendChild(labelRenderer.domElement);
// console.log(positions);
distanceData = point3ds[0].distanceTo(new THREE.Vector3(mouse.x, mouse.y, mouse.z));
showDistanceData = Math.round(distanceData * 1000);
// console.log(point3ds[0]);
// console.log(point3ds[1]);
// console.log(distanceData);
// console.log(showDistanceData)
ajay.textContent = showDistanceData + ' mm';
// console.log(labelRenderer)
}
// console.log(labelRenderer.domElement)
// document.getElementsByClassName("ajay").remove();
// document.getElementsByClassName("ajay").outerHTML = "";
}
}
// add point
function addPoint(event) {
if (count < maxPoint) {
console.log("point nr " + count + ": " + mouse.x + " " + mouse.y + " " + mouse.z);
positions[count * 3 + 0] = mouse.x;
positions[count * 3 + 1] = mouse.y;
positions[count * 3 + 2] = mouse.z
count++;
line.geometry.setDrawRange(0, count);
updateLine();
point3ds.push(new THREE.Vector3(mouse.x, mouse.y, mouse.z));
} else {
console.log('max points reached: ' + maxPoint);
}
}
function getPointInBetweenByLen(pointA, pointB, length) {
var dir = pointB.clone().sub(pointA).normalize().multiplyScalar(length);
return pointA.clone().add(dir);
}
// mouse down handler
function onMouseDown(evt) {
mouseDownBoolean = true;
// force add an extra point on first click so buffer line can display
// buffer geometry requires two points to display, so first click should add two points
if (count === 0) {
addPoint();
}
if (count < maxPoint) {
addPoint();
}
}
function onMouseUp(event){
mouseUpBoolean = true;
if(mouseUpBoolean == true){
// showDistanceData = Math.round(distanceData * 1000);
arrAjay.push(showDistanceData);
console.log(arrAjay);
arrAjayFinal = arrAjay.splice(-1)[0];
var geometry = line.geometry;
geometry.computeBoundingBox();
center = geometry.boundingBox.getCenter();
if (mouseDownBoolean == true) {
labelAjayFinal.position.set(center.x, center.y, center.z);
scene.add(labelAjayFinal);
document.body.appendChild(labelRenderer.domElement);
// distanceData = point3ds[0].distanceTo(new THREE.Vector3(mouse.x, mouse.y, mouse.z));
// showDistanceData = Math.round(distanceData * 1000);
console.log('arrAjayFinal', arrAjayFinal);
ajayInsert.textContent = arrAjayFinal;
}
}
}
// render
function render() {
renderer.render(scene, camera);
labelRenderer.render(scene, camera);
}
// animate
function animate() {
requestAnimationFrame(animate);
render();
}
// loop through all the segments and create their 3D
function create3D() {
if (!mesh3D && point3ds && point3ds.length) {
console.log('creating 3D');
mesh3D = new THREE.Mesh(); // metpy mesh but is the root mesh for all 3D
scene.add(mesh3D);
// prepare create segments from point3ds - every two points create a segement
var index = 1;
var segmentHeight = 56;
point3ds.forEach(point3d => {
if (index < point3ds.length) {
var seg = new Segment(point3d, point3ds[index], someMaterial, segmentHeight);
mesh3D.add(seg.mesh3D);
index++;
}
});
}
}
function createUI() {
// create3D
var btn = document.createElement('button');
document.body.appendChild(btn);
btn.innerHTML = 'Create3D';
btn.addEventListener('mousedown', () => {
create3D();
// add orbiting controls to both cameras
var controls;
if (!perspOrbit) {
perspOrbit = new THREE.OrbitControls(perspCam, renderer.domElement);
perspOrbit.screenSpacePanning = true;
// raotation is enabled once create3D is pressed
setToFullOrbit(perspOrbit);
perspOrbit.enabled = true; // set to true by default
}
// add orbit to orthocam
if (!orthoOrbit) {
orthoOrbit = new THREE.OrbitControls(orthoCam, renderer.domElement);
orthoOrbit.screenSpacePanning = true;
orthoOrbit.enabled = false; // set to false by default
//orthoOrbit.enableDamping = true;
//orthoOrbit.dampingFactor = .15;
}
});
}
function switchCam() {
usePerspectiveCamera = !usePerspectiveCamera;
if (usePerspectiveCamera) {
if (perspCam) {
camera = perspCam;
perspOrbit.enabled = true;
orthoOrbit.enabled = false;
} else {
throw new Error('Switch to perspective cam failed, perspective cam is null');
}
} else {
if (orthoCam) {
camera = orthoCam;
orthoOrbit.enabled = true;
perspOrbit.enabled = false;
} else {
throw new Error('Switch to ortho cam failed, orthoCam is null');
}
}
}
function rotateCam90() {
if (camera instanceof THREE.OrthographicCamera) {
orthoOrbit.update();
camera.applyMatrix(new THREE.Matrix4().makeRotationZ(Math.PI / 2));
}
}
function reset() {
scene.remove(mesh3D);
mesh3D = null;
for (var i = 0; i < 3 * 8; i++) {
positions[i] = 0;
}
count = 0;
line.geometry.setDrawRange(0, count);
updateLine();
point3ds = [];
}
function setToFullOrbit(orbitControl) {
// how far you can orbit vertically
orbitControl.minPolarAngle = 0;
orbitControl.maxPolarAngle = Math.PI;
// How far you can dolly in and out ( PerspectiveCamera only )
orbitControl.minDistance = 0;
orbitControl.maxDistance = Infinity;
orbitControl.enableZoom = true; // Set to false to disable zooming
orbitControl.zoomSpeed = 1.0;
orbitControl.enableRotate = true;
// allow keyboard arrows
orbitControl.enableKeys = true;
// Set to false to disable panning (ie vertical and horizontal translations)
orbitControl.enablePan = true;
}
// each segment knows how to create its 3D
class Segment {
constructor(start, end, material, height) {
this.start = start;
this.end = end;
this.height = height; // height of the segment's 3D
this.material = material;
this.mesh3D = null;
this.create3D();
}
create3D() {
if (this.start && this.end) {
//create the shape geometry
var distStartToEnd = this.start.distanceTo(this.end);
var vec2s = [
new THREE.Vector2(),
new THREE.Vector2(0, this.height),
new THREE.Vector2(distStartToEnd, this.height),
new THREE.Vector2(distStartToEnd, 0)
];
console.log('vec2s', vec2s);
var shape = new THREE.Shape(vec2s);
var geo = new THREE.BoxGeometry(5, 5, 5);
// console.log('shape', shape);
var geo = new THREE.ShapeGeometry(shape);
geo.applyMatrix(new THREE.Matrix4().makeRotationX(THREE.Math.degToRad(90)));
this.mesh3D = new THREE.Mesh(geo, this.material);
this.alignRotation();
this.alignPosition();
// the mesh3D should be added to the scene outside of this class
}
}
alignRotation() {
var p1 = this.start.clone();
var p2 = this.end.clone();
var direction = new THREE.Vector3();
direction.subVectors(p2, p1);
direction.normalize();
this.mesh3D.quaternion.setFromUnitVectors(new THREE.Vector3(1, 0, 0), direction);
}
alignPosition() {
if (this.mesh3D) {
this.mesh3D.position.copy(this.start);
} else {
throw new Error('mesh3D null');
}
}
}
The linewidth parameter relies on native WebGL support for drawing line thickness, but its performance is very spotty across browsers & operating systems. I think Windows doesn't support it, but MacOS does, so it shouldn't be relied upon. See this discussion on the Three.js Github for several bug reports.
As a workaround, they've created LineGeometry, which sort of re-builds a line with geometry to allow for thickness. See this example for how to use it. It even allows for dashed lines. After importing the module, you can implement it with:
const geometry = new LineGeometry();
geometry.setPositions( positions );
geometry.setColors( colors );
matLine = new LineMaterial( {
color: 0xffffff,
linewidth: 5, // in pixels
vertexColors: true,
dashed: false
} );
line = new Line2( geometry, matLine );
line.computeLineDistances();
scene.add( line );

Animating Three.js vertices

I'm trying to animate individual vertices from a collada model.
The vertices ARE animating fine once but then they don't animate anymore.
1: Load my collada
// name obj
var orgVerts = false;
var desVerts = false;
var material = new THREE.MeshBasicMaterial({ color:0x1e5679, wireframe:true });
var loader = new THREE.ColladaLoader();
var nameModel = false;
loader.options.convertUpAxis = true;
loader.load( '3d/name.dae', function ( collada ) {
nameModel = collada.scene.children[0].children[0];
nameModel.material = material;
nameModel.geometry.dynamic = true;
nameModel.position.set(0,0,0);//x,z,y- if you think in blender dimensions ;)
nameModel.scale.x = .0035;
nameModel.scale.y = .0035;
nameModel.scale.z = .0035;
scene.add(nameModel);
orgVerts = nameModel.geometry.vertices; // make a backup of all verts
genVerts(); // create a new array of random verts
});
2: in my render function
var render = function () {
requestAnimationFrame(render);
// do stuff
if(nameModel){
if(window.globalCurrentSlide != 0){
for(var r=0; r < nameModel.geometry.vertices.length; r++){
var vert = desVerts[r]; // loop through all the destination verts
nameModel.geometry.vertices[r].x = nameModel.geometry.vertices[r].x - (nameModel.geometry.vertices[r].x - vert.x)/20;
nameModel.geometry.vertices[r].y = nameModel.geometry.vertices[r].y - (nameModel.geometry.vertices[r].y - vert.y)/20;
nameModel.geometry.vertices[r].z = nameModel.geometry.vertices[r].z - (nameModel.geometry.vertices[r].z - vert.z)/20;
}
}else{
for(var t=0; t < nameModel.geometry.vertices.length; t++){
var vert2 = orgVerts[t]; // loop through all the original verts
nameModel.geometry.vertices[t].x = nameModel.geometry.vertices[t].x - (nameModel.geometry.vertices[t].x - vert2.x)/20;
nameModel.geometry.vertices[t].y = nameModel.geometry.vertices[t].y - (nameModel.geometry.vertices[t].y - vert2.y)/20;
nameModel.geometry.vertices[t].z = nameModel.geometry.vertices[t].z - (nameModel.geometry.vertices[t].z - vert2.z)/20;
}
}
nameModel.geometry.verticesNeedUpdate = true;
nameModel.rotation.y += .005;
}
renderer.render( scene, camera );
}
window.globalCurrentSlide is set to 0 to start with and everything is fine. if I change window.globalCurrentSlide to 1, all the vertices animate correctly... BUT when I change window.globalCurrentSlide back to 0 the vertices don't animate back to their original positions. I've debugged heaps and can 100% say that BOTH desVerts and orgVerts don't change and they are correct. Any ideas? It's driving me nuts.
PS: I know the code could be condensed, I'm just playing ATM
The answer was that my orgVerts was just a reference NOT a clone. use:
var geometry = nameModel.geometry.clone();
orgVerts = geometry.vertices;

Picking Object3D loaded via OBJMTLLoader

When we load an Object3D with OBJMTLLoader, it is not possible to use raycaster to pick this object with mouse. Intersection array length is always 0. Any one knows the reason? Below is the code...
The loader routine
var loader2 = new THREE.OBJMTLLoader();
loader2.load('/assets/unwrap/masa/dogtasmasa.obj', '/assets/unwrap/masa/dogtasmasa.mtl', function (object) {
object.position.y = 1.5;
object.position.x = 0;
object.position.z = 2;
object.rotateX(-Math.PI / 2);
object.rotateZ(-Math.PI / 2);
object.scale.set(0.04, 0.04, 0.04);
object.castShadow = true;
scene.add(object);
});
and the picking
function onDocumentMouseDown(event) {
event.preventDefault();
SCREEN_WIDTH = window.innerWidth - 5;
SCREEN_HEIGHT = window.innerHeight - 5;
var vector = new THREE.Vector3((event.clientX / SCREEN_WIDTH) * 2 - 1, -(event.clientY / SCREEN_HEIGHT) * 2 + 1, 0.5);
projector.unprojectVector(vector, camera);
var raycaster = new THREE.Raycaster(camera.position, vector.sub(camera.position).normalize());
var intersects = raycaster.intersectObjects(scene.children);
if (intersects.length > 0) {
for (var i = 0; i < intersects.length; i++) {
var obj = intersects[i].object;
controls.enabled = false;
tControls.attach();
}
}
else {
controls.enabled = true;
tControls.detach();
}
}
The scene is the whole browser window. Any other mesh cerated via THREE types can be picked, but object3d not...
Thanks for all kinds of help
Add the recursive flag like so:
var intersects = raycaster.intersectObjects( objects, true );
three.js r.66

Three js How to move scene

I would like to move the container (which contains my 3d objects)
Could you tell me how?
I am using Three JS
THREE.DragControls = function(camera, scene, domElement) {
this.childs = [];
this.childOffsets = [];
if (scene instanceof THREE.Scene) {
childs = scene.children;
scene = scene.children;
}
var _projector = new THREE.Projector();
var mouse = new THREE.Vector3();
var offset = new THREE.Vector3();
var selected;
domElement.addEventListener('mousemove', onDocumentMouseMove, false);
domElement.addEventListener('mousedown', onDocumentMouseDown, false);
domElement.addEventListener('mouseup', onDocumentMouseUp, false);
function onDocumentMouseMove(event) {
event.preventDefault();
mouse.x = (event.clientX / domElement.width) * 2 - 1;
mouse.y = -(event.clientY / domElement.height) * 2 + 1;
var ray = _projector.pickingRay(mouse, camera);
if (selected) {
var targetPos = ray.ray.direction.clone().multiplyScalar(selected.distance).add(ray.ray.origin);
selected.object.position.copy(targetPos.sub(offset));
return;
}
var intersects = ray.intersectObjects(scene);
if (intersects.length > 0) {
domElement.style.cursor = 'pointer';
} else {
domElement.style.cursor = 'auto';
}
}
function onDocumentMouseDown(event) {
event.preventDefault();
mouse.x = (event.clientX / domElement.width) * 2 - 1;
mouse.y = -(event.clientY / domElement.height) * 2 + 1;
var ray = _projector.pickingRay(mouse, camera);
var intersects = ray.intersectObjects(scene);
if (intersects.length > 0) {
selected = intersects[0];
offset.copy(selected.point).sub(selected.object.position);
domElement.style.cursor = 'move';
}
}
function onDocumentMouseUp(event) {
event.preventDefault();
if (selected) {
selected = null;
}
domElement.style.cursor = 'auto';
}
}
If I understand correctly you would like to MOVE all 3D Objects within a scene?
The way I did that is to create a THREE.Group(). E.g.
var holder = new THREE.Group();
Then add all models/lights/etc to that holder group. Then add the holder to the scene.
var scene = new THREE.Scene();
holder.add(model);
scene.add(holder);
Then moving the holder would move all objects added to it.
I think you are asking for moving you're scene container which maybe a canvas or a dom element if that's the case you can apply drag and drop to your container which is rendering your scene.
Reference below :
https://www.google.com/url?sa=t&source=web&rct=j&url=https://konvajs.org/docs/drag_and_drop/Drag_and_Drop.html&ved=2ahUKEwjOscW6zpj1AhXSEXAKHb21Bz8QFnoECAYQAQ&usg=AOvVaw2aV1F114SqLcBDVL8BiN0Q

Three.js - Updating texture on plane

I'm trying to make a terrain editor. So I have a plane with multiple materials on it, which I initialize like so:
var materials = [];
var grasstexture = THREE.ImageUtils.loadTexture('Img/grass.png', {}, function() { });
var rocktexture = THREE.ImageUtils.loadTexture('Img/rock.png', {}, function() { });
materials.push(new THREE.MeshPhongMaterial({color: 0xffffff, map: grasstexture}));
materials.push(new THREE.MeshPhongMaterial({color: 0xffffff, map: rocktexture}));
// Plane
this.geometry.materials = materials;
for(var i = 0; i < this.geometry.faces.length; i++)
{
this.geometry.faces[i].materialIndex = 0;
}
this.geometry.dynamic = true;
this.mesh = new THREE.Mesh( this.geometry, new THREE.MeshFaceMaterial( ) );
this.mesh.receiveShadow = true;
The result is a square plane stripped with the two textures.
Now, I can't seem to update the materialIndex for each vertex during the runtime. I have tried:
face.materialIndex = 1;
for(var i = 0; i < geo.materials.length; i++){
geo.materials[i].needsUpdate = true;
}
for(var i = 0; i < this.geometry.faces.length; i++)
{
geo.faces[i].materialIndex = 0;
}
for(var i = 0; i < geo.materials.length; i++){
geo.materials[i].needsUpdate = true;
}
this.mesh.needsUpdate = true;
this.mesh.material.needsUpdate = true;
geo.verticesNeedUpdate = true;
geo.normalsNeedUpdate = true;
geo.colorsNeedUpdate = true;
geo.computeFaceNormals();
geo.computeVertexNormals();
geo.computeCentroids();
geo.computeTangents() ;
And every other 'materialIndex' and 'needsUpdate' variable in Three.js I was able to find, but still nothing happens. How do I force an update on the material indices?
You can't. materialIndex is only used in the first render to partition the geometry into chunks, with each chunk having the same material. Also, you cannot re-partition the chunks.
What you can do, however, is change a material in the materials array.
materials[ 0 ] = new THREE.MeshBasicMaterial();
You do not need to set any needsUpdate flags to true after doing so, either.
An alternate thing you can do is give each face it's own material from the start, and then, for example, just change a material's texture.
mesh.geometry.materials[ 0 ].map = texture;
texture.needsUpdate = true;

Resources