/** * <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(); }
/** * 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); }
/** 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(); } } }
/** * 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); }
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(); }
/** * <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; }
/** * 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); }
/** * 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); }
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); }
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(); }
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; }
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; }
/** * 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); }
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; }
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); } }