NGL@1.0.0-beta.7 Home Manual Reference Source Gallery

src/buffer/tubemesh-buffer.js

/**
 * @file Tube Mesh Buffer
 * @author Alexander Rose <alexander.rose@weirdbyte.de>
 * @private
 */

import { Vector3 } from '../../lib/three.es6.js'

import { defaults, getUintArray } from '../utils.js'
import { serialArray } from '../math/array-utils.js'
import MeshBuffer from './mesh-buffer.js'

var vTangent = new Vector3()
var vMeshNormal = new Vector3()

/**
 * Tube mesh buffer. Draws a tube.
 */
class TubeMeshBuffer extends MeshBuffer {
    /**
     * @param  {Object} data - attribute object
     * @param  {Float32Array} data.position - positions
     * @param  {Float32Array} data.normal - normals
     * @param  {Float32Array} data.binormal - binormals
     * @param  {Float32Array} data.tangent - tangents
     * @param  {Float32Array} data.color - colors
     * @param  {Float32Array} data.size - sizes
     * @param  {Picker} data.picking - picking ids
     * @param  {BufferParameters} params - parameter object
     */
  constructor (data, params) {
    var d = data || {}
    var p = params || {}

    var radialSegments = defaults(p.radialSegments, 4)
    var capped = defaults(p.capped, false)

    var capVertices = capped ? radialSegments : 0
    var capTriangles = capped ? radialSegments - 2 : 0

    var n = d.position.length / 3
    var n1 = n - 1
    var x = n * radialSegments * 3 + 2 * capVertices * 3
    var xi = n1 * 2 * radialSegments * 3 + 2 * capTriangles * 3

    var meshPosition = new Float32Array(x)
    var meshColor = new Float32Array(x)
    var meshNormal = new Float32Array(x)
    var meshIndex = getUintArray(xi, x / 3)

    super({
      position: meshPosition,
      color: meshColor,
      index: meshIndex,
      normal: meshNormal,
      picking: d.picking
    }, p)

        //

    this.aspectRatio = defaults(p.aspectRatio, 1.0)
    this.radialSegments = radialSegments
    this.capped = capped

    this.capVertices = capVertices
    this.capTriangles = capTriangles
    this.size2 = n

    d.primitiveId = serialArray(n)
    this.setAttributes(d)

    this.meshIndex = meshIndex
    this.makeIndex()
  }

