aboutsummaryrefslogtreecommitdiff
path: root/engine/src/ogre
diff options
context:
space:
mode:
authorScott Barta <sbarta@google.com>2012-03-01 12:35:35 -0800
committerScott Barta <sbarta@google.com>2012-03-01 12:40:08 -0800
commit59b2e6871c65f58fdad78cd7229c292f6a177578 (patch)
tree2d4e7bfc05b93f40b34675d77e403dd1c25efafd /engine/src/ogre
parentf9b30489e75ac1eabc365064959804e99534f7ab (diff)
downloadjmonkeyengine-59b2e6871c65f58fdad78cd7229c292f6a177578.tar.gz
Adds the jMonkeyEngine library to the build.
Adds the jMonkeyEngine open source 3D game engine to the build. This is built as a static library and is only used by the Finsky client. Change-Id: I06a3f054df7b8a67757267d884854f70c5a16ca0
Diffstat (limited to 'engine/src/ogre')
-rw-r--r--engine/src/ogre/com/jme3/scene/plugins/ogre/AnimData.java48
-rw-r--r--engine/src/ogre/com/jme3/scene/plugins/ogre/MaterialLoader.java473
-rw-r--r--engine/src/ogre/com/jme3/scene/plugins/ogre/MeshAnimationLoader.java239
-rw-r--r--engine/src/ogre/com/jme3/scene/plugins/ogre/MeshLoader.java892
-rw-r--r--engine/src/ogre/com/jme3/scene/plugins/ogre/OgreMeshKey.java85
-rw-r--r--engine/src/ogre/com/jme3/scene/plugins/ogre/SceneLoader.java468
-rw-r--r--engine/src/ogre/com/jme3/scene/plugins/ogre/SkeletonLoader.java302
-rw-r--r--engine/src/ogre/com/jme3/scene/plugins/ogre/matext/MaterialExtension.java86
-rw-r--r--engine/src/ogre/com/jme3/scene/plugins/ogre/matext/MaterialExtensionLoader.java138
-rw-r--r--engine/src/ogre/com/jme3/scene/plugins/ogre/matext/MaterialExtensionSet.java85
-rw-r--r--engine/src/ogre/com/jme3/scene/plugins/ogre/matext/OgreMaterialKey.java73
-rw-r--r--engine/src/ogre/com/jme3/scene/plugins/ogre/matext/package.html40
12 files changed, 2929 insertions, 0 deletions
diff --git a/engine/src/ogre/com/jme3/scene/plugins/ogre/AnimData.java b/engine/src/ogre/com/jme3/scene/plugins/ogre/AnimData.java
new file mode 100644
index 0000000..f8d99aa
--- /dev/null
+++ b/engine/src/ogre/com/jme3/scene/plugins/ogre/AnimData.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2009-2010 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.jme3.scene.plugins.ogre;
+
+import com.jme3.animation.Animation;
+import com.jme3.animation.Skeleton;
+import java.util.ArrayList;
+
+public class AnimData {
+
+ public final Skeleton skeleton;
+ public final ArrayList<Animation> anims;
+
+ public AnimData(Skeleton skeleton, ArrayList<Animation> anims) {
+ this.skeleton = skeleton;
+ this.anims = anims;
+ }
+}
diff --git a/engine/src/ogre/com/jme3/scene/plugins/ogre/MaterialLoader.java b/engine/src/ogre/com/jme3/scene/plugins/ogre/MaterialLoader.java
new file mode 100644
index 0000000..887ba5e
--- /dev/null
+++ b/engine/src/ogre/com/jme3/scene/plugins/ogre/MaterialLoader.java
@@ -0,0 +1,473 @@
+/*
+ * Copyright (c) 2009-2010 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.jme3.scene.plugins.ogre;
+
+import com.jme3.asset.*;
+import com.jme3.material.Material;
+import com.jme3.material.MaterialList;
+import com.jme3.material.RenderState;
+import com.jme3.math.ColorRGBA;
+import com.jme3.scene.plugins.ogre.matext.MaterialExtensionLoader;
+import com.jme3.scene.plugins.ogre.matext.MaterialExtensionSet;
+import com.jme3.scene.plugins.ogre.matext.OgreMaterialKey;
+import com.jme3.texture.Texture;
+import com.jme3.texture.Texture.WrapMode;
+import com.jme3.texture.Texture2D;
+import com.jme3.util.PlaceholderAssets;
+import com.jme3.util.blockparser.BlockLanguageParser;
+import com.jme3.util.blockparser.Statement;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Scanner;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+public class MaterialLoader implements AssetLoader {
+
+ private static final Logger logger = Logger.getLogger(MaterialLoader.class.getName());
+
+ private String folderName;
+ private AssetManager assetManager;
+ private ColorRGBA ambient, diffuse, specular, emissive;
+ private Texture[] textures = new Texture[4];
+ private String texName;
+ private String matName;
+ private float shinines;
+ private boolean vcolor = false;
+ private boolean blend = false;
+ private boolean twoSide = false;
+ private boolean noLight = false;
+ private boolean separateTexCoord = false;
+ private int texUnit = 0;
+
+ private ColorRGBA readColor(String content){
+ String[] split = content.split("\\s");
+
+ ColorRGBA color = new ColorRGBA();
+ color.r = Float.parseFloat(split[0]);
+ color.g = Float.parseFloat(split[1]);
+ color.b = Float.parseFloat(split[2]);
+ if (split.length >= 4){
+ color.a = Float.parseFloat(split[3]);
+ }
+ return color;
+ }
+
+ private void readTextureImage(String content){
+ // texture image def
+ String path = null;
+
+ // find extension
+ int extStart = content.lastIndexOf(".");
+ for (int i = extStart; i < content.length(); i++){
+ char c = content.charAt(i);
+ if (Character.isWhitespace(c)){
+ // extension ends here
+ path = content.substring(0, i).trim();
+ content = content.substring(i+1).trim();
+ break;
+ }
+ }
+ if (path == null){
+ path = content.trim();
+ content = "";
+ }
+
+ Scanner lnScan = new Scanner(content);
+ String mips = null;
+ String type = null;
+ if (lnScan.hasNext()){
+ // more params
+ type = lnScan.next();
+// if (!lnScan.hasNext("\n") && lnScan.hasNext()){
+// mips = lnScan.next();
+// if (lnScan.hasNext()){
+ // even more params..
+ // will have to ignore
+// }
+// }
+ }
+
+ boolean genMips = true;
+ boolean cubic = false;
+ if (type != null && type.equals("0"))
+ genMips = false;
+
+ if (type != null && type.equals("cubic")){
+ cubic = true;
+ }
+
+ TextureKey texKey = new TextureKey(folderName + path, false);
+ texKey.setGenerateMips(genMips);
+ texKey.setAsCube(cubic);
+
+ try {
+ Texture loadedTexture = assetManager.loadTexture(texKey);
+
+ textures[texUnit].setImage(loadedTexture.getImage());
+ textures[texUnit].setMinFilter(loadedTexture.getMinFilter());
+ textures[texUnit].setKey(loadedTexture.getKey());
+
+ // XXX: Is this really neccessary?
+ textures[texUnit].setWrap(WrapMode.Repeat);
+ if (texName != null){
+ textures[texUnit].setName(texName);
+ texName = null;
+ }else{
+ textures[texUnit].setName(texKey.getName());
+ }
+ } catch (AssetNotFoundException ex){
+ logger.log(Level.WARNING, "Cannot locate {0} for material {1}", new Object[]{texKey, matName});
+ textures[texUnit].setImage(PlaceholderAssets.getPlaceholderImage());
+ }
+ }
+
+ private void readTextureUnitStatement(Statement statement){
+ String[] split = statement.getLine().split(" ", 2);
+ String keyword = split[0];
+ if (keyword.equals("texture")){
+ readTextureImage(split[1]);
+ }else if (keyword.equals("tex_address_mode")){
+ String mode = split[1];
+ if (mode.equals("wrap")){
+ textures[texUnit].setWrap(WrapMode.Repeat);
+ }else if (mode.equals("clamp")){
+ textures[texUnit].setWrap(WrapMode.Clamp);
+ }else if (mode.equals("mirror")){
+ textures[texUnit].setWrap(WrapMode.MirroredRepeat);
+ }else if (mode.equals("border")){
+ textures[texUnit].setWrap(WrapMode.BorderClamp);
+ }
+ }else if (keyword.equals("filtering")){
+ // ignored.. only anisotropy is considered
+ }else if (keyword.equals("tex_coord_set")){
+ int texCoord = Integer.parseInt(split[1]);
+ if (texCoord == 1){
+ separateTexCoord = true;
+ }
+ }else if (keyword.equals("max_anisotropy")){
+ int amount = Integer.parseInt(split[1]);
+ textures[texUnit].setAnisotropicFilter(amount);
+ }else{
+ logger.log(Level.WARNING, "Unsupported texture_unit directive: {0}", keyword);
+ }
+ }
+
+ private void readTextureUnit(Statement statement){
+ String[] split = statement.getLine().split(" ", 2);
+ // name is optional
+ if (split.length == 2){
+ texName = split[1];
+ }else{
+ texName = null;
+ }
+
+ textures[texUnit] = new Texture2D();
+ for (Statement texUnitStat : statement.getContents()){
+ readTextureUnitStatement(texUnitStat);
+ }
+ if (textures[texUnit].getImage() != null){
+ texUnit++;
+ }else{
+ // no image was loaded, ignore
+ textures[texUnit] = null;
+ }
+ }
+
+ private void readPassStatement(Statement statement){
+ // read until newline
+ String[] split = statement.getLine().split(" ", 2);
+ String keyword = split[0];
+ if (keyword.equals("diffuse")){
+ if (split[1].equals("vertexcolour")){
+ // use vertex colors
+ diffuse = ColorRGBA.White;
+ vcolor = true;
+ }else{
+ diffuse = readColor(split[1]);
+ }
+ }else if(keyword.equals("ambient")) {
+ if (split[1].equals("vertexcolour")){
+ // use vertex colors
+ ambient = ColorRGBA.White;
+ }else{
+ ambient = readColor(split[1]);
+ }
+ }else if (keyword.equals("emissive")){
+ emissive = readColor(split[1]);
+ }else if (keyword.equals("specular")){
+ String[] subsplit = split[1].split("\\s");
+ specular = new ColorRGBA();
+ specular.r = Float.parseFloat(subsplit[0]);
+ specular.g = Float.parseFloat(subsplit[1]);
+ specular.b = Float.parseFloat(subsplit[2]);
+ float unknown = Float.parseFloat(subsplit[3]);
+ if (subsplit.length >= 5){
+ // using 5 float values
+ specular.a = unknown;
+ shinines = Float.parseFloat(subsplit[4]);
+ }else{
+ // using 4 float values
+ specular.a = 1f;
+ shinines = unknown;
+ }
+ }else if (keyword.equals("texture_unit")){
+ readTextureUnit(statement);
+ }else if (keyword.equals("scene_blend")){
+ String mode = split[1];
+ if (mode.equals("alpha_blend")){
+ blend = true;
+ }
+ }else if (keyword.equals("cull_hardware")){
+ String mode = split[1];
+ if (mode.equals("none")){
+ twoSide = true;
+ }
+ }else if (keyword.equals("cull_software")){
+ // ignore
+ }else if (keyword.equals("lighting")){
+ String isOn = split[1];
+ if (isOn.equals("on")){
+ noLight = false;
+ }else if (isOn.equals("off")){
+ noLight = true;
+ }
+ }else{
+ logger.log(Level.WARNING, "Unsupported pass directive: {0}", keyword);
+ }
+ }
+
+ private void readPass(Statement statement){
+ String name;
+ String[] split = statement.getLine().split(" ", 2);
+ if (split.length == 1){
+ // no name
+ name = null;
+ }else{
+ name = split[1];
+ }
+
+ for (Statement passStat : statement.getContents()){
+ readPassStatement(passStat);
+ }
+
+ texUnit = 0;
+ }
+
+ private void readTechnique(Statement statement){
+ String[] split = statement.getLine().split(" ", 2);
+ String name;
+ if (split.length == 1){
+ // no name
+ name = null;
+ }else{
+ name = split[1];
+ }
+ for (Statement techStat : statement.getContents()){
+ readPass(techStat);
+ }
+ }
+
+ private void readMaterialStatement(Statement statement){
+ if (statement.getLine().startsWith("technique")){
+ readTechnique(statement);
+ }else if (statement.getLine().startsWith("receive_shadows")){
+ String isOn = statement.getLine().split("\\s")[1];
+ if (isOn != null && isOn.equals("true")){
+ }
+ }
+ }
+
+ @SuppressWarnings("empty-statement")
+ private void readMaterial(Statement statement){
+ for (Statement materialStat : statement.getContents()){
+ readMaterialStatement(materialStat);
+ }
+ }
+
+ private Material compileMaterial(){
+ Material mat;
+ if (noLight){
+ mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+ }else{
+ mat = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md");
+ }
+ if (blend){
+ RenderState rs = mat.getAdditionalRenderState();
+ rs.setAlphaTest(true);
+ rs.setAlphaFallOff(0.01f);
+ rs.setBlendMode(RenderState.BlendMode.Alpha);
+
+ if (twoSide){
+ rs.setFaceCullMode(RenderState.FaceCullMode.Off);
+ }
+
+// rs.setDepthWrite(false);
+ mat.setTransparent(true);
+ if (!noLight){
+ mat.setBoolean("UseAlpha", true);
+ }
+ }else{
+ if (twoSide){
+ RenderState rs = mat.getAdditionalRenderState();
+ rs.setFaceCullMode(RenderState.FaceCullMode.Off);
+ }
+ }
+
+ if (!noLight){
+ if (shinines > 0f) {
+ mat.setFloat("Shininess", shinines);
+ } else {
+ mat.setFloat("Shininess", 16f); // set shininess to some value anyway..
+ }
+
+ if (vcolor)
+ mat.setBoolean("UseVertexColor", true);
+
+ if (textures[0] != null)
+ mat.setTexture("DiffuseMap", textures[0]);
+
+ mat.setBoolean("UseMaterialColors", true);
+ if(diffuse != null){
+ mat.setColor("Diffuse", diffuse);
+ }else{
+ mat.setColor("Diffuse", ColorRGBA.White);
+ }
+
+ if(ambient != null){
+ mat.setColor("Ambient", ambient);
+ }else{
+ mat.setColor("Ambient", ColorRGBA.DarkGray);
+ }
+
+ if(specular != null){
+ mat.setColor("Specular", specular);
+ }else{
+ mat.setColor("Specular", ColorRGBA.Black);
+ }
+
+ if (emissive != null){
+ mat.setColor("GlowColor", emissive);
+ }
+ }else{
+ if (vcolor) {
+ mat.setBoolean("VertexColor", true);
+ }
+
+ if (textures[0] != null && textures[1] == null){
+ if (separateTexCoord){
+ mat.setTexture("LightMap", textures[0]);
+ mat.setBoolean("SeparateTexCoord", true);
+ }else{
+ mat.setTexture("ColorMap", textures[0]);
+ }
+ }else if (textures[1] != null){
+ mat.setTexture("ColorMap", textures[0]);
+ mat.setTexture("LightMap", textures[1]);
+ if (separateTexCoord){
+ mat.setBoolean("SeparateTexCoord", true);
+ }
+ }
+
+ if(diffuse != null){
+ mat.setColor("Color", diffuse);
+ }
+
+ if (emissive != null){
+ mat.setColor("GlowColor", emissive);
+ }
+ }
+
+ noLight = false;
+ Arrays.fill(textures, null);
+ diffuse = null;
+ specular = null;
+ shinines = 0f;
+ vcolor = false;
+ blend = false;
+ texUnit = 0;
+ separateTexCoord = false;
+ return mat;
+ }
+
+ private MaterialList load(AssetManager assetManager, AssetKey key, InputStream in) throws IOException{
+ folderName = key.getFolder();
+ this.assetManager = assetManager;
+
+ MaterialList list = null;
+ List<Statement> statements = BlockLanguageParser.parse(in);
+
+ for (Statement statement : statements){
+ if (statement.getLine().startsWith("import")){
+ MaterialExtensionSet matExts = null;
+ if (key instanceof OgreMaterialKey){
+ matExts = ((OgreMaterialKey)key).getMaterialExtensionSet();
+ }
+
+ if (matExts == null){
+ throw new IOException("Must specify MaterialExtensionSet when loading\n"+
+ "Ogre3D materials with extended materials");
+ }
+
+ list = new MaterialExtensionLoader().load(assetManager, key, matExts, statements);
+ break;
+ }else if (statement.getLine().startsWith("material")){
+ if (list == null){
+ list = new MaterialList();
+ }
+ String[] split = statement.getLine().split(" ", 2);
+ matName = split[1].trim();
+ readMaterial(statement);
+ Material mat = compileMaterial();
+ list.put(matName, mat);
+ }
+ }
+
+ return list;
+ }
+
+ public Object load(AssetInfo info) throws IOException {
+ InputStream in = null;
+ try {
+ in = info.openStream();
+ return load(info.getManager(), info.getKey(), in);
+ } finally {
+ if (in != null){
+ in.close();
+ }
+ }
+ }
+
+}
diff --git a/engine/src/ogre/com/jme3/scene/plugins/ogre/MeshAnimationLoader.java b/engine/src/ogre/com/jme3/scene/plugins/ogre/MeshAnimationLoader.java
new file mode 100644
index 0000000..75ff865
--- /dev/null
+++ b/engine/src/ogre/com/jme3/scene/plugins/ogre/MeshAnimationLoader.java
@@ -0,0 +1,239 @@
+/*
+ * Copyright (c) 2009-2010 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.jme3.scene.plugins.ogre;
+
+//import static com.jmex.model.XMLUtil.getAttribute;
+//import static com.jmex.model.XMLUtil.getIntAttribute;
+//
+//import java.util.ArrayList;
+//import java.util.List;
+//import java.util.Map;
+//
+//import org.w3c.dom.Node;
+//
+//import com.jme.math.Vector3f;
+//import com.jmex.model.XMLUtil;
+//import com.jmex.model.ogrexml.anim.PoseTrack.PoseFrame;
+
+/**
+ * Utility class used by OgreLoader to load poses and mesh animations.
+ */
+public class MeshAnimationLoader {
+
+// public static void loadMeshAnimations(Node animationsNode, List<Pose> poseList, OgreMesh sharedgeom, List<OgreMesh> submeshes, Map<String, Animation> animations){
+// Node animationNode = animationsNode.getFirstChild();
+// while (animationNode != null){
+// if (animationNode.getNodeName().equals("animation")){
+// MeshAnimation mAnim =
+// loadMeshAnimation(animationNode, poseList, sharedgeom, submeshes);
+//
+// Animation anim = animations.get(mAnim.getName());
+// if (anim != null){
+// anim.setMeshAnimation(mAnim);
+// }else{
+// anim = new Animation(null, mAnim);
+// animations.put(anim.getName(), anim);
+// }
+// }
+// animationNode = animationNode.getNextSibling();
+// }
+//
+//// Map<TriMesh, List<Pose>> trimeshPoses = new HashMap<TriMesh, List<Pose>>();
+////
+//// // find the poses for each mesh
+//// for (Pose p : poses){
+//// List<Pose> poseList = trimeshPoses.get(p.getTarget());
+//// if (poseList == null){
+//// poseList = new ArrayList<Pose>();
+//// trimeshPoses.put(p.getTarget(), poseList);
+//// }
+////
+//// poseList.add(p);
+//// }
+////
+//// for (Map.Entry<TriMesh, List<Pose>> poseEntry: trimeshPoses){
+//// PoseController
+//// }
+// }
+//
+// public static MeshAnimation loadMeshAnimation(Node animationNode, List<Pose> poseList, OgreMesh sharedgeom, List<OgreMesh> submeshes){
+// String name = XMLUtil.getAttribute(animationNode, "name");
+// float length = XMLUtil.getFloatAttribute(animationNode, "length");
+//
+// MeshAnimation anim = new MeshAnimation(name, length);
+// List<Track> tracks = new ArrayList<Track>();
+//
+// Node tracksNode = XMLUtil.getChildNode(animationNode, "tracks");
+// if (tracksNode != null){
+// Node trackNode = tracksNode.getFirstChild();
+// while (trackNode != null){
+// if (trackNode.getNodeName().equals("track")){
+// int targetMeshIndex;
+// if (XMLUtil.getAttribute(trackNode, "target").equals("mesh")){
+// targetMeshIndex = -1;
+// }else{
+// if (XMLUtil.getAttribute(trackNode, "index") == null)
+// targetMeshIndex = 0;
+// else
+// targetMeshIndex = getIntAttribute(trackNode, "index");
+// }
+//
+// if (XMLUtil.getAttribute(trackNode, "type").equals("pose")){
+// PoseTrack pt = loadPoseTrack(trackNode, targetMeshIndex, poseList);
+// tracks.add(pt);
+// }else{
+// throw new UnsupportedOperationException("Morph animations not supported!");
+// }
+// }
+//
+// trackNode = trackNode.getNextSibling();
+// }
+// }
+//
+// anim.setTracks(tracks.toArray(new Track[0]));
+//
+// return anim;
+// }
+//
+// public static List<Pose> loadPoses(Node posesNode, OgreMesh sharedgeom, List<OgreMesh> submeshes){
+// List<Pose> poses = new ArrayList<Pose>();
+// Node poseNode = posesNode.getFirstChild();
+// while (poseNode != null){
+// if (poseNode.getNodeName().equals("pose")){
+// int targetMeshIndex = 0;
+// if (getAttribute(poseNode, "target").equals("mesh")){
+// targetMeshIndex = -1;
+// }else{
+// if (getAttribute(poseNode, "index") == null)
+// targetMeshIndex = 0;
+// else
+// targetMeshIndex = getIntAttribute(poseNode, "index");
+// }
+//
+// Pose p = MeshAnimationLoader.loadPose(poseNode, targetMeshIndex);
+// poses.add(p);
+// }
+//
+// poseNode = poseNode.getNextSibling();
+// }
+//
+// return poses;
+// }
+//
+// public static Pose loadPose(Node poseNode, int targetMeshIndex){
+// String name = XMLUtil.getAttribute(poseNode, "name");
+//
+// List<Vector3f> offsets = new ArrayList<Vector3f>();
+// List<Integer> indices = new ArrayList<Integer>();
+//
+// Node poseoffsetNode = poseNode.getFirstChild();
+// while (poseoffsetNode != null){
+// if (poseoffsetNode.getNodeName().equals("poseoffset")){
+// int vertIndex = XMLUtil.getIntAttribute(poseoffsetNode, "index");
+// Vector3f offset = new Vector3f();
+// offset.x = XMLUtil.getFloatAttribute(poseoffsetNode, "x");
+// offset.y = XMLUtil.getFloatAttribute(poseoffsetNode, "y");
+// offset.z = XMLUtil.getFloatAttribute(poseoffsetNode, "z");
+//
+// offsets.add(offset);
+// indices.add(vertIndex);
+// }
+//
+// poseoffsetNode = poseoffsetNode.getNextSibling();
+// }
+//
+// int[] indicesArray = new int[indices.size()];
+// for (int i = 0; i < indicesArray.length; i++){
+// indicesArray[i] = indices.get(i);
+// }
+//
+// Pose pose = new Pose(name,
+// targetMeshIndex,
+// offsets.toArray(new Vector3f[0]),
+// indicesArray);
+//
+// return pose;
+// }
+//
+// public static PoseTrack loadPoseTrack(Node trackNode, int targetMeshIndex, List<Pose> posesList){
+// List<Float> times = new ArrayList<Float>();
+// List<PoseFrame> frames = new ArrayList<PoseFrame>();
+//
+// Node keyframesNode = XMLUtil.getChildNode(trackNode, "keyframes");
+// Node keyframeNode = keyframesNode.getFirstChild();
+// while (keyframeNode != null){
+// if (keyframeNode.getNodeName().equals("keyframe")){
+// float time = XMLUtil.getFloatAttribute(keyframeNode, "time");
+// List<Pose> poses = new ArrayList<Pose>();
+// List<Float> weights = new ArrayList<Float>();
+//
+// Node poserefNode = keyframeNode.getFirstChild();
+// while (poserefNode != null){
+// if (poserefNode.getNodeName().equals("poseref")){
+// int poseindex = XMLUtil.getIntAttribute(poserefNode, "poseindex");
+// poses.add(posesList.get(poseindex));
+// float weight = XMLUtil.getFloatAttribute(poserefNode, "influence");
+// weights.add(weight);
+// }
+//
+// poserefNode = poserefNode.getNextSibling();
+// }
+//
+// // convert poses and weights to arrays and create a PoseFrame
+// float[] weightsArray = new float[weights.size()];
+// for (int i = 0; i < weightsArray.length; i++){
+// weightsArray[i] = weights.get(i);
+// }
+// PoseFrame frame = new PoseFrame(poses.toArray(new Pose[0]), weightsArray);
+//
+// times.add(time);
+// frames.add(frame);
+// }
+//
+// keyframeNode = keyframeNode.getNextSibling();
+// }
+//
+// // convert times and frames to arrays and write to the track
+// float[] timesArray = new float[times.size()];
+// for (int i = 0; i < timesArray.length; i++){
+// timesArray[i] = times.get(i);
+// }
+//
+// PoseTrack track = new PoseTrack(targetMeshIndex,
+// timesArray,
+// frames.toArray(new PoseFrame[0]));
+//
+// return track;
+// }
+
+}
diff --git a/engine/src/ogre/com/jme3/scene/plugins/ogre/MeshLoader.java b/engine/src/ogre/com/jme3/scene/plugins/ogre/MeshLoader.java
new file mode 100644
index 0000000..e7f4b10
--- /dev/null
+++ b/engine/src/ogre/com/jme3/scene/plugins/ogre/MeshLoader.java
@@ -0,0 +1,892 @@
+/*
+ * Copyright (c) 2009-2010 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.jme3.scene.plugins.ogre;
+
+import com.jme3.animation.AnimControl;
+import com.jme3.animation.Animation;
+import com.jme3.animation.SkeletonControl;
+import com.jme3.asset.*;
+import com.jme3.material.Material;
+import com.jme3.material.MaterialList;
+import com.jme3.math.ColorRGBA;
+import com.jme3.renderer.queue.RenderQueue.Bucket;
+import com.jme3.scene.*;
+import com.jme3.scene.VertexBuffer.Format;
+import com.jme3.scene.VertexBuffer.Type;
+import com.jme3.scene.VertexBuffer.Usage;
+import com.jme3.scene.plugins.ogre.matext.OgreMaterialKey;
+import com.jme3.util.BufferUtils;
+import com.jme3.util.IntMap;
+import com.jme3.util.IntMap.Entry;
+import com.jme3.util.PlaceholderAssets;
+import static com.jme3.util.xml.SAXUtil.*;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.nio.*;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParserFactory;
+import org.xml.sax.Attributes;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.XMLReader;
+import org.xml.sax.helpers.DefaultHandler;
+
+/**
+ * Loads Ogre3D mesh.xml files.
+ */
+public class MeshLoader extends DefaultHandler implements AssetLoader {
+
+ private static final Logger logger = Logger.getLogger(MeshLoader.class.getName());
+ public static boolean AUTO_INTERLEAVE = true;
+ public static boolean HARDWARE_SKINNING = false;
+ private static final Type[] TEXCOORD_TYPES =
+ new Type[]{
+ Type.TexCoord,
+ Type.TexCoord2,
+ Type.TexCoord3,
+ Type.TexCoord4,
+ Type.TexCoord5,
+ Type.TexCoord6,
+ Type.TexCoord7,
+ Type.TexCoord8,};
+ private AssetKey key;
+ private String meshName;
+ private String folderName;
+ private AssetManager assetManager;
+ private MaterialList materialList;
+ // Data per submesh/sharedgeom
+ private ShortBuffer sb;
+ private IntBuffer ib;
+ private FloatBuffer fb;
+ private VertexBuffer vb;
+ private Mesh mesh;
+ private Geometry geom;
+ private ByteBuffer indicesData;
+ private FloatBuffer weightsFloatData;
+ private boolean actuallyHasWeights = false;
+ private int vertCount;
+ private boolean usesSharedVerts;
+ private boolean usesBigIndices;
+ // Global data
+ private Mesh sharedMesh;
+ private int meshIndex = 0;
+ private int texCoordIndex = 0;
+ private String ignoreUntilEnd = null;
+ private List<Geometry> geoms = new ArrayList<Geometry>();
+ private ArrayList<Boolean> usesSharedMesh = new ArrayList<Boolean>();
+ private IntMap<List<VertexBuffer>> lodLevels = new IntMap<List<VertexBuffer>>();
+ private AnimData animData;
+
+ public MeshLoader() {
+ super();
+ }
+
+ @Override
+ public void startDocument() {
+ geoms.clear();
+ lodLevels.clear();
+
+ sb = null;
+ ib = null;
+ fb = null;
+ vb = null;
+ mesh = null;
+ geom = null;
+ sharedMesh = null;
+
+ usesSharedMesh.clear();
+ usesSharedVerts = false;
+ vertCount = 0;
+ meshIndex = 0;
+ texCoordIndex = 0;
+ ignoreUntilEnd = null;
+
+ animData = null;
+
+ actuallyHasWeights = false;
+ indicesData = null;
+ weightsFloatData = null;
+ }
+
+ @Override
+ public void endDocument() {
+ }
+
+ private void pushIndex(int index){
+ if (ib != null){
+ ib.put(index);
+ }else{
+ sb.put((short)index);
+ }
+ }
+
+ private void pushFace(String v1, String v2, String v3) throws SAXException {
+ // TODO: fan/strip support
+ switch (mesh.getMode()){
+ case Triangles:
+ pushIndex(parseInt(v1));
+ pushIndex(parseInt(v2));
+ pushIndex(parseInt(v3));
+ break;
+ case Lines:
+ pushIndex(parseInt(v1));
+ pushIndex(parseInt(v2));
+ break;
+ case Points:
+ pushIndex(parseInt(v1));
+ break;
+ }
+ }
+
+// private boolean isUsingSharedVerts(Geometry geom) {
+ // Old code for buffer sharer
+ //return geom.getUserData(UserData.JME_SHAREDMESH) != null;
+// }
+
+ private void startFaces(String count) throws SAXException {
+ int numFaces = parseInt(count);
+ int indicesPerFace = 0;
+
+ switch (mesh.getMode()){
+ case Triangles:
+ indicesPerFace = 3;
+ break;
+ case Lines:
+ indicesPerFace = 2;
+ break;
+ case Points:
+ indicesPerFace = 1;
+ break;
+ default:
+ throw new SAXException("Strips or fans not supported!");
+ }
+
+ int numIndices = indicesPerFace * numFaces;
+
+ vb = new VertexBuffer(VertexBuffer.Type.Index);
+ if (!usesBigIndices) {
+ sb = BufferUtils.createShortBuffer(numIndices);
+ ib = null;
+ vb.setupData(Usage.Static, indicesPerFace, Format.UnsignedShort, sb);
+ } else {
+ ib = BufferUtils.createIntBuffer(numIndices);
+ sb = null;
+ vb.setupData(Usage.Static, indicesPerFace, Format.UnsignedInt, ib);
+ }
+ mesh.setBuffer(vb);
+ }
+
+ private void applyMaterial(Geometry geom, String matName) {
+ Material mat = null;
+ if (matName.endsWith(".j3m")) {
+ // load as native jme3 material instance
+ try {
+ mat = assetManager.loadMaterial(matName);
+ } catch (AssetNotFoundException ex){
+ // Warning will be raised (see below)
+ if (!ex.getMessage().equals(matName)){
+ throw ex;
+ }
+ }
+ } else {
+ if (materialList != null) {
+ mat = materialList.get(matName);
+ }
+ }
+
+ if (mat == null) {
+ logger.log(Level.WARNING, "Cannot locate {0} for model {1}", new Object[]{matName, key});
+ mat = PlaceholderAssets.getPlaceholderMaterial(assetManager);
+ }
+
+ if (mat.isTransparent()) {
+ geom.setQueueBucket(Bucket.Transparent);
+ }
+
+ geom.setMaterial(mat);
+ }
+
+ private void startSubMesh(String matName, String usesharedvertices, String use32bitIndices, String opType) throws SAXException {
+ mesh = new Mesh();
+ if (opType == null || opType.equals("triangle_list")) {
+ mesh.setMode(Mesh.Mode.Triangles);
+ //} else if (opType.equals("triangle_strip")) {
+ // mesh.setMode(Mesh.Mode.TriangleStrip);
+ //} else if (opType.equals("triangle_fan")) {
+ // mesh.setMode(Mesh.Mode.TriangleFan);
+ } else if (opType.equals("line_list")) {
+ mesh.setMode(Mesh.Mode.Lines);
+ } else {
+ throw new SAXException("Unsupported operation type: " + opType);
+ }
+
+ usesBigIndices = parseBool(use32bitIndices, false);
+ usesSharedVerts = parseBool(usesharedvertices, false);
+ if (usesSharedVerts) {
+ usesSharedMesh.add(true);
+
+ // Old code for buffer sharer
+ // import vertexbuffers from shared geom
+// IntMap<VertexBuffer> sharedBufs = sharedMesh.getBuffers();
+// for (Entry<VertexBuffer> entry : sharedBufs) {
+// mesh.setBuffer(entry.getValue());
+// }
+ }else{
+ usesSharedMesh.add(false);
+ }
+
+ if (meshName == null) {
+ geom = new Geometry("OgreSubmesh-" + (++meshIndex), mesh);
+ } else {
+ geom = new Geometry(meshName + "-geom-" + (++meshIndex), mesh);
+ }
+
+ if (usesSharedVerts) {
+ // Old code for buffer sharer
+ // this mesh is shared!
+ //geom.setUserData(UserData.JME_SHAREDMESH, sharedMesh);
+ }
+
+ applyMaterial(geom, matName);
+ geoms.add(geom);
+ }
+
+ private void startSharedGeom(String vertexcount) throws SAXException {
+ sharedMesh = new Mesh();
+ vertCount = parseInt(vertexcount);
+ usesSharedVerts = false;
+
+ geom = null;
+ mesh = sharedMesh;
+ }
+
+ private void startGeometry(String vertexcount) throws SAXException {
+ vertCount = parseInt(vertexcount);
+ }
+
+ /**
+ * Normalizes weights if needed and finds largest amount of weights used
+ * for all vertices in the buffer.
+ */
+ private void endBoneAssigns() {
+// if (mesh != sharedMesh && isUsingSharedVerts(geom)) {
+// return;
+// }
+
+ if (!actuallyHasWeights){
+ // No weights were actually written (the tag didn't have any entries)
+ // remove those buffers
+ mesh.clearBuffer(Type.BoneIndex);
+ mesh.clearBuffer(Type.BoneWeight);
+
+ weightsFloatData = null;
+ indicesData = null;
+
+ return;
+ }
+
+ //int vertCount = mesh.getVertexCount();
+ int maxWeightsPerVert = 0;
+ weightsFloatData.rewind();
+ for (int v = 0; v < vertCount; v++) {
+ float w0 = weightsFloatData.get(),
+ w1 = weightsFloatData.get(),
+ w2 = weightsFloatData.get(),
+ w3 = weightsFloatData.get();
+
+ if (w3 != 0) {
+ maxWeightsPerVert = Math.max(maxWeightsPerVert, 4);
+ } else if (w2 != 0) {
+ maxWeightsPerVert = Math.max(maxWeightsPerVert, 3);
+ } else if (w1 != 0) {
+ maxWeightsPerVert = Math.max(maxWeightsPerVert, 2);
+ } else if (w0 != 0) {
+ maxWeightsPerVert = Math.max(maxWeightsPerVert, 1);
+ }
+
+ float sum = w0 + w1 + w2 + w3;
+ if (sum != 1f) {
+ weightsFloatData.position(weightsFloatData.position() - 4);
+ // compute new vals based on sum
+ float sumToB = 1f / sum;
+ weightsFloatData.put(w0 * sumToB);
+ weightsFloatData.put(w1 * sumToB);
+ weightsFloatData.put(w2 * sumToB);
+ weightsFloatData.put(w3 * sumToB);
+ }
+ }
+ weightsFloatData.rewind();
+
+ actuallyHasWeights = false;
+ weightsFloatData = null;
+ indicesData = null;
+
+ mesh.setMaxNumWeights(maxWeightsPerVert);
+ }
+
+ private void startBoneAssigns() {
+ if (mesh != sharedMesh && usesSharedVerts) {
+ // will use bone assignments from shared mesh (?)
+ return;
+ }
+
+ // current mesh will have bone assigns
+ //int vertCount = mesh.getVertexCount();
+ // each vertex has
+ // - 4 bone weights
+ // - 4 bone indices
+ if (HARDWARE_SKINNING) {
+ weightsFloatData = BufferUtils.createFloatBuffer(vertCount * 4);
+ indicesData = BufferUtils.createByteBuffer(vertCount * 4);
+ } else {
+ // create array-backed buffers if software skinning for access speed
+ weightsFloatData = FloatBuffer.allocate(vertCount * 4);
+ indicesData = ByteBuffer.allocate(vertCount * 4);
+ }
+
+ VertexBuffer weights = new VertexBuffer(Type.BoneWeight);
+ VertexBuffer indices = new VertexBuffer(Type.BoneIndex);
+
+ Usage usage = HARDWARE_SKINNING ? Usage.Static : Usage.CpuOnly;
+ weights.setupData(usage, 4, Format.Float, weightsFloatData);
+ indices.setupData(usage, 4, Format.UnsignedByte, indicesData);
+
+ mesh.setBuffer(weights);
+ mesh.setBuffer(indices);
+ }
+
+ private void startVertexBuffer(Attributes attribs) throws SAXException {
+ if (parseBool(attribs.getValue("positions"), false)) {
+ vb = new VertexBuffer(Type.Position);
+ fb = BufferUtils.createFloatBuffer(vertCount * 3);
+ vb.setupData(Usage.Static, 3, Format.Float, fb);
+ mesh.setBuffer(vb);
+ }
+ if (parseBool(attribs.getValue("normals"), false)) {
+ vb = new VertexBuffer(Type.Normal);
+ fb = BufferUtils.createFloatBuffer(vertCount * 3);
+ vb.setupData(Usage.Static, 3, Format.Float, fb);
+ mesh.setBuffer(vb);
+ }
+ if (parseBool(attribs.getValue("colours_diffuse"), false)) {
+ vb = new VertexBuffer(Type.Color);
+ fb = BufferUtils.createFloatBuffer(vertCount * 4);
+ vb.setupData(Usage.Static, 4, Format.Float, fb);
+ mesh.setBuffer(vb);
+ }
+ if (parseBool(attribs.getValue("tangents"), false)) {
+ int dimensions = parseInt(attribs.getValue("tangent_dimensions"), 3);
+ vb = new VertexBuffer(Type.Tangent);
+ fb = BufferUtils.createFloatBuffer(vertCount * dimensions);
+ vb.setupData(Usage.Static, dimensions, Format.Float, fb);
+ mesh.setBuffer(vb);
+ }
+ if (parseBool(attribs.getValue("binormals"), false)) {
+ vb = new VertexBuffer(Type.Binormal);
+ fb = BufferUtils.createFloatBuffer(vertCount * 3);
+ vb.setupData(Usage.Static, 3, Format.Float, fb);
+ mesh.setBuffer(vb);
+ }
+
+ int texCoords = parseInt(attribs.getValue("texture_coords"), 0);
+ for (int i = 0; i < texCoords; i++) {
+ int dims = parseInt(attribs.getValue("texture_coord_dimensions_" + i), 2);
+ if (dims < 1 || dims > 4) {
+ throw new SAXException("Texture coord dimensions must be 1 <= dims <= 4");
+ }
+
+ if (i <= 7) {
+ vb = new VertexBuffer(TEXCOORD_TYPES[i]);
+ } else {
+ // more than 8 texture coordinates are not supported by ogre.
+ throw new SAXException("More than 8 texture coordinates not supported");
+ }
+ fb = BufferUtils.createFloatBuffer(vertCount * dims);
+ vb.setupData(Usage.Static, dims, Format.Float, fb);
+ mesh.setBuffer(vb);
+ }
+ }
+
+ private void startVertex() {
+ texCoordIndex = 0;
+ }
+
+ private void pushAttrib(Type type, Attributes attribs) throws SAXException {
+ try {
+ FloatBuffer buf = (FloatBuffer) mesh.getBuffer(type).getData();
+ buf.put(parseFloat(attribs.getValue("x"))).put(parseFloat(attribs.getValue("y"))).put(parseFloat(attribs.getValue("z")));
+ } catch (Exception ex) {
+ throw new SAXException("Failed to push attrib", ex);
+ }
+ }
+
+ private void pushTangent(Attributes attribs) throws SAXException {
+ try {
+ VertexBuffer tangentBuf = mesh.getBuffer(Type.Tangent);
+ FloatBuffer buf = (FloatBuffer) tangentBuf.getData();
+ buf.put(parseFloat(attribs.getValue("x"))).put(parseFloat(attribs.getValue("y"))).put(parseFloat(attribs.getValue("z")));
+ if (tangentBuf.getNumComponents() == 4) {
+ buf.put(parseFloat(attribs.getValue("w")));
+ }
+ } catch (Exception ex) {
+ throw new SAXException("Failed to push attrib", ex);
+ }
+ }
+
+ private void pushTexCoord(Attributes attribs) throws SAXException {
+ if (texCoordIndex >= 8) {
+ return; // More than 8 not supported by ogre.
+ }
+ Type type = TEXCOORD_TYPES[texCoordIndex];
+
+ VertexBuffer tcvb = mesh.getBuffer(type);
+ FloatBuffer buf = (FloatBuffer) tcvb.getData();
+
+ buf.put(parseFloat(attribs.getValue("u")));
+ if (tcvb.getNumComponents() >= 2) {
+ buf.put(parseFloat(attribs.getValue("v")));
+ if (tcvb.getNumComponents() >= 3) {
+ buf.put(parseFloat(attribs.getValue("w")));
+ if (tcvb.getNumComponents() == 4) {
+ buf.put(parseFloat(attribs.getValue("x")));
+ }
+ }
+ }
+
+ texCoordIndex++;
+ }
+
+ private void pushColor(Attributes attribs) throws SAXException {
+ FloatBuffer buf = (FloatBuffer) mesh.getBuffer(Type.Color).getData();
+ String value = parseString(attribs.getValue("value"));
+ String[] vals = value.split("\\s");
+ if (vals.length != 3 && vals.length != 4) {
+ throw new SAXException("Color value must contain 3 or 4 components");
+ }
+
+ ColorRGBA color = new ColorRGBA();
+ color.r = parseFloat(vals[0]);
+ color.g = parseFloat(vals[1]);
+ color.b = parseFloat(vals[2]);
+ if (vals.length == 3) {
+ color.a = 1f;
+ } else {
+ color.a = parseFloat(vals[3]);
+ }
+
+ buf.put(color.r).put(color.g).put(color.b).put(color.a);
+ }
+
+ private void startLodFaceList(String submeshindex, String numfaces) {
+ int index = Integer.parseInt(submeshindex);
+ mesh = geoms.get(index).getMesh();
+ int faceCount = Integer.parseInt(numfaces);
+
+ VertexBuffer originalIndexBuffer = mesh.getBuffer(Type.Index);
+ vb = new VertexBuffer(VertexBuffer.Type.Index);
+ if (originalIndexBuffer.getFormat() == Format.UnsignedInt){
+ // LOD buffer should also be integer
+ ib = BufferUtils.createIntBuffer(faceCount * 3);
+ sb = null;
+ vb.setupData(Usage.Static, 3, Format.UnsignedInt, ib);
+ }else{
+ sb = BufferUtils.createShortBuffer(faceCount * 3);
+ ib = null;
+ vb.setupData(Usage.Static, 3, Format.UnsignedShort, sb);
+ }
+
+ List<VertexBuffer> levels = lodLevels.get(index);
+ if (levels == null) {
+ // Create the LOD levels list
+ levels = new ArrayList<VertexBuffer>();
+
+ // Add the first LOD level (always the original index buffer)
+ levels.add(originalIndexBuffer);
+ lodLevels.put(index, levels);
+ }
+ levels.add(vb);
+ }
+
+ private void startLevelOfDetail(String numlevels) {
+// numLevels = Integer.parseInt(numlevels);
+ }
+
+ private void endLevelOfDetail() {
+ // set the lod data for each mesh
+ for (Entry<List<VertexBuffer>> entry : lodLevels) {
+ Mesh m = geoms.get(entry.getKey()).getMesh();
+ List<VertexBuffer> levels = entry.getValue();
+ VertexBuffer[] levelArray = new VertexBuffer[levels.size()];
+ levels.toArray(levelArray);
+ m.setLodLevels(levelArray);
+ }
+ }
+
+ private void startLodGenerated(String depthsqr) {
+ }
+
+ private void pushBoneAssign(String vertIndex, String boneIndex, String weight) throws SAXException {
+ int vert = parseInt(vertIndex);
+ float w = parseFloat(weight);
+ byte bone = (byte) parseInt(boneIndex);
+
+ assert bone >= 0;
+ assert vert >= 0 && vert < mesh.getVertexCount();
+
+ int i;
+ float v = 0;
+ // see which weights are unused for a given bone
+ for (i = vert * 4; i < vert * 4 + 4; i++) {
+ v = weightsFloatData.get(i);
+ if (v == 0) {
+ break;
+ }
+ }
+ if (v != 0) {
+ logger.log(Level.WARNING, "Vertex {0} has more than 4 weights per vertex! Ignoring..", vert);
+ return;
+ }
+
+ weightsFloatData.put(i, w);
+ indicesData.put(i, bone);
+ actuallyHasWeights = true;
+ }
+
+ private void startSkeleton(String name) {
+ AssetKey assetKey = new AssetKey(folderName + name + ".xml");
+ try {
+ animData = (AnimData) assetManager.loadAsset(assetKey);
+ } catch (AssetNotFoundException ex){
+ logger.log(Level.WARNING, "Cannot locate {0} for model {1}", new Object[]{assetKey, key});
+ animData = null;
+ }
+ }
+
+ private void startSubmeshName(String indexStr, String nameStr) {
+ int index = Integer.parseInt(indexStr);
+ geoms.get(index).setName(nameStr);
+ }
+
+ @Override
+ public void startElement(String uri, String localName, String qName, Attributes attribs) throws SAXException {
+ if (ignoreUntilEnd != null) {
+ return;
+ }
+
+ if (qName.equals("texcoord")) {
+ pushTexCoord(attribs);
+ } else if (qName.equals("vertexboneassignment")) {
+ pushBoneAssign(attribs.getValue("vertexindex"),
+ attribs.getValue("boneindex"),
+ attribs.getValue("weight"));
+ } else if (qName.equals("face")) {
+ pushFace(attribs.getValue("v1"),
+ attribs.getValue("v2"),
+ attribs.getValue("v3"));
+ } else if (qName.equals("position")) {
+ pushAttrib(Type.Position, attribs);
+ } else if (qName.equals("normal")) {
+ pushAttrib(Type.Normal, attribs);
+ } else if (qName.equals("tangent")) {
+ pushTangent(attribs);
+ } else if (qName.equals("binormal")) {
+ pushAttrib(Type.Binormal, attribs);
+ } else if (qName.equals("colour_diffuse")) {
+ pushColor(attribs);
+ } else if (qName.equals("vertex")) {
+ startVertex();
+ } else if (qName.equals("faces")) {
+ startFaces(attribs.getValue("count"));
+ } else if (qName.equals("geometry")) {
+ String count = attribs.getValue("vertexcount");
+ if (count == null) {
+ count = attribs.getValue("count");
+ }
+ startGeometry(count);
+ } else if (qName.equals("vertexbuffer")) {
+ startVertexBuffer(attribs);
+ } else if (qName.equals("lodfacelist")) {
+ startLodFaceList(attribs.getValue("submeshindex"),
+ attribs.getValue("numfaces"));
+ } else if (qName.equals("lodgenerated")) {
+ startLodGenerated(attribs.getValue("fromdepthsquared"));
+ } else if (qName.equals("levelofdetail")) {
+ startLevelOfDetail(attribs.getValue("numlevels"));
+ } else if (qName.equals("boneassignments")) {
+ startBoneAssigns();
+ } else if (qName.equals("submesh")) {
+ startSubMesh(attribs.getValue("material"),
+ attribs.getValue("usesharedvertices"),
+ attribs.getValue("use32bitindexes"),
+ attribs.getValue("operationtype"));
+ } else if (qName.equals("sharedgeometry")) {
+ String count = attribs.getValue("vertexcount");
+ if (count == null) {
+ count = attribs.getValue("count");
+ }
+
+ if (count != null && !count.equals("0")) {
+ startSharedGeom(count);
+ }
+ } else if (qName.equals("submeshes")) {
+ // ok
+ } else if (qName.equals("skeletonlink")) {
+ startSkeleton(attribs.getValue("name"));
+ } else if (qName.equals("submeshnames")) {
+ // ok
+ } else if (qName.equals("submeshname")) {
+ startSubmeshName(attribs.getValue("index"), attribs.getValue("name"));
+ } else if (qName.equals("mesh")) {
+ // ok
+ } else {
+ logger.log(Level.WARNING, "Unknown tag: {0}. Ignoring.", qName);
+ ignoreUntilEnd = qName;
+ }
+ }
+
+ @Override
+ public void endElement(String uri, String name, String qName) {
+ if (ignoreUntilEnd != null) {
+ if (ignoreUntilEnd.equals(qName)) {
+ ignoreUntilEnd = null;
+ }
+ return;
+ }
+
+ if (qName.equals("submesh")) {
+ usesBigIndices = false;
+ geom = null;
+ mesh = null;
+ } else if (qName.equals("submeshes")) {
+ // IMPORTANT: restore sharedmesh, for use with shared boneweights
+ geom = null;
+ mesh = sharedMesh;
+ usesSharedVerts = false;
+ } else if (qName.equals("faces")) {
+ if (ib != null) {
+ ib.flip();
+ } else {
+ sb.flip();
+ }
+
+ vb = null;
+ ib = null;
+ sb = null;
+ } else if (qName.equals("vertexbuffer")) {
+ fb = null;
+ vb = null;
+ } else if (qName.equals("geometry")
+ || qName.equals("sharedgeometry")) {
+ // finish writing to buffers
+ IntMap<VertexBuffer> bufs = mesh.getBuffers();
+ for (Entry<VertexBuffer> entry : bufs) {
+ Buffer data = entry.getValue().getData();
+ if (data.position() != 0) {
+ data.flip();
+ }
+ }
+ mesh.updateBound();
+ mesh.setStatic();
+
+ if (qName.equals("sharedgeometry")) {
+ geom = null;
+ mesh = null;
+ }
+ } else if (qName.equals("lodfacelist")) {
+ sb.flip();
+ vb = null;
+ sb = null;
+ } else if (qName.equals("levelofdetail")) {
+ endLevelOfDetail();
+ } else if (qName.equals("boneassignments")) {
+ endBoneAssigns();
+ }
+ }
+
+ @Override
+ public void characters(char ch[], int start, int length) {
+ }
+
+ private Node compileModel() {
+ Node model = new Node(meshName + "-ogremesh");
+
+ for (int i = 0; i < geoms.size(); i++) {
+ Geometry g = geoms.get(i);
+ Mesh m = g.getMesh();
+
+ // New code for buffer extract
+ if (sharedMesh != null && usesSharedMesh.get(i)) {
+ m.extractVertexData(sharedMesh);
+ }
+
+ // Old code for buffer sharer
+ //if (sharedMesh != null && isUsingSharedVerts(g)) {
+ // m.setBound(sharedMesh.getBound().clone());
+ //}
+ model.attachChild(geoms.get(i));
+ }
+
+ // Do not attach shared geometry to the node!
+
+ if (animData != null) {
+ // This model uses animation
+
+ // Old code for buffer sharer
+ // generate bind pose for mesh
+ // ONLY if not using shared geometry
+ // This includes the shared geoemtry itself actually
+ //if (sharedMesh != null) {
+ // sharedMesh.generateBindPose(!HARDWARE_SKINNING);
+ //}
+
+ for (int i = 0; i < geoms.size(); i++) {
+ Geometry g = geoms.get(i);
+ Mesh m = geoms.get(i).getMesh();
+
+ m.generateBindPose(!HARDWARE_SKINNING);
+
+ // Old code for buffer sharer
+ //boolean useShared = isUsingSharedVerts(g);
+ //if (!useShared) {
+ // create bind pose
+ //m.generateBindPose(!HARDWARE_SKINNING);
+ //}
+ }
+
+ // Put the animations in the AnimControl
+ HashMap<String, Animation> anims = new HashMap<String, Animation>();
+ ArrayList<Animation> animList = animData.anims;
+ for (int i = 0; i < animList.size(); i++) {
+ Animation anim = animList.get(i);
+ anims.put(anim.getName(), anim);
+ }
+
+ AnimControl ctrl = new AnimControl(animData.skeleton);
+ ctrl.setAnimations(anims);
+ model.addControl(ctrl);
+
+ // Put the skeleton in the skeleton control
+ SkeletonControl skeletonControl = new SkeletonControl(animData.skeleton);
+
+ // This will acquire the targets from the node
+ model.addControl(skeletonControl);
+ }
+
+ return model;
+ }
+
+ public Object load(AssetInfo info) throws IOException {
+ try {
+ key = info.getKey();
+ meshName = key.getName();
+ folderName = key.getFolder();
+ String ext = key.getExtension();
+ meshName = meshName.substring(0, meshName.length() - ext.length() - 1);
+ if (folderName != null && folderName.length() > 0) {
+ meshName = meshName.substring(folderName.length());
+ }
+ assetManager = info.getManager();
+
+ if (key instanceof OgreMeshKey) {
+ // OgreMeshKey is being used, try getting the material list
+ // from it
+ OgreMeshKey meshKey = (OgreMeshKey) key;
+ materialList = meshKey.getMaterialList();
+ String materialName = meshKey.getMaterialName();
+
+ // Material list not set but material name is available
+ if (materialList == null && materialName != null) {
+ OgreMaterialKey materialKey = new OgreMaterialKey(folderName + materialName + ".material");
+ try {
+ materialList = (MaterialList) assetManager.loadAsset(materialKey);
+ } catch (AssetNotFoundException e) {
+ logger.log(Level.WARNING, "Cannot locate {0} for model {1}", new Object[]{materialKey, key});
+ }
+ }
+ }else{
+ // Make sure to reset it to null so that previous state
+ // doesn't leak onto this one
+ materialList = null;
+ }
+
+ // If for some reason material list could not be found through
+ // OgreMeshKey, or if regular ModelKey specified, load using
+ // default method.
+ if (materialList == null){
+ OgreMaterialKey materialKey = new OgreMaterialKey(folderName + meshName + ".material");
+ try {
+ materialList = (MaterialList) assetManager.loadAsset(materialKey);
+ } catch (AssetNotFoundException e) {
+ logger.log(Level.WARNING, "Cannot locate {0} for model {1}", new Object[]{ materialKey, key });
+ }
+ }
+
+ // Added by larynx 25.06.2011
+ // Android needs the namespace aware flag set to true
+ // Kirill 30.06.2011
+ // Now, hack is applied for both desktop and android to avoid
+ // checking with JmeSystem.
+ SAXParserFactory factory = SAXParserFactory.newInstance();
+ factory.setNamespaceAware(true);
+
+ XMLReader xr = factory.newSAXParser().getXMLReader();
+ xr.setContentHandler(this);
+ xr.setErrorHandler(this);
+
+ InputStreamReader r = null;
+ try {
+ r = new InputStreamReader(info.openStream());
+ xr.parse(new InputSource(r));
+ } finally {
+ if (r != null){
+ r.close();
+ }
+ }
+
+ return compileModel();
+ } catch (SAXException ex) {
+ IOException ioEx = new IOException("Error while parsing Ogre3D mesh.xml");
+ ioEx.initCause(ex);
+ throw ioEx;
+ } catch (ParserConfigurationException ex) {
+ IOException ioEx = new IOException("Error while parsing Ogre3D mesh.xml");
+ ioEx.initCause(ex);
+ throw ioEx;
+ }
+
+ }
+}
diff --git a/engine/src/ogre/com/jme3/scene/plugins/ogre/OgreMeshKey.java b/engine/src/ogre/com/jme3/scene/plugins/ogre/OgreMeshKey.java
new file mode 100644
index 0000000..1141944
--- /dev/null
+++ b/engine/src/ogre/com/jme3/scene/plugins/ogre/OgreMeshKey.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2009-2010 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.jme3.scene.plugins.ogre;
+
+import com.jme3.asset.ModelKey;
+import com.jme3.material.MaterialList;
+
+/**
+ * OgreMeshKey is used to load Ogre3D mesh.xml models with a specific
+ * material file or list. This allows customizing from where the materials
+ * are retrieved, instead of loading the material file as the same
+ * name as the model (the default).
+ *
+ * @author Kirill Vainer
+ */
+public class OgreMeshKey extends ModelKey {
+
+ private MaterialList materialList;
+ private String materialName;
+
+ public OgreMeshKey(){
+ super();
+ }
+
+ public OgreMeshKey(String name){
+ super(name);
+ }
+
+ public OgreMeshKey(String name, MaterialList materialList){
+ super(name);
+ this.materialList = materialList;
+ }
+
+ public OgreMeshKey(String name, String materialName){
+ super(name);
+ this.materialName = materialName;
+ }
+
+ public MaterialList getMaterialList() {
+ return materialList;
+ }
+
+ public void setMaterialList(MaterialList materialList){
+ this.materialList = materialList;
+ }
+
+ public String getMaterialName() {
+ return materialName;
+ }
+
+ public void setMaterialName(String name) {
+ materialName = name;
+ }
+
+}
diff --git a/engine/src/ogre/com/jme3/scene/plugins/ogre/SceneLoader.java b/engine/src/ogre/com/jme3/scene/plugins/ogre/SceneLoader.java
new file mode 100644
index 0000000..b59275e
--- /dev/null
+++ b/engine/src/ogre/com/jme3/scene/plugins/ogre/SceneLoader.java
@@ -0,0 +1,468 @@
+/*
+ * Copyright (c) 2009-2010 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.jme3.scene.plugins.ogre;
+
+import com.jme3.asset.*;
+import com.jme3.light.DirectionalLight;
+import com.jme3.light.Light;
+import com.jme3.light.PointLight;
+import com.jme3.light.SpotLight;
+import com.jme3.material.MaterialList;
+import com.jme3.math.Quaternion;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Spatial;
+import com.jme3.scene.plugins.ogre.matext.OgreMaterialKey;
+import com.jme3.util.PlaceholderAssets;
+import com.jme3.util.xml.SAXUtil;
+import static com.jme3.util.xml.SAXUtil.*;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.Stack;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParserFactory;
+import org.xml.sax.Attributes;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.XMLReader;
+import org.xml.sax.helpers.DefaultHandler;
+
+public class SceneLoader extends DefaultHandler implements AssetLoader {
+
+ private static final Logger logger = Logger.getLogger(SceneLoader.class.getName());
+
+ private Stack<String> elementStack = new Stack<String>();
+ private AssetKey key;
+ private String sceneName;
+ private String folderName;
+ private AssetManager assetManager;
+ private MaterialList materialList;
+ private com.jme3.scene.Node root;
+ private com.jme3.scene.Node node;
+ private com.jme3.scene.Node entityNode;
+ private Light light;
+ private int nodeIdx = 0;
+ private static volatile int sceneIdx = 0;
+
+ public SceneLoader(){
+ super();
+ }
+
+ @Override
+ public void startDocument() {
+ }
+
+ @Override
+ public void endDocument() {
+ }
+
+ private void reset(){
+ elementStack.clear();
+ nodeIdx = 0;
+
+ // NOTE: Setting some of those to null is only needed
+ // if the parsed file had an error e.g. startElement was called
+ // but not endElement
+ root = null;
+ node = null;
+ entityNode = null;
+ light = null;
+ }
+
+ private void checkTopNode(String topNode) throws SAXException{
+ if (!elementStack.peek().equals(topNode)){
+ throw new SAXException("dotScene parse error: Expected parent node to be " + topNode);
+ }
+ }
+
+ private Quaternion parseQuat(Attributes attribs) throws SAXException{
+ if (attribs.getValue("x") != null){
+ // defined as quaternion
+ float x = parseFloat(attribs.getValue("x"));
+ float y = parseFloat(attribs.getValue("y"));
+ float z = parseFloat(attribs.getValue("z"));
+ float w = parseFloat(attribs.getValue("w"));
+ return new Quaternion(x,y,z,w);
+ }else if (attribs.getValue("qx") != null){
+ // defined as quaternion with prefix "q"
+ float x = parseFloat(attribs.getValue("qx"));
+ float y = parseFloat(attribs.getValue("qy"));
+ float z = parseFloat(attribs.getValue("qz"));
+ float w = parseFloat(attribs.getValue("qw"));
+ return new Quaternion(x,y,z,w);
+ }else if (attribs.getValue("angle") != null){
+ // defined as angle + axis
+ float angle = parseFloat(attribs.getValue("angle"));
+ float axisX = parseFloat(attribs.getValue("axisX"));
+ float axisY = parseFloat(attribs.getValue("axisY"));
+ float axisZ = parseFloat(attribs.getValue("axisZ"));
+ Quaternion q = new Quaternion();
+ q.fromAngleAxis(angle, new Vector3f(axisX, axisY, axisZ));
+ return q;
+ }else{
+ // defines as 3 angles along XYZ axes
+ float angleX = parseFloat(attribs.getValue("angleX"));
+ float angleY = parseFloat(attribs.getValue("angleY"));
+ float angleZ = parseFloat(attribs.getValue("angleZ"));
+ Quaternion q = new Quaternion();
+ q.fromAngles(angleX, angleY, angleZ);
+ return q;
+ }
+ }
+
+ private void parseLightNormal(Attributes attribs) throws SAXException {
+ checkTopNode("light");
+
+ // SpotLight will be supporting a direction-normal, too.
+ if (light instanceof DirectionalLight)
+ ((DirectionalLight) light).setDirection(parseVector3(attribs));
+ else if (light instanceof SpotLight){
+ ((SpotLight) light).setDirection(parseVector3(attribs));
+ }
+ }
+
+ private void parseLightAttenuation(Attributes attribs) throws SAXException {
+ // NOTE: Derives range based on "linear" if it is used solely
+ // for the attenuation. Otherwise derives it from "range"
+ checkTopNode("light");
+
+ if (light instanceof PointLight || light instanceof SpotLight){
+ float range = parseFloat(attribs.getValue("range"));
+ float constant = parseFloat(attribs.getValue("constant"));
+ float linear = parseFloat(attribs.getValue("linear"));
+
+ String quadraticStr = attribs.getValue("quadratic");
+ if (quadraticStr == null)
+ quadraticStr = attribs.getValue("quadric");
+
+ float quadratic = parseFloat(quadraticStr);
+
+ if (constant == 1 && quadratic == 0 && linear > 0){
+ range = 1f / linear;
+ }
+
+ if (light instanceof PointLight){
+ ((PointLight) light).setRadius(range);
+ }else{
+ ((SpotLight)light).setSpotRange(range);
+ }
+ }
+ }
+
+ private void parseLightSpotLightRange(Attributes attribs) throws SAXException{
+ checkTopNode("light");
+
+ float outer = SAXUtil.parseFloat(attribs.getValue("outer"));
+ float inner = SAXUtil.parseFloat(attribs.getValue("inner"));
+
+ if (!(light instanceof SpotLight)){
+ throw new SAXException("dotScene parse error: spotLightRange "
+ + "can only appear under 'spot' light elements");
+ }
+
+ SpotLight sl = (SpotLight) light;
+ sl.setSpotInnerAngle(inner * 0.5f);
+ sl.setSpotOuterAngle(outer * 0.5f);
+ }
+
+ private void parseLight(Attributes attribs) throws SAXException {
+ if (node == null || node.getParent() == null)
+ throw new SAXException("dotScene parse error: light can only appear under a node");
+
+ checkTopNode("node");
+
+ String lightType = parseString(attribs.getValue("type"), "point");
+ if(lightType.equals("point")) {
+ light = new PointLight();
+ } else if(lightType.equals("directional") || lightType.equals("sun")) {
+ light = new DirectionalLight();
+ // Assuming "normal" property is not provided
+ ((DirectionalLight)light).setDirection(Vector3f.UNIT_Z);
+ } else if(lightType.equals("spotLight") || lightType.equals("spot")) {
+ light = new SpotLight();
+ } else {
+ logger.log(Level.WARNING, "No matching jME3 LightType found for OGRE LightType: {0}", lightType);
+ }
+ logger.log(Level.FINEST, "{0} created.", light);
+
+ if (!parseBool(attribs.getValue("visible"), true)){
+ // set to disabled
+ }
+
+ // "attach" it to the parent of this node
+ if (light != null)
+ node.getParent().addLight(light);
+ }
+
+ @Override
+ public void startElement(String uri, String localName, String qName, Attributes attribs) throws SAXException{
+ if (qName.equals("scene")){
+ if (elementStack.size() != 0){
+ throw new SAXException("dotScene parse error: 'scene' element must be the root XML element");
+ }
+
+ String version = attribs.getValue("formatVersion");
+ if (version == null || (!version.equals("1.0.0") && !version.equals("1.0.1")))
+ logger.log(Level.WARNING, "Unrecognized version number"
+ + " in dotScene file: {0}", version);
+
+ }else if (qName.equals("nodes")){
+ if (root != null){
+ throw new SAXException("dotScene parse error: nodes element was specified twice");
+ }
+ if (sceneName == null)
+ root = new com.jme3.scene.Node("OgreDotScene"+(++sceneIdx));
+ else
+ root = new com.jme3.scene.Node(sceneName+"-scene_node");
+
+ node = root;
+ }else if (qName.equals("externals")){
+ checkTopNode("scene");
+ // Not loaded currently
+ }else if (qName.equals("item")){
+ checkTopNode("externals");
+ }else if (qName.equals("file")){
+ checkTopNode("item");
+
+ // XXX: Currently material file name is based
+ // on the scene's filename. THIS IS NOT CORRECT.
+ // To solve, port SceneLoader to use DOM instead of SAX
+
+ //String matFile = folderName+attribs.getValue("name");
+ //try {
+ // materialList = (MaterialList) assetManager.loadAsset(new OgreMaterialKey(matFile));
+ //} catch (AssetNotFoundException ex){
+ // materialList = null;
+ // logger.log(Level.WARNING, "Cannot locate material file: {0}", matFile);
+ //}
+ }else if (qName.equals("node")){
+ String curElement = elementStack.peek();
+ if (!curElement.equals("node") && !curElement.equals("nodes")){
+ throw new SAXException("dotScene parse error: "
+ + "node element can only appear under 'node' or 'nodes'");
+ }
+
+ String name = attribs.getValue("name");
+ if (name == null)
+ name = "OgreNode-" + (++nodeIdx);
+
+ com.jme3.scene.Node newNode = new com.jme3.scene.Node(name);
+ if (node != null){
+ node.attachChild(newNode);
+ }
+ node = newNode;
+ }else if (qName.equals("property")){
+ if (node != null){
+ String type = attribs.getValue("type");
+ String name = attribs.getValue("name");
+ String data = attribs.getValue("data");
+ if (type.equals("BOOL")){
+ node.setUserData(name, Boolean.parseBoolean(data)||data.equals("1"));
+ }else if (type.equals("FLOAT")){
+ node.setUserData(name, Float.parseFloat(data));
+ }else if (type.equals("STRING")){
+ node.setUserData(name, data);
+ }else if (type.equals("INT")){
+ node.setUserData(name, Integer.parseInt(data));
+ }
+ }
+ }else if (qName.equals("entity")){
+ checkTopNode("node");
+
+ String name = attribs.getValue("name");
+ if (name == null)
+ name = "OgreEntity-" + (++nodeIdx);
+ else
+ name += "-entity";
+
+ String meshFile = attribs.getValue("meshFile");
+ if (meshFile == null) {
+ throw new SAXException("Required attribute 'meshFile' missing for 'entity' node");
+ }
+
+ // TODO: Not currently used
+ String materialName = attribs.getValue("materialName");
+
+ if (folderName != null) {
+ meshFile = folderName + meshFile;
+ }
+
+ // NOTE: append "xml" since its assumed mesh files are binary in dotScene
+ meshFile += ".xml";
+
+ entityNode = new com.jme3.scene.Node(name);
+ OgreMeshKey meshKey = new OgreMeshKey(meshFile, materialList);
+ try {
+ Spatial ogreMesh = assetManager.loadModel(meshKey);
+ entityNode.attachChild(ogreMesh);
+ } catch (AssetNotFoundException ex){
+ logger.log(Level.WARNING, "Cannot locate {0} for scene {1}", new Object[]{meshKey, key});
+ // Attach placeholder asset.
+ entityNode.attachChild(PlaceholderAssets.getPlaceholderModel(assetManager));
+ }
+
+ node.attachChild(entityNode);
+ node = null;
+ }else if (qName.equals("position")){
+ if (elementStack.peek().equals("node")){
+ node.setLocalTranslation(SAXUtil.parseVector3(attribs));
+ }
+ }else if (qName.equals("quaternion") || qName.equals("rotation")){
+ node.setLocalRotation(parseQuat(attribs));
+ }else if (qName.equals("scale")){
+ node.setLocalScale(SAXUtil.parseVector3(attribs));
+ } else if (qName.equals("light")) {
+ parseLight(attribs);
+ } else if (qName.equals("colourDiffuse") || qName.equals("colorDiffuse")) {
+ if (elementStack.peek().equals("light")){
+ if (light != null){
+ light.setColor(parseColor(attribs));
+ }
+ }else{
+ checkTopNode("environment");
+ }
+ } else if (qName.equals("normal") || qName.equals("direction")) {
+ checkTopNode("light");
+ parseLightNormal(attribs);
+ } else if (qName.equals("lightAttenuation")) {
+ parseLightAttenuation(attribs);
+ } else if (qName.equals("spotLightRange") || qName.equals("lightRange")) {
+ parseLightSpotLightRange(attribs);
+ }
+
+ elementStack.push(qName);
+ }
+
+ @Override
+ public void endElement(String uri, String name, String qName) throws SAXException {
+ if (qName.equals("node")){
+ node = node.getParent();
+ }else if (qName.equals("nodes")){
+ node = null;
+ }else if (qName.equals("entity")){
+ node = entityNode.getParent();
+ entityNode = null;
+ }else if (qName.equals("light")){
+ // apply the node's world transform on the light..
+ root.updateGeometricState();
+ if (light != null){
+ if (light instanceof DirectionalLight){
+ DirectionalLight dl = (DirectionalLight) light;
+ Quaternion q = node.getWorldRotation();
+ Vector3f dir = dl.getDirection();
+ q.multLocal(dir);
+ dl.setDirection(dir);
+ }else if (light instanceof PointLight){
+ PointLight pl = (PointLight) light;
+ Vector3f pos = node.getWorldTranslation();
+ pl.setPosition(pos);
+ }else if (light instanceof SpotLight){
+ SpotLight sl = (SpotLight) light;
+
+ Vector3f pos = node.getWorldTranslation();
+ sl.setPosition(pos);
+
+ Quaternion q = node.getWorldRotation();
+ Vector3f dir = sl.getDirection();
+ q.multLocal(dir);
+ sl.setDirection(dir);
+ }
+ }
+ light = null;
+ }
+ checkTopNode(qName);
+ elementStack.pop();
+ }
+
+ @Override
+ public void characters(char ch[], int start, int length) {
+ }
+
+
+
+ public Object load(AssetInfo info) throws IOException {
+ try{
+ key = info.getKey();
+ assetManager = info.getManager();
+ sceneName = key.getName();
+ String ext = key.getExtension();
+ folderName = key.getFolder();
+ sceneName = sceneName.substring(0, sceneName.length() - ext.length() - 1);
+
+ OgreMaterialKey materialKey = new OgreMaterialKey(sceneName+".material");
+ try {
+ materialList = (MaterialList) assetManager.loadAsset(materialKey);
+ } catch (AssetNotFoundException ex){
+ logger.log(Level.WARNING, "Cannot locate {0} for scene {1}", new Object[]{materialKey, key});
+ materialList = null;
+ }
+
+ reset();
+
+ // Added by larynx 25.06.2011
+ // Android needs the namespace aware flag set to true
+ // Kirill 30.06.2011
+ // Now, hack is applied for both desktop and android to avoid
+ // checking with JmeSystem.
+ SAXParserFactory factory = SAXParserFactory.newInstance();
+ factory.setNamespaceAware(true);
+ XMLReader xr = factory.newSAXParser().getXMLReader();
+
+ xr.setContentHandler(this);
+ xr.setErrorHandler(this);
+
+ InputStreamReader r = null;
+
+ try {
+ r = new InputStreamReader(info.openStream());
+ xr.parse(new InputSource(r));
+ } finally {
+ if (r != null){
+ r.close();
+ }
+ }
+
+ return root;
+ }catch (SAXException ex){
+ IOException ioEx = new IOException("Error while parsing Ogre3D dotScene");
+ ioEx.initCause(ex);
+ throw ioEx;
+ } catch (ParserConfigurationException ex) {
+ IOException ioEx = new IOException("Error while parsing Ogre3D dotScene");
+ ioEx.initCause(ex);
+ throw ioEx;
+ }
+ }
+
+}
diff --git a/engine/src/ogre/com/jme3/scene/plugins/ogre/SkeletonLoader.java b/engine/src/ogre/com/jme3/scene/plugins/ogre/SkeletonLoader.java
new file mode 100644
index 0000000..c96a7b7
--- /dev/null
+++ b/engine/src/ogre/com/jme3/scene/plugins/ogre/SkeletonLoader.java
@@ -0,0 +1,302 @@
+/*
+ * Copyright (c) 2009-2010 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.jme3.scene.plugins.ogre;
+
+import com.jme3.animation.Animation;
+import com.jme3.animation.Bone;
+import com.jme3.animation.BoneTrack;
+import com.jme3.animation.Skeleton;
+import com.jme3.asset.AssetInfo;
+import com.jme3.asset.AssetLoader;
+import com.jme3.asset.AssetManager;
+import com.jme3.math.Quaternion;
+import com.jme3.math.Vector3f;
+import com.jme3.util.xml.SAXUtil;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Stack;
+import java.util.logging.Logger;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParserFactory;
+import org.xml.sax.Attributes;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.XMLReader;
+import org.xml.sax.helpers.DefaultHandler;
+
+public class SkeletonLoader extends DefaultHandler implements AssetLoader {
+
+ private static final Logger logger = Logger.getLogger(SceneLoader.class.getName());
+ private AssetManager assetManager;
+ private Stack<String> elementStack = new Stack<String>();
+ private HashMap<Integer, Bone> indexToBone = new HashMap<Integer, Bone>();
+ private HashMap<String, Bone> nameToBone = new HashMap<String, Bone>();
+ private BoneTrack track;
+ private ArrayList<BoneTrack> tracks = new ArrayList<BoneTrack>();
+ private Animation animation;
+ private ArrayList<Animation> animations;
+ private Bone bone;
+ private Skeleton skeleton;
+ private ArrayList<Float> times = new ArrayList<Float>();
+ private ArrayList<Vector3f> translations = new ArrayList<Vector3f>();
+ private ArrayList<Quaternion> rotations = new ArrayList<Quaternion>();
+ private ArrayList<Vector3f> scales = new ArrayList<Vector3f>();
+ private float time = -1;
+ private Vector3f position;
+ private Quaternion rotation;
+ private Vector3f scale;
+ private float angle;
+ private Vector3f axis;
+
+ public void startElement(String uri, String localName, String qName, Attributes attribs) throws SAXException {
+ if (qName.equals("position") || qName.equals("translate")) {
+ position = SAXUtil.parseVector3(attribs);
+ } else if (qName.equals("rotation") || qName.equals("rotate")) {
+ angle = SAXUtil.parseFloat(attribs.getValue("angle"));
+ } else if (qName.equals("axis")) {
+ assert elementStack.peek().equals("rotation")
+ || elementStack.peek().equals("rotate");
+ axis = SAXUtil.parseVector3(attribs);
+ } else if (qName.equals("scale")) {
+ scale = SAXUtil.parseVector3(attribs);
+ } else if (qName.equals("keyframe")) {
+ assert elementStack.peek().equals("keyframes");
+ time = SAXUtil.parseFloat(attribs.getValue("time"));
+ } else if (qName.equals("keyframes")) {
+ assert elementStack.peek().equals("track");
+ } else if (qName.equals("track")) {
+ assert elementStack.peek().equals("tracks");
+ String boneName = SAXUtil.parseString(attribs.getValue("bone"));
+ Bone bone = nameToBone.get(boneName);
+ int index = skeleton.getBoneIndex(bone);
+ track = new BoneTrack(index);
+ } else if (qName.equals("boneparent")) {
+ assert elementStack.peek().equals("bonehierarchy");
+ String boneName = attribs.getValue("bone");
+ String parentName = attribs.getValue("parent");
+ Bone bone = nameToBone.get(boneName);
+ Bone parent = nameToBone.get(parentName);
+ parent.addChild(bone);
+ } else if (qName.equals("bone")) {
+ assert elementStack.peek().equals("bones");
+
+ // insert bone into indexed map
+ bone = new Bone(attribs.getValue("name"));
+ int id = SAXUtil.parseInt(attribs.getValue("id"));
+ indexToBone.put(id, bone);
+ nameToBone.put(bone.getName(), bone);
+ } else if (qName.equals("tracks")) {
+ assert elementStack.peek().equals("animation");
+ tracks.clear();
+ } else if (qName.equals("animation")) {
+ assert elementStack.peek().equals("animations");
+ String name = SAXUtil.parseString(attribs.getValue("name"));
+ float length = SAXUtil.parseFloat(attribs.getValue("length"));
+ animation = new Animation(name, length);
+ } else if (qName.equals("bonehierarchy")) {
+ assert elementStack.peek().equals("skeleton");
+ } else if (qName.equals("animations")) {
+ assert elementStack.peek().equals("skeleton");
+ animations = new ArrayList<Animation>();
+ } else if (qName.equals("bones")) {
+ assert elementStack.peek().equals("skeleton");
+ } else if (qName.equals("skeleton")) {
+ assert elementStack.size() == 0;
+ }
+ elementStack.add(qName);
+ }
+
+ public void endElement(String uri, String name, String qName) {
+ if (qName.equals("translate") || qName.equals("position") || qName.equals("scale")) {
+ } else if (qName.equals("axis")) {
+ } else if (qName.equals("rotate") || qName.equals("rotation")) {
+ rotation = new Quaternion();
+ axis.normalizeLocal();
+ rotation.fromAngleNormalAxis(angle, axis);
+ angle = 0;
+ axis = null;
+ } else if (qName.equals("bone")) {
+ bone.setBindTransforms(position, rotation, scale);
+ bone = null;
+ position = null;
+ rotation = null;
+ scale = null;
+ } else if (qName.equals("bonehierarchy")) {
+ Bone[] bones = new Bone[indexToBone.size()];
+ // find bones without a parent and attach them to the skeleton
+ // also assign the bones to the bonelist
+ for (Map.Entry<Integer, Bone> entry : indexToBone.entrySet()) {
+ Bone bone = entry.getValue();
+ bones[entry.getKey()] = bone;
+ }
+ indexToBone.clear();
+ skeleton = new Skeleton(bones);
+ } else if (qName.equals("animation")) {
+ animations.add(animation);
+ animation = null;
+ } else if (qName.equals("track")) {
+ if (track != null) { // if track has keyframes
+ tracks.add(track);
+ track = null;
+ }
+ } else if (qName.equals("tracks")) {
+ BoneTrack[] trackList = tracks.toArray(new BoneTrack[tracks.size()]);
+ animation.setTracks(trackList);
+ tracks.clear();
+ } else if (qName.equals("keyframe")) {
+ assert time >= 0;
+ assert position != null;
+ assert rotation != null;
+
+ times.add(time);
+ translations.add(position);
+ rotations.add(rotation);
+ if (scale != null) {
+ scales.add(scale);
+ }else{
+ scales.add(new Vector3f(1,1,1));
+ }
+
+ time = -1;
+ position = null;
+ rotation = null;
+ scale = null;
+ } else if (qName.equals("keyframes")) {
+ if (times.size() > 0) {
+ float[] timesArray = new float[times.size()];
+ for (int i = 0; i < timesArray.length; i++) {
+ timesArray[i] = times.get(i);
+ }
+
+ Vector3f[] transArray = translations.toArray(new Vector3f[translations.size()]);
+ Quaternion[] rotArray = rotations.toArray(new Quaternion[rotations.size()]);
+ Vector3f[] scalesArray = scales.toArray(new Vector3f[scales.size()]);
+
+ track.setKeyframes(timesArray, transArray, rotArray, scalesArray);
+ //track.setKeyframes(timesArray, transArray, rotArray);
+ } else {
+ track = null;
+ }
+
+ times.clear();
+ translations.clear();
+ rotations.clear();
+ scales.clear();
+ } else if (qName.equals("skeleton")) {
+ nameToBone.clear();
+ }
+ assert elementStack.peek().equals(qName);
+ elementStack.pop();
+ }
+
+ /**
+ * Reset the SkeletonLoader in case an error occured while parsing XML.
+ * This allows future use of the loader even after an error.
+ */
+ private void fullReset() {
+ elementStack.clear();
+ indexToBone.clear();
+ nameToBone.clear();
+ track = null;
+ tracks.clear();
+ animation = null;
+ if (animations != null) {
+ animations.clear();
+ }
+
+ bone = null;
+ skeleton = null;
+ times.clear();
+ rotations.clear();
+ translations.clear();
+ time = -1;
+ position = null;
+ rotation = null;
+ scale = null;
+ angle = 0;
+ axis = null;
+ }
+
+ public Object load(InputStream in) throws IOException {
+ try {
+
+ // Added by larynx 25.06.2011
+ // Android needs the namespace aware flag set to true
+ // Kirill 30.06.2011
+ // Now, hack is applied for both desktop and android to avoid
+ // checking with JmeSystem.
+ SAXParserFactory factory = SAXParserFactory.newInstance();
+ factory.setNamespaceAware(true);
+ XMLReader xr = factory.newSAXParser().getXMLReader();
+
+ xr.setContentHandler(this);
+ xr.setErrorHandler(this);
+ InputStreamReader r = new InputStreamReader(in);
+ xr.parse(new InputSource(r));
+ if (animations == null) {
+ animations = new ArrayList<Animation>();
+ }
+ AnimData data = new AnimData(skeleton, animations);
+ skeleton = null;
+ animations = null;
+ return data;
+ } catch (SAXException ex) {
+ IOException ioEx = new IOException("Error while parsing Ogre3D dotScene");
+ ioEx.initCause(ex);
+ fullReset();
+ throw ioEx;
+ } catch (ParserConfigurationException ex) {
+ IOException ioEx = new IOException("Error while parsing Ogre3D dotScene");
+ ioEx.initCause(ex);
+ fullReset();
+ throw ioEx;
+ }
+
+ }
+
+ public Object load(AssetInfo info) throws IOException {
+ assetManager = info.getManager();
+ InputStream in = null;
+ try {
+ in = info.openStream();
+ return load(in);
+ } finally {
+ if (in != null){
+ in.close();
+ }
+ }
+ }
+}
diff --git a/engine/src/ogre/com/jme3/scene/plugins/ogre/matext/MaterialExtension.java b/engine/src/ogre/com/jme3/scene/plugins/ogre/matext/MaterialExtension.java
new file mode 100644
index 0000000..d68b7ae
--- /dev/null
+++ b/engine/src/ogre/com/jme3/scene/plugins/ogre/matext/MaterialExtension.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2009-2010 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.jme3.scene.plugins.ogre.matext;
+
+import java.util.HashMap;
+
+/**
+ * <code>MaterialExtension</code> defines a mapping from an Ogre3D "base" material
+ * to a jME3 material definition.
+ */
+public class MaterialExtension {
+
+ private String baseMatName;
+ private String jmeMatDefName;
+ private HashMap<String, String> textureMappings = new HashMap<String, String>();
+
+ /**
+ * Material extension defines a mapping from an Ogre3D "base" material
+ * to a jME3 material definition.
+ *
+ * @param baseMatName The base material name for Ogre3D
+ * @param jmeMatDefName The material definition name for jME3
+ */
+ public MaterialExtension(String baseMatName, String jmeMatDefName) {
+ this.baseMatName = baseMatName;
+ this.jmeMatDefName = jmeMatDefName;
+ }
+
+ public String getBaseMaterialName() {
+ return baseMatName;
+ }
+
+ public String getJmeMatDefName() {
+ return jmeMatDefName;
+ }
+
+ /**
+ * Set mapping from an Ogre3D base material texture alias to a
+ * jME3 texture param
+ * @param ogreTexAlias The texture alias in the Ogre3D base material
+ * @param jmeTexParam The texture param name in the jME3 material definition.
+ */
+ public void setTextureMapping(String ogreTexAlias, String jmeTexParam){
+ textureMappings.put(ogreTexAlias, jmeTexParam);
+ }
+
+ /**
+ * Retreives a mapping from an Ogre3D base material texture alias
+ * to a jME3 texture param
+ * @param ogreTexAlias The texture alias in the Ogre3D base material
+ * @return The texture alias in the Ogre3D base material
+ */
+ public String getTextureMapping(String ogreTexAlias){
+ return textureMappings.get(ogreTexAlias);
+ }
+}
diff --git a/engine/src/ogre/com/jme3/scene/plugins/ogre/matext/MaterialExtensionLoader.java b/engine/src/ogre/com/jme3/scene/plugins/ogre/matext/MaterialExtensionLoader.java
new file mode 100644
index 0000000..d497d51
--- /dev/null
+++ b/engine/src/ogre/com/jme3/scene/plugins/ogre/matext/MaterialExtensionLoader.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright (c) 2009-2010 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.jme3.scene.plugins.ogre.matext;
+
+import com.jme3.asset.AssetKey;
+import com.jme3.asset.AssetManager;
+import com.jme3.asset.AssetNotFoundException;
+import com.jme3.asset.TextureKey;
+import com.jme3.material.Material;
+import com.jme3.material.MaterialList;
+import com.jme3.scene.plugins.ogre.MaterialLoader;
+import com.jme3.texture.Texture;
+import com.jme3.texture.Texture.WrapMode;
+import com.jme3.texture.Texture2D;
+import com.jme3.util.PlaceholderAssets;
+import com.jme3.util.blockparser.Statement;
+import java.io.IOException;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Used internally by {@link MaterialLoader}
+ */
+public class MaterialExtensionLoader {
+
+ private static final Logger logger = Logger.getLogger(MaterialExtensionLoader.class.getName());
+
+ private AssetKey key;
+ private AssetManager assetManager;
+ private MaterialList list;
+ private MaterialExtensionSet matExts;
+ private MaterialExtension matExt;
+ private String matName;
+ private Material material;
+
+
+ private void readExtendingMaterialStatement(Statement statement) throws IOException {
+ if (statement.getLine().startsWith("set_texture_alias")){
+ String[] split = statement.getLine().split(" ", 3);
+ String aliasName = split[1];
+ String texturePath = split[2];
+
+ String jmeParamName = matExt.getTextureMapping(aliasName);
+
+ TextureKey texKey = new TextureKey(texturePath, false);
+ texKey.setGenerateMips(true);
+ texKey.setAsCube(false);
+ Texture tex;
+
+ try {
+ tex = assetManager.loadTexture(texKey);
+ tex.setWrap(WrapMode.Repeat);
+ } catch (AssetNotFoundException ex){
+ logger.log(Level.WARNING, "Cannot locate {0} for material {1}", new Object[]{texKey, key});
+ tex = new Texture2D( PlaceholderAssets.getPlaceholderImage() );
+ }
+
+ material.setTexture(jmeParamName, tex);
+ }
+ }
+
+ private Material readExtendingMaterial(Statement statement) throws IOException{
+ String[] split = statement.getLine().split(" ", 2);
+ String[] subsplit = split[1].split(":");
+ matName = subsplit[0].trim();
+ String extendedMat = subsplit[1].trim();
+
+ matExt = matExts.getMaterialExtension(extendedMat);
+ if (matExt == null){
+ logger.log(Level.WARNING, "Cannot find MaterialExtension for: {0}. Ignoring material.", extendedMat);
+ matExt = null;
+ return null;
+ }
+
+ material = new Material(assetManager, matExt.getJmeMatDefName());
+ for (Statement extMatStat : statement.getContents()){
+ readExtendingMaterialStatement(extMatStat);
+ }
+ return material;
+ }
+
+ public MaterialList load(AssetManager assetManager, AssetKey key, MaterialExtensionSet matExts,
+ List<Statement> statements) throws IOException{
+ this.assetManager = assetManager;
+ this.matExts = matExts;
+ this.key = key;
+
+ list = new MaterialList();
+
+ for (Statement statement : statements){
+ if (statement.getLine().startsWith("import")){
+ // ignore
+ continue;
+ }else if (statement.getLine().startsWith("material")){
+ Material material = readExtendingMaterial(statement);
+ list.put(matName, material);
+ List<String> matAliases = matExts.getNameMappings(matName);
+ if(matAliases != null){
+ for (String string : matAliases) {
+ list.put(string, material);
+ }
+ }
+ }
+ }
+ return list;
+ }
+}
diff --git a/engine/src/ogre/com/jme3/scene/plugins/ogre/matext/MaterialExtensionSet.java b/engine/src/ogre/com/jme3/scene/plugins/ogre/matext/MaterialExtensionSet.java
new file mode 100644
index 0000000..2c81e7b
--- /dev/null
+++ b/engine/src/ogre/com/jme3/scene/plugins/ogre/matext/MaterialExtensionSet.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2009-2010 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.jme3.scene.plugins.ogre.matext;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+/**
+ * <code>MaterialExtensionSet</code> is simply a container for several
+ * {@link MaterialExtension}s so that it can be set globally for all
+ * {@link OgreMaterialKey}s used.
+ */
+public class MaterialExtensionSet {
+ private HashMap<String, MaterialExtension> extensions
+ = new HashMap<String, MaterialExtension>();
+ private HashMap<String, List<String>> nameMappings = new HashMap<String, List<String>>();
+
+ /**
+ * Adds a new material extension to the set of extensions.
+ * @param extension The {@link MaterialExtension} to add.
+ */
+ public void addMaterialExtension(MaterialExtension extension){
+ extensions.put(extension.getBaseMaterialName(), extension);
+ }
+
+ /**
+ * Returns the {@link MaterialExtension} for a given Ogre3D base
+ * material name.
+ *
+ * @param baseMatName The ogre3D base material name.
+ * @return {@link MaterialExtension} that is set, or null if not set.
+ */
+ public MaterialExtension getMaterialExtension(String baseMatName){
+ return extensions.get(baseMatName);
+ }
+
+ /**
+ * Adds an alternative name for a material
+ * @param name The material name to be found in a .mesh.xml file
+ * @param alias The material name to be found in a .material file
+ */
+ public void setNameMapping(String name, String alias){
+ List<String> list = nameMappings.get(name);
+ if(list==null){
+ list = new ArrayList<String>();
+ nameMappings.put(name, list);
+ }
+ list.add(alias);
+ }
+
+ public List<String> getNameMappings(String name){
+ return nameMappings.get(name);
+ }
+}
diff --git a/engine/src/ogre/com/jme3/scene/plugins/ogre/matext/OgreMaterialKey.java b/engine/src/ogre/com/jme3/scene/plugins/ogre/matext/OgreMaterialKey.java
new file mode 100644
index 0000000..fa57d48
--- /dev/null
+++ b/engine/src/ogre/com/jme3/scene/plugins/ogre/matext/OgreMaterialKey.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2009-2012 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.jme3.scene.plugins.ogre.matext;
+
+import com.jme3.asset.AssetKey;
+import com.jme3.material.MaterialList;
+
+/**
+ * <code>OgreMaterialKey</code> allows specifying material extensions,
+ * which map from Ogre3D base materials to jME3 materials
+ */
+public class OgreMaterialKey extends AssetKey<MaterialList> {
+
+ private MaterialExtensionSet matExts;
+
+ public OgreMaterialKey(String name){
+ super(name);
+ }
+
+ public OgreMaterialKey(){
+ super();
+ }
+
+ /**
+ * Set the {@link MaterialExtensionSet} to use for mapping
+ * base materials to jME3 matdefs when loading.
+ * Set to <code>null</code> to disable this functionality.
+ *
+ * @param matExts The {@link MaterialExtensionSet} to use
+ */
+ public void setMaterialExtensionSet(MaterialExtensionSet matExts){
+ this.matExts = matExts;
+ }
+
+ /**
+ * Returns the {@link MaterialExtensionSet} previously set using
+ * {@link OgreMaterialKey#setMaterialExtensionSet(com.jme3.scene.plugins.ogre.matext.MaterialExtensionSet) } method.
+ * @return
+ */
+ public MaterialExtensionSet getMaterialExtensionSet() {
+ return matExts;
+ }
+}
diff --git a/engine/src/ogre/com/jme3/scene/plugins/ogre/matext/package.html b/engine/src/ogre/com/jme3/scene/plugins/ogre/matext/package.html
new file mode 100644
index 0000000..0b2725c
--- /dev/null
+++ b/engine/src/ogre/com/jme3/scene/plugins/ogre/matext/package.html
@@ -0,0 +1,40 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+
+<head>
+<title></title>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+</head>
+<body>
+
+<code>com.jme3.scene.plugins.ogre.matext</code> allows loading of more advanced
+Ogre3D materials that use "base" materials to abstract functionality.
+<br/><br/>
+E.g. example of an Ogre3D material instance:<br/>
+<code>
+import * from "materials/baselighting.material"
+
+material MyMaterial : BaseLightingMaterial
+{
+ set_texture_alias MyTexture textures/mytex.png
+}
+</code>
+
+<h3>Usage</h3>
+
+<p>
+Example code of loading the above material:<br/>
+<code>
+MaterialExtensionSet matExts = new MaterialExtensionSet();<br/>
+MaterialExtension baseLightExt = new MaterialExtension("BaseLightingMaterial", <br/>
+ "Common/MatDefs/Light/Lighting.j3md");<br/>
+baseLightExt.setTextureMapping("MyTexture", "m_DiffuseMap");<br/>
+matExts.addMaterialExtension(baseLightExt);<br/>
+<br/>
+OgreMaterialKey matKey = new OgreMaterialKey("materials/mymaterial.material");<br/>
+matKey.setMaterialExtensionSet(matExts);<br/>
+MaterialList ogreMats = assetManager.loadAsset(matKey);<br/>
+</code>
+
+</body>
+</html>