Редактирование сетки: вершины
Сегодня сделал, пожалуй, самый значимый для себя функционал. Мне было очень интересно сделать возможность редактирования сетки объекта. Пока реализовано только перемещение вершин по одной. На реализацию у меня ушло 2 дня, и еще больше на обдумывание и теоретическую подготовку.
Редактирование реализовано для BufferGeometry.
Сначала в места вершин расставляю маленькие сферы
addPoints(){ let points = getPoints( this.mesh ); let vertexes = getVertexes( points ); addSpheresToVertexes( this.mesh, vertexes ); }
Так как координаты вершин хранятся в буфере, представляющем собой массив значений с шагом 3: [ x1, y1, z1, x2, y2, z2 … xn, yn, zn ], нужно получить координаты точек в виде векторов
function getPoints( mesh ) { let pointsArray = mesh.geometry.attributes.position.array; let itemSize = mesh.geometry.attributes.position.itemSize; let points = []; for (let i = 0; i < pointsArray.length; i += itemSize ) { points.push( new Vector3( pointsArray[i], pointsArray[i+1], pointsArray[i+2])); } return points; }
Но точки повторяются. Например, для куба (8 вершин) — 24 точки, по 3 точки на вершину (одна точка для одной грани)
Получим массив координат вершин:
function getVertexes( points ) { let vertexes = []; points.forEach( ( indexPoints ) => { let equal = false; vertexes.forEach( ( indexVertex ) => { if (indexPoints.equals( indexVertex) ){ equal = true; return; } }) if ( !equal ) { vertexes.push( indexPoints ); } }) return vertexes; }
В каждую вершину помещаем сферу
function addSpheresToVertexes( mesh, vertexes ){ let ta_Scene = new TA_Scene(); let sphereGeometry = new SphereBufferGeometry( 0.3, 4, 4 ); ta_Scene.tempSelectableObjects = ta_Scene.tempSelectableObjects.concat( ta_Scene.selectableObjects ); ta_Scene.selectableObjects = []; let group = new Group(); group.name = 'shperesForMeshEdit'; vertexes.map( (item, index) => { let sphere = new Mesh( sphereGeometry, new MeshBasicMaterial( { color: new Color( 'red' ) } ) ); sphere.name = 'editMeshHelper' sphere.userData.vertexNumer = `${index}` ; group.add( sphere ); sphere.position.set( item.x, item.y, item.z); ta_Scene.selectableObjects.push( sphere ); }) mesh.add( group ); }
Редактирование геометрии я сделал статическим методом. Передаю в него объект, номер вершины, которая изменятся и её положение
static changeGeometry( object, vertexNumber, position ){ let ta_Entities = new TA_Entities(); let points = getPoints( object ); let vertexes = getVertexes( points ); let VertexesPointsIndexes = getVertexesPointsIndexes( points, vertexes); ta_Entities.removeWireframeAndBoundingBox(object ); vertexes[ +vertexNumber ] = position; let newPoints = vertexesChangePoints ( vertexes, points, VertexesPointsIndexes); pointsChangeAttributesPosition( object, newPoints ); object.geometry.attributes.position.needsUpdate = true; object.updateMatrix(); // object.add( ta_Entities.createBoundingBox( object ) ); object.add( ta_Entities.createWireframe( object ) ); }
Теперь нужно сделать обратную процедуру.
Находим в каких позициях массива points содержатся координаты вершин
function getVertexesPointsIndexes( points, vertexes){ let indexesArray = []; vertexes.map ( (itemVertex ) => { let indexes = []; points.forEach( (itemPoints, index) => { if( itemPoints.equals( itemVertex ) ) indexes.push( index ); }) indexesArray.push( indexes ); }) return indexesArray; }
Меняем значение положения вершины
vertexes[ +vertexNumber ] = position;
и меняем массив points
function vertexesChangePoints ( vertexes, points, VertexesPointsIndexes) { // let newPoints = points; vertexes.map( ( itemVertex, index ) => { let arrayIndexes = VertexesPointsIndexes[ index ]; arrayIndexes.map( ( item ) => points[ item ] = itemVertex); }) points[0] = vertexes[0]; return points; }
Который в свою очередь меняет значения в буфере объекта
function pointsChangeAttributesPosition( mesh, points ) { let positions = []; points.map( (item) => { positions.push( item.x); positions.push( item.y); positions.push( item.z); } ) let arrayAttr = mesh.geometry.attributes.position.array; arrayAttr.map( (item, index) => { mesh.geometry.attributes.position.array[index] = positions[index] }) }
Изменение происходит на событие
this.transformControls.addEventListener( 'objectChange', function( event ) { if( event.target.mode === 'translate' ){ if ( scope.mode.action === 'meshEdit' ){ let vertexNumber = event.target.object.userData.vertexNumer; let position = event.target.object.position; let object = event.target.object.parent.parent; MeshEdit.changeGeometry( object, +vertexNumber, position ); }
Очень интересный проект. Внимательно слежу. Радует что он достаточно динамично развивается и обновляется.