Package com.ardor3d.scenegraph

Source Code of com.ardor3d.scenegraph.Node

/**
* Copyright (c) 2008-2012 Ardor Labs, Inc.
*
* This file is part of Ardor3D.
*
* 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>.
*/

package com.ardor3d.scenegraph;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.EnumSet;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

import com.ardor3d.bounding.BoundingVolume;
import com.ardor3d.math.Vector3;
import com.ardor3d.renderer.Renderer;
import com.ardor3d.renderer.state.RenderState;
import com.ardor3d.scenegraph.event.DirtyType;
import com.ardor3d.scenegraph.visitor.Visitor;
import com.ardor3d.util.Ardor3dException;
import com.ardor3d.util.export.InputCapsule;
import com.ardor3d.util.export.OutputCapsule;
import com.ardor3d.util.scenegraph.RenderDelegate;

/**
* Node defines an internal node of a scene graph. The internal node maintains a collection of children and handles
* merging said children into a single bound to allow for very fast culling of multiple nodes. Node allows for any
* number of children to be attached.
*/
public class Node extends Spatial {
    private static final Logger logger = Logger.getLogger(Node.class.getName());

    /** This node's children. */
    protected final List<Spatial> _children;

    /**
     * Constructs a new Spatial.
     */
    public Node() {
        super();
        _children = Collections.synchronizedList(new ArrayList<Spatial>(1));
    }

    /**
     * Constructs a new <code>Node</code> with a given name.
     *
     * @param name
     *            the name of the node. This is required for identification purposes.
     */
    public Node(final String name) {
        this(name, Collections.synchronizedList(new ArrayList<Spatial>(1)));
    }

    /**
     * Constructs a new <code>Node</code> with a given name.
     *
     * @param name
     *            the name of the node. This is required for identification purposes.
     * @param children
     *            the list to use for storing children. Defaults to a synchronized ArrayList, but using this
     *            constructor, you can select a different kind of list.
     */
    public Node(final String name, final List<Spatial> children) {
        super(name);
        _children = children;
    }

    /**
     *
     * <code>attachChild</code> attaches a child to this node. This node becomes the child's parent. The current number
     * of children maintained is returned. <br>
     * If the child already had a parent it is detached from that former parent.
     *
     * @param child
     *            the child to attach to this node.
     * @return the number of children maintained by this node.
     */
    public int attachChild(final Spatial child) {
        if (child != null) {
            if (child == this || (child instanceof Node && hasAncestor((Node) child))) {
                throw new IllegalArgumentException("Child is already part of this hierarchy.");
            }
            if (child.getParent() != this) {
                if (child.getParent() != null) {
                    child.getParent().detachChild(child);
                }
                child.setParent(this);
                _children.add(child);
                child.markDirty(DirtyType.Attached);
                if (logger.isLoggable(Level.FINE)) {
                    logger.fine("Child (" + child.getName() + ") attached to this" + " node (" + getName() + ")");
                }
            }
        }

        return _children.size();
    }

    /**
     *
     * <code>attachChildAt</code> attaches a child to this node at an index. This node becomes the child's parent. The
     * current number of children maintained is returned. <br>
     * If the child already had a parent it is detached from that former parent.
     *
     * @param child
     *            the child to attach to this node.
     * @return the number of children maintained by this node.
     */
    public int attachChildAt(final Spatial child, final int index) {
        if (child != null) {
            if (child == this || (child instanceof Node && hasAncestor((Node) child))) {
                throw new IllegalArgumentException("Child is already part of this hierarchy.");
            }
            if (child.getParent() != this) {
                if (child.getParent() != null) {
                    child.getParent().detachChild(child);
                }
                child.setParent(this);
                _children.add(index, child);
                child.markDirty(DirtyType.Attached);
                if (logger.isLoggable(Level.FINE)) {
                    logger.fine("Child (" + child.getName() + ") attached to this" + " node (" + getName() + ")");
                }
            }
        }

        return _children.size();
    }

    /**
     * <code>detachChild</code> removes a given child from the node's list. This child will no longe be maintained.
     *
     * @param child
     *            the child to remove.
     * @return the index the child was at. -1 if the child was not in the list.
     */
    public int detachChild(final Spatial child) {
        if (child == null) {
            return -1;
        }
        if (child.getParent() == this) {
            final int index = _children.indexOf(child);
            if (index != -1) {
                detachChildAt(index);
            }
            return index;
        }

        return -1;
    }

