com.badlogic.gdx.graphics.g3d.model.Node Maven / Gradle / Ivy
The newest version!
/*******************************************************************************
* Copyright 2011 See AUTHORS file.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package com.badlogic.gdx.graphics.g3d.model;
import com.badlogic.gdx.graphics.g3d.Material;
import com.badlogic.gdx.graphics.g3d.Model;
import com.badlogic.gdx.math.Matrix4;
import com.badlogic.gdx.math.Quaternion;
import com.badlogic.gdx.math.Vector3;
import com.badlogic.gdx.math.collision.BoundingBox;
import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.GdxRuntimeException;
/** A node is part of a hierarchy of Nodes in a {@link Model}. A Node encodes a transform relative to its parents. A Node can have
* child nodes. Optionally a node can specify a {@link MeshPart} and a {@link Material} to be applied to the mesh part.
* @author badlogic */
public class Node {
/** the id, may be null, FIXME is this unique? **/
public String id;
/** Whether this node should inherit the transformation of its parent node, defaults to true. When this flag is false the value
* of {@link #globalTransform} will be the same as the value of {@link #localTransform} causing the transform to be independent
* of its parent transform. */
public boolean inheritTransform = true;
/** Whether this node is currently being animated, if so the translation, rotation and scale values are not used. */
public boolean isAnimated;
/** the translation, relative to the parent, not modified by animations **/
public final Vector3 translation = new Vector3();
/** the rotation, relative to the parent, not modified by animations **/
public final Quaternion rotation = new Quaternion(0, 0, 0, 1);
/** the scale, relative to the parent, not modified by animations **/
public final Vector3 scale = new Vector3(1, 1, 1);
/** the local transform, based on translation/rotation/scale ({@link #calculateLocalTransform()}) or any applied animation **/
public final Matrix4 localTransform = new Matrix4();
/** the global transform, product of local transform and transform of the parent node, calculated via
* {@link #calculateWorldTransform()} **/
public final Matrix4 globalTransform = new Matrix4();
public Array parts = new Array(2);
protected Node parent;
private final Array children = new Array(2);
/** Calculates the local transform based on the translation, scale and rotation
* @return the local transform */
public Matrix4 calculateLocalTransform () {
if (!isAnimated) localTransform.set(translation, rotation, scale);
return localTransform;
}
/** Calculates the world transform; the product of local transform and the parent's world transform.
* @return the world transform */
public Matrix4 calculateWorldTransform () {
if (inheritTransform && parent != null)
globalTransform.set(parent.globalTransform).mul(localTransform);
else
globalTransform.set(localTransform);
return globalTransform;
}
/** Calculates the local and world transform of this node and optionally all its children.
*
* @param recursive whether to calculate the local/world transforms for children. */
public void calculateTransforms (boolean recursive) {
calculateLocalTransform();
calculateWorldTransform();
if (recursive) {
for (Node child : children) {
child.calculateTransforms(true);
}
}
}
public void calculateBoneTransforms (boolean recursive) {
for (final NodePart part : parts) {
if (part.invBoneBindTransforms == null || part.bones == null || part.invBoneBindTransforms.size != part.bones.length)
continue;
final int n = part.invBoneBindTransforms.size;
for (int i = 0; i < n; i++)
part.bones[i].set(part.invBoneBindTransforms.keys[i].globalTransform).mul(part.invBoneBindTransforms.values[i]);
}
if (recursive) {
for (Node child : children) {
child.calculateBoneTransforms(true);
}
}
}
/** Calculate the bounding box of this Node. This is a potential slow operation, it is advised to cache the result. */
public BoundingBox calculateBoundingBox (final BoundingBox out) {
out.inf();
return extendBoundingBox(out);
}
/** Calculate the bounding box of this Node. This is a potential slow operation, it is advised to cache the result. */
public BoundingBox calculateBoundingBox (final BoundingBox out, boolean transform) {
out.inf();
return extendBoundingBox(out, transform);
}
/** Extends the bounding box with the bounds of this Node. This is a potential slow operation, it is advised to cache the
* result. */
public BoundingBox extendBoundingBox (final BoundingBox out) {
return extendBoundingBox(out, true);
}
/** Extends the bounding box with the bounds of this Node. This is a potential slow operation, it is advised to cache the
* result. */
public BoundingBox extendBoundingBox (final BoundingBox out, boolean transform) {
final int partCount = parts.size;
for (int i = 0; i < partCount; i++) {
final NodePart part = parts.get(i);
if (part.enabled) {
final MeshPart meshPart = part.meshPart;
if (transform)
meshPart.mesh.extendBoundingBox(out, meshPart.offset, meshPart.size, globalTransform);
else
meshPart.mesh.extendBoundingBox(out, meshPart.offset, meshPart.size);
}
}
final int childCount = children.size;
for (int i = 0; i < childCount; i++)
children.get(i).extendBoundingBox(out);
return out;
}
/** Adds this node as child to specified parent Node, synonym for: parent.addChild(this)
* @param parent The Node to attach this Node to. */
public void attachTo (T parent) {
parent.addChild(this);
}
/** Removes this node from its current parent, if any. Short for: this.getParent().removeChild(this)
*/
public void detach () {
if (parent != null) {
parent.removeChild(this);
parent = null;
}
}
/** @return whether this Node has one or more children (true) or not (false) */
public boolean hasChildren () {
return children != null && children.size > 0;
}
/** @return The number of child nodes that this Node current contains.
* @see #getChild(int) */
public int getChildCount () {
return children.size;
}
/** @param index The zero-based index of the child node to get, must be: 0 <= index < {@link #getChildCount()}.
* @return The child node at the specified index */
public Node getChild (final int index) {
return children.get(index);
}
/** @param recursive false to fetch a root child only, true to search the entire node tree for the specified node.
* @return The node with the specified id, or null if not found. */
public Node getChild (final String id, boolean recursive, boolean ignoreCase) {
return getNode(children, id, recursive, ignoreCase);
}
/** Adds the specified node as the currently last child of this node. If the node is already a child of another node, then it
* is removed from its current parent.
* @param child The Node to add as child of this Node
* @return the zero-based index of the child */
public int addChild (final T child) {
return insertChild(-1, child);
}
/** Adds the specified nodes as the currently last child of this node. If the node is already a child of another node, then it
* is removed from its current parent.
* @param nodes The Node to add as child of this Node
* @return the zero-based index of the first added child */
public int addChildren (final Iterable nodes) {
return insertChildren(-1, nodes);
}
/** Insert the specified node as child of this node at the specified index. If the node is already a child of another node,
* then it is removed from its current parent. If the specified index is less than zero or equal or greater than
* {@link #getChildCount()} then the Node is added as the currently last child.
* @param index The zero-based index at which to add the child
* @param child The Node to add as child of this Node
* @return the zero-based index of the child */
public int insertChild (int index, final T child) {
for (Node p = this; p != null; p = p.getParent()) {
if (p == child) throw new GdxRuntimeException("Cannot add a parent as a child");
}
Node p = child.getParent();
if (p != null && !p.removeChild(child)) throw new GdxRuntimeException("Could not remove child from its current parent");
if (index < 0 || index >= children.size) {
index = children.size;
children.add(child);
} else
children.insert(index, child);
child.parent = this;
return index;
}
/** Insert the specified nodes as children of this node at the specified index. If the node is already a child of another node,
* then it is removed from its current parent. If the specified index is less than zero or equal or greater than
* {@link #getChildCount()} then the Node is added as the currently last child.
* @param index The zero-based index at which to add the child
* @param nodes The nodes to add as child of this Node
* @return the zero-based index of the first inserted child */
public int insertChildren (int index, final Iterable nodes) {
if (index < 0 || index > children.size) index = children.size;
int i = index;
for (T child : nodes)
insertChild(i++, child);
return index;
}
/** Removes the specified node as child of this node. On success, the child node will be not attached to any parent node (its
* {@link #getParent()} method will return null). If the specified node currently isn't a child of this node then the removal
* is considered to be unsuccessful and the method will return false.
* @param child The child node to remove.
* @return Whether the removal was successful. */
public boolean removeChild (final T child) {
if (!children.removeValue(child, true)) return false;
child.parent = null;
return true;
}
/** @return An {@link Iterable} to all child nodes that this node contains. */
public Iterable getChildren () {
return children;
}
/** @return The parent node that holds this node as child node, may be null. */
public Node getParent () {
return parent;
}
/** @return Whether (true) is this Node is a child node of another node or not (false). */
public boolean hasParent () {
return parent != null;
}
/** Creates a nested copy of this Node, any child nodes are copied using this method as well. The {@link #parts} are copied
* using the {@link NodePart#copy()} method. Note that that method copies the material and nodes (bones) by reference. If you
* intend to use the copy in a different node tree (e.g. a different Model or ModelInstance) then you will need to update these
* references afterwards.
*
* Override this method in your custom Node class to instantiate that class, in that case you should override the
* {@link #set(Node)} method as well. */
public Node copy () {
return new Node().set(this);
}
/** Creates a nested copy of this Node, any child nodes are copied using the {@link #copy()} method. This will detach this node
* from its parent, but does not attach it to the parent of node being copied. The {@link #parts} are copied using the
* {@link NodePart#copy()} method. Note that that method copies the material and nodes (bones) by reference. If you intend to
* use this node in a different node tree (e.g. a different Model or ModelInstance) then you will need to update these
* references afterwards.
*
* Override this method in your custom Node class to copy any additional fields you've added.
* @return This Node for chaining */
protected Node set (Node other) {
detach();
id = other.id;
isAnimated = other.isAnimated;
inheritTransform = other.inheritTransform;
translation.set(other.translation);
rotation.set(other.rotation);
scale.set(other.scale);
localTransform.set(other.localTransform);
globalTransform.set(other.globalTransform);
parts.clear();
for (NodePart nodePart : other.parts) {
parts.add(nodePart.copy());
}
children.clear();
for (Node child : other.getChildren()) {
addChild(child.copy());
}
return this;
}
/** Helper method to recursive fetch a node from an array
* @param recursive false to fetch a root node only, true to search the entire node tree for the specified node.
* @return The node with the specified id, or null if not found. */
public static Node getNode (final Array nodes, final String id, boolean recursive, boolean ignoreCase) {
final int n = nodes.size;
Node node;
if (ignoreCase) {
for (int i = 0; i < n; i++)
if ((node = nodes.get(i)).id.equalsIgnoreCase(id)) return node;
} else {
for (int i = 0; i < n; i++)
if ((node = nodes.get(i)).id.equals(id)) return node;
}
if (recursive) {
for (int i = 0; i < n; i++)
if ((node = getNode(nodes.get(i).children, id, true, ignoreCase)) != null) return node;
}
return null;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy