From bb32d03c66b5738d75d271f3c79d9d0689b85b37 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 12 Apr 2006 03:34:10 +0000 Subject: [PATCH] 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 --- .../com/threerings/jme/model/BoneNode.java | 99 +++++ src/java/com/threerings/jme/model/Model.java | 122 ++++++ .../com/threerings/jme/model/ModelMesh.java | 263 +++++++++++++ .../com/threerings/jme/model/ModelNode.java | 139 +++++++ .../threerings/jme/model/ModelSpatial.java | 51 +++ .../com/threerings/jme/model/SkinMesh.java | 137 +++++++ .../threerings/jme/tools/AnimationDef.java | 62 +++ .../jme/tools/CompileModelTask.java | 106 +++++ .../com/threerings/jme/tools/ModelDef.java | 364 ++++++++++++++++++ .../jme/tools/xml/AnimationParser.java | 86 +++++ .../threerings/jme/tools/xml/ModelParser.java | 113 ++++++ src/ms/TRAnimationExporter.mcr | 92 +++++ src/ms/TRModelExporter.mcr | 172 +++++++++ 13 files changed, 1806 insertions(+) create mode 100644 src/java/com/threerings/jme/model/BoneNode.java create mode 100644 src/java/com/threerings/jme/model/Model.java create mode 100644 src/java/com/threerings/jme/model/ModelMesh.java create mode 100644 src/java/com/threerings/jme/model/ModelNode.java create mode 100644 src/java/com/threerings/jme/model/ModelSpatial.java create mode 100644 src/java/com/threerings/jme/model/SkinMesh.java create mode 100644 src/java/com/threerings/jme/tools/AnimationDef.java create mode 100644 src/java/com/threerings/jme/tools/CompileModelTask.java create mode 100644 src/java/com/threerings/jme/tools/ModelDef.java create mode 100644 src/java/com/threerings/jme/tools/xml/AnimationParser.java create mode 100644 src/java/com/threerings/jme/tools/xml/ModelParser.java create mode 100644 src/ms/TRAnimationExporter.mcr create mode 100644 src/ms/TRModelExporter.mcr diff --git a/src/java/com/threerings/jme/model/BoneNode.java b/src/java/com/threerings/jme/model/BoneNode.java new file mode 100644 index 000000000..f83c6580e --- /dev/null +++ b/src/java/com/threerings/jme/model/BoneNode.java @@ -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; +} diff --git a/src/java/com/threerings/jme/model/Model.java b/src/java/com/threerings/jme/model/Model.java new file mode 100644 index 000000000..51b9d53e4 --- /dev/null +++ b/src/java/com/threerings/jme/model/Model.java @@ -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; +} diff --git a/src/java/com/threerings/jme/model/ModelMesh.java b/src/java/com/threerings/jme/model/ModelMesh.java new file mode 100644 index 000000000..12a5d5e9c --- /dev/null +++ b/src/java/com/threerings/jme/model/ModelMesh.java @@ -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 null). */ + 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; +} diff --git a/src/java/com/threerings/jme/model/ModelNode.java b/src/java/com/threerings/jme/model/ModelNode.java new file mode 100644 index 000000000..56ff69233 --- /dev/null +++ b/src/java/com/threerings/jme/model/ModelNode.java @@ -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; +} diff --git a/src/java/com/threerings/jme/model/ModelSpatial.java b/src/java/com/threerings/jme/model/ModelSpatial.java new file mode 100644 index 000000000..5f8848f09 --- /dev/null +++ b/src/java/com/threerings/jme/model/ModelSpatial.java @@ -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); +} diff --git a/src/java/com/threerings/jme/model/SkinMesh.java b/src/java/com/threerings/jme/model/SkinMesh.java new file mode 100644 index 000000000..44cc6fbe2 --- /dev/null +++ b/src/java/com/threerings/jme/model/SkinMesh.java @@ -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 indices.length * + * bones.length): 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; +} diff --git a/src/java/com/threerings/jme/tools/AnimationDef.java b/src/java/com/threerings/jme/tools/AnimationDef.java new file mode 100644 index 000000000..788143251 --- /dev/null +++ b/src/java/com/threerings/jme/tools/AnimationDef.java @@ -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 transforms = new ArrayList(); + + 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 frames = new ArrayList(); + + public void addFrame (Frame frame) + { + frames.add(frame); + } +} diff --git a/src/java/com/threerings/jme/tools/CompileModelTask.java b/src/java/com/threerings/jme/tools/CompileModelTask.java new file mode 100644 index 000000000..ff213dc50 --- /dev/null +++ b/src/java/com/threerings/jme/tools/CompileModelTask.java @@ -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 _filesets = new ArrayList(); + + /** A parser for the model definitions. */ + protected ModelParser _mparser = new ModelParser(); +} diff --git a/src/java/com/threerings/jme/tools/ModelDef.java b/src/java/com/threerings/jme/tools/ModelDef.java new file mode 100644 index 000000000..ac89c7b6c --- /dev/null +++ b/src/java/com/threerings/jme/tools/ModelDef.java @@ -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 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 vertices = new ArrayList(); + + /** The triangle indices. */ + public ArrayList indices = new ArrayList(); + + /** 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 nodes) + { + super.resolveReferences(nodes); + + // divide the vertices up by weight groups + HashMap, WeightGroupDef> groups = + new HashMap, WeightGroupDef>(); + for (int ii = 0, nn = vertices.size(); ii < nn; ii++) { + SkinVertex svertex = (SkinVertex)vertices.get(ii); + HashSet 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, 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 boneWeights = new ArrayList(); + + public void addBoneWeight (BoneWeight weight) + { + boneWeights.add(weight); + } + + /** Returns the set of names of the bones influencing this vertex. */ + public HashSet getBoneNames () + { + HashSet names = new HashSet(); + 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 indices = new ArrayList(); + + /** The interleaved vertex weights. */ + public ArrayList weights = new ArrayList(); + } + + /** The meshes and bones comprising the model. */ + public ArrayList spatials = new ArrayList(); + + 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 nodes = new HashMap(); + 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 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 list) + { + float[] array = new float[list.size()]; + for (int ii = 0, nn = list.size(); ii < nn; ii++) { + array[ii] = list.get(ii); + } + return array; + } +} diff --git a/src/java/com/threerings/jme/tools/xml/AnimationParser.java b/src/java/com/threerings/jme/tools/xml/AnimationParser.java new file mode 100644 index 000000000..f8f348563 --- /dev/null +++ b/src/java/com/threerings/jme/tools/xml/AnimationParser.java @@ -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; +} diff --git a/src/java/com/threerings/jme/tools/xml/ModelParser.java b/src/java/com/threerings/jme/tools/xml/ModelParser.java new file mode 100644 index 000000000..a73964f17 --- /dev/null +++ b/src/java/com/threerings/jme/tools/xml/ModelParser.java @@ -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; +} diff --git a/src/ms/TRAnimationExporter.mcr b/src/ms/TRAnimationExporter.mcr new file mode 100644 index 000000000..59ba38dc5 --- /dev/null +++ b/src/ms/TRAnimationExporter.mcr @@ -0,0 +1,92 @@ +-- +-- $Id$ +-- +-- 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 + +macroScript TRAnimationExporter category:"File" \ + buttonText:"Export Animation as XML..." toolTip:"Export Animation as XML" ( + + -- Writes a point3-valued attribute to the file + fn writePoint3Attr attr p outFile = + ( + format "%=\"%, %, %\"" attr p.x p.y p.z to:outFile + ) + + -- Writes a quat-valued attribute to the file + fn writeQuatAttr attr q outFile = + ( + format "%=\"%, %, %, %\"" attr q.x q.y q.z q.w to:outFile + ) + + -- Writes a single node transform + fn writeTransform node outFile = ( + format " \n" to:outFile + ) + + -- Writes a single animation frame + fn writeFrame nodes outFile = ( + format " \n" to:outFile + for node in nodes do in coordsys parent ( + writeTransform node outFile + ) + format " \n\n" to:outFile + ) + + -- Writes animation to the named file + fn writeAnimation fileName = + ( + outFile = createfile fileName + format "\n\n" to:outFile + format "\n\n" to:outFile + local nodes + if selection.count > 0 then ( + nodes = selection + ) else ( + nodes = objects + ) + for i = animationRange.start to animationRange.end do at time i ( + writeFrame nodes outFile + ) + format "\n" to:outFile + close outFile + ) + + -- + -- Main entry point + -- + + -- Get the target filename + persistent global xmlAnimFileName + local fileName + if (xmlAnimFileName == undefined) then ( + fileName = maxFilePath + (getFilenameFile maxFileName) + ".xml" + ) else ( + fileName = xmlAnimFileName + ) + fileName = getSaveFileName caption:"Select File to Export" \ + filename:fileName types:"XML Animations (*.XML)|*.xml|All|*.*" + if fileName != undefined do ( + xmlAnimFileName = fileName + writeAnimation fileName + ) +) diff --git a/src/ms/TRModelExporter.mcr b/src/ms/TRModelExporter.mcr new file mode 100644 index 000000000..07cc9b199 --- /dev/null +++ b/src/ms/TRModelExporter.mcr @@ -0,0 +1,172 @@ +-- +-- $Id$ +-- +-- 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 + +macroScript TRModelExporter category:"File" \ + buttonText:"Export Model as XML..." toolTip:"Export Model as XML" ( + + -- Writes a point3-valued attribute to the file + fn writePoint3Attr attr p outFile = + ( + format "%=\"%, %, %\"" attr p.x p.y p.z to:outFile + ) + + -- Writes a quat-valued attribute to the file + fn writeQuatAttr attr q outFile = + ( + format "%=\"%, %, %, %\"" attr q.x q.y q.z q.w to:outFile + ) + + -- Writes a skinned vertex's bone weights to the file + fn writeSkinBoneWeights skin vidx outFile = + ( + weightCount = skinOps.getVertexWeightCount skin vidx + for i = 1 to weightCount do ( + bone = skinOps.getBoneName skin (skinOps.getVertexWeightBoneID \ + skin vidx i) 0 + weight = skinOps.getVertexWeight skin vidx i + format " \n" bone \ + weight to:outFile + ) + ) + + -- Writes a physiqued vertex's bone weights to the file + fn writePhysiqueBoneWeights node vidx outFile = + ( + boneCount = physiqueOps.getVertexBoneCount node vidx + for i = 1 to boneCount do ( + bone = physiqueOps.getVertexBone node vidx i + weight = physiqueOps.getVertexWeight node vidx i + format " \n" bone.name \ + weight to:outFile + ) + ) + + -- Writes a mesh's vertices to the file + fn writeVertices mesh outFile = + ( + for i = 1 to mesh.numfaces do ( + face = getFace mesh i + local tvface + if mesh.numtverts > 0 do ( + tvface = getTVFace mesh i + ) + for i = 1 to 3 do ( + format " \n" to:outFile + if isProperty mesh #skin then ( + writeSkinBoneWeights mesh.skin face[i] outFile + ) else ( + writePhysiqueBoneWeights mesh face[i] outFile + ) + format " \n" to:outFile + ) else ( + format "/>\n" to:outFile + ) + ) + ) + ) + + -- Writes a node to the file + fn writeNode node outFile = + ( + local kind + isMesh = false + if isKindOf node Editable_Mesh then ( + isMesh = true + if isProperty node #skin or isProperty node #physique then ( + kind = "skinMesh" + ) else ( + kind = "triMesh" + ) + ) else if isKindOf node BoneGeometry then ( + kind = "boneNode" + ) else ( + kind = "node" + ) + format " <% name=\"%\"" kind node.name to:outFile + if node.parent != undefined do ( + format " parent=\"%\"" node.parent.name to:outFile + ) + in coordsys parent ( + writePoint3Attr " translation" node.transform.translationPart \ + outFile + writeQuatAttr " rotation" (inverse node.transform.rotationPart) \ + outFile + writePoint3Attr " scale" node.transform.scalePart outFile + ) + if isMesh then ( + format ">\n" to:outFile + if isProperty node #skin do ( + modPanel.setCurrentObject node.skin node:node ui:true + ) + writeVertices node outFile + format " \n\n" kind to:outFile + + ) else ( + format "\>\n\n" to:outFile + ) + ) + + -- Writes the model to the named file + fn writeModel fileName = + ( + outFile = createfile fileName + format "\n\n" to:outFile + format "\n\n" to:outFile + local nodes + if selection.count > 0 then ( + nodes = selection + ) else ( + nodes = objects + ) + for node in nodes do ( + writeNode node outFile + ) + format "\n" to:outFile + close outFile + ) + + -- + -- Main entry point + -- + + -- Get the target filename + persistent global xmlModelFileName + local fileName + if (xmlModelFileName == undefined) then ( + fileName = maxFilePath + (getFilenameFile maxFileName) + ".xml" + ) else ( + fileName = xmlModelFileName + ) + fileName = getSaveFileName caption:"Select File to Export" \ + filename:fileName types:"XML Models (*.XML)|*.xml|All|*.*" + if fileName != undefined do ( + xmlModelFileName = fileName + writeModel fileName + ) +)