  setAttributes (data) {
    var aspectRatio = this.aspectRatio

    var n = this.size2
    var n1 = n - 1
    var radialSegments = this.radialSegments

    var attributes = this.geometry.attributes

    var position, normal, binormal, tangent, color, size, primitiveId
    var meshPosition, meshColor, meshNormal, meshPrimitiveId

    if (data.position) {
      position = data.position
      normal = data.normal
      binormal = data.binormal
      tangent = data.tangent
      size = data.size

      meshPosition = attributes.position.array
      meshNormal = attributes.normal.array

      attributes.position.needsUpdate = true
      attributes.normal.needsUpdate = true
    }

    if (data.color) {
      color = data.color
      meshColor = attributes.color.array
      attributes.color.needsUpdate = true
    }

    if (data.primitiveId) {
      primitiveId = data.primitiveId
      meshPrimitiveId = attributes.primitiveId.array
      attributes.primitiveId.needsUpdate = true
    }

    var i, j, k, l, s, t
    var v, cx, cy
    var cx1, cy1, cx2, cy2
    var radius

    var normX, normY, normZ
    var biX, biY, biZ
    var posX, posY, posZ

    var cxArr = []
    var cyArr = []
    var cx1Arr = []
    var cy1Arr = []
    var cx2Arr = []
    var cy2Arr = []

    if (position) {
      for (j = 0; j < radialSegments; ++j) {
        v = (j / radialSegments) * 2 * Math.PI

        cxArr[ j ] = aspectRatio * Math.cos(v)
        cyArr[ j ] = Math.sin(v)

        cx1Arr[ j ] = aspectRatio * Math.cos(v - 0.01)
        cy1Arr[ j ] = Math.sin(v - 0.01)
        cx2Arr[ j ] = aspectRatio * Math.cos(v + 0.01)
        cy2Arr[ j ] = Math.sin(v + 0.01)
      }
    }

    for (i = 0; i < n; ++i) {
      k = i * 3
      l = k * radialSegments

      if (position) {
        vTangent.set(
                    tangent[ k ], tangent[ k + 1 ], tangent[ k + 2 ]
                )

        normX = normal[ k ]
        normY = normal[ k + 1 ]
        normZ = normal[ k + 2 ]

        biX = binormal[ k ]
        biY = binormal[ k + 1 ]
        biZ = binormal[ k + 2 ]

        posX = position[ k ]
        posY = position[ k + 1 ]
        posZ = position[ k + 2 ]

        radius = size[ i ]
      }

      for (j = 0; j < radialSegments; ++j) {
        s = l + j * 3

        if (position) {
          cx = -radius * cxArr[ j ] // TODO: Hack: Negating it so it faces outside.
          cy = radius * cyArr[ j ]

          cx1 = -radius * cx1Arr[ j ]
          cy1 = radius * cy1Arr[ j ]
          cx2 = -radius * cx2Arr[ j ]
          cy2 = radius * cy2Arr[ j ]

          meshPosition[ s ] = posX + cx * normX + cy * biX
          meshPosition[ s + 1 ] = posY + cx * normY + cy * biY
          meshPosition[ s + 2 ] = posZ + cx * normZ + cy * biZ

                    // TODO half of these are symmetric
          vMeshNormal.set(
                        // ellipse tangent approximated as vector from/to adjacent points
                        (cx2 * normX + cy2 * biX) -
                            (cx1 * normX + cy1 * biX),
                        (cx2 * normY + cy2 * biY) -
                            (cx1 * normY + cy1 * biY),
                        (cx2 * normZ + cy2 * biZ) -
                            (cx1 * normZ + cy1 * biZ)
                    ).cross(vTangent)

          meshNormal[ s ] = vMeshNormal.x
          meshNormal[ s + 1 ] = vMeshNormal.y
          meshNormal[ s + 2 ] = vMeshNormal.z
        }

        if (color) {
          meshColor[ s ] = color[ k ]
          meshColor[ s + 1 ] = color[ k + 1 ]
          meshColor[ s + 2 ] = color[ k + 2 ]
        }

        if (primitiveId) {
          meshPrimitiveId[ i * radialSegments + j ] = primitiveId[ i ]
        }
      }
    }

        // front cap

    k = 0
    l = n * 3 * radialSegments

    for (j = 0; j < radialSegments; ++j) {
      s = k + j * 3
      t = l + j * 3

      if (position) {
        meshPosition[ t ] = meshPosition[ s ]
        meshPosition[ t + 1 ] = meshPosition[ s + 1 ]
        meshPosition[ t + 2 ] = meshPosition[ s + 2 ]

        meshNormal[ t ] = tangent[ k ]
        meshNormal[ t + 1 ] = tangent[ k + 1 ]
        meshNormal[ t + 2 ] = tangent[ k + 2 ]
      }

      if (color) {
        meshColor[ t ] = meshColor[ s ]
        meshColor[ t + 1 ] = meshColor[ s + 1 ]
        meshColor[ t + 2 ] = meshColor[ s + 2 ]
      }

      if (primitiveId) {
        meshPrimitiveId[ n * radialSegments + j ] = meshPrimitiveId[ 0 + j ]
      }
    }

        // back cap

    k = (n - 1) * 3 * radialSegments
    l = (n + 1) * 3 * radialSegments

    for (j = 0; j < radialSegments; ++j) {
      s = k + j * 3
      t = l + j * 3

      if (position) {
        meshPosition[ t ] = meshPosition[ s ]
        meshPosition[ t + 1 ] = meshPosition[ s + 1 ]
        meshPosition[ t + 2 ] = meshPosition[ s + 2 ]

        meshNormal[ t ] = tangent[ n1 * 3 ]
        meshNormal[ t + 1 ] = tangent[ n1 * 3 + 1 ]
        meshNormal[ t + 2 ] = tangent[ n1 * 3 + 2 ]
      }

      if (color) {
        meshColor[ t ] = meshColor[ s ]
        meshColor[ t + 1 ] = meshColor[ s + 1 ]
        meshColor[ t + 2 ] = meshColor[ s + 2 ]
      }

      if (primitiveId) {
        meshPrimitiveId[ (n + 1) * radialSegments + j ] = meshPrimitiveId[ (n - 1) * radialSegments + j ]
      }
    }
  }

  makeIndex () {
    var meshIndex = this.meshIndex

    var n = this.size2
    var n1 = n - 1
    var capTriangles = this.capTriangles
    var radialSegments = this.radialSegments
    var radialSegments1 = this.radialSegments + 1

    var i, k, irs, irs1, l, j

    for (i = 0; i < n1; ++i) {
      k = i * radialSegments * 3 * 2

      irs = i * radialSegments
      irs1 = (i + 1) * radialSegments

      for (j = 0; j < radialSegments; ++j) {
        l = k + j * 3 * 2

                // meshIndex[ l + 0 ] = irs + ( ( j + 0 ) % radialSegments );
        meshIndex[ l ] = irs + j
        meshIndex[ l + 1 ] = irs + ((j + 1) % radialSegments)
                // meshIndex[ l + 2 ] = irs1 + ( ( j + 0 ) % radialSegments );
        meshIndex[ l + 2 ] = irs1 + j

                // meshIndex[ l + 3 ] = irs1 + ( ( j + 0 ) % radialSegments );
        meshIndex[ l + 3 ] = irs1 + j
        meshIndex[ l + 4 ] = irs + ((j + 1) % radialSegments)
        meshIndex[ l + 5 ] = irs1 + ((j + 1) % radialSegments)
      }
    }

        // capping

    var strip = [ 0 ]

    for (j = 1; j < radialSegments1 / 2; ++j) {
      strip.push(j)
      if (radialSegments - j !== j) {
        strip.push(radialSegments - j)
      }
    }

        // front cap

    l = n1 * radialSegments * 3 * 2
    k = n * radialSegments

    for (j = 0; j < strip.length - 2; ++j) {
      if (j % 2 === 0) {
        meshIndex[ l + j * 3 + 0 ] = k + strip[ j + 0 ]
        meshIndex[ l + j * 3 + 1 ] = k + strip[ j + 1 ]
        meshIndex[ l + j * 3 + 2 ] = k + strip[ j + 2 ]
      } else {
        meshIndex[ l + j * 3 + 0 ] = k + strip[ j + 2 ]
        meshIndex[ l + j * 3 + 1 ] = k + strip[ j + 1 ]
        meshIndex[ l + j * 3 + 2 ] = k + strip[ j + 0 ]
      }
    }

        // back cap

    l = n1 * radialSegments * 3 * 2 + 3 * capTriangles
    k = n * radialSegments + radialSegments

    for (j = 0; j < strip.length - 2; ++j) {
      if (j % 2 === 0) {
        meshIndex[ l + j * 3 + 0 ] = k + strip[ j + 0 ]
        meshIndex[ l + j * 3 + 1 ] = k + strip[ j + 1 ]
        meshIndex[ l + j * 3 + 2 ] = k + strip[ j + 2 ]
      } else {
        meshIndex[ l + j * 3 + 0 ] = k + strip[ j + 2 ]
        meshIndex[ l + j * 3 + 1 ] = k + strip[ j + 1 ]
        meshIndex[ l + j * 3 + 2 ] = k + strip[ j + 0 ]
      }
    }
  }
}

export default TubeMeshBuffer