Files
nenya/src/java/com/threerings/jme/model/ModelNode.java
T
Michael Bayne 8fc55ee79b A bunch of Eclipse facilitated cleanup.
git-svn-id: svn+ssh://src.earth.threerings.net/nenya/trunk@734 ed5b42cb-e716-0410-a449-f6a68f950b19
2008-12-09 02:00:28 +00:00

415 lines
13 KiB
Java

//
// $Id$
//
// Nenya library - tools for developing networked games
// Copyright (C) 2002-2007 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/nenya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.jme.model;
import java.io.IOException;
import java.util.HashSet;
import java.util.List;
import com.jme.math.Matrix4f;
import com.jme.math.Quaternion;
import com.jme.math.Vector3f;
import com.jme.renderer.Renderer;
import com.jme.scene.Node;
import com.jme.scene.Spatial;
import com.jme.scene.state.RenderState;
import com.jme.util.export.JMEExporter;
import com.jme.util.export.JMEImporter;
import com.jme.util.export.InputCapsule;
import com.jme.util.export.OutputCapsule;
import com.threerings.jme.util.JmeUtil;
import com.threerings.jme.util.ShaderCache;
/**
* A {@link Node} with a serialization mechanism tailored to stored models.
*/
public class ModelNode extends Node
implements ModelSpatial
{
/**
* No-arg constructor for deserialization.
*/
public ModelNode ()
{
super("node");
}
/**
* Standard constructor.
*/
public ModelNode (String name)
{
super(name);
}
@Override // documentation inherited
public int hashCode ()
{
// hash on the name rather than the identity for consistent ordering
return getName().hashCode();
}
/**
* Recursively searches the scene graph rooted at this node for a
* node with the provided name.
*/
public Spatial getDescendant (String name)
{
for (int ii = 0, nn = getQuantity(); ii < nn; ii++) {
Spatial child = getChild(ii);
if (child.getName().equals(name)) {
return child;
} else if (child instanceof ModelNode) {
child = ((ModelNode)child).getDescendant(name);
if (child != null) {
return child;
}
}
}
return null;
}
/**
* Returns a reference to the model space transform of the node.
*/
public Matrix4f getModelTransform ()
{
return _modelTransform;
}
@Override // documentation inherited
public void updateWorldData (float time)
{
// we use locked bounds as an indication that we can skip the update
// altogether
if ((lockedMode & LOCKED_BOUNDS) == 0) {
super.updateWorldData(time);
}
}
@Override // documentation inherited
public void updateWorldBound ()
{
// don't bother updating if we know there are no visible descendants
if (_hasVisibleDescendants) {
super.updateWorldBound();
}
}
@Override // documentation inherited
public void updateWorldVectors ()
{
super.updateWorldVectors();
if (parent instanceof ModelNode) {
JmeUtil.setTransform(getLocalTranslation(), getLocalRotation(),
getLocalScale(), _localTransform);
((ModelNode)parent).getModelTransform().mult(_localTransform,
_modelTransform);
} else {
_modelTransform.loadIdentity();
}
}
// documentation inherited from interface ModelSpatial
public Spatial putClone (Spatial store, Model.CloneCreator properties)
{
ModelNode mstore = (ModelNode)properties.originalToCopy.get(this);
if (mstore != null) {
return mstore;
} else if (store == null) {
mstore = new ModelNode(getName());
} else {
mstore = (ModelNode)store;
}
properties.originalToCopy.put(this, mstore);
mstore.normalsMode = normalsMode;
mstore.cullMode = cullMode;
for (int ii = 0; ii < RenderState.RS_MAX_STATE; ii++) {
RenderState rstate = getRenderState(ii);
if (rstate != null) {
mstore.setRenderState(rstate);
}
}
mstore.renderQueueMode = renderQueueMode;
mstore.lockedMode = lockedMode;
mstore.lightCombineMode = lightCombineMode;
mstore.textureCombineMode = textureCombineMode;
mstore.name = name;
mstore.isCollidable = isCollidable;
mstore.localRotation.set(localRotation);
mstore.localTranslation.set(localTranslation);
mstore.localScale.set(localScale);
for (Object controller : getControllers()) {
if (controller instanceof ModelController) {
mstore.addController(
((ModelController)controller).putClone(null, properties));
}
}
for (int ii = 0, nn = getQuantity(); ii < nn; ii++) {
Spatial child = getChild(ii);
if (child instanceof ModelSpatial) {
mstore.attachChild(
((ModelSpatial)child).putClone(null, properties));
}
}
mstore._hasVisibleDescendants = _hasVisibleDescendants;
return mstore;
}
@Override // documentation inherited
public void read (JMEImporter im)
throws IOException
{
InputCapsule capsule = im.getCapsule(this);
setName(capsule.readString("name", null));
setLocalTranslation((Vector3f)capsule.readSavable(
"localTranslation", null));
setLocalRotation((Quaternion)capsule.readSavable(
"localRotation", null));
setLocalScale((Vector3f)capsule.readSavable(
"localScale", null));
List<?> children = capsule.readSavableArrayList("children", null);
if (children != null) {
for (Object child : children) {
attachChild((Spatial)child);
}
}
}
@Override // documentation inherited
public void write (JMEExporter ex)
throws IOException
{
OutputCapsule capsule = ex.getCapsule(this);
capsule.write(getName(), "name", null);
capsule.write(getLocalTranslation(), "localTranslation", null);
capsule.write(getLocalRotation(), "localRotation", null);
capsule.write(getLocalScale(), "localScale", null);
capsule.writeSavableArrayList(getChildren(), "children", null);
}
// documentation inherited from interface ModelSpatial
public void expandModelBounds ()
{
for (int ii = 0, nn = getQuantity(); ii < nn; ii++) {
Spatial child = getChild(ii);
if (child instanceof ModelSpatial) {
((ModelSpatial)child).expandModelBounds();
}
}
}
// documentation inherited from interface ModelSpatial
public void setReferenceTransforms ()
{
updateWorldVectors();
for (int ii = 0, nn = getQuantity(); ii < nn; ii++) {
Spatial child = getChild(ii);
if (child instanceof ModelSpatial) {
((ModelSpatial)child).setReferenceTransforms();
}
}
}
// documentation inherited from interface ModelSpatial
public void lockStaticMeshes (
Renderer renderer, boolean useVBOs, boolean useDisplayLists)
{
for (int ii = 0, nn = getQuantity(); ii < nn; ii++) {
Spatial child = getChild(ii);
if (child instanceof ModelSpatial) {
((ModelSpatial)child).lockStaticMeshes(renderer, useVBOs,
useDisplayLists);
}
}
}
// documentation inherited from interface ModelSpatial
public void resolveTextures (TextureProvider tprov)
{
for (int ii = 0, nn = getQuantity(); ii < nn; ii++) {
Spatial child = getChild(ii);
if (child instanceof ModelSpatial) {
((ModelSpatial)child).resolveTextures(tprov);
}
}
}
// documentation inherited from interface ModelSpatial
public void configureShaders (ShaderCache scache)
{
for (int ii = 0, nn = getQuantity(); ii < nn; ii++) {
Spatial child = getChild(ii);
if (child instanceof ModelSpatial) {
((ModelSpatial)child).configureShaders(scache);
}
}
}
// documentation inherited from interface ModelSpatial
public void storeMeshFrame (int frameId, boolean blend)
{
for (int ii = 0, nn = getQuantity(); ii < nn; ii++) {
Spatial child = getChild(ii);
if (child instanceof ModelSpatial) {
((ModelSpatial)child).storeMeshFrame(frameId, blend);
}
}
}
// documentation inherited from interface ModelSpatial
public void setMeshFrame (int frameId)
{
for (int ii = 0, nn = getQuantity(); ii < nn; ii++) {
Spatial child = getChild(ii);
if (child instanceof ModelSpatial) {
((ModelSpatial)child).setMeshFrame(frameId);
}
}
}
// documentation inherited from interface ModelSpatial
public void blendMeshFrames (int frameId1, int frameId2, float alpha)
{
for (int ii = 0, nn = getQuantity(); ii < nn; ii++) {
Spatial child = getChild(ii);
if (child instanceof ModelSpatial) {
((ModelSpatial)child).blendMeshFrames(
frameId1, frameId2, alpha);
}
}
}
/**
* Sets whether this node should be culled even if it has mesh
* descendants.
*/
public void setForceCull (boolean force)
{
_forceCull = force;
}
/**
* Checks whether this node should be culled even if it has mesh
* descendants.
*/
public boolean getForceCull ()
{
return _forceCull;
}
/**
* Sets the cull state of any nodes that do not contain geometric
* descendants to {@link CULL_ALWAYS} so that they don't waste
* rendering time.
*
* @return true if this node should be drawn, false if it contains
* no mesh descendants
*/
protected boolean cullInvisibleNodes ()
{
for (int ii = 0, nn = getQuantity(); ii < nn; ii++) {
Spatial child = getChild(ii);
if (!(child instanceof ModelNode) ||
((ModelNode)child).cullInvisibleNodes()) {
_hasVisibleDescendants = true;
}
}
updateCullMode();
return _hasVisibleDescendants;
}
/**
* Locks the transforms and bounds of this instance with the assumption
* that the position will never change.
*
* @param targets the targets of the model's controllers, which determine
* the subset of nodes that can be locked
* @return true if this node is a target or contains any targets, otherwise
* false
*/
protected boolean lockInstance (HashSet<Spatial> targets)
{
updateWorldVectors();
lockedMode |= LOCKED_TRANSFORMS;
boolean containsTargets = false;
for (int ii = 0, nn = getQuantity(); ii < nn; ii++) {
Spatial child = getChild(ii);
if (targets.contains(child) || (child instanceof ModelNode &&
((ModelNode)child).lockInstance(targets))) {
containsTargets = true;
} else if (child instanceof ModelMesh) {
((ModelMesh)child).lockInstance();
}
}
if (containsTargets) {
return true;
} else {
updateWorldBound();
lockedMode |= LOCKED_BOUNDS;
return false;
}
}
/**
* Recursively culls all model nodes under this one in preparation for
* activating the ones listed in an animation.
*/
protected void cullModelNodes ()
{
for (int ii = 0, nn = getQuantity(); ii < nn; ii++) {
Spatial child = getChild(ii);
if (child instanceof ModelNode) {
child.setCullMode(CULL_ALWAYS);
((ModelNode)child).cullModelNodes();
}
}
}
/**
* Makes this node visible if and only if it has visible descendants and
* has not been specifically culled.
*/
protected void updateCullMode ()
{
setCullMode((_hasVisibleDescendants && !_forceCull) ?
CULL_INHERIT : CULL_ALWAYS);
}
/** The node's transform in local and model space. */
protected Matrix4f _localTransform = new Matrix4f(),
_modelTransform = new Matrix4f();
/** Whether or not this node has mesh descendants. */
protected boolean _hasVisibleDescendants;
/** If true, cull the node even if it has mesh descendants. */
protected boolean _forceCull;
private static final long serialVersionUID = 1;
}