1/*
2 * Copyright (c) 2009-2010 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 */
32package com.jme3.scene;
33
34import com.jme3.asset.Asset;
35import com.jme3.asset.AssetKey;
36import com.jme3.bounding.BoundingVolume;
37import com.jme3.collision.Collidable;
38import com.jme3.export.*;
39import com.jme3.light.Light;
40import com.jme3.light.LightList;
41import com.jme3.material.Material;
42import com.jme3.math.*;
43import com.jme3.renderer.Camera;
44import com.jme3.renderer.RenderManager;
45import com.jme3.renderer.ViewPort;
46import com.jme3.renderer.queue.RenderQueue;
47import com.jme3.renderer.queue.RenderQueue.Bucket;
48import com.jme3.renderer.queue.RenderQueue.ShadowMode;
49import com.jme3.scene.control.Control;
50import com.jme3.util.SafeArrayList;
51import com.jme3.util.TempVars;
52import java.io.IOException;
53import java.util.*;
54import java.util.logging.Logger;
55
56/**
57 * <code>Spatial</code> defines the base class for scene graph nodes. It
58 * maintains a link to a parent, it's local transforms and the world's
59 * transforms. All other scene graph elements, such as {@link Node} and
60 * {@link Geometry} are subclasses of <code>Spatial</code>.
61 *
62 * @author Mark Powell
63 * @author Joshua Slack
64 * @version $Revision: 4075 $, $Data$
65 */
66public abstract class Spatial implements Savable, Cloneable, Collidable, Asset {
67
68    private static final Logger logger = Logger.getLogger(Spatial.class.getName());
69
70    /**
71     * Specifies how frustum culling should be handled by
72     * this spatial.
73     */
74    public enum CullHint {
75
76        /**
77         * Do whatever our parent does. If no parent, default to {@link #Dynamic}.
78         */
79        Inherit,
80        /**
81         * Do not draw if we are not at least partially within the view frustum
82         * of the camera. This is determined via the defined
83         * Camera planes whether or not this Spatial should be culled.
84         */
85        Dynamic,
86        /**
87         * Always cull this from the view, throwing away this object
88         * and any children from rendering commands.
89         */
90        Always,
91        /**
92         * Never cull this from view, always draw it.
93         * Note that we will still get culled if our parent is culled.
94         */
95        Never;
96    }
97
98    /**
99     * Specifies if this spatial should be batched
100     */
101    public enum BatchHint {
102
103        /**
104         * Do whatever our parent does. If no parent, default to {@link #Always}.
105         */
106        Inherit,
107        /**
108         * This spatial will always be batched when attached to a BatchNode.
109         */
110        Always,
111        /**
112         * This spatial will never be batched when attached to a BatchNode.
113         */
114        Never;
115    }
116    /**
117     * Refresh flag types
118     */
119    protected static final int RF_TRANSFORM = 0x01, // need light resort + combine transforms
120            RF_BOUND = 0x02,
121            RF_LIGHTLIST = 0x04; // changes in light lists
122    protected CullHint cullHint = CullHint.Inherit;
123    protected BatchHint batchHint = BatchHint.Inherit;
124    /**
125     * Spatial's bounding volume relative to the world.
126     */
127    protected BoundingVolume worldBound;
128    /**
129     * LightList
130     */
131    protected LightList localLights;
132    protected transient LightList worldLights;
133    /**
134     * This spatial's name.
135     */
136    protected String name;
137    // scale values
138    protected transient Camera.FrustumIntersect frustrumIntersects = Camera.FrustumIntersect.Intersects;
139    protected RenderQueue.Bucket queueBucket = RenderQueue.Bucket.Inherit;
140    protected ShadowMode shadowMode = RenderQueue.ShadowMode.Inherit;
141    public transient float queueDistance = Float.NEGATIVE_INFINITY;
142    protected Transform localTransform;
143    protected Transform worldTransform;
144    protected SafeArrayList<Control> controls = new SafeArrayList<Control>(Control.class);
145    protected HashMap<String, Savable> userData = null;
146    /**
147     * Used for smart asset caching
148     *
149     * @see AssetKey#useSmartCache()
150     */
151    protected AssetKey key;
152    /**
153     * Spatial's parent, or null if it has none.
154     */
155    protected transient Node parent;
156    /**
157     * Refresh flags. Indicate what data of the spatial need to be
158     * updated to reflect the correct state.
159     */
160    protected transient int refreshFlags = 0;
161
162    /**
163     * Serialization only. Do not use.
164     */
165    public Spatial() {
166        localTransform = new Transform();
167        worldTransform = new Transform();
168
169        localLights = new LightList(this);
170        worldLights = new LightList(this);
171
172        refreshFlags |= RF_BOUND;
173    }
174
175    /**
176     * Constructor instantiates a new <code>Spatial</code> object setting the
177     * rotation, translation and scale value to defaults.
178     *
179     * @param name
180     *            the name of the scene element. This is required for
181     *            identification and comparison purposes.
182     */
183    public Spatial(String name) {
184        this();
185        this.name = name;
186    }
187
188    public void setKey(AssetKey key) {
189        this.key = key;
190    }
191
192    public AssetKey getKey() {
193        return key;
194    }
195
196    /**
197     * Indicate that the transform of this spatial has changed and that
198     * a refresh is required.
199     */
200    protected void setTransformRefresh() {
201        refreshFlags |= RF_TRANSFORM;
202        setBoundRefresh();
203    }
204
205    protected void setLightListRefresh() {
206        refreshFlags |= RF_LIGHTLIST;
207    }
208
209    /**
210     * Indicate that the bounding of this spatial has changed and that
211     * a refresh is required.
212     */
213    protected void setBoundRefresh() {
214        refreshFlags |= RF_BOUND;
215
216        // XXX: Replace with a recursive call?
217        Spatial p = parent;
218        while (p != null) {
219            if ((p.refreshFlags & RF_BOUND) != 0) {
220                return;
221            }
222
223            p.refreshFlags |= RF_BOUND;
224            p = p.parent;
225        }
226    }
227
228    /**
229     * <code>checkCulling</code> checks the spatial with the camera to see if it
230     * should be culled.
231     * <p>
232     * This method is called by the renderer. Usually it should not be called
233     * directly.
234     *
235     * @param cam The camera to check against.
236     * @return true if inside or intersecting camera frustum
237     * (should be rendered), false if outside.
238     */
239    public boolean checkCulling(Camera cam) {
240        if (refreshFlags != 0) {
241            throw new IllegalStateException("Scene graph is not properly updated for rendering.\n"
242                    + "State was changed after rootNode.updateGeometricState() call. \n"
243                    + "Make sure you do not modify the scene from another thread!\n"
244                    + "Problem spatial name: " + getName());
245        }
246
247        CullHint cm = getCullHint();
248        assert cm != CullHint.Inherit;
249        if (cm == Spatial.CullHint.Always) {
250            setLastFrustumIntersection(Camera.FrustumIntersect.Outside);
251            return false;
252        } else if (cm == Spatial.CullHint.Never) {
253            setLastFrustumIntersection(Camera.FrustumIntersect.Intersects);
254            return true;
255        }
256
257        // check to see if we can cull this node
258        frustrumIntersects = (parent != null ? parent.frustrumIntersects
259                : Camera.FrustumIntersect.Intersects);
260
261        if (frustrumIntersects == Camera.FrustumIntersect.Intersects) {
262            if (getQueueBucket() == Bucket.Gui) {
263                return cam.containsGui(getWorldBound());
264            } else {
265                frustrumIntersects = cam.contains(getWorldBound());
266            }
267        }
268
269        return frustrumIntersects != Camera.FrustumIntersect.Outside;
270    }
271
272    /**
273     * Sets the name of this spatial.
274     *
275     * @param name
276     *            The spatial's new name.
277     */
278    public void setName(String name) {
279        this.name = name;
280    }
281
282    /**
283     * Returns the name of this spatial.
284     *
285     * @return This spatial's name.
286     */
287    public String getName() {
288        return name;
289    }
290
291    /**
292     * Returns the local {@link LightList}, which are the lights
293     * that were directly attached to this <code>Spatial</code> through the
294     * {@link #addLight(com.jme3.light.Light) } and
295     * {@link #removeLight(com.jme3.light.Light) } methods.
296     *
297     * @return The local light list
298     */
299    public LightList getLocalLightList() {
300        return localLights;
301    }
302
303    /**
304     * Returns the world {@link LightList}, containing the lights
305     * combined from all this <code>Spatial's</code> parents up to and including
306     * this <code>Spatial</code>'s lights.
307     *
308     * @return The combined world light list
309     */
310    public LightList getWorldLightList() {
311        return worldLights;
312    }
313
314    /**
315     * <code>getWorldRotation</code> retrieves the absolute rotation of the
316     * Spatial.
317     *
318     * @return the Spatial's world rotation quaternion.
319     */
320    public Quaternion getWorldRotation() {
321        checkDoTransformUpdate();
322        return worldTransform.getRotation();
323    }
324
325    /**
326     * <code>getWorldTranslation</code> retrieves the absolute translation of
327     * the spatial.
328     *
329     * @return the Spatial's world tranlsation vector.
330     */
331    public Vector3f getWorldTranslation() {
332        checkDoTransformUpdate();
333        return worldTransform.getTranslation();
334    }
335
336    /**
337     * <code>getWorldScale</code> retrieves the absolute scale factor of the
338     * spatial.
339     *
340     * @return the Spatial's world scale factor.
341     */
342    public Vector3f getWorldScale() {
343        checkDoTransformUpdate();
344        return worldTransform.getScale();
345    }
346
347    /**
348     * <code>getWorldTransform</code> retrieves the world transformation
349     * of the spatial.
350     *
351     * @return the world transform.
352     */
353    public Transform getWorldTransform() {
354        checkDoTransformUpdate();
355        return worldTransform;
356    }
357
358    /**
359     * <code>rotateUpTo</code> is a utility function that alters the
360     * local rotation to point the Y axis in the direction given by newUp.
361     *
362     * @param newUp
363     *            the up vector to use - assumed to be a unit vector.
364     */
365    public void rotateUpTo(Vector3f newUp) {
366        TempVars vars = TempVars.get();
367
368        Vector3f compVecA = vars.vect1;
369        Quaternion q = vars.quat1;
370
371        // First figure out the current up vector.
372        Vector3f upY = compVecA.set(Vector3f.UNIT_Y);
373        Quaternion rot = localTransform.getRotation();
374        rot.multLocal(upY);
375
376        // get angle between vectors
377        float angle = upY.angleBetween(newUp);
378
379        // figure out rotation axis by taking cross product
380        Vector3f rotAxis = upY.crossLocal(newUp).normalizeLocal();
381
382        // Build a rotation quat and apply current local rotation.
383        q.fromAngleNormalAxis(angle, rotAxis);
384        q.mult(rot, rot);
385
386        vars.release();
387
388        setTransformRefresh();
389    }
390
391    /**
392     * <code>lookAt</code> is a convenience method for auto-setting the local
393     * rotation based on a position and an up vector. It computes the rotation
394     * to transform the z-axis to point onto 'position' and the y-axis to 'up'.
395     * Unlike {@link Quaternion#lookAt(com.jme3.math.Vector3f, com.jme3.math.Vector3f) }
396     * this method takes a world position to look at and not a relative direction.
397     *
398     * @param position
399     *            where to look at in terms of world coordinates
400     * @param upVector
401     *            a vector indicating the (local) up direction. (typically {0,
402     *            1, 0} in jME.)
403     */
404    public void lookAt(Vector3f position, Vector3f upVector) {
405        Vector3f worldTranslation = getWorldTranslation();
406
407        TempVars vars = TempVars.get();
408
409        Vector3f compVecA = vars.vect4;
410        vars.release();
411
412        compVecA.set(position).subtractLocal(worldTranslation);
413        getLocalRotation().lookAt(compVecA, upVector);
414
415        setTransformRefresh();
416    }
417
418    /**
419     * Should be overridden by Node and Geometry.
420     */
421    protected void updateWorldBound() {
422        // the world bound of a leaf is the same as it's model bound
423        // for a node, the world bound is a combination of all it's children
424        // bounds
425        // -> handled by subclass
426        refreshFlags &= ~RF_BOUND;
427    }
428
429    protected void updateWorldLightList() {
430        if (parent == null) {
431            worldLights.update(localLights, null);
432            refreshFlags &= ~RF_LIGHTLIST;
433        } else {
434            if ((parent.refreshFlags & RF_LIGHTLIST) == 0) {
435                worldLights.update(localLights, parent.worldLights);
436                refreshFlags &= ~RF_LIGHTLIST;
437            } else {
438                assert false;
439            }
440        }
441    }
442
443    /**
444     * Should only be called from updateGeometricState().
445     * In most cases should not be subclassed.
446     */
447    protected void updateWorldTransforms() {
448        if (parent == null) {
449            worldTransform.set(localTransform);
450            refreshFlags &= ~RF_TRANSFORM;
451        } else {
452            // check if transform for parent is updated
453            assert ((parent.refreshFlags & RF_TRANSFORM) == 0);
454            worldTransform.set(localTransform);
455            worldTransform.combineWithParent(parent.worldTransform);
456            refreshFlags &= ~RF_TRANSFORM;
457        }
458    }
459
460    /**
461     * Computes the world transform of this Spatial in the most
462     * efficient manner possible.
463     */
464    void checkDoTransformUpdate() {
465        if ((refreshFlags & RF_TRANSFORM) == 0) {
466            return;
467        }
468
469        if (parent == null) {
470            worldTransform.set(localTransform);
471            refreshFlags &= ~RF_TRANSFORM;
472        } else {
473            TempVars vars = TempVars.get();
474
475            Spatial[] stack = vars.spatialStack;
476            Spatial rootNode = this;
477            int i = 0;
478            while (true) {
479                Spatial hisParent = rootNode.parent;
480                if (hisParent == null) {
481                    rootNode.worldTransform.set(rootNode.localTransform);
482                    rootNode.refreshFlags &= ~RF_TRANSFORM;
483                    i--;
484                    break;
485                }
486
487                stack[i] = rootNode;
488
489                if ((hisParent.refreshFlags & RF_TRANSFORM) == 0) {
490                    break;
491                }
492
493                rootNode = hisParent;
494                i++;
495            }
496
497            vars.release();
498
499            for (int j = i; j >= 0; j--) {
500                rootNode = stack[j];
501                //rootNode.worldTransform.set(rootNode.localTransform);
502                //rootNode.worldTransform.combineWithParent(rootNode.parent.worldTransform);
503                //rootNode.refreshFlags &= ~RF_TRANSFORM;
504                rootNode.updateWorldTransforms();
505            }
506        }
507    }
508
509    /**
510     * Computes this Spatial's world bounding volume in the most efficient
511     * manner possible.
512     */
513    void checkDoBoundUpdate() {
514        if ((refreshFlags & RF_BOUND) == 0) {
515            return;
516        }
517
518        checkDoTransformUpdate();
519
520        // Go to children recursively and update their bound
521        if (this instanceof Node) {
522            Node node = (Node) this;
523            int len = node.getQuantity();
524            for (int i = 0; i < len; i++) {
525                Spatial child = node.getChild(i);
526                child.checkDoBoundUpdate();
527            }
528        }
529
530        // All children's bounds have been updated. Update my own now.
531        updateWorldBound();
532    }
533
534    private void runControlUpdate(float tpf) {
535        if (controls.isEmpty()) {
536            return;
537        }
538
539        for (Control c : controls.getArray()) {
540            c.update(tpf);
541        }
542    }
543
544    /**
545     * Called when the Spatial is about to be rendered, to notify
546     * controls attached to this Spatial using the Control.render() method.
547     *
548     * @param rm The RenderManager rendering the Spatial.
549     * @param vp The ViewPort to which the Spatial is being rendered to.
550     *
551     * @see Spatial#addControl(com.jme3.scene.control.Control)
552     * @see Spatial#getControl(java.lang.Class)
553     */
554    public void runControlRender(RenderManager rm, ViewPort vp) {
555        if (controls.isEmpty()) {
556            return;
557        }
558
559        for (Control c : controls.getArray()) {
560            c.render(rm, vp);
561        }
562    }
563
564    /**
565     * Add a control to the list of controls.
566     * @param control The control to add.
567     *
568     * @see Spatial#removeControl(java.lang.Class)
569     */
570    public void addControl(Control control) {
571        controls.add(control);
572        control.setSpatial(this);
573    }
574
575    /**
576     * Removes the first control that is an instance of the given class.
577     *
578     * @see Spatial#addControl(com.jme3.scene.control.Control)
579     */
580    public void removeControl(Class<? extends Control> controlType) {
581        for (int i = 0; i < controls.size(); i++) {
582            if (controlType.isAssignableFrom(controls.get(i).getClass())) {
583                Control control = controls.remove(i);
584                control.setSpatial(null);
585            }
586        }
587    }
588
589    /**
590     * Removes the given control from this spatial's controls.
591     *
592     * @param control The control to remove
593     * @return True if the control was successfuly removed. False if
594     * the control is not assigned to this spatial.
595     *
596     * @see Spatial#addControl(com.jme3.scene.control.Control)
597     */
598    public boolean removeControl(Control control) {
599        boolean result = controls.remove(control);
600        if (result) {
601            control.setSpatial(null);
602        }
603
604        return result;
605    }
606
607    /**
608     * Returns the first control that is an instance of the given class,
609     * or null if no such control exists.
610     *
611     * @param controlType The superclass of the control to look for.
612     * @return The first instance in the list of the controlType class, or null.
613     *
614     * @see Spatial#addControl(com.jme3.scene.control.Control)
615     */
616    public <T extends Control> T getControl(Class<T> controlType) {
617        for (Control c : controls.getArray()) {
618            if (controlType.isAssignableFrom(c.getClass())) {
619                return (T) c;
620            }
621        }
622        return null;
623    }
624
625    /**
626     * Returns the control at the given index in the list.
627     *
628     * @param index The index of the control in the list to find.
629     * @return The control at the given index.
630     *
631     * @throws IndexOutOfBoundsException
632     *      If the index is outside the range [0, getNumControls()-1]
633     *
634     * @see Spatial#addControl(com.jme3.scene.control.Control)
635     */
636    public Control getControl(int index) {
637        return controls.get(index);
638    }
639
640    /**
641     * @return The number of controls attached to this Spatial.
642     * @see Spatial#addControl(com.jme3.scene.control.Control)
643     * @see Spatial#removeControl(java.lang.Class)
644     */
645    public int getNumControls() {
646        return controls.size();
647    }
648
649    /**
650     * <code>updateLogicalState</code> calls the <code>update()</code> method
651     * for all controls attached to this Spatial.
652     *
653     * @param tpf Time per frame.
654     *
655     * @see Spatial#addControl(com.jme3.scene.control.Control)
656     */
657    public void updateLogicalState(float tpf) {
658        runControlUpdate(tpf);
659    }
660
661    /**
662     * <code>updateGeometricState</code> updates the lightlist,
663     * computes the world transforms, and computes the world bounds
664     * for this Spatial.
665     * Calling this when the Spatial is attached to a node
666     * will cause undefined results. User code should only call this
667     * method on Spatials having no parent.
668     *
669     * @see Spatial#getWorldLightList()
670     * @see Spatial#getWorldTransform()
671     * @see Spatial#getWorldBound()
672     */
673    public void updateGeometricState() {
674        // assume that this Spatial is a leaf, a proper implementation
675        // for this method should be provided by Node.
676
677        // NOTE: Update world transforms first because
678        // bound transform depends on them.
679        if ((refreshFlags & RF_LIGHTLIST) != 0) {
680            updateWorldLightList();
681        }
682        if ((refreshFlags & RF_TRANSFORM) != 0) {
683            updateWorldTransforms();
684        }
685        if ((refreshFlags & RF_BOUND) != 0) {
686            updateWorldBound();
687        }
688
689        assert refreshFlags == 0;
690    }
691
692    /**
693     * Convert a vector (in) from this spatials' local coordinate space to world
694     * coordinate space.
695     *
696     * @param in
697     *            vector to read from
698     * @param store
699     *            where to write the result (null to create a new vector, may be
700     *            same as in)
701     * @return the result (store)
702     */
703    public Vector3f localToWorld(final Vector3f in, Vector3f store) {
704        checkDoTransformUpdate();
705        return worldTransform.transformVector(in, store);
706    }
707
708    /**
709     * Convert a vector (in) from world coordinate space to this spatials' local
710     * coordinate space.
711     *
712     * @param in
713     *            vector to read from
714     * @param store
715     *            where to write the result
716     * @return the result (store)
717     */
718    public Vector3f worldToLocal(final Vector3f in, final Vector3f store) {
719        checkDoTransformUpdate();
720        return worldTransform.transformInverseVector(in, store);
721    }
722
723    /**
724     * <code>getParent</code> retrieves this node's parent. If the parent is
725     * null this is the root node.
726     *
727     * @return the parent of this node.
728     */
729    public Node getParent() {
730        return parent;
731    }
732
733    /**
734     * Called by {@link Node#attachChild(Spatial)} and
735     * {@link Node#detachChild(Spatial)} - don't call directly.
736     * <code>setParent</code> sets the parent of this node.
737     *
738     * @param parent
739     *            the parent of this node.
740     */
741    protected void setParent(Node parent) {
742        this.parent = parent;
743    }
744
745    /**
746     * <code>removeFromParent</code> removes this Spatial from it's parent.
747     *
748     * @return true if it has a parent and performed the remove.
749     */
750    public boolean removeFromParent() {
751        if (parent != null) {
752            parent.detachChild(this);
753            return true;
754        }
755        return false;
756    }
757
758    /**
759     * determines if the provided Node is the parent, or parent's parent, etc. of this Spatial.
760     *
761     * @param ancestor
762     *            the ancestor object to look for.
763     * @return true if the ancestor is found, false otherwise.
764     */
765    public boolean hasAncestor(Node ancestor) {
766        if (parent == null) {
767            return false;
768        } else if (parent.equals(ancestor)) {
769            return true;
770        } else {
771            return parent.hasAncestor(ancestor);
772        }
773    }
774
775    /**
776     * <code>getLocalRotation</code> retrieves the local rotation of this
777     * node.
778     *
779     * @return the local rotation of this node.
780     */
781    public Quaternion getLocalRotation() {
782        return localTransform.getRotation();
783    }
784
785    /**
786     * <code>setLocalRotation</code> sets the local rotation of this node
787     * by using a {@link Matrix3f}.
788     *
789     * @param rotation
790     *            the new local rotation.
791     */
792    public void setLocalRotation(Matrix3f rotation) {
793        localTransform.getRotation().fromRotationMatrix(rotation);
794        setTransformRefresh();
795    }
796
797    /**
798     * <code>setLocalRotation</code> sets the local rotation of this node.
799     *
800     * @param quaternion
801     *            the new local rotation.
802     */
803    public void setLocalRotation(Quaternion quaternion) {
804        localTransform.setRotation(quaternion);
805        setTransformRefresh();
806    }
807
808    /**
809     * <code>getLocalScale</code> retrieves the local scale of this node.
810     *
811     * @return the local scale of this node.
812     */
813    public Vector3f getLocalScale() {
814        return localTransform.getScale();
815    }
816
817    /**
818     * <code>setLocalScale</code> sets the local scale of this node.
819     *
820     * @param localScale
821     *            the new local scale, applied to x, y and z
822     */
823    public void setLocalScale(float localScale) {
824        localTransform.setScale(localScale);
825        setTransformRefresh();
826    }
827
828    /**
829     * <code>setLocalScale</code> sets the local scale of this node.
830     */
831    public void setLocalScale(float x, float y, float z) {
832        localTransform.setScale(x, y, z);
833        setTransformRefresh();
834    }
835
836    /**
837     * <code>setLocalScale</code> sets the local scale of this node.
838     *
839     * @param localScale
840     *            the new local scale.
841     */
842    public void setLocalScale(Vector3f localScale) {
843        localTransform.setScale(localScale);
844        setTransformRefresh();
845    }
846
847    /**
848     * <code>getLocalTranslation</code> retrieves the local translation of
849     * this node.
850     *
851     * @return the local translation of this node.
852     */
853    public Vector3f getLocalTranslation() {
854        return localTransform.getTranslation();
855    }
856
857    /**
858     * <code>setLocalTranslation</code> sets the local translation of this
859     * spatial.
860     *
861     * @param localTranslation
862     *            the local translation of this spatial.
863     */
864    public void setLocalTranslation(Vector3f localTranslation) {
865        this.localTransform.setTranslation(localTranslation);
866        setTransformRefresh();
867    }
868
869    /**
870     * <code>setLocalTranslation</code> sets the local translation of this
871     * spatial.
872     */
873    public void setLocalTranslation(float x, float y, float z) {
874        this.localTransform.setTranslation(x, y, z);
875        setTransformRefresh();
876    }
877
878    /**
879     * <code>setLocalTransform</code> sets the local transform of this
880     * spatial.
881     */
882    public void setLocalTransform(Transform t) {
883        this.localTransform.set(t);
884        setTransformRefresh();
885    }
886
887    /**
888     * <code>getLocalTransform</code> retrieves the local transform of
889     * this spatial.
890     *
891     * @return the local transform of this spatial.
892     */
893    public Transform getLocalTransform() {
894        return localTransform;
895    }
896
897    /**
898     * Applies the given material to the Spatial, this will propagate the
899     * material down to the geometries in the scene graph.
900     *
901     * @param material The material to set.
902     */
903    public void setMaterial(Material material) {
904    }
905
906    /**
907     * <code>addLight</code> adds the given light to the Spatial; causing
908     * all child Spatials to be effected by it.
909     *
910     * @param light The light to add.
911     */
912    public void addLight(Light light) {
913        localLights.add(light);
914        setLightListRefresh();
915    }
916
917    /**
918     * <code>removeLight</code> removes the given light from the Spatial.
919     *
920     * @param light The light to remove.
921     * @see Spatial#addLight(com.jme3.light.Light)
922     */
923    public void removeLight(Light light) {
924        localLights.remove(light);
925        setLightListRefresh();
926    }
927
928    /**
929     * Translates the spatial by the given translation vector.
930     *
931     * @return The spatial on which this method is called, e.g <code>this</code>.
932     */
933    public Spatial move(float x, float y, float z) {
934        this.localTransform.getTranslation().addLocal(x, y, z);
935        setTransformRefresh();
936
937        return this;
938    }
939
940    /**
941     * Translates the spatial by the given translation vector.
942     *
943     * @return The spatial on which this method is called, e.g <code>this</code>.
944     */
945    public Spatial move(Vector3f offset) {
946        this.localTransform.getTranslation().addLocal(offset);
947        setTransformRefresh();
948
949        return this;
950    }
951
952    /**
953     * Scales the spatial by the given value
954     *
955     * @return The spatial on which this method is called, e.g <code>this</code>.
956     */
957    public Spatial scale(float s) {
958        return scale(s, s, s);
959    }
960
961    /**
962     * Scales the spatial by the given scale vector.
963     *
964     * @return The spatial on which this method is called, e.g <code>this</code>.
965     */
966    public Spatial scale(float x, float y, float z) {
967        this.localTransform.getScale().multLocal(x, y, z);
968        setTransformRefresh();
969
970        return this;
971    }
972
973    /**
974     * Rotates the spatial by the given rotation.
975     *
976     * @return The spatial on which this method is called, e.g <code>this</code>.
977     */
978    public Spatial rotate(Quaternion rot) {
979        this.localTransform.getRotation().multLocal(rot);
980        setTransformRefresh();
981
982        return this;
983    }
984
985    /**
986     * Rotates the spatial by the yaw, roll and pitch angles (in radians),
987     * in the local coordinate space.
988     *
989     * @return The spatial on which this method is called, e.g <code>this</code>.
990     */
991    public Spatial rotate(float yaw, float roll, float pitch) {
992        TempVars vars = TempVars.get();
993        Quaternion q = vars.quat1;
994        q.fromAngles(yaw, roll, pitch);
995        rotate(q);
996        vars.release();
997
998        return this;
999    }
1000
1001    /**
1002     * Centers the spatial in the origin of the world bound.
1003     * @return The spatial on which this method is called, e.g <code>this</code>.
1004     */
1005    public Spatial center() {
1006        Vector3f worldTrans = getWorldTranslation();
1007        Vector3f worldCenter = getWorldBound().getCenter();
1008
1009        Vector3f absTrans = worldTrans.subtract(worldCenter);
1010        setLocalTranslation(absTrans);
1011
1012        return this;
1013    }
1014
1015    /**
1016     * @see #setCullHint(CullHint)
1017     * @return the cull mode of this spatial, or if set to CullHint.Inherit,
1018     * the cullmode of it's parent.
1019     */
1020    public CullHint getCullHint() {
1021        if (cullHint != CullHint.Inherit) {
1022            return cullHint;
1023        } else if (parent != null) {
1024            return parent.getCullHint();
1025        } else {
1026            return CullHint.Dynamic;
1027        }
1028    }
1029
1030    public BatchHint getBatchHint() {
1031        if (batchHint != BatchHint.Inherit) {
1032            return batchHint;
1033        } else if (parent != null) {
1034            return parent.getBatchHint();
1035        } else {
1036            return BatchHint.Always;
1037        }
1038    }
1039
1040    /**
1041     * Returns this spatial's renderqueue bucket. If the mode is set to inherit,
1042     * then the spatial gets its renderqueue bucket from its parent.
1043     *
1044     * @return The spatial's current renderqueue mode.
1045     */
1046    public RenderQueue.Bucket getQueueBucket() {
1047        if (queueBucket != RenderQueue.Bucket.Inherit) {
1048            return queueBucket;
1049        } else if (parent != null) {
1050            return parent.getQueueBucket();
1051        } else {
1052            return RenderQueue.Bucket.Opaque;
1053        }
1054    }
1055
1056    /**
1057     * @return The shadow mode of this spatial, if the local shadow
1058     * mode is set to inherit, then the parent's shadow mode is returned.
1059     *
1060     * @see Spatial#setShadowMode(com.jme3.renderer.queue.RenderQueue.ShadowMode)
1061     * @see ShadowMode
1062     */
1063    public RenderQueue.ShadowMode getShadowMode() {
1064        if (shadowMode != RenderQueue.ShadowMode.Inherit) {
1065            return shadowMode;
1066        } else if (parent != null) {
1067            return parent.getShadowMode();
1068        } else {
1069            return ShadowMode.Off;
1070        }
1071    }
1072
1073    /**
1074     * Sets the level of detail to use when rendering this Spatial,
1075     * this call propagates to all geometries under this Spatial.
1076     *
1077     * @param lod The lod level to set.
1078     */
1079    public void setLodLevel(int lod) {
1080    }
1081
1082    /**
1083     * <code>updateModelBound</code> recalculates the bounding object for this
1084     * Spatial.
1085     */
1086    public abstract void updateModelBound();
1087
1088    /**
1089     * <code>setModelBound</code> sets the bounding object for this Spatial.
1090     *
1091     * @param modelBound
1092     *            the bounding object for this spatial.
1093     */
1094    public abstract void setModelBound(BoundingVolume modelBound);
1095
1096    /**
1097     * @return The sum of all verticies under this Spatial.
1098     */
1099    public abstract int getVertexCount();
1100
1101    /**
1102     * @return The sum of all triangles under this Spatial.
1103     */
1104    public abstract int getTriangleCount();
1105
1106    /**
1107     * @return A clone of this Spatial, the scene graph in its entirety
1108     * is cloned and can be altered independently of the original scene graph.
1109     *
1110     * Note that meshes of geometries are not cloned explicitly, they
1111     * are shared if static, or specially cloned if animated.
1112     *
1113     * All controls will be cloned using the Control.cloneForSpatial method
1114     * on the clone.
1115     *
1116     * @see Mesh#cloneForAnim()
1117     */
1118    public Spatial clone(boolean cloneMaterial) {
1119        try {
1120            Spatial clone = (Spatial) super.clone();
1121            if (worldBound != null) {
1122                clone.worldBound = worldBound.clone();
1123            }
1124            clone.worldLights = worldLights.clone();
1125            clone.localLights = localLights.clone();
1126
1127            // Set the new owner of the light lists
1128            clone.localLights.setOwner(clone);
1129            clone.worldLights.setOwner(clone);
1130
1131            // No need to force cloned to update.
1132            // This node already has the refresh flags
1133            // set below so it will have to update anyway.
1134            clone.worldTransform = worldTransform.clone();
1135            clone.localTransform = localTransform.clone();
1136
1137            if (clone instanceof Node) {
1138                Node node = (Node) this;
1139                Node nodeClone = (Node) clone;
1140                nodeClone.children = new SafeArrayList<Spatial>(Spatial.class);
1141                for (Spatial child : node.children) {
1142                    Spatial childClone = child.clone(cloneMaterial);
1143                    childClone.parent = nodeClone;
1144                    nodeClone.children.add(childClone);
1145                }
1146            }
1147
1148            clone.parent = null;
1149            clone.setBoundRefresh();
1150            clone.setTransformRefresh();
1151            clone.setLightListRefresh();
1152
1153            clone.controls = new SafeArrayList<Control>(Control.class);
1154            for (int i = 0; i < controls.size(); i++) {
1155                clone.controls.add(controls.get(i).cloneForSpatial(clone));
1156            }
1157
1158            if (userData != null) {
1159                clone.userData = (HashMap<String, Savable>) userData.clone();
1160            }
1161
1162            return clone;
1163        } catch (CloneNotSupportedException ex) {
1164            throw new AssertionError();
1165        }
1166    }
1167
1168    /**
1169     * @return A clone of this Spatial, the scene graph in its entirety
1170     * is cloned and can be altered independently of the original scene graph.
1171     *
1172     * Note that meshes of geometries are not cloned explicitly, they
1173     * are shared if static, or specially cloned if animated.
1174     *
1175     * All controls will be cloned using the Control.cloneForSpatial method
1176     * on the clone.
1177     *
1178     * @see Mesh#cloneForAnim()
1179     */
1180    @Override
1181    public Spatial clone() {
1182        return clone(true);
1183    }
1184
1185    /**
1186     * @return Similar to Spatial.clone() except will create a deep clone
1187     * of all geometry's meshes, normally this method shouldn't be used
1188     * instead use Spatial.clone()
1189     *
1190     * @see Spatial#clone()
1191     */
1192    public abstract Spatial deepClone();
1193
1194    public void setUserData(String key, Object data) {
1195        if (userData == null) {
1196            userData = new HashMap<String, Savable>();
1197        }
1198
1199        if (data instanceof Savable) {
1200            userData.put(key, (Savable) data);
1201        } else {
1202            userData.put(key, new UserData(UserData.getObjectType(data), data));
1203        }
1204    }
1205
1206    @SuppressWarnings("unchecked")
1207    public <T> T getUserData(String key) {
1208        if (userData == null) {
1209            return null;
1210        }
1211
1212        Savable s = userData.get(key);
1213        if (s instanceof UserData) {
1214            return (T) ((UserData) s).getValue();
1215        } else {
1216            return (T) s;
1217        }
1218    }
1219
1220    public Collection<String> getUserDataKeys() {
1221        if (userData != null) {
1222            return userData.keySet();
1223        }
1224
1225        return Collections.EMPTY_SET;
1226    }
1227
1228    /**
1229     * Note that we are <i>matching</i> the pattern, therefore the pattern
1230     * must match the entire pattern (i.e. it behaves as if it is sandwiched
1231     * between "^" and "$").
1232     * You can set regex modes, like case insensitivity, by using the (?X)
1233     * or (?X:Y) constructs.
1234     *
1235     * @param spatialSubclass Subclass which this must implement.
1236     *                        Null causes all Spatials to qualify.
1237     * @param nameRegex  Regular expression to match this name against.
1238     *                        Null causes all Names to qualify.
1239     * @return true if this implements the specified class and this's name
1240     *         matches the specified pattern.
1241     *
1242     * @see java.util.regex.Pattern
1243     */
1244    public boolean matches(Class<? extends Spatial> spatialSubclass,
1245            String nameRegex) {
1246        if (spatialSubclass != null && !spatialSubclass.isInstance(this)) {
1247            return false;
1248        }
1249
1250        if (nameRegex != null && (name == null || !name.matches(nameRegex))) {
1251            return false;
1252        }
1253
1254        return true;
1255    }
1256
1257    public void write(JmeExporter ex) throws IOException {
1258        OutputCapsule capsule = ex.getCapsule(this);
1259        capsule.write(name, "name", null);
1260        capsule.write(worldBound, "world_bound", null);
1261        capsule.write(cullHint, "cull_mode", CullHint.Inherit);
1262        capsule.write(batchHint, "batch_hint", BatchHint.Inherit);
1263        capsule.write(queueBucket, "queue", RenderQueue.Bucket.Inherit);
1264        capsule.write(shadowMode, "shadow_mode", ShadowMode.Inherit);
1265        capsule.write(localTransform, "transform", Transform.IDENTITY);
1266        capsule.write(localLights, "lights", null);
1267
1268        // Shallow clone the controls array to convert its type.
1269        capsule.writeSavableArrayList(new ArrayList(controls), "controlsList", null);
1270        capsule.writeStringSavableMap(userData, "user_data", null);
1271    }
1272
1273    public void read(JmeImporter im) throws IOException {
1274        InputCapsule ic = im.getCapsule(this);
1275
1276        name = ic.readString("name", null);
1277        worldBound = (BoundingVolume) ic.readSavable("world_bound", null);
1278        cullHint = ic.readEnum("cull_mode", CullHint.class, CullHint.Inherit);
1279        batchHint = ic.readEnum("batch_hint", BatchHint.class, BatchHint.Inherit);
1280        queueBucket = ic.readEnum("queue", RenderQueue.Bucket.class,
1281                RenderQueue.Bucket.Inherit);
1282        shadowMode = ic.readEnum("shadow_mode", ShadowMode.class,
1283                ShadowMode.Inherit);
1284
1285        localTransform = (Transform) ic.readSavable("transform", Transform.IDENTITY);
1286
1287        localLights = (LightList) ic.readSavable("lights", null);
1288        localLights.setOwner(this);
1289
1290        //changed for backward compatibility with j3o files generated before the AnimControl/SkeletonControl split
1291        //the AnimControl creates the SkeletonControl for old files and add it to the spatial.
1292        //The SkeletonControl must be the last in the stack so we add the list of all other control before it.
1293        //When backward compatibility won't be needed anymore this can be replaced by :
1294        //controls = ic.readSavableArrayList("controlsList", null));
1295        controls.addAll(0, ic.readSavableArrayList("controlsList", null));
1296
1297        userData = (HashMap<String, Savable>) ic.readStringSavableMap("user_data", null);
1298    }
1299
1300    /**
1301     * <code>getWorldBound</code> retrieves the world bound at this node
1302     * level.
1303     *
1304     * @return the world bound at this level.
1305     */
1306    public BoundingVolume getWorldBound() {
1307        checkDoBoundUpdate();
1308        return worldBound;
1309    }
1310
1311    /**
1312     * <code>setCullHint</code> sets how scene culling should work on this
1313     * spatial during drawing. NOTE: You must set this AFTER attaching to a
1314     * parent or it will be reset with the parent's cullMode value.
1315     *
1316     * @param hint
1317     *            one of CullHint.Dynamic, CullHint.Always, CullHint.Inherit or
1318     *            CullHint.Never
1319     */
1320    public void setCullHint(CullHint hint) {
1321        cullHint = hint;
1322    }
1323
1324    /**
1325     * <code>setBatchHint</code> sets how batching should work on this
1326     * spatial. NOTE: You must set this AFTER attaching to a
1327     * parent or it will be reset with the parent's cullMode value.
1328     *
1329     * @param hint
1330     *            one of BatchHint.Never, BatchHint.Always, BatchHint.Inherit
1331     */
1332    public void setBatchHint(BatchHint hint) {
1333        batchHint = hint;
1334    }
1335
1336    /**
1337     * @return the cullmode set on this Spatial
1338     */
1339    public CullHint getLocalCullHint() {
1340        return cullHint;
1341    }
1342
1343    /**
1344     * @return the batchHint set on this Spatial
1345     */
1346    public BatchHint getLocalBatchHint() {
1347        return batchHint;
1348    }
1349
1350    /**
1351     * <code>setQueueBucket</code> determines at what phase of the
1352     * rendering process this Spatial will rendered. See the
1353     * {@link Bucket} enum for an explanation of the various
1354     * render queue buckets.
1355     *
1356     * @param queueBucket
1357     *            The bucket to use for this Spatial.
1358     */
1359    public void setQueueBucket(RenderQueue.Bucket queueBucket) {
1360        this.queueBucket = queueBucket;
1361    }
1362
1363    /**
1364     * Sets the shadow mode of the spatial
1365     * The shadow mode determines how the spatial should be shadowed,
1366     * when a shadowing technique is used. See the
1367     * documentation for the class {@link ShadowMode} for more information.
1368     *
1369     * @see ShadowMode
1370     *
1371     * @param shadowMode The local shadow mode to set.
1372     */
1373    public void setShadowMode(RenderQueue.ShadowMode shadowMode) {
1374        this.shadowMode = shadowMode;
1375    }
1376
1377    /**
1378     * @return The locally set queue bucket mode
1379     *
1380     * @see Spatial#setQueueBucket(com.jme3.renderer.queue.RenderQueue.Bucket)
1381     */
1382    public RenderQueue.Bucket getLocalQueueBucket() {
1383        return queueBucket;
1384    }
1385
1386    /**
1387     * @return The locally set shadow mode
1388     *
1389     * @see Spatial#setShadowMode(com.jme3.renderer.queue.RenderQueue.ShadowMode)
1390     */
1391    public RenderQueue.ShadowMode getLocalShadowMode() {
1392        return shadowMode;
1393    }
1394
1395    /**
1396     * Returns this spatial's last frustum intersection result. This int is set
1397     * when a check is made to determine if the bounds of the object fall inside
1398     * a camera's frustum. If a parent is found to fall outside the frustum, the
1399     * value for this spatial will not be updated.
1400     *
1401     * @return The spatial's last frustum intersection result.
1402     */
1403    public Camera.FrustumIntersect getLastFrustumIntersection() {
1404        return frustrumIntersects;
1405    }
1406
1407    /**
1408     * Overrides the last intersection result. This is useful for operations
1409     * that want to start rendering at the middle of a scene tree and don't want
1410     * the parent of that node to influence culling.
1411     *
1412     * @param intersects
1413     *            the new value
1414     */
1415    public void setLastFrustumIntersection(Camera.FrustumIntersect intersects) {
1416        frustrumIntersects = intersects;
1417    }
1418
1419    /**
1420     * Returns the Spatial's name followed by the class of the spatial <br>
1421     * Example: "MyNode (com.jme3.scene.Spatial)
1422     *
1423     * @return Spatial's name followed by the class of the Spatial
1424     */
1425    @Override
1426    public String toString() {
1427        return name + " (" + this.getClass().getSimpleName() + ')';
1428    }
1429
1430    /**
1431     * Creates a transform matrix that will convert from this spatials'
1432     * local coordinate space to the world coordinate space
1433     * based on the world transform.
1434     *
1435     * @param store Matrix where to store the result, if null, a new one
1436     * will be created and returned.
1437     *
1438     * @return store if not null, otherwise, a new matrix containing the result.
1439     *
1440     * @see Spatial#getWorldTransform()
1441     */
1442    public Matrix4f getLocalToWorldMatrix(Matrix4f store) {
1443        if (store == null) {
1444            store = new Matrix4f();
1445        } else {
1446            store.loadIdentity();
1447        }
1448        // multiply with scale first, then rotate, finally translate (cf.
1449        // Eberly)
1450        store.scale(getWorldScale());
1451        store.multLocal(getWorldRotation());
1452        store.setTranslation(getWorldTranslation());
1453        return store;
1454    }
1455
1456    /**
1457     * Visit each scene graph element ordered by DFS
1458     * @param visitor
1459     */
1460    public abstract void depthFirstTraversal(SceneGraphVisitor visitor);
1461
1462    /**
1463     * Visit each scene graph element ordered by BFS
1464     * @param visitor
1465     */
1466    public void breadthFirstTraversal(SceneGraphVisitor visitor) {
1467        Queue<Spatial> queue = new LinkedList<Spatial>();
1468        queue.add(this);
1469
1470        while (!queue.isEmpty()) {
1471            Spatial s = queue.poll();
1472            visitor.visit(s);
1473            s.breadthFirstTraversal(visitor, queue);
1474        }
1475    }
1476
1477    protected abstract void breadthFirstTraversal(SceneGraphVisitor visitor, Queue<Spatial> queue);
1478}
1479