protected void splitMesh( final Mesh mesh, final int sectionStart, final int sectionEnd, final boolean doSort) { _mesh = makeRef(mesh); // Split range in half final int rangeSize = sectionEnd - sectionStart; final int halfRange = rangeSize / 2; // odd number will give +1 to right. // left half: // if half size == 1, create as regular CollisionTree if (halfRange == 1) { // compute section final int section = sectionStart; // create the left child _left = new CollisionTree(_type); _left._primitiveIndices = new int[mesh.getMeshData().getPrimitiveCount(section)]; for (int i = 0; i < _left._primitiveIndices.length; i++) { _left._primitiveIndices[i] = i; } _left._mesh = _mesh; _left.createTree(section, 0, _left._primitiveIndices.length, doSort); } else { // otherwise, make an empty collision tree and call split with new range _left = new CollisionTree(_type); _left.splitMesh(mesh, sectionStart, sectionStart + halfRange, doSort); } // right half: // if rangeSize - half size == 1, create as regular CollisionTree if (rangeSize - halfRange == 1) { // compute section final int section = sectionStart + 1; // create the left child _right = new CollisionTree(_type); _right._primitiveIndices = new int[mesh.getMeshData().getPrimitiveCount(section)]; for (int i = 0; i < _right._primitiveIndices.length; i++) { _right._primitiveIndices[i] = i; } _right._mesh = _mesh; _right.createTree(section, 0, _right._primitiveIndices.length, doSort); } else { // otherwise, make an empty collision tree and call split with new range _right = new CollisionTree(_type); _right.splitMesh(mesh, sectionStart + halfRange, sectionEnd, doSort); } // Ok, now since we technically have no primitives, we need our bounds to be the merging of our // children bounds // instead: _bounds = _left._bounds.clone(_bounds); _bounds.mergeLocal(_right._bounds); _worldBounds = _bounds.clone(_worldBounds); }
/** * Tests if the world bounds of the node at this level intersects a provided bounding volume. If * an intersection occurs, true is returned, otherwise false is returned. If the provided volume * is invalid, false is returned. * * @param volume the volume to intersect with. * @return true if there is an intersect, false otherwise. */ public boolean intersectsBounding(final BoundingVolume volume) { switch (volume.getType()) { case AABB: return _worldBounds.intersectsBoundingBox((BoundingBox) volume); case OBB: return _worldBounds.intersectsOrientedBoundingBox((OrientedBoundingBox) volume); case Sphere: return _worldBounds.intersectsSphere((BoundingSphere) volume); default: return false; } }
/** * <code>mergeLocal</code> combines this sphere with a second bounding sphere locally. Altering * this sphere to contain both the original and the additional sphere volumes; * * @param volume the sphere to combine with this sphere. * @return this */ @Override public BoundingVolume mergeLocal(final BoundingVolume volume) { if (volume == null) { return this; } switch (volume.getType()) { case Sphere: { final BoundingSphere sphere = (BoundingSphere) volume; final double temp_radius = sphere.getRadius(); final ReadOnlyVector3 temp_center = sphere.getCenter(); return merge(temp_radius, temp_center, this); } case AABB: { final BoundingBox box = (BoundingBox) volume; final Vector3 temp_center = box._center; _compVect1.set(box.getXExtent(), box.getYExtent(), box.getZExtent()); final double radius = _compVect1.length(); return merge(radius, temp_center, this); } case OBB: { return mergeLocalOBB((OrientedBoundingBox) volume); } default: return null; } }
@Override public boolean intersects(final BoundingVolume bv) { if (bv == null) { return false; } return bv.intersectsSphere(this); }
@Override public void read(final InputCapsule capsule) throws IOException { super.read(capsule); try { setRadius(capsule.readDouble("radius", 0)); } catch (final IOException ex) { logger.logp( Level.SEVERE, this.getClass().toString(), "read(Ardor3DImporter)", "Exception", ex); } }
@Override public void write(final OutputCapsule capsule) throws IOException { super.write(capsule); try { capsule.write(getRadius(), "radius", 0); } catch (final IOException ex) { logger.logp( Level.SEVERE, this.getClass().toString(), "write(Ardor3DExporter)", "Exception", ex); } }
/** * <code>clone</code> creates a new BoundingSphere object containing the same data as this one. * * @param store where to store the cloned information. if null or wrong class, a new store is * created. * @return the new BoundingSphere */ @Override public BoundingVolume clone(final BoundingVolume store) { if (store != null && store.getType() == Type.Sphere) { final BoundingSphere rVal = (BoundingSphere) store; rVal._center.set(_center); rVal.setRadius(_radius); rVal._checkPlane = _checkPlane; return rVal; } return new BoundingSphere(getRadius(), _center); }
/** * Creates a Collision Tree by recursively creating children nodes, splitting the primitives this * node is responsible for in half until the desired primitive count is reached. * * @param start The start index of the primitivesArray, inclusive. * @param end The end index of the primitivesArray, exclusive. * @param doSort True if the primitives should be sorted at each level, false otherwise. */ public void createTree(final int section, final int start, final int end, final boolean doSort) { _section = section; _start = start; _end = end; if (_primitiveIndices == null) { return; } createBounds(); // the bounds at this level should contain all the primitives this level is responsible for. _bounds.computeFromPrimitives( getMesh().getMeshData(), _section, _primitiveIndices, _start, _end); // check to see if we are a leaf, if the number of primitives we reference is less than or equal // to the maximum // defined by the CollisionTreeManager we are done. if (_end - _start + 1 <= CollisionTreeManager.getInstance().getMaxPrimitivesPerLeaf()) { return; } // if doSort is set we need to attempt to optimize the referenced primitives. optimizing the // sorting of the // primitives will help group them spatially in the left/right children better. if (doSort) { sortPrimitives(); } // create the left child if (_left == null) { _left = new CollisionTree(_type); } _left._primitiveIndices = _primitiveIndices; _left._mesh = _mesh; _left.createTree(_section, _start, (_start + _end) / 2, doSort); // create the right child if (_right == null) { _right = new CollisionTree(_type); } _right._primitiveIndices = _primitiveIndices; _right._mesh = _mesh; _right.createTree(_section, (_start + _end) / 2, _end, doSort); }
/** * intersect checks for collisions between this collision tree and a provided Ray. Any collisions * are stored in a provided list as primitive index values. The ray is assumed to have a * normalized direction for accurate calculations. * * @param ray the ray to test for intersections. * @param store a list to fill with the index values of the primitive hit. if null, a new List is * created. * @return the list. */ public List<PrimitiveKey> intersect(final Ray3 ray, final List<PrimitiveKey> store) { List<PrimitiveKey> result = store; if (result == null) { result = new ArrayList<PrimitiveKey>(); } // if our ray doesn't hit the bounds, then it must not hit a primitive. if (!_worldBounds.intersects(ray)) { return result; } // This is not a leaf node, therefore, check each child (left/right) for intersection with the // ray. if (_left != null) { _left._worldBounds = _left._bounds.transform(getMesh().getWorldTransform(), _left._worldBounds); _left.intersect(ray, result); } if (_right != null) { _right._worldBounds = _right._bounds.transform(getMesh().getWorldTransform(), _right._worldBounds); _right.intersect(ray, result); } else if (_left == null) { // This is a leaf node. We can therefore check each primitive this node contains. If an // intersection occurs, // place it in the list. final MeshData data = getMesh().getMeshData(); final ReadOnlyTransform transform = getMesh().getWorldTransform(); Vector3[] points = null; for (int i = _start; i < _end; i++) { points = data.getPrimitiveVertices(_primitiveIndices[i], _section, points); for (int t = 0; t < points.length; t++) { transform.applyForward(points[t]); } if (ray.intersects(points, null)) { result.add(new PrimitiveKey(_primitiveIndices[i], _section)); } } } return result; }
@Override public BoundingVolume transform(final ReadOnlyTransform transform, final BoundingVolume store) { BoundingSphere sphere; if (store == null || store.getType() != BoundingVolume.Type.Sphere) { sphere = new BoundingSphere(1, new Vector3(0, 0, 0)); } else { sphere = (BoundingSphere) store; } transform.applyForward(_center, sphere._center); if (!transform.isRotationMatrix()) { final Vector3 scale = new Vector3(1, 1, 1); transform.applyForwardVector(scale); sphere.setRadius(Math.abs(maxAxis(scale) * getRadius()) + radiusEpsilon - 1); } else { final ReadOnlyVector3 scale = transform.getScale(); sphere.setRadius(Math.abs(maxAxis(scale) * getRadius()) + radiusEpsilon - 1); } return sphere; }
/** * <code>merge</code> combines this sphere with a second bounding sphere. This new sphere contains * both bounding spheres and is returned. * * @param volume the sphere to combine with this sphere. * @return a new sphere */ @Override public BoundingVolume merge(final BoundingVolume volume) { if (volume == null) { return this; } switch (volume.getType()) { case Sphere: { final BoundingSphere sphere = (BoundingSphere) volume; final double temp_radius = sphere.getRadius(); final ReadOnlyVector3 tempCenter = sphere.getCenter(); final BoundingSphere rVal = new BoundingSphere(); return merge(temp_radius, tempCenter, rVal); } case AABB: { final BoundingBox box = (BoundingBox) volume; final Vector3 radVect = new Vector3(box.getXExtent(), box.getYExtent(), box.getZExtent()); final Vector3 tempCenter = box._center; final BoundingSphere rVal = new BoundingSphere(); return merge(radVect.length(), tempCenter, rVal); } case OBB: { final OrientedBoundingBox box = (OrientedBoundingBox) volume; final BoundingSphere rVal = (BoundingSphere) this.clone(null); return rVal.mergeLocalOBB(box); } default: return null; } }
/** Get the point and distance to seek to */ @Override public double getSeekPointAndDistance(Vector3 point) { BoundingVolume bv = getWorldBound(); point.set(bv.getCenter()); return (getRadius() * 1.5); }