    /**
     * <code>detachChild</code> removes a given child from the node's list. This child will no longe be maintained. Only
     * the first child with a matching name is removed.
     *
     * @param childName
     *            the child to remove.
     * @return the index the child was at. -1 if the child was not in the list.
     */
    public int detachChildNamed(final String childName) {
        if (childName == null) {
            return -1;
        }
        for (int i = getNumberOfChildren() - 1; i >= 0; i--) {
            final Spatial child = _children.get(i);
            if (childName.equals(child.getName())) {
                detachChildAt(i);
                return i;
            }
        }
        return -1;
    }

    /**
     *
     * <code>detachChildAt</code> removes a child at a given index. That child is returned for saving purposes.
     *
     * @param index
     *            the index of the child to be removed.
     * @return the child at the supplied index.
     */
    public Spatial detachChildAt(final int index) {
        final Spatial child = _children.remove(index);
        if (child != null) {
            child.setParent(null);
            markDirty(child, DirtyType.Detached);
            if (child.getListener() != null) {
                child.setListener(null);
            }
            if (logger.isLoggable(Level.INFO)) {
                logger.fine("Child removed.");
            }
        }
        return child;
    }

    /**
     *
     * <code>detachAllChildren</code> removes all children attached to this node.
     */
    public void detachAllChildren() {
        for (int i = getNumberOfChildren() - 1; i >= 0; i--) {
            detachChildAt(i);
        }
        logger.fine("All children removed.");
    }

    /**
     * Get the index of the specified spatial.
     *
     * @param sp
     *            spatial to retrieve index for.
     * @return the index
     */
    public int getChildIndex(final Spatial sp) {
        return _children.indexOf(sp);
    }

    /**
     * Returns all children to this node.
     *
     * @return a list containing all children to this node
     */
    public List<Spatial> getChildren() {
        return _children;
    }

    /**
     * Swaps two children.
     *
     * @param index1
     * @param index2
     */
    public void swapChildren(final int index1, final int index2) {
        final Spatial c2 = _children.get(index2);
        final Spatial c1 = _children.remove(index1);
        _children.add(index1, c2);
        _children.remove(index2);
        _children.add(index2, c1);
    }

    @Override
    protected void updateChildren(final double time) {
        for (int i = getNumberOfChildren() - 1; i >= 0; i--) {
            final Spatial pkChild = getChild(i);
            if (pkChild != null) {
                pkChild.updateGeometricState(time, false);
            }
        }
    }

    /**
     *
     * <code>getChild</code> returns a child at a given index.
     *
     * @param i
     *            the index to retrieve the child from.
     * @return the child at a specified index.
     */
    public Spatial getChild(final int i) {
        return _children.get(i);
    }

    /**
     * <code>getChild</code> returns the first child found with exactly the given name (case sensitive.) If our children
     * are Nodes, we will search their children as well.
     *
     * @param name
     *            the name of the child to retrieve. If null, we'll return null.
     * @return the child if found, or null.
     */
    public Spatial getChild(final String name) {
        if (name == null) {
            return null;
        }
        for (int i = getNumberOfChildren() - 1; i >= 0; i--) {
            final Spatial child = _children.get(i);
            if (name.equals(child.getName())) {
                return child;
            } else if (child instanceof Node) {
                final Spatial out = ((Node) child).getChild(name);
                if (out != null) {
                    return out;
                }
            }
        }
        return null;
    }

    /**
     * determines if the provided Spatial is contained in the children list of this node.
     *
     * @param spat
     *            the child object to look for.
     * @return true if the object is contained, false otherwise.
     */
    public boolean hasChild(final Spatial spat) {
        if (_children.contains(spat)) {
            return true;
        }

        for (int i = getNumberOfChildren() - 1; i >= 0; i--) {
            final Spatial child = _children.get(i);
            if (child instanceof Node && ((Node) child).hasChild(spat)) {
                return true;
            }
        }

        return false;
    }

    /**
     *
     * <code>getNumberOfChildren</code> returns the number of children this node maintains.
     *
     * @return the number of children this node maintains.
     */
    public int getNumberOfChildren() {
        return _children.size();
    }

    @Override
    protected void propagateDirtyDown(final EnumSet<DirtyType> dirtyTypes) {
        super.propagateDirtyDown(dirtyTypes);

        for (int i = getNumberOfChildren() - 1; i >= 0; i--) {
            final Spatial child = _children.get(i);

            child.propagateDirtyDown(dirtyTypes);
        }
    }

    @Override
    public void updateWorldTransform(final boolean recurse) {
        super.updateWorldTransform(recurse);

        if (recurse) {
            for (int i = getNumberOfChildren() - 1; i >= 0; i--) {
                _children.get(i).updateWorldTransform(true);
            }
        }
    }

