2 * Copyright (c) 2003-2005 jMonkeyEngine
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
9 * * Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
12 * * Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
16 * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
17 * may be used to endorse or promote products derived from this software
18 * without specific prior written permission.
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
24 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
27 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
28 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
29 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 package com.jme.scene;
35 import com.jme.math.FastMath;
36 import com.jme.math.Matrix3f;
37 import com.jme.math.Vector3f;
38 import com.jme.renderer.Camera;
39 import com.jme.renderer.Renderer;
42 * <code>BillboardNode</code> defines a node that always orients towards the
43 * camera. However, it does not tilt up/down as the camera rises. This keep
44 * geometry from appearing to fall over if the camera rises or lowers.
45 * <code>BillboardNode</code> is useful to contain a single quad that has a
46 * image applied to it for lowest detail models. This quad, with the texture,
47 * will appear to be a full model at great distances, and save on rendering and
48 * memory. It is important to note that for AXIAL mode, the billboards
49 * orientation will always be up (0,1,0). This means that a "standard" jME
50 * camera with up (0,1,0) is the only camera setting compatible with AXIAL mode.
53 * @author Joshua Slack
54 * @version $Id: BillboardNode.java,v 1.21 2005-09-20 09:47:18 irrisor Exp $
56 public class BillboardNode extends Node {
57 private static final long serialVersionUID = 1L;
59 private float lastTime;
61 private Matrix3f orient;
63 private Vector3f look;
65 private Vector3f left;
71 /** Alligns this Billboard Node to the screen. */
72 public static final int SCREEN_ALIGNED = 0;
74 /** Alligns this Billboard Node to the screen, but keeps the Y axis fixed. */
75 public static final int AXIAL = 1;
77 /** Alligns this Billboard Node to the camera position. */
78 public static final int CAMERA_ALIGNED = 2;
81 * Constructor instantiates a new <code>BillboardNode</code>. The name of
82 * the node is supplied during construction.
85 * the name of the node.
87 public BillboardNode(String name) {
89 orient = new Matrix3f();
91 look = new Vector3f();
92 left = new Vector3f();
93 type = SCREEN_ALIGNED;
97 * <code>updateWorldData</code> defers the updating of the billboards
98 * orientation until rendering. This keeps the billboard from being
99 * needlessly oriented if the player can not actually see it.
102 * the time between frames.
103 * @see com.jme.scene.Spatial#updateWorldData(float)
105 public void updateWorldData(float time) {
111 * <code>draw</code> updates the billboards orientation then renders the
112 * billboard's children.
115 * the renderer used to draw.
116 * @see com.jme.scene.Spatial#draw(com.jme.renderer.Renderer)
118 public void draw(Renderer r) {
119 Camera cam = r.getCamera();
120 rotateBillboard(cam);
126 * rotate the billboard based on the type set
131 public void rotateBillboard(Camera cam) {
132 // get the scale, translation and rotation of the node in world space
133 updateWorldVectors();
140 rotateScreenAligned(cam);
143 rotateCameraAligned(cam);
147 for (int i = 0, cSize = getChildren().size(); i < cSize; i++) {
148 Spatial child = (Spatial) getChildren().get(i);
150 child.updateGeometricState(lastTime, false);
156 * rotateCameraAligned
161 private void rotateCameraAligned(Camera camera) {
162 look.set(camera.getLocation()).subtractLocal(worldTranslation);
163 look.normalizeLocal();
165 float el = FastMath.asin(look.y);
166 float az = FastMath.atan2(look.x, look.z);
167 float elCos = FastMath.cos(el);
168 float azCos = FastMath.cos(az);
169 float elSin = FastMath.sin(el);
170 float azSin = FastMath.sin(az);
172 // compute the local orientation matrix for the billboard
174 orient.m01 = azSin * -elSin;
175 orient.m02 = azSin * elCos;
180 orient.m21 = azCos * -elSin;
181 orient.m22 = azCos * elCos;
183 // The billboard must be oriented to face the camera before it is
184 // transformed into the world.
185 worldRotation.apply(orient);
189 * Rotate the billboard so it points directly opposite the direction the
195 private void rotateScreenAligned(Camera camera) {
196 // coopt diff for our in direction:
197 look.set(camera.getDirection()).negateLocal();
198 // coopt loc for our left direction:
199 left.set(camera.getLeft()).negateLocal();
200 orient.fromAxes(left, camera.getUp(), look);
201 worldRotation.fromRotationMatrix(orient);
205 * Rotate the billboard towards the camera, but keeping the y axis fixed.
210 private void rotateAxial(Camera camera) {
211 // Compute the additional rotation required for the billboard to face
212 // the camera. To do this, the camera must be inverse-transformed into
213 // the model space of the billboard.
214 look.set(camera.getLocation()).subtractLocal(worldTranslation);
215 worldRotation.mult(look, left); // coopt left for our own purposes.
216 left.x *= 1.0f / worldScale.x;
217 left.y *= 1.0f / worldScale.y;
218 left.z *= 1.0f / worldScale.z;
220 // squared length of the camera projection in the xz-plane
221 float lengthSquared = left.x * left.x + left.z * left.z;
222 if (lengthSquared < FastMath.FLT_EPSILON) {
223 // camera on the billboard axis, rotation not defined
227 // unitize the projection
228 float invLength = FastMath.invSqrt(lengthSquared);
233 // compute the local orientation matrix for the billboard
240 orient.m20 = -left.x;
244 // The billboard must be oriented to face the camera before it is
245 // transformed into the world.
246 worldRotation.apply(orient);
250 * Returns the type of rotation this BillboardNode is set too.
252 * @return The type of rotation, AXIAL or SCREEN.
254 public int getType() {
259 * Sets the type of rotation this BillboardNode will have. The type can be
260 * either SCREEN_ALIGNED or AXIAL. Invalid types will assume no billboard
263 public void setType(int type) {