Behold, Nenya, Ring of Water and repository for our media and animation related
goodies, both Java 2D and LWJGL/JME 3D. git-svn-id: svn+ssh://src.earth.threerings.net/nenya/trunk@1 ed5b42cb-e716-0410-a449-f6a68f950b19
This commit is contained in:
@@ -0,0 +1,79 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2005 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// 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.util.Properties;
|
||||
|
||||
import com.jme.math.Vector3f;
|
||||
import com.jme.scene.Geometry;
|
||||
import com.jme.scene.Spatial;
|
||||
import com.jme.util.geom.BufferUtils;
|
||||
|
||||
/**
|
||||
* A model controller whose target represents an emitter.
|
||||
*/
|
||||
public abstract class EmissionController extends ModelController
|
||||
{
|
||||
@Override // documentation inherited
|
||||
public void configure (Properties props, Spatial target)
|
||||
{
|
||||
// substitute underlying mesh for geometry targets
|
||||
if (target instanceof ModelNode) {
|
||||
Spatial mesh = ((ModelNode)target).getChild("mesh");
|
||||
if (mesh != null) {
|
||||
target = mesh;
|
||||
}
|
||||
}
|
||||
super.configure(props, target);
|
||||
}
|
||||
|
||||
@Override // documentation inherited
|
||||
public void init (Model model)
|
||||
{
|
||||
super.init(model);
|
||||
_target.setCullMode(Spatial.CULL_ALWAYS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the current location of the emitter in world coordinates.
|
||||
*/
|
||||
protected void getEmitterLocation (Vector3f result)
|
||||
{
|
||||
result.set(_target.getWorldTranslation());
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the current direction of the emitter in world coordinates.
|
||||
*/
|
||||
protected void getEmitterDirection (Vector3f result)
|
||||
{
|
||||
if (_target instanceof Geometry) {
|
||||
BufferUtils.populateFromBuffer(result,
|
||||
((Geometry)_target).getNormalBuffer(0), 0);
|
||||
} else {
|
||||
result.set(0f, 0f, -1f);
|
||||
}
|
||||
_target.getWorldRotation().multLocal(result);
|
||||
}
|
||||
|
||||
private static final long serialVersionUID = 1;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,167 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2005 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// 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.Externalizable;
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInput;
|
||||
import java.io.ObjectOutput;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Properties;
|
||||
|
||||
import com.jme.scene.Controller;
|
||||
import com.jme.scene.Node;
|
||||
import com.jme.scene.Spatial;
|
||||
|
||||
import com.samskivert.util.StringUtil;
|
||||
|
||||
/**
|
||||
* The superclass of procedural animation controllers for models.
|
||||
*/
|
||||
public abstract class ModelController extends Controller
|
||||
implements Externalizable
|
||||
{
|
||||
/**
|
||||
* Configures this controller based on the supplied (sub-)properties and
|
||||
* controller target.
|
||||
*/
|
||||
public void configure (Properties props, Spatial target)
|
||||
{
|
||||
_target = target;
|
||||
String[] anims = StringUtil.parseStringArray(
|
||||
props.getProperty("animations", ""));
|
||||
if (anims.length == 0) {
|
||||
return;
|
||||
}
|
||||
_animations = new HashSet<String>();
|
||||
Collections.addAll(_animations, anims);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a reference to the controller's target.
|
||||
*/
|
||||
public Spatial getTarget ()
|
||||
{
|
||||
return _target;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves any textures required by the controller.
|
||||
*/
|
||||
public void resolveTextures (TextureProvider tprov)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes this controller.
|
||||
*/
|
||||
public void init (Model model)
|
||||
{
|
||||
model.addAnimationObserver(_animobs);
|
||||
if (_animations != null) {
|
||||
setActive(false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates or populates and returns a clone of this object using the given
|
||||
* clone properties.
|
||||
*
|
||||
* @param store an instance of this class to populate, or <code>null</code>
|
||||
* to create a new instance
|
||||
*/
|
||||
public Controller putClone (
|
||||
Controller store, Model.CloneCreator properties)
|
||||
{
|
||||
if (store == null) {
|
||||
return null;
|
||||
}
|
||||
ModelController mstore = (ModelController)store;
|
||||
mstore._target = ((ModelSpatial)_target).putClone(null, properties);
|
||||
mstore._animations = _animations;
|
||||
return mstore;
|
||||
}
|
||||
|
||||
// documentation inherited from interface Externalizable
|
||||
public void writeExternal (ObjectOutput out)
|
||||
throws IOException
|
||||
{
|
||||
out.writeObject(_target);
|
||||
out.writeObject(_animations);
|
||||
}
|
||||
|
||||
// documentation inherited from interface Externalizable
|
||||
public void readExternal (ObjectInput in)
|
||||
throws IOException, ClassNotFoundException
|
||||
{
|
||||
_target = (Spatial)in.readObject();
|
||||
_animations = (HashSet<String>)in.readObject();
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when an animation is started on the model.
|
||||
*/
|
||||
protected void animationStarted (String anim)
|
||||
{
|
||||
if (_animations != null && _animations.contains(anim)) {
|
||||
setActive(true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when an animation is stopped on the model.
|
||||
*/
|
||||
protected void animationStopped (String anim)
|
||||
{
|
||||
if (_animations != null) {
|
||||
setActive(false);
|
||||
}
|
||||
}
|
||||
|
||||
/** The target to control. */
|
||||
protected Spatial _target;
|
||||
|
||||
/** The animations for which this controller should be active, or
|
||||
* <code>null</code> for all of them. */
|
||||
protected HashSet<String> _animations;
|
||||
|
||||
/** Listens to the model's animation state. */
|
||||
protected Model.AnimationObserver _animobs =
|
||||
new Model.AnimationObserver() {
|
||||
public boolean animationStarted (Model model, String anim) {
|
||||
ModelController.this.animationStarted(anim);
|
||||
return true;
|
||||
}
|
||||
public boolean animationCompleted (Model model, String anim) {
|
||||
animationStopped(anim);
|
||||
return true;
|
||||
}
|
||||
public boolean animationCancelled (Model model, String anim) {
|
||||
animationStopped(anim);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
private static final long serialVersionUID = 1;
|
||||
}
|
||||
@@ -0,0 +1,557 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2005 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// 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.Externalizable;
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInput;
|
||||
import java.io.ObjectOutput;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.FloatBuffer;
|
||||
import java.nio.IntBuffer;
|
||||
import java.nio.MappedByteBuffer;
|
||||
import java.nio.channels.FileChannel;
|
||||
|
||||
import java.util.Properties;
|
||||
|
||||
import com.jme.bounding.BoundingVolume;
|
||||
import com.jme.math.Quaternion;
|
||||
import com.jme.math.Vector3f;
|
||||
import com.jme.renderer.Renderer;
|
||||
import com.jme.scene.Controller;
|
||||
import com.jme.scene.SharedMesh;
|
||||
import com.jme.scene.Spatial;
|
||||
import com.jme.scene.TriMesh;
|
||||
import com.jme.scene.VBOInfo;
|
||||
import com.jme.scene.batch.TriangleBatch;
|
||||
import com.jme.scene.state.AlphaState;
|
||||
import com.jme.scene.state.CullState;
|
||||
import com.jme.scene.state.RenderState;
|
||||
import com.jme.scene.state.TextureState;
|
||||
import com.jme.scene.state.ZBufferState;
|
||||
import com.jme.system.DisplaySystem;
|
||||
import com.jme.util.geom.BufferUtils;
|
||||
|
||||
import com.samskivert.util.StringUtil;
|
||||
|
||||
import com.threerings.jme.Log;
|
||||
|
||||
/**
|
||||
* A {@link TriMesh} with a serialization mechanism tailored to stored models.
|
||||
*/
|
||||
public class ModelMesh extends TriMesh
|
||||
implements Externalizable, ModelSpatial
|
||||
{
|
||||
/**
|
||||
* No-arg constructor for deserialization.
|
||||
*/
|
||||
public ModelMesh ()
|
||||
{
|
||||
super("mesh");
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a mesh with no vertex data.
|
||||
*/
|
||||
public ModelMesh (String name)
|
||||
{
|
||||
super(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures this mesh based on the given parameters and (sub-)properties.
|
||||
*
|
||||
* @param texture the texture specified in the model export, if any (can be
|
||||
* overridden by textures specified in the properties)
|
||||
* @param solid whether or not the mesh allows back face culling
|
||||
* @param transparent whether or not the mesh is (partially) transparent
|
||||
*/
|
||||
public void configure (
|
||||
boolean solid, String texture, boolean transparent, Properties props)
|
||||
{
|
||||
_textures = (texture == null) ? null : StringUtil.parseStringArray(
|
||||
props.getProperty(texture, texture));
|
||||
_solid = solid;
|
||||
_transparent = transparent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the buffers as {@link ByteBuffer}s, because we can't create byte
|
||||
* views of non-byte buffers. This method is where the model is
|
||||
* initialized after loading.
|
||||
*/
|
||||
public void reconstruct (
|
||||
ByteBuffer vertices, ByteBuffer normals, ByteBuffer colors,
|
||||
ByteBuffer textures, ByteBuffer indices)
|
||||
{
|
||||
reconstruct(
|
||||
vertices == null ? null : vertices.asFloatBuffer(),
|
||||
normals == null ? null : normals.asFloatBuffer(),
|
||||
colors == null ? null : colors.asFloatBuffer(),
|
||||
textures == null ? null : textures.asFloatBuffer(),
|
||||
indices == null ? null : indices.asIntBuffer());
|
||||
_vertexByteBuffer = vertices;
|
||||
_normalByteBuffer = normals;
|
||||
_colorByteBuffer = colors;
|
||||
_textureByteBuffer = textures;
|
||||
_indexByteBuffer = indices;
|
||||
|
||||
// initialize the model if we're displaying
|
||||
if (DisplaySystem.getDisplaySystem() == null) {
|
||||
return;
|
||||
}
|
||||
if (_backCull == null) {
|
||||
initSharedStates();
|
||||
}
|
||||
if (_solid) {
|
||||
setRenderState(_backCull);
|
||||
}
|
||||
if (_transparent) {
|
||||
setRenderQueueMode(Renderer.QUEUE_TRANSPARENT);
|
||||
setRenderState(_blendAlpha);
|
||||
setRenderState(_overlayZBuffer);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adjusts the vertices and the transform of the mesh so that the mesh's
|
||||
* position lies at the center of its bounding volume.
|
||||
*/
|
||||
public void centerVertices ()
|
||||
{
|
||||
Vector3f offset = getBatch(0).getModelBound().getCenter().negate();
|
||||
if (!offset.equals(Vector3f.ZERO)) {
|
||||
getLocalTranslation().subtractLocal(offset);
|
||||
getBatch(0).getModelBound().getCenter().set(Vector3f.ZERO);
|
||||
getBatch(0).translatePoints(offset);
|
||||
}
|
||||
}
|
||||
|
||||
@Override // documentation inherited
|
||||
public void reconstruct (
|
||||
FloatBuffer vertices, FloatBuffer normals, FloatBuffer colors,
|
||||
FloatBuffer textures, IntBuffer indices)
|
||||
{
|
||||
super.reconstruct(vertices, normals, colors, textures, indices);
|
||||
|
||||
_vertexBufferSize = (vertices == null) ? 0 : vertices.capacity();
|
||||
_normalBufferSize = (normals == null) ? 0 : normals.capacity();
|
||||
_colorBufferSize = (colors == null) ? 0 : colors.capacity();
|
||||
_textureBufferSize = (textures == null) ? 0 : textures.capacity();
|
||||
_indexBufferSize = (indices == null) ? 0 : indices.capacity();
|
||||
}
|
||||
|
||||
@Override // documentation inherited
|
||||
public void reconstruct (
|
||||
FloatBuffer vertices, FloatBuffer normals, FloatBuffer colors,
|
||||
FloatBuffer textures)
|
||||
{
|
||||
super.reconstruct(vertices, normals, colors, textures);
|
||||
|
||||
_vertexBufferSize = (vertices == null) ? 0 : vertices.capacity();
|
||||
_normalBufferSize = (normals == null) ? 0 : normals.capacity();
|
||||
_colorBufferSize = (colors == null) ? 0 : colors.capacity();
|
||||
_textureBufferSize = (textures == null) ? 0 : textures.capacity();
|
||||
}
|
||||
|
||||
// documentation inherited from interface ModelSpatial
|
||||
public Spatial putClone (Spatial store, Model.CloneCreator properties)
|
||||
{
|
||||
ModelMesh mstore = (ModelMesh)properties.originalToCopy.get(this);
|
||||
if (mstore != null) {
|
||||
return mstore;
|
||||
} else if (store == null) {
|
||||
mstore = new ModelMesh(getName());
|
||||
} else {
|
||||
mstore = (ModelMesh)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));
|
||||
}
|
||||
}
|
||||
TriangleBatch batch = (TriangleBatch)getBatch(0),
|
||||
mbatch = (TriangleBatch)mstore.getBatch(0);
|
||||
mbatch.setVertexBuffer(properties.isSet("vertices") ?
|
||||
batch.getVertexBuffer() :
|
||||
BufferUtils.clone(batch.getVertexBuffer()));
|
||||
mbatch.setColorBuffer(properties.isSet("colors") ?
|
||||
batch.getColorBuffer() :
|
||||
BufferUtils.clone(batch.getColorBuffer()));
|
||||
mbatch.setNormalBuffer(properties.isSet("normals") ?
|
||||
batch.getNormalBuffer() :
|
||||
BufferUtils.clone(batch.getNormalBuffer()));
|
||||
FloatBuffer texcoords;
|
||||
for (int ii = 0; (texcoords = batch.getTextureBuffer(ii)) != null;
|
||||
ii++) {
|
||||
mbatch.setTextureBuffer(properties.isSet("texcoords") ?
|
||||
texcoords : BufferUtils.clone(texcoords), ii);
|
||||
}
|
||||
mbatch.setIndexBuffer(properties.isSet("indices") ?
|
||||
batch.getIndexBuffer() :
|
||||
BufferUtils.clone(batch.getIndexBuffer()));
|
||||
if (properties.isSet("vboinfo")) {
|
||||
mbatch.setVBOInfo(batch.getVBOInfo());
|
||||
}
|
||||
if (properties.isSet("obbtree")) {
|
||||
mbatch.setCollisionTree(batch.getCollisionTree());
|
||||
}
|
||||
if (properties.isSet("displaylistid")) {
|
||||
mbatch.setDisplayListID(batch.getDisplayListID());
|
||||
}
|
||||
if (batch.getModelBound() != null) {
|
||||
mbatch.setModelBound(properties.isSet("bound") ?
|
||||
batch.getModelBound() : batch.getModelBound().clone(null));
|
||||
}
|
||||
if (_textures != null && _textures.length > 1) {
|
||||
int tidx = properties.random % _textures.length;
|
||||
mstore._textures = new String[] { _textures[tidx] };
|
||||
mstore._tstates = new TextureState[] { _tstates[tidx] };
|
||||
mstore.setRenderState(_tstates[tidx]);
|
||||
} else {
|
||||
mstore._textures = _textures;
|
||||
mstore._tstates = _tstates;
|
||||
}
|
||||
return mstore;
|
||||
}
|
||||
|
||||
@Override // documentation inherited
|
||||
public void updateWorldVectors ()
|
||||
{
|
||||
if (!_transformLocked) {
|
||||
super.updateWorldVectors();
|
||||
}
|
||||
}
|
||||
|
||||
// documentation inherited from interface Externalizable
|
||||
public void writeExternal (ObjectOutput out)
|
||||
throws IOException
|
||||
{
|
||||
out.writeUTF(getName());
|
||||
out.writeObject(getLocalTranslation());
|
||||
out.writeObject(getLocalRotation());
|
||||
out.writeObject(getLocalScale());
|
||||
out.writeObject(getBatch(0).getModelBound());
|
||||
out.writeInt(_vertexBufferSize);
|
||||
out.writeInt(_normalBufferSize);
|
||||
out.writeInt(_colorBufferSize);
|
||||
out.writeInt(_textureBufferSize);
|
||||
out.writeInt(_indexBufferSize);
|
||||
out.writeObject(_textures);
|
||||
out.writeBoolean(_solid);
|
||||
out.writeBoolean(_transparent);
|
||||
}
|
||||
|
||||
// documentation inherited from interface Externalizable
|
||||
public void readExternal (ObjectInput in)
|
||||
throws IOException, ClassNotFoundException
|
||||
{
|
||||
setName(in.readUTF());
|
||||
setLocalTranslation((Vector3f)in.readObject());
|
||||
setLocalRotation((Quaternion)in.readObject());
|
||||
setLocalScale((Vector3f)in.readObject());
|
||||
setModelBound((BoundingVolume)in.readObject());
|
||||
_vertexBufferSize = in.readInt();
|
||||
_normalBufferSize = in.readInt();
|
||||
_colorBufferSize = in.readInt();
|
||||
_textureBufferSize = in.readInt();
|
||||
_indexBufferSize = in.readInt();
|
||||
_textures = (String[])in.readObject();
|
||||
_solid = in.readBoolean();
|
||||
_transparent = in.readBoolean();
|
||||
}
|
||||
|
||||
// documentation inherited from interface ModelSpatial
|
||||
public void expandModelBounds ()
|
||||
{
|
||||
// no-op
|
||||
}
|
||||
|
||||
// documentation inherited from interface ModelSpatial
|
||||
public void setReferenceTransforms ()
|
||||
{
|
||||
// no-op
|
||||
}
|
||||
|
||||
// documentation inherited from interface ModelSpatial
|
||||
public void lockStaticMeshes (
|
||||
Renderer renderer, boolean useVBOs, boolean useDisplayLists)
|
||||
{
|
||||
if (useVBOs && renderer.supportsVBO()) {
|
||||
VBOInfo vboinfo = new VBOInfo(true);
|
||||
vboinfo.setVBOIndexEnabled(true);
|
||||
setVBOInfo(vboinfo);
|
||||
|
||||
} else if (useDisplayLists) {
|
||||
lockMeshes(renderer);
|
||||
}
|
||||
}
|
||||
|
||||
// documentation inherited from interface ModelSpatial
|
||||
public void resolveTextures (TextureProvider tprov)
|
||||
{
|
||||
if (_textures == null) {
|
||||
return;
|
||||
}
|
||||
_tstates = new TextureState[_textures.length];
|
||||
for (int ii = 0; ii < _textures.length; ii++) {
|
||||
_tstates[ii] = tprov.getTexture(_textures[ii]);
|
||||
}
|
||||
if (_tstates[0] != null) {
|
||||
setRenderState(_tstates[0]);
|
||||
}
|
||||
}
|
||||
|
||||
// documentation inherited from interface ModelSpatial
|
||||
public void storeMeshFrame (int frameId, boolean blend)
|
||||
{
|
||||
// no-op
|
||||
}
|
||||
|
||||
// documentation inherited from interface ModelSpatial
|
||||
public void setMeshFrame (int frameId)
|
||||
{
|
||||
// no-op
|
||||
}
|
||||
|
||||
// documentation inherited from interface ModelSpatial
|
||||
public void blendMeshFrames (int frameId1, int frameId2, float alpha)
|
||||
{
|
||||
// no-op
|
||||
}
|
||||
|
||||
// documentation inherited from interface ModelSpatial
|
||||
public void writeBuffers (FileChannel out)
|
||||
throws IOException
|
||||
{
|
||||
if (_vertexBufferSize > 0) {
|
||||
_vertexByteBuffer.rewind();
|
||||
convertOrder(_vertexByteBuffer, ByteOrder.LITTLE_ENDIAN);
|
||||
out.write(_vertexByteBuffer);
|
||||
convertOrder(_vertexByteBuffer, ByteOrder.nativeOrder());
|
||||
}
|
||||
if (_normalBufferSize > 0) {
|
||||
_normalByteBuffer.rewind();
|
||||
convertOrder(_normalByteBuffer, ByteOrder.LITTLE_ENDIAN);
|
||||
out.write(_normalByteBuffer);
|
||||
convertOrder(_normalByteBuffer, ByteOrder.nativeOrder());
|
||||
}
|
||||
if (_colorBufferSize > 0) {
|
||||
_colorByteBuffer.rewind();
|
||||
convertOrder(_colorByteBuffer, ByteOrder.LITTLE_ENDIAN);
|
||||
out.write(_colorByteBuffer);
|
||||
convertOrder(_colorByteBuffer, ByteOrder.nativeOrder());
|
||||
}
|
||||
if (_textureBufferSize > 0) {
|
||||
_textureByteBuffer.rewind();
|
||||
convertOrder(_textureByteBuffer, ByteOrder.LITTLE_ENDIAN);
|
||||
out.write(_textureByteBuffer);
|
||||
convertOrder(_textureByteBuffer, ByteOrder.nativeOrder());
|
||||
}
|
||||
if (_indexBufferSize > 0) {
|
||||
_indexByteBuffer.rewind();
|
||||
convertOrder(_indexByteBuffer, ByteOrder.LITTLE_ENDIAN);
|
||||
out.write(_indexByteBuffer);
|
||||
convertOrder(_indexByteBuffer, ByteOrder.nativeOrder());
|
||||
}
|
||||
}
|
||||
|
||||
// documentation inherited from interface ModelSpatial
|
||||
public void readBuffers (FileChannel in)
|
||||
throws IOException
|
||||
{
|
||||
ByteBuffer vbbuf = null, nbbuf = null, cbbuf = null, tbbuf = null,
|
||||
ibbuf = null;
|
||||
ByteOrder le = ByteOrder.LITTLE_ENDIAN;
|
||||
if (_vertexBufferSize > 0) {
|
||||
vbbuf = ByteBuffer.allocateDirect(_vertexBufferSize*4).order(le);
|
||||
in.read(vbbuf);
|
||||
vbbuf.rewind();
|
||||
convertOrder(vbbuf, ByteOrder.nativeOrder());
|
||||
}
|
||||
if (_normalBufferSize > 0) {
|
||||
nbbuf = ByteBuffer.allocateDirect(_normalBufferSize*4).order(le);
|
||||
in.read(nbbuf);
|
||||
nbbuf.rewind();
|
||||
convertOrder(nbbuf, ByteOrder.nativeOrder());
|
||||
}
|
||||
if (_colorBufferSize > 0) {
|
||||
cbbuf = ByteBuffer.allocateDirect(_colorBufferSize*4).order(le);
|
||||
in.read(cbbuf);
|
||||
cbbuf.rewind();
|
||||
convertOrder(cbbuf, ByteOrder.nativeOrder());
|
||||
}
|
||||
if (_textureBufferSize > 0) {
|
||||
tbbuf = ByteBuffer.allocateDirect(_textureBufferSize*4).order(le);
|
||||
in.read(tbbuf);
|
||||
tbbuf.rewind();
|
||||
convertOrder(tbbuf, ByteOrder.nativeOrder());
|
||||
}
|
||||
if (_indexBufferSize > 0) {
|
||||
ibbuf = ByteBuffer.allocateDirect(_indexBufferSize*4).order(le);
|
||||
in.read(ibbuf);
|
||||
ibbuf.rewind();
|
||||
convertOrder(ibbuf, ByteOrder.nativeOrder());
|
||||
}
|
||||
reconstruct(vbbuf, nbbuf, cbbuf, tbbuf, ibbuf);
|
||||
}
|
||||
|
||||
// documentation inherited from interface ModelSpatial
|
||||
public void sliceBuffers (MappedByteBuffer map)
|
||||
{
|
||||
ByteBuffer vbbuf = null, nbbuf = null, cbbuf = null, tbbuf = null,
|
||||
ibbuf = null;
|
||||
int total = 0;
|
||||
if (_vertexBufferSize > 0) {
|
||||
int npos = map.position() + _vertexBufferSize*4;
|
||||
map.limit(npos);
|
||||
vbbuf = map.slice().order(ByteOrder.LITTLE_ENDIAN);
|
||||
map.position(npos);
|
||||
}
|
||||
if (_normalBufferSize > 0) {
|
||||
int npos = map.position() + _normalBufferSize*4;
|
||||
map.limit(npos);
|
||||
nbbuf = map.slice().order(ByteOrder.LITTLE_ENDIAN);
|
||||
map.position(npos);
|
||||
}
|
||||
if (_colorBufferSize > 0) {
|
||||
int npos = map.position() + _colorBufferSize*4;
|
||||
map.limit(npos);
|
||||
cbbuf = map.slice().order(ByteOrder.LITTLE_ENDIAN);
|
||||
map.position(npos);
|
||||
}
|
||||
if (_textureBufferSize > 0) {
|
||||
int npos = map.position() + _textureBufferSize*4;
|
||||
map.limit(npos);
|
||||
tbbuf = map.slice().order(ByteOrder.LITTLE_ENDIAN);
|
||||
map.position(npos);
|
||||
}
|
||||
if (_indexBufferSize > 0) {
|
||||
int npos = map.position() + _indexBufferSize*4;
|
||||
map.limit(npos);
|
||||
ibbuf = map.slice().order(ByteOrder.LITTLE_ENDIAN);
|
||||
map.position(npos);
|
||||
}
|
||||
reconstruct(vbbuf, nbbuf, cbbuf, tbbuf, ibbuf);
|
||||
}
|
||||
|
||||
/**
|
||||
* Locks the transform and bounds of this mesh on the assumption that its
|
||||
* position will not change.
|
||||
*/
|
||||
protected void lockInstance ()
|
||||
{
|
||||
lockBounds();
|
||||
_transformLocked = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Imposes the specified order on the given buffer of 32 bit values.
|
||||
*/
|
||||
protected static void convertOrder (ByteBuffer buf, ByteOrder order)
|
||||
{
|
||||
if (buf.order() == order) {
|
||||
return;
|
||||
}
|
||||
IntBuffer obuf = buf.asIntBuffer(),
|
||||
nbuf = buf.order(order).asIntBuffer();
|
||||
while (obuf.hasRemaining()) {
|
||||
nbuf.put(obuf.get());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the states shared between all models. Requires an active
|
||||
* display.
|
||||
*/
|
||||
protected static void initSharedStates ()
|
||||
{
|
||||
Renderer renderer = DisplaySystem.getDisplaySystem().getRenderer();
|
||||
_backCull = renderer.createCullState();
|
||||
_backCull.setCullMode(CullState.CS_BACK);
|
||||
_blendAlpha = renderer.createAlphaState();
|
||||
_blendAlpha.setBlendEnabled(true);
|
||||
_overlayZBuffer = renderer.createZBufferState();
|
||||
_overlayZBuffer.setFunction(ZBufferState.CF_LEQUAL);
|
||||
_overlayZBuffer.setWritable(false);
|
||||
}
|
||||
|
||||
/** The sizes of the various buffers (zero for <code>null</code>). */
|
||||
protected int _vertexBufferSize, _normalBufferSize, _colorBufferSize,
|
||||
_textureBufferSize, _indexBufferSize;
|
||||
|
||||
/** The backing byte buffers for the various buffers. */
|
||||
protected ByteBuffer _vertexByteBuffer, _normalByteBuffer,
|
||||
_colorByteBuffer, _textureByteBuffer, _indexByteBuffer;
|
||||
|
||||
/** The type of bounding volume that this mesh should use. */
|
||||
protected int _boundingType;
|
||||
|
||||
/** The name of this model's textures, or <code>null</code> for none. */
|
||||
protected String[] _textures;
|
||||
|
||||
/** Whether or not this mesh can enable back-face culling. */
|
||||
protected boolean _solid;
|
||||
|
||||
/** Whether or not this mesh must be rendered as transparent. */
|
||||
protected boolean _transparent;
|
||||
|
||||
/** For prototype meshes, the resolved texture states. */
|
||||
protected TextureState[] _tstates;
|
||||
|
||||
/** Whether or not the transform has been locked. This operates in a
|
||||
* slightly different way than JME's locking, in that it allows applying
|
||||
* transformations to display lists. */
|
||||
protected boolean _transformLocked;
|
||||
|
||||
/** The shared state for back face culling. */
|
||||
protected static CullState _backCull;
|
||||
|
||||
/** The shared state for alpha blending. */
|
||||
protected static AlphaState _blendAlpha;
|
||||
|
||||
/** The shared state for checking, but not writing to, the z buffer. */
|
||||
protected static ZBufferState _overlayZBuffer;
|
||||
|
||||
private static final long serialVersionUID = 1;
|
||||
}
|
||||
@@ -0,0 +1,407 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2005 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// 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.DataOutput;
|
||||
import java.io.Externalizable;
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInput;
|
||||
import java.io.ObjectOutput;
|
||||
|
||||
import java.nio.MappedByteBuffer;
|
||||
import java.nio.channels.FileChannel;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
|
||||
import com.jme.math.Matrix4f;
|
||||
import com.jme.math.Quaternion;
|
||||
import com.jme.math.Vector3f;
|
||||
import com.jme.renderer.Renderer;
|
||||
import com.jme.scene.Controller;
|
||||
import com.jme.scene.Node;
|
||||
import com.jme.scene.Spatial;
|
||||
import com.jme.scene.state.RenderState;
|
||||
|
||||
import com.threerings.jme.Log;
|
||||
|
||||
/**
|
||||
* A {@link Node} with a serialization mechanism tailored to stored models.
|
||||
*/
|
||||
public class ModelNode extends Node
|
||||
implements Externalizable, ModelSpatial
|
||||
{
|
||||
/**
|
||||
* No-arg constructor for deserialization.
|
||||
*/
|
||||
public ModelNode ()
|
||||
{
|
||||
super("node");
|
||||
}
|
||||
|
||||
/**
|
||||
* Standard constructor.
|
||||
*/
|
||||
public ModelNode (String name)
|
||||
{
|
||||
super(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 ()
|
||||
{
|
||||
// if the node is culled, there are no mesh descendants and thus no
|
||||
// bounds
|
||||
if (cullMode != CULL_ALWAYS) {
|
||||
super.updateWorldBound();
|
||||
}
|
||||
}
|
||||
|
||||
@Override // documentation inherited
|
||||
public void updateWorldVectors ()
|
||||
{
|
||||
super.updateWorldVectors();
|
||||
if (parent instanceof ModelNode) {
|
||||
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));
|
||||
}
|
||||
}
|
||||
return mstore;
|
||||
}
|
||||
|
||||
// documentation inherited from interface Externalizable
|
||||
public void writeExternal (ObjectOutput out)
|
||||
throws IOException
|
||||
{
|
||||
out.writeUTF(getName());
|
||||
out.writeObject(getLocalTranslation());
|
||||
out.writeObject(getLocalRotation());
|
||||
out.writeObject(getLocalScale());
|
||||
out.writeObject(getChildren());
|
||||
}
|
||||
|
||||
// documentation inherited from interface Externalizable
|
||||
public void readExternal (ObjectInput in)
|
||||
throws IOException, ClassNotFoundException
|
||||
{
|
||||
setName(in.readUTF());
|
||||
setLocalTranslation((Vector3f)in.readObject());
|
||||
setLocalRotation((Quaternion)in.readObject());
|
||||
setLocalScale((Vector3f)in.readObject());
|
||||
ArrayList<Spatial> children = (ArrayList<Spatial>)in.readObject();
|
||||
if (children != null) {
|
||||
for (Spatial child : children) {
|
||||
attachChild(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// documentation inherited from interface ModelSpatial
|
||||
public void writeBuffers (FileChannel out)
|
||||
throws IOException
|
||||
{
|
||||
for (int ii = 0, nn = getQuantity(); ii < nn; ii++) {
|
||||
Spatial child = getChild(ii);
|
||||
if (child instanceof ModelSpatial) {
|
||||
((ModelSpatial)child).writeBuffers(out);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// documentation inherited from interface ModelSpatial
|
||||
public void readBuffers (FileChannel in)
|
||||
throws IOException
|
||||
{
|
||||
for (int ii = 0, nn = getQuantity(); ii < nn; ii++) {
|
||||
Spatial child = getChild(ii);
|
||||
if (child instanceof ModelSpatial) {
|
||||
((ModelSpatial)child).readBuffers(in);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// documentation inherited from interface ModelSpatial
|
||||
public void sliceBuffers (MappedByteBuffer map)
|
||||
{
|
||||
for (int ii = 0, nn = getQuantity(); ii < nn; ii++) {
|
||||
Spatial child = getChild(ii);
|
||||
if (child instanceof ModelSpatial) {
|
||||
((ModelSpatial)child).sliceBuffers(map);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 ()
|
||||
{
|
||||
boolean hasVisibleDescendants = false;
|
||||
for (int ii = 0, nn = getQuantity(); ii < nn; ii++) {
|
||||
Spatial child = getChild(ii);
|
||||
if (!(child instanceof ModelNode) ||
|
||||
((ModelNode)child).cullInvisibleNodes()) {
|
||||
hasVisibleDescendants = true;
|
||||
}
|
||||
}
|
||||
setCullMode(hasVisibleDescendants ? CULL_INHERIT : CULL_ALWAYS);
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a matrix to the transform defined by the given translation,
|
||||
* rotation, and scale values.
|
||||
*/
|
||||
protected static Matrix4f setTransform (
|
||||
Vector3f translation, Quaternion rotation, Vector3f scale,
|
||||
Matrix4f result)
|
||||
{
|
||||
result.set(rotation);
|
||||
result.setTranslation(translation);
|
||||
|
||||
result.m00 *= scale.x;
|
||||
result.m01 *= scale.y;
|
||||
result.m02 *= scale.z;
|
||||
|
||||
result.m10 *= scale.x;
|
||||
result.m11 *= scale.y;
|
||||
result.m12 *= scale.z;
|
||||
|
||||
result.m20 *= scale.x;
|
||||
result.m21 *= scale.y;
|
||||
result.m22 *= scale.z;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/** The node's transform in local and model space. */
|
||||
protected Matrix4f _localTransform = new Matrix4f(),
|
||||
_modelTransform = new Matrix4f();
|
||||
|
||||
private static final long serialVersionUID = 1;
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2005 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// 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.Externalizable;
|
||||
import java.io.IOException;
|
||||
|
||||
import java.nio.MappedByteBuffer;
|
||||
import java.nio.channels.FileChannel;
|
||||
|
||||
import com.jme.math.Matrix4f;
|
||||
import com.jme.renderer.Renderer;
|
||||
import com.jme.scene.Spatial;
|
||||
|
||||
/**
|
||||
* Contains method common to both {@link ModelNode}s and {@link ModelMesh}es.
|
||||
*/
|
||||
public interface ModelSpatial
|
||||
{
|
||||
/**
|
||||
* Recursively expands the model bounds of any deformable meshes so that
|
||||
* they include the current vertex positions.
|
||||
*/
|
||||
public void expandModelBounds ();
|
||||
|
||||
/**
|
||||
* Recursively sets the reference transforms for any bones in the model.
|
||||
*/
|
||||
public void setReferenceTransforms ();
|
||||
|
||||
/**
|
||||
* Recursively compiles any static meshes to vertex buffer objects (VBOs)
|
||||
* or display lists.
|
||||
*
|
||||
* @param useVBOs if true, use VBOs if the graphics card supports them
|
||||
* @param useDisplayLists if true and not using VBOs, compile static
|
||||
* objects to display lists
|
||||
*/
|
||||
public void lockStaticMeshes (
|
||||
Renderer renderer, boolean useVBOs, boolean useDisplayLists);
|
||||
|
||||
/**
|
||||
* Recursively resolves texture references using the given provider.
|
||||
*/
|
||||
public void resolveTextures (TextureProvider tprov);
|
||||
|
||||
/**
|
||||
* Recursively requests that the current state of all skinned meshes be
|
||||
* stored as an animation frame on the next update.
|
||||
*
|
||||
* @param frameId the frame id, which uniquely identifies one frame of
|
||||
* one animation
|
||||
* @param blend whether or not the stored frames will be retrieved by
|
||||
* calls to {@link #blendAnimationFrames} as opposed to
|
||||
* {@link #setAnimationFrames}
|
||||
*/
|
||||
public void storeMeshFrame (int frameId, boolean blend);
|
||||
|
||||
/**
|
||||
* Recursively switches all skinned meshes to a stored animation frame for
|
||||
* the next update.
|
||||
*/
|
||||
public void setMeshFrame (int frameId);
|
||||
|
||||
/**
|
||||
* Recursively blends all skinned meshes between two stored animation
|
||||
* frames for the next update.
|
||||
*/
|
||||
public void blendMeshFrames (int frameId1, int frameId2, float alpha);
|
||||
|
||||
/**
|
||||
* Creates or populates and returns a clone of this object using the given
|
||||
* clone properties.
|
||||
*
|
||||
* @param store an instance of this class to populate, or <code>null</code>
|
||||
* to create a new instance
|
||||
*/
|
||||
public Spatial putClone (Spatial store, Model.CloneCreator properties);
|
||||
|
||||
/**
|
||||
* Recursively writes any data buffers to the output channel.
|
||||
*/
|
||||
public void writeBuffers (FileChannel out)
|
||||
throws IOException;
|
||||
|
||||
/**
|
||||
* Recursively reads any data buffers from the input channel.
|
||||
*/
|
||||
public void readBuffers (FileChannel in)
|
||||
throws IOException;
|
||||
|
||||
/**
|
||||
* Recursively slices any data buffers from the buffer map.
|
||||
*/
|
||||
public void sliceBuffers (MappedByteBuffer map);
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2005 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// 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.io.ObjectInput;
|
||||
import java.io.ObjectOutput;
|
||||
|
||||
import java.util.Properties;
|
||||
|
||||
import com.jme.math.Quaternion;
|
||||
import com.jme.math.Vector3f;
|
||||
import com.jme.scene.Controller;
|
||||
import com.jme.scene.Spatial;
|
||||
|
||||
import com.samskivert.util.StringUtil;
|
||||
|
||||
import com.threerings.jme.Log;
|
||||
|
||||
/**
|
||||
* A procedural animation that rotates a node around at a constant angular
|
||||
* velocity.
|
||||
*/
|
||||
public class Rotator extends ModelController
|
||||
{
|
||||
@Override // documentation inherited
|
||||
public void configure (Properties props, Spatial target)
|
||||
{
|
||||
super.configure(props, target);
|
||||
String axisstr = props.getProperty("axis", "x"),
|
||||
rpsstr = props.getProperty("radpersec", "3.14");
|
||||
if (axisstr.equalsIgnoreCase("x")) {
|
||||
_axis = Vector3f.UNIT_X;
|
||||
} else if (axisstr.equalsIgnoreCase("y")) {
|
||||
_axis = Vector3f.UNIT_Y;
|
||||
} else if (axisstr.equalsIgnoreCase("z")) {
|
||||
_axis = Vector3f.UNIT_Z;
|
||||
} else {
|
||||
float[] axis = StringUtil.parseFloatArray(axisstr);
|
||||
if (axis != null && axis.length == 3) {
|
||||
_axis = new Vector3f(axis[0], axis[1],
|
||||
axis[2]).normalizeLocal();
|
||||
|
||||
} else {
|
||||
Log.warning("Invalid rotation axis [axis=" + axisstr + "].");
|
||||
}
|
||||
}
|
||||
try {
|
||||
_radpersec = Float.parseFloat(rpsstr);
|
||||
} catch (NumberFormatException e) {
|
||||
Log.warning("Invalid rotation rate [radpersec=" + rpsstr + "].");
|
||||
}
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public void update (float time)
|
||||
{
|
||||
if (!isActive()) {
|
||||
return;
|
||||
}
|
||||
_rot.fromAngleNormalAxis(time * _radpersec, _axis);
|
||||
_target.getLocalRotation().multLocal(_rot);
|
||||
}
|
||||
|
||||
@Override // documentation inherited
|
||||
public Controller putClone (
|
||||
Controller store, Model.CloneCreator properties)
|
||||
{
|
||||
Rotator rstore;
|
||||
if (store == null) {
|
||||
rstore = new Rotator();
|
||||
} else {
|
||||
rstore = (Rotator)store;
|
||||
}
|
||||
super.putClone(rstore, properties);
|
||||
rstore._axis = _axis;
|
||||
rstore._radpersec = _radpersec;
|
||||
return rstore;
|
||||
}
|
||||
|
||||
// documentation inherited from interface Externalizable
|
||||
public void writeExternal (ObjectOutput out)
|
||||
throws IOException
|
||||
{
|
||||
super.writeExternal(out);
|
||||
out.writeObject(_axis);
|
||||
out.writeFloat(_radpersec);
|
||||
}
|
||||
|
||||
// documentation inherited from interface Externalizable
|
||||
public void readExternal (ObjectInput in)
|
||||
throws IOException, ClassNotFoundException
|
||||
{
|
||||
super.readExternal(in);
|
||||
_axis = (Vector3f)in.readObject();
|
||||
_radpersec = in.readFloat();
|
||||
}
|
||||
|
||||
/** The axis about which to rotate. */
|
||||
protected Vector3f _axis;
|
||||
|
||||
/** The velocity at which to rotate in radians per second. */
|
||||
protected float _radpersec;
|
||||
|
||||
/** A temporary quaternion. */
|
||||
protected Quaternion _rot = new Quaternion();
|
||||
|
||||
private static final long serialVersionUID = 1;
|
||||
}
|
||||
@@ -0,0 +1,495 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2005 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// 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.io.ObjectInput;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.ObjectOutput;
|
||||
import java.io.Serializable;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.FloatBuffer;
|
||||
import java.nio.IntBuffer;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
|
||||
import com.jme.bounding.BoundingVolume;
|
||||
import com.jme.math.Matrix4f;
|
||||
import com.jme.math.Vector3f;
|
||||
import com.jme.renderer.Renderer;
|
||||
import com.jme.scene.Spatial;
|
||||
import com.jme.scene.TriMesh;
|
||||
import com.jme.scene.VBOInfo;
|
||||
import com.jme.scene.batch.SharedBatch;
|
||||
import com.jme.scene.batch.TriangleBatch;
|
||||
import com.jme.system.DisplaySystem;
|
||||
import com.jme.util.geom.BufferUtils;
|
||||
|
||||
import com.samskivert.util.HashIntMap;
|
||||
|
||||
import com.threerings.jme.Log;
|
||||
|
||||
/**
|
||||
* A triangle mesh that deforms according to a bone hierarchy.
|
||||
*/
|
||||
public class SkinMesh extends ModelMesh
|
||||
{
|
||||
/** Represents the vertex weights of a group of vertices influenced by the
|
||||
* same set of bones. */
|
||||
public static class WeightGroup
|
||||
implements Serializable
|
||||
{
|
||||
/** The number of vertices in this weight group. */
|
||||
public int vertexCount;
|
||||
|
||||
/** The bones influencing this group. */
|
||||
public Bone[] bones;
|
||||
|
||||
/** The array of interleaved weights (of length <code>vertexCount *
|
||||
* boneIndices.length</code>): weights for first vertex, weights for
|
||||
* second, etc. */
|
||||
public float[] weights;
|
||||
|
||||
/**
|
||||
* Rebinds this weight group for a prototype instance.
|
||||
*
|
||||
* @param bmap the mapping from prototype to instance bones
|
||||
*/
|
||||
public WeightGroup rebind (HashMap<Bone, Bone> bmap)
|
||||
{
|
||||
WeightGroup wgroup = new WeightGroup();
|
||||
wgroup.vertexCount = vertexCount;
|
||||
wgroup.bones = new Bone[bones.length];
|
||||
for (int ii = 0; ii < bones.length; ii++) {
|
||||
wgroup.bones[ii] = bmap.get(bones[ii]);
|
||||
}
|
||||
wgroup.weights = weights;
|
||||
return wgroup;
|
||||
}
|
||||
|
||||
private static final long serialVersionUID = 1;
|
||||
}
|
||||
|
||||
/** Represents a bone that influences the mesh. */
|
||||
public static class Bone
|
||||
implements Serializable
|
||||
{
|
||||
/** The node that defines the bone's position. */
|
||||
public ModelNode node;
|
||||
|
||||
/** The inverse of the bone's model space reference transform. */
|
||||
public transient Matrix4f invRefTransform;
|
||||
|
||||
/** The bone's current transform in model space. */
|
||||
public transient Matrix4f transform;
|
||||
|
||||
public Bone (ModelNode node)
|
||||
{
|
||||
this.node = node;
|
||||
transform = new Matrix4f();
|
||||
}
|
||||
|
||||
/**
|
||||
* Rebinds this bone for a prototype instance.
|
||||
*
|
||||
* @param pnodes a mapping from prototype nodes to instance nodes
|
||||
*/
|
||||
public Bone rebind (HashMap pnodes)
|
||||
{
|
||||
Bone bone = new Bone((ModelNode)pnodes.get(node));
|
||||
bone.invRefTransform = invRefTransform;
|
||||
bone.transform = new Matrix4f();
|
||||
return bone;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the bone's transient state.
|
||||
*/
|
||||
private void readObject (ObjectInputStream in)
|
||||
throws IOException, ClassNotFoundException
|
||||
{
|
||||
in.defaultReadObject();
|
||||
transform = new Matrix4f();
|
||||
}
|
||||
|
||||
private static final long serialVersionUID = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* No-arg constructor for deserialization.
|
||||
*/
|
||||
public SkinMesh ()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an empty mesh.
|
||||
*/
|
||||
public SkinMesh (String name)
|
||||
{
|
||||
super(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the array of weight groups that determine how bones affect
|
||||
* each vertex.
|
||||
*/
|
||||
public void setWeightGroups (WeightGroup[] weightGroups)
|
||||
{
|
||||
_weightGroups = weightGroups;
|
||||
|
||||
// compile a list of all referenced bones
|
||||
HashSet<Bone> bones = new HashSet<Bone>();
|
||||
for (WeightGroup group : weightGroups) {
|
||||
Collections.addAll(bones, group.bones);
|
||||
}
|
||||
_bones = bones.toArray(new Bone[bones.size()]);
|
||||
}
|
||||
|
||||
@Override // documentation inherited
|
||||
public void reconstruct (
|
||||
ByteBuffer vertices, ByteBuffer normals, ByteBuffer colors,
|
||||
ByteBuffer textures, ByteBuffer indices)
|
||||
{
|
||||
super.reconstruct(vertices, normals, colors, textures, indices);
|
||||
|
||||
// store the current buffers as the originals
|
||||
storeOriginalBuffers();
|
||||
|
||||
// initialize the quantized frame table
|
||||
_frames = new HashIntMap<Object>();
|
||||
}
|
||||
|
||||
@Override // documentation inherited
|
||||
public void centerVertices ()
|
||||
{
|
||||
super.centerVertices();
|
||||
storeOriginalBuffers();
|
||||
}
|
||||
|
||||
@Override // documentation inherited
|
||||
public Spatial putClone (Spatial store, Model.CloneCreator properties)
|
||||
{
|
||||
SkinMesh mstore = (SkinMesh)properties.originalToCopy.get(this);
|
||||
if (mstore != null) {
|
||||
return mstore;
|
||||
} else if (store == null) {
|
||||
mstore = new SkinMesh(getName());
|
||||
} else {
|
||||
mstore = (SkinMesh)store;
|
||||
}
|
||||
properties.removeProperty("vertices");
|
||||
properties.removeProperty("normals");
|
||||
properties.removeProperty("displaylistid");
|
||||
super.putClone(mstore, properties);
|
||||
properties.addProperty("vertices");
|
||||
properties.addProperty("normals");
|
||||
properties.addProperty("displaylistid");
|
||||
mstore._frames = _frames;
|
||||
mstore._useDisplayLists = _useDisplayLists;
|
||||
mstore._invRefTransform = _invRefTransform;
|
||||
mstore._bones = new Bone[_bones.length];
|
||||
HashMap<Bone, Bone> bmap = new HashMap<Bone, Bone>();
|
||||
for (int ii = 0; ii < _bones.length; ii++) {
|
||||
bmap.put(_bones[ii], mstore._bones[ii] =
|
||||
_bones[ii].rebind(properties.originalToCopy));
|
||||
}
|
||||
mstore._weightGroups = new WeightGroup[_weightGroups.length];
|
||||
for (int ii = 0; ii < _weightGroups.length; ii++) {
|
||||
mstore._weightGroups[ii] = _weightGroups[ii].rebind(bmap);
|
||||
}
|
||||
mstore._ovbuf = _ovbuf;
|
||||
mstore._onbuf = _onbuf;
|
||||
mstore._vbuf = new float[_vbuf.length];
|
||||
mstore._nbuf = new float[_nbuf.length];
|
||||
return mstore;
|
||||
}
|
||||
|
||||
@Override // documentation inherited
|
||||
public void writeExternal (ObjectOutput out)
|
||||
throws IOException
|
||||
{
|
||||
super.writeExternal(out);
|
||||
out.writeObject(_weightGroups);
|
||||
}
|
||||
|
||||
@Override // documentation inherited
|
||||
public void readExternal (ObjectInput in)
|
||||
throws IOException, ClassNotFoundException
|
||||
{
|
||||
super.readExternal(in);
|
||||
setWeightGroups((WeightGroup[])in.readObject());
|
||||
}
|
||||
|
||||
@Override // documentation inherited
|
||||
public void expandModelBounds ()
|
||||
{
|
||||
BoundingVolume obound =
|
||||
(BoundingVolume)getBatch(0).getModelBound().clone(null);
|
||||
updateModelBound();
|
||||
getBatch(0).getModelBound().mergeLocal(obound);
|
||||
}
|
||||
|
||||
@Override // documentation inherited
|
||||
public void setReferenceTransforms ()
|
||||
{
|
||||
_invRefTransform = new Matrix4f();
|
||||
if (parent instanceof ModelNode) {
|
||||
Matrix4f transform = new Matrix4f();
|
||||
ModelNode.setTransform(getLocalTranslation(), getLocalRotation(),
|
||||
getLocalScale(), transform);
|
||||
((ModelNode)parent).getModelTransform().mult(transform,
|
||||
_invRefTransform);
|
||||
_invRefTransform.invertLocal();
|
||||
}
|
||||
for (Bone bone : _bones) {
|
||||
bone.invRefTransform =
|
||||
_invRefTransform.mult(bone.node.getModelTransform()).invert();
|
||||
}
|
||||
}
|
||||
|
||||
@Override // documentation inherited
|
||||
public void lockStaticMeshes (
|
||||
Renderer renderer, boolean useVBOs, boolean useDisplayLists)
|
||||
{
|
||||
// we can use VBOs for color, texture, and indices
|
||||
if (useVBOs && renderer.supportsVBO()) {
|
||||
VBOInfo vboinfo = new VBOInfo(false);
|
||||
vboinfo.setVBOColorEnabled(true);
|
||||
vboinfo.setVBOTextureEnabled(true);
|
||||
vboinfo.setVBOIndexEnabled(true);
|
||||
setVBOInfo(vboinfo);
|
||||
}
|
||||
_useDisplayLists = useDisplayLists;
|
||||
}
|
||||
|
||||
@Override // documentation inherited
|
||||
public void storeMeshFrame (int frameId, boolean blend)
|
||||
{
|
||||
_storeFrameId = frameId;
|
||||
_storeBlend = blend;
|
||||
}
|
||||
|
||||
@Override // documentation inherited
|
||||
public void setMeshFrame (int frameId)
|
||||
{
|
||||
TriangleBatch batch = getBatch(0),
|
||||
tbatch = (TriangleBatch)_frames.get(frameId);
|
||||
if (batch instanceof SharedBatch) {
|
||||
((SharedBatch)batch).setTarget(tbatch);
|
||||
} else {
|
||||
clearBatches();
|
||||
addBatch(new SharedBatch(tbatch));
|
||||
getBatch(0).updateRenderState();
|
||||
}
|
||||
}
|
||||
|
||||
@Override // documentation inherited
|
||||
public void blendMeshFrames (int frameId1, int frameId2, float alpha)
|
||||
{
|
||||
BlendFrame frame1 = (BlendFrame)_frames.get(frameId1),
|
||||
frame2 = (BlendFrame)_frames.get(frameId2);
|
||||
frame1.blend(frame2, alpha, _vbuf, _nbuf);
|
||||
FloatBuffer vbuf = getVertexBuffer(0), nbuf = getNormalBuffer(0);
|
||||
vbuf.rewind();
|
||||
vbuf.put(_vbuf);
|
||||
nbuf.rewind();
|
||||
nbuf.put(_nbuf);
|
||||
}
|
||||
|
||||
@Override // documentation inherited
|
||||
public void updateWorldData (float time)
|
||||
{
|
||||
super.updateWorldData(time);
|
||||
if (_weightGroups == null || _storeFrameId == -1) {
|
||||
return;
|
||||
}
|
||||
// update the bone transforms
|
||||
for (Bone bone : _bones) {
|
||||
_invRefTransform.mult(bone.node.getModelTransform(),
|
||||
bone.transform);
|
||||
bone.transform.multLocal(bone.invRefTransform);
|
||||
}
|
||||
|
||||
// deform the mesh according to the positions of the bones (this code
|
||||
// is ugly as sin because it's optimized at a low level)
|
||||
Bone[] bones;
|
||||
int vertexCount, jj, kk, ww;
|
||||
float[] weights;
|
||||
Matrix4f m;
|
||||
float weight, ovx, ovy, ovz, onx, ony, onz, vx, vy, vz, nx, ny, nz;
|
||||
for (int ii = 0, bidx = 0; ii < _weightGroups.length; ii++) {
|
||||
vertexCount = _weightGroups[ii].vertexCount;
|
||||
bones = _weightGroups[ii].bones;
|
||||
weights = _weightGroups[ii].weights;
|
||||
for (jj = 0, ww = 0; jj < vertexCount; jj++) {
|
||||
ovx = _ovbuf[bidx];
|
||||
ovy = _ovbuf[bidx + 1];
|
||||
ovz = _ovbuf[bidx + 2];
|
||||
onx = _onbuf[bidx];
|
||||
ony = _onbuf[bidx + 1];
|
||||
onz = _onbuf[bidx + 2];
|
||||
vx = vy = vz = 0f;
|
||||
nx = ny = nz = 0f;
|
||||
for (kk = 0; kk < bones.length; kk++) {
|
||||
m = bones[kk].transform;
|
||||
weight = weights[ww++];
|
||||
|
||||
vx += (ovx*m.m00 + ovy*m.m01 + ovz*m.m02 + m.m03) * weight;
|
||||
vy += (ovx*m.m10 + ovy*m.m11 + ovz*m.m12 + m.m13) * weight;
|
||||
vz += (ovx*m.m20 + ovy*m.m21 + ovz*m.m22 + m.m23) * weight;
|
||||
|
||||
nx += (onx*m.m00 + ony*m.m01 + onz*m.m02) * weight;
|
||||
ny += (onx*m.m10 + ony*m.m11 + onz*m.m12) * weight;
|
||||
nz += (onx*m.m20 + ony*m.m21 + onz*m.m22) * weight;
|
||||
}
|
||||
_vbuf[bidx] = vx;
|
||||
_vbuf[bidx + 1] = vy;
|
||||
_vbuf[bidx + 2] = vz;
|
||||
_nbuf[bidx++] = nx;
|
||||
_nbuf[bidx++] = ny;
|
||||
_nbuf[bidx++] = nz;
|
||||
}
|
||||
}
|
||||
|
||||
// if skinning in real time, copy the data from arrays to buffers;
|
||||
// otherwise, store the mesh as an animation frame
|
||||
if (_storeFrameId == 0) {
|
||||
FloatBuffer vbuf = getVertexBuffer(0), nbuf = getNormalBuffer(0);
|
||||
vbuf.rewind();
|
||||
vbuf.put(_vbuf);
|
||||
nbuf.rewind();
|
||||
nbuf.put(_nbuf);
|
||||
} else {
|
||||
storeFrame();
|
||||
_storeFrameId = -1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores the current frame data for later use.
|
||||
*/
|
||||
protected void storeFrame ()
|
||||
{
|
||||
if (_storeBlend) {
|
||||
_frames.put(_storeFrameId, new BlendFrame(
|
||||
(float[])_vbuf.clone(), (float[])_nbuf.clone()));
|
||||
} else {
|
||||
TriangleBatch batch = getBatch(0), tbatch = new TriangleBatch();
|
||||
tbatch.setParentGeom(DUMMY_MESH);
|
||||
tbatch.setColorBuffer(batch.getColorBuffer());
|
||||
tbatch.setTextureBuffer(batch.getTextureBuffer(0), 0);
|
||||
tbatch.setIndexBuffer(batch.getIndexBuffer());
|
||||
tbatch.setVertexBuffer(BufferUtils.createFloatBuffer(_vbuf));
|
||||
tbatch.setNormalBuffer(BufferUtils.createFloatBuffer(_nbuf));
|
||||
VBOInfo ovboinfo = batch.getVBOInfo();
|
||||
if (ovboinfo != null) {
|
||||
VBOInfo vboinfo = new VBOInfo(true);
|
||||
vboinfo.setVBOIndexEnabled(true);
|
||||
vboinfo.setVBOColorID(ovboinfo.getVBOColorID());
|
||||
vboinfo.setVBOTextureID(0, ovboinfo.getVBOTextureID(0));
|
||||
vboinfo.setVBOIndexID(ovboinfo.getVBOIndexID());
|
||||
tbatch.setVBOInfo(vboinfo);
|
||||
} else if (_useDisplayLists) {
|
||||
tbatch.lockMeshes(
|
||||
DisplaySystem.getDisplaySystem().getRenderer());
|
||||
}
|
||||
_frames.put(_storeFrameId, tbatch);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores the current vertex and normal buffers for later deformation.
|
||||
*/
|
||||
protected void storeOriginalBuffers ()
|
||||
{
|
||||
FloatBuffer vbuf = getVertexBuffer(0), nbuf = getNormalBuffer(0);
|
||||
vbuf.rewind();
|
||||
nbuf.rewind();
|
||||
FloatBuffer.wrap(_ovbuf = new float[vbuf.capacity()]).put(vbuf);
|
||||
FloatBuffer.wrap(_onbuf = new float[nbuf.capacity()]).put(nbuf);
|
||||
_vbuf = new float[_ovbuf.length];
|
||||
_nbuf = new float[_onbuf.length];
|
||||
}
|
||||
|
||||
/** A stored frame used for linear blending. */
|
||||
protected static class BlendFrame
|
||||
{
|
||||
/** The skinned vertex and normal values. */
|
||||
public float[] vbuf, nbuf;
|
||||
|
||||
public BlendFrame (float[] vbuf, float[] nbuf)
|
||||
{
|
||||
this.vbuf = vbuf;
|
||||
this.nbuf = nbuf;
|
||||
}
|
||||
|
||||
public void blend (
|
||||
BlendFrame next, float alpha, float[] rvbuf, float[] rnbuf)
|
||||
{
|
||||
float[] nvbuf = next.vbuf, nnbuf = next.nbuf;
|
||||
float ialpha = 1f - alpha;
|
||||
for (int ii = 0, nn = vbuf.length; ii < nn; ii++) {
|
||||
rvbuf[ii] = vbuf[ii] * ialpha + nvbuf[ii] * alpha;
|
||||
rnbuf[ii] = nbuf[ii] * ialpha + nnbuf[ii] * alpha;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Pre-skinned {@link TriangleBatch}es or {@link BlendFrame}s shared
|
||||
* between all instances corresponding to frame ids from
|
||||
* {@link #storeAnimationFrame}. */
|
||||
protected HashIntMap<Object> _frames;
|
||||
|
||||
/** Whether or to use display lists if VBOs are unavailable for quantized
|
||||
* meshes. */
|
||||
protected boolean _useDisplayLists;
|
||||
|
||||
/** The inverse of the model space reference transform. */
|
||||
protected Matrix4f _invRefTransform;
|
||||
|
||||
/** The groups of vertices influenced by different sets of bones. */
|
||||
protected WeightGroup[] _weightGroups;
|
||||
|
||||
/** The bones referenced by the weight groups. */
|
||||
protected Bone[] _bones;
|
||||
|
||||
/** The original (undeformed) vertex and normal buffers and the deformed
|
||||
* versions. */
|
||||
protected float[] _ovbuf, _onbuf, _vbuf, _nbuf;
|
||||
|
||||
/** The frame id to store on the next update. If 0, don't store any frame
|
||||
* and skin the mesh as normal. If -1, a frame has been stored and thus
|
||||
* skinning should only take place when further frames are requested. */
|
||||
protected int _storeFrameId;
|
||||
|
||||
/** Whether or not the stored frame id will be used for blending. */
|
||||
protected boolean _storeBlend;
|
||||
|
||||
/** A dummy mesh that simply hold transformation values. */
|
||||
protected static final TriMesh DUMMY_MESH = new TriMesh();
|
||||
|
||||
private static final long serialVersionUID = 1;
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2005 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// 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 com.jme.scene.state.TextureState;
|
||||
|
||||
/**
|
||||
* Provides a means for models to resolve their texture references.
|
||||
*/
|
||||
public interface TextureProvider
|
||||
{
|
||||
/**
|
||||
* Returns a texture state containing the named texture.
|
||||
*
|
||||
* @param name the name of the texture, which will be interpreted as an
|
||||
* absolute resource path if it starts with a forward slash; otherwise,
|
||||
* as a relative resource path
|
||||
*/
|
||||
public TextureState getTexture (String name);
|
||||
}
|
||||
Reference in New Issue
Block a user