1

is there some helper method in THREE.js that allows one to see the number assigned to each vertex in a mesh loaded from an obj file? [minimal obj file]

I'm trying to rig up some bones inside a mesh, and want to position those bones between particular vertices, but don't know the vertex numbers yet. A little tool like this could be super helpful for this purpose.

If there's no such method in THREE.js, I'll likely build a tool to this effect, but I wanted to save the time if I can. Any suggestions on where to look in THREE.js for this functionality would be greatly appreciated!

duhaime
  • 25,611
  • 17
  • 169
  • 224

1 Answers1

4

The solution is:

  1. Place small boxes on each vertex,
  2. Add tooltip to each box,
  3. Tooltip text set to vertex index.

show vertex indexes in three.js

How to add tooltip in three.js find in my answer here: Threejs Tooltip

Working jsfiddle for your convenience find here: http://jsfiddle.net/mmalex/fux6srgv/

Javascript code:

var scene = new THREE.Scene();
var raycaster = new THREE.Raycaster();

//create some camera
camera = new THREE.PerspectiveCamera(55, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.x = 3;
camera.position.y = 3;
camera.position.z = 3;
camera.lookAt(0, 0, 0);

var controls = new THREE.OrbitControls(camera);

var renderer = new THREE.WebGLRenderer({
  antialias: true
});
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setClearColor(new THREE.Color(0x595959));
document.body.appendChild(renderer.domElement);

// white spotlight shining from the side, casting a shadow
var spotLight = new THREE.SpotLight(0xffffff, 2.5, 25, Math.PI / 6);
spotLight.position.set(4, 10, 7);
scene.add(spotLight);

// collect objects for raycasting, 
// for better performance don't raytrace all scene
var tooltipEnabledObjects = [];

var colors = new RayysWebColors();
var dodecahedronGeometry = new THREE.DodecahedronBufferGeometry(1, 0);
var dodecahedronMaterial = new THREE.MeshPhongMaterial({
  color: colors.pickRandom().hex,
  transparent: true,
  opacity: 0.75
});
var dodecahedron = new THREE.Mesh(dodecahedronGeometry, dodecahedronMaterial);
scene.add(dodecahedron);

var size = 0.1;
var vertGeometry = new THREE.BoxGeometry(size, size, size);
var vertMaterial = new THREE.MeshBasicMaterial({
  color: 0x0000ff,
  transparent: false
});

var verts = dodecahedronGeometry.attributes.position.array;
for (let k=0; k<verts.length; k+=3) {
  var vertMarker = new THREE.Mesh(vertGeometry, vertMaterial);

  // this is how tooltip text is defined for each box
  let tooltipText = `idx: ${k}, pos: [${verts[k].toFixed(3)},${verts[k+1].toFixed(3)},${verts[k+2].toFixed(3)}]`;
  vertMarker.userData.tooltipText = tooltipText;

  vertMarker.applyMatrix(new THREE.Matrix4().makeTranslation(verts[k],verts[k+1],verts[k+2]));
  scene.add(vertMarker);
  tooltipEnabledObjects.push(vertMarker);
}

function animate() {
  requestAnimationFrame(animate);
  controls.update();
  renderer.render(scene, camera);
};

// this will be 2D coordinates of the current mouse position, [0,0] is middle of the screen.
var mouse = new THREE.Vector2();

var latestMouseProjection; // this is the latest projection of the mouse on object (i.e. intersection with ray)
var hoveredObj; // this objects is hovered at the moment

// tooltip will not appear immediately. If object was hovered shortly,
// - the timer will be canceled and tooltip will not appear at all.
var tooltipDisplayTimeout;

// This will move tooltip to the current mouse position and show it by timer.
function showTooltip() {
  var divElement = $("#tooltip");

  if (divElement && latestMouseProjection) {
    divElement.css({
      display: "block",
      opacity: 0.0
    });

    var canvasHalfWidth = renderer.domElement.offsetWidth / 2;
    var canvasHalfHeight = renderer.domElement.offsetHeight / 2;

    var tooltipPosition = latestMouseProjection.clone().project(camera);
    tooltipPosition.x = (tooltipPosition.x * canvasHalfWidth) + canvasHalfWidth + renderer.domElement.offsetLeft;
    tooltipPosition.y = -(tooltipPosition.y * canvasHalfHeight) + canvasHalfHeight + renderer.domElement.offsetTop;

    var tootipWidth = divElement[0].offsetWidth;
    var tootipHeight = divElement[0].offsetHeight;

    divElement.css({
      left: `${tooltipPosition.x - tootipWidth/2}px`,
      top: `${tooltipPosition.y - tootipHeight - 5}px`
    });

    // var position = new THREE.Vector3();
    // var quaternion = new THREE.Quaternion();
    // var scale = new THREE.Vector3();
    // hoveredObj.matrix.decompose(position, quaternion, scale);
    divElement.text(hoveredObj.userData.tooltipText);

    setTimeout(function() {
      divElement.css({
        opacity: 1.0
      });
    }, 25);
  }
}

// This will immediately hide tooltip.
function hideTooltip() {
  var divElement = $("#tooltip");
  if (divElement) {
    divElement.css({
      display: "none"
    });
  }
}

// Following two functions will convert mouse coordinates
// from screen to three.js system (where [0,0] is in the middle of the screen)
function updateMouseCoords(event, coordsObj) {
  coordsObj.x = ((event.clientX - renderer.domElement.offsetLeft + 0.5) / window.innerWidth) * 2 - 1;
  coordsObj.y = -((event.clientY - renderer.domElement.offsetTop + 0.5) / window.innerHeight) * 2 + 1;
}

function handleManipulationUpdate() {
  raycaster.setFromCamera(mouse, camera);
  {
    var intersects = raycaster.intersectObjects(tooltipEnabledObjects);
    if (intersects.length > 0) {
      latestMouseProjection = intersects[0].point;
      hoveredObj = intersects[0].object;
    }
  }

  if (tooltipDisplayTimeout || !latestMouseProjection) {
    clearTimeout(tooltipDisplayTimeout);
    tooltipDisplayTimeout = undefined;
    hideTooltip();
  }

  if (!tooltipDisplayTimeout && latestMouseProjection) {
    tooltipDisplayTimeout = setTimeout(function() {
      tooltipDisplayTimeout = undefined;
      showTooltip();
    }, 330);
  }
}

function onMouseMove(event) {
  updateMouseCoords(event, mouse);

  latestMouseProjection = undefined;
  hoveredObj = undefined;
  handleManipulationUpdate();
}

window.addEventListener('mousemove', onMouseMove, false);

animate();

HTML code:

<p style="margin-left: 12px;">Mouse hover verts to see vert index and coordinates</p>
<div id="tooltip"></div>

CSS of tooltip element:

#tooltip {
  position: fixed;
  left: 0;
  top: 0;
  min-width: 10px;
  text-align: center;
  padding: 2px 2px;
  font-family: monospace;
  background: #a0c020;
  display: none;
  opacity: 0;
  border: 0px solid transparent;
  box-shadow: 2px 2px 3px rgba(0, 0, 0, 0.5);
  transition: opacity 0.25s linear;
  border-radius: 0px;
}

Given approach allows to attach and display any text info per vertex.

Alex Khoroshylov
  • 2,234
  • 1
  • 17
  • 28