OSDN Git Service

avoid vector creation in intersectsSphere, some corrections in stress tests for profi...
[mikumikustudio/MikuMikuStudio.git] / src / com / jme / scene / BillboardNode.java
1 /*
2  * Copyright (c) 2003-2005 jMonkeyEngine
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are
7  * met:
8  *
9  * * Redistributions of source code must retain the above copyright
10  *   notice, this list of conditions and the following disclaimer.
11  *
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.
15  *
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.
19  *
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.
31  */
32
33 package com.jme.scene;
34
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;
40
41 /**
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.
51  * 
52  * @author Mark Powell
53  * @author Joshua Slack
54  * @version $Id: BillboardNode.java,v 1.21 2005-09-20 09:47:18 irrisor Exp $
55  */
56 public class BillboardNode extends Node {
57     private static final long serialVersionUID = 1L;
58
59     private float lastTime;
60
61     private Matrix3f orient;
62
63     private Vector3f look;
64
65     private Vector3f left;
66
67     private Vector3f up;
68
69     private int type;
70
71     /** Alligns this Billboard Node to the screen. */
72     public static final int SCREEN_ALIGNED = 0;
73
74     /** Alligns this Billboard Node to the screen, but keeps the Y axis fixed. */
75     public static final int AXIAL = 1;
76
77     /** Alligns this Billboard Node to the camera position. */
78     public static final int CAMERA_ALIGNED = 2;
79
80     /**
81      * Constructor instantiates a new <code>BillboardNode</code>. The name of
82      * the node is supplied during construction.
83      * 
84      * @param name
85      *            the name of the node.
86      */
87     public BillboardNode(String name) {
88         super(name);
89         orient = new Matrix3f();
90         up = new Vector3f();
91         look = new Vector3f();
92         left = new Vector3f();
93         type = SCREEN_ALIGNED;
94     }
95
96     /**
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.
100      * 
101      * @param time
102      *            the time between frames.
103      * @see com.jme.scene.Spatial#updateWorldData(float)
104      */
105     public void updateWorldData(float time) {
106         lastTime = time;
107         updateWorldBound();
108     }
109
110     /**
111      * <code>draw</code> updates the billboards orientation then renders the
112      * billboard's children.
113      * 
114      * @param r
115      *            the renderer used to draw.
116      * @see com.jme.scene.Spatial#draw(com.jme.renderer.Renderer)
117      */
118     public void draw(Renderer r) {
119         Camera cam = r.getCamera();
120         rotateBillboard(cam);
121
122         super.draw(r);
123     }
124
125     /**
126      * rotate the billboard based on the type set
127      * 
128      * @param cam
129      *            Camera
130      */
131     public void rotateBillboard(Camera cam) {
132         // get the scale, translation and rotation of the node in world space
133         updateWorldVectors();
134
135         switch (type) {
136         case AXIAL:
137             rotateAxial(cam);
138             break;
139         case SCREEN_ALIGNED:
140             rotateScreenAligned(cam);
141             break;
142         case CAMERA_ALIGNED:
143             rotateCameraAligned(cam);
144             break;
145         }
146
147         for (int i = 0, cSize = getChildren().size(); i < cSize; i++) {
148             Spatial child = (Spatial) getChildren().get(i);
149             if (child != null) {
150                 child.updateGeometricState(lastTime, false);
151             }
152         }
153     }
154
155     /**
156      * rotateCameraAligned
157      * 
158      * @param camera
159      *            Camera
160      */
161     private void rotateCameraAligned(Camera camera) {
162         look.set(camera.getLocation()).subtractLocal(worldTranslation);
163         look.normalizeLocal();
164
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);
171
172         // compute the local orientation matrix for the billboard
173         orient.m00 = azCos;
174         orient.m01 = azSin * -elSin;
175         orient.m02 = azSin * elCos;
176         orient.m10 = 0;
177         orient.m11 = elCos;
178         orient.m12 = elSin;
179         orient.m20 = -azSin;
180         orient.m21 = azCos * -elSin;
181         orient.m22 = azCos * elCos;
182
183         // The billboard must be oriented to face the camera before it is
184         // transformed into the world.
185         worldRotation.apply(orient);
186     }
187
188     /**
189      * Rotate the billboard so it points directly opposite the direction the
190      * camera's facing
191      * 
192      * @param camera
193      *            Camera
194      */
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);
202     }
203
204     /**
205      * Rotate the billboard towards the camera, but keeping the y axis fixed.
206      * 
207      * @param camera
208      *            Camera
209      */
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;
219
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
224             return;
225         }
226
227         // unitize the projection
228         float invLength = FastMath.invSqrt(lengthSquared);
229         left.x *= invLength;
230         left.y = 0.0f;
231         left.z *= invLength;
232
233         // compute the local orientation matrix for the billboard
234         orient.m00 = left.z;
235         orient.m01 = 0;
236         orient.m02 = left.x;
237         orient.m10 = 0;
238         orient.m11 = 1;
239         orient.m12 = 0;
240         orient.m20 = -left.x;
241         orient.m21 = 0;
242         orient.m22 = left.z;
243
244         // The billboard must be oriented to face the camera before it is
245         // transformed into the world.
246         worldRotation.apply(orient);
247     }
248
249     /**
250      * Returns the type of rotation this BillboardNode is set too.
251      * 
252      * @return The type of rotation, AXIAL or SCREEN.
253      */
254     public int getType() {
255         return type;
256     }
257
258     /**
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
261      * rotation.
262      */
263     public void setType(int type) {
264         this.type = type;
265     }
266 }