Checkpoint checkin for new model exporting/loading/animating code.

git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@4013 542714f4-19e9-0310-aa3c-eee0fc999fb1
This commit is contained in:
Andrzej Kapolka
2006-04-12 03:34:10 +00:00
parent 4aa1971f30
commit bb32d03c66
13 changed files with 1806 additions and 0 deletions
@@ -0,0 +1,99 @@
//
// $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 com.jme.scene.Node;
import com.jme.math.Quaternion;
import com.jme.math.TransformMatrix;
import com.jme.math.Vector3f;
import com.threerings.jme.Log;
/**
* Represents a bone in a scene. Bones can have simple meshes as children,
* or they can be used to deform {@link SkinMesh}es.
*/
public class BoneNode extends ModelNode
{
/**
* No-arg constructor for deserialization.
*/
public BoneNode ()
{
}
/**
* Default constructor.
*
* @param name the name of the node
*/
public BoneNode (String name)
{
super(name);
}
@Override // documentation inherited
public void setReferenceTransforms ()
{
super.setReferenceTransforms();
// store the inverse of the reference transform
_invRefTransform.set(worldRotation, worldTranslation);
_invRefTransform.setScale(worldScale);
_invRefTransform.inverse();
}
@Override // documentation inherited
public void updateWorldVectors ()
{
super.updateWorldVectors();
_skinTransform.set(worldRotation, worldTranslation);
_skinTransform.setScale(worldScale);
_skinTransform.multLocal(_invRefTransform, _tempStore);
}
/**
* Returns a reference to the matrix that transforms vertices in the
* reference position to vertices in the current position.
*/
public TransformMatrix getSkinTransform ()
{
return _skinTransform;
}
/** The inverse of the bone's world space reference transform. */
protected TransformMatrix _invRefTransform = new TransformMatrix();
/** The bone's current skin transform. */
protected TransformMatrix _skinTransform = new TransformMatrix();
/** A temporary vector for transformations. */
protected Vector3f _tempStore = new Vector3f();
private static final long serialVersionUID = 1;
}
@@ -0,0 +1,122 @@
//
// $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.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectInputStream;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;
import java.nio.channels.FileChannel;
import com.threerings.jme.Log;
/**
* The base node for models.
*/
public class Model extends ModelNode
{
/**
* Attempts to read a model from the specified file.
*
* @param map if true, map buffers into memory directly from the
* file
*/
public static Model readFromFile (File file, boolean map)
throws IOException
{
// read the serialized model and its children
FileInputStream fis = new FileInputStream(file);
ObjectInputStream ois = new ObjectInputStream(fis);
Model model;
try {
model = (Model)ois.readObject();
} catch (ClassNotFoundException e) {
Log.warning("Encountered unknown class [error=" + e + "].");
return null;
}
// then either read or map the buffers
FileChannel fc = fis.getChannel();
if (map) {
long pos = fc.position();
model.sliceBuffers(fc.map(FileChannel.MapMode.READ_ONLY,
pos, fc.size() - pos));
} else {
model.readBuffers(fc);
}
ois.close();
return model;
}
/**
* No-arg constructor for deserialization.
*/
public Model ()
{
}
/**
* Standard constructor.
*/
public Model (String name)
{
super(name);
}
/**
* Writes this model out to a file.
*/
public void writeToFile (File file)
throws IOException
{
// start out by writing this node and its children
FileOutputStream fos = new FileOutputStream(file);
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(this);
// now traverse the scene graph appending buffers to the
// end of the file
writeBuffers(fos.getChannel());
oos.close();
}
@Override // documentation inherited
public void writeExternal (ObjectOutput out)
throws IOException
{
super.writeExternal(out);
}
@Override // documentation inherited
public void readExternal (ObjectInput in)
throws IOException
{
super.readExternal(in);
}
private static final long serialVersionUID = 1;
}
@@ -0,0 +1,263 @@
//
// $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.FloatBuffer;
import java.nio.IntBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import com.jme.math.Quaternion;
import com.jme.math.Vector3f;
import com.jme.scene.TriMesh;
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 ()
{
}
/**
* Creates a mesh with no vertex data.
*/
public ModelMesh (String name)
{
super(name);
}
/**
* Creates and populates a mesh.
*/
public ModelMesh (
String name, FloatBuffer vertices, FloatBuffer normals,
FloatBuffer colors, FloatBuffer tcoords, IntBuffer indices)
{
super(name, vertices, normals, colors, tcoords, indices);
}
/**
* Sets the buffers as {@link ByteBuffer}s, because we can't create byte
* views of non-byte buffers.
*/
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;
}
// 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();
}
// 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 Externalizable
public void writeExternal (ObjectOutput out)
throws IOException
{
out.writeUTF(getName());
out.writeObject(getLocalTranslation());
out.writeObject(getLocalRotation());
out.writeObject(getLocalScale());
out.writeInt(_vertexBufferSize);
out.writeInt(_normalBufferSize);
out.writeInt(_colorBufferSize);
out.writeInt(_textureBufferSize);
out.writeInt(_indexBufferSize);
}
// documentation inherited from interface Externalizable
public void readExternal (ObjectInput in)
throws IOException
{
setName(in.readUTF());
try {
setLocalTranslation((Vector3f)in.readObject());
setLocalRotation((Quaternion)in.readObject());
setLocalScale((Vector3f)in.readObject());
} catch (ClassNotFoundException e) {
Log.warning("Encounted unknown class [mesh=" + getName() +
", exception=" + e + "].");
}
_vertexBufferSize = in.readInt();
_normalBufferSize = in.readInt();
_colorBufferSize = in.readInt();
_textureBufferSize = in.readInt();
_indexBufferSize = in.readInt();
}
// documentation inherited from interface ModelSpatial
public void writeBuffers (FileChannel out)
throws IOException
{
if (_vertexBufferSize > 0) {
_vertexByteBuffer.rewind();
out.write(_vertexByteBuffer);
}
if (_normalBufferSize > 0) {
_normalByteBuffer.rewind();
out.write(_normalByteBuffer);
}
if (_colorBufferSize > 0) {
_colorByteBuffer.rewind();
out.write(_colorByteBuffer);
}
if (_textureBufferSize > 0) {
_indexByteBuffer.rewind();
out.write(_indexByteBuffer);
}
if (_indexBufferSize > 0) {
_indexByteBuffer.rewind();
out.write(_indexByteBuffer);
}
}
// documentation inherited from interface ModelSpatial
public void readBuffers (FileChannel in)
throws IOException
{
ByteBuffer vbbuf = null, nbbuf = null, cbbuf = null, tbbuf = null,
ibbuf = null;
if (_vertexBufferSize > 0) {
vbbuf = ByteBuffer.allocateDirect(_vertexBufferSize*3*4);
in.read(vbbuf);
vbbuf.rewind();
}
if (_normalBufferSize > 0) {
nbbuf = ByteBuffer.allocateDirect(_normalBufferSize*3*4);
in.read(nbbuf);
nbbuf.rewind();
}
if (_colorBufferSize > 0) {
cbbuf = ByteBuffer.allocateDirect(_colorBufferSize*4*4);
in.read(cbbuf);
cbbuf.rewind();
}
if (_textureBufferSize > 0) {
tbbuf = ByteBuffer.allocateDirect(_textureBufferSize*2*4);
in.read(tbbuf);
tbbuf.rewind();
}
if (_indexBufferSize > 0) {
ibbuf = ByteBuffer.allocateDirect(_indexBufferSize*4);
in.read(ibbuf);
ibbuf.rewind();
}
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;
if (_vertexBufferSize > 0) {
int npos = map.position() + _vertexBufferSize*3*4;
map.limit(npos);
vbbuf = map.slice();
map.position(npos);
}
if (_normalBufferSize > 0) {
int npos = map.position() + _normalBufferSize*3*4;
map.limit(npos);
nbbuf = map.slice();
map.position(npos);
}
if (_colorBufferSize > 0) {
int npos = map.position() + _colorBufferSize*4*4;
map.limit(npos);
cbbuf = map.slice();
map.position(npos);
}
if (_textureBufferSize > 0) {
int npos = map.position() + _textureBufferSize*2*4;
map.limit(npos);
tbbuf = map.slice();
map.position(npos);
}
if (_indexBufferSize > 0) {
int npos = map.position() + _indexBufferSize*4;
map.limit(npos);
ibbuf = map.slice();
map.position(npos);
}
reconstruct(vbbuf, nbbuf, cbbuf, tbbuf, ibbuf);
}
/** 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;
private static final long serialVersionUID = 1;
}
@@ -0,0 +1,139 @@
//
// $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 com.jme.math.Quaternion;
import com.jme.math.Vector3f;
import com.jme.scene.Node;
import com.jme.scene.Spatial;
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 ()
{
}
/**
* Standard constructor.
*/
public ModelNode (String name)
{
super(name);
}
/**
* Recursively sets the reference transforms for any {@link BoneNode}s in
* the model.
*/
public void setReferenceTransforms ()
{
for (Object child : getChildren()) {
if (child instanceof ModelNode) {
((ModelNode)child).setReferenceTransforms();
}
}
}
// 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
{
setName(in.readUTF());
try {
setLocalTranslation((Vector3f)in.readObject());
setLocalRotation((Quaternion)in.readObject());
setLocalScale((Vector3f)in.readObject());
ArrayList children = (ArrayList)in.readObject();
for (int ii = 0, nn = children.size(); ii < nn; ii++) {
attachChild((Spatial)children.get(ii));
}
} catch (ClassNotFoundException e) {
Log.warning("Encounted unknown class [node=" + getName() +
", exception=" + e + "].");
}
}
// documentation inherited from interface ModelSpatial
public void writeBuffers (FileChannel out)
throws IOException
{
for (Object child : getChildren()) {
if (child instanceof ModelSpatial) {
((ModelSpatial)child).writeBuffers(out);
}
}
}
// documentation inherited from interface ModelSpatial
public void readBuffers (FileChannel in)
throws IOException
{
for (Object child : getChildren()) {
if (child instanceof ModelSpatial) {
((ModelSpatial)child).readBuffers(in);
}
}
}
// documentation inherited from interface ModelSpatial
public void sliceBuffers (MappedByteBuffer map)
{
for (Object child : getChildren()) {
if (child instanceof ModelSpatial) {
((ModelSpatial)child).sliceBuffers(map);
}
}
}
private static final long serialVersionUID = 1;
}
@@ -0,0 +1,51 @@
//
// $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;
/**
* Contains method common to both {@link ModelNode}s and {@link ModelMesh}es.
*/
public interface ModelSpatial
{
/**
* 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,137 @@
//
// $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.io.Serializable;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
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 indices of the affected vertices. */
public int[] indices;
/** The bones influencing this group. */
public BoneNode[] bones;
/** The array of interleaved weights (of length <code>indices.length *
* bones.length</code>): weights for first vertex, weights for second,
* etc. */
public float[] weights;
private static final long serialVersionUID = 1;
}
/**
* No-arg constructor for deserialization.
*/
public SkinMesh ()
{
}
/**
* Creates an empty mesh.
*/
public SkinMesh (String name)
{
super(name);
}
/**
* Creates and populates a mesh.
*/
public SkinMesh (
String name, FloatBuffer vertices, FloatBuffer normals,
FloatBuffer color, FloatBuffer texture, IntBuffer indices)
{
super(name, vertices, normals, color, texture, indices);
}
/**
* Sets the weight groups that determine how vertices are affected by
* bones.
*/
public void setWeightGroups (WeightGroup[] weightGroups)
{
_weightGroups = weightGroups;
}
/**
* Returns a reference to the array of weight groups.
*/
public WeightGroup[] getWeightGroups ()
{
return _weightGroups;
}
@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
{
super.readExternal(in);
try {
_weightGroups = (WeightGroup[])in.readObject();
} catch (ClassNotFoundException e) {
Log.warning("Encounted unknown class [mesh=" + getName() +
", exception=" + e + "].");
}
}
@Override // documentation inherited
public void updateWorldData (float time)
{
super.updateWorldData(time);
// deform the mesh according to the positions of the bones
for (int ii = 0; ii < _weightGroups.length; ii++) {
}
}
/** The groups of vertices influenced by different sets of bones. */
protected WeightGroup[] _weightGroups;
private static final long serialVersionUID = 1;
}
@@ -0,0 +1,62 @@
//
// $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.tools;
import java.util.ArrayList;
/**
* A basic representation for keyframe animations.
*/
public class AnimationDef
{
/** A single frame of the animation. */
public static class Frame
{
/** Transforms for affected nodes. */
public ArrayList<Transform> transforms = new ArrayList<Transform>();
public void addTransform (Transform transform)
{
transforms.add(transform);
}
}
/** A transform for a single node. */
public static class Transform
{
/** The name of the node to transform. */
public String name;
/** The transformation parameters. */
public float[] translation;
public float[] rotation;
public float[] scale;
}
/** The individual frames of the animation. */
public ArrayList<Frame> frames = new ArrayList<Frame>();
public void addFrame (Frame frame)
{
frames.add(frame);
}
}
@@ -0,0 +1,106 @@
//
// $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.tools;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.DirectoryScanner;
import org.apache.tools.ant.Task;
import org.apache.tools.ant.types.FileSet;
import com.threerings.jme.model.BoneNode;
import com.threerings.jme.model.Model;
import com.threerings.jme.model.ModelMesh;
import com.threerings.jme.model.ModelNode;
import com.threerings.jme.model.SkinMesh;
import com.threerings.jme.tools.xml.ModelParser;
/**
* An ant task for compiling 3D models defined in XML to fast-loading binary
* files.
*/
public class CompileModelTask extends Task
{
public void addFileset (FileSet set)
{
_filesets.add(set);
}
public void execute ()
throws BuildException
{
for (int ii = 0, nn = _filesets.size(); ii < nn; ii++) {
FileSet fs = _filesets.get(ii);
DirectoryScanner ds = fs.getDirectoryScanner(getProject());
File fromDir = fs.getDir(getProject());
String[] srcFiles = ds.getIncludedFiles();
for (int f = 0; f < srcFiles.length; f++) {
File cfile = new File(fromDir, srcFiles[f]);
String target = srcFiles[f];
int didx = target.lastIndexOf(".");
target = (didx == -1) ? target : target.substring(0, didx);
target += ".dat";
compileModel(cfile, new File(fromDir, target));
}
}
}
protected void compileModel (File source, File target)
{
if (source.lastModified() < target.lastModified()) {
return;
}
System.out.println("Compiling " + source + "...");
ModelDef mdef;
try {
mdef = _mparser.parseModel(source.toString());
} catch (Exception e) {
System.err.println("Error parsing '" + source + "': " + e);
return;
}
Model model = mdef.createModel("model");
try {
FileOutputStream fos = new FileOutputStream(target);
new ObjectOutputStream(fos).writeObject(model);
} catch (IOException e) {
System.err.println("Error writing '" + target + "': " + e);
}
}
/** A list of filesets that contain XML models. */
protected ArrayList<FileSet> _filesets = new ArrayList<FileSet>();
/** A parser for the model definitions. */
protected ModelParser _mparser = new ModelParser();
}
@@ -0,0 +1,364 @@
//
// $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.tools;
import java.nio.ByteBuffer;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import com.jme.scene.Spatial;
import com.jme.util.geom.BufferUtils;
import com.threerings.jme.Log;
import com.threerings.jme.model.BoneNode;
import com.threerings.jme.model.Model;
import com.threerings.jme.model.ModelMesh;
import com.threerings.jme.model.ModelNode;
import com.threerings.jme.model.SkinMesh;
/**
* An intermediate representation for model nodes using in XML parsing.
*/
public class ModelDef
{
/** The base class of nodes in the model. */
public abstract static class SpatialDef
{
/** The node's name. */
public String name;
/** The name of the node's parent. */
public String parent;
/** The node's transformation. */
public float[] translation;
public float[] rotation;
public float[] scale;
/** Returns a JME node for this definition. */
public Spatial getSpatial ()
{
if (_spatial == null) {
_spatial = createSpatial();
setTransform();
}
return _spatial;
}
/** Sets the transform of the created node. */
protected void setTransform ()
{
_spatial.getLocalTranslation().set(translation[0], translation[1],
translation[2]);
_spatial.getLocalRotation().set(rotation[0], rotation[1],
rotation[2], rotation[3]);
_spatial.getLocalScale().set(scale[0], scale[1], scale[2]);
}
/** Creates a JME node for this definition. */
public abstract Spatial createSpatial ();
/** Resolves any name references using the supplied map. */
public void resolveReferences (HashMap<String, Spatial> nodes)
{
Spatial pnode = nodes.get(parent);
if (pnode instanceof ModelNode) {
((ModelNode)pnode).attachChild(_spatial);
} else if (parent != null) {
Log.warning("Missing or invalid parent node [spatial=" +
name + ", parent=" + parent + "].");
}
}
/** The JME node created for this definition. */
protected Spatial _spatial;
}
/** A rigid triangle mesh. */
public static class TriMeshDef extends SpatialDef
{
/** The vertices of the mesh. */
public ArrayList<Vertex> vertices = new ArrayList<Vertex>();
/** The triangle indices. */
public ArrayList<Integer> indices = new ArrayList<Integer>();
/** Whether or not any of the vertices have texture coordinates. */
public boolean tcoords;
public void addVertex (Vertex vertex)
{
int idx = vertices.indexOf(vertex);
if (idx != -1) {
indices.add(idx);
} else {
indices.add(vertices.size());
vertices.add(vertex);
}
tcoords = tcoords || vertex.tcoords != null;
}
// documentation inherited
public Spatial createSpatial ()
{
return setBuffers(new ModelMesh(name));
}
/** Sets the mesh's geometry buffers and returns it. */
protected ModelMesh setBuffers (ModelMesh mmesh)
{
int vsize = vertices.size();
ByteBuffer vbbuf = ByteBuffer.allocateDirect(vsize*3*4),
nbbuf = ByteBuffer.allocateDirect(vsize*3*4),
tbbuf = tcoords ? ByteBuffer.allocateDirect(vsize*2*4) : null,
ibbuf = ByteBuffer.allocateDirect(indices.size()*4);
FloatBuffer vbuf = vbbuf.asFloatBuffer(),
nbuf = nbbuf.asFloatBuffer(),
tbuf = tcoords ? tbbuf.asFloatBuffer() : null;
for (int ii = 0; ii < vsize; ii++) {
vertices.get(ii).setInBuffers(vbuf, nbuf, tbuf);
}
IntBuffer ibuf = ibbuf.asIntBuffer();
for (int ii = 0, nn = indices.size(); ii < nn; ii++) {
ibuf.put(indices.get(ii));
}
mmesh.reconstruct(vbbuf, nbbuf, null, tbbuf, ibbuf);
return mmesh;
}
}
/** A triangle mesh that deforms according to bone positions. */
public static class SkinMeshDef extends TriMeshDef
{
// documentation inherited
public Spatial createSpatial ()
{
return setBuffers(new SkinMesh(name));
}
@Override // documentation inherited
public void resolveReferences (HashMap<String, Spatial> nodes)
{
super.resolveReferences(nodes);
// divide the vertices up by weight groups
HashMap<HashSet<String>, WeightGroupDef> groups =
new HashMap<HashSet<String>, WeightGroupDef>();
for (int ii = 0, nn = vertices.size(); ii < nn; ii++) {
SkinVertex svertex = (SkinVertex)vertices.get(ii);
HashSet<String> bones = svertex.getBoneNames();
WeightGroupDef group = groups.get(bones);
if (group == null) {
groups.put(bones, group = new WeightGroupDef());
}
group.indices.add(ii);
for (String bone : bones) {
group.weights.add(svertex.getWeight(bone));
}
}
// resolve names and set in mesh
SkinMesh.WeightGroup[] wgroups =
new SkinMesh.WeightGroup[groups.size()];
int ii = 0;
for (Map.Entry<HashSet<String>, WeightGroupDef> entry :
groups.entrySet()) {
SkinMesh.WeightGroup wgroup = new SkinMesh.WeightGroup();
wgroup.indices = toArray(entry.getValue().indices);
wgroup.bones = new BoneNode[entry.getKey().size()];
int jj = 0;
for (String bone : entry.getKey()) {
Spatial node = nodes.get(bone);
if (!(node instanceof BoneNode)) {
Log.warning("Missing or invalid bone reference " +
"[mesh=" + name + ", bone=" + bone + "].");
return;
}
wgroup.bones[jj++] = (BoneNode)node;
}
wgroup.weights = toArray(entry.getValue().weights);
wgroups[ii++] = wgroup;
}
((SkinMesh)_spatial).setWeightGroups(wgroups);
}
}
/** A generic node. */
public static class NodeDef extends SpatialDef
{
// documentation inherited
public Spatial createSpatial ()
{
return new ModelNode(name);
}
}
/** A bone that influences skinned meshes. */
public static class BoneNodeDef extends NodeDef
{
// documentation inherited
public Spatial createSpatial ()
{
return new BoneNode(name);
}
}
/** A basic vertex. */
public static class Vertex
{
public float[] location;
public float[] normal;
public float[] tcoords;
public void setInBuffers (
FloatBuffer vbuf, FloatBuffer nbuf, FloatBuffer tbuf)
{
vbuf.put(location);
nbuf.put(normal);
if (tbuf != null) {
tbuf.put(tcoords);
}
}
public boolean equals (Object obj)
{
Vertex overt = (Vertex)obj;
return Arrays.equals(location, overt.location) &&
Arrays.equals(normal, overt.normal) &&
Arrays.equals(tcoords, overt.tcoords);
}
}
/** A vertex influenced by a number of bones. */
public static class SkinVertex extends Vertex
{
/** The bones influencing the vertex. */
public ArrayList<BoneWeight> boneWeights = new ArrayList<BoneWeight>();
public void addBoneWeight (BoneWeight weight)
{
boneWeights.add(weight);
}
/** Returns the set of names of the bones influencing this vertex. */
public HashSet<String> getBoneNames ()
{
HashSet<String> names = new HashSet<String>();
for (BoneWeight bweight : boneWeights) {
names.add(bweight.bone);
}
return names;
}
/** Returns the weight of the named bone. */
public float getWeight (String bone)
{
for (BoneWeight bweight : boneWeights) {
if (bweight.bone.equals(bone)) {
return bweight.weight;
}
}
return 0f;
}
}
/** The influence of a bone on a vertex. */
public static class BoneWeight
{
/** The name of the influencing bone. */
public String bone;
/** The amount of influence. */
public float weight;
}
/** A group of vertices influenced by the same bone. */
public static class WeightGroupDef
{
/** The indices of the affected vertex. */
public ArrayList<Integer> indices = new ArrayList<Integer>();
/** The interleaved vertex weights. */
public ArrayList<Float> weights = new ArrayList<Float>();
}
/** The meshes and bones comprising the model. */
public ArrayList<SpatialDef> spatials = new ArrayList<SpatialDef>();
public void addSpatial (SpatialDef spatial)
{
spatials.add(spatial);
}
/**
* Creates the model node defined herein.
*/
public Model createModel (String name)
{
Model model = new Model(name);
// start by creating the spatials and mapping them to their names
HashMap<String, Spatial> nodes = new HashMap<String, Spatial>();
for (int ii = 0, nn = spatials.size(); ii < nn; ii++) {
Spatial spatial = spatials.get(ii).getSpatial();
nodes.put(spatial.getName(), spatial);
}
// then go through again, resolving any name references and attaching
// root children
for (int ii = 0, nn = spatials.size(); ii < nn; ii++) {
SpatialDef sdef = spatials.get(ii);
sdef.resolveReferences(nodes);
if (sdef.getSpatial().getParent() == null) {
model.attachChild(sdef.getSpatial());
}
}
return model;
}
/** Converts a boxed Integer list to an unboxed int array. */
protected static int[] toArray (ArrayList<Integer> list)
{
int[] array = new int[list.size()];
for (int ii = 0, nn = list.size(); ii < nn; ii++) {
array[ii] = list.get(ii);
}
return array;
}
/** Converts a boxed Float list to an unboxed float array. */
protected static float[] toArray (ArrayList<Float> list)
{
float[] array = new float[list.size()];
for (int ii = 0, nn = list.size(); ii < nn; ii++) {
array[ii] = list.get(ii);
}
return array;
}
}
@@ -0,0 +1,86 @@
//
// $Id: SceneParser.java 3749 2005-11-09 04:00:16Z mdb $
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2004 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.tools.xml;
import java.io.FileInputStream;
import java.io.IOException;
import org.xml.sax.SAXException;
import org.apache.commons.digester.Digester;
import com.samskivert.xml.SetPropertyFieldsRule;
import com.threerings.jme.tools.AnimationDef;
/**
* Parses XML files containing animations.
*/
public class AnimationParser
{
public AnimationParser ()
{
// create and configure our digester
_digester = new Digester();
// add the rules
String anim = "animation";
_digester.addObjectCreate(anim, AnimationDef.class.getName());
_digester.addSetNext(anim, "setAnimation",
AnimationDef.class.getName());
String frame = anim + "/frame";
_digester.addObjectCreate(frame, AnimationDef.Frame.class.getName());
_digester.addSetNext(frame, "addFrame",
AnimationDef.Frame.class.getName());
String xform = frame + "/transform";
_digester.addObjectCreate(xform,
AnimationDef.Transform.class.getName());
_digester.addRule(xform, new SetPropertyFieldsRule());
_digester.addSetNext(xform, "addTransform",
AnimationDef.Transform.class.getName());
}
/**
* Parses the XML file at the specified path into an animation
* definition.
*/
public AnimationDef parseAnimation (String path)
throws IOException, SAXException
{
_animation = null;
_digester.push(this);
_digester.parse(new FileInputStream(path));
return _animation;
}
/**
* Called by the parser once the animation is parsed.
*/
public void setAnimation (AnimationDef animation)
{
_animation = animation;
}
protected Digester _digester;
protected AnimationDef _animation;
}
@@ -0,0 +1,113 @@
//
// $Id: SceneParser.java 3749 2005-11-09 04:00:16Z mdb $
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2004 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.tools.xml;
import java.io.FileInputStream;
import java.io.IOException;
import org.xml.sax.SAXException;
import org.apache.commons.digester.Digester;
import com.samskivert.xml.SetPropertyFieldsRule;
import com.threerings.jme.tools.ModelDef;
/**
* Parses XML files containing 3D models.
*/
public class ModelParser
{
public ModelParser ()
{
// create and configure our digester
_digester = new Digester();
// add the rules
String model = "model";
_digester.addObjectCreate(model, ModelDef.class.getName());
_digester.addSetNext(model, "setModel", ModelDef.class.getName());
String tmesh = model + "/triMesh";
_digester.addObjectCreate(tmesh, ModelDef.TriMeshDef.class.getName());
_digester.addRule(tmesh, new SetPropertyFieldsRule());
_digester.addSetNext(tmesh, "addSpatial",
ModelDef.SpatialDef.class.getName());
String smesh = model + "/skinMesh";
_digester.addObjectCreate(smesh,
ModelDef.SkinMeshDef.class.getName());
_digester.addRule(smesh, new SetPropertyFieldsRule());
_digester.addSetNext(smesh, "addSpatial",
ModelDef.SpatialDef.class.getName());
String node = model + "/node";
_digester.addObjectCreate(node, ModelDef.NodeDef.class.getName());
_digester.addRule(node, new SetPropertyFieldsRule());
_digester.addSetNext(node, "addSpatial",
ModelDef.SpatialDef.class.getName());
String bnode = model + "/boneNode";
_digester.addObjectCreate(bnode, ModelDef.BoneNodeDef.class.getName());
_digester.addRule(bnode, new SetPropertyFieldsRule());
_digester.addSetNext(bnode, "addSpatial",
ModelDef.SpatialDef.class.getName());
String vertex = tmesh + "/vertex";
_digester.addObjectCreate(vertex, ModelDef.Vertex.class.getName());
_digester.addObjectCreate(smesh + "/vertex",
ModelDef.SkinVertex.class.getName());
_digester.addRule("*/vertex", new SetPropertyFieldsRule());
String bweight = smesh + "/vertex/boneWeight";
_digester.addObjectCreate(bweight,
ModelDef.BoneWeight.class.getName());
_digester.addRule(bweight, new SetPropertyFieldsRule());
_digester.addSetNext(bweight, "addBoneWeight",
ModelDef.BoneWeight.class.getName());
_digester.addSetNext("*/vertex", "addVertex",
ModelDef.Vertex.class.getName());
}
/**
* Parses the XML file at the specified path into a model definition.
*/
public ModelDef parseModel (String path)
throws IOException, SAXException
{
_model = null;
_digester.push(this);
_digester.parse(new FileInputStream(path));
return _model;
}
/**
* Called by the parser once the model is parsed.
*/
public void setModel (ModelDef model)
{
_model = model;
}
protected Digester _digester;
protected ModelDef _model;
}