mattdesl / three-line-2d

lines expanded in a vertex shader
MIT License
236 stars 34 forks source link

Raycaster can't interset the line [feature request] #8

Closed OhBonsai closed 7 years ago

OhBonsai commented 8 years ago

THREE Raycaster can't intersect the line when use THREE.Mesh. And get ugly line when you THREE.Line. But the Raycaster is useful. Maybe need rewrite the LineGeometrys' raycaster function extend in THREE.object3D

lojjic commented 8 years ago

I ran into this too. The issue is that Mesh.raycast uses the original vertex positions from the geometry, which results in triangles of area=0 so they never intersect the ray.

My hackish solution is to override the Mesh's raycast method to temporarily replace the geometry with one whose vertices are transformed outward along the normals in the same way the shader does. I'm not happy that this requires transforming the geometry -- seems to defeat the purpose of doing those transforms shader-side to begin with -- but it's better than nothing. I'd be very interested in helping to find a more elegant solution, if anyone has ideas.

The code:

class Line2DMesh extends THREE.Mesh {
  raycast() {
    let origGeometry = this.geometry

    // Create a temporary clone of the Line2D geometry
    let tempGeometry = origGeometry.clone()
    let positions = tempGeometry.getAttribute('position')
    let normals = tempGeometry.getAttribute('lineNormal')
    let miters = tempGeometry.getAttribute('lineMiter')
    let thickness = this.material.uniforms.thickness.value

    // TODO - Line2D doesn't seem to set BufferArray.count correctly sometimes, need to investigate
    let count = positions.array.length / positions.itemSize

    // Transform each vertex outward along the normal just as the vertex shader would
    for (let i = 0; i < count; i++) {
      positions.setXY(
        i,
        positions.getX(i) + normals.getX(i) * (thickness / 2) * miters.getX(i),
        positions.getY(i) + normals.getY(i) * (thickness / 2) * miters.getX(i)
      )
    }

    // Temporarily replace the geometry with our transformed one and invoke the raycaster
    this.geometry = tempGeometry
    let result = super.raycast(...arguments)
    this.geometry = origGeometry

    return result
  }
}
OhBonsai commented 8 years ago

Thanks mate ^_^ I have a solution wroten in ES5 but forget to paste it ... Code in https://github.com/OhBonsai/oneTile/blob/master/js/app/paint/LineMesh.js

But it seems like your solution is better @lojjic ??>_<!