diff options
author | Scott Barta <sbarta@google.com> | 2012-03-01 12:35:35 -0800 |
---|---|---|
committer | Scott Barta <sbarta@google.com> | 2012-03-01 12:40:08 -0800 |
commit | 59b2e6871c65f58fdad78cd7229c292f6a177578 (patch) | |
tree | 2d4e7bfc05b93f40b34675d77e403dd1c25efafd /engine/src/ogre | |
parent | f9b30489e75ac1eabc365064959804e99534f7ab (diff) | |
download | jmonkeyengine-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')
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> |