diff options
Diffstat (limited to 'ardor3d-core/src')
-rw-r--r-- | ardor3d-core/src/main/java/com/ardor3d/scenegraph/Spatial.java | 1 | ||||
-rw-r--r-- | ardor3d-core/src/main/java/com/ardor3d/scenegraph/extension/BillboardNode.java | 197 |
2 files changed, 124 insertions, 74 deletions
diff --git a/ardor3d-core/src/main/java/com/ardor3d/scenegraph/Spatial.java b/ardor3d-core/src/main/java/com/ardor3d/scenegraph/Spatial.java index 077f1ea..0aab2e2 100644 --- a/ardor3d-core/src/main/java/com/ardor3d/scenegraph/Spatial.java +++ b/ardor3d-core/src/main/java/com/ardor3d/scenegraph/Spatial.java @@ -106,6 +106,7 @@ public abstract class Spatial implements Savable, Hintable { /** The default delegate reference to use if none provided. */ private static final Object defaultDelegateRef = new Object(); + protected static final EnumSet<DirtyType> ON_DIRTY_TRANSFORM_ONLY = EnumSet.of(DirtyType.Transform); protected static final EnumSet<DirtyType> ON_DIRTY_TRANSFORM = EnumSet.of(DirtyType.Bounding, DirtyType.Transform); protected static final EnumSet<DirtyType> ON_DIRTY_RENDERSTATE = EnumSet.of(DirtyType.RenderState); protected static final EnumSet<DirtyType> ON_DIRTY_BOUNDING = EnumSet.of(DirtyType.Bounding); diff --git a/ardor3d-core/src/main/java/com/ardor3d/scenegraph/extension/BillboardNode.java b/ardor3d-core/src/main/java/com/ardor3d/scenegraph/extension/BillboardNode.java index 373dd0a..7c29fc8 100644 --- a/ardor3d-core/src/main/java/com/ardor3d/scenegraph/extension/BillboardNode.java +++ b/ardor3d-core/src/main/java/com/ardor3d/scenegraph/extension/BillboardNode.java @@ -3,7 +3,7 @@ * * This file is part of Ardor3D. * - * Ardor3D is free software: you can redistribute it and/or modify it + * Ardor3D is free software: you can redistribute it and/or modify it * under the terms of its license which may be found in the accompanying * LICENSE file or at <http://www.ardor3d.com/LICENSE>. */ @@ -24,34 +24,73 @@ import com.ardor3d.util.export.InputCapsule; import com.ardor3d.util.export.OutputCapsule; /** - * <code>BillboardNode</code> defines a node that always orients towards the camera. However, it does not tilt up/down - * as the camera rises. This keep geometry from appearing to fall over if the camera rises or lowers. - * <code>BillboardNode</code> is useful to contain a single quad that has a image applied to it for lowest detail - * models. This quad, with the texture, will appear to be a full model at great distances, and save on rendering and - * memory. It is important to note that for AXIAL mode, the billboards orientation will always be up (0,1,0). This means - * that a "standard" ardor3d camera with up (0,1,0) is the only camera setting compatible with AXIAL mode. + * <p> + * <code>BillboardNode</code> defines a node that will attempt to orient itself in relation to the current camera. The + * way it does this depends on the alignment type set via {@link #setAlignment(BillboardAlignment)}. + * </p> + * <p> + * <code>BillboardNode</code> is often a useful way fake complex distant geometry using a single quad that has an image + * applied to it. This quad, with the texture, will appear to be a full model at great distances, and save on rendering + * and memory. + * </p> + * <p> + * It is also worth noting that you can use any geometry with this node, not just quads. + * </p> */ public class BillboardNode extends Node { - private double _lastTime; - private final Matrix3 _orient = new Matrix3(Matrix3.IDENTITY); private final Vector3 _look = new Vector3(Vector3.ZERO); private final Vector3 _left = new Vector3(Vector3.ZERO); + // Used to denote whether our billboard updates should also request bounds updates. + private boolean _updateBounds = true; + + /** + * Method of alignment to use. See individual enum values for details. + */ public enum BillboardAlignment { - ScreenAligned, CameraAligned, AxialY, AxialZ + /** + * Do not change the node's rotation. Useful for situations where you can not remove a billboard node from a + * hierarchy, but do not want the rotation to change. + */ + None, + + /** + * Rotate the billboard so it points directly opposite the direction the camera's facing. + */ + ScreenAligned, + + /** + * Rotate the billboard so it points directly towards the camera's direction. + */ + CameraAligned, + + /** + * Rotate the billboard to face in the camera's direction by rotating around the X axis. + */ + AxialX, + + /** + * Rotate the billboard to face in the camera's direction by rotating around the Y axis. + */ + AxialY, + + /** + * Rotate the billboard to face in the camera's direction by rotating around the Z axis. + */ + AxialZ } private BillboardAlignment _alignment; - public BillboardNode() {} + public BillboardNode() { /**/} /** * Constructor instantiates a new <code>BillboardNode</code>. The name of the node is supplied during construction. - * + * * @param name * the name of the node. */ @@ -60,15 +99,9 @@ public class BillboardNode extends Node { _alignment = BillboardAlignment.ScreenAligned; } - @Override - public void updateWorldTransform(final boolean recurse) { - _lastTime = 0; // time - super.updateWorldTransform(recurse); - } - /** * <code>draw</code> updates the billboards orientation then renders the billboard's children. - * + * * @param r * the renderer used to draw. * @see com.ardor3d.scenegraph.Spatial#draw(com.ardor3d.renderer.Renderer) @@ -81,10 +114,30 @@ public class BillboardNode extends Node { } /** - * rotate the billboard based on the type set - * - * @param cam - * Camera + * Normally a billboard triggers transform and bounds updates on its children. Setting this to false will only + * trigger dirty flags for transform, potentially saving some expensive bounds calculations if those are deemed + * unnecessary (for example, when using sphere bounds and quad billboards. Setting to false may cause odd culling + * behavior. + * + * @param doUpdate + * true (the default) if we should request bounds updates for children, false if not. + */ + public void setUpdateBounds(final boolean doUpdate) { + _updateBounds = doUpdate; + } + + /** + * @return true if bounds are dirtied for children each frame. + * @see #setUpdateBounds(boolean) + */ + public boolean isUpdateBounds() { + return _updateBounds; + } + + /** + * Rotate the billboard based on the type set and the currently set camera. + * + * @see Camera#getCurrentCamera() */ public void rotateBillboard() { // get the scale, translation and rotation of the node in world space @@ -97,11 +150,17 @@ public class BillboardNode extends Node { case CameraAligned: rotateCameraAligned(); break; + case AxialX: + rotateAxial(Vector3.UNIT_X); + break; case AxialY: - rotateAxial(new Vector3(Vector3.UNIT_Y)); + rotateAxial(Vector3.UNIT_Y); break; case AxialZ: - rotateAxial(new Vector3(Vector3.UNIT_Z)); + rotateAxial(Vector3.UNIT_Z); + break; + case None: + // nothing to do here. break; } @@ -109,80 +168,51 @@ public class BillboardNode extends Node { return; } - propagateDirtyDown(ON_DIRTY_TRANSFORM); + if (_updateBounds) { + propagateDirtyDown(ON_DIRTY_TRANSFORM); + } else { + propagateDirtyDown(ON_DIRTY_TRANSFORM_ONLY); + } + for (int i = 0, cSize = getNumberOfChildren(); i < cSize; i++) { final Spatial child = getChild(i); if (child != null) { - child.updateGeometricState(_lastTime, false); + child.updateGeometricState(0, false); } } } /** * Aligns this Billboard Node so that it points to the camera position. - * - * @param camera - * Camera */ private void rotateCameraAligned() { final Camera camera = Camera.getCurrentCamera(); - _look.set(camera.getLocation()).subtractLocal(_worldTransform.getTranslation()); - // coopt left for our own purposes. - final Vector3 xzp = _left; - // The xzp vector is the projection of the look vector on the xz plane - xzp.set(_look.getX(), 0, _look.getZ()); - - // check for undefined rotation... - if (xzp.equals(Vector3.ZERO)) { - return; - } - - _look.normalizeLocal(); - xzp.normalizeLocal(); - final double cosp = _look.dot(xzp); - - // compute the local orientation matrix for the billboard - _orient.setValue(0, 0, xzp.getZ()); - _orient.setValue(0, 1, xzp.getX() * -_look.getY()); - _orient.setValue(0, 2, xzp.getX() * cosp); - _orient.setValue(1, 0, 0); - _orient.setValue(1, 1, cosp); - _orient.setValue(1, 2, _look.getY()); - _orient.setValue(2, 0, -xzp.getX()); - _orient.setValue(2, 1, xzp.getZ() * -_look.getY()); - _orient.setValue(2, 2, xzp.getZ() * cosp); - - // The billboard must be oriented to face the camera before it is - // transformed into the world. - final Matrix3 mat = Matrix3.fetchTempInstance().set(_worldTransform.getMatrix()).multiplyLocal(_orient); - _worldTransform.setRotation(mat); - Matrix3.releaseTempInstance(mat); + _look.set(camera.getLocation()).subtractLocal(_worldTransform.getTranslation()).normalizeLocal(); + _left.set(camera.getUp()).crossLocal(_look); + final Vector3 up = Vector3.fetchTempInstance(); + up.set(_look).crossLocal(_left); + _orient.fromAxes(_left, up, _look); + _worldTransform.setRotation(_orient); + Vector3.releaseTempInstance(up); } /** * Rotate the billboard so it points directly opposite the direction the camera's facing - * - * @param camera - * Camera */ private void rotateScreenAligned() { final Camera camera = Camera.getCurrentCamera(); - // coopt diff for our in direction: _look.set(camera.getDirection()).negateLocal(); - // coopt loc for our left direction: _left.set(camera.getLeft()).negateLocal(); _orient.fromAxes(_left, camera.getUp(), _look); _worldTransform.setRotation(_orient); } /** - * Rotate the billboard towards the camera, but keeping a given axis fixed. - * - * @param camera - * Camera + * Rotate the billboard towards the current camera, but keeping a given axis fixed. */ - private void rotateAxial(final Vector3 axis) { + private void rotateAxial(final ReadOnlyVector3 axis) { final Camera camera = Camera.getCurrentCamera(); + // Compute the additional rotation required for the billboard to face // the camera. To do this, the camera must be inverse-transformed into // the model space of the billboard. @@ -233,6 +263,22 @@ public class BillboardNode extends Node { _orient.setValue(2, 0, 0); _orient.setValue(2, 1, 0); _orient.setValue(2, 2, 1); + } else if (axis.getX() == 1) { + _left.setX(0.0); + _left.setY(_left.getY() * invLength); + _left.setZ(_left.getZ() * invLength); + _left.normalizeLocal(); + + // compute the local orientation matrix for the billboard + _orient.setValue(0, 0, 1); + _orient.setValue(0, 1, 0); + _orient.setValue(0, 2, 0); + _orient.setValue(1, 0, 0); + _orient.setValue(1, 1, _left.getZ()); + _orient.setValue(1, 2, _left.getY()); + _orient.setValue(2, 0, 0); + _orient.setValue(2, 1, -_left.getY()); + _orient.setValue(2, 2, _left.getZ()); } // The billboard must be oriented to face the camera before it is @@ -243,9 +289,9 @@ public class BillboardNode extends Node { } /** - * Returns the alignment this BillboardNode is set too. - * - * @return The alignment of rotation, ScreenAligned, CameraAligned, AxialY or AxialZ. + * Returns the alignment this BillboardNode is set to. + * + * @return The alignment of rotation. */ public BillboardAlignment getAlignment() { return _alignment; @@ -257,6 +303,7 @@ public class BillboardNode extends Node { */ public void setAlignment(final BillboardAlignment alignment) { _alignment = alignment; + _worldTransform.setRotation(Matrix3.IDENTITY); } @Override @@ -266,6 +313,7 @@ public class BillboardNode extends Node { capsule.write(_look, "look", new Vector3(Vector3.ZERO)); capsule.write(_left, "left", new Vector3(Vector3.ZERO)); capsule.write(_alignment, "alignment", BillboardAlignment.ScreenAligned); + capsule.write(_updateBounds, "updateBounds", true); } @Override @@ -275,5 +323,6 @@ public class BillboardNode extends Node { _look.set((Vector3) capsule.readSavable("look", new Vector3(Vector3.ZERO))); _left.set((Vector3) capsule.readSavable("left", new Vector3(Vector3.ZERO))); _alignment = capsule.readEnum("alignment", BillboardAlignment.class, BillboardAlignment.ScreenAligned); + _updateBounds = capsule.readBoolean("updateBounds", true); } }
\ No newline at end of file |