/** * Computes the bounds of multiple bounding volumes * * @param bv * @return */ public static BoundingBox computeUnionBound(List<BoundingVolume> bv) { BoundingBox bbox = new BoundingBox(); for (int i = 0; i < bv.size(); i++) { BoundingVolume vol = bv.get(i); bbox.mergeLocal(vol); } return bbox; }
/** * Compute bounds of a geomList * * @param list * @param transform * @return */ public static BoundingBox computeUnionBound(GeometryList list, Transform transform) { BoundingBox bbox = new BoundingBox(); for (int i = 0; i < list.size(); i++) { BoundingVolume vol = list.get(i).getWorldBound(); BoundingVolume newVol = vol.transform(transform); // Nehon : prevent NaN and infinity values to screw the final bounding box if (!Float.isNaN(newVol.getCenter().x) && !Float.isInfinite(newVol.getCenter().x)) { bbox.mergeLocal(newVol); } } return bbox; }
/** * Compute bounds of a geomList * * @param list * @param mat * @return */ public static BoundingBox computeUnionBound(GeometryList list, Matrix4f mat) { BoundingBox bbox = new BoundingBox(); BoundingVolume store = null; for (int i = 0; i < list.size(); i++) { BoundingVolume vol = list.get(i).getWorldBound(); store = vol.clone().transform(mat, null); // Nehon : prevent NaN and infinity values to screw the final bounding box if (!Float.isNaN(store.getCenter().x) && !Float.isInfinite(store.getCenter().x)) { bbox.mergeLocal(store); } } return bbox; }
/** * 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 static void findChildBound(BoundingBox bbox, int side) { float extent = bbox.getXExtent() * 0.5f; bbox.getCenter() .set( bbox.getCenter().x + extent * Octnode.extentMult[side].x, bbox.getCenter().y + extent * Octnode.extentMult[side].y, bbox.getCenter().z + extent * Octnode.extentMult[side].z); bbox.setXExtent(extent); bbox.setYExtent(extent); bbox.setZExtent(extent); }
private static Transform convertPositions(FloatBuffer input, BoundingBox bbox, Buffer output) { if (output.capacity() < input.capacity()) throw new RuntimeException("Output must be at least as large as input!"); Vector3f offset = bbox.getCenter().negate(); Vector3f size = new Vector3f(bbox.getXExtent(), bbox.getYExtent(), bbox.getZExtent()); size.multLocal(2); ShortBuffer sb = null; ByteBuffer bb = null; float dataTypeSize; float dataTypeOffset; if (output instanceof ShortBuffer) { sb = (ShortBuffer) output; dataTypeOffset = shortOff; dataTypeSize = shortSize; } else { bb = (ByteBuffer) output; dataTypeOffset = byteOff; dataTypeSize = byteSize; } Vector3f scale = new Vector3f(); scale.set(dataTypeSize, dataTypeSize, dataTypeSize).divideLocal(size); Vector3f invScale = new Vector3f(); invScale.set(size).divideLocal(dataTypeSize); offset.multLocal(scale); offset.addLocal(dataTypeOffset, dataTypeOffset, dataTypeOffset); // offset = (-modelOffset * shortSize)/modelSize + shortOff // scale = shortSize / modelSize input.clear(); output.clear(); Vector3f temp = new Vector3f(); int vertexCount = input.capacity() / 3; for (int i = 0; i < vertexCount; i++) { BufferUtils.populateFromBuffer(temp, input, i); // offset and scale vector into -32768 ... 32767 // or into -128 ... 127 if using bytes temp.multLocal(scale); temp.addLocal(offset); // quantize and store if (sb != null) { short v1 = (short) temp.getX(); short v2 = (short) temp.getY(); short v3 = (short) temp.getZ(); sb.put(v1).put(v2).put(v3); } else { byte v1 = (byte) temp.getX(); byte v2 = (byte) temp.getY(); byte v3 = (byte) temp.getZ(); bb.put(v1).put(v2).put(v3); } } Transform transform = new Transform(); transform.setTranslation(offset.negate().multLocal(invScale)); transform.setScale(invScale); return transform; }
public void generateRenderSet( Geometry[] globalGeomList, Set<Geometry> renderSet, Camera cam, BoundingBox parentBox, boolean isRoot) { tempBox.setCenter(parentBox.getCenter()); tempBox.setXExtent(parentBox.getXExtent()); tempBox.setYExtent(parentBox.getYExtent()); tempBox.setZExtent(parentBox.getZExtent()); if (!isRoot) { findChildBound(tempBox, getSide()); } tempBox.setCheckPlane(0); cam.setPlaneState(0); Camera.FrustumIntersect result = cam.contains(tempBox); if (result != Camera.FrustumIntersect.Outside) { if (length != 0) { int start = getOffset(); int end = start + length; for (int i = start; i < end; i++) { renderSet.add(globalGeomList[i]); } } if (child == null) return; FastOctnode node = child; float x = tempBox.getCenter().x; float y = tempBox.getCenter().y; float z = tempBox.getCenter().z; float ext = tempBox.getXExtent(); while (node != null) { if (result == Camera.FrustumIntersect.Inside) { node.generateRenderSetNoCheck(globalGeomList, renderSet, cam); } else { node.generateRenderSet(globalGeomList, renderSet, cam, tempBox, false); } tempBox.getCenter().set(x, y, z); tempBox.setXExtent(ext); tempBox.setYExtent(ext); tempBox.setZExtent(ext); node = node.next; } } }
/** * 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; }
/** Computes which points are inside the hull */ private Mesh buildPreviewMesh() { long start = System.currentTimeMillis(); // First get the bounding box. updateWorldBound(); BoundingBox bound = (BoundingBox) getWorldBound(); Vector3f maxBound = bound.getMax(null); Vector3f originPoint = bound.getMin(null); originPoint.x = Math.min(originPoint.x, -maxBound.x); originPoint.y = Math.min(originPoint.y, -maxBound.y); originPoint.z = Math.min(originPoint.z, -maxBound.z); // Thread Pool ForkJoinPool pool = new ForkJoinPool(); // Create an octree from the data OctreeNode octree = new OctreeNode(originPoint, maxBound); OctreeConstructionTask dcOctreeTask = new OctreeConstructionTask(octree, primitives, 3, 6); pool.invoke(dcOctreeTask); // Contour the octree. AdaptiveDualContouringTask adaptiveTask = new AdaptiveDualContouringTask(octree, primitives); pool.invoke(adaptiveTask); // Retrieve computed data. ArrayList<Vector3f> verticesList = dcOctreeTask.getVertices(); ArrayList<Vector3i> triangles = adaptiveTask.getTriangles(); int numberOfVerticesBefore = verticesList.size(); int numberOfTrianglesBefore = triangles.size(); // Compute normals both from data and triangles. Vector3f normals[] = MeshUtils.facetedNormalsFromFaces( triangles, verticesList, primitives, (float) Math.toRadians(10)); // Drop the triangles to an array. int index = 0; int[] triangleList = new int[3 * triangles.size()]; for (Vector3i v : triangles) { triangleList[index++] = v.x; triangleList[index++] = v.y; triangleList[index++] = v.z; } // Finally, make the mesh itself: Mesh mesh = new Mesh(); mesh.setBuffer( Type.Position, 3, BufferUtils.createFloatBuffer(verticesList.toArray(new Vector3f[0]))); mesh.setBuffer(Type.Index, 3, BufferUtils.createIntBuffer(triangleList)); mesh.setBuffer(Type.Normal, 3, BufferUtils.createFloatBuffer(normals)); mesh.updateBound(); mesh.setStatic(); long timeTaken = System.currentTimeMillis() - start; System.out.println( String.format( "%d Vertices, %d Triangles in %d Milliseconds", verticesList.size(), triangles.size(), timeTaken)); return mesh; }
public void simpleInitApp() { DirectionalLight dl = new DirectionalLight(); dl.setDirection(new Vector3f(-1, -1, -1).normalizeLocal()); rootNode.addLight(dl); AmbientLight al = new AmbientLight(); al.setColor(ColorRGBA.White.mult(0.6f)); rootNode.addLight(al); // model = (Node) assetManager.loadModel("Models/Sinbad/Sinbad.mesh.xml"); Spatial s = assetManager.loadModel(MODEL); if (s instanceof Node) { model = (Node) s; } else { model = new Node(); model.attachChild(s); } BoundingBox b = ((BoundingBox) model.getWorldBound()); model.setLocalScale(1.2f / (b.getYExtent() * 2)); // model.setLocalTranslation(0,-(b.getCenter().y - b.getYExtent())* model.getLocalScale().y, // 0); for (Spatial spatial : model.getChildren()) { if (spatial instanceof Geometry) { Geometry geom = (Geometry) spatial; Material mat = geom.getMaterial(); mat.setTransparent(true); mat.getAdditionalRenderState().setAlphaTest(true); mat.getAdditionalRenderState().setBlendMode(RenderState.BlendMode.Alpha); geom.setQueueBucket(RenderQueue.Bucket.Transparent); listGeoms.add(geom); } } ChaseCamera chaseCam = new ChaseCamera(cam, inputManager); model.addControl(chaseCam); chaseCam.setLookAtOffset(b.getCenter()); chaseCam.setDefaultDistance(5); chaseCam.setMinVerticalRotation(-FastMath.HALF_PI + 0.01f); chaseCam.setZoomSensitivity(0.5f); // ch = model.getControl(AnimControl.class).createChannel(); // ch.setAnim("Wave"); SkeletonControl c = model.getControl(SkeletonControl.class); if (c != null) { c.setEnabled(false); } reductionvalue = 0.80f; lodLevel = 1; // for (final Geometry geometry : listGeoms) { // LodGenerator lodGenerator = new LodGenerator(geometry); // lodGenerator.bakeLods(LodGenerator.TriangleReductionMethod.PROPORTIONAL, // reductionvalue); // geometry.setLodLevel(lodLevel); // // } rootNode.attachChild(model); flyCam.setEnabled(false); guiFont = assetManager.loadFont("Interface/Fonts/Default.fnt"); hudText = new BitmapText(guiFont, false); hudText.setSize(guiFont.getCharSet().getRenderedSize()); hudText.setText(computeNbTri() + " tris"); hudText.setLocalTranslation(cam.getWidth() / 2, hudText.getLineHeight(), 0); guiNode.attachChild(hudText); inputManager.addListener( new ActionListener() { public void onAction(String name, boolean isPressed, float tpf) { if (isPressed) { if (name.equals("plus")) { // lodLevel++; // for (Geometry geometry : listGeoms) { // if (geometry.getMesh().getNumLodLevels() <= lodLevel) // { // lodLevel = 0; // } // geometry.setLodLevel(lodLevel); // } // jaimeText.setText(computeNbTri() + " tris"); reductionvalue += 0.05f; updateLod(); } if (name.equals("minus")) { // lodLevel--; // for (Geometry geometry : listGeoms) { // if (lodLevel < 0) { // lodLevel = geometry.getMesh().getNumLodLevels() - // 1; // } // geometry.setLodLevel(lodLevel); // } // jaimeText.setText(computeNbTri() + " tris"); reductionvalue -= 0.05f; updateLod(); } if (name.equals("wireFrame")) { wireFrame = !wireFrame; for (Geometry geometry : listGeoms) { geometry.getMaterial().getAdditionalRenderState().setWireframe(wireFrame); } } } } private void updateLod() { reductionvalue = FastMath.clamp(reductionvalue, 0.0f, 1.0f); makeLod(LodGenerator.TriangleReductionMethod.PROPORTIONAL, reductionvalue, 1); } }, "plus", "minus", "wireFrame"); inputManager.addMapping("plus", new KeyTrigger(KeyInput.KEY_ADD)); inputManager.addMapping("minus", new KeyTrigger(KeyInput.KEY_SUBTRACT)); inputManager.addMapping("wireFrame", new KeyTrigger(KeyInput.KEY_SPACE)); }