📄 md2tojme.java
字号:
/*
* Copyright (c) 2003-2009 jMonkeyEngine
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.jmex.model.converters;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.logging.Logger;
import com.jme.math.Vector2f;
import com.jme.math.Vector3f;
import com.jme.scene.Controller;
import com.jme.scene.Node;
import com.jme.scene.TexCoords;
import com.jme.scene.TriMesh;
import com.jme.system.DisplaySystem;
import com.jme.system.JmeException;
import com.jme.system.dummy.DummySystemProvider;
import com.jme.util.BinaryFileReader;
import com.jme.util.export.binary.BinaryExporter;
import com.jme.util.geom.BufferUtils;
import com.jmex.model.animation.KeyframeController;
/**
* Started Date: Jun 14, 2004<br>
* <br>
* This class converts a .md2 file to jME's binary format.
*
* @author Jack Lindamood
*/
public class Md2ToJme extends FormatConverter {
private static final Logger logger = Logger.getLogger(Md2ToJme.class
.getName());
/**
* Converts an Md2 file to jME format. The syntax is: "Md2ToJme drfreak.md2
* outfile.jme".
*
* @param args
* The array of parameters
*/
public static void main(String[] args) {
DisplaySystem.getDisplaySystem(DummySystemProvider.DUMMY_SYSTEM_IDENTIFIER);
new Md2ToJme().attemptFileConvert(args);
}
/**
* It creates a node from a .md2 stream and then writes that node to the
* given OutputStream in jME's binary format.
*
* @param Md2Stream
* A stream representing the .md2 file
* @param o
* The stream to write it's binary equivalent to
* @throws java.io.IOException
* If anything funky goes wrong with reading information
*/
public void convert(InputStream Md2Stream, OutputStream o)
throws IOException {
if (Md2Stream == null)
throw new NullPointerException("Unable to load null streams");
Md2ConverterCopy mcc = new Md2ConverterCopy(Md2Stream);
Node newnode = new Node(mcc.mesh.getName());
newnode.attachChild(mcc.mesh);
BinaryExporter.getInstance().save(newnode, o);
}
/**
* 95% a Copy/paste of the .md2 loader by Mark Powell modifyed for
* efficiency (use of empty TriMesh) and VertexController as well as a few
* tiny adjustments here and there on memory.
*
* @author Mark Powell
* @author Jack Lindamood
*/
private static class Md2ConverterCopy {
private static final long serialVersionUID = 1L;
private BinaryFileReader bis = null;
private Header header;
private Vector2f[] texCoords;
private Md2Face[] triangles;
private Md2Frame[] frames;
// holds each keyframe.
private TriMesh[] triMesh;
// controller responsible for handling keyframe morphing.
private KeyframeController controller;
public TriMesh mesh;
/**
* Loads an MD2 model. The corresponding <code>TriMesh</code> objects
* are created and attached to the model. Each keyframe is then loaded
* and assigned to a <code>KeyframeController</code>. MD2 does not
* keep track of it's own texture or material settings, so the user is
* responsible for setting these.
*
* @param input
* the InputStream of the file to load.
*/
public Md2ConverterCopy(InputStream input) {
if (null == input) {
throw new JmeException("Null data. Cannot load.");
}
mesh = new TriMesh("MD2 mesh" + new Random().nextInt());
bis = new BinaryFileReader(input);
header = new Header();
if (header.version != 8) {
throw new JmeException("Invalid file format (Version not 8)!");
}
parseMesh();
convertDataStructures();
triangles = null;
texCoords = null;
frames = null;
}
/**
* <code>getAnimationController</code> returns the animation
* controller used for MD2 animation (VertexKeyframeController).
*
* @return
* @see com.jmex.model.Model#getAnimationController()
*/
public Controller getAnimationController() {
return controller;
}
/**
* <code>parseMesh</code> reads the MD2 file and builds the necessary
* data structures. These structures are specific to MD2 and therefore
* require later conversion to jME data structures.
*/
private void parseMesh() {
String[] skins = new String[header.numSkins];
texCoords = new Vector2f[header.numTexCoords];
triangles = new Md2Face[header.numTriangles];
frames = new Md2Frame[header.numFrames];
// start with skins. Move the file pointer to the correct position.
bis.setOffset(header.offsetSkins);
// Read in each skin for this model
for (int j = 0; j < header.numSkins; j++) {
skins[j] = bis.readString(64);
}
// Now read in texture coordinates.
bis.setOffset(header.offsetTexCoords);
for (int j = 0; j < header.numTexCoords; j++) {
texCoords[j] = new Vector2f();
texCoords[j].x = (float) bis.readShort();
texCoords[j].y = (float) bis.readShort();
}
// read the vertex data.
bis.setOffset(header.offsetTriangles);
for (int j = 0; j < header.numTriangles; j++) {
triangles[j] = new Md2Face();
}
bis.setOffset(header.offsetFrames);
// Each keyframe has the same type of data, so read each
// keyframe one at a time.
for (int i = 0; i < header.numFrames; i++) {
VectorKeyframe frame = new VectorKeyframe();
frames[i] = new Md2Frame();
frames[i].vertices = new Triangle[header.numVertices];
Vector3f[] aliasVertices = new Vector3f[header.numVertices];
int[] aliasLightNormals = new int[header.numVertices];
// Read in the first frame of animation
for (int j = 0; j < header.numVertices; j++) {
aliasVertices[j] = new Vector3f(bis.readByte(), bis
.readByte(), bis.readByte());
aliasLightNormals[j] = bis.readByte();
}
// Copy the name of the animation to our frames array
frames[i].name = frame.name;
Triangle[] verices = frames[i].vertices;
for (int j = 0; j < header.numVertices; j++) {
verices[j] = new Triangle();
verices[j].vertex.x = aliasVertices[j].x * frame.scale.x
+ frame.translate.x;
verices[j].vertex.z = -1
* (aliasVertices[j].y * frame.scale.y + frame.translate.y);
verices[j].vertex.y = aliasVertices[j].z * frame.scale.z
+ frame.translate.z;
if (aliasLightNormals[j] < norms.length) {
verices[j].normal.x = norms[aliasLightNormals[j]][0];
verices[j].normal.y = norms[aliasLightNormals[j]][2];
verices[j].normal.z = -norms[aliasLightNormals[j]][1];
} else {
verices[j].normal.set(0, 1, 0); // DEFAULT?
logger.warning("Referenced an invalid normal: "+aliasLightNormals[j]);
}
}
}
// TODO: Read OPENGL commands here...
bis.setOffset(header.offsetGlCommands);
}
private static class VectorTex {
private VectorTex(final int vIndex, final int tIndex) {
v = vIndex;
t = tIndex;
}
public boolean equals(final Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
final VectorTex that = (VectorTex) o;
if (t != that.t)
return false;
if (v != that.v)
return false;
return true;
}
public int hashCode() {
int l_result;
l_result = v;
l_result = 31 * l_result + t;
return l_result;
}
int v;
int t;
public boolean matches(final Md2Face face, final int faceVert) {
return face.vertexIndices[faceVert] == v
&& face.textureIndices[faceVert] == t;
}
}
/**
* <code>convertDataStructures</code> takes the loaded MD2 data and
* converts it into jME data.
*/
private void convertDataStructures() {
triMesh = new TriMesh[header.numFrames];
List<VectorTex> vectorTexcoords = new ArrayList<VectorTex>();
controller = new KeyframeController();
for (int i = 0; i < header.numFrames; i++) {
int numOfVerts = header.numVertices;
int numTexVertex = header.numTexCoords;
if (i != 0)
triMesh[i] = new TriMesh();
else
triMesh[i] = mesh;
Vector3f[] uniqueVerts = new Vector3f[numOfVerts];
Vector3f[] uniqueNorms = new Vector3f[numOfVerts];
Vector2f[] texVerts = new Vector2f[numTexVertex];
// assign a vector array for the trimesh.
for (int j = 0; j < numOfVerts; j++) {
if (i != 0) {
uniqueVerts[j] = frames[i].vertices[j].vertex;
uniqueNorms[j] = frames[i].vertices[j].normal;
} else {
uniqueVerts[j] = new Vector3f(
frames[i].vertices[j].vertex);
uniqueNorms[j] = new Vector3f(
frames[i].vertices[j].normal);
}
}
if (i == 0) {
// texture coordinates.
for (int j = 0; j < numTexVertex; j++) {
texVerts[j] = new Vector2f();
texVerts[j].x = texCoords[j].x / (header.skinWidth);
texVerts[j].y = 1 - texCoords[j].y
/ (header.skinHeight);
}
// collect all used combinations of vertices and texcoords
if (numTexVertex != 0) {
for (int j = 0; j < header.numTriangles; j++) {
for (int k = 0; k < 3; k++) {
final VectorTex l_tex = new VectorTex(
triangles[j].vertexIndices[k],
triangles[j].textureIndices[k]);
if (!vectorTexcoords.contains(l_tex)) {
vectorTexcoords.add(l_tex);
}
}
}
}
// build indices
List<Integer> indices = new ArrayList<Integer>();
for (int j = 0; j < header.numTriangles; j++) {
for (int k = 0; k < 3; k++) {
for (int i1 = 0; i1 < vectorTexcoords.size(); i1++) {
VectorTex vectorTexcoord = vectorTexcoords
.get(i1);
if (vectorTexcoord.matches(triangles[j], k)) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -