morpheus05
morpheus05

Reputation: 4872

Interactable Grid in ThreeJs

I need to draw a rectangular (not quadratic) "support" grid: Users can hover an tile of this grid and place a specific object there. E.g. I hover over (x:0 y:15) and I click, I get an object at (x:0 y:15).

enter image description here

This Grid was done with a PlaneGeometry and drawn as a wireframe (in r58).

new THREE.PlaneGeometry(x,y,16, 9);
var mat = new THREE.MeshBasicMaterial({
    color: 0x004488,
    wireframe: true
});
mat.color.r = r;
mat.color.g = g;
mat.color.b = b;

After I updated the code base to r94 I get now triangles instead of quads for the different tiles of the plane.

I tried the following:

Drawing lines: After looking at the source code of GridHelper, I created a rectangular version. It looks very nice but the hit detection of a tile is off. Hit detection is implemented with a Raycaster. I think the problem is, thaht lines only get detected if the mouse is actually over a line. With the PlaneGeometry the mouse could be inside a tile and the geometry was detected correctly.

Drawing a PlaneGeometry with a Texture The next think I tried was using a PlaneGeometry with a custom Texture, like this:

let canvas = document.createElement('canvas');
let context = canvas.getContext('2d');
canvas.height = 32;
canvas.width = 32;
context.beginPath();
context.rect(0, 0, 32, 32);
context.lineWidth = 2;
context.strokeStyle = color.getStyle();
context.stroke();

// canvas contents will be used for a texture
let texture = new THREE.Texture(canvas);

texture.needsUpdate = true;

let material = new THREE.MeshBasicMaterial();
// let material = new THREE.LineBasicMaterial();
material.map = texture;
material.flatShading = true;
material.fog = false;
material.lights = false;
material.side = THREE.DoubleSide;
material.transparent = true;

return material;

Now hit detection works like a charm, as it did with r54, but now it looks very ugly if the viewing angle is flat:

enter image description here

I'm running out of ideas, so I would appreciate a hint or something how I can draw nice looking grid, which is one geometry so the current hit detection system can be used.

TL;DR; I need a grid, which behaves like a PlaneGeometry (hit detection) but only drawing squares instead of triangles.

Upvotes: 3

Views: 3055

Answers (1)

prisoner849
prisoner849

Reputation: 17596

You can have a grid, built from lines. For that, you can create THREE.PlaneBufferGeometry() and change its .index propery. Then use this geometry with THREE.LineSegments().

enter image description here

Reference

And the source code for changing the indices:

  Object.assign(THREE.PlaneBufferGeometry.prototype, {
    toGrid: function() {
      let segmentsX = this.parameters.widthSegments || 1;
      let segmentsY = this.parameters.heightSegments || 1;
      let indices = [];
      for (let i = 0; i < segmentsY + 1; i++) {
        let index11 = 0;
        let index12 = 0;
        for (let j = 0; j < segmentsX; j++) {
          index11 = (segmentsX + 1) * i + j;
          index12 = index11 + 1;
          let index21 = index11;
          let index22 = index11 + (segmentsX + 1);
          indices.push(index11, index12);
          if (index22 < ((segmentsX + 1) * (segmentsY + 1) - 1)) {
            indices.push(index21, index22);
          }
        }
        if ((index12 + segmentsX + 1) <= ((segmentsX + 1) * (segmentsY + 1) - 1)) {
          indices.push(index12, index12 + segmentsX + 1);
        }
      }
      this.setIndex(indices);
      return this;
    }
  });

Using:

var planeGeom = new THREE.PlaneBufferGeometry(10, 5, 10, 5).toGrid();
var gridPlane = new THREE.LineSegments(planeGeom, new THREE.LineBasicMaterial({color: "yellow"}));

Upvotes: 3

Related Questions