Java tutorial
/******************************************************************************* * Copyright 2011 See AUTHORS file. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.badlogic.gdx.graphics.g3d.loaders.ogre; import java.io.InputStream; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBException; import javax.xml.bind.Unmarshaller; import com.badlogic.gdx.files.FileHandle; import com.badlogic.gdx.graphics.Color; import com.badlogic.gdx.graphics.GL10; import com.badlogic.gdx.graphics.VertexAttribute; import com.badlogic.gdx.graphics.VertexAttributes; import com.badlogic.gdx.graphics.VertexAttributes.Usage; import com.badlogic.gdx.graphics.g3d.loaders.ogre.mesh.BaseGeometry; import com.badlogic.gdx.graphics.g3d.loaders.ogre.mesh.Boneassignments; import com.badlogic.gdx.graphics.g3d.loaders.ogre.mesh.ColourDiffuse; import com.badlogic.gdx.graphics.g3d.loaders.ogre.mesh.Face; import com.badlogic.gdx.graphics.g3d.loaders.ogre.mesh.Geometry; import com.badlogic.gdx.graphics.g3d.loaders.ogre.mesh.Mesh; import com.badlogic.gdx.graphics.g3d.loaders.ogre.mesh.Submesh; import com.badlogic.gdx.graphics.g3d.loaders.ogre.mesh.Submeshname; import com.badlogic.gdx.graphics.g3d.loaders.ogre.mesh.Texcoord; import com.badlogic.gdx.graphics.g3d.loaders.ogre.mesh.Vertex; import com.badlogic.gdx.graphics.g3d.loaders.ogre.mesh.Vertexboneassignment; import com.badlogic.gdx.graphics.g3d.loaders.ogre.mesh.Vertexbuffer; import com.badlogic.gdx.graphics.g3d.loaders.ogre.skeleton.Animation; import com.badlogic.gdx.graphics.g3d.loaders.ogre.skeleton.Bone; import com.badlogic.gdx.graphics.g3d.loaders.ogre.skeleton.Boneparent; import com.badlogic.gdx.graphics.g3d.loaders.ogre.skeleton.Keyframe; import com.badlogic.gdx.graphics.g3d.loaders.ogre.skeleton.Track; import com.badlogic.gdx.graphics.g3d.model.SubMesh; import com.badlogic.gdx.graphics.g3d.model.skeleton.Skeleton; import com.badlogic.gdx.graphics.g3d.model.skeleton.SkeletonAnimation; import com.badlogic.gdx.graphics.g3d.model.skeleton.SkeletonJoint; import com.badlogic.gdx.graphics.g3d.model.skeleton.SkeletonKeyframe; import com.badlogic.gdx.graphics.g3d.model.skeleton.SkeletonModel; import com.badlogic.gdx.graphics.g3d.model.skeleton.SkeletonSubMesh; import com.badlogic.gdx.graphics.g3d.model.still.StillModel; import com.badlogic.gdx.graphics.g3d.model.still.StillSubMesh; import com.badlogic.gdx.graphics.glutils.ShaderProgram; import com.badlogic.gdx.math.MathUtils; import com.badlogic.gdx.math.Matrix4; import com.badlogic.gdx.utils.Array; import com.badlogic.gdx.utils.FloatArray; import com.badlogic.gdx.utils.GdxRuntimeException; import com.badlogic.gdx.utils.IntArray; public class OgreXmlLoader { public SubMesh[] loadMeshes(FileHandle file) { InputStream in = null; try { in = file.read(); return loadMesh(in); } catch (Throwable t) { throw new GdxRuntimeException("Couldn't load file '" + file.name() + "'", t); } finally { if (in != null) try { in.close(); } catch (Exception e) { } } } public SubMesh[] loadMesh(InputStream in) { try { Mesh ogreMesh = loadOgreMesh(in); SubMesh[] meshes = generateSubMeshes(ogreMesh); return meshes; } catch (Throwable t) { throw new GdxRuntimeException("Couldn't load meshes", t); } } public SkeletonModel load(FileHandle mesh, FileHandle skeleton) { SubMesh[] meshes = loadMeshes(mesh); return new SkeletonModel(loadSkeleton(skeleton), meshes); } public StillModel load(FileHandle mesh) { SubMesh[] meshes = loadMeshes(mesh); return new StillModel(meshes); } private SubMesh[] generateSubMeshes(Mesh ogreMesh) { List<Submesh> ogreSubmeshes = ogreMesh.getSubmeshes().getSubmesh(); SubMesh[] submeshes = new SubMesh[ogreSubmeshes.size()]; for (int i = 0; i < ogreSubmeshes.size(); i++) { Submesh ogreSubmesh = ogreSubmeshes.get(i); boolean usesTriangleList = false; if (ogreSubmesh.use32Bitindexes) throw new GdxRuntimeException("submesh '" + i + "' uses 32-bit indices"); if (ogreSubmesh.getOperationtype().equals("triangle_list")) { usesTriangleList = true; } short[] indices = new short[ogreSubmesh.getFaces().count * (usesTriangleList ? 3 : 1)]; for (int j = 0, idx = 0; j < ogreSubmesh.getFaces().count; j++) { Face face = ogreSubmesh.getFaces().getFace().get(j); indices[idx++] = face.v1; if (usesTriangleList || j == 0) { indices[idx++] = face.v2; indices[idx++] = face.v3; } } List<VertexAttribute> attributes = new ArrayList<VertexAttribute>(); IntArray offsets = new IntArray(); int offset = 0; BaseGeometry geom; if (ogreSubmesh.useSharedVertices) { geom = ogreMesh.getSharedgeometry(); } else { geom = ogreSubmesh.getGeometry(); } for (int j = 0; j < geom.getVertexbuffer().size(); j++) { Vertexbuffer buffer = geom.getVertexbuffer().get(j); offsets.add(offset); if (buffer.positions) { attributes.add(new VertexAttribute(Usage.Position, 3, ShaderProgram.POSITION_ATTRIBUTE)); offset += 3; } if (buffer.normals) { attributes.add(new VertexAttribute(Usage.Normal, 3, ShaderProgram.NORMAL_ATTRIBUTE)); offset += 3; } if (buffer.tangents) { attributes.add(new VertexAttribute(Usage.Generic, buffer.tangentDimensions, ShaderProgram.TANGENT_ATTRIBUTE)); offset += buffer.tangentDimensions; } if (buffer.binormals) { attributes.add(new VertexAttribute(Usage.Generic, 3, ShaderProgram.BINORMAL_ATTRIBUTE)); offset += 3; } if (buffer.coloursDiffuse) { attributes.add(new VertexAttribute(Usage.ColorPacked, 4, ShaderProgram.COLOR_ATTRIBUTE)); offset += 4; } for (int k = 0; k < buffer.textureCoords; k++) { try { int numTexCoords = 0; switch (k) { case 0: numTexCoords = Integer.valueOf(buffer.getTextureCoordDimensions0()); break; case 1: numTexCoords = Integer.valueOf(buffer.getTextureCoordDimensions1()); break; case 2: numTexCoords = Integer.valueOf(buffer.getTextureCoordDimensions2()); break; case 3: numTexCoords = Integer.valueOf(buffer.getTextureCoordDimensions3()); break; case 4: numTexCoords = Integer.valueOf(buffer.getTextureCoordDimensions4()); break; case 5: numTexCoords = Integer.valueOf(buffer.getTextureCoordDimensions5()); break; case 6: numTexCoords = Integer.valueOf(buffer.getTextureCoordDimensions6()); break; case 7: numTexCoords = Integer.valueOf(buffer.getTextureCoordDimensions7()); break; } attributes.add(new VertexAttribute(Usage.TextureCoordinates, numTexCoords, ShaderProgram.TEXCOORD_ATTRIBUTE + k)); offset += numTexCoords; } catch (NumberFormatException e) { throw new GdxRuntimeException( "Can't process texture coords with dimensions != 1, 2, 3, 4 (e.g. float1)"); } } } VertexAttributes attribs = new VertexAttributes(attributes.toArray(new VertexAttribute[0])); int vertexSize = offset; float[] vertices = new float[geom.getVertexCount() * offset]; for (int j = 0; j < geom.getVertexbuffer().size(); j++) { Vertexbuffer buffer = geom.getVertexbuffer().get(j); offset = offsets.get(j); int idx = offset; for (int k = 0; k < buffer.getVertex().size(); k++) { Vertex v = buffer.getVertex().get(k); if (v.getPosition() != null) { vertices[idx++] = v.getPosition().x; vertices[idx++] = v.getPosition().y; vertices[idx++] = v.getPosition().z; } if (v.getNormal() != null) { vertices[idx++] = v.getNormal().x; vertices[idx++] = v.getNormal().y; vertices[idx++] = v.getNormal().z; } if (v.getTangent() != null) { vertices[idx++] = v.getTangent().x; vertices[idx++] = v.getTangent().y; vertices[idx++] = v.getTangent().z; if (buffer.tangentDimensions == 4) vertices[idx++] = v.getTangent().w; } if (v.getBinormal() != null) { vertices[idx++] = v.getBinormal().x; vertices[idx++] = v.getBinormal().y; vertices[idx++] = v.getBinormal().z; } if (v.getColourDiffuse() != null) { float color = getColor(v.getColourDiffuse()); vertices[idx++] = color; } if (v.getTexcoord() != null) { for (int l = 0; l < v.getTexcoord().size(); l++) { Texcoord texCoord = v.getTexcoord().get(l); int numTexCoords = 0; switch (l) { case 0: numTexCoords = Integer.valueOf(buffer.getTextureCoordDimensions0()); break; case 1: numTexCoords = Integer.valueOf(buffer.getTextureCoordDimensions1()); break; case 2: numTexCoords = Integer.valueOf(buffer.getTextureCoordDimensions2()); break; case 3: numTexCoords = Integer.valueOf(buffer.getTextureCoordDimensions3()); break; case 4: numTexCoords = Integer.valueOf(buffer.getTextureCoordDimensions4()); break; case 5: numTexCoords = Integer.valueOf(buffer.getTextureCoordDimensions5()); break; case 6: numTexCoords = Integer.valueOf(buffer.getTextureCoordDimensions6()); break; case 7: numTexCoords = Integer.valueOf(buffer.getTextureCoordDimensions7()); break; } if (numTexCoords == 1) { vertices[idx++] = texCoord.u; } if (numTexCoords == 2) { vertices[idx++] = texCoord.u; vertices[idx++] = texCoord.v; } if (numTexCoords == 3) { vertices[idx++] = texCoord.u; vertices[idx++] = texCoord.v; vertices[idx++] = texCoord.w; } if (numTexCoords == 4) { vertices[idx++] = texCoord.u; vertices[idx++] = texCoord.v; vertices[idx++] = texCoord.w; vertices[idx++] = texCoord.x; } } } offset += vertexSize; idx = offset; } } com.badlogic.gdx.graphics.Mesh mesh = new com.badlogic.gdx.graphics.Mesh(false, vertices.length / vertexSize, indices.length, attribs); mesh.setIndices(indices); mesh.setVertices(vertices); String meshName = ""; if (ogreMesh.getSubmeshnames() != null) { List<Submeshname> names = ogreMesh.getSubmeshnames().getSubmeshname(); for (int n = 0; n < names.size(); ++n) { if (Integer.parseInt(names.get(n).getIndex()) == i) meshName = names.get(n).getName(); } } SubMesh subMesh; Boneassignments boneAssigments = (ogreSubmesh.getBoneassignments() != null) ? ogreSubmesh.getBoneassignments() : ogreMesh.getBoneassignments(); if (boneAssigments != null) { subMesh = new SkeletonSubMesh(meshName, mesh, GL10.GL_TRIANGLES); } else { subMesh = new StillSubMesh(meshName, mesh, GL10.GL_TRIANGLES); } // FIXME ? subMesh.materialName = ogreSubmesh.material; if (boneAssigments != null) { SkeletonSubMesh subSkelMesh = (SkeletonSubMesh) subMesh; subSkelMesh.setVertices(vertices); subSkelMesh.setIndices(indices); subSkelMesh.skinnedVertices = new float[vertices.length]; System.arraycopy(subSkelMesh.vertices, 0, subSkelMesh.skinnedVertices, 0, subSkelMesh.vertices.length); loadBones(boneAssigments, subSkelMesh); } if (ogreSubmesh.getOperationtype().equals("triangle_list")) subMesh.primitiveType = GL10.GL_TRIANGLES; if (ogreSubmesh.getOperationtype().equals("triangle_fan")) subMesh.primitiveType = GL10.GL_TRIANGLE_FAN; if (ogreSubmesh.getOperationtype().equals("triangle_strip")) subMesh.primitiveType = GL10.GL_TRIANGLE_STRIP; submeshes[i] = subMesh; } return submeshes; } private void loadBones(Boneassignments boneAssigments, SkeletonSubMesh subMesh) { Array<IntArray> boneAssignments = new Array<IntArray>(); Array<FloatArray> boneWeights = new Array<FloatArray>(); for (int j = 0; j < subMesh.getMesh().getNumVertices(); j++) { boneAssignments.add(new IntArray(4)); boneWeights.add(new FloatArray(4)); } List<Vertexboneassignment> vertexboneassignment = boneAssigments.getVertexboneassignment(); for (int j = 0; j < vertexboneassignment.size(); j++) { Vertexboneassignment assignment = vertexboneassignment.get(j); int boneIndex = assignment.boneindex; int vertexIndex = assignment.vertexindex; float weight = assignment.weight; boneAssignments.get(vertexIndex).add(boneIndex); boneWeights.get(vertexIndex).add(weight); } subMesh.boneAssignments = new int[boneAssignments.size][]; subMesh.boneWeights = new float[boneWeights.size][]; for (int j = 0; j < boneAssignments.size; j++) { subMesh.boneAssignments[j] = new int[boneAssignments.get(j).size]; subMesh.boneWeights[j] = new float[boneWeights.get(j).size]; for (int k = 0; k < boneAssignments.get(j).size; k++) { subMesh.boneAssignments[j][k] = boneAssignments.get(j).get(k); } for (int k = 0; k < boneWeights.get(j).size; k++) { subMesh.boneWeights[j][k] = boneWeights.get(j).get(k); } } } Color color = new Color(); private float getColor(ColourDiffuse colourDiffuse) { String[] tokens = colourDiffuse.getValue().split(" "); if (tokens.length == 3) color.set(Float.valueOf(tokens[0]), Float.valueOf(tokens[1]), Float.valueOf(tokens[2]), 1); else color.set(Float.valueOf(tokens[0]), Float.valueOf(tokens[1]), Float.valueOf(tokens[2]), Float.valueOf(tokens[3])); return color.toFloatBits(); } private Mesh loadOgreMesh(InputStream in) throws JAXBException { JAXBContext context = JAXBContext.newInstance(Mesh.class); Unmarshaller unmarshaller = context.createUnmarshaller(); long start = System.nanoTime(); Mesh mesh = (Mesh) unmarshaller.unmarshal(in); System.out.println("took: " + (System.nanoTime() - start) / 1000000000.0f); return mesh; } public Skeleton loadSkeleton(FileHandle file) { InputStream in = null; try { in = file.read(); return loadSkeleton(in); } catch (Throwable t) { throw new GdxRuntimeException("Couldn't load file '" + file.name() + "'", t); } finally { if (in != null) try { in.close(); } catch (Exception e) { } } } public Skeleton loadSkeleton(InputStream in) { try { com.badlogic.gdx.graphics.g3d.loaders.ogre.skeleton.Skeleton ogreSkel = loadOgreSkeleton(in); return generateSkeleton(ogreSkel); } catch (Throwable t) { throw new GdxRuntimeException("Couldn't load model", t); } } private Skeleton generateSkeleton(com.badlogic.gdx.graphics.g3d.loaders.ogre.skeleton.Skeleton ogreSkel) { List<Bone> bones = ogreSkel.getBones().getBone(); List<SkeletonJoint> joints = new ArrayList<SkeletonJoint>(); Map<String, SkeletonJoint> nameToJoint = new HashMap<String, SkeletonJoint>(); for (int i = 0; i < bones.size(); i++) { Bone bone = bones.get(i); SkeletonJoint joint = new SkeletonJoint(); joint.name = bone.name; joint.position.set(bone.position.x, bone.position.y, bone.position.z); joint.rotation.setFromAxis(bone.rotation.axis.x, bone.rotation.axis.y, bone.rotation.axis.z, MathUtils.radiansToDegrees * bone.rotation.angle); if (bone.scale != null) { if (bone.scale.factor == 0) joint.scale.set(bone.scale.x, bone.scale.y, bone.scale.z); else joint.scale.set(bone.scale.factor, bone.scale.factor, bone.scale.factor); } joints.add(joint); nameToJoint.put(joint.name, joint); } List<Boneparent> hierarchy = ogreSkel.getBonehierarchy().getBoneparent(); for (int i = 0; i < hierarchy.size(); i++) { Boneparent link = hierarchy.get(i); SkeletonJoint joint = nameToJoint.get(link.getBone()); SkeletonJoint parent = nameToJoint.get(link.getParent()); parent.children.add(joint); joint.parent = parent; } Skeleton skel = new Skeleton(); for (int i = 0; i < joints.size(); i++) { SkeletonJoint joint = joints.get(i); if (joint.parent == null) skel.hierarchy.add(joint); } skel.buildFromHierarchy(); List<Animation> animations = ogreSkel.getAnimations().getAnimation(); for (int i = 0; i < animations.size(); i++) { Animation animation = animations.get(i); SkeletonKeyframe[][] perJointkeyFrames = new SkeletonKeyframe[skel.bindPoseJoints.size][]; List<Track> tracks = animation.getTracks().getTrack(); if (tracks.size() != perJointkeyFrames.length) throw new IllegalArgumentException("Number of tracks does not equal number of joints"); Matrix4 rotation = new Matrix4(); Matrix4 transform = new Matrix4(); for (int j = 0; j < tracks.size(); j++) { Track track = tracks.get(j); String jointName = track.getBone(); int jointIndex = skel.namesToIndices.get(jointName); if (perJointkeyFrames[jointIndex] != null) throw new IllegalArgumentException("Track for bone " + jointName + " in animation " + animation.name + " already defined!"); SkeletonKeyframe[] jointKeyFrames = new SkeletonKeyframe[track.getKeyframes().getKeyframe().size()]; perJointkeyFrames[jointIndex] = jointKeyFrames; for (int k = 0; k < track.getKeyframes().getKeyframe().size(); k++) { Keyframe keyFrame = track.getKeyframes().getKeyframe().get(k); SkeletonKeyframe jointKeyframe = new SkeletonKeyframe(); jointKeyframe.timeStamp = keyFrame.time; jointKeyframe.position.set(keyFrame.translate.x, keyFrame.translate.y, keyFrame.translate.z); if (keyFrame.scale != null) { if (keyFrame.scale.factor == 0) jointKeyframe.scale.set(keyFrame.scale.x, keyFrame.scale.y, keyFrame.scale.z); else jointKeyframe.scale.set(keyFrame.scale.factor, keyFrame.scale.factor, keyFrame.scale.factor); } jointKeyframe.rotation.setFromAxis(keyFrame.rotate.axis.x, keyFrame.rotate.axis.y, keyFrame.rotate.axis.z, MathUtils.radiansToDegrees * keyFrame.rotate.angle).nor(); jointKeyframe.parentIndex = skel.bindPoseJoints.get(jointIndex).parentIndex; jointKeyFrames[k] = jointKeyframe; rotation.set(jointKeyframe.rotation); rotation.trn(jointKeyframe.position); transform.set(skel.sceneMatrices.get(jointIndex)); transform.mul(rotation); if (jointKeyframe.parentIndex != -1) { rotation.set(skel.offsetMatrices.get(jointKeyframe.parentIndex)).mul(transform); transform.set(rotation); } transform.getTranslation(jointKeyframe.position); transform.getRotation(jointKeyframe.rotation); } } for (int j = 0; j < perJointkeyFrames.length; j++) { if (perJointkeyFrames[j] == null) throw new IllegalArgumentException("No track for bone " + skel.jointNames.get(j)); } skel.animations.put(animation.name, new SkeletonAnimation(animation.name, animation.length, perJointkeyFrames)); } return skel; } private com.badlogic.gdx.graphics.g3d.loaders.ogre.skeleton.Skeleton loadOgreSkeleton(InputStream in) throws JAXBException { JAXBContext context = JAXBContext .newInstance(com.badlogic.gdx.graphics.g3d.loaders.ogre.skeleton.Skeleton.class); Unmarshaller unmarshaller = context.createUnmarshaller(); long start = System.nanoTime(); com.badlogic.gdx.graphics.g3d.loaders.ogre.skeleton.Skeleton skel = (com.badlogic.gdx.graphics.g3d.loaders.ogre.skeleton.Skeleton) unmarshaller .unmarshal(in); System.out.println("took: " + (System.nanoTime() - start) / 1000000000.0f); return skel; } }