+ listDemos.add(new DemoLaunchEntry("jme3test.android.TestMotionPath", "Shows cinematics - see a teapot on its journey - model loading needs a long time - just let it load, looks like freezed"));
+ //listDemos.add(new DemoLaunchEntry("com.jme3.androiddemo.TestSimpleWater", "Post processors - not working correctly due to missing framebuffer support, looks interresting :)"));
+ * If the sky has only generated textures applied then they will have the following size (both width and height). If 2d textures are used then the generated
+ * textures will get their proper size.
+ */
+ protected int skyGeneratedTextureSize = 1000;
+ /** The radius of a shape that will be used while creating the generated texture for the sky. The higher it is the larger part of the texture will be seen. */
+ protected float skyGeneratedTextureRadius = 1;
+ /** The shape against which the generated texture for the sky will be created. */
+ * This field tells if the importer should optimise the use of textures or not. If set to true, then textures of the same mapping type will be merged together
+ * and textures that in the final result will never be visible - will be discarded.
+ */
+ protected boolean optimiseTextures;
+
+ /**
+ * Constructor used by serialization mechanisms.
+ */
+ public BlenderKey() {
+ }
+
+ /**
+ * Constructor. Creates a key for the given file name.
+ * @param name
+ * the name (path) of a file
+ */
+ public BlenderKey(String name) {
+ super(name);
+ }
+
+ /**
+ * This method returns frames per second amount. The default value is BlenderKey.DEFAULT_FPS = 25.
+ * @return the frames per second amount
+ */
+ public int getFps() {
+ return fps;
+ }
+
+ /**
+ * This method sets frames per second amount.
+ * @param fps
+ * the frames per second amount
+ */
+ public void setFps(int fps) {
+ this.fps = fps;
+ }
+
+ /**
+ * This method returns the face cull mode.
+ * @return the face cull mode
+ */
+ public FaceCullMode getFaceCullMode() {
+ return faceCullMode;
+ }
+
+ /**
+ * This method sets the face cull mode.
+ * @param faceCullMode
+ * the face cull mode
+ */
+ public void setFaceCullMode(FaceCullMode faceCullMode) {
+ this.faceCullMode = faceCullMode;
+ }
+
+ /**
+ * This method sets layers to be loaded.
+ * @param layersToLoad
+ * layers to be loaded
+ */
+ public void setLayersToLoad(int layersToLoad) {
+ this.layersToLoad = layersToLoad;
+ }
+
+ /**
+ * This method returns layers to be loaded.
+ * @return layers to be loaded
+ */
+ public int getLayersToLoad() {
+ return layersToLoad;
+ }
+
+ /**
+ * This method sets the properies loading policy.
+ * By default the value is true.
+ * @param loadObjectProperties
+ * true to load properties and false to suspend their loading
+ */
+ public void setLoadObjectProperties(boolean loadObjectProperties) {
+ * @return the radius of a shape that will be used while creating the generated texture for the sky, the higher it is the larger part of the texture will be seen
+ */
+ public float getSkyGeneratedTextureRadius() {
+ return skyGeneratedTextureRadius;
+ }
+
+ /**
+ * @param skyGeneratedTextureRadius
+ * the radius of a shape that will be used while creating the generated texture for the sky, the higher it is the larger part of the texture will be seen
+ */
+ public void setSkyGeneratedTextureRadius(float skyGeneratedTextureRadius) {
+ result = prime * result + (assetRootPath == null ? 0 : assetRootPath.hashCode());
+ result = prime * result + (defaultMaterial == null ? 0 : defaultMaterial.hashCode());
+ result = prime * result + (faceCullMode == null ? 0 : faceCullMode.hashCode());
+ result = prime * result + featuresToLoad;
+ result = prime * result + (fixUpAxis ? 1231 : 1237);
+ result = prime * result + fps;
+ result = prime * result + generatedTexturePPU;
+ result = prime * result + layersToLoad;
+ result = prime * result + (loadGeneratedTextures ? 1231 : 1237);
+ result = prime * result + (loadObjectProperties ? 1231 : 1237);
+ result = prime * result + (loadUnlinkedAssets ? 1231 : 1237);
+ result = prime * result + maxTextureSize;
+ result = prime * result + (mipmapGenerationMethod == null ? 0 : mipmapGenerationMethod.hashCode());
+ result = prime * result + (optimiseTextures ? 1231 : 1237);
+ result = prime * result + Float.floatToIntBits(skyGeneratedTextureRadius);
+ result = prime * result + (skyGeneratedTextureShape == null ? 0 : skyGeneratedTextureShape.hashCode());
+ result = prime * result + skyGeneratedTextureSize;
+ result = prime * result + (usedWorld == null ? 0 : usedWorld.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (!super.equals(obj)) {
+ return false;
+ }
+ if (this.getClass() != obj.getClass()) {
+ return false;
+ }
+ BlenderKey other = (BlenderKey) obj;
+ if (assetRootPath == null) {
+ if (other.assetRootPath != null) {
+ return false;
+ }
+ } else if (!assetRootPath.equals(other.assetRootPath)) {
+ return false;
+ }
+ if (defaultMaterial == null) {
+ if (other.defaultMaterial != null) {
+ return false;
+ }
+ } else if (!defaultMaterial.equals(other.defaultMaterial)) {
+ return false;
+ }
+ if (faceCullMode != other.faceCullMode) {
+ return false;
+ }
+ if (featuresToLoad != other.featuresToLoad) {
+ return false;
+ }
+ if (fixUpAxis != other.fixUpAxis) {
+ return false;
+ }
+ if (fps != other.fps) {
+ return false;
+ }
+ if (generatedTexturePPU != other.generatedTexturePPU) {
+ return false;
+ }
+ if (layersToLoad != other.layersToLoad) {
+ return false;
+ }
+ if (loadGeneratedTextures != other.loadGeneratedTextures) {
+ return false;
+ }
+ if (loadObjectProperties != other.loadObjectProperties) {
+ return false;
+ }
+ if (loadUnlinkedAssets != other.loadUnlinkedAssets) {
+ return false;
+ }
+ if (maxTextureSize != other.maxTextureSize) {
+ return false;
+ }
+ if (mipmapGenerationMethod != other.mipmapGenerationMethod) {
+ return false;
+ }
+ if (optimiseTextures != other.optimiseTextures) {
+ return false;
+ }
+ if (Float.floatToIntBits(skyGeneratedTextureRadius) != Float.floatToIntBits(other.skyGeneratedTextureRadius)) {
+ return false;
+ }
+ if (skyGeneratedTextureShape != other.skyGeneratedTextureShape) {
+ return false;
+ }
+ if (skyGeneratedTextureSize != other.skyGeneratedTextureSize) {
+ return false;
+ }
+ if (usedWorld == null) {
+ if (other.usedWorld != null) {
+ return false;
+ }
+ } else if (!usedWorld.equals(other.usedWorld)) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * This enum tells the importer if the mipmaps for textures will be generated by jme. <li>NEVER_GENERATE and ALWAYS_GENERATE are quite understandable <li>GENERATE_WHEN_NEEDED is an option that checks if the texture had 'Generate mipmaps' option set in blender, mipmaps are generated only when the option is set
+ if (propertyNames != null && propertyNames.size() > 0) {
+ for (String propertyName : propertyNames) {
+ Object value = properties.findValue(propertyName);
+ if (value instanceof Savable || value instanceof Boolean || value instanceof String || value instanceof Float || value instanceof Integer || value instanceof Long) {
+ * This class holds the basic data that describes a bone.
+ *
+ * @author Marcin Roguski (Kaelthas)
+ */
+public class BoneContext {
+ // the flags of the bone
+ public static final int CONNECTED_TO_PARENT = 0x10;
+
+ /**
+ * The bones' matrices have, unlike objects', the coordinate system identical to JME's (Y axis is UP, X to the right and Z toward us).
+ * So in order to have them loaded properly we need to transform their armature matrix (which blender sees as rotated) to make sure we get identical results.
+ */
+ public static final Matrix4f BONE_ARMATURE_TRANSFORMATION_MATRIX = new Matrix4f(1, 0, 0, 0, 0, 0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 1);
+
+ private BlenderContext blenderContext;
+ /** The OMA of the bone's armature object. */
+ private Long armatureObjectOMA;
+ /** The OMA of the model that owns the bone's skeleton. */
+ private Long skeletonOwnerOma;
+ /** The structure of the bone. */
+ private Structure boneStructure;
+ /** Bone's name. */
+ private String boneName;
+ /** The bone's flag. */
+ private int flag;
+ /** The bone's matrix in world space. */
+ private Matrix4f globalBoneMatrix;
+ /** The bone's matrix in the model space. */
+ private Matrix4f boneMatrixInModelSpace;
+ /** The parent context. */
+ private BoneContext parent;
+ /** The children of this context. */
+ private List<BoneContext> children = new ArrayList<BoneContext>();
+ /** Created bone (available after calling 'buildBone' method). */
+ private Bone bone;
+ /** The length of the bone. */
+ private float length;
+
+ /**
+ * Constructor. Creates the basic set of bone's data.
+ *
+ * @param armatureObjectOMA
+ * the OMA of the bone's armature object
+ * @param boneStructure
+ * the bone's structure
+ * @param blenderContext
+ * the blender context
+ * @throws BlenderFileException
+ * an exception is thrown when problem with blender data reading
+ * This method returns the frame where last bezier triple center point of
+ * the specified bezier curve is located.
+ *
+ * @return the frame number of the last defined bezier triple point for the
+ * specified ipo
+ */
+ public int getLastFrame() {
+ int result = 1;
+ for (int i = 0; i < bezierCurves.length; ++i) {
+ int tempResult = bezierCurves[i].getLastFrame();
+ if (tempResult > result) {
+ result = tempResult;
+ }
+ }
+ return result;
+ }
+
+ /**
+ * This method calculates the value of the curves as a bone track between
+ * the specified frames.
+ *
+ * @param targetIndex
+ * the index of the target for which the method calculates the
+ * tracks IMPORTANT! Aet to -1 (or any negative number) if you
+ * want to load spatial animation.
+ * @param localTranslation
+ * the local translation of the object/bone that will be animated by
+ * the track
+ * @param localRotation
+ * the local rotation of the object/bone that will be animated by
+ * the track
+ * @param localScale
+ * the local scale of the object/bone that will be animated by
+ * the track
+ * @param startFrame
+ * the first frame of tracks (inclusive)
+ * @param stopFrame
+ * the last frame of the tracks (inclusive)
+ * @param fps
+ * frame rate (frames per second)
+ * @param spatialTrack
+ * this flag indicates if the track belongs to a spatial or to a
+ * bone; the difference is important because it appears that bones
+ * in blender have the same type of coordinate system (Y as UP)
+ * as jme while other features have different one (Z is UP)
+ * @return bone track for the specified bone
+ */
+ public Track calculateTrack(int targetIndex, Vector3f localTranslation, Quaternion localRotation, Vector3f localScale, int startFrame, int stopFrame, int fps, boolean spatialTrack) {
+ if (calculatedTrack == null) {
+ // preparing data for track
+ int framesAmount = stopFrame - startFrame;
+ float timeBetweenFrames = 1.0f / fps;
+
+ float[] times = new float[framesAmount + 1];
+ Vector3f[] translations = new Vector3f[framesAmount + 1];
+ * This method creates an ipo with only a single value. No track type is
+ * specified so do not use it for calculating tracks.
+ *
+ * @param constValue
+ * the value of this ipo
+ * @return constant ipo
+ */
+ public Ipo fromValue(float constValue) {
+ return new ConstIpo(constValue);
+ }
+
+ /**
+ * Ipo constant curve. This is a curve with only one value and no specified
+ * type. This type of ipo cannot be used to calculate tracks. It should only
+ * be used to calculate single value for a given frame.
+ *
+ * @author Marcin Roguski (Kaelthas)
+ */
+ private class ConstIpo extends Ipo {
+
+ /** The constant value of this ipo. */
+ private float constValue;
+
+ /**
+ * Constructor. Stores the constant value of this ipo.
+ *
+ * @param constValue
+ * the constant value of this ipo
+ */
+ public ConstIpo(float constValue) {
+ super(null, false, 0);// the version is not important here
+ this.constValue = constValue;
+ }
+
+ @Override
+ public float calculateValue(int frame) {
+ return constValue;
+ }
+
+ @Override
+ public float calculateValue(int frame, int curveIndex) {
+ return constValue;
+ }
+
+ @Override
+ public BoneTrack calculateTrack(int boneIndex, Vector3f localTranslation, Quaternion localRotation, Vector3f localScale, int startFrame, int stopFrame, int fps, boolean boneTrack) {
+ throw new IllegalStateException("Constatnt ipo object cannot be used for calculating bone tracks!");
+ if (targetOMA.longValue() != boneContext.getArmatureObjectOMA().longValue()) {
+ LOGGER.log(Level.WARNING, "Bone constraint {0} must target bone in the its own skeleton! Targeting bone in another skeleton is not supported!", name);
+ result = new Transform(boneMatrixInWorldSpace.toTranslationVector(), boneMatrixInWorldSpace.toRotationQuat(), boneMatrixInWorldSpace.toScaleVector());
+ break;
+ case CONSTRAINT_SPACE_LOCAL:
+ assert bone.getParent() != null : "CONSTRAINT_SPACE_LOCAL should be evaluated as CONSTRAINT_SPACE_POSE if the bone has no parent!";
+ result = new Transform(bone.getLocalPosition(), bone.getLocalRotation(), bone.getLocalScale());
+ if (constraint.isImplemented() && constraint.validate()) {
+ result.add(constraint);
+ } else {
+ LOGGER.log(Level.WARNING, "Constraint named: ''{0}'' of type ''{1}'' is not implemented and will NOT be applied!", new Object[] { constraint.name, constraint.getConstraintTypeName() });
+ }
+ }
+ }
+ return result.size() > 0 ? result : null;
+ }
+
+ /**
+ * Creates the initial transforms for all bones in the skelketon.
+ * @return the map where the key is the bone index and the value us the bone's initial transformation
+ throw new BlenderFileException("No data stored for address: " + oldMemoryAddress + ". Make sure you did not open the newer blender file with older blender version.");
+ if (loadedTextures != null && loadedTextures.size() > 0) {
+ int textureIndex = 0;
+ if (loadedTextures.size() > TextureHelper.TEXCOORD_TYPES.length) {
+ LOGGER.log(Level.WARNING, "The blender file has defined more than {0} different textures. JME supports only {0} UV mappings.", TextureHelper.TEXCOORD_TYPES.length);
+ }
+ for (CombinedTexture combinedTexture : loadedTextures) {
+ if (textureIndex < TextureHelper.TEXCOORD_TYPES.length) {
+ LOGGER.log(Level.WARNING, "The texture could not be applied because JME only supports up to {0} different UV's.", TextureHelper.TEXCOORD_TYPES.length);
+ LOGGER.fine("No textures found for the mesh, but UV coordinates are applied.");
+ int textureIndex = 0;
+ if (userDefinedUVCoordinates.size() > TextureHelper.TEXCOORD_TYPES.length) {
+ LOGGER.log(Level.WARNING, "The blender file has defined more than {0} different UV coordinates for the mesh. JME supports only {0} UV coordinates buffers.", TextureHelper.TEXCOORD_TYPES.length);
+ }
+ for (Entry<String, List<Vector2f>> entry : userDefinedUVCoordinates.entrySet()) {
+ if (textureIndex < TextureHelper.TEXCOORD_TYPES.length) {
+ List<Vector2f> uvs = entry.getValue();
+ VertexBuffer uvCoordsBuffer = new VertexBuffer(TextureHelper.TEXCOORD_TYPES[textureIndex++]);
+ LOGGER.log(Level.WARNING, "The texture could not be applied because JME only supports up to {0} different UV's.", TextureHelper.TEXCOORD_TYPES.length);
+ LOGGER.warning("The importer came accross mesh that points to a null material. Default material is used to prevent loader from crashing, " + "but the model might look not the way it should. Sometimes blender does not assign materials properly. " + "Enter the edit mode and assign materials once more to your faces.");
+ }
+ }
+ } else {
+ // add UV coordinates if they are defined even if the material is not applied to the model