Ejemplo n.º 1
0
 update(context, parent) {
   super.update(context, parent);
   if (this.hasChanged) {
     // The line should point the ground...
     let center = vec3.create();
     vec3.transformMat4(center, center, this.globalMatrix);
     let point = vec3.create();
     vec3.copy(point, center);
     point[1] = 0;
     // Scale the line to match the size
     this.guideLine.transform.scale[0] = center[1];
     // Here comes the hard part... setting rotation.
     let hand = vec4.fromValues(0, -1, 0, 0);
     let inv = mat4.create();
     mat4.invert(inv, this.globalMatrix);
     vec4.transformMat4(hand, hand, inv);
     vec4.normalize(hand, hand);
     quat.rotationTo(this.guideLine.transform.rotation, [1, 0, 0], hand);
     this.guideLine.transform.invalidate();
   }
 }
function projectPoint(p: Point, pixelPosMatrix: Float32Array) {
    const point = vec4.transformMat4([], [p.x, p.y, 0, 1], pixelPosMatrix);
    return new Point(point[0] / point[3], point[1] / point[3]);
}
// The code that utilizes Matrix4 does the same calculation as their mat4 counterparts,
// has lower performance but provides error checking.
// Uncomment when debugging
function calculateMatrixAndOffset(_ref) {
  var projectionMode = _ref.projectionMode,
      positionOrigin = _ref.positionOrigin,
      viewport = _ref.viewport,
      modelMatrix = _ref.modelMatrix;
  var viewMatrixUncentered = viewport.viewMatrixUncentered,
      viewMatrix = viewport.viewMatrix,
      projectionMatrix = viewport.projectionMatrix,
      viewProjectionMatrix = viewport.viewProjectionMatrix;


  var projectionCenter = void 0;
  var modelViewMatrix = void 0;

  switch (projectionMode) {

    case COORDINATE_SYSTEM.IDENTITY:
    case COORDINATE_SYSTEM.LNGLAT:
      projectionCenter = ZERO_VECTOR;
      // modelViewMatrix = new Matrix4(viewMatrix);
      modelViewMatrix = mat4.copy([], viewMatrix);
      break;

    // TODO: make lighitng work for meter offset mode
    case COORDINATE_SYSTEM.METER_OFFSETS:
      // Calculate transformed projectionCenter (in 64 bit precision)
      // This is the key to offset mode precision (avoids doing this
      // addition in 32 bit precision)
      var positionPixels = viewport.projectFlat(positionOrigin);
      // projectionCenter = new Matrix4(viewProjectionMatrix)
      //   .transformVector([positionPixels[0], positionPixels[1], 0.0, 1.0]);
      projectionCenter = vec4.transformMat4([], [positionPixels[0], positionPixels[1], 0.0, 1.0], viewProjectionMatrix);

      // Always apply uncentered projection matrix if available (shader adds center)
      // Zero out 4th coordinate ("after" model matrix) - avoids further translations
      // modelViewMatrix = new Matrix4(viewMatrixUncentered || viewMatrix)
      //   .multiplyRight(VECTOR_TO_POINT_MATRIX);
      modelViewMatrix = mat4.multiply([], viewMatrixUncentered || viewMatrix, VECTOR_TO_POINT_MATRIX);
      break;

    default:
      throw new Error('Unknown projection mode');
  }

  var viewMatrixInv = mat4.invert([], modelViewMatrix) || modelViewMatrix;

  if (modelMatrix) {
    // Apply model matrix if supplied
    // modelViewMatrix.multiplyRight(modelMatrix);
    mat4.multiply(modelViewMatrix, modelViewMatrix, modelMatrix);
  }

  // const modelViewProjectionMatrix = new Matrix4(projectionMatrix).multiplyRight(modelViewMatrix);
  var modelViewProjectionMatrix = mat4.multiply([], projectionMatrix, modelViewMatrix);
  var cameraPos = [viewMatrixInv[12], viewMatrixInv[13], viewMatrixInv[14]];

  return {
    modelViewMatrix: modelViewMatrix,
    modelViewProjectionMatrix: modelViewProjectionMatrix,
    projectionCenter: projectionCenter,
    cameraPos: cameraPos
  };
}
 value: function transformVector(matrix, vector) {
   var result = vec4.transformMat4([0, 0, 0, 0], vector, matrix);
   var scale = 1 / result[3];
   vec4.multiply(result, result, [scale, scale, scale, scale]);
   return result;
 }
Ejemplo n.º 5
0
  constructor(geometries, transforms, name) {
    super(name);
    this.type = [];
    // Attributes data can be stored linearly, without any sorting. However,
    // indices data must be sorted by types in order to reduce draw calls.

    // First, compute total vertices count of the geometries.
    // Compute the indices too.
    let verticesCount = geometries.reduce((before, geometry) =>
      before + geometry.getVertexCount()
    , 0);
    let indicesCount = geometries.reduce((before, geometry) =>
      before + geometry.getIndices().length
    , 0);
    this.verticesCount = verticesCount;
    // Then, create the indices - We must sort them in types to reduce
    // draw calls.
    this.indices = createIndicesArray(verticesCount, indicesCount);
    // Calculate total indices per type, this should be more efficient than
    // creating buffer for each type.
    let typeIndicesSize = {};
    for (let i = 0; i < geometries.length; ++i) {
      let geometry = geometries[i];
      if (Array.isArray(geometry.type)) {
        geometry.type.forEach(entry => {
          typeIndicesSize[entry.type] = (typeIndicesSize[entry.type] || 0) +
            entry.count;
        });
      } else {
        typeIndicesSize[geometry.type] = (typeIndicesSize[geometry.type] || 0) +
          geometry.indices.length;
      }
    }
    // Then, calculate each type's offset. (Order shouldn't really matter)
    let typeIndicesPos = {};
    let typeIndicesOffset = {};
    let indicesPos = 0;
    for (let key in typeIndicesSize) {
      typeIndicesPos[key] = indicesPos;
      typeIndicesOffset[key] = indicesPos;
      indicesPos += typeIndicesSize[key];
    }
    // Then, create attributes used by geometries. We have to handle axis
    // conflict and other stuff too.
    // If one geometry uses a attribute and others don't, other geometries
    // will be filled with 0 instead. (Default behavior of typed array)
    // Processing attributes after the indices can save one for loop
    // (it doesn't really matter though)
    this.attributes = {};
    let vertPos = 0;
    for (let i = 0; i < geometries.length; ++i) {
      let geometry = geometries[i];
      let transform = (transforms && transforms[i]) || {};
      // Calculate attributes
      let attribData = geometry.getAttributes();
      for (let key in transform) {
        if (attribData[key] == null) {
          // Dump constant data to the buffer...
          let axis = transform[key].length;
          let buffer;
          if (this.attributes[key] == null) {
            // Create buffer and put data
            // TODO support other than Float32Array
            buffer = new Float32Array(axis * verticesCount);
            // ....Then set the attributes.
            this.attributes[key] = {
              axis: axis,
              data: buffer
            };
          } else {
            // Do a simple type check
            let combinedData = this.attributes[key];
            if (combinedData.axis !== axis) {
              throw new Error('Vertices data axis mismatch');
            }
            buffer = combinedData.data;
            // If everything is okay, continue and put the data.
          }
          let size = geometry.getVertexCount();
          for (let i = 0; i < size; ++i) {
            buffer.set(transform[key], axis * (vertPos + i));
          }
        }
      }
      for (let key in attribData) {
        let data = attribData[key];
        let buffer;
        if (this.attributes[key] == null) {
          // Create buffer and put data
          // TODO support other than Float32Array
          buffer = new Float32Array(data.axis * verticesCount);
          // ....Then set the attributes.
          this.attributes[key] = {
            axis: data.axis,
            data: buffer
          };
        } else {
          // Do a simple type check
          let combinedData = this.attributes[key];
          if (combinedData.data.constructor !== data.data.constructor) {
            throw new Error('Vertices data type mismatch');
          }
          if (combinedData.axis !== data.axis) {
            throw new Error('Vertices data axis mismatch');
          }
          buffer = combinedData.data;
          // If everything is okay, continue and put the data.
        }
        // Overwrite buffer data. However, if transform is enabled, we have
        // to set them one by one.
        if (transform && transform[key]) {
          let size = geometry.getVertexCount();
          let attribTransform = transform[key];
          for (let i = 0; i < size; ++i) {
            let original = data.data.slice(data.axis * i,
              data.axis * i + data.axis);
            if (attribTransform instanceof Float32Array ||
              Array.isArray(attribTransform)
            ) {
              if (attribTransform.length === data.axis) {
                // Constant transform
                original = attribTransform;
              } else if (data.axis === 4 && attribTransform.length === 16) {
                // Matrix transform (4D, 4x4)
                vec4.transformMat4(original, original, attribTransform);
              } else if (data.axis === 3 && attribTransform.length === 16) {
                // Matrix transform (3D, 4x4)
                vec3.transformMat4(original, original, attribTransform);
              } else if (data.axis === 3 && attribTransform.length === 9) {
                // Matrix transform (3D, 3x3)
                vec3.transformMat3(original, original, attribTransform);
              } else if (data.axis === 2 && attribTransform.length === 9) {
                // Matrix transform (2D, 3x3)
                vec2.transformMat3(original, original, attribTransform);
              } else if (data.axis === 2 && attribTransform.length === 6) {
                // Matrix transform (2D, 2x3)
                vec2.transformMat2d(original, original, attribTransform);
              } else if (data.axis === 2 && attribTransform.length === 4) {
                // Matrix transform (2D, 2x2)
                vec2.transformMat2(original, original, attribTransform);
              } else {
                // Unsupported
                throw new Error('Unsupported array transform type');
              }
            } else if (typeof attribTransform === 'function') {
              // Run the function.
              original = attribTransform(original, i);
            } else {
              // Unsupported. what?
              throw new Error('Unsupported transform type');
            }
            buffer.set(original, data.axis * (vertPos + i));
          }
        } else {
          buffer.set(data.data, data.axis * vertPos);
        }
      }
      let types = geometry.type;
      if (!Array.isArray(geometry.type)) {
        types = [{
          type: geometry.type,
          first: 0,
          count: geometry.indices.length
        }];
      }
      let geomIndices = geometry.getIndices();
      types.forEach(entry => {
        // Calculate indices. In order to add vertex position to the indices,
        // we have to process one index at a time.
        let indicesPos = typeIndicesPos[entry.type];
        for (let j = 0; j < entry.count; ++j) {
          this.indices[indicesPos + j] =
            geomIndices[j + entry.first] + vertPos;
        }
        // Finally, increment the pointer.
        typeIndicesPos[entry.type] += entry.count;
      });
      vertPos += geometry.getVertexCount();
    }
    // ... Set the type.
    this.type = [];
    for (let key in typeIndicesSize) {
      this.type.push({
        first: typeIndicesOffset[key],
        count: typeIndicesSize[key],
        type: key
      });
    }
  }
Ejemplo n.º 6
0
export default function mousePick(renderer) {
  const gl = renderer.gl;
  // Generate box data we need
  // We're literally spamming drawing calls - but it doesn't matter because
  // that's not in the scope of this example.
  let meshList = [];
  for (let i = 0; i < 100; ++i) {
    let transform = new MeshTransform();
    transform.translate([
      Math.random() * 30 - 15,
      Math.random() * 30 - 15,
      Math.random() * 30 - 15
    ]);
    meshList.push({
      transform: transform,
      color: packColor(i)
    });
  }
  let box = renderer.geometries.create(calcNormals(boxGeom()));
  let texture = renderer.textures.create(require('../texture/2.png'));
  let borderShader = renderer.shaders.create(
    require('../shader/minimalBias.vert'),
    require('../shader/monoColor.frag')
  );
  let pickShader = renderer.shaders.create(
    require('../shader/minimal.vert'),
    require('../shader/monoColor.frag')
  );
  let shader = renderer.shaders.create(
    require('../shader/phong.vert'),
    require('../shader/phong.frag')
  );

  let pickTexture = renderer.textures.create(null, {
    format: gl.RGBA
  });
  let pickFramebuffer = renderer.framebuffers.create({
    color: pickTexture,
    depth: gl.DEPTH_COMPONENT16 // Automatically use renderbuffer
  });

  let align = true;
  let alignColor = '#ff0000';
  let alignDir = [1, 0, 0];

  let alignGeom = renderer.geometries.create({
    attributes: {
      aPosition: [[-1, 0, 0], [1, 0, 0]]
    },
    mode: gl.LINES
  });

  let selectedId = 0;
  let contextCache;
  let mouseDown = false;
  let mousePosX = 0;
  let mousePosY = 0;
  let relativeOffset = vec2.create();
  return {
    update(delta, context) {
      contextCache = context;
      renderer.render({
        options: {
          clearColor: new Float32Array([0, 0, 0, 1]),
          clearDepth: 1,
          cull: gl.BACK,
          depth: gl.LEQUAL
        },
        uniforms: Object.assign({}, context.camera, {
          uPointLight: [],
          uDirectionalLight: {
            direction: [0, 0.7 / 1.22, 1 / 1.22],
            color: '#aaaaaa',
            intensity: [0.3, 1.0, 1.0]
          }
        }),
        passes: [meshList.map((data, id) => ({
          shader: shader,
          geometry: box,
          uniforms: {
            uMaterial: {
              ambient: '#ffffff',
              diffuse: '#ffffff',
              specular: '#555555',
              shininess: 30
            },
            uDiffuseMap: texture,
            uModel: data.transform.get,
            uNormal: data.transform.getNormal
          },
          passes: id === selectedId ? [{
            options: {
              // depthMask: true,
              cull: gl.FRONT
            },
            uniforms: {
              uBias: [0.1, 0],
              uColor: '#ffffff'
            },
            shader: borderShader
          }, {}] : [{}]
        })), align && meshList[selectedId] != null && {
          shader: pickShader,
          geometry: alignGeom,
          uniforms: {
            uColor: alignColor,
            uModel: [
              alignDir[0] * 1000, alignDir[1] * 1000, alignDir[2] * 1000, 0,
              0, 0, 0, 0,
              0, 0, 0, 0,
              meshList[selectedId].transform.position[0],
              meshList[selectedId].transform.position[1],
              meshList[selectedId].transform.position[2],
              1
            ]
          }
        }]
      });
    },
    mousedown(e, ndc) {
      if (e.button !== 0) return;
      // Render mouse pick data
      renderer.render({
        options: {
          clearColor: new Float32Array([1, 1, 1, 1]),
          clearDepth: 1,
          cull: gl.BACK,
          depth: gl.LEQUAL
        },
        uniforms: contextCache.camera,
        passes: meshList.map(data => ({
          shader: pickShader,
          geometry: box,
          uniforms: {
            uColor: data.color,
            uModel: data.transform.get,
            uNormal: data.transform.getNormal
          }
        })),
        framebuffer: pickFramebuffer
      });
      // Get mouse pixel
      let pixel = new Uint8Array(4);
      pickFramebuffer.readPixelsRGBA(e.clientX,
        gl.drawingBufferHeight - e.clientY, 1, 1, pixel);
      selectedId = unpackColor(pixel);
      mousePosX = ndc[0];
      mousePosY = ndc[1];
      mouseDown = true;
      if (meshList[selectedId] == null) return;
      if (align) {
        // We're aligning to axis - Get relative offset from origin to clicked
        // point
        let transform = meshList[selectedId].transform;
        // Project current model position to projection space
        // (to get Z value)
        let perspPos = vec4.fromValues(0, 0, 0, 1);
        vec4.transformMat4(perspPos, perspPos, transform.get());
        vec4.transformMat4(perspPos, perspPos, contextCache.cameraObj.getPV());
        vec4.scale(perspPos, perspPos, 1 / perspPos[3]);
        // Last, store relative offset for future use
        vec2.subtract(relativeOffset, ndc, perspPos);
      }
    },
    keydown(e) {
      if (e.keyCode === 67) {
        align = false;
      } else if (e.keyCode === 88) {
        align = true;
        alignColor = '#ff0000';
        alignDir = [1, 0, 0];
      } else if (e.keyCode === 89) {
        align = true;
        alignColor = '#00ff00';
        alignDir = [0, 1, 0];
      } else if (e.keyCode === 90) {
        align = true;
        alignColor = '#0000ff';
        alignDir = [0, 0, 1];
      }
    },
    mouseup() {
      mouseDown = false;
    },
    mousemove(e, ndc) {
      if (!mouseDown) return;
      let deltaX = ndc[0] - mousePosX;
      let deltaY = ndc[1] - mousePosY;
      mousePosX = ndc[0];
      mousePosY = ndc[1];
      if (meshList[selectedId] == null) return;
      if (!align) {
        // Freestyle translation
        let transform = meshList[selectedId].transform;
        // Project current model position to projection space
        let perspPos = vec4.fromValues(0, 0, 0, 1);
        vec4.transformMat4(perspPos, perspPos, transform.get());
        vec4.transformMat4(perspPos, perspPos, contextCache.cameraObj.getPV());
        // Then move using delta value
        perspPos[0] += deltaX * perspPos[3];
        perspPos[1] += deltaY * perspPos[3];
        // Inverse-project to world space
        vec4.transformMat4(perspPos, perspPos, contextCache.cameraObj.
          getInverseProjection());
        vec4.transformMat4(perspPos, perspPos, contextCache.cameraObj.
          transform.get());
        // Last, write the pos to transform
        vec3.copy(transform.position, perspPos);
        transform.invalidate();
      } else {
        // How much should it move in viewport space in order to move (1, 0, 0)?
        let transform = meshList[selectedId].transform;
        // Project current model position to projection space
        let perspPos = vec4.fromValues(0, 0, 0, 1);
        vec4.transformMat4(perspPos, perspPos, transform.get());
        let addedPos = vec4.create();
        addedPos[3] = 1;
        vec3.add(addedPos, perspPos, alignDir);
        vec4.transformMat4(perspPos, perspPos, contextCache.cameraObj.getPV());
        vec4.transformMat4(addedPos, addedPos, contextCache.cameraObj.getPV());
        let centerPos = vec2.create();
        vec2.copy(centerPos, perspPos);
        vec2.scale(centerPos, centerPos, 1 / perspPos[3]);
        let dirPos = vec2.create();
        vec2.copy(dirPos, addedPos);
        vec2.scale(dirPos, dirPos, 1 / addedPos[3]);
        vec2.subtract(dirPos, dirPos, centerPos);
        let dirNorm = vec2.create();
        vec2.normalize(dirNorm, dirPos);
        // Now we've got everything, calculated required transform length
        // and translate to it
        let projected = vec2.create();
        vec2.subtract(projected, ndc, centerPos);
        vec2.subtract(projected, projected, relativeOffset);
        let dist = vec2.dot(projected, dirNorm);
        let transSize = dist / vec2.length(dirPos);
        let translation = vec3.create();
        vec3.copy(translation, alignDir);
        vec3.scale(translation, translation, transSize);
        vec3.add(transform.position, transform.position, translation);
        transform.invalidate();
      }
    }
  };
}
Ejemplo n.º 7
0
    calcOwnPosition () {
        var position = vec4.fromValues(0,0,0,1);
        vec4.transformMat4(position, position, this.placementMatrix);

        this.position = position;
    }
Ejemplo n.º 8
0
/*
 *  Update the `dynamicLayoutVertexBuffer` for the buffer with the correct glyph positions for the current map view.
 *  This is only run on labels that are aligned with lines. Horizontal labels are handled entirely in the shader.
 */
function updateLineLabels(bucket: SymbolBucket,
                          posMatrix: mat4,
                          painter: Painter,
                          isText: boolean,
                          labelPlaneMatrix: mat4,
                          glCoordMatrix: mat4,
                          pitchWithMap: boolean,
                          keepUpright: boolean) {

    const sizeData = isText ? bucket.textSizeData : bucket.iconSizeData;
    const partiallyEvaluatedSize = symbolSize.evaluateSizeForZoom(sizeData, painter.transform.zoom,
        symbolLayoutProperties.properties[isText ? 'text-size' : 'icon-size']);

    const clippingBuffer = [256 / painter.width * 2 + 1, 256 / painter.height * 2 + 1];

    const dynamicLayoutVertexArray = isText ?
        bucket.text.dynamicLayoutVertexArray :
        bucket.icon.dynamicLayoutVertexArray;
    dynamicLayoutVertexArray.clear();

    const lineVertexArray = bucket.lineVertexArray;
    const placedSymbols = isText ? bucket.text.placedSymbolArray : bucket.icon.placedSymbolArray;

    const aspectRatio = painter.transform.width / painter.transform.height;

    let useVertical = false;

    for (let s = 0; s < placedSymbols.length; s++) {
        const symbol: any = placedSymbols.get(s);
        // Don't do calculations for vertical glyphs unless the previous symbol was horizontal
        // and we determined that vertical glyphs were necessary.
        // Also don't do calculations for symbols that are collided and fully faded out
        if (symbol.hidden || symbol.writingMode === WritingMode.vertical && !useVertical) {
            hideGlyphs(symbol.numGlyphs, dynamicLayoutVertexArray);
            continue;
        }
        // Awkward... but we're counting on the paired "vertical" symbol coming immediately after its horizontal counterpart
        useVertical = false;

        const anchorPos = [symbol.anchorX, symbol.anchorY, 0, 1];
        vec4.transformMat4(anchorPos, anchorPos, posMatrix);

        // Don't bother calculating the correct point for invisible labels.
        if (!isVisible(anchorPos, clippingBuffer)) {
            hideGlyphs(symbol.numGlyphs, dynamicLayoutVertexArray);
            continue;
        }

        const cameraToAnchorDistance = anchorPos[3];
        const perspectiveRatio = 0.5 + 0.5 * (cameraToAnchorDistance / painter.transform.cameraToCenterDistance);

        const fontSize = symbolSize.evaluateSizeForFeature(sizeData, partiallyEvaluatedSize, symbol);
        const pitchScaledFontSize = pitchWithMap ?
            fontSize * perspectiveRatio :
            fontSize / perspectiveRatio;

        const tileAnchorPoint = new Point(symbol.anchorX, symbol.anchorY);
        const anchorPoint = project(tileAnchorPoint, labelPlaneMatrix).point;
        const projectionCache = {};

        const placeUnflipped: any = placeGlyphsAlongLine(symbol, pitchScaledFontSize, false /*unflipped*/, keepUpright, posMatrix, labelPlaneMatrix, glCoordMatrix,
            bucket.glyphOffsetArray, lineVertexArray, dynamicLayoutVertexArray, anchorPoint, tileAnchorPoint, projectionCache, aspectRatio);

        useVertical = placeUnflipped.useVertical;

        if (placeUnflipped.notEnoughRoom || useVertical ||
            (placeUnflipped.needsFlipping &&
             placeGlyphsAlongLine(symbol, pitchScaledFontSize, true /*flipped*/, keepUpright, posMatrix, labelPlaneMatrix, glCoordMatrix,
                 bucket.glyphOffsetArray, lineVertexArray, dynamicLayoutVertexArray, anchorPoint, tileAnchorPoint, projectionCache, aspectRatio).notEnoughRoom)) {
            hideGlyphs(symbol.numGlyphs, dynamicLayoutVertexArray);
        }
    }

    if (isText) {
        bucket.text.dynamicLayoutVertexBuffer.updateData(dynamicLayoutVertexArray);
    } else {
        bucket.icon.dynamicLayoutVertexBuffer.updateData(dynamicLayoutVertexArray);
    }
}
Ejemplo n.º 9
0
 coordinatePoint: function(coord) {
     var matrix = this.coordinatePointMatrix(coord.zoom);
     var p = vec4.transformMat4([], [coord.column, coord.row, 0, 1], matrix);
     return new Point(p[0] / p[3], p[1] / p[3]);
 },
Ejemplo n.º 10
0
 /**
  * Given a coordinate, return the screen point that corresponds to it
  * @param {Coordinate} coord
  * @returns {Point} screen point
  */
 coordinatePoint(coord: MercatorCoordinate) {
     const p = [coord.x * this.worldSize, coord.y * this.worldSize, 0, 1];
     vec4.transformMat4(p, p, this.pixelMatrix);
     return new Point(p[0] / p[3], p[1] / p[3]);
 }
Ejemplo n.º 11
0
    calcBoneMatrix (index, bone, animation, time, cameraPos, invPlacementMat){
        if (bone.isCalculated) return;
        var boneDefinition = this.m2Geom.m2File.bones[index];
        var parentBone = boneDefinition.parent_bone;
        var animationRecord = this.m2Geom.m2File.animations[animation];

        //2. Calc current transformation matrix

        var tranformMat = mat4.create();
        tranformMat = mat4.identity(tranformMat);

        if (parentBone>=0) {
            this.calcBoneMatrix(parentBone, this.bones[parentBone], animation, time, cameraPos, invPlacementMat);
            mat4.multiply(tranformMat, tranformMat, this.bones[parentBone].tranformMat);
        }

        mat4.translate(tranformMat, tranformMat, [
            boneDefinition.pivot.x,
            boneDefinition.pivot.y,
            boneDefinition.pivot.z,
            0
        ]);

        if (boneDefinition.translation.valuesPerAnimation.length > 0) {
            var transVec = this.getTimedValue(
                0,
                time,
                animationRecord.length,
                animation,
                boneDefinition.translation);

            if (transVec) {
                transVec = mat4.translate(tranformMat, tranformMat, [
                    transVec[0],
                    transVec[1],
                    transVec[2],
                    0
                ]);
                this.isAnimated = true;
            }
        }

        if (((boneDefinition.flags & 0x8) > 0) || ((boneDefinition.flags & 0x40) > 0)) {
            //From http://gamedev.stackexchange.com/questions/112270/calculating-rotation-matrix-for-an-object-relative-to-a-planets-surface-in-monog
            var modelForward = vec3.create();
            var cameraInlocalPos = vec4.create();

            vec4.copy(cameraInlocalPos, cameraPos);
            vec4.transformMat4(cameraInlocalPos, cameraInlocalPos, invPlacementMat);

            if (parentBone>=0) {
                vec4.transformMat4(cameraInlocalPos, cameraInlocalPos, this.bones[parentBone].inverTransforMat);
            }
            vec4.subtract(cameraInlocalPos, cameraInlocalPos, [
                boneDefinition.pivot.x,
                boneDefinition.pivot.y,
                boneDefinition.pivot.z,
                0
            ]);

            vec3.normalize(modelForward, cameraInlocalPos);

            if ((boneDefinition.flags & 0x40) > 0) {
                //Cylindrical billboard

                var modelUp = vec3.fromValues(0,0,1);

                var modelRight = vec3.create();
                vec3.cross(modelRight, modelUp, modelForward);
                vec3.normalize(modelRight, modelRight);

                vec3.cross(modelForward, modelRight, modelUp);
                vec3.normalize(modelForward, modelForward);

                vec3.cross(modelRight, modelUp, modelForward);
                vec3.normalize(modelRight, modelRight);

            } else {
                //Spherical billboard
                var modelRight = vec3.create();
                vec3.cross(modelRight, [0, 0, 1], modelForward);
                vec3.normalize(modelRight, modelRight);

                var modelUp = vec3.create();
                vec3.cross(modelUp, modelForward, modelRight);
                vec3.normalize(modelUp, modelUp);
            }


            mat4.multiply(tranformMat, tranformMat,
                [
                    modelForward[0],modelForward[1],modelForward[2],0,
                    modelRight[0],modelRight[1],modelRight[2],0,
                    modelUp[0],modelUp[1],modelUp[2],0,
                    0,0,0,1
                ]);
            this.isAnimated = true;
        } else if (boneDefinition.rotation.valuesPerAnimation.length > 0) {

            var quaternionVec4 = this.getTimedValue(
                1,
                time,
                animationRecord.length,
                animation,
                boneDefinition.rotation);

            if (quaternionVec4) {
                var orientMatrix = mat4.create();
                mat4.fromQuat(orientMatrix, quaternionVec4 );
                mat4.multiply(tranformMat, tranformMat, orientMatrix);
                this.isAnimated = true;
            }
        }

        if (boneDefinition.scale.valuesPerAnimation.length > 0) {

            var scaleVec3 = this.getTimedValue(
                0,
                time,
                animationRecord.length,
                animation,
                boneDefinition.scale);

            if (scaleVec3) {
                mat4.scale(tranformMat, tranformMat, [
                        scaleVec3[0],
                        scaleVec3[1],
                        scaleVec3[2]
                    ]
                );
            }
            this.isAnimated = true;
        }

        mat4.translate(tranformMat, tranformMat, [
            -boneDefinition.pivot.x,
            -boneDefinition.pivot.y,
            -boneDefinition.pivot.z,
            0
        ]);

        var invertTransformMat = mat4.create();
        mat4.invert(invertTransformMat, tranformMat);
        bone.tranformMat = tranformMat;
        bone.inverTransforMat = invertTransformMat;
    }
 // TODO - replace with math.gl
 transformVector(matrix, vector) {
   const result = vec4.transformMat4([0, 0, 0, 0], vector, matrix);
   const scale = 1 / result[3];
   vec4.multiply(result, result, [scale, scale, scale, scale]);
   return result;
 }