Beispiel #1
0
  /**
   * <code>rotateUpTo</code> is a utility function that alters the local rotation to point the Y
   * axis in the direction given by newUp.
   *
   * @param newUp the up vector to use - assumed to be a unit vector.
   */
  public void rotateUpTo(Vector3f newUp) {
    TempVars vars = TempVars.get();

    Vector3f compVecA = vars.vect1;
    Quaternion q = vars.quat1;

    // First figure out the current up vector.
    Vector3f upY = compVecA.set(Vector3f.UNIT_Y);
    Quaternion rot = localTransform.getRotation();
    rot.multLocal(upY);

    // get angle between vectors
    float angle = upY.angleBetween(newUp);

    // figure out rotation axis by taking cross product
    Vector3f rotAxis = upY.crossLocal(newUp).normalizeLocal();

    // Build a rotation quat and apply current local rotation.
    q.fromAngleNormalAxis(angle, rotAxis);
    q.mult(rot, rot);

    vars.release();

    setTransformRefresh();
  }
Beispiel #2
0
 /**
  * <code>lookAt</code> is a convienence method for auto-setting the quaternion based on a
  * direction and an up vector. It computes the rotation to transform the z-axis to point into
  * 'direction' and the y-axis to 'up'.
  *
  * @param direction where to look at in terms of local coordinates
  * @param up a vector indicating the local up direction. (typically {0, 1, 0} in jME.)
  */
 public void lookAt(Vector3f direction, Vector3f up) {
   TempVars vars = TempVars.get();
   assert vars.lock();
   vars.vect3.set(direction).normalizeLocal();
   vars.vect1.set(up).crossLocal(direction).normalizeLocal();
   vars.vect2.set(direction).crossLocal(vars.vect1).normalizeLocal();
   fromAxes(vars.vect1, vars.vect2, vars.vect3);
   assert vars.unlock();
 }
Beispiel #3
0
  /**
   * Rotates the spatial by the xAngle, yAngle and zAngle angles (in radians), (aka pitch, yaw,
   * roll) in the local coordinate space.
   *
   * @return The spatial on which this method is called, e.g <code>this</code>.
   */
  public Spatial rotate(float xAngle, float yAngle, float zAngle) {
    TempVars vars = TempVars.get();
    Quaternion q = vars.quat1;
    q.fromAngles(xAngle, yAngle, zAngle);
    rotate(q);
    vars.release();

    return this;
  }
 public int collideWith(Collidable other) {
   TempVars tempVars = TempVars.get();
   try {
     CollisionResults tempResults = tempVars.collisionResults;
     tempResults.clear();
     return collideWith(other, tempResults);
   } finally {
     tempVars.release();
   }
 }
  private void doTransforms(
      FloatBuffer bindBufPos,
      FloatBuffer bindBufNorm,
      FloatBuffer bufPos,
      FloatBuffer bufNorm,
      int start,
      int end,
      Matrix4f transform) {
    TempVars vars = TempVars.get();
    Vector3f pos = vars.vect1;
    Vector3f norm = vars.vect2;

    int length = (end - start) * 3;

    // offset is given in element units
    // convert to be in component units
    int offset = start * 3;
    bindBufPos.rewind();
    bindBufNorm.rewind();
    // bufPos.position(offset);
    // bufNorm.position(offset);
    bindBufPos.get(tmpFloat, 0, length);
    bindBufNorm.get(tmpFloatN, 0, length);
    int index = 0;
    while (index < length) {
      pos.x = tmpFloat[index];
      norm.x = tmpFloatN[index++];
      pos.y = tmpFloat[index];
      norm.y = tmpFloatN[index++];
      pos.z = tmpFloat[index];
      norm.z = tmpFloatN[index];

      transform.mult(pos, pos);
      transform.multNormal(norm, norm);

      index -= 2;
      tmpFloat[index] = pos.x;
      tmpFloatN[index++] = norm.x;
      tmpFloat[index] = pos.y;
      tmpFloatN[index++] = norm.y;
      tmpFloat[index] = pos.z;
      tmpFloatN[index++] = norm.z;
    }
    vars.release();
    bufPos.position(offset);
    // using bulk put as it's faster
    bufPos.put(tmpFloat, 0, length);
    bufNorm.position(offset);
    // using bulk put as it's faster
    bufNorm.put(tmpFloatN, 0, length);
  }
Beispiel #6
0
  /**
   * Updates the shadow camera to properly contain the given points (which contain the eye camera
   * frustum corners)
   *
   * @param shadowCam
   * @param points
   */
  public static void updateShadowCamera(Camera shadowCam, Vector3f[] points) {
    boolean ortho = shadowCam.isParallelProjection();
    shadowCam.setProjectionMatrix(null);

    if (ortho) {
      shadowCam.setFrustum(-1, 1, -1, 1, 1, -1);
    } else {
      shadowCam.setFrustumPerspective(45, 1, 1, 150);
    }

    Matrix4f viewProjMatrix = shadowCam.getViewProjectionMatrix();
    Matrix4f projMatrix = shadowCam.getProjectionMatrix();

    BoundingBox splitBB = computeBoundForPoints(points, viewProjMatrix);

    TempVars vars = TempVars.get();

    Vector3f splitMin = splitBB.getMin(vars.vect1);
    Vector3f splitMax = splitBB.getMax(vars.vect2);

    //        splitMin.z = 0;

    // Create the crop matrix.
    float scaleX, scaleY, scaleZ;
    float offsetX, offsetY, offsetZ;

    scaleX = 2.0f / (splitMax.x - splitMin.x);
    scaleY = 2.0f / (splitMax.y - splitMin.y);
    offsetX = -0.5f * (splitMax.x + splitMin.x) * scaleX;
    offsetY = -0.5f * (splitMax.y + splitMin.y) * scaleY;
    scaleZ = 1.0f / (splitMax.z - splitMin.z);
    offsetZ = -splitMin.z * scaleZ;

    Matrix4f cropMatrix = vars.tempMat4;
    cropMatrix.set(
        scaleX, 0f, 0f, offsetX, 0f, scaleY, 0f, offsetY, 0f, 0f, scaleZ, offsetZ, 0f, 0f, 0f, 1f);

    Matrix4f result = new Matrix4f();
    result.set(cropMatrix);
    result.multLocal(projMatrix);

    vars.release();
    shadowCam.setProjectionMatrix(result);
  }
Beispiel #7
0
  /** Computes the world transform of this Spatial in the most efficient manner possible. */
  void checkDoTransformUpdate() {
    if ((refreshFlags & RF_TRANSFORM) == 0) {
      return;
    }

    if (parent == null) {
      worldTransform.set(localTransform);
      refreshFlags &= ~RF_TRANSFORM;
    } else {
      TempVars vars = TempVars.get();

      Spatial[] stack = vars.spatialStack;
      Spatial rootNode = this;
      int i = 0;
      while (true) {
        Spatial hisParent = rootNode.parent;
        if (hisParent == null) {
          rootNode.worldTransform.set(rootNode.localTransform);
          rootNode.refreshFlags &= ~RF_TRANSFORM;
          i--;
          break;
        }

        stack[i] = rootNode;

        if ((hisParent.refreshFlags & RF_TRANSFORM) == 0) {
          break;
        }

        rootNode = hisParent;
        i++;
      }

      vars.release();

      for (int j = i; j >= 0; j--) {
        rootNode = stack[j];
        // rootNode.worldTransform.set(rootNode.localTransform);
        // rootNode.worldTransform.combineWithParent(rootNode.parent.worldTransform);
        // rootNode.refreshFlags &= ~RF_TRANSFORM;
        rootNode.updateWorldTransforms();
      }
    }
  }
  private void doCopyBuffer(FloatBuffer inBuf, int offset, FloatBuffer outBuf, int componentSize) {
    TempVars vars = TempVars.get();
    Vector3f pos = vars.vect1;

    // offset is given in element units
    // convert to be in component units
    offset *= componentSize;

    for (int i = 0; i < inBuf.limit() / componentSize; i++) {
      pos.x = inBuf.get(i * componentSize + 0);
      pos.y = inBuf.get(i * componentSize + 1);
      pos.z = inBuf.get(i * componentSize + 2);

      outBuf.put(offset + i * componentSize + 0, pos.x);
      outBuf.put(offset + i * componentSize + 1, pos.y);
      outBuf.put(offset + i * componentSize + 2, pos.z);
    }
    vars.release();
  }
Beispiel #9
0
  /**
   * <code>lookAt</code> is a convenience method for auto-setting the local rotation based on a
   * position in world space and an up vector. It computes the rotation to transform the z-axis to
   * point onto 'position' and the y-axis to 'up'. Unlike {@link
   * Quaternion#lookAt(com.jme3.math.Vector3f, com.jme3.math.Vector3f) } this method takes a world
   * position to look at and not a relative direction.
   *
   * <p>Note : 28/01/2013 this method has been fixed as it was not taking into account the parent
   * rotation. This was resulting in improper rotation when the spatial had rotated parent nodes.
   * This method is intended to work in world space, so no matter what parent graph the spatial has,
   * it will look at the given position in world space.
   *
   * @param position where to look at in terms of world coordinates
   * @param upVector a vector indicating the (local) up direction. (typically {0, 1, 0} in jME.)
   */
  public void lookAt(Vector3f position, Vector3f upVector) {
    Vector3f worldTranslation = getWorldTranslation();

    TempVars vars = TempVars.get();

    Vector3f compVecA = vars.vect4;

    compVecA.set(position).subtractLocal(worldTranslation);
    getLocalRotation().lookAt(compVecA, upVector);

    if (getParent() != null) {
      Quaternion rot = vars.quat1;
      rot = rot.set(parent.getWorldRotation()).inverseLocal().multLocal(getLocalRotation());
      rot.normalizeLocal();
      setLocalRotation(rot);
    }
    vars.release();
    setTransformRefresh();
  }
  /**
   * Creates a debug shape from the given collision shape. This is mostly used internally.<br>
   * To attach a debug shape to a physics object, call <code>attachDebugShape(AssetManager manager);
   * </code> on it.
   *
   * @param collisionShape
   * @return
   */
  public static Spatial getDebugShape(CollisionShape collisionShape) {
    if (collisionShape == null) {
      return null;
    }
    Spatial debugShape;
    if (collisionShape instanceof CompoundCollisionShape) {
      CompoundCollisionShape shape = (CompoundCollisionShape) collisionShape;
      List<ChildCollisionShape> children = shape.getChildren();
      Node node = new Node("DebugShapeNode");
      for (Iterator<ChildCollisionShape> it = children.iterator(); it.hasNext(); ) {
        ChildCollisionShape childCollisionShape = it.next();
        CollisionShape ccollisionShape = childCollisionShape.shape;
        Geometry geometry = createDebugShape(ccollisionShape);

        // apply translation
        geometry.setLocalTranslation(childCollisionShape.location);

        // apply rotation
        TempVars vars = TempVars.get();

        Matrix3f tempRot = vars.tempMat3;

        tempRot.set(geometry.getLocalRotation());
        childCollisionShape.rotation.mult(tempRot, tempRot);
        geometry.setLocalRotation(tempRot);

        vars.release();

        node.attachChild(geometry);
      }
      debugShape = node;
    } else {
      debugShape = createDebugShape(collisionShape);
    }
    if (debugShape == null) {
      return null;
    }
    debugShape.updateGeometricState();
    return debugShape;
  }
  /**
   * Enable or disable the ragdoll behaviour. if ragdollEnabled is true, the character motion will
   * only be powerd by physics else, the characted will be animated by the keyframe animation, but
   * will be able to physically interact with its physic environnement
   *
   * @param ragdollEnabled
   */
  protected void setMode(Mode mode) {
    this.mode = mode;
    AnimControl animControl = targetModel.getControl(AnimControl.class);
    animControl.setEnabled(mode == Mode.Kinematic);

    baseRigidBody.setKinematic(mode == Mode.Kinematic);
    TempVars vars = TempVars.get();

    for (PhysicsBoneLink link : boneLinks.values()) {
      link.rigidBody.setKinematic(mode == Mode.Kinematic);
      if (mode == Mode.Ragdoll) {
        Quaternion tmpRot1 = vars.quat1;
        Vector3f position = vars.vect1;
        // making sure that the ragdoll is at the correct place.
        matchPhysicObjectToBone(link, position, tmpRot1);
      }
    }
    vars.release();

    for (Bone bone : skeleton.getRoots()) {
      RagdollUtils.setUserControl(bone, mode == Mode.Ragdoll);
    }
  }
  /**
   * Smoothly blend from Ragdoll mode to Kinematic mode This is useful to blend ragdoll actual
   * position to a keyframe animation for example
   *
   * @param blendTime the blending time between ragdoll to anim.
   */
  public void blendToKinematicMode(float blendTime) {
    if (mode == Mode.Kinematic) {
      return;
    }
    blendedControl = true;
    this.blendTime = blendTime;
    mode = Mode.Kinematic;
    AnimControl animControl = targetModel.getControl(AnimControl.class);
    animControl.setEnabled(true);

    TempVars vars = TempVars.get();
    for (PhysicsBoneLink link : boneLinks.values()) {

      Vector3f p = link.rigidBody.getMotionState().getWorldLocation();
      Vector3f position = vars.vect1;

      targetModel.getWorldTransform().transformInverseVector(p, position);

      Quaternion q = link.rigidBody.getMotionState().getWorldRotationQuat();
      Quaternion q2 = vars.quat1;
      Quaternion q3 = vars.quat2;

      q2.set(q).multLocal(link.initalWorldRotation).normalizeLocal();
      q3.set(targetModel.getWorldRotation()).inverseLocal().mult(q2, q2);
      q2.normalizeLocal();
      link.startBlendingPos.set(position);
      link.startBlendingRot.set(q2);
      link.rigidBody.setKinematic(true);
    }
    vars.release();

    for (Bone bone : skeleton.getRoots()) {
      RagdollUtils.setUserControl(bone, false);
    }

    blendStart = 0;
  }
Beispiel #13
0
  /**
   * Compute bounds from an array of points
   *
   * @param pts
   * @param mat
   * @return
   */
  public static BoundingBox computeBoundForPoints(Vector3f[] pts, Matrix4f mat) {
    Vector3f min = new Vector3f(Vector3f.POSITIVE_INFINITY);
    Vector3f max = new Vector3f(Vector3f.NEGATIVE_INFINITY);
    TempVars vars = TempVars.get();
    Vector3f temp = vars.vect1;

    for (int i = 0; i < pts.length; i++) {
      float w = mat.multProj(pts[i], temp);

      temp.x /= w;
      temp.y /= w;
      // Why was this commented out?
      temp.z /= w;

      min.minLocal(temp);
      max.maxLocal(temp);
    }
    vars.release();
    Vector3f center = min.add(max).multLocal(0.5f);
    Vector3f extent = max.subtract(min).multLocal(0.5f);
    // Nehon 08/18/2010 : Added an offset to the extend to avoid banding artifacts when the frustum
    // are aligned
    return new BoundingBox(center, extent.x + 2.0f, extent.y + 2.0f, extent.z + 2.5f);
  }
Beispiel #14
0
  protected void renderMultipassLighting(Shader shader, Geometry g, RenderManager rm) {

    Renderer r = rm.getRenderer();
    LightList lightList = g.getWorldLightList();
    Uniform lightDir = shader.getUniform("g_LightDirection");
    Uniform lightColor = shader.getUniform("g_LightColor");
    Uniform lightPos = shader.getUniform("g_LightPosition");
    Uniform ambientColor = shader.getUniform("g_AmbientLightColor");
    boolean isFirstLight = true;
    boolean isSecondLight = false;

    for (int i = 0; i < lightList.size(); i++) {
      Light l = lightList.get(i);
      if (l instanceof AmbientLight) {
        continue;
      }

      if (isFirstLight) {
        // set ambient color for first light only
        ambientColor.setValue(VarType.Vector4, getAmbientColor(lightList));
        isFirstLight = false;
        isSecondLight = true;
      } else if (isSecondLight) {
        ambientColor.setValue(VarType.Vector4, ColorRGBA.Black);
        // apply additive blending for 2nd and future lights
        r.applyRenderState(additiveLight);
        isSecondLight = false;
      }

      TempVars vars = TempVars.get();
      Quaternion tmpLightDirection = vars.quat1;
      Quaternion tmpLightPosition = vars.quat2;
      ColorRGBA tmpLightColor = vars.color;
      Vector4f tmpVec = vars.vect4f;

      ColorRGBA color = l.getColor();
      tmpLightColor.set(color);
      tmpLightColor.a = l.getType().getId();
      lightColor.setValue(VarType.Vector4, tmpLightColor);

      switch (l.getType()) {
        case Directional:
          DirectionalLight dl = (DirectionalLight) l;
          Vector3f dir = dl.getDirection();

          tmpLightPosition.set(dir.getX(), dir.getY(), dir.getZ(), -1);
          lightPos.setValue(VarType.Vector4, tmpLightPosition);
          tmpLightDirection.set(0, 0, 0, 0);
          lightDir.setValue(VarType.Vector4, tmpLightDirection);
          break;
        case Point:
          PointLight pl = (PointLight) l;
          Vector3f pos = pl.getPosition();
          float invRadius = pl.getInvRadius();

          tmpLightPosition.set(pos.getX(), pos.getY(), pos.getZ(), invRadius);
          lightPos.setValue(VarType.Vector4, tmpLightPosition);
          tmpLightDirection.set(0, 0, 0, 0);
          lightDir.setValue(VarType.Vector4, tmpLightDirection);
          break;
        case Spot:
          SpotLight sl = (SpotLight) l;
          Vector3f pos2 = sl.getPosition();
          Vector3f dir2 = sl.getDirection();
          float invRange = sl.getInvSpotRange();
          float spotAngleCos = sl.getPackedAngleCos();

          tmpLightPosition.set(pos2.getX(), pos2.getY(), pos2.getZ(), invRange);
          lightPos.setValue(VarType.Vector4, tmpLightPosition);

          // We transform the spot directoin in view space here to save 5 varying later in the
          // lighting shader
          // one vec4 less and a vec4 that becomes a vec3
          // the downside is that spotAngleCos decoding happen now in the frag shader.
          tmpVec.set(dir2.getX(), dir2.getY(), dir2.getZ(), 0);
          rm.getCurrentCamera().getViewMatrix().mult(tmpVec, tmpVec);
          tmpLightDirection.set(tmpVec.getX(), tmpVec.getY(), tmpVec.getZ(), spotAngleCos);

          lightDir.setValue(VarType.Vector4, tmpLightDirection);

          break;
        default:
          throw new UnsupportedOperationException("Unknown type of light: " + l.getType());
      }
      vars.release();
      r.setShader(shader);
      r.renderMesh(g.getMesh(), g.getLodLevel(), 1);
    }

    if (isFirstLight && lightList.size() > 0) {
      // There are only ambient lights in the scene. Render
      // a dummy "normal light" so we can see the ambient
      ambientColor.setValue(VarType.Vector4, getAmbientColor(lightList));
      lightColor.setValue(VarType.Vector4, ColorRGBA.BlackNoAlpha);
      lightPos.setValue(VarType.Vector4, nullDirLight);
      r.setShader(shader);
      r.renderMesh(g.getMesh(), g.getLodLevel(), 1);
    }
  }
Beispiel #15
0
  /**
   * Updates the shadow camera to properly contain the given points (which contain the eye camera
   * frustum corners) and the shadow occluder objects.
   *
   * @param occluders
   * @param shadowCam
   * @param points
   */
  public static void updateShadowCamera(
      GeometryList occluders,
      GeometryList receivers,
      Camera shadowCam,
      Vector3f[] points,
      GeometryList splitOccluders) {

    boolean ortho = shadowCam.isParallelProjection();

    shadowCam.setProjectionMatrix(null);

    if (ortho) {
      shadowCam.setFrustum(-1, 1, -1, 1, 1, -1);
    }

    // create transform to rotate points to viewspace
    Matrix4f viewProjMatrix = shadowCam.getViewProjectionMatrix();

    BoundingBox splitBB = computeBoundForPoints(points, viewProjMatrix);

    ArrayList<BoundingVolume> visRecvList = new ArrayList<BoundingVolume>();
    for (int i = 0; i < receivers.size(); i++) {
      // convert bounding box to light's viewproj space
      Geometry receiver = receivers.get(i);
      BoundingVolume bv = receiver.getWorldBound();
      BoundingVolume recvBox = bv.transform(viewProjMatrix, null);

      if (splitBB.intersects(recvBox)) {
        visRecvList.add(recvBox);
      }
    }

    ArrayList<BoundingVolume> visOccList = new ArrayList<BoundingVolume>();
    for (int i = 0; i < occluders.size(); i++) {
      // convert bounding box to light's viewproj space
      Geometry occluder = occluders.get(i);
      BoundingVolume bv = occluder.getWorldBound();
      BoundingVolume occBox = bv.transform(viewProjMatrix, null);

      boolean intersects = splitBB.intersects(occBox);
      if (!intersects && occBox instanceof BoundingBox) {
        BoundingBox occBB = (BoundingBox) occBox;
        // Kirill 01/10/2011
        // Extend the occluder further into the frustum
        // This fixes shadow dissapearing issues when
        // the caster itself is not in the view camera
        // but its shadow is in the camera
        //      The number is in world units
        occBB.setZExtent(occBB.getZExtent() + 50);
        occBB.setCenter(occBB.getCenter().addLocal(0, 0, 25));
        if (splitBB.intersects(occBB)) {
          // To prevent extending the depth range too much
          // We return the bound to its former shape
          // Before adding it
          occBB.setZExtent(occBB.getZExtent() - 50);
          occBB.setCenter(occBB.getCenter().subtractLocal(0, 0, 25));
          visOccList.add(occBox);
          if (splitOccluders != null) {
            splitOccluders.add(occluder);
          }
        }
      } else if (intersects) {
        visOccList.add(occBox);
        if (splitOccluders != null) {
          splitOccluders.add(occluder);
        }
      }
    }

    BoundingBox casterBB = computeUnionBound(visOccList);
    BoundingBox receiverBB = computeUnionBound(visRecvList);

    // Nehon 08/18/2010 this is to avoid shadow bleeding when the ground is set to only receive
    // shadows
    if (visOccList.size() != visRecvList.size()) {
      casterBB.setXExtent(casterBB.getXExtent() + 2.0f);
      casterBB.setYExtent(casterBB.getYExtent() + 2.0f);
      casterBB.setZExtent(casterBB.getZExtent() + 2.0f);
    }

    TempVars vars = TempVars.get();

    Vector3f casterMin = casterBB.getMin(vars.vect1);
    Vector3f casterMax = casterBB.getMax(vars.vect2);

    Vector3f receiverMin = receiverBB.getMin(vars.vect3);
    Vector3f receiverMax = receiverBB.getMax(vars.vect4);

    Vector3f splitMin = splitBB.getMin(vars.vect5);
    Vector3f splitMax = splitBB.getMax(vars.vect6);

    splitMin.z = 0;

    //        if (!ortho) {
    //            shadowCam.setFrustumPerspective(45, 1, 1, splitMax.z);
    //        }

    Matrix4f projMatrix = shadowCam.getProjectionMatrix();

    Vector3f cropMin = vars.vect7;
    Vector3f cropMax = vars.vect8;

    // IMPORTANT: Special handling for Z values
    cropMin.x = max(max(casterMin.x, receiverMin.x), splitMin.x);
    cropMax.x = min(min(casterMax.x, receiverMax.x), splitMax.x);

    cropMin.y = max(max(casterMin.y, receiverMin.y), splitMin.y);
    cropMax.y = min(min(casterMax.y, receiverMax.y), splitMax.y);

    cropMin.z = min(casterMin.z, splitMin.z);
    cropMax.z = min(receiverMax.z, splitMax.z);

    // Create the crop matrix.
    float scaleX, scaleY, scaleZ;
    float offsetX, offsetY, offsetZ;

    scaleX = (2.0f) / (cropMax.x - cropMin.x);
    scaleY = (2.0f) / (cropMax.y - cropMin.y);

    offsetX = -0.5f * (cropMax.x + cropMin.x) * scaleX;
    offsetY = -0.5f * (cropMax.y + cropMin.y) * scaleY;

    scaleZ = 1.0f / (cropMax.z - cropMin.z);
    offsetZ = -cropMin.z * scaleZ;

    Matrix4f cropMatrix = vars.tempMat4;
    cropMatrix.set(
        scaleX, 0f, 0f, offsetX, 0f, scaleY, 0f, offsetY, 0f, 0f, scaleZ, offsetZ, 0f, 0f, 0f, 1f);

    Matrix4f result = new Matrix4f();
    result.set(cropMatrix);
    result.multLocal(projMatrix);
    vars.release();

    shadowCam.setProjectionMatrix(result);
  }
Beispiel #16
0
  /**
   * Specific method for skinning with tangents to avoid cluttering the classic skinning calculation
   * with null checks that would slow down the process even if tangents don't have to be computed.
   * Also the iteration has additional indexes since tangent has 4 components instead of 3 for pos
   * and norm
   *
   * @param maxWeightsPerVert maximum number of weights per vertex
   * @param mesh the mesh
   * @param offsetMatrices the offsetMaytrices to apply
   * @param tb the tangent vertexBuffer
   */
  private void applySkinningTangents(Mesh mesh, Matrix4f[] offsetMatrices, VertexBuffer tb) {
    int maxWeightsPerVert = mesh.getMaxNumWeights();

    if (maxWeightsPerVert <= 0) {
      throw new IllegalStateException("Max weights per vert is incorrectly set!");
    }

    int fourMinusMaxWeights = 4 - maxWeightsPerVert;

    // NOTE: This code assumes the vertex buffer is in bind pose
    // resetToBind() has been called this frame
    VertexBuffer vb = mesh.getBuffer(Type.Position);
    FloatBuffer fvb = (FloatBuffer) vb.getData();
    fvb.rewind();

    VertexBuffer nb = mesh.getBuffer(Type.Normal);

    FloatBuffer fnb = (FloatBuffer) nb.getData();
    fnb.rewind();

    FloatBuffer ftb = (FloatBuffer) tb.getData();
    ftb.rewind();

    // get boneIndexes and weights for mesh
    ByteBuffer ib = (ByteBuffer) mesh.getBuffer(Type.BoneIndex).getData();
    FloatBuffer wb = (FloatBuffer) mesh.getBuffer(Type.BoneWeight).getData();

    ib.rewind();
    wb.rewind();

    float[] weights = wb.array();
    byte[] indices = ib.array();
    int idxWeights = 0;

    TempVars vars = TempVars.get();

    float[] posBuf = vars.skinPositions;
    float[] normBuf = vars.skinNormals;
    float[] tanBuf = vars.skinTangents;

    int iterations = (int) FastMath.ceil(fvb.capacity() / ((float) posBuf.length));
    int bufLength = 0;
    int tanLength = 0;
    for (int i = iterations - 1; i >= 0; i--) {
      // read next set of positions and normals from native buffer
      bufLength = Math.min(posBuf.length, fvb.remaining());
      tanLength = Math.min(tanBuf.length, ftb.remaining());
      fvb.get(posBuf, 0, bufLength);
      fnb.get(normBuf, 0, bufLength);
      ftb.get(tanBuf, 0, tanLength);
      int verts = bufLength / 3;
      int idxPositions = 0;
      // tangents has their own index because of the 4 components
      int idxTangents = 0;

      // iterate vertices and apply skinning transform for each effecting bone
      for (int vert = verts - 1; vert >= 0; vert--) {
        float nmx = normBuf[idxPositions];
        float vtx = posBuf[idxPositions++];
        float nmy = normBuf[idxPositions];
        float vty = posBuf[idxPositions++];
        float nmz = normBuf[idxPositions];
        float vtz = posBuf[idxPositions++];

        float tnx = tanBuf[idxTangents++];
        float tny = tanBuf[idxTangents++];
        float tnz = tanBuf[idxTangents++];

        // skipping the 4th component of the tangent since it doesn't have to be transformed
        idxTangents++;

        float rx = 0, ry = 0, rz = 0, rnx = 0, rny = 0, rnz = 0, rtx = 0, rty = 0, rtz = 0;

        for (int w = maxWeightsPerVert - 1; w >= 0; w--) {
          float weight = weights[idxWeights];
          Matrix4f mat = offsetMatrices[indices[idxWeights++]];

          rx += (mat.m00 * vtx + mat.m01 * vty + mat.m02 * vtz + mat.m03) * weight;
          ry += (mat.m10 * vtx + mat.m11 * vty + mat.m12 * vtz + mat.m13) * weight;
          rz += (mat.m20 * vtx + mat.m21 * vty + mat.m22 * vtz + mat.m23) * weight;

          rnx += (nmx * mat.m00 + nmy * mat.m01 + nmz * mat.m02) * weight;
          rny += (nmx * mat.m10 + nmy * mat.m11 + nmz * mat.m12) * weight;
          rnz += (nmx * mat.m20 + nmy * mat.m21 + nmz * mat.m22) * weight;

          rtx += (tnx * mat.m00 + tny * mat.m01 + tnz * mat.m02) * weight;
          rty += (tnx * mat.m10 + tny * mat.m11 + tnz * mat.m12) * weight;
          rtz += (tnx * mat.m20 + tny * mat.m21 + tnz * mat.m22) * weight;
        }

        idxWeights += fourMinusMaxWeights;

        idxPositions -= 3;

        normBuf[idxPositions] = rnx;
        posBuf[idxPositions++] = rx;
        normBuf[idxPositions] = rny;
        posBuf[idxPositions++] = ry;
        normBuf[idxPositions] = rnz;
        posBuf[idxPositions++] = rz;

        idxTangents -= 4;

        tanBuf[idxTangents++] = rtx;
        tanBuf[idxTangents++] = rty;
        tanBuf[idxTangents++] = rtz;

        // once again skipping the 4th component of the tangent
        idxTangents++;
      }

      fvb.position(fvb.position() - bufLength);
      fvb.put(posBuf, 0, bufLength);
      fnb.position(fnb.position() - bufLength);
      fnb.put(normBuf, 0, bufLength);
      ftb.position(ftb.position() - tanLength);
      ftb.put(tanBuf, 0, tanLength);
    }

    vars.release();

    vb.updateData(fvb);
    nb.updateData(fnb);
    tb.updateData(ftb);
  }
  public void update(float tpf) {
    if (!enabled) {
      return;
    }
    TempVars vars = TempVars.get();

    Quaternion tmpRot1 = vars.quat1;
    Quaternion tmpRot2 = vars.quat2;

    // if the ragdoll has the control of the skeleton, we update each bone with its position in
    // physic world space.
    if (mode == mode.Ragdoll && targetModel.getLocalTranslation().equals(modelPosition)) {
      for (PhysicsBoneLink link : boneLinks.values()) {

        Vector3f position = vars.vect1;

        // retrieving bone position in physic world space
        Vector3f p = link.rigidBody.getMotionState().getWorldLocation();
        // transforming this position with inverse transforms of the model
        targetModel.getWorldTransform().transformInverseVector(p, position);

        // retrieving bone rotation in physic world space
        Quaternion q = link.rigidBody.getMotionState().getWorldRotationQuat();

        // multiplying this rotation by the initialWorld rotation of the bone,
        // then transforming it with the inverse world rotation of the model
        tmpRot1.set(q).multLocal(link.initalWorldRotation);
        tmpRot2.set(targetModel.getWorldRotation()).inverseLocal().mult(tmpRot1, tmpRot1);
        tmpRot1.normalizeLocal();

        // if the bone is the root bone, we apply the physic's transform to the model, so its
        // position and rotation are correctly updated
        if (link.bone.getParent() == null) {

          // offsetting the physic's position/rotation by the root bone inverse model space
          // position/rotaion
          modelPosition.set(p).subtractLocal(link.bone.getWorldBindPosition());
          targetModel
              .getParent()
              .getWorldTransform()
              .transformInverseVector(modelPosition, modelPosition);
          modelRotation
              .set(q)
              .multLocal(tmpRot2.set(link.bone.getWorldBindRotation()).inverseLocal());

          // applying transforms to the model
          targetModel.setLocalTranslation(modelPosition);

          targetModel.setLocalRotation(modelRotation);

          // Applying computed transforms to the bone
          link.bone.setUserTransformsWorld(position, tmpRot1);

        } else {
          // if boneList is empty, this means that every bone in the ragdoll has a collision shape,
          // so we just update the bone position
          if (boneList.isEmpty()) {
            link.bone.setUserTransformsWorld(position, tmpRot1);
          } else {
            // boneList is not empty, this means some bones of the skeleton might not be associated
            // with a collision shape.
            // So we update them recusively
            RagdollUtils.setTransform(link.bone, position, tmpRot1, false, boneList);
          }
        }
      }
    } else {
      // the ragdoll does not have the controll, so the keyframed animation updates the physic
      // position of the physic bonces
      for (PhysicsBoneLink link : boneLinks.values()) {

        Vector3f position = vars.vect1;

        // if blended control this means, keyframed animation is updating the skeleton,
        // but to allow smooth transition, we blend this transformation with the saved position of
        // the ragdoll
        if (blendedControl) {
          Vector3f position2 = vars.vect2;
          // initializing tmp vars with the start position/rotation of the ragdoll
          position.set(link.startBlendingPos);
          tmpRot1.set(link.startBlendingRot);

          // interpolating between ragdoll position/rotation and keyframed position/rotation
          tmpRot2.set(tmpRot1).nlerp(link.bone.getModelSpaceRotation(), blendStart / blendTime);
          position2
              .set(position)
              .interpolate(link.bone.getModelSpacePosition(), blendStart / blendTime);
          tmpRot1.set(tmpRot2);
          position.set(position2);

          // updating bones transforms
          if (boneList.isEmpty()) {
            // we ensure we have the control to update the bone
            link.bone.setUserControl(true);
            link.bone.setUserTransformsWorld(position, tmpRot1);
            // we give control back to the key framed animation.
            link.bone.setUserControl(false);
          } else {
            RagdollUtils.setTransform(link.bone, position, tmpRot1, true, boneList);
          }
        }
        // setting skeleton transforms to the ragdoll
        matchPhysicObjectToBone(link, position, tmpRot1);
        modelPosition.set(targetModel.getLocalTranslation());
      }

      // time control for blending
      if (blendedControl) {
        blendStart += tpf;
        if (blendStart > blendTime) {
          blendedControl = false;
        }
      }
    }
    vars.release();
  }
  private void doTransformsTangents(
      FloatBuffer bindBufPos,
      FloatBuffer bindBufNorm,
      FloatBuffer bindBufTangents,
      FloatBuffer bufPos,
      FloatBuffer bufNorm,
      FloatBuffer bufTangents,
      int start,
      int end,
      Matrix4f transform) {
    TempVars vars = TempVars.get();
    Vector3f pos = vars.vect1;
    Vector3f norm = vars.vect2;
    Vector3f tan = vars.vect3;

    int length = (end - start) * 3;
    int tanLength = (end - start) * 4;

    // offset is given in element units
    // convert to be in component units
    int offset = start * 3;
    int tanOffset = start * 4;

    bindBufPos.rewind();
    bindBufNorm.rewind();
    bindBufTangents.rewind();
    bindBufPos.get(tmpFloat, 0, length);
    bindBufNorm.get(tmpFloatN, 0, length);
    bindBufTangents.get(tmpFloatT, 0, tanLength);

    int index = 0;
    int tanIndex = 0;
    while (index < length) {
      pos.x = tmpFloat[index];
      norm.x = tmpFloatN[index++];
      pos.y = tmpFloat[index];
      norm.y = tmpFloatN[index++];
      pos.z = tmpFloat[index];
      norm.z = tmpFloatN[index];

      tan.x = tmpFloatT[tanIndex++];
      tan.y = tmpFloatT[tanIndex++];
      tan.z = tmpFloatT[tanIndex++];

      transform.mult(pos, pos);
      transform.multNormal(norm, norm);
      transform.multNormal(tan, tan);

      index -= 2;
      tanIndex -= 3;

      tmpFloat[index] = pos.x;
      tmpFloatN[index++] = norm.x;
      tmpFloat[index] = pos.y;
      tmpFloatN[index++] = norm.y;
      tmpFloat[index] = pos.z;
      tmpFloatN[index++] = norm.z;

      tmpFloatT[tanIndex++] = tan.x;
      tmpFloatT[tanIndex++] = tan.y;
      tmpFloatT[tanIndex++] = tan.z;

      // Skipping 4th element of tangent buffer (handedness)
      tanIndex++;
    }
    vars.release();
    bufPos.position(offset);
    // using bulk put as it's faster
    bufPos.put(tmpFloat, 0, length);
    bufNorm.position(offset);
    // using bulk put as it's faster
    bufNorm.put(tmpFloatN, 0, length);
    bufTangents.position(tanOffset);
    // using bulk put as it's faster
    bufTangents.put(tmpFloatT, 0, tanLength);
  }
Beispiel #19
0
  public final int intersectWhere(
      Collidable col,
      BoundingBox box,
      Matrix4f worldMatrix,
      BIHTree tree,
      CollisionResults results) {

    TempVars vars = TempVars.get();
    ArrayList<BIHStackData> stack = vars.bihStack;
    stack.clear();

    float[] minExts = {
      box.getCenter().x - box.getXExtent(),
      box.getCenter().y - box.getYExtent(),
      box.getCenter().z - box.getZExtent()
    };

    float[] maxExts = {
      box.getCenter().x + box.getXExtent(),
      box.getCenter().y + box.getYExtent(),
      box.getCenter().z + box.getZExtent()
    };

    // stack.add(new BIHStackData(this, 0, 0));
    vars.addStackData(this, 0f, 0f);

    Triangle t = new Triangle();
    int cols = 0;

    stackloop:
    while (stack.size() > 0) {
      BIHNode node = stack.remove(stack.size() - 1).node;

      while (node.axis != 3) {
        int a = node.axis;

        float maxExt = maxExts[a];
        float minExt = minExts[a];

        if (node.leftPlane < node.rightPlane) {
          // means there's a gap in the middle
          // if the box is in that gap, we stop there
          if (minExt > node.leftPlane && maxExt < node.rightPlane) {
            continue stackloop;
          }
        }

        if (maxExt < node.rightPlane) {
          node = node.left;
        } else if (minExt > node.leftPlane) {
          node = node.right;
        } else {
          // stack.add(new BIHStackData(node.right, 0, 0));
          vars.addStackData(node.right, 0f, 0f);
          node = node.left;
        }
        //                if (maxExt < node.leftPlane
        //                 && maxExt < node.rightPlane){
        //                    node = node.left;
        //                }else if (minExt > node.leftPlane
        //                       && minExt > node.rightPlane){
        //                    node = node.right;
        //                }else{

        //                }
      }

      for (int i = node.leftIndex; i <= node.rightIndex; i++) {
        tree.getTriangle(i, t.get1(), t.get2(), t.get3());
        if (worldMatrix != null) {
          worldMatrix.mult(t.get1(), t.get1());
          worldMatrix.mult(t.get2(), t.get2());
          worldMatrix.mult(t.get3(), t.get3());
        }

        int added = col.collideWith(t, results);

        if (added > 0) {
          int index = tree.getTriangleIndex(i);
          int start = results.size() - added;

          for (int j = start; j < results.size(); j++) {
            CollisionResult cr = results.getCollisionDirect(j);
            cr.setTriangleIndex(index);
          }

          cols += added;
        }
      }
    }
    vars.release();
    return cols;
  }
Beispiel #20
0
  public final int intersectWhere(
      Ray r,
      Matrix4f worldMatrix,
      BIHTree tree,
      float sceneMin,
      float sceneMax,
      CollisionResults results) {

    TempVars vars = TempVars.get();
    ArrayList<BIHStackData> stack = vars.bihStack;
    stack.clear();

    //        float tHit = Float.POSITIVE_INFINITY;

    Vector3f o = vars.vect1.set(r.getOrigin());
    Vector3f d = vars.vect2.set(r.getDirection());

    Matrix4f inv = vars.tempMat4.set(worldMatrix).invertLocal();

    inv.mult(r.getOrigin(), r.getOrigin());

    // Fixes rotation collision bug
    inv.multNormal(r.getDirection(), r.getDirection());
    //        inv.multNormalAcross(r.getDirection(), r.getDirection());

    // this is a no-no: allocating float arrays for immediate use? blarny!
    /*float[] origins = {r.getOrigin().x,
        r.getOrigin().y,
        r.getOrigin().z};

    float[] invDirections = {1f / r.getDirection().x,
        1f / r.getDirection().y,
        1f / r.getDirection().z};*/

    r.getDirection().normalizeLocal();

    Vector3f v1 = vars.vect3, v2 = vars.vect4, v3 = vars.vect5;
    int cols = 0;

    // stack.add(new BIHStackData(this, sceneMin, sceneMax));
    vars.addStackData(this, sceneMin, sceneMax);
    stackloop:
    while (stack.size() > 0) {

      BIHStackData data = stack.remove(stack.size() - 1);
      BIHNode node = data.node;
      float tMin = data.min, tMax = data.max;

      if (tMax < tMin) {
        continue;
      }

      leafloop:
      while (node.axis != 3) { // while node is not a leaf
        int a = node.axis;

        // find the origin and direction value for the given axis
        float origin, invDirection;
        switch (a) {
          default:
          case 0: // x
            origin = r.getOrigin().x;
            invDirection = 1f / r.getDirection().x;
            break;
          case 1: // y
            origin = r.getOrigin().y;
            invDirection = 1f / r.getDirection().y;
            break;
          case 2: // z
            origin = r.getOrigin().z;
            invDirection = 1f / r.getDirection().z;
            break;
        }
        // float origin = origins[a];
        // float invDirection = invDirections[a];

        float tNearSplit, tFarSplit;
        BIHNode nearNode, farNode;

        tNearSplit = (node.leftPlane - origin) * invDirection;
        tFarSplit = (node.rightPlane - origin) * invDirection;
        nearNode = node.left;
        farNode = node.right;

        if (invDirection < 0) {
          float tmpSplit = tNearSplit;
          tNearSplit = tFarSplit;
          tFarSplit = tmpSplit;

          BIHNode tmpNode = nearNode;
          nearNode = farNode;
          farNode = tmpNode;
        }

        if (tMin > tNearSplit && tMax < tFarSplit) {
          continue stackloop;
        }

        if (tMin > tNearSplit) {
          tMin = max(tMin, tFarSplit);
          node = farNode;
        } else if (tMax < tFarSplit) {
          tMax = min(tMax, tNearSplit);
          node = nearNode;
        } else {
          // stack.add(new BIHStackData(farNode, max(tMin, tFarSplit), tMax));
          vars.addStackData(farNode, max(tMin, tFarSplit), tMax);
          tMax = min(tMax, tNearSplit);
          node = nearNode;
        }
      }

      //            if ( (node.rightIndex - node.leftIndex) > minTrisPerNode){
      //                // on demand subdivision
      //                node.subdivide();
      //                stack.add(new BIHStackData(node, tMin, tMax));
      //                continue stackloop;
      //            }

      // a leaf
      for (int i = node.leftIndex; i <= node.rightIndex; i++) {
        tree.getTriangle(i, v1, v2, v3);

        float t = r.intersects(v1, v2, v3);
        if (!Float.isInfinite(t)) {
          if (worldMatrix != null) {
            worldMatrix.mult(v1, v1);
            worldMatrix.mult(v2, v2);
            worldMatrix.mult(v3, v3);
            vars.ray.setOrigin(o);
            vars.ray.setDirection(d);
            float t_world = vars.ray.intersects(v1, v2, v3);
            t = t_world;
          }

          Vector3f contactPoint = vars.vect4.set(d).multLocal(t).addLocal(o);
          float worldSpaceDist = o.distance(contactPoint);

          // don't add the collision if it is longer than the ray length
          if (worldSpaceDist <= r.limit) {
            CollisionResult cr =
                results.addReusedCollision(
                    contactPoint.x, contactPoint.y, contactPoint.z, worldSpaceDist);
            if (cr.getContactNormal() == null) {
              cr.setContactNormal(Triangle.computeTriangleNormal(v1, v2, v3, null));
            } else {
              Triangle.computeTriangleNormal(v1, v2, v3, cr.getContactNormal());
            }
            cr.setTriangleIndex(tree.getTriangleIndex(i));
            cols++;
          }
        }
      }
    }
    vars.release();
    r.setOrigin(o);
    r.setDirection(d);

    return cols;
  }
Beispiel #21
0
  public final int intersectBrute(
      Ray r,
      Matrix4f worldMatrix,
      BIHTree tree,
      float sceneMin,
      float sceneMax,
      CollisionResults results) {
    float tHit = Float.POSITIVE_INFINITY;

    TempVars vars = TempVars.get();

    Vector3f v1 = vars.vect1, v2 = vars.vect2, v3 = vars.vect3;

    int cols = 0;

    ArrayList<BIHStackData> stack = vars.bihStack;
    stack.clear();
    // stack.add(new BIHStackData(this, 0, 0));
    vars.addStackData(this, 0f, 0f);
    stackloop:
    while (stack.size() > 0) {

      BIHStackData data = stack.remove(stack.size() - 1);
      BIHNode node = data.node;

      leafloop:
      while (node.axis != 3) { // while node is not a leaf
        BIHNode nearNode, farNode;
        nearNode = node.left;
        farNode = node.right;

        // stack.add(new BIHStackData(farNode, 0, 0));
        vars.addStackData(farNode, 0f, 0f);
        node = nearNode;
      }

      // a leaf
      for (int i = node.leftIndex; i <= node.rightIndex; i++) {
        tree.getTriangle(i, v1, v2, v3);

        if (worldMatrix != null) {
          worldMatrix.mult(v1, v1);
          worldMatrix.mult(v2, v2);
          worldMatrix.mult(v3, v3);
        }

        float t = r.intersects(v1, v2, v3);
        if (t < tHit) {
          tHit = t;
          vars.vect4.set(r.direction).multLocal(tHit).addLocal(r.origin);
          CollisionResult cr =
              results.addReusedCollision(vars.vect4.x, vars.vect4.y, vars.vect4.z, tHit);
          cr.setTriangleIndex(tree.getTriangleIndex(i));
          results.addCollision(cr);
          cols++;
        }
      }
    }
    vars.release();
    return cols;
  }