    @Override
    protected void updateWorldRenderStates(final boolean recurse, final RenderState.StateStack stack) {
        super.updateWorldRenderStates(recurse, stack);

        if (recurse) {
            for (int i = getNumberOfChildren() - 1; i >= 0; i--) {
                _children.get(i).updateWorldRenderStates(true, stack);
            }
        }
    }

    /**
     * <code>draw</code> calls the onDraw method for each child maintained by this node.
     *
     * @see com.ardor3d.scenegraph.Spatial#draw(com.ardor3d.renderer.Renderer)
     * @param r
     *            the renderer to draw to.
     */
    @Override
    public void draw(final Renderer r) {

        final RenderDelegate delegate = getCurrentRenderDelegate();
        if (delegate == null) {
            Spatial child;
            for (int i = getNumberOfChildren() - 1; i >= 0; i--) {
                child = _children.get(i);
                if (child != null) {
                    child.onDraw(r);
                }
            }
        } else {
            // Queue as needed
            if (!r.isProcessingQueue()) {
                if (r.checkAndAdd(this)) {
                    return;
                }
            }

            delegate.render(this, r);
        }
    }

    /**
     * <code>updateWorldBound</code> merges the bounds of all the children maintained by this node. This will allow for
     * faster culling operations.
     *
     * @see com.ardor3d.scenegraph.Spatial#updateWorldBound(boolean)
     */
    @Override
    public void updateWorldBound(final boolean recurse) {
        BoundingVolume worldBound = null;
        for (int i = getNumberOfChildren() - 1; i >= 0; i--) {
            final Spatial child = _children.get(i);
            if (child != null) {
                if (recurse) {
                    child.updateWorldBound(true);
                }
                if (worldBound != null) {
                    // merge current world bound with child world bound
                    worldBound.mergeLocal(child.getWorldBound());

                    // simple check to catch NaN issues
                    if (!Vector3.isValid(worldBound.getCenter())) {
                        throw new Ardor3dException("WorldBound center is invalid after merge between " + this + " and "
                                + child);
                    }
                } else {
                    // set world bound to first non-null child world bound
                    if (child.getWorldBound() != null) {
                        worldBound = child.getWorldBound().clone(_worldBound);
                    }
                }
            }
        }
        _worldBound = worldBound;
        clearDirty(DirtyType.Bounding);
    }

    @Override
    public void acceptVisitor(final Visitor visitor, final boolean preexecute) {
        if (preexecute) {
            visitor.visit(this);
        }

        Spatial child;
        for (int i = getNumberOfChildren() - 1; i >= 0; i--) {
            child = _children.get(i);
            if (child != null) {
                child.acceptVisitor(visitor, preexecute);
            }
        }

        if (!preexecute) {
            visitor.visit(this);
        }
    }

    @Override
    public void sortLights() {
        for (int i = getNumberOfChildren() - 1; i >= 0; i--) {
            final Spatial pkChild = getChild(i);
            if (pkChild != null) {
                pkChild.sortLights();
            }
        }
    }

    @Override
    public Node makeCopy(final boolean shareGeometricData) {
        // get copy of basic spatial info
        final Node node = (Node) super.makeCopy(shareGeometricData);

        // add copy of children
        for (final Spatial child : getChildren()) {
            final Spatial copy = child.makeCopy(shareGeometricData);
            node.attachChild(copy);
        }

        // return
        return node;
    }

    @Override
    public Node makeInstanced() {
        // get copy of basic spatial info
        final Node node = (Node) super.makeInstanced();
        // add copy of children
        for (final Spatial child : getChildren()) {
            final Spatial copy = child.makeInstanced();
            node.attachChild(copy);
        }
        return node;
    }

    // /////////////////
    // Methods for Savable
    // /////////////////

    @Override
    public Class<? extends Node> getClassTag() {
        return this.getClass();
    }

    @Override
    public void write(final OutputCapsule capsule) throws IOException {
        super.write(capsule);
        capsule.writeSavableList(new ArrayList<Spatial>(_children), "children", null);
    }

    @Override
    public void read(final InputCapsule capsule) throws IOException {
        super.read(capsule);
        final List<Spatial> cList = capsule.readSavableList("children", null);
        _children.clear();
        if (cList != null) {
            _children.addAll(cList);
        }

        // go through children and set parent to this node
        for (int i = getNumberOfChildren() - 1; i >= 0; i--) {
            final Spatial child = _children.get(i);
            child._parent = this;
        }
    }
}
TOP

Related Classes of com.ardor3d.scenegraph.Node

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.