diff --git a/README.md b/README.md index 5112b5efa..f55251ca0 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -jMonkeyEngine +jMonkeyEngine ============= [![Build Status](https://github.com/jMonkeyEngine/jmonkeyengine/workflows/Build%20jMonkeyEngine/badge.svg)](https://github.com/jMonkeyEngine/jmonkeyengine/actions) @@ -37,8 +37,8 @@ Note: The master branch on GitHub is a development version of the engine and is - NetBeans Platform - Gradle -Plus a bunch of awesome libraries & tight integrations like Bullet, Blender, NiftyGUI and other goodies. - +Plus a bunch of awesome libraries & tight integrations like Bullet, NiftyGUI and other goodies. + ### Documentation Did you miss it? Don't sweat it, [here it is again](https://jmonkeyengine.github.io/wiki). diff --git a/jme3-blender/build.gradle b/jme3-blender/build.gradle deleted file mode 100644 index a556d7a65..000000000 --- a/jme3-blender/build.gradle +++ /dev/null @@ -1,12 +0,0 @@ -if (!hasProperty('mainClass')) { - ext.mainClass = '' -} - -dependencies { - compile project(':jme3-core') - compile project(':jme3-desktop') - compile project(':jme3-effects') - compile ('org.ejml:core:0.27') - compile ('org.ejml:dense64:0.27') - compile ('org.ejml:simple:0.27') -} \ No newline at end of file diff --git a/jme3-blender/examples/src/main/java/jme3test/blender/TestBlenderLoader.java b/jme3-blender/examples/src/main/java/jme3test/blender/TestBlenderLoader.java deleted file mode 100644 index 0ff740bfb..000000000 --- a/jme3-blender/examples/src/main/java/jme3test/blender/TestBlenderLoader.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * 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 jme3test.blender; - -import com.jme3.app.SimpleApplication; -import com.jme3.light.DirectionalLight; -import com.jme3.math.ColorRGBA; -import com.jme3.math.Vector3f; -import com.jme3.scene.Spatial; - -public class TestBlenderLoader extends SimpleApplication { - - public static void main(String[] args){ - TestBlenderLoader app = new TestBlenderLoader(); - app.start(); - } - - @Override - public void simpleInitApp() { - viewPort.setBackgroundColor(ColorRGBA.DarkGray); - - //load model with packed images - Spatial ogre = assetManager.loadModel("Blender/2.4x/Sinbad.blend"); - rootNode.attachChild(ogre); - - //load model with referenced images - Spatial track = assetManager.loadModel("Blender/2.4x/MountainValley_Track.blend"); - rootNode.attachChild(track); - - // sunset light - DirectionalLight dl = new DirectionalLight(); - dl.setDirection(new Vector3f(-0.1f,-0.7f,1).normalizeLocal()); - dl.setColor(new ColorRGBA(0.44f, 0.30f, 0.20f, 1.0f)); - rootNode.addLight(dl); - - // skylight - dl = new DirectionalLight(); - dl.setDirection(new Vector3f(-0.6f,-1,-0.6f).normalizeLocal()); - dl.setColor(new ColorRGBA(0.10f, 0.22f, 0.44f, 1.0f)); - rootNode.addLight(dl); - - // white ambient light - dl = new DirectionalLight(); - dl.setDirection(new Vector3f(1, -0.5f,-0.1f).normalizeLocal()); - dl.setColor(new ColorRGBA(0.80f, 0.70f, 0.80f, 1.0f)); - rootNode.addLight(dl); - } - - @Override - public void simpleUpdate(float tpf){ - } - -} diff --git a/jme3-blender/examples/src/main/java/jme3test/light/TestTangentGenBadModels.java b/jme3-blender/examples/src/main/java/jme3test/light/TestTangentGenBadModels.java deleted file mode 100644 index 63f2c23aa..000000000 --- a/jme3-blender/examples/src/main/java/jme3test/light/TestTangentGenBadModels.java +++ /dev/null @@ -1,136 +0,0 @@ -package jme3test.light; - -import com.jme3.app.SimpleApplication; -import com.jme3.font.BitmapText; -import com.jme3.input.KeyInput; -import com.jme3.input.controls.ActionListener; -import com.jme3.input.controls.KeyTrigger; -import com.jme3.light.DirectionalLight; -import com.jme3.light.PointLight; -import com.jme3.material.Material; -import com.jme3.math.ColorRGBA; -import com.jme3.math.FastMath; -import com.jme3.math.Vector3f; -import com.jme3.renderer.queue.RenderQueue.Bucket; -import com.jme3.scene.*; -import com.jme3.scene.Spatial.CullHint; -import com.jme3.scene.shape.Sphere; -import com.jme3.util.TangentBinormalGenerator; - -/** - * - * @author Kirusha - */ -public class TestTangentGenBadModels extends SimpleApplication { - - float angle; - PointLight pl; - Geometry lightMdl; - - public static void main(String[] args){ - TestTangentGenBadModels app = new TestTangentGenBadModels(); - app.start(); - } - - @Override - public void simpleInitApp() { -// assetManager.registerLocator("http://jme-glsl-shaders.googlecode.com/hg/assets/Models/LightBlow/", UrlLocator.class); -// assetManager.registerLocator("http://jmonkeyengine.googlecode.com/files/", UrlLocator.class); - - final Spatial badModel = assetManager.loadModel("Models/TangentBugs/test.blend"); -// badModel.setLocalScale(1f); - - Material mat = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md"); - mat.setTexture("NormalMap", assetManager.loadTexture("Models/TangentBugs/test_normal.png")); -// Material mat = assetManager.loadMaterial("Textures/BumpMapTest/Tangent.j3m"); - badModel.setMaterial(mat); - rootNode.attachChild(badModel); - - // TODO: For some reason blender loader fails to load this. - // need to check it -// Spatial model = assetManager.loadModel("test.blend"); -// rootNode.attachChild(model); - - final Node debugTangents = new Node("debug tangents"); - debugTangents.setCullHint(CullHint.Always); - rootNode.attachChild(debugTangents); - - final Material debugMat = assetManager.loadMaterial("Common/Materials/VertexColor.j3m"); - - badModel.depthFirstTraversal(new SceneGraphVisitorAdapter(){ - @Override - public void visit(Geometry g){ - Mesh m = g.getMesh(); - Material mat = g.getMaterial(); - -// if (mat.getParam("DiffuseMap") != null){ -// mat.setTexture("DiffuseMap", null); -// } - TangentBinormalGenerator.generate(m); - - Geometry debug = new Geometry( - "debug tangents geom", - TangentBinormalGenerator.genTbnLines(g.getMesh(), 0.2f) - ); - debug.setMaterial(debugMat); - debug.setCullHint(Spatial.CullHint.Never); - debug.setLocalTransform(g.getWorldTransform()); - debugTangents.attachChild(debug); - } - }); - - DirectionalLight dl = new DirectionalLight(); - dl.setDirection(new Vector3f(-0.8f, -0.6f, -0.08f).normalizeLocal()); - dl.setColor(new ColorRGBA(1,1,1,1)); - rootNode.addLight(dl); - - lightMdl = new Geometry("Light", new Sphere(10, 10, 0.1f)); - lightMdl.setMaterial(assetManager.loadMaterial("Common/Materials/RedColor.j3m")); - lightMdl.getMesh().setStatic(); - rootNode.attachChild(lightMdl); - - pl = new PointLight(); - pl.setColor(ColorRGBA.White); -// rootNode.addLight(pl); - - - BitmapText info = new BitmapText(guiFont); - info.setText("Press SPACE to switch between lighting and tangent display"); - info.setQueueBucket(Bucket.Gui); - info.move(0, settings.getHeight() - info.getLineHeight(), 0); - rootNode.attachChild(info); - - inputManager.addMapping("space", new KeyTrigger(KeyInput.KEY_SPACE)); - inputManager.addListener(new ActionListener() { - - private boolean isLit = true; - - @Override - public void onAction(String name, boolean isPressed, float tpf) { - if (isPressed) return; - Material mat; - if (isLit){ - mat = assetManager.loadMaterial("Textures/BumpMapTest/Tangent.j3m"); - debugTangents.setCullHint(CullHint.Inherit); - }else{ - mat = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md"); - mat.setTexture("NormalMap", assetManager.loadTexture("Models/TangentBugs/test_normal.png")); - debugTangents.setCullHint(CullHint.Always); - } - isLit = !isLit; - badModel.setMaterial(mat); - } - }, "space"); - } - - @Override - public void simpleUpdate(float tpf){ - angle += tpf; - angle %= FastMath.TWO_PI; - - pl.setPosition(new Vector3f(FastMath.cos(angle) * 2f, 2f, FastMath.sin(angle) * 2f)); - lightMdl.setLocalTranslation(pl.getPosition()); - } - - -} diff --git a/jme3-blender/examples/src/main/java/jme3test/model/anim/TestBlenderAnim.java b/jme3-blender/examples/src/main/java/jme3test/model/anim/TestBlenderAnim.java deleted file mode 100644 index 4cf175da2..000000000 --- a/jme3-blender/examples/src/main/java/jme3test/model/anim/TestBlenderAnim.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright (c) 2009-2020 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 jme3test.model.anim; - -import com.jme3.anim.AnimClip; -import com.jme3.anim.AnimComposer; -import com.jme3.anim.util.AnimMigrationUtils; -import com.jme3.app.SimpleApplication; -import com.jme3.asset.BlenderKey; -import com.jme3.light.DirectionalLight; -import com.jme3.math.ColorRGBA; -import com.jme3.math.Quaternion; -import com.jme3.math.Vector3f; -import com.jme3.scene.Node; -import com.jme3.scene.Spatial; - -public class TestBlenderAnim extends SimpleApplication { - - public static void main(String[] args) { - TestBlenderAnim app = new TestBlenderAnim(); - app.start(); - } - - @Override - public void simpleInitApp() { - flyCam.setMoveSpeed(10f); - cam.setLocation(new Vector3f(6.4013605f, 7.488437f, 12.843031f)); - cam.setRotation(new Quaternion(-0.060740203f, 0.93925786f, -0.2398315f, -0.2378785f)); - - DirectionalLight dl = new DirectionalLight(); - dl.setDirection(new Vector3f(-0.1f, -0.7f, -1).normalizeLocal()); - dl.setColor(new ColorRGBA(1f, 1f, 1f, 1.0f)); - rootNode.addLight(dl); - - BlenderKey blenderKey = new BlenderKey("Blender/2.4x/BaseMesh_249.blend"); - - Spatial scene = assetManager.loadModel(blenderKey); - rootNode.attachChild(scene); - - Spatial model = this.findNode(rootNode, "BaseMesh_01"); - AnimMigrationUtils.migrate(model); - model.center(); - - AnimComposer animComposer = model.getControl(AnimComposer.class); - animComposer.getAnimClips().forEach(animClip -> System.out.println("AnimClip name: " + animClip.getName())); - AnimClip animClip = animComposer.getAnimClip("run_01"); // run_sideway_left, aim, run_sideway_right, base_stand, run_01, base, jump - animComposer.setCurrentAction(animClip.getName()); - } - - /** - * This method finds a node of a given name. - * - * @param rootNode - * the root node to search - * @param name - * the name of the searched node - * @return the found node or null - */ - private Spatial findNode(Node rootNode, String name) { - if (name.equals(rootNode.getName())) { - return rootNode; - } - return rootNode.getChild(name); - } -} diff --git a/jme3-blender/examples/src/main/java/jme3test/model/anim/TestBlenderObjectAnim.java b/jme3-blender/examples/src/main/java/jme3test/model/anim/TestBlenderObjectAnim.java deleted file mode 100644 index 928ddd79e..000000000 --- a/jme3-blender/examples/src/main/java/jme3test/model/anim/TestBlenderObjectAnim.java +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright (c) 2009-2020 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 jme3test.model.anim; - -import com.jme3.anim.AnimClip; -import com.jme3.anim.AnimComposer; -import com.jme3.anim.util.AnimMigrationUtils; -import com.jme3.app.SimpleApplication; -import com.jme3.asset.BlenderKey; -import com.jme3.light.DirectionalLight; -import com.jme3.math.ColorRGBA; -import com.jme3.math.Quaternion; -import com.jme3.math.Vector3f; -import com.jme3.scene.Node; -import com.jme3.scene.Spatial; - -public class TestBlenderObjectAnim extends SimpleApplication { - - public static void main(String[] args) { - TestBlenderObjectAnim app = new TestBlenderObjectAnim(); - app.start(); - } - - @Override - public void simpleInitApp() { - flyCam.setMoveSpeed(10f); - cam.setLocation(new Vector3f(6.4013605f, 7.488437f, 12.843031f)); - cam.setRotation(new Quaternion(-0.060740203f, 0.93925786f, -0.2398315f, -0.2378785f)); - - DirectionalLight dl = new DirectionalLight(); - dl.setDirection(new Vector3f(-0.1f, -0.7f, -1).normalizeLocal()); - dl.setColor(new ColorRGBA(1f, 1f, 1f, 1.0f)); - rootNode.addLight(dl); - - BlenderKey blenderKey = new BlenderKey("Blender/2.4x/animtest.blend"); - - Spatial scene = assetManager.loadModel(blenderKey); - rootNode.attachChild(scene); - - Spatial model = this.findNode(rootNode, "Cube"); - model.center(); - - // Because it's old .blend file need to migrate object. - AnimMigrationUtils.migrate(model); - - AnimComposer animComposer = model.getControl(AnimComposer.class); - animComposer.getAnimClips().forEach(animClip -> System.out.println("AnimClip name: " + animClip.getName())); - AnimClip animClip = animComposer.getAnimClip("Action"); // Action, Action.001 - animComposer.setCurrentAction(animClip.getName()); - } - - /** - * This method finds a node of a given name. - * @param rootNode the root node to search - * @param name the name of the searched node - * @return the found node or null - */ - private Spatial findNode(Node rootNode, String name) { - if (name.equals(rootNode.getName())) { - return rootNode; - } - return rootNode.getChild(name); - } -} diff --git a/jme3-blender/src/main/java/com/jme3/asset/BlenderKey.java b/jme3-blender/src/main/java/com/jme3/asset/BlenderKey.java deleted file mode 100644 index 6dbb205b5..000000000 --- a/jme3-blender/src/main/java/com/jme3/asset/BlenderKey.java +++ /dev/null @@ -1,733 +0,0 @@ -/* - * Copyright (c) 2009-2018 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.asset; - -import java.io.IOException; - -import com.jme3.export.InputCapsule; -import com.jme3.export.JmeExporter; -import com.jme3.export.JmeImporter; -import com.jme3.export.OutputCapsule; -import com.jme3.material.Material; -import com.jme3.material.RenderState.FaceCullMode; - -/** - * Blender key. Contains path of the blender file and its loading properties. - * @author Marcin Roguski (Kaelthas) - */ -public class BlenderKey extends ModelKey { - protected static final int DEFAULT_FPS = 25; - /** - * FramesPerSecond parameter describe how many frames there are in each second. It allows to calculate the time - * between the frames. - */ - protected int fps = DEFAULT_FPS; - /** - * This variable is a bitwise flag of FeatureToLoad interface values; By default everything is being loaded. - */ - protected int featuresToLoad = FeaturesToLoad.ALL; - /** The variable that tells if content of the file (along with data unlinked to any feature on the scene) should be stored as 'user data' in the result spatial. */ - protected boolean loadUnlinkedAssets; - /** The root path for all the assets. */ - protected String assetRootPath; - /** This variable indicate if Y axis is UP axis. If not then Z is up. By default set to true. */ - protected boolean fixUpAxis = true; - /** Generated textures resolution (PPU - Pixels Per Unit). */ - protected int generatedTexturePPU = 128; - /** - * The name of world settings that the importer will use. If not set or specified name does not occur in the file - * then the first world settings in the file will be used. - */ - protected String usedWorld; - /** - * User's default material that is set for objects that have no material definition in blender. The default value is - * null. If the value is null the importer will use its own default material (gray color - like in blender). - */ - protected Material defaultMaterial; - /** Face cull mode. By default it is disabled. */ - protected FaceCullMode faceCullMode = FaceCullMode.Back; - /** - * Variable describes which layers will be loaded. N-th bit set means N-th layer will be loaded. - * If set to -1 then the current layer will be loaded. - */ - protected int layersToLoad = -1; - /** A variable that toggles the object custom properties loading. */ - protected boolean loadObjectProperties = true; - /** - * Maximum texture size. Might be dependant on the graphic card. - * This value is taken from org.lwjgl.opengl.GL11.GL_MAX_TEXTURE_SIZE. - */ - protected int maxTextureSize = 8192; - /** Allows to toggle generated textures loading. Disabled by default because it very often takes too much memory and needs to be used wisely. */ - protected boolean loadGeneratedTextures; - /** Tells if the mipmaps will be generated by jme or not. By default generation is dependant on the blender settings. */ - protected MipmapGenerationMethod mipmapGenerationMethod = MipmapGenerationMethod.GENERATE_WHEN_NEEDED; - /** - * 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. */ - protected SkyGeneratedTextureShape skyGeneratedTextureShape = SkyGeneratedTextureShape.SPHERE; - /** - * 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; - /** The method of matching animations to skeletons. The default value is: AT_LEAST_ONE_NAME_MATCH. */ - protected AnimationMatchMethod animationMatchMethod = AnimationMatchMethod.AT_LEAST_ONE_NAME_MATCH; - /** The size of points that are loaded and do not belong to any edge of the mesh. */ - protected float pointsSize = 1; - /** The width of edges that are loaded from the mesh and do not belong to any face. */ - protected float linesWidth = 1; - - /** - * 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) { - this.loadObjectProperties = loadObjectProperties; - } - - /** - * @return the current properties loading properties - */ - public boolean isLoadObjectProperties() { - return loadObjectProperties; - } - - /** - * The default value for this parameter is the same as defined by: org.lwjgl.opengl.GL11.GL_MAX_TEXTURE_SIZE. - * If by any means this is too large for user's hardware configuration use the 'setMaxTextureSize' method to change that. - * @return maximum texture size (width/height) - */ - public int getMaxTextureSize() { - return maxTextureSize; - } - - /** - * This method sets the maximum texture size. - * @param maxTextureSize - * the maximum texture size - */ - public void setMaxTextureSize(int maxTextureSize) { - this.maxTextureSize = maxTextureSize; - } - - /** - * This method sets the flag that toggles the generated textures loading. - * @param loadGeneratedTextures - * true if generated textures should be loaded and false otherwise - */ - public void setLoadGeneratedTextures(boolean loadGeneratedTextures) { - this.loadGeneratedTextures = loadGeneratedTextures; - } - - /** - * @return tells if the generated textures should be loaded (false is the default value) - */ - public boolean isLoadGeneratedTextures() { - return loadGeneratedTextures; - } - - /** - * Not used any more. - * This method sets the asset root path. - * @param assetRootPath - * the assets root path - */ - @Deprecated - public void setAssetRootPath(String assetRootPath) { - this.assetRootPath = assetRootPath; - } - - /** - * Not used any more. - * This method returns the asset root path. - * @return the asset root path - */ - @Deprecated - public String getAssetRootPath() { - return assetRootPath; - } - - /** - * This method adds features to be loaded. - * @param featuresToLoad - * bitwise flag of FeaturesToLoad interface values - */ - @Deprecated - public void includeInLoading(int featuresToLoad) { - this.featuresToLoad |= featuresToLoad; - } - - /** - * This method removes features from being loaded. - * @param featuresNotToLoad - * bitwise flag of FeaturesToLoad interface values - */ - @Deprecated - public void excludeFromLoading(int featuresNotToLoad) { - featuresToLoad &= ~featuresNotToLoad; - } - - @Deprecated - public boolean shouldLoad(int featureToLoad) { - return (featuresToLoad & featureToLoad) != 0; - } - - /** - * This method returns bitwise value of FeaturesToLoad interface value. It describes features that will be loaded by - * the blender file loader. - * @return features that will be loaded by the blender file loader - */ - @Deprecated - public int getFeaturesToLoad() { - return featuresToLoad; - } - - /** - * This method determines if unlinked assets should be loaded. - * If not then only objects on selected layers will be loaded and their assets if required. - * If yes then all assets will be loaded even if they are on inactive layers or are not linked - * to anything. - * @return true if unlinked assets should be loaded and false otherwise - */ - public boolean isLoadUnlinkedAssets() { - return loadUnlinkedAssets; - } - - /** - * This method sets if unlinked assets should be loaded. - * If not then only objects on selected layers will be loaded and their assets if required. - * If yes then all assets will be loaded even if they are on inactive layers or are not linked - * to anything. - * @param loadUnlinkedAssets - * true if unlinked assets should be loaded and false otherwise - */ - public void setLoadUnlinkedAssets(boolean loadUnlinkedAssets) { - this.loadUnlinkedAssets = loadUnlinkedAssets; - } - - /** - * This method sets the fix up axis state. If set to true then Y is up axis. Otherwise the up i Z axis. By default Y - * is up axis. - * @param fixUpAxis - * the up axis state variable - */ - public void setFixUpAxis(boolean fixUpAxis) { - this.fixUpAxis = fixUpAxis; - } - - /** - * This method returns the fix up axis state. If set to true then Y is up axis. Otherwise the up i Z axis. By - * default Y is up axis. - * @return the up axis state variable - */ - public boolean isFixUpAxis() { - return fixUpAxis; - } - - /** - * This method sets the generated textures resolution. - * @param generatedTexturePPU - * the generated textures resolution - */ - public void setGeneratedTexturePPU(int generatedTexturePPU) { - this.generatedTexturePPU = generatedTexturePPU; - } - - /** - * @return the generated textures resolution - */ - public int getGeneratedTexturePPU() { - return generatedTexturePPU; - } - - /** - * @return mipmaps generation method - */ - public MipmapGenerationMethod getMipmapGenerationMethod() { - return mipmapGenerationMethod; - } - - /** - * @param mipmapGenerationMethod - * mipmaps generation method - */ - public void setMipmapGenerationMethod(MipmapGenerationMethod mipmapGenerationMethod) { - this.mipmapGenerationMethod = mipmapGenerationMethod; - } - - /** - * @return the size of the generated textures for the sky (used if no flat textures are applied) - */ - public int getSkyGeneratedTextureSize() { - return skyGeneratedTextureSize; - } - - /** - * @param skyGeneratedTextureSize - * the size of the generated textures for the sky (used if no flat textures are applied) - */ - public void setSkyGeneratedTextureSize(int skyGeneratedTextureSize) { - if (skyGeneratedTextureSize <= 0) { - throw new IllegalArgumentException("The texture size must be a positive value (the value given as a parameter: " + skyGeneratedTextureSize + ")!"); - } - this.skyGeneratedTextureSize = skyGeneratedTextureSize; - } - - /** - * @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) { - this.skyGeneratedTextureRadius = skyGeneratedTextureRadius; - } - - /** - * @return the shape against which the generated texture for the sky will be created (by default it is a sphere). - */ - public SkyGeneratedTextureShape getSkyGeneratedTextureShape() { - return skyGeneratedTextureShape; - } - - /** - * @param skyGeneratedTextureShape - * the shape against which the generated texture for the sky will be created - */ - public void setSkyGeneratedTextureShape(SkyGeneratedTextureShape skyGeneratedTextureShape) { - if (skyGeneratedTextureShape == null) { - throw new IllegalArgumentException("The sky generated shape type cannot be null!"); - } - this.skyGeneratedTextureShape = skyGeneratedTextureShape; - } - - /** - * 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. - * @param optimiseTextures - * the variable that tells if the textures should be optimised or not - */ - public void setOptimiseTextures(boolean optimiseTextures) { - this.optimiseTextures = optimiseTextures; - } - - /** - * @return the variable that tells if the textures should be optimised or not (by default the optimisation is disabled) - */ - public boolean isOptimiseTextures() { - return optimiseTextures; - } - - /** - * Sets the way the animations will be matched with skeletons. - * - * @param animationMatchMethod - * the way the animations will be matched with skeletons - */ - public void setAnimationMatchMethod(AnimationMatchMethod animationMatchMethod) { - this.animationMatchMethod = animationMatchMethod; - } - - /** - * @return the way the animations will be matched with skeletons - */ - public AnimationMatchMethod getAnimationMatchMethod() { - return animationMatchMethod; - } - - /** - * @return the size of points that are loaded and do not belong to any edge of the mesh - */ - public float getPointsSize() { - return pointsSize; - } - - /** - * Sets the size of points that are loaded and do not belong to any edge of the mesh. - * @param pointsSize - * The size of points that are loaded and do not belong to any edge of the mesh - */ - public void setPointsSize(float pointsSize) { - this.pointsSize = pointsSize; - } - - /** - * @return the width of edges that are loaded from the mesh and do not belong to any face - */ - public float getLinesWidth() { - return linesWidth; - } - - /** - * Sets the width of edges that are loaded from the mesh and do not belong to any face. - * @param linesWidth - * the width of edges that are loaded from the mesh and do not belong to any face - */ - public void setLinesWidth(float linesWidth) { - this.linesWidth = linesWidth; - } - - /** - * This method sets the name of the WORLD data block that should be used during file loading. By default the name is - * not set. If no name is set or the given name does not occur in the file - the first WORLD data block will be used - * during loading (assuming any exists in the file). - * @param usedWorld - * the name of the WORLD block used during loading - */ - public void setUsedWorld(String usedWorld) { - this.usedWorld = usedWorld; - } - - /** - * This method returns the name of the WORLD data block that should be used during file loading. - * @return the name of the WORLD block used during loading - */ - public String getUsedWorld() { - return usedWorld; - } - - /** - * This method sets the default material for objects. - * @param defaultMaterial - * the default material - */ - public void setDefaultMaterial(Material defaultMaterial) { - this.defaultMaterial = defaultMaterial; - } - - /** - * This method returns the default material. - * @return the default material - */ - public Material getDefaultMaterial() { - return defaultMaterial; - } - - @Override - public void write(JmeExporter e) throws IOException { - super.write(e); - OutputCapsule oc = e.getCapsule(this); - oc.write(fps, "fps", DEFAULT_FPS); - oc.write(featuresToLoad, "features-to-load", FeaturesToLoad.ALL); - oc.write(loadUnlinkedAssets, "load-unlinked-assets", false); - oc.write(assetRootPath, "asset-root-path", null); - oc.write(fixUpAxis, "fix-up-axis", true); - oc.write(generatedTexturePPU, "generated-texture-ppu", 128); - oc.write(usedWorld, "used-world", null); - oc.write(defaultMaterial, "default-material", null); - oc.write(faceCullMode, "face-cull-mode", FaceCullMode.Off); - oc.write(layersToLoad, "layers-to-load", -1); - oc.write(mipmapGenerationMethod, "mipmap-generation-method", MipmapGenerationMethod.GENERATE_WHEN_NEEDED); - oc.write(skyGeneratedTextureSize, "sky-generated-texture-size", 1000); - oc.write(skyGeneratedTextureRadius, "sky-generated-texture-radius", 1f); - oc.write(skyGeneratedTextureShape, "sky-generated-texture-shape", SkyGeneratedTextureShape.SPHERE); - oc.write(optimiseTextures, "optimise-textures", false); - oc.write(animationMatchMethod, "animation-match-method", AnimationMatchMethod.AT_LEAST_ONE_NAME_MATCH); - oc.write(pointsSize, "points-size", 1); - oc.write(linesWidth, "lines-width", 1); - } - - @Override - public void read(JmeImporter e) throws IOException { - super.read(e); - InputCapsule ic = e.getCapsule(this); - fps = ic.readInt("fps", DEFAULT_FPS); - featuresToLoad = ic.readInt("features-to-load", FeaturesToLoad.ALL); - loadUnlinkedAssets = ic.readBoolean("load-unlinked-assets", false); - assetRootPath = ic.readString("asset-root-path", null); - fixUpAxis = ic.readBoolean("fix-up-axis", true); - generatedTexturePPU = ic.readInt("generated-texture-ppu", 128); - usedWorld = ic.readString("used-world", null); - defaultMaterial = (Material) ic.readSavable("default-material", null); - faceCullMode = ic.readEnum("face-cull-mode", FaceCullMode.class, FaceCullMode.Off); - layersToLoad = ic.readInt("layers-to=load", -1); - mipmapGenerationMethod = ic.readEnum("mipmap-generation-method", MipmapGenerationMethod.class, MipmapGenerationMethod.GENERATE_WHEN_NEEDED); - skyGeneratedTextureSize = ic.readInt("sky-generated-texture-size", 1000); - skyGeneratedTextureRadius = ic.readFloat("sky-generated-texture-radius", 1f); - skyGeneratedTextureShape = ic.readEnum("sky-generated-texture-shape", SkyGeneratedTextureShape.class, SkyGeneratedTextureShape.SPHERE); - optimiseTextures = ic.readBoolean("optimise-textures", false); - animationMatchMethod = ic.readEnum("animation-match-method", AnimationMatchMethod.class, AnimationMatchMethod.AT_LEAST_ONE_NAME_MATCH); - pointsSize = ic.readFloat("points-size", 1); - linesWidth = ic.readFloat("lines-width", 1); - } - - @Override - public int hashCode() { - final int prime = 31; - int result = super.hashCode(); - result = prime * result + (animationMatchMethod == null ? 0 : animationMatchMethod.hashCode()); - 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()); - result = prime * result + (int) pointsSize; - result = prime * result + (int) linesWidth; - return result; - } - - @Override - public boolean equals(Object obj) { - if (obj instanceof BlenderKey) { - return false; - } - BlenderKey other = (BlenderKey) obj; - if (animationMatchMethod != other.animationMatchMethod) { - return false; - } - 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; - } - if (pointsSize != other.pointsSize) { - return false; - } - if (linesWidth != other.linesWidth) { - return false; - } - return true; - } - - /** - * This enum tells the importer if the mipmaps for textures will be generated by jme.
  • NEVER_GENERATE and ALWAYS_GENERATE are quite understandable
  • 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 - * @author Marcin Roguski (Kaelthas) - */ - public static enum MipmapGenerationMethod { - NEVER_GENERATE, ALWAYS_GENERATE, GENERATE_WHEN_NEEDED; - } - - /** - * This interface describes the features of the scene that are to be loaded. - * @deprecated this interface is deprecated and is not used anymore; to ensure the loading models consistency - * everything must be loaded because in blender one feature might depend on another - * @author Marcin Roguski (Kaelthas) - */ - @Deprecated - public static interface FeaturesToLoad { - - int SCENES = 0x0000FFFF; - int OBJECTS = 0x0000000B; - int ANIMATIONS = 0x00000004; - int MATERIALS = 0x00000003; - int TEXTURES = 0x00000001; - int CAMERAS = 0x00000020; - int LIGHTS = 0x00000010; - int WORLD = 0x00000040; - int ALL = 0xFFFFFFFF; - } - - /** - * The shape againts which the sky generated texture will be created. - * - * @author Marcin Roguski (Kaelthas) - */ - public static enum SkyGeneratedTextureShape { - CUBE, SPHERE; - } - - /** - * This enum describes which animations should be attached to which armature. - * Blender does not store the mapping between action and armature. That is why the importer - * will try to match those by comparing bone name of the armature with the channel names - * int the actions. - * - * @author Marcin Roguski (Kaelthas) - */ - public static enum AnimationMatchMethod { - /** - * Animation is matched with skeleton when at leas one bone name matches the name of the action channel. - * All the bones that do not have their corresponding channel in the animation will not get the proper tracks for - * this particulat animation. - * Also the channel will not be used for the animation if it does not find the proper bone name. - */ - AT_LEAST_ONE_NAME_MATCH, - /** - * Animation is matched when all action names are covered by the target names (bone names or the name of the - * animated spatial. - */ - ALL_NAMES_MATCH; - } -} diff --git a/jme3-blender/src/main/java/com/jme3/asset/GeneratedTextureKey.java b/jme3-blender/src/main/java/com/jme3/asset/GeneratedTextureKey.java deleted file mode 100644 index 6bd9017ae..000000000 --- a/jme3-blender/src/main/java/com/jme3/asset/GeneratedTextureKey.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * 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.asset; - -/** - * This key is mostly used to distinguish between textures that are loaded from - * the given assets and those being generated automatically. Every generated - * texture will have this kind of key attached. - * - * @author Marcin Roguski (Kaelthas) - */ -public class GeneratedTextureKey extends TextureKey { - - /** - * Constructor. Stores the name. Extension and folder name are empty - * strings. - * - * @param name - * the name of the texture - */ - public GeneratedTextureKey(String name) { - super(name); - } - - @Override - public String getExtension() { - return ""; - } - - @Override - public String getFolder() { - return ""; - } - - @Override - public String toString() { - return "Generated texture [" + name + "]"; - } -} diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/AbstractBlenderHelper.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/AbstractBlenderHelper.java deleted file mode 100644 index 142f757e2..000000000 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/AbstractBlenderHelper.java +++ /dev/null @@ -1,193 +0,0 @@ -/* - * 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.blender; - -import java.util.Arrays; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.logging.Level; -import java.util.logging.Logger; - -import com.jme3.asset.AssetNotFoundException; -import com.jme3.asset.BlenderKey; -import com.jme3.export.Savable; -import com.jme3.math.FastMath; -import com.jme3.math.Quaternion; -import com.jme3.scene.Spatial; -import com.jme3.scene.plugins.blender.BlenderContext.LoadedDataType; -import com.jme3.scene.plugins.blender.file.BlenderFileException; -import com.jme3.scene.plugins.blender.file.Pointer; -import com.jme3.scene.plugins.blender.file.Structure; -import com.jme3.scene.plugins.blender.objects.Properties; - -/** - * A purpose of the helper class is to split calculation code into several classes. Each helper after use should be cleared because it can - * hold the state of the calculations. - * @author Marcin Roguski - */ -public abstract class AbstractBlenderHelper { - private static final Logger LOGGER = Logger.getLogger(AbstractBlenderHelper.class.getName()); - - /** The blender context. */ - protected BlenderContext blenderContext; - /** The version of the blend file. */ - protected final int blenderVersion; - /** This variable indicates if the Y asxis is the UP axis or not. */ - protected boolean fixUpAxis; - /** Quaternion used to rotate data when Y is up axis. */ - protected Quaternion upAxisRotationQuaternion; - - /** - * This constructor parses the given blender version and stores the result. Some functionalities may differ in different blender - * versions. - * @param blenderVersion - * the version read from the blend file - * @param blenderContext - * the blender context - */ - public AbstractBlenderHelper(String blenderVersion, BlenderContext blenderContext) { - this.blenderVersion = Integer.parseInt(blenderVersion); - this.blenderContext = blenderContext; - fixUpAxis = blenderContext.getBlenderKey().isFixUpAxis(); - if (fixUpAxis) { - upAxisRotationQuaternion = new Quaternion().fromAngles(-FastMath.HALF_PI, 0, 0); - } - } - - /** - * This method loads the properties if they are available and defined for the structure. - * @param structure - * the structure we read the properties from - * @param blenderContext - * the blender context - * @return loaded properties or null if they are not available - * @throws BlenderFileException - * an exception is thrown when the blend file is somehow corrupted - */ - protected Properties loadProperties(Structure structure, BlenderContext blenderContext) throws BlenderFileException { - Properties properties = null; - Structure id = (Structure) structure.getFieldValue("ID"); - if (id != null) { - Pointer pProperties = (Pointer) id.getFieldValue("properties"); - if (pProperties.isNotNull()) { - Structure propertiesStructure = pProperties.fetchData().get(0); - properties = new Properties(); - properties.load(propertiesStructure, blenderContext); - } - } - return properties; - } - - /** - * The method applies properties to the given spatial. The Properties - * instance cannot be directly applied because the end-user might not have - * the blender plugin jar file and thus receive ClassNotFoundException. The - * values are set by name instead. - * - * @param spatial - * the spatial that is to have properties applied - * @param properties - * the properties to be applied - */ - public void applyProperties(Spatial spatial, Properties properties) { - List propertyNames = properties.getSubPropertiesNames(); - 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) { - spatial.setUserData(propertyName, value); - } else if (value instanceof Double) { - spatial.setUserData(propertyName, ((Double) value).floatValue()); - } else if (value instanceof int[]) { - spatial.setUserData(propertyName, Arrays.toString((int[]) value)); - } else if (value instanceof float[]) { - spatial.setUserData(propertyName, Arrays.toString((float[]) value)); - } else if (value instanceof double[]) { - spatial.setUserData(propertyName, Arrays.toString((double[]) value)); - } - } - } - } - - /** - * The method loads library of a given ID from linked blender file. - * @param id - * the ID of the linked feature (it contains its name and blender path) - * @return loaded feature or null if none was found - * @throws BlenderFileException - * and exception is throw when problems with reading a blend file occur - */ - protected Object loadLibrary(Structure id) throws BlenderFileException { - Pointer pLib = (Pointer) id.getFieldValue("lib"); - if (pLib.isNotNull()) { - String fullName = id.getFieldValue("name").toString();// we need full name with the prefix - String nameOfFeatureToLoad = id.getName(); - Structure library = pLib.fetchData().get(0); - String path = library.getFieldValue("filepath").toString(); - - if (!blenderContext.getLinkedFeatures().keySet().contains(path)) { - Spatial loadedAsset = null; - BlenderKey blenderKey = new BlenderKey(path); - blenderKey.setLoadUnlinkedAssets(true); - try { - loadedAsset = blenderContext.getAssetManager().loadAsset(blenderKey); - } catch (AssetNotFoundException e) { - LOGGER.log(Level.FINEST, "Cannot locate linked resource at path: {0}.", path); - } - - if (loadedAsset != null) { - Map> linkedData = loadedAsset.getUserData("linkedData"); - - for (Entry> entry : linkedData.entrySet()) { - String linkedDataFilePath = "this".equals(entry.getKey()) ? path : entry.getKey(); - blenderContext.getLinkedFeatures().put(linkedDataFilePath, entry.getValue()); - } - } else { - LOGGER.log(Level.WARNING, "No features loaded from path: {0}.", path); - } - } - - Object result = blenderContext.getLinkedFeature(path, fullName); - if (result == null) { - LOGGER.log(Level.WARNING, "Could NOT find asset named {0} in the library of path: {1}.", new Object[] { nameOfFeatureToLoad, path }); - } else { - blenderContext.addLoadedFeatures(id.getOldMemoryAddress(), LoadedDataType.STRUCTURE, id); - blenderContext.addLoadedFeatures(id.getOldMemoryAddress(), LoadedDataType.FEATURE, result); - } - return result; - } else { - LOGGER.warning("Library link points to nothing!"); - } - return null; - } -} diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/BlenderContext.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/BlenderContext.java deleted file mode 100644 index fea54f13b..000000000 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/BlenderContext.java +++ /dev/null @@ -1,767 +0,0 @@ -/* - * Copyright (c) 2009-2019 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.blender; - -import java.util.ArrayList; -import java.util.EmptyStackException; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Stack; - -import com.jme3.animation.Animation; -import com.jme3.animation.Bone; -import com.jme3.animation.Skeleton; -import com.jme3.asset.AssetManager; -import com.jme3.asset.BlenderKey; -import com.jme3.light.Light; -import com.jme3.material.Material; -import com.jme3.math.ColorRGBA; -import com.jme3.post.Filter; -import com.jme3.renderer.Camera; -import com.jme3.scene.Node; -import com.jme3.scene.plugins.blender.animations.BlenderAction; -import com.jme3.scene.plugins.blender.animations.BoneContext; -import com.jme3.scene.plugins.blender.constraints.Constraint; -import com.jme3.scene.plugins.blender.file.BlenderInputStream; -import com.jme3.scene.plugins.blender.file.DnaBlockData; -import com.jme3.scene.plugins.blender.file.FileBlockHeader; -import com.jme3.scene.plugins.blender.file.FileBlockHeader.BlockCode; -import com.jme3.scene.plugins.blender.file.Structure; -import com.jme3.scene.plugins.blender.materials.MaterialContext; -import com.jme3.scene.plugins.blender.meshes.TemporalMesh; -import com.jme3.texture.Texture; - -/** - * The class that stores temporary data and manages it during loading the belnd - * file. This class is intended to be used in a single loading thread. It holds - * the state of loading operations. - * - * @author Marcin Roguski (Kaelthas) - */ -public class BlenderContext { - /** The blender file version. */ - private int blenderVersion; - /** The blender key. */ - private BlenderKey blenderKey; - /** The header of the file block. */ - private DnaBlockData dnaBlockData; - /** The scene structure. */ - private Structure sceneStructure; - /** The input stream of the blend file. */ - private BlenderInputStream inputStream; - /** The asset manager. */ - private AssetManager assetManager; - /** The blocks read from the file. */ - protected List blocks = new ArrayList(); - /** - * A map containing the file block headers. The key is the old memory address. - */ - private Map fileBlockHeadersByOma = new HashMap(); - /** A map containing the file block headers. The key is the block code. */ - private Map> fileBlockHeadersByCode = new HashMap>(); - /** - * This map stores the loaded features by their old memory address. The - * first object in the value table is the loaded structure and the second - - * the structure already converted into proper data. - */ - private Map> loadedFeatures = new HashMap>(); - /** Features loaded from external blender files. The key is the file path and the value is a map between feature name and loaded feature. */ - private Map> linkedFeatures = new HashMap>(); - /** A stack that hold the parent structure of currently loaded feature. */ - private Stack parentStack = new Stack(); - /** A list of constraints for the specified object. */ - protected Map> constraints = new HashMap>(); - /** Animations loaded for features. */ - private Map> animations = new HashMap>(); - /** Loaded skeletons. */ - private Map skeletons = new HashMap(); - /** A map between skeleton and node it modifies. */ - private Map nodesWithSkeletons = new HashMap(); - /** A map of bone contexts. */ - protected Map boneContexts = new HashMap(); - /** A map og helpers that perform loading. */ - private Map helpers = new HashMap(); - /** Markers used by loading classes to store some custom data. This is made to avoid putting this data into user properties. */ - private Map> markers = new HashMap>(); - /** A map of blender actions. The key is the action name and the value is the action itself. */ - private Map actions = new HashMap(); - - /** - * This method sets the blender file version. - * - * @param blenderVersion - * the blender file version - */ - public void setBlenderVersion(String blenderVersion) { - this.blenderVersion = Integer.parseInt(blenderVersion); - } - - /** - * @return the blender file version - */ - public int getBlenderVersion() { - return blenderVersion; - } - - /** - * This method sets the blender key. - * - * @param blenderKey - * the blender key - */ - public void setBlenderKey(BlenderKey blenderKey) { - this.blenderKey = blenderKey; - } - - /** - * This method returns the blender key. - * - * @return the blender key - */ - public BlenderKey getBlenderKey() { - return blenderKey; - } - - /** - * This method sets the dna block data. - * - * @param dnaBlockData - * the dna block data - */ - public void setBlockData(DnaBlockData dnaBlockData) { - this.dnaBlockData = dnaBlockData; - } - - /** - * This method returns the dna block data. - * - * @return the dna block data - */ - public DnaBlockData getDnaBlockData() { - return dnaBlockData; - } - - /** - * This method sets the scene structure data. - * - * @param sceneStructure - * the scene structure data - */ - public void setSceneStructure(Structure sceneStructure) { - this.sceneStructure = sceneStructure; - } - - /** - * This method returns the scene structure data. - * - * @return the scene structure data - */ - public Structure getSceneStructure() { - return sceneStructure; - } - - /** - * This method returns the asset manager. - * - * @return the asset manager - */ - public AssetManager getAssetManager() { - return assetManager; - } - - /** - * This method sets the asset manager. - * - * @param assetManager - * the asset manager - */ - public void setAssetManager(AssetManager assetManager) { - this.assetManager = assetManager; - } - - /** - * This method returns the input stream of the blend file. - * - * @return the input stream of the blend file - */ - public BlenderInputStream getInputStream() { - return inputStream; - } - - /** - * This method sets the input stream of the blend file. - * - * @param inputStream - * the input stream of the blend file - */ - public void setInputStream(BlenderInputStream inputStream) { - this.inputStream = inputStream; - } - - /** - * This method adds a file block header to the map. Its old memory address - * is the key. - * - * @param oldMemoryAddress - * the address of the block header - * @param fileBlockHeader - * the block header to store - */ - public void addFileBlockHeader(Long oldMemoryAddress, FileBlockHeader fileBlockHeader) { - blocks.add(fileBlockHeader); - fileBlockHeadersByOma.put(oldMemoryAddress, fileBlockHeader); - List headers = fileBlockHeadersByCode.get(fileBlockHeader.getCode()); - if (headers == null) { - headers = new ArrayList(); - fileBlockHeadersByCode.put(fileBlockHeader.getCode(), headers); - } - headers.add(fileBlockHeader); - } - - /** - * @return the block headers - */ - public List getBlocks() { - return blocks; - } - - /** - * This method returns the block header of a given memory address. If the - * header is not present then null is returned. - * - * @param oldMemoryAddress - * the address of the block header - * @return loaded header or null if it was not yet loaded - */ - public FileBlockHeader getFileBlock(Long oldMemoryAddress) { - return fileBlockHeadersByOma.get(oldMemoryAddress); - } - - /** - * This method returns a list of file blocks' headers of a specified code. - * - * @param code - * the code of file blocks - * @return a list of file blocks' headers of a specified code - */ - public List getFileBlocks(BlockCode code) { - return fileBlockHeadersByCode.get(code); - } - - /** - * This method adds a helper instance to the helpers' map. - * - * @param - * the type of the helper - * @param clazz - * helper's class definition - * @param helper - * the helper instance - */ - public void putHelper(Class clazz, AbstractBlenderHelper helper) { - helpers.put(clazz.getSimpleName(), helper); - } - - @SuppressWarnings("unchecked") - public T getHelper(Class clazz) { - return (T) helpers.get(clazz.getSimpleName()); - } - - /** - * This method adds a loaded feature to the map. The key is its unique old - * memory address. - * - * @param oldMemoryAddress - * the address of the feature - * @param featureDataType - * @param feature - * the feature we want to store - */ - public void addLoadedFeatures(Long oldMemoryAddress, LoadedDataType featureDataType, Object feature) { - if (oldMemoryAddress == null || featureDataType == null || feature == null) { - throw new IllegalArgumentException("One of the given arguments is null!"); - } - Map map = loadedFeatures.get(oldMemoryAddress); - if (map == null) { - map = new HashMap(); - loadedFeatures.put(oldMemoryAddress, map); - } - map.put(featureDataType, feature); - } - - /** - * This method returns the feature of a given memory address. If the feature - * is not yet loaded then null is returned. - * - * @param oldMemoryAddress - * the address of the feature - * @param loadedFeatureDataType - * the type of data we want to retrieve it can be either filled - * structure or already converted feature - * @return loaded feature or null if it was not yet loaded - */ - public Object getLoadedFeature(Long oldMemoryAddress, LoadedDataType loadedFeatureDataType) { - Map result = loadedFeatures.get(oldMemoryAddress); - if (result != null) { - return result.get(loadedFeatureDataType); - } - return null; - } - - /** - * The method adds linked content to the blender context. - * @param blenderFilePath - * the path of linked blender file - * @param featureGroup - * the linked feature group (ie. scenes, materials, meshes, etc.) - * @param feature - * the linked feature - */ - @Deprecated - public void addLinkedFeature(String blenderFilePath, String featureGroup, Object feature) { - // the method is deprecated and empty at the moment - } - - /** - * The method returns linked feature of a given name from the specified blender path. - * @param blenderFilePath - * the blender file path - * @param featureName - * the feature name we want to get - * @return linked feature or null if none was found - */ - @SuppressWarnings("unchecked") - public Object getLinkedFeature(String blenderFilePath, String featureName) { - Map linkedFeatures = this.linkedFeatures.get(blenderFilePath); - if(linkedFeatures != null) { - String namePrefix = (featureName.charAt(0) + "" + featureName.charAt(1)).toUpperCase(); - featureName = featureName.substring(2); - - if("SC".equals(namePrefix)) { - List scenes = (List) linkedFeatures.get("scenes"); - if(scenes != null) { - for(Node scene : scenes) { - if(featureName.equals(scene.getName())) { - return scene; - } - } - } - } else if("OB".equals(namePrefix)) { - List features = (List) linkedFeatures.get("objects"); - if(features != null) { - for(Node feature : features) { - if(featureName.equals(feature.getName())) { - return feature; - } - } - } - } else if("ME".equals(namePrefix)) { - List temporalMeshes = (List) linkedFeatures.get("meshes"); - if(temporalMeshes != null) { - for(TemporalMesh temporalMesh : temporalMeshes) { - if(featureName.equals(temporalMesh.getName())) { - return temporalMesh; - } - } - } - } else if("MA".equals(namePrefix)) { - List features = (List) linkedFeatures.get("materials"); - if(features != null) { - for(MaterialContext feature : features) { - if(featureName.equals(feature.getName())) { - return feature; - } - } - } - } else if("TX".equals(namePrefix)) { - List features = (List) linkedFeatures.get("textures"); - if(features != null) { - for(Texture feature : features) { - if(featureName.equals(feature.getName())) { - return feature; - } - } - } - } else if("IM".equals(namePrefix)) { - List features = (List) linkedFeatures.get("images"); - if(features != null) { - for(Texture feature : features) { - if(featureName.equals(feature.getName())) { - return feature; - } - } - } - } else if("AC".equals(namePrefix)) { - List features = (List) linkedFeatures.get("animations"); - if(features != null) { - for(Animation feature : features) { - if(featureName.equals(feature.getName())) { - return feature; - } - } - } - } else if("CA".equals(namePrefix)) { - List features = (List) linkedFeatures.get("cameras"); - if(features != null) { - for(Camera feature : features) { - if(featureName.equals(feature.getName())) { - return feature; - } - } - } - } else if("LA".equals(namePrefix)) { - List features = (List) linkedFeatures.get("lights"); - if(features != null) { - for(Light feature : features) { - if(featureName.equals(feature.getName())) { - return feature; - } - } - } - } else if("FI".equals(featureName)) { - List features = (List) linkedFeatures.get("lights"); - if(features != null) { - for(Filter feature : features) { - if(featureName.equals(feature.getName())) { - return feature; - } - } - } - } - } - return null; - } - - /** - * @return all linked features for the current blend file - */ - public Map> getLinkedFeatures() { - return linkedFeatures; - } - - /** - * This method adds the structure to the parent stack. - * - * @param parent - * the structure to be added to the stack - */ - public void pushParent(Structure parent) { - parentStack.push(parent); - } - - /** - * This method removes the structure from the top of the parent's stack. - * - * @return the structure that was removed from the stack - */ - public Structure popParent() { - try { - return parentStack.pop(); - } catch (EmptyStackException e) { - return null; - } - } - - /** - * This method retrieves the structure at the top of the parent's stack but - * does not remove it. - * - * @return the structure from the top of the stack - */ - public Structure peekParent() { - try { - return parentStack.peek(); - } catch (EmptyStackException e) { - return null; - } - } - - /** - * This method adds a new modifier to the list. - * - * @param ownerOMA - * the owner's old memory address - * @param constraints - * the object's constraints - */ - public void addConstraints(Long ownerOMA, List constraints) { - List objectConstraints = this.constraints.get(ownerOMA); - if (objectConstraints == null) { - objectConstraints = new ArrayList(); - this.constraints.put(ownerOMA, objectConstraints); - } - objectConstraints.addAll(constraints); - } - - /** - * Returns constraints applied to the feature of the given OMA. - * @param ownerOMA - * the constraints' owner OMA - * @return a list of constraints or null if no constraints are applied to the feature - */ - public List getConstraints(Long ownerOMA) { - return constraints.get(ownerOMA); - } - - /** - * @return all available constraints - */ - public List getAllConstraints() { - List result = new ArrayList(); - for (Entry> entry : constraints.entrySet()) { - result.addAll(entry.getValue()); - } - return result; - } - - /** - * This method adds the animation for the specified OMA of its owner. - * - * @param ownerOMA - * the owner's old memory address - * @param animation - * the animation for the feature specified by ownerOMA - */ - public void addAnimation(Long ownerOMA, Animation animation) { - List animList = animations.get(ownerOMA); - if (animList == null) { - animList = new ArrayList(); - animations.put(ownerOMA, animList); - } - animList.add(animation); - } - - /** - * This method returns the animation data for the specified owner. - * - * @param ownerOMA - * the old memory address of the animation data owner - * @return the animation or null if none exists - */ - public List getAnimations(Long ownerOMA) { - return animations.get(ownerOMA); - } - - /** - * This method sets the skeleton for the specified OMA of its owner. - * - * @param skeletonOMA - * the skeleton's old memory address - * @param skeleton - * the skeleton specified by the given OMA - */ - public void setSkeleton(Long skeletonOMA, Skeleton skeleton) { - skeletons.put(skeletonOMA, skeleton); - } - - /** - * The method stores a binding between the skeleton and the proper armature - * node. - * - * @param skeleton - * the skeleton - * @param node - * the armature node - */ - public void setNodeForSkeleton(Skeleton skeleton, Node node) { - nodesWithSkeletons.put(skeleton, node); - } - - /** - * This method returns the armature node that is defined for the skeleton. - * - * @param skeleton - * the skeleton - * @return the armature node that defines the skeleton in blender - */ - public Node getControlledNode(Skeleton skeleton) { - return nodesWithSkeletons.get(skeleton); - } - - /** - * This method returns the skeleton for the specified OMA of its owner. - * - * @param skeletonOMA - * the skeleton's old memory address - * @return the skeleton specified by the given OMA - */ - public Skeleton getSkeleton(Long skeletonOMA) { - return skeletons.get(skeletonOMA); - } - - /** - * This method sets the bone context for the given bone old memory address. - * If the context is already set it will be replaced. - * - * @param boneOMA - * the bone's old memory address - * @param boneContext - * the bones's context - */ - public void setBoneContext(Long boneOMA, BoneContext boneContext) { - boneContexts.put(boneOMA, boneContext); - } - - /** - * This method returns the bone context for the given bone old memory - * address. If no context exists then null is returned. - * - * @param boneOMA - * the bone's old memory address - * @return bone's context - */ - public BoneContext getBoneContext(Long boneOMA) { - return boneContexts.get(boneOMA); - } - - /** - * Returns bone by given name. - * - * @param skeletonOMA - * the OMA of the skeleton where the bone will be searched - * @param name - * the name of the bone - * @return found bone or null if none bone of a given name exists - */ - public BoneContext getBoneByName(Long skeletonOMA, String name) { - for (Entry entry : boneContexts.entrySet()) { - if (entry.getValue().getArmatureObjectOMA().equals(skeletonOMA)) { - Bone bone = entry.getValue().getBone(); - if (bone != null && name.equals(bone.getName())) { - return entry.getValue(); - } - } - } - return null; - } - - /** - * Returns bone context for the given bone. - * - * @param bone - * the bone - * @return the bone's bone context - */ - public BoneContext getBoneContext(Bone bone) { - for (Entry entry : boneContexts.entrySet()) { - if (entry.getValue().getBone().getName().equals(bone.getName())) { - return entry.getValue(); - } - } - throw new IllegalStateException("Cannot find context for bone: " + bone); - } - - /** - * This metod returns the default material. - * - * @return the default material - */ - public synchronized Material getDefaultMaterial() { - if (blenderKey.getDefaultMaterial() == null) { - Material defaultMaterial = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); - defaultMaterial.setColor("Color", ColorRGBA.DarkGray); - blenderKey.setDefaultMaterial(defaultMaterial); - } - return blenderKey.getDefaultMaterial(); - } - - /** - * Adds a custom marker for scene's feature. - * - * @param marker - * the marker name - * @param feature - * te scene's feature (can be node, material or texture or - * anything else) - * @param markerValue - * the marker value - */ - public void addMarker(String marker, Object feature, Object markerValue) { - if (markerValue == null) { - throw new IllegalArgumentException("The marker's value cannot be null."); - } - Map markersMap = markers.get(marker); - if (markersMap == null) { - markersMap = new HashMap(); - markers.put(marker, markersMap); - } - markersMap.put(feature, markerValue); - } - - /** - * Returns the marker value. The returned value is null if no marker was - * defined for the given feature. - * - * @param marker - * the marker name - * @param feature - * the scene's feature - * @return marker value or null if it was not defined - */ - public Object getMarkerValue(String marker, Object feature) { - Map markersMap = markers.get(marker); - return markersMap == null ? null : markersMap.get(feature); - } - - /** - * Adds blender action to the context. - * @param action - * the action loaded from the blend file - */ - public void addAction(BlenderAction action) { - actions.put(action.getName(), action); - } - - /** - * @return a map of blender actions; the key is the action name and the value is action itself - */ - public Map getActions() { - return actions; - } - - /** - * This enum defines what loaded data type user wants to retrieve. It can be - * either filled structure or already converted data. - * - * @author Marcin Roguski (Kaelthas) - */ - public static enum LoadedDataType { - STRUCTURE, FEATURE, TEMPORAL_MESH; - } - - @Override - public String toString() { - return blenderKey == null ? "BlenderContext [key = null]" : "BlenderContext [ key = " + blenderKey.toString() + " ]"; - } -} diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/BlenderLoader.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/BlenderLoader.java deleted file mode 100644 index b7d9df841..000000000 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/BlenderLoader.java +++ /dev/null @@ -1,419 +0,0 @@ -/* - * Copyright (c) 2009-2019 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.blender; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.logging.Level; -import java.util.logging.Logger; - -import com.jme3.animation.Animation; -import com.jme3.asset.AssetInfo; -import com.jme3.asset.AssetKey; -import com.jme3.asset.AssetLoader; -import com.jme3.asset.AssetLocator; -import com.jme3.asset.AssetManager; -import com.jme3.asset.BlenderKey; -import com.jme3.asset.ModelKey; -import com.jme3.asset.StreamAssetInfo; -import com.jme3.light.Light; -import com.jme3.math.ColorRGBA; -import com.jme3.post.Filter; -import com.jme3.renderer.Camera; -import com.jme3.scene.CameraNode; -import com.jme3.scene.LightNode; -import com.jme3.scene.Node; -import com.jme3.scene.Spatial; -import com.jme3.scene.plugins.blender.animations.AnimationHelper; -import com.jme3.scene.plugins.blender.cameras.CameraHelper; -import com.jme3.scene.plugins.blender.constraints.ConstraintHelper; -import com.jme3.scene.plugins.blender.curves.CurvesHelper; -import com.jme3.scene.plugins.blender.file.BlenderFileException; -import com.jme3.scene.plugins.blender.file.BlenderInputStream; -import com.jme3.scene.plugins.blender.file.FileBlockHeader; -import com.jme3.scene.plugins.blender.file.FileBlockHeader.BlockCode; -import com.jme3.scene.plugins.blender.file.Pointer; -import com.jme3.scene.plugins.blender.file.Structure; -import com.jme3.scene.plugins.blender.landscape.LandscapeHelper; -import com.jme3.scene.plugins.blender.lights.LightHelper; -import com.jme3.scene.plugins.blender.materials.MaterialContext; -import com.jme3.scene.plugins.blender.materials.MaterialHelper; -import com.jme3.scene.plugins.blender.meshes.MeshHelper; -import com.jme3.scene.plugins.blender.meshes.TemporalMesh; -import com.jme3.scene.plugins.blender.modifiers.ModifierHelper; -import com.jme3.scene.plugins.blender.objects.ObjectHelper; -import com.jme3.scene.plugins.blender.particles.ParticlesHelper; -import com.jme3.scene.plugins.blender.textures.TextureHelper; -import com.jme3.texture.Texture; - -/** - * This is the main loading class. Have in notice that asset manager needs to have loaders for resources like textures. - * @author Marcin Roguski (Kaelthas) - */ -public class BlenderLoader implements AssetLoader { - private static final Logger LOGGER = Logger.getLogger(BlenderLoader.class.getName()); - - @Override - public Spatial load(AssetInfo assetInfo) throws IOException { - try { - BlenderContext blenderContext = this.setup(assetInfo); - - AnimationHelper animationHelper = blenderContext.getHelper(AnimationHelper.class); - animationHelper.loadAnimations(); - - BlenderKey blenderKey = blenderContext.getBlenderKey(); - LoadedFeatures loadedFeatures = new LoadedFeatures(); - for (FileBlockHeader block : blenderContext.getBlocks()) { - switch (block.getCode()) { - case BLOCK_OB00: - ObjectHelper objectHelper = blenderContext.getHelper(ObjectHelper.class); - Node object = (Node) objectHelper.toObject(block.getStructure(blenderContext), blenderContext); - if (LOGGER.isLoggable(Level.FINE)) { - LOGGER.log(Level.FINE, "{0}: {1}--> {2}", new Object[] { object.getName(), object.getLocalTranslation().toString(), object.getParent() == null ? "null" : object.getParent().getName() }); - } - if (object.getParent() == null) { - loadedFeatures.objects.add(object); - } - if (object instanceof LightNode && ((LightNode) object).getLight() != null) { - loadedFeatures.lights.add(((LightNode) object).getLight()); - } else if (object instanceof CameraNode && ((CameraNode) object).getCamera() != null) { - loadedFeatures.cameras.add(((CameraNode) object).getCamera()); - } - break; - case BLOCK_SC00:// Scene - loadedFeatures.sceneBlocks.add(block); - break; - case BLOCK_MA00:// Material - MaterialHelper materialHelper = blenderContext.getHelper(MaterialHelper.class); - MaterialContext materialContext = materialHelper.toMaterialContext(block.getStructure(blenderContext), blenderContext); - loadedFeatures.materials.add(materialContext); - break; - case BLOCK_ME00:// Mesh - MeshHelper meshHelper = blenderContext.getHelper(MeshHelper.class); - TemporalMesh temporalMesh = meshHelper.toTemporalMesh(block.getStructure(blenderContext), blenderContext); - loadedFeatures.meshes.add(temporalMesh); - break; - case BLOCK_IM00:// Image - TextureHelper textureHelper = blenderContext.getHelper(TextureHelper.class); - Texture image = textureHelper.loadImageAsTexture(block.getStructure(blenderContext), 0, blenderContext); - if (image != null && image.getImage() != null) {// render results are stored as images but are not being loaded - loadedFeatures.images.add(image); - } - break; - case BLOCK_TE00: - Structure textureStructure = block.getStructure(blenderContext); - int type = ((Number) textureStructure.getFieldValue("type")).intValue(); - if (type == TextureHelper.TEX_IMAGE) { - TextureHelper texHelper = blenderContext.getHelper(TextureHelper.class); - Texture texture = texHelper.getTexture(textureStructure, null, blenderContext); - if (texture != null) {// null is returned when texture has no image - loadedFeatures.textures.add(texture); - } - } else { - LOGGER.fine("Only image textures can be loaded as unlinked assets. Generated textures will be applied to an existing object."); - } - break; - case BLOCK_WO00:// World - LandscapeHelper landscapeHelper = blenderContext.getHelper(LandscapeHelper.class); - Structure worldStructure = block.getStructure(blenderContext); - - String worldName = worldStructure.getName(); - if (blenderKey.getUsedWorld() == null || blenderKey.getUsedWorld().equals(worldName)) { - - Light ambientLight = landscapeHelper.toAmbientLight(worldStructure); - if (ambientLight != null) { - loadedFeatures.objects.add(new LightNode(null, ambientLight)); - loadedFeatures.lights.add(ambientLight); - } - loadedFeatures.sky = landscapeHelper.toSky(worldStructure); - loadedFeatures.backgroundColor = landscapeHelper.toBackgroundColor(worldStructure); - - Filter fogFilter = landscapeHelper.toFog(worldStructure); - if (fogFilter != null) { - loadedFeatures.filters.add(landscapeHelper.toFog(worldStructure)); - } - } - break; - case BLOCK_AC00: - LOGGER.fine("Loading unlinked animations is not yet supported!"); - break; - default: - LOGGER.log(Level.FINEST, "Ommiting the block: {0}.", block.getCode()); - } - } - - LOGGER.fine("Baking constraints after every feature is loaded."); - ConstraintHelper constraintHelper = blenderContext.getHelper(ConstraintHelper.class); - constraintHelper.bakeConstraints(blenderContext); - - LOGGER.fine("Loading scenes and attaching them to the root object."); - for (FileBlockHeader sceneBlock : loadedFeatures.sceneBlocks) { - loadedFeatures.scenes.add(this.toScene(sceneBlock.getStructure(blenderContext), blenderContext)); - } - - LOGGER.fine("Creating the root node of the model and applying loaded nodes of the scene and loaded features to it."); - Node modelRoot = new Node(blenderKey.getName()); - for (Node scene : loadedFeatures.scenes) { - modelRoot.attachChild(scene); - } - - if (blenderKey.isLoadUnlinkedAssets()) { - LOGGER.fine("Setting loaded content as user data in resulting sptaial."); - Map> linkedData = new HashMap>(); - - Map thisFileData = new HashMap(); - thisFileData.put("scenes", loadedFeatures.scenes == null ? new ArrayList() : loadedFeatures.scenes); - thisFileData.put("objects", loadedFeatures.objects == null ? new ArrayList() : loadedFeatures.objects); - thisFileData.put("meshes", loadedFeatures.meshes == null ? new ArrayList() : loadedFeatures.meshes); - thisFileData.put("materials", loadedFeatures.materials == null ? new ArrayList() : loadedFeatures.materials); - thisFileData.put("textures", loadedFeatures.textures == null ? new ArrayList() : loadedFeatures.textures); - thisFileData.put("images", loadedFeatures.images == null ? new ArrayList() : loadedFeatures.images); - thisFileData.put("animations", loadedFeatures.animations == null ? new ArrayList() : loadedFeatures.animations); - thisFileData.put("cameras", loadedFeatures.cameras == null ? new ArrayList() : loadedFeatures.cameras); - thisFileData.put("lights", loadedFeatures.lights == null ? new ArrayList() : loadedFeatures.lights); - thisFileData.put("filters", loadedFeatures.filters == null ? new ArrayList() : loadedFeatures.filters); - thisFileData.put("backgroundColor", loadedFeatures.backgroundColor); - thisFileData.put("sky", loadedFeatures.sky); - - linkedData.put("this", thisFileData); - linkedData.putAll(blenderContext.getLinkedFeatures()); - - modelRoot.setUserData("linkedData", linkedData); - } - - return modelRoot; - } catch (BlenderFileException e) { - throw new IOException(e.getLocalizedMessage(), e); - } catch (Exception e) { - throw new IOException("Unexpected importer exception occurred: " + e.getLocalizedMessage(), e); - } finally { - this.clear(assetInfo); - } - } - - /** - * This method converts the given structure to a scene node. - * @param structure - * structure of a scene - * @param blenderContext the blender context - * @return scene's node - * @throws BlenderFileException - * an exception throw when problems with blender file occur - */ - private Node toScene(Structure structure, BlenderContext blenderContext) throws BlenderFileException { - ObjectHelper objectHelper = blenderContext.getHelper(ObjectHelper.class); - Node result = new Node(structure.getName()); - List base = ((Structure) structure.getFieldValue("base")).evaluateListBase(); - for (Structure b : base) { - Pointer pObject = (Pointer) b.getFieldValue("object"); - if (pObject.isNotNull()) { - Structure objectStructure = pObject.fetchData().get(0); - - Object object = objectHelper.toObject(objectStructure, blenderContext); - if (object instanceof Node) { - if (LOGGER.isLoggable(Level.FINE)) { - LOGGER.log(Level.FINE, "{0}: {1}--> {2}", new Object[] { ((Node) object).getName(), ((Node) object).getLocalTranslation().toString(), ((Node) object).getParent() == null ? "null" : ((Node) object).getParent().getName() }); - } - - if (((Node) object).getParent() == null) { - result.attachChild((Spatial) object); - } - - if(object instanceof LightNode) { - result.addLight(((LightNode) object).getLight()); - } - } - } - } - return result; - } - - /** - * This method sets up the loader. - * @param assetInfo - * the asset info - * @throws BlenderFileException - * an exception is throw when something wrong happens with blender file - */ - protected BlenderContext setup(AssetInfo assetInfo) throws BlenderFileException { - // registering loaders - ModelKey modelKey = (ModelKey) assetInfo.getKey(); - BlenderKey blenderKey; - if (modelKey instanceof BlenderKey) { - blenderKey = (BlenderKey) modelKey; - } else { - blenderKey = new BlenderKey(modelKey.getName()); - } - - // opening stream - BlenderInputStream inputStream = new BlenderInputStream(assetInfo.openStream()); - - // reading blocks - List blocks = new ArrayList(); - FileBlockHeader fileBlock; - BlenderContext blenderContext = new BlenderContext(); - blenderContext.setBlenderVersion(inputStream.getVersionNumber()); - blenderContext.setAssetManager(assetInfo.getManager()); - blenderContext.setInputStream(inputStream); - blenderContext.setBlenderKey(blenderKey); - - // creating helpers - blenderContext.putHelper(AnimationHelper.class, new AnimationHelper(inputStream.getVersionNumber(), blenderContext)); - blenderContext.putHelper(TextureHelper.class, new TextureHelper(inputStream.getVersionNumber(), blenderContext)); - blenderContext.putHelper(MeshHelper.class, new MeshHelper(inputStream.getVersionNumber(), blenderContext)); - blenderContext.putHelper(ObjectHelper.class, new ObjectHelper(inputStream.getVersionNumber(), blenderContext)); - blenderContext.putHelper(CurvesHelper.class, new CurvesHelper(inputStream.getVersionNumber(), blenderContext)); - blenderContext.putHelper(LightHelper.class, new LightHelper(inputStream.getVersionNumber(), blenderContext)); - blenderContext.putHelper(CameraHelper.class, new CameraHelper(inputStream.getVersionNumber(), blenderContext)); - blenderContext.putHelper(ModifierHelper.class, new ModifierHelper(inputStream.getVersionNumber(), blenderContext)); - blenderContext.putHelper(MaterialHelper.class, new MaterialHelper(inputStream.getVersionNumber(), blenderContext)); - blenderContext.putHelper(ConstraintHelper.class, new ConstraintHelper(inputStream.getVersionNumber(), blenderContext)); - blenderContext.putHelper(ParticlesHelper.class, new ParticlesHelper(inputStream.getVersionNumber(), blenderContext)); - blenderContext.putHelper(LandscapeHelper.class, new LandscapeHelper(inputStream.getVersionNumber(), blenderContext)); - - // reading the blocks (dna block is automatically saved in the blender context when found) - FileBlockHeader sceneFileBlock = null; - do { - fileBlock = new FileBlockHeader(inputStream, blenderContext); - if (!fileBlock.isDnaBlock()) { - blocks.add(fileBlock); - // save the scene's file block - if (fileBlock.getCode() == BlockCode.BLOCK_SC00) { - sceneFileBlock = fileBlock; - } - } - } while (!fileBlock.isLastBlock()); - if (sceneFileBlock != null) { - blenderContext.setSceneStructure(sceneFileBlock.getStructure(blenderContext)); - } - - // adding locator for linked content - assetInfo.getManager().registerLocator(assetInfo.getKey().getName(), LinkedContentLocator.class); - - return blenderContext; - } - - /** - * The internal data is only needed during loading so make it unreachable so that the GC can release - * that memory (which can be quite large amount). - */ - protected void clear(AssetInfo assetInfo) { - assetInfo.getManager().unregisterLocator(assetInfo.getKey().getName(), LinkedContentLocator.class); - } - - /** - * This class holds the loading results according to the given loading flag. - * @author Marcin Roguski (Kaelthas) - */ - private static class LoadedFeatures { - private List sceneBlocks = new ArrayList(); - /** The scenes from the file. */ - private List scenes = new ArrayList(); - /** Objects from all scenes. */ - private List objects = new ArrayList(); - /** All meshes. */ - private List meshes = new ArrayList(); - /** Materials from all objects. */ - private List materials = new ArrayList(); - /** Textures from all objects. */ - private List textures = new ArrayList(); - /** The images stored in the blender file. */ - private List images = new ArrayList(); - /** Animations of all objects. */ - private List animations = new ArrayList(); - /** All cameras from the file. */ - private List cameras = new ArrayList(); - /** All lights from the file. */ - private List lights = new ArrayList(); - /** Loaded sky. */ - private Spatial sky; - /** Scene filters (ie. FOG). */ - private List filters = new ArrayList(); - /** - * The background color of the render loaded from the horizon color of the world. If no world is used than the gray color - * is set to default (as in blender editor. - */ - private ColorRGBA backgroundColor = ColorRGBA.Gray; - } - - public static class LinkedContentLocator implements AssetLocator { - private File rootFolder; - - @Override - public void setRootPath(String rootPath) { - rootFolder = new File(rootPath); - if(rootFolder.isFile()) { - rootFolder = rootFolder.getParentFile(); - } - } - - @SuppressWarnings("rawtypes") - @Override - public AssetInfo locate(AssetManager manager, AssetKey key) { - if(key instanceof BlenderKey) { - File linkedAbsoluteFile = new File(key.getName()); - if(linkedAbsoluteFile.exists() && linkedAbsoluteFile.isFile()) { - try { - return new StreamAssetInfo(manager, key, new FileInputStream(linkedAbsoluteFile)); - } catch (FileNotFoundException e) { - return null; - } - } - - File linkedFileInCurrentAssetFolder = new File(rootFolder, linkedAbsoluteFile.getName()); - if(linkedFileInCurrentAssetFolder.exists() && linkedFileInCurrentAssetFolder.isFile()) { - try { - return new StreamAssetInfo(manager, key, new FileInputStream(linkedFileInCurrentAssetFolder)); - } catch (FileNotFoundException e) { - return null; - } - } - - File linkedFileInCurrentFolder = new File(".", linkedAbsoluteFile.getName()); - if(linkedFileInCurrentFolder.exists() && linkedFileInCurrentFolder.isFile()) { - try { - return new StreamAssetInfo(manager, key, new FileInputStream(linkedFileInCurrentFolder)); - } catch (FileNotFoundException e) { - return null; - } - } - } - return null; - } - } -} diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/BlenderModelLoader.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/BlenderModelLoader.java deleted file mode 100644 index eae047421..000000000 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/BlenderModelLoader.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * 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.blender; - -/** - * This is the main loading class. Have in notice that asset manager needs to have loaders for resources like textures. - * @deprecated this class is deprecated; use BlenderLoader instead - * @author Marcin Roguski (Kaelthas) - */ -public class BlenderModelLoader extends BlenderLoader { -} diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/animations/AnimationHelper.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/animations/AnimationHelper.java deleted file mode 100644 index 1d889f992..000000000 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/animations/AnimationHelper.java +++ /dev/null @@ -1,391 +0,0 @@ -package com.jme3.scene.plugins.blender.animations; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map.Entry; -import java.util.Set; -import java.util.logging.Level; -import java.util.logging.Logger; - -import com.jme3.animation.AnimControl; -import com.jme3.animation.Animation; -import com.jme3.animation.BoneTrack; -import com.jme3.animation.Skeleton; -import com.jme3.animation.SkeletonControl; -import com.jme3.animation.SpatialTrack; -import com.jme3.asset.BlenderKey.AnimationMatchMethod; -import com.jme3.scene.Node; -import com.jme3.scene.plugins.blender.AbstractBlenderHelper; -import com.jme3.scene.plugins.blender.BlenderContext; -import com.jme3.scene.plugins.blender.BlenderContext.LoadedDataType; -import com.jme3.scene.plugins.blender.animations.Ipo.ConstIpo; -import com.jme3.scene.plugins.blender.curves.BezierCurve; -import com.jme3.scene.plugins.blender.file.BlenderFileException; -import com.jme3.scene.plugins.blender.file.BlenderInputStream; -import com.jme3.scene.plugins.blender.file.FileBlockHeader; -import com.jme3.scene.plugins.blender.file.FileBlockHeader.BlockCode; -import com.jme3.scene.plugins.blender.file.Pointer; -import com.jme3.scene.plugins.blender.file.Structure; -import com.jme3.scene.plugins.blender.objects.ObjectHelper; - -/** - * The helper class that helps in animations loading. - * @author Marcin Roguski (Kaelthas) - */ -public class AnimationHelper extends AbstractBlenderHelper { - private static final Logger LOGGER = Logger.getLogger(AnimationHelper.class.getName()); - - public AnimationHelper(String blenderVersion, BlenderContext blenderContext) { - super(blenderVersion, blenderContext); - } - - /** - * Loads all animations that are stored in the blender file. The animations are not yet applied to the scene features. - * This should be called before objects are loaded. - * @throws BlenderFileException - * an exception is thrown when problems with blender file reading occur - */ - public void loadAnimations() throws BlenderFileException { - LOGGER.info("Loading animations that will be later applied to scene features."); - List actionHeaders = blenderContext.getFileBlocks(BlockCode.BLOCK_AC00); - if (actionHeaders != null) { - for (FileBlockHeader header : actionHeaders) { - Structure actionStructure = header.getStructure(blenderContext); - LOGGER.log(Level.INFO, "Found animation: {0}.", actionStructure.getName()); - blenderContext.addAction(this.getTracks(actionStructure, blenderContext)); - } - } - } - - /** - * The method applies animations to the given node. The names of the animations should be the same as actions names in the blender file. - * @param node - * the node to whom the animations will be applied - * @param animationMatchMethod - * the way animation should be matched with node - */ - public void applyAnimations(Node node, AnimationMatchMethod animationMatchMethod) { - List actions = this.getActions(node, animationMatchMethod); - if (actions.size() > 0) { - List animations = new ArrayList(); - for (BlenderAction action : actions) { - SpatialTrack[] tracks = action.toTracks(node, blenderContext); - if (tracks != null && tracks.length > 0) { - Animation spatialAnimation = new Animation(action.getName(), action.getAnimationTime()); - spatialAnimation.setTracks(tracks); - animations.add(spatialAnimation); - blenderContext.addAnimation((Long) node.getUserData(ObjectHelper.OMA_MARKER), spatialAnimation); - } - } - - if (animations.size() > 0) { - AnimControl control = new AnimControl(); - HashMap anims = new HashMap(animations.size()); - for (int i = 0; i < animations.size(); ++i) { - Animation animation = animations.get(i); - anims.put(animation.getName(), animation); - } - control.setAnimations(anims); - node.addControl(control); - } - } - } - - /** - * The method applies skeleton animations to the given node. - * @param node - * the node where the animations will be applied - * @param skeleton - * the skeleton of the node - * @param animationMatchMethod - * the way animation should be matched with skeleton - */ - public void applyAnimations(Node node, Skeleton skeleton, AnimationMatchMethod animationMatchMethod) { - node.addControl(new SkeletonControl(skeleton)); - blenderContext.setNodeForSkeleton(skeleton, node); - List actions = this.getActions(skeleton, animationMatchMethod); - - if (actions.size() > 0) { - List animations = new ArrayList(); - for (BlenderAction action : actions) { - BoneTrack[] tracks = action.toTracks(skeleton, blenderContext); - if (tracks != null && tracks.length > 0) { - Animation boneAnimation = new Animation(action.getName(), action.getAnimationTime()); - boneAnimation.setTracks(tracks); - animations.add(boneAnimation); - Long animatedNodeOMA = ((Number) blenderContext.getMarkerValue(ObjectHelper.OMA_MARKER, node)).longValue(); - blenderContext.addAnimation(animatedNodeOMA, boneAnimation); - } - } - if (animations.size() > 0) { - AnimControl control = new AnimControl(skeleton); - HashMap anims = new HashMap(animations.size()); - for (int i = 0; i < animations.size(); ++i) { - Animation animation = animations.get(i); - anims.put(animation.getName(), animation); - } - control.setAnimations(anims); - node.addControl(control); - - // make sure that SkeletonControl is added AFTER the AnimControl - SkeletonControl skeletonControl = node.getControl(SkeletonControl.class); - if (skeletonControl != null) { - node.removeControl(SkeletonControl.class); - node.addControl(skeletonControl); - } - } - } - } - - /** - * This method creates an ipo object used for interpolation calculations. - * - * @param ipoStructure - * the structure with ipo definition - * @param blenderContext - * the blender context - * @return the ipo object - * @throws BlenderFileException - * this exception is thrown when the blender file is somehow - * corrupted - */ - public Ipo fromIpoStructure(Structure ipoStructure, BlenderContext blenderContext) throws BlenderFileException { - Structure curvebase = (Structure) ipoStructure.getFieldValue("curve"); - - // preparing bezier curves - Ipo result = null; - List curves = curvebase.evaluateListBase();// IpoCurve - if (curves.size() > 0) { - BezierCurve[] bezierCurves = new BezierCurve[curves.size()]; - int frame = 0; - for (Structure curve : curves) { - Pointer pBezTriple = (Pointer) curve.getFieldValue("bezt"); - List bezTriples = pBezTriple.fetchData(); - int type = ((Number) curve.getFieldValue("adrcode")).intValue(); - bezierCurves[frame++] = new BezierCurve(type, bezTriples, 2); - } - curves.clear(); - result = new Ipo(bezierCurves, fixUpAxis, blenderContext.getBlenderVersion()); - Long ipoOma = ipoStructure.getOldMemoryAddress(); - blenderContext.addLoadedFeatures(ipoOma, LoadedDataType.STRUCTURE, ipoStructure); - blenderContext.addLoadedFeatures(ipoOma, LoadedDataType.FEATURE, result); - } - return result; - } - - /** - * 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); - } - - /** - * This method retuns the bone tracks for animation. - * - * @param actionStructure - * the structure containing the tracks - * @param blenderContext - * the blender context - * @return a list of tracks for the specified animation - * @throws BlenderFileException - * an exception is thrown when there are problems with the blend - * file - */ - private BlenderAction getTracks(Structure actionStructure, BlenderContext blenderContext) throws BlenderFileException { - if (blenderVersion < 250) { - return this.getTracks249(actionStructure, blenderContext); - } else { - return this.getTracks250(actionStructure, blenderContext); - } - } - - /** - * This method retuns the bone tracks for animation for blender version 2.50 - * and higher. - * - * @param actionStructure - * the structure containing the tracks - * @param blenderContext - * the blender context - * @return a list of tracks for the specified animation - * @throws BlenderFileException - * an exception is thrown when there are problems with the blend - * file - */ - private BlenderAction getTracks250(Structure actionStructure, BlenderContext blenderContext) throws BlenderFileException { - LOGGER.log(Level.FINE, "Getting tracks!"); - Structure groups = (Structure) actionStructure.getFieldValue("groups"); - List actionGroups = groups.evaluateListBase();// bActionGroup - BlenderAction blenderAction = new BlenderAction(actionStructure.getName(), blenderContext.getBlenderKey().getFps()); - int lastFrame = 1; - for (Structure actionGroup : actionGroups) { - String name = actionGroup.getFieldValue("name").toString(); - List channels = ((Structure) actionGroup.getFieldValue("channels")).evaluateListBase(); - BezierCurve[] bezierCurves = new BezierCurve[channels.size()]; - int channelCounter = 0; - for (Structure c : channels) { - int type = this.getCurveType(c, blenderContext); - Pointer pBezTriple = (Pointer) c.getFieldValue("bezt"); - List bezTriples = pBezTriple.fetchData(); - bezierCurves[channelCounter++] = new BezierCurve(type, bezTriples, 2); - } - - Ipo ipo = new Ipo(bezierCurves, fixUpAxis, blenderContext.getBlenderVersion()); - lastFrame = Math.max(lastFrame, ipo.getLastFrame()); - blenderAction.featuresTracks.put(name, ipo); - } - blenderAction.stopFrame = lastFrame; - return blenderAction; - } - - /** - * This method retuns the bone tracks for animation for blender version 2.49 - * (and probably several lower versions too). - * - * @param actionStructure - * the structure containing the tracks - * @param blenderContext - * the blender context - * @return a list of tracks for the specified animation - * @throws BlenderFileException - * an exception is thrown when there are problems with the blend - * file - */ - private BlenderAction getTracks249(Structure actionStructure, BlenderContext blenderContext) throws BlenderFileException { - LOGGER.log(Level.FINE, "Getting tracks!"); - Structure chanbase = (Structure) actionStructure.getFieldValue("chanbase"); - List actionChannels = chanbase.evaluateListBase();// bActionChannel - BlenderAction blenderAction = new BlenderAction(actionStructure.getName(), blenderContext.getBlenderKey().getFps()); - int lastFrame = 1; - for (Structure bActionChannel : actionChannels) { - String animatedFeatureName = bActionChannel.getFieldValue("name").toString(); - Pointer p = (Pointer) bActionChannel.getFieldValue("ipo"); - if (!p.isNull()) { - Structure ipoStructure = p.fetchData().get(0); - Ipo ipo = this.fromIpoStructure(ipoStructure, blenderContext); - if (ipo != null) {// this can happen when ipo with no curves appear in blender file - lastFrame = Math.max(lastFrame, ipo.getLastFrame()); - blenderAction.featuresTracks.put(animatedFeatureName, ipo); - } - } - } - blenderAction.stopFrame = lastFrame; - return blenderAction; - } - - /** - * This method returns the type of the ipo curve. - * - * @param structure - * the structure must contain the 'rna_path' field and - * 'array_index' field (the type is not important here) - * @param blenderContext - * the blender context - * @return the type of the curve - */ - public int getCurveType(Structure structure, BlenderContext blenderContext) { - // reading rna path first - BlenderInputStream bis = blenderContext.getInputStream(); - int currentPosition = bis.getPosition(); - Pointer pRnaPath = (Pointer) structure.getFieldValue("rna_path"); - FileBlockHeader dataFileBlock = blenderContext.getFileBlock(pRnaPath.getOldMemoryAddress()); - bis.setPosition(dataFileBlock.getBlockPosition()); - String rnaPath = bis.readString(); - bis.setPosition(currentPosition); - int arrayIndex = ((Number) structure.getFieldValue("array_index")).intValue(); - - // determining the curve type - if (rnaPath.endsWith("location")) { - return Ipo.AC_LOC_X + arrayIndex; - } - if (rnaPath.endsWith("rotation_quaternion")) { - return Ipo.AC_QUAT_W + arrayIndex; - } - if (rnaPath.endsWith("scale")) { - return Ipo.AC_SIZE_X + arrayIndex; - } - if (rnaPath.endsWith("rotation") || rnaPath.endsWith("rotation_euler")) { - return Ipo.OB_ROT_X + arrayIndex; - } - LOGGER.log(Level.WARNING, "Unknown curve rna path: {0}", rnaPath); - return -1; - } - - /** - * The method returns the actions for the given skeleton. The actions represent armature animation in blender. - * @param skeleton - * the skeleton we fetch the actions for - * @param animationMatchMethod - * the method of animation matching - * @return a list of animations for the specified skeleton - */ - private List getActions(Skeleton skeleton, AnimationMatchMethod animationMatchMethod) { - List result = new ArrayList(); - - // first get a set of bone names - Set boneNames = new HashSet(); - for (int i = 0; i < skeleton.getBoneCount(); ++i) { - String boneName = skeleton.getBone(i).getName(); - if (boneName != null && boneName.length() > 0) { - boneNames.add(skeleton.getBone(i).getName()); - } - } - - // finding matches - Set matchingNames = new HashSet(); - for (Entry actionEntry : blenderContext.getActions().entrySet()) { - // compute how many action tracks match the skeleton bones' names - for (String boneName : boneNames) { - if (actionEntry.getValue().hasTrackName(boneName)) { - matchingNames.add(boneName); - } - } - - BlenderAction action = null; - if (animationMatchMethod == AnimationMatchMethod.AT_LEAST_ONE_NAME_MATCH && matchingNames.size() > 0) { - action = actionEntry.getValue(); - } else if (matchingNames.size() == actionEntry.getValue().getTracksCount()) { - action = actionEntry.getValue(); - } - - if (action != null) { - // remove the tracks that do not match the bone names if the matching method is different from ALL_NAMES_MATCH - if (animationMatchMethod != AnimationMatchMethod.ALL_NAMES_MATCH) { - action = action.clone(); - action.removeTracksThatAreNotInTheCollection(matchingNames); - } - result.add(action); - } - - matchingNames.clear(); - } - return result; - } - - /** - * The method returns the actions for the given node. The actions represent object animation in blender. - * @param node - * the node we fetch the actions for - * @param animationMatchMethod - * the method of animation matching - * @return a list of animations for the specified node - */ - private List getActions(Node node, AnimationMatchMethod animationMatchMethod) { - List result = new ArrayList(); - - for (Entry actionEntry : blenderContext.getActions().entrySet()) { - if (actionEntry.getValue().hasTrackName(node.getName())) { - result.add(actionEntry.getValue()); - } - } - return result; - } -} diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/animations/BlenderAction.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/animations/BlenderAction.java deleted file mode 100644 index 38a437ba0..000000000 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/animations/BlenderAction.java +++ /dev/null @@ -1,134 +0,0 @@ -package com.jme3.scene.plugins.blender.animations; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; - -import com.jme3.animation.BoneTrack; -import com.jme3.animation.Skeleton; -import com.jme3.animation.SpatialTrack; -import com.jme3.scene.Node; -import com.jme3.scene.plugins.blender.BlenderContext; - -/** - * An abstract representation of animation. The data stored here is mainly a - * raw action data loaded from blender. It can later be transformed into - * bone or spatial animation and applied to the specified node. - * - * @author Marcin Roguski (Kaelthas) - */ -public class BlenderAction implements Cloneable { - /** The action name. */ - /* package */final String name; - /** Animation speed - frames per second. */ - /* package */int fps; - /** - * The last frame of the animation (the last ipo curve node position is - * used as a last frame). - */ - /* package */int stopFrame; - /** - * Tracks of the features. In case of bone animation the keys are the - * names of the bones. In case of spatial animation - the node's name is - * used. A single ipo contains all tracks for location, rotation and - * scales. - */ - /* package */Map featuresTracks = new HashMap(); - - public BlenderAction(String name, int fps) { - this.name = name; - this.fps = fps; - } - - public void removeTracksThatAreNotInTheCollection(Collection trackNames) { - Map newTracks = new HashMap(); - for (String trackName : trackNames) { - if (featuresTracks.containsKey(trackName)) { - newTracks.put(trackName, featuresTracks.get(trackName)); - } - } - featuresTracks = newTracks; - } - - @Override - public BlenderAction clone() { - BlenderAction result = new BlenderAction(name, fps); - result.stopFrame = stopFrame; - result.featuresTracks = new HashMap(featuresTracks); - return result; - } - - /** - * Converts the action into JME spatial animation tracks. - * - * @param node - * the node that will be animated - * @return the spatial tracks for the node - */ - public SpatialTrack[] toTracks(Node node, BlenderContext blenderContext) { - List tracks = new ArrayList(featuresTracks.size()); - for (Entry entry : featuresTracks.entrySet()) { - tracks.add((SpatialTrack) entry.getValue().calculateTrack(0, null, node.getLocalTranslation(), node.getLocalRotation(), node.getLocalScale(), 1, stopFrame, fps, true)); - } - return tracks.toArray(new SpatialTrack[tracks.size()]); - } - - /** - * Converts the action into JME bone animation tracks. - * - * @param skeleton - * the skeleton that will be animated - * @return the bone tracks for the node - */ - public BoneTrack[] toTracks(Skeleton skeleton, BlenderContext blenderContext) { - List tracks = new ArrayList(featuresTracks.size()); - for (Entry entry : featuresTracks.entrySet()) { - int boneIndex = skeleton.getBoneIndex(entry.getKey()); - BoneContext boneContext = blenderContext.getBoneContext(skeleton.getBone(boneIndex)); - tracks.add((BoneTrack) entry.getValue().calculateTrack(boneIndex, boneContext, boneContext.getBone().getBindPosition(), boneContext.getBone().getBindRotation(), boneContext.getBone().getBindScale(), 1, stopFrame, fps, false)); - } - return tracks.toArray(new BoneTrack[tracks.size()]); - } - - /** - * @return the name of the action - */ - public String getName() { - return name; - } - - /** - * @return the time of animations (in seconds) - */ - public float getAnimationTime() { - return (stopFrame - 1) / (float) fps; - } - - /** - * Determines if the current action has a track of a given name. - * CAUTION! The names are case sensitive. - * - * @param name - * the name of the track - * @return true if the track of a given name exists for the - * action and false otherwise - */ - public boolean hasTrackName(String name) { - return featuresTracks.containsKey(name); - } - - /** - * @return the amount of tracks in current action - */ - public int getTracksCount() { - return featuresTracks.size(); - } - - @Override - public String toString() { - return "BlenderTrack [name = " + name + "; tracks = [" + featuresTracks.keySet() + "]]"; - } -} diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/animations/BoneContext.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/animations/BoneContext.java deleted file mode 100644 index adc0a5c4c..000000000 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/animations/BoneContext.java +++ /dev/null @@ -1,400 +0,0 @@ -package com.jme3.scene.plugins.blender.animations; - -import java.util.ArrayList; -import java.util.List; - -import com.jme3.animation.Bone; -import com.jme3.animation.Skeleton; -import com.jme3.math.Matrix4f; -import com.jme3.math.Quaternion; -import com.jme3.math.Vector3f; -import com.jme3.scene.Spatial; -import com.jme3.scene.plugins.blender.BlenderContext; -import com.jme3.scene.plugins.blender.BlenderContext.LoadedDataType; -import com.jme3.scene.plugins.blender.constraints.ConstraintHelper; -import com.jme3.scene.plugins.blender.file.BlenderFileException; -import com.jme3.scene.plugins.blender.file.DynamicArray; -import com.jme3.scene.plugins.blender.file.Pointer; -import com.jme3.scene.plugins.blender.file.Structure; -import com.jme3.scene.plugins.blender.objects.ObjectHelper; - -/** - * 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 SELECTED = 0x000001; - public static final int CONNECTED_TO_PARENT = 0x000010; - public static final int DEFORM = 0x001000; - public static final int NO_LOCAL_LOCATION = 0x400000; - public static final int NO_INHERIT_SCALE = 0x008000; - public static final int NO_INHERIT_ROTATION = 0x000200; - - /** - * 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 static final int IKFLAG_LOCK_X = 0x01; - private static final int IKFLAG_LOCK_Y = 0x02; - private static final int IKFLAG_LOCK_Z = 0x04; - private static final int IKFLAG_LIMIT_X = 0x08; - private static final int IKFLAG_LIMIT_Y = 0x10; - private static final int IKFLAG_LIMIT_Z = 0x20; - - 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 children = new ArrayList(); - /** Created bone (available after calling 'buildBone' method). */ - private Bone bone; - /** The length of the bone. */ - private float length; - /** The bone's deform envelope. */ - private BoneEnvelope boneEnvelope; - - // The below data is used only for IK constraint computations. - - /** The bone's stretch value. */ - private float ikStretch; - /** Bone's rotation minimum values. */ - private Vector3f limitMin; - /** Bone's rotation maximum values. */ - private Vector3f limitMax; - /** The bone's stiffness values (how much it rotates during IK computations. */ - private Vector3f stiffness; - /** Values that indicate if any axis' rotation should be limited by some angle. */ - private boolean[] limits; - /** Values that indicate if any axis' rotation should be disabled during IK computations. */ - private boolean[] locks; - - /** - * 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 - * occurs - */ - public BoneContext(Long armatureObjectOMA, Structure boneStructure, BlenderContext blenderContext) throws BlenderFileException { - this(boneStructure, armatureObjectOMA, null, blenderContext); - } - - /** - * Constructor. Creates the basic set of bone's data. - * - * @param boneStructure - * the bone's structure - * @param armatureObjectOMA - * the OMA of the bone's armature object - * @param parent - * bone's parent (null if the bone is the root bone) - * @param blenderContext - * the blender context - * @throws BlenderFileException - * an exception is thrown when problem with blender data reading - * occurs - */ - @SuppressWarnings("unchecked") - private BoneContext(Structure boneStructure, Long armatureObjectOMA, BoneContext parent, BlenderContext blenderContext) throws BlenderFileException { - this.parent = parent; - this.blenderContext = blenderContext; - this.boneStructure = boneStructure; - this.armatureObjectOMA = armatureObjectOMA; - boneName = boneStructure.getFieldValue("name").toString(); - flag = ((Number) boneStructure.getFieldValue("flag")).intValue(); - length = ((Number) boneStructure.getFieldValue("length")).floatValue(); - ObjectHelper objectHelper = blenderContext.getHelper(ObjectHelper.class); - - // first get the bone matrix in its armature space - globalBoneMatrix = objectHelper.getMatrix(boneStructure, "arm_mat", blenderContext.getBlenderKey().isFixUpAxis()); - if (blenderContext.getBlenderKey().isFixUpAxis()) { - // then make sure it is rotated in a proper way to fit the jme bone transformation conventions - globalBoneMatrix.multLocal(BONE_ARMATURE_TRANSFORMATION_MATRIX); - } - - Structure armatureStructure = blenderContext.getFileBlock(armatureObjectOMA).getStructure(blenderContext); - Spatial armature = (Spatial) objectHelper.toObject(armatureStructure, blenderContext); - ConstraintHelper constraintHelper = blenderContext.getHelper(ConstraintHelper.class); - Matrix4f armatureWorldMatrix = constraintHelper.toMatrix(armature.getWorldTransform(), new Matrix4f()); - - // and now compute the final bone matrix in world space - globalBoneMatrix = armatureWorldMatrix.mult(globalBoneMatrix); - - // load the bone deformation envelope if necessary - if ((flag & DEFORM) == 0) {// if the flag is NOT set then the DEFORM is in use - boneEnvelope = new BoneEnvelope(boneStructure, armatureWorldMatrix, blenderContext.getBlenderKey().isFixUpAxis()); - } - - // load bone's pose channel data - Pointer pPose = (Pointer) armatureStructure.getFieldValue("pose"); - if (pPose != null && pPose.isNotNull()) { - List poseChannels = ((Structure) pPose.fetchData().get(0).getFieldValue("chanbase")).evaluateListBase(); - for (Structure poseChannel : poseChannels) { - Long boneOMA = ((Pointer) poseChannel.getFieldValue("bone")).getOldMemoryAddress(); - if (boneOMA.equals(this.boneStructure.getOldMemoryAddress())) { - ikStretch = ((Number) poseChannel.getFieldValue("ikstretch")).floatValue(); - DynamicArray limitMin = (DynamicArray) poseChannel.getFieldValue("limitmin"); - this.limitMin = new Vector3f(limitMin.get(0).floatValue(), limitMin.get(1).floatValue(), limitMin.get(2).floatValue()); - - DynamicArray limitMax = (DynamicArray) poseChannel.getFieldValue("limitmax"); - this.limitMax = new Vector3f(limitMax.get(0).floatValue(), limitMax.get(1).floatValue(), limitMax.get(2).floatValue()); - - DynamicArray stiffness = (DynamicArray) poseChannel.getFieldValue("stiffness"); - this.stiffness = new Vector3f(stiffness.get(0).floatValue(), stiffness.get(1).floatValue(), stiffness.get(2).floatValue()); - - int ikFlag = ((Number) poseChannel.getFieldValue("ikflag")).intValue(); - locks = new boolean[] { (ikFlag & IKFLAG_LOCK_X) != 0, (ikFlag & IKFLAG_LOCK_Y) != 0, (ikFlag & IKFLAG_LOCK_Z) != 0 }; - // limits are enabled when locks are disabled, so we ween to take that into account here - limits = new boolean[] { (ikFlag & IKFLAG_LIMIT_X & ~IKFLAG_LOCK_X) != 0, (ikFlag & IKFLAG_LIMIT_Y & ~IKFLAG_LOCK_Y) != 0, (ikFlag & IKFLAG_LIMIT_Z & ~IKFLAG_LOCK_Z) != 0 }; - break;// we have found what we need, no need to search further - } - } - } - - // create the children - List childbase = ((Structure) boneStructure.getFieldValue("childbase")).evaluateListBase(); - for (Structure child : childbase) { - children.add(new BoneContext(child, armatureObjectOMA, this, blenderContext)); - } - - blenderContext.setBoneContext(boneStructure.getOldMemoryAddress(), this); - } - - /** - * This method builds the bone. It recursively builds the bone's children. - * - * @param bones - * a list of bones where the newly created bone will be added - * @param skeletonOwnerOma - * the spatial of the object that will own the skeleton - * @param blenderContext - * the blender context - * @return newly created bone - */ - public Bone buildBone(List bones, Long skeletonOwnerOma, BlenderContext blenderContext) { - this.skeletonOwnerOma = skeletonOwnerOma; - Long boneOMA = boneStructure.getOldMemoryAddress(); - bone = new Bone(boneName); - bones.add(bone); - blenderContext.addLoadedFeatures(boneOMA, LoadedDataType.STRUCTURE, boneStructure); - blenderContext.addLoadedFeatures(boneOMA, LoadedDataType.FEATURE, bone); - ObjectHelper objectHelper = blenderContext.getHelper(ObjectHelper.class); - - Structure skeletonOwnerObjectStructure = (Structure) blenderContext.getLoadedFeature(skeletonOwnerOma, LoadedDataType.STRUCTURE); - // I could load 'imat' here, but apparently in some older blenders there were bugs or unfinished functionalities that stored ZERO matrix in imat field - // loading 'obmat' and inverting it makes us avoid errors in such cases - Matrix4f invertedObjectOwnerGlobalMatrix = objectHelper.getMatrix(skeletonOwnerObjectStructure, "obmat", blenderContext.getBlenderKey().isFixUpAxis()).invertLocal(); - if (objectHelper.isParent(skeletonOwnerOma, armatureObjectOMA)) { - boneMatrixInModelSpace = globalBoneMatrix.mult(invertedObjectOwnerGlobalMatrix); - } else { - boneMatrixInModelSpace = invertedObjectOwnerGlobalMatrix.mult(globalBoneMatrix); - } - - Matrix4f boneLocalMatrix = parent == null ? boneMatrixInModelSpace : parent.boneMatrixInModelSpace.invert().multLocal(boneMatrixInModelSpace); - - Vector3f poseLocation = parent == null || !this.is(CONNECTED_TO_PARENT) ? boneLocalMatrix.toTranslationVector() : new Vector3f(0, parent.length, 0); - Quaternion rotation = boneLocalMatrix.toRotationQuat().normalizeLocal(); - Vector3f scale = boneLocalMatrix.toScaleVector(); - - bone.setBindTransforms(poseLocation, rotation, scale); - for (BoneContext child : children) { - bone.addChild(child.buildBone(bones, skeletonOwnerOma, blenderContext)); - } - - return bone; - } - - /** - * @return built bone (available after calling 'buildBone' method) - */ - public Bone getBone() { - return bone; - } - - /** - * @return the old memory address of the bone - */ - public Long getBoneOma() { - return boneStructure.getOldMemoryAddress(); - } - - /** - * The method returns the length of the bone. - * If you want to use it for bone debugger take model space scale into account and do - * something like this: - * boneContext.getLength() * boneContext.getBone().getModelSpaceScale().y. - * Otherwise the bones might not look as they should in the bone debugger. - * @return the length of the bone - */ - public float getLength() { - return length; - } - - /** - * @return OMA of the bone's armature object - */ - public Long getArmatureObjectOMA() { - return armatureObjectOMA; - } - - /** - * @return the OMA of the model that owns the bone's skeleton - */ - public Long getSkeletonOwnerOma() { - return skeletonOwnerOma; - } - - /** - * @return the skeleton the bone of this context belongs to - */ - public Skeleton getSkeleton() { - return blenderContext.getSkeleton(armatureObjectOMA); - } - - /** - * @return the initial bone's matrix in model space - */ - public Matrix4f getBoneMatrixInModelSpace() { - return boneMatrixInModelSpace; - } - - /** - * @return the vertex assigning envelope of the bone - */ - public BoneEnvelope getBoneEnvelope() { - return boneEnvelope; - } - - /** - * @return bone's stretch factor - */ - public float getIkStretch() { - return ikStretch; - } - - /** - * @return indicates if the X rotation should be limited - */ - public boolean isLimitX() { - return limits != null ? limits[0] : false; - } - - /** - * @return indicates if the Y rotation should be limited - */ - public boolean isLimitY() { - return limits != null ? limits[1] : false; - } - - /** - * @return indicates if the Z rotation should be limited - */ - public boolean isLimitZ() { - return limits != null ? limits[2] : false; - } - - /** - * @return indicates if the X rotation should be disabled - */ - public boolean isLockX() { - return locks != null ? locks[0] : false; - } - - /** - * @return indicates if the Y rotation should be disabled - */ - public boolean isLockY() { - return locks != null ? locks[1] : false; - } - - /** - * @return indicates if the Z rotation should be disabled - */ - public boolean isLockZ() { - return locks != null ? locks[2] : false; - } - - /** - * @return the minimum values in rotation limitation (if limitation is enabled for specific axis). - */ - public Vector3f getLimitMin() { - return limitMin; - } - - /** - * @return the maximum values in rotation limitation (if limitation is enabled for specific axis). - */ - public Vector3f getLimitMax() { - return limitMax; - } - - /** - * @return the stiffness of the bone - */ - public Vector3f getStiffness() { - return stiffness; - } - - /** - * Tells if the bone is of specified property defined by its flag. - * @param flagMask - * the mask of the flag (constants defined in this class) - * @return true if the bone IS of specified proeprty and false otherwise - */ - public boolean is(int flagMask) { - return (flag & flagMask) != 0; - } - - /** - * @return the root bone context of this bone context - */ - public BoneContext getRoot() { - BoneContext result = this; - while (result.parent != null) { - result = result.parent; - } - return result; - } - - /** - * @return a number of bones from this bone to its root - */ - public int getDistanceFromRoot() { - int result = 0; - BoneContext boneContext = this; - while (boneContext.parent != null) { - boneContext = boneContext.parent; - ++result; - } - return result; - } - - @Override - public String toString() { - return "BoneContext: " + boneName; - } -} diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/animations/BoneEnvelope.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/animations/BoneEnvelope.java deleted file mode 100644 index 83e708dc1..000000000 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/animations/BoneEnvelope.java +++ /dev/null @@ -1,133 +0,0 @@ -package com.jme3.scene.plugins.blender.animations; - -import com.jme3.math.Matrix4f; -import com.jme3.math.Vector3f; -import com.jme3.scene.plugins.blender.file.DynamicArray; -import com.jme3.scene.plugins.blender.file.Structure; - -/** - * An implementation of bone envelope. Used when assigning bones to the mesh by envelopes. - * - * @author Marcin Roguski - */ -public class BoneEnvelope { - /** A defined distance that will be included in the envelope space. */ - private float distance; - /** The bone's weight. */ - private float weight; - /** The radius of the bone's head. */ - private float boneHeadRadius; - /** The radius of the bone's tail. */ - private float boneTailRadius; - /** Head position in rest pose in world space. */ - private Vector3f head; - /** Tail position in rest pose in world space. */ - private Vector3f tail; - - /** - * The constructor of bone envelope. It reads all the needed data. Take notice that the positions of head and tail - * are computed in the world space and that the points' positions given for computations should be in world space as well. - * - * @param boneStructure - * the blender bone structure - * @param armatureWorldMatrix - * the world matrix of the armature object - * @param fixUpAxis - * a variable that tells if we use the Y-is up axis orientation - */ - @SuppressWarnings("unchecked") - public BoneEnvelope(Structure boneStructure, Matrix4f armatureWorldMatrix, boolean fixUpAxis) { - distance = ((Number) boneStructure.getFieldValue("dist")).floatValue(); - weight = ((Number) boneStructure.getFieldValue("weight")).floatValue(); - boneHeadRadius = ((Number) boneStructure.getFieldValue("rad_head")).floatValue(); - boneTailRadius = ((Number) boneStructure.getFieldValue("rad_tail")).floatValue(); - - DynamicArray headArray = (DynamicArray) boneStructure.getFieldValue("arm_head"); - head = new Vector3f(headArray.get(0).floatValue(), headArray.get(1).floatValue(), headArray.get(2).floatValue()); - if (fixUpAxis) { - float z = head.z; - head.z = -head.y; - head.y = z; - } - armatureWorldMatrix.mult(head, head);// move the head point to global space - - DynamicArray tailArray = (DynamicArray) boneStructure.getFieldValue("arm_tail"); - tail = new Vector3f(tailArray.get(0).floatValue(), tailArray.get(1).floatValue(), tailArray.get(2).floatValue()); - if (fixUpAxis) { - float z = tail.z; - tail.z = -tail.y; - tail.y = z; - } - armatureWorldMatrix.mult(tail, tail);// move the tail point to global space - } - - /** - * The method verifies if the given point is inside the envelope. - * @param point - * the point in 3D space (MUST be in a world coordinate space) - * @return true if the point is inside the envelope and false otherwise - */ - public boolean isInEnvelope(Vector3f point) { - Vector3f v = tail.subtract(head); - float boneLength = v.length(); - v.normalizeLocal(); - - // computing a plane that contains 'point' and v is its normal vector - // the plane's equation is: Ax + By + Cz + D = 0, where v = [A, B, C] - float D = -v.dot(point); - - // computing a point where a line that contains head and tail crosses the plane - float temp = -(v.dot(head) + D) / v.dot(v); - Vector3f p = head.add(v.x * temp, v.y * temp, v.z * temp); - - // determining if the point p is on the same or other side of head than the tail point - Vector3f headToPointOnLineVector = p.subtract(head); - float headToPointLength = headToPointOnLineVector.length(); - float cosinus = headToPointOnLineVector.dot(v) / headToPointLength;// the length of v is already = 1; cosinus should be either 1, 0 or -1 - if (cosinus < 0 && headToPointLength > boneHeadRadius || headToPointLength > boneLength + boneTailRadius) { - return false;// the point is outside the anvelope - } - - // now check if the point is inside and envelope - float pointDistanceFromLine = point.subtract(p).length(), maximumDistance = 0; - if (cosinus < 0) { - // checking if the distance from p to point is inside the half sphere defined by head envelope - // compute the distance from the line to the half sphere border - maximumDistance = boneHeadRadius; - } else if (headToPointLength < boneLength) { - // compute the maximum available distance - if (boneTailRadius > boneHeadRadius) { - // compute the distance from head to p - float headToPDistance = p.subtract(head).length(); - // from tangens function we have - float x = headToPDistance * ((boneTailRadius - boneHeadRadius) / boneLength); - maximumDistance = x + boneHeadRadius; - } else if (boneTailRadius < boneHeadRadius) { - // compute the distance from head to p - float tailToPDistance = p.subtract(tail).length(); - // from tangens function we have - float x = tailToPDistance * ((boneHeadRadius - boneTailRadius) / boneLength); - maximumDistance = x + boneTailRadius; - } else { - maximumDistance = boneTailRadius; - } - } else { - // checking if the distance from p to point is inside the half sphere defined by tail envelope - maximumDistance = boneTailRadius; - } - - return pointDistanceFromLine <= maximumDistance + distance; - } - - /** - * @return the weight of the bone - */ - public float getWeight() { - return weight; - } - - @Override - public String toString() { - return "BoneEnvelope [d=" + distance + ", w=" + weight + ", hr=" + boneHeadRadius + ", tr=" + boneTailRadius + ", (" + head + ") -> (" + tail + ")]"; - } -} diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/animations/Ipo.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/animations/Ipo.java deleted file mode 100644 index 6665752b7..000000000 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/animations/Ipo.java +++ /dev/null @@ -1,317 +0,0 @@ -package com.jme3.scene.plugins.blender.animations; - -import java.util.logging.Level; -import java.util.logging.Logger; - -import com.jme3.animation.BoneTrack; -import com.jme3.animation.SpatialTrack; -import com.jme3.animation.Track; -import com.jme3.math.FastMath; -import com.jme3.math.Quaternion; -import com.jme3.math.Vector3f; -import com.jme3.scene.plugins.blender.curves.BezierCurve; - -/** - * This class is used to calculate bezier curves value for the given frames. The - * Ipo (interpolation object) consists of several b-spline curves (connected 3rd - * degree bezier curves) of a different type. - * - * @author Marcin Roguski - */ -public class Ipo { - private static final Logger LOGGER = Logger.getLogger(Ipo.class.getName()); - - public static final int AC_LOC_X = 1; - public static final int AC_LOC_Y = 2; - public static final int AC_LOC_Z = 3; - public static final int OB_ROT_X = 7; - public static final int OB_ROT_Y = 8; - public static final int OB_ROT_Z = 9; - public static final int AC_SIZE_X = 13; - public static final int AC_SIZE_Y = 14; - public static final int AC_SIZE_Z = 15; - public static final int AC_QUAT_W = 25; - public static final int AC_QUAT_X = 26; - public static final int AC_QUAT_Y = 27; - public static final int AC_QUAT_Z = 28; - - /** A list of bezier curves for this interpolation object. */ - private BezierCurve[] bezierCurves; - /** Each ipo contains one bone track. */ - private Track calculatedTrack; - /** This variable indicates if the Y asxis is the UP axis or not. */ - protected boolean fixUpAxis; - /** - * Depending on the blender version rotations are stored in degrees or - * radians so we need to know the version that is used. - */ - protected final int blenderVersion; - - /** - * Constructor. Stores the bezier curves. - * - * @param bezierCurves - * a table of bezier curves - * @param fixUpAxis - * indicates if the Y is the up axis or not - * @param blenderVersion - * the blender version that is currently used - */ - public Ipo(BezierCurve[] bezierCurves, boolean fixUpAxis, int blenderVersion) { - this.bezierCurves = bezierCurves; - this.fixUpAxis = fixUpAxis; - this.blenderVersion = blenderVersion; - } - - /** - * This method calculates the ipo value for the first curve. - * - * @param frame - * the frame for which the value is calculated - * @return calculated ipo value - */ - public double calculateValue(int frame) { - return this.calculateValue(frame, 0); - } - - /** - * This method calculates the ipo value for the curve of the specified - * index. Make sure you do not exceed the curves amount. Alway chech the - * amount of curves before calling this method. - * - * @param frame - * the frame for which the value is calculated - * @param curveIndex - * the index of the curve - * @return calculated ipo value - */ - public double calculateValue(int frame, int curveIndex) { - return bezierCurves[curveIndex].evaluate(frame, BezierCurve.Y_VALUE); - } - - /** - * 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, BoneContext boneContext, 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]; - float[] translation = new float[3]; - Quaternion[] rotations = new Quaternion[framesAmount + 1]; - float[] quaternionRotation = new float[] { localRotation.getX(), localRotation.getY(), localRotation.getZ(), localRotation.getW(), }; - float[] eulerRotation = localRotation.toAngles(null); - Vector3f[] scales = new Vector3f[framesAmount + 1]; - float[] scale = new float[] { localScale.x, localScale.y, localScale.z }; - float degreeToRadiansFactor = 1; - if (blenderVersion < 250) {// in blender earlier than 2.50 the values are stored in degrees - degreeToRadiansFactor *= FastMath.DEG_TO_RAD * 10;// the values in blender are divided by 10, so we need to mult it here - } - int yIndex = 1, zIndex = 2; - boolean swapAxes = spatialTrack && fixUpAxis; - if (swapAxes) { - yIndex = 2; - zIndex = 1; - } - boolean eulerRotationUsed = false, queternionRotationUsed = false; - - // calculating track data - for (int frame = startFrame; frame <= stopFrame; ++frame) { - boolean translationSet = false; - translation[0] = translation[1] = translation[2] = 0; - int index = frame - startFrame; - times[index] = index * timeBetweenFrames;// start + (frame - 1) * timeBetweenFrames; - for (int j = 0; j < bezierCurves.length; ++j) { - double value = bezierCurves[j].evaluate(frame, BezierCurve.Y_VALUE); - switch (bezierCurves[j].getType()) { - // LOCATION - case AC_LOC_X: - translation[0] = (float) value; - translationSet = true; - break; - case AC_LOC_Y: - if (swapAxes && value != 0) { - value = -value; - } - translation[yIndex] = (float) value; - translationSet = true; - break; - case AC_LOC_Z: - translation[zIndex] = (float) value; - translationSet = true; - break; - - // EULER ROTATION - case OB_ROT_X: - eulerRotationUsed = true; - eulerRotation[0] = (float) value * degreeToRadiansFactor; - break; - case OB_ROT_Y: - eulerRotationUsed = true; - if (swapAxes && value != 0) { - value = -value; - } - eulerRotation[yIndex] = (float) value * degreeToRadiansFactor; - break; - case OB_ROT_Z: - eulerRotationUsed = true; - eulerRotation[zIndex] = (float) value * degreeToRadiansFactor; - break; - - // SIZE - case AC_SIZE_X: - scale[0] = (float) value; - break; - case AC_SIZE_Y: - scale[yIndex] = (float) value; - break; - case AC_SIZE_Z: - scale[zIndex] = (float) value; - break; - - // QUATERNION ROTATION (used with bone animation) - case AC_QUAT_W: - queternionRotationUsed = true; - quaternionRotation[3] = (float) value; - break; - case AC_QUAT_X: - queternionRotationUsed = true; - quaternionRotation[0] = (float) value; - break; - case AC_QUAT_Y: - queternionRotationUsed = true; - if (swapAxes && value != 0) { - value = -value; - } - quaternionRotation[yIndex] = (float) value; - break; - case AC_QUAT_Z: - quaternionRotation[zIndex] = (float) value; - break; - default: - LOGGER.log(Level.WARNING, "Unknown ipo curve type: {0}.", bezierCurves[j].getType()); - } - } - if(translationSet) { - translations[index] = localRotation.multLocal(new Vector3f(translation[0], translation[1], translation[2])); - } else { - translations[index] = new Vector3f(); - } - - if(boneContext != null) { - if(boneContext.getBone().getParent() == null && boneContext.is(BoneContext.NO_LOCAL_LOCATION)) { - float temp = translations[index].z; - translations[index].z = -translations[index].y; - translations[index].y = temp; - } - } - - if (queternionRotationUsed) { - rotations[index] = new Quaternion(quaternionRotation[0], quaternionRotation[1], quaternionRotation[2], quaternionRotation[3]); - } else { - rotations[index] = new Quaternion().fromAngles(eulerRotation); - } - - scales[index] = new Vector3f(scale[0], scale[1], scale[2]); - } - if (spatialTrack) { - calculatedTrack = new SpatialTrack(times, translations, rotations, scales); - } else { - calculatedTrack = new BoneTrack(targetIndex, times, translations, rotations, scales); - } - - if (queternionRotationUsed && eulerRotationUsed) { - LOGGER.warning("Animation uses both euler and quaternion tracks for rotations. Quaternion rotation is applied. Make sure that this is what you wanted!"); - } - } - - return calculatedTrack; - } - - /** - * 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) - */ - /* package */static 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 double calculateValue(int frame) { - return constValue; - } - - @Override - public double calculateValue(int frame, int curveIndex) { - return constValue; - } - - @Override - public BoneTrack calculateTrack(int boneIndex, BoneContext boneContext, 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!"); - } - } -} diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/cameras/CameraHelper.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/cameras/CameraHelper.java deleted file mode 100644 index 70cb09b1f..000000000 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/cameras/CameraHelper.java +++ /dev/null @@ -1,148 +0,0 @@ -package com.jme3.scene.plugins.blender.cameras; - -import java.util.logging.Level; -import java.util.logging.Logger; - -import com.jme3.math.FastMath; -import com.jme3.renderer.Camera; -import com.jme3.scene.plugins.blender.AbstractBlenderHelper; -import com.jme3.scene.plugins.blender.BlenderContext; -import com.jme3.scene.plugins.blender.file.BlenderFileException; -import com.jme3.scene.plugins.blender.file.Structure; - -/** - * A class that is used to load cameras into the scene. - * @author Marcin Roguski - */ -public class CameraHelper extends AbstractBlenderHelper { - - private static final Logger LOGGER = Logger.getLogger(CameraHelper.class.getName()); - protected static final int DEFAULT_CAM_WIDTH = 640; - protected static final int DEFAULT_CAM_HEIGHT = 480; - - /** - * This constructor parses the given blender version and stores the result. Some functionalities may differ in - * different blender versions. - * @param blenderVersion - * the version read from the blend file - * @param blenderContext - * the blender context - */ - public CameraHelper(String blenderVersion, BlenderContext blenderContext) { - super(blenderVersion, blenderContext); - } - - /** - * This method converts the given structure to jme camera. - * - * @param structure - * camera structure - * @return jme camera object - * @throws BlenderFileException - * an exception is thrown when there are problems with the - * blender file - */ - public Camera toCamera(Structure structure, BlenderContext blenderContext) throws BlenderFileException { - if (blenderVersion >= 250) { - return this.toCamera250(structure, blenderContext.getSceneStructure()); - } else { - return this.toCamera249(structure); - } - } - - /** - * This method converts the given structure to jme camera. Should be used form blender 2.5+. - * - * @param structure - * camera structure - * @param sceneStructure - * scene structure - * @return jme camera object - * @throws BlenderFileException - * an exception is thrown when there are problems with the - * blender file - */ - private Camera toCamera250(Structure structure, Structure sceneStructure) throws BlenderFileException { - int width = DEFAULT_CAM_WIDTH; - int height = DEFAULT_CAM_HEIGHT; - if (sceneStructure != null) { - Structure renderData = (Structure) sceneStructure.getFieldValue("r"); - width = ((Number) renderData.getFieldValue("xsch")).shortValue(); - height = ((Number) renderData.getFieldValue("ysch")).shortValue(); - } - Camera camera = new Camera(width, height); - int type = ((Number) structure.getFieldValue("type")).intValue(); - if (type != 0 && type != 1) { - LOGGER.log(Level.WARNING, "Unknown camera type: {0}. Perspective camera is being used!", type); - type = 0; - } - // type==0 - perspective; type==1 - orthographic; perspective is used as default - camera.setParallelProjection(type == 1); - float aspect = width / (float) height; - float fovY; // Vertical field of view in degrees - float clipsta = ((Number) structure.getFieldValue("clipsta")).floatValue(); - float clipend = ((Number) structure.getFieldValue("clipend")).floatValue(); - if (type == 0) { - // Convert lens MM to vertical degrees in fovY, see Blender rna_Camera_angle_get() - // Default sensor size prior to 2.60 was 32. - float sensor = 32.0f; - boolean sensorVertical = false; - Number sensorFit = (Number) structure.getFieldValue("sensor_fit"); - if (sensorFit != null) { - // If sensor_fit is vert (2), then sensor_y is used - sensorVertical = sensorFit.byteValue() == 2; - String sensorName = "sensor_x"; - if (sensorVertical) { - sensorName = "sensor_y"; - } - sensor = ((Number) structure.getFieldValue(sensorName)).floatValue(); - } - float focalLength = ((Number) structure.getFieldValue("lens")).floatValue(); - float fov = 2.0f * FastMath.atan(sensor / 2.0f / focalLength); - if (sensorVertical) { - fovY = fov * FastMath.RAD_TO_DEG; - } else { - // Convert fov from horizontal to vertical - fovY = 2.0f * FastMath.atan(FastMath.tan(fov / 2.0f) / aspect) * FastMath.RAD_TO_DEG; - } - } else { - // This probably is not correct. - fovY = ((Number) structure.getFieldValue("ortho_scale")).floatValue(); - } - camera.setFrustumPerspective(fovY, aspect, clipsta, clipend); - camera.setName(structure.getName()); - return camera; - } - - /** - * This method converts the given structure to jme camera. Should be used form blender 2.49. - * - * @param structure - * camera structure - * @return jme camera object - * @throws BlenderFileException - * an exception is thrown when there are problems with the - * blender file - */ - private Camera toCamera249(Structure structure) throws BlenderFileException { - Camera camera = new Camera(DEFAULT_CAM_WIDTH, DEFAULT_CAM_HEIGHT); - int type = ((Number) structure.getFieldValue("type")).intValue(); - if (type != 0 && type != 1) { - LOGGER.log(Level.WARNING, "Unknown camera type: {0}. Perspective camera is being used!", type); - type = 0; - } - // type==0 - perspective; type==1 - orthographic; perspective is used as default - camera.setParallelProjection(type == 1); - float aspect = 0; - float clipsta = ((Number) structure.getFieldValue("clipsta")).floatValue(); - float clipend = ((Number) structure.getFieldValue("clipend")).floatValue(); - if (type == 0) { - aspect = ((Number) structure.getFieldValue("lens")).floatValue(); - } else { - aspect = ((Number) structure.getFieldValue("ortho_scale")).floatValue(); - } - camera.setFrustumPerspective(aspect, camera.getWidth() / camera.getHeight(), clipsta, clipend); - camera.setName(structure.getName()); - return camera; - } -} diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/BoneConstraint.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/BoneConstraint.java deleted file mode 100644 index fdba8af3f..000000000 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/BoneConstraint.java +++ /dev/null @@ -1,88 +0,0 @@ -package com.jme3.scene.plugins.blender.constraints; - -import java.util.logging.Level; -import java.util.logging.Logger; - -import com.jme3.scene.Spatial; -import com.jme3.scene.plugins.blender.BlenderContext; -import com.jme3.scene.plugins.blender.BlenderContext.LoadedDataType; -import com.jme3.scene.plugins.blender.animations.BoneContext; -import com.jme3.scene.plugins.blender.animations.Ipo; -import com.jme3.scene.plugins.blender.file.BlenderFileException; -import com.jme3.scene.plugins.blender.file.Structure; -import com.jme3.scene.plugins.blender.objects.ObjectHelper; - -/** - * Constraint applied on the bone. - * - * @author Marcin Roguski (Kaelthas) - */ -/* package */class BoneConstraint extends Constraint { - private static final Logger LOGGER = Logger.getLogger(BoneConstraint.class.getName()); - - /** - * The bone constraint constructor. - * - * @param constraintStructure - * the constraint's structure - * @param ownerOMA - * the OMA of the bone that owns the constraint - * @param influenceIpo - * the influence interpolation curve - * @param blenderContext - * the blender context - * @throws BlenderFileException - * exception thrown when problems with blender file occur - */ - public BoneConstraint(Structure constraintStructure, Long ownerOMA, Ipo influenceIpo, BlenderContext blenderContext) throws BlenderFileException { - super(constraintStructure, ownerOMA, influenceIpo, blenderContext); - } - - @Override - public boolean validate() { - if (targetOMA != null) { - Spatial nodeTarget = (Spatial) blenderContext.getLoadedFeature(targetOMA, LoadedDataType.FEATURE); - if (nodeTarget == null) { - LOGGER.log(Level.WARNING, "Cannot find target for constraint: {0}.", name); - return false; - } - // the second part of the if expression verifies if the found node - // (if any) is an armature node - if (blenderContext.getMarkerValue(ObjectHelper.ARMATURE_NODE_MARKER, nodeTarget) != null) { - if (subtargetName.trim().isEmpty()) { - LOGGER.log(Level.WARNING, "No bone target specified for constraint: {0}.", name); - return false; - } - // if the target is not an object node then it is an Armature, - // so make sure the bone is in the current skeleton - BoneContext boneContext = blenderContext.getBoneContext(ownerOMA); - 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); - return false; - } - } - } - return constraintDefinition == null ? true : constraintDefinition.isTargetRequired(); - } - - @Override - public void apply(int frame) { - super.apply(frame); - blenderContext.getBoneContext(ownerOMA).getBone().updateModelTransforms(); - } - - @Override - public Long getTargetOMA() { - if(targetOMA != null && subtargetName != null && !subtargetName.trim().isEmpty()) { - Spatial nodeTarget = (Spatial) blenderContext.getLoadedFeature(targetOMA, LoadedDataType.FEATURE); - if(nodeTarget != null) { - if(blenderContext.getMarkerValue(ObjectHelper.ARMATURE_NODE_MARKER, nodeTarget) != null) { - BoneContext boneContext = blenderContext.getBoneByName(targetOMA, subtargetName); - return boneContext != null ? boneContext.getBoneOma() : 0L; - } - return targetOMA; - } - } - return 0L; - } -} diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/Constraint.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/Constraint.java deleted file mode 100644 index d96a1f2ce..000000000 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/Constraint.java +++ /dev/null @@ -1,186 +0,0 @@ -package com.jme3.scene.plugins.blender.constraints; - -import java.util.Set; -import java.util.logging.Level; -import java.util.logging.Logger; - -import com.jme3.math.Transform; -import com.jme3.scene.plugins.blender.BlenderContext; -import com.jme3.scene.plugins.blender.animations.Ipo; -import com.jme3.scene.plugins.blender.constraints.ConstraintHelper.Space; -import com.jme3.scene.plugins.blender.constraints.definitions.ConstraintDefinition; -import com.jme3.scene.plugins.blender.constraints.definitions.ConstraintDefinitionFactory; -import com.jme3.scene.plugins.blender.file.BlenderFileException; -import com.jme3.scene.plugins.blender.file.Pointer; -import com.jme3.scene.plugins.blender.file.Structure; - -/** - * The implementation of a constraint. - * - * @author Marcin Roguski (Kaelthas) - */ -public abstract class Constraint { - private static final Logger LOGGER = Logger.getLogger(Constraint.class.getName()); - - /** The name of this constraint. */ - protected final String name; - /** Indicates if the constraint is already baked or not. */ - protected boolean baked; - - protected Space ownerSpace; - protected final ConstraintDefinition constraintDefinition; - protected Long ownerOMA; - - protected Long targetOMA; - protected Space targetSpace; - protected String subtargetName; - - /** The ipo object defining influence. */ - protected final Ipo ipo; - /** The blender context. */ - protected final BlenderContext blenderContext; - protected final ConstraintHelper constraintHelper; - - /** - * This constructor creates the constraint instance. - * - * @param constraintStructure - * the constraint's structure (bConstraint clss in blender 2.49). - * @param ownerOMA - * the old memory address of the constraint owner - * @param influenceIpo - * the ipo curve of the influence factor - * @param blenderContext - * the blender context - * @throws BlenderFileException - * this exception is thrown when the blender file is somehow - * corrupted - */ - public Constraint(Structure constraintStructure, Long ownerOMA, Ipo influenceIpo, BlenderContext blenderContext) throws BlenderFileException { - this.blenderContext = blenderContext; - name = constraintStructure.getFieldValue("name").toString(); - Pointer pData = (Pointer) constraintStructure.getFieldValue("data"); - if (pData.isNotNull()) { - Structure data = pData.fetchData().get(0); - constraintDefinition = ConstraintDefinitionFactory.createConstraintDefinition(data, name, ownerOMA, blenderContext); - Pointer pTar = (Pointer) data.getFieldValue("tar"); - if (pTar != null && pTar.isNotNull()) { - targetOMA = pTar.getOldMemoryAddress(); - targetSpace = Space.valueOf(((Number) constraintStructure.getFieldValue("tarspace")).byteValue()); - Object subtargetValue = data.getFieldValue("subtarget"); - if (subtargetValue != null) {// not all constraint data have the - // subtarget field - subtargetName = subtargetValue.toString(); - } - } - } else { - // Null constraint has no data, so create it here - constraintDefinition = ConstraintDefinitionFactory.createConstraintDefinition(null, name, null, blenderContext); - } - ownerSpace = Space.valueOf(((Number) constraintStructure.getFieldValue("ownspace")).byteValue()); - ipo = influenceIpo; - this.ownerOMA = ownerOMA; - constraintHelper = blenderContext.getHelper(ConstraintHelper.class); - LOGGER.log(Level.INFO, "Created constraint: {0} with definition: {1}", new Object[] { name, constraintDefinition }); - } - - /** - * @return true if the constraint is implemented and false - * otherwise - */ - public boolean isImplemented() { - return constraintDefinition == null ? true : constraintDefinition.isImplemented(); - } - - /** - * @return the name of the constraint type, similar to the constraint name - * used in Blender - */ - public String getConstraintTypeName() { - return constraintDefinition.getConstraintTypeName(); - } - - /** - * @return the OMAs of the features whose transform had been altered beside the constraint owner - */ - public Set getAlteredOmas() { - return constraintDefinition.getAlteredOmas(); - } - - /** - * Performs validation before baking. Checks factors that can prevent - * constraint from baking that could not be checked during constraint - * loading. - */ - public abstract boolean validate(); - - /** - * @return the OMA of the target or 0 if no target is specified for the constraint - */ - public abstract Long getTargetOMA(); - - /** - * Applies the constraint to owner (and in some cases can alter other bones of the skeleton). - * @param frame - * the frame of the animation - */ - public void apply(int frame) { - if (LOGGER.isLoggable(Level.FINEST)) { - LOGGER.log(Level.FINEST, "Applying constraint: {0} for frame {1}", new Object[] { name, frame }); - } - Transform targetTransform = targetOMA != null ? constraintHelper.getTransform(targetOMA, subtargetName, targetSpace) : null; - constraintDefinition.bake(ownerSpace, targetSpace, targetTransform, (float) ipo.calculateValue(frame)); - } - - /** - * @return determines if the definition of the constraint will change the bone in any way; in most cases - * it is possible to tell that even before the constraint baking simulation is started, so we can discard such bones from constraint - * computing to improve the computation speed and lower the computations complexity - */ - public boolean isTrackToBeChanged() { - return constraintDefinition == null ? false : constraintDefinition.isTrackToBeChanged(); - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + (name == null ? 0 : name.hashCode()); - result = prime * result + (ownerOMA == null ? 0 : ownerOMA.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (this.getClass() != obj.getClass()) { - return false; - } - Constraint other = (Constraint) obj; - if (name == null) { - if (other.name != null) { - return false; - } - } else if (!name.equals(other.name)) { - return false; - } - if (ownerOMA == null) { - if (other.ownerOMA != null) { - return false; - } - } else if (!ownerOMA.equals(other.ownerOMA)) { - return false; - } - return true; - } - - @Override - public String toString() { - return "Constraint(name = " + name + ", def = " + constraintDefinition + ")"; - } -} \ No newline at end of file diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/ConstraintHelper.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/ConstraintHelper.java deleted file mode 100644 index eb253396a..000000000 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/ConstraintHelper.java +++ /dev/null @@ -1,476 +0,0 @@ -package com.jme3.scene.plugins.blender.constraints; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.logging.Logger; - -import com.jme3.animation.Bone; -import com.jme3.animation.Skeleton; -import com.jme3.math.Matrix4f; -import com.jme3.math.Quaternion; -import com.jme3.math.Transform; -import com.jme3.math.Vector3f; -import com.jme3.scene.Spatial; -import com.jme3.scene.plugins.blender.AbstractBlenderHelper; -import com.jme3.scene.plugins.blender.BlenderContext; -import com.jme3.scene.plugins.blender.BlenderContext.LoadedDataType; -import com.jme3.scene.plugins.blender.animations.AnimationHelper; -import com.jme3.scene.plugins.blender.animations.BoneContext; -import com.jme3.scene.plugins.blender.animations.Ipo; -import com.jme3.scene.plugins.blender.file.BlenderFileException; -import com.jme3.scene.plugins.blender.file.Pointer; -import com.jme3.scene.plugins.blender.file.Structure; -import com.jme3.scene.plugins.blender.objects.ObjectHelper; -import com.jme3.util.TempVars; - -/** - * This class should be used for constraint calculations. - * - * @author Marcin Roguski (Kaelthas) - */ -public class ConstraintHelper extends AbstractBlenderHelper { - private static final Logger LOGGER = Logger.getLogger(ConstraintHelper.class.getName()); - - /** - * Helper constructor. - * - * @param blenderVersion - * the version read from the blend file - * @param blenderContext - * the blender context - */ - public ConstraintHelper(String blenderVersion, BlenderContext blenderContext) { - super(blenderVersion, blenderContext); - } - - /** - * This method reads constraints for for the given structure. The - * constraints are loaded only once for object/bone. - * - * @param objectStructure - * the structure we read constraint's for - * @param blenderContext - * the blender context - * @throws BlenderFileException - */ - public void loadConstraints(Structure objectStructure, BlenderContext blenderContext) throws BlenderFileException { - LOGGER.fine("Loading constraints."); - // reading influence ipos for the constraints - AnimationHelper animationHelper = blenderContext.getHelper(AnimationHelper.class); - Map> constraintsIpos = new HashMap>(); - Pointer pActions = (Pointer) objectStructure.getFieldValue("action"); - if (pActions.isNotNull()) { - List actions = pActions.fetchData(); - for (Structure action : actions) { - Structure chanbase = (Structure) action.getFieldValue("chanbase"); - List actionChannels = chanbase.evaluateListBase(); - for (Structure actionChannel : actionChannels) { - Map ipos = new HashMap(); - Structure constChannels = (Structure) actionChannel.getFieldValue("constraintChannels"); - List constraintChannels = constChannels.evaluateListBase(); - for (Structure constraintChannel : constraintChannels) { - Pointer pIpo = (Pointer) constraintChannel.getFieldValue("ipo"); - if (pIpo.isNotNull()) { - String constraintName = constraintChannel.getFieldValue("name").toString(); - Ipo ipo = animationHelper.fromIpoStructure(pIpo.fetchData().get(0), blenderContext); - ipos.put(constraintName, ipo); - } - } - String actionName = actionChannel.getFieldValue("name").toString(); - constraintsIpos.put(actionName, ipos); - } - } - } - - // loading constraints connected with the object's bones - Pointer pPose = (Pointer) objectStructure.getFieldValue("pose"); - if (pPose.isNotNull()) { - List poseChannels = ((Structure) pPose.fetchData().get(0).getFieldValue("chanbase")).evaluateListBase(); - for (Structure poseChannel : poseChannels) { - List constraintsList = new ArrayList(); - Long boneOMA = Long.valueOf(((Pointer) poseChannel.getFieldValue("bone")).getOldMemoryAddress()); - - // the name is read directly from structure because bone might - // not yet be loaded - String name = blenderContext.getFileBlock(boneOMA).getStructure(blenderContext).getFieldValue("name").toString(); - List constraints = ((Structure) poseChannel.getFieldValue("constraints")).evaluateListBase(); - for (Structure constraint : constraints) { - String constraintName = constraint.getFieldValue("name").toString(); - Map ipoMap = constraintsIpos.get(name); - Ipo ipo = ipoMap == null ? null : ipoMap.get(constraintName); - if (ipo == null) { - float enforce = ((Number) constraint.getFieldValue("enforce")).floatValue(); - ipo = animationHelper.fromValue(enforce); - } - constraintsList.add(new BoneConstraint(constraint, boneOMA, ipo, blenderContext)); - } - blenderContext.addConstraints(boneOMA, constraintsList); - } - } - - // loading constraints connected with the object itself - List constraints = ((Structure) objectStructure.getFieldValue("constraints")).evaluateListBase(); - if (constraints != null && constraints.size() > 0) { - Pointer pData = (Pointer) objectStructure.getFieldValue("data"); - String dataType = pData.isNotNull() ? pData.fetchData().get(0).getType() : null; - List constraintsList = new ArrayList(constraints.size()); - - for (Structure constraint : constraints) { - String constraintName = constraint.getFieldValue("name").toString(); - String objectName = objectStructure.getName(); - - Map objectConstraintsIpos = constraintsIpos.get(objectName); - Ipo ipo = objectConstraintsIpos != null ? objectConstraintsIpos.get(constraintName) : null; - if (ipo == null) { - float enforce = ((Number) constraint.getFieldValue("enforce")).floatValue(); - ipo = animationHelper.fromValue(enforce); - } - - constraintsList.add(this.createConstraint(dataType, constraint, objectStructure.getOldMemoryAddress(), ipo, blenderContext)); - } - blenderContext.addConstraints(objectStructure.getOldMemoryAddress(), constraintsList); - } - } - - /** - * This method creates a proper constraint object depending on the object's - * data type. Supported data types:
  • Mesh
  • Armature
  • Camera
  • - * Lamp Bone constraints are created in a different place. - * - * @param dataType - * the type of the object's data - * @param constraintStructure - * the constraint structure - * @param ownerOMA - * the owner OMA - * @param influenceIpo - * the influence interpolation curve - * @param blenderContext - * the blender context - * @return constraint object for the required type - * @throws BlenderFileException - * thrown when problems with blender file occurred - */ - private Constraint createConstraint(String dataType, Structure constraintStructure, Long ownerOMA, Ipo influenceIpo, BlenderContext blenderContext) throws BlenderFileException { - if (dataType == null || "Mesh".equalsIgnoreCase(dataType) || "Camera".equalsIgnoreCase(dataType) || "Lamp".equalsIgnoreCase(dataType)) { - return new SpatialConstraint(constraintStructure, ownerOMA, influenceIpo, blenderContext); - } else if ("Armature".equalsIgnoreCase(dataType)) { - return new SkeletonConstraint(constraintStructure, ownerOMA, influenceIpo, blenderContext); - } else { - throw new IllegalArgumentException("Unsupported data type for applying constraints: " + dataType); - } - } - - /** - * The method bakes all available and valid constraints. - * - * @param blenderContext - * the blender context - */ - public void bakeConstraints(BlenderContext blenderContext) { - Set owners = new HashSet(); - for (Constraint constraint : blenderContext.getAllConstraints()) { - if(constraint instanceof BoneConstraint) { - BoneContext boneContext = blenderContext.getBoneContext(constraint.ownerOMA); - owners.add(boneContext.getArmatureObjectOMA()); - } else { - Spatial spatial = (Spatial) blenderContext.getLoadedFeature(constraint.ownerOMA, LoadedDataType.FEATURE); - while (spatial.getParent() != null) { - spatial = spatial.getParent(); - } - owners.add((Long)blenderContext.getMarkerValue(ObjectHelper.OMA_MARKER, spatial)); - } - } - - List simulationRootNodes = new ArrayList(owners.size()); - for(Long ownerOMA : owners) { - simulationRootNodes.add(new SimulationNode(ownerOMA, blenderContext)); - } - - for (SimulationNode node : simulationRootNodes) { - node.simulate(); - } - } - - /** - * The method retrieves the transform from a feature in a given space. - * - * @param oma - * the OMA of the feature (spatial or armature node) - * @param subtargetName - * the feature's subtarget (bone in a case of armature's node) - * @param space - * the space the transform is evaluated to - * @return the transform of a feature in a given space - */ - public Transform getTransform(Long oma, String subtargetName, Space space) { - Spatial feature = (Spatial) blenderContext.getLoadedFeature(oma, LoadedDataType.FEATURE); - boolean isArmature = blenderContext.getMarkerValue(ObjectHelper.ARMATURE_NODE_MARKER, feature) != null; - if (isArmature) { - blenderContext.getSkeleton(oma).updateWorldVectors(); - BoneContext targetBoneContext = blenderContext.getBoneByName(oma, subtargetName); - Bone bone = targetBoneContext.getBone(); - - if (bone.getParent() == null && (space == Space.CONSTRAINT_SPACE_LOCAL || space == Space.CONSTRAINT_SPACE_PARLOCAL)) { - space = Space.CONSTRAINT_SPACE_POSE; - } - - TempVars tempVars = TempVars.get();// use readable names of the matrices so that the code is more clear - Transform result; - switch (space) { - case CONSTRAINT_SPACE_WORLD: - Spatial model = (Spatial) blenderContext.getLoadedFeature(targetBoneContext.getSkeletonOwnerOma(), LoadedDataType.FEATURE); - Matrix4f boneModelMatrix = this.toMatrix(bone.getModelSpacePosition(), bone.getModelSpaceRotation(), bone.getModelSpaceScale(), tempVars.tempMat4); - Matrix4f modelWorldMatrix = this.toMatrix(model.getWorldTransform(), tempVars.tempMat42); - Matrix4f boneMatrixInWorldSpace = modelWorldMatrix.multLocal(boneModelMatrix); - 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()); - break; - case CONSTRAINT_SPACE_POSE: { - Matrix4f boneWorldMatrix = this.toMatrix(this.getTransform(oma, subtargetName, Space.CONSTRAINT_SPACE_WORLD), tempVars.tempMat4); - Matrix4f armatureInvertedWorldMatrix = this.toMatrix(feature.getWorldTransform(), tempVars.tempMat42).invertLocal(); - Matrix4f bonePoseMatrix = armatureInvertedWorldMatrix.multLocal(boneWorldMatrix); - result = new Transform(bonePoseMatrix.toTranslationVector(), bonePoseMatrix.toRotationQuat(), bonePoseMatrix.toScaleVector()); - break; - } - case CONSTRAINT_SPACE_PARLOCAL: { - Matrix4f boneWorldMatrix = this.toMatrix(this.getTransform(oma, subtargetName, Space.CONSTRAINT_SPACE_WORLD), tempVars.tempMat4); - Matrix4f armatureInvertedWorldMatrix = this.toMatrix(feature.getWorldTransform(), tempVars.tempMat42).invertLocal(); - Matrix4f bonePoseMatrix = armatureInvertedWorldMatrix.multLocal(boneWorldMatrix); - result = new Transform(bonePoseMatrix.toTranslationVector(), bonePoseMatrix.toRotationQuat(), bonePoseMatrix.toScaleVector()); - Bone parent = bone.getParent(); - if(parent != null) { - BoneContext parentContext = blenderContext.getBoneContext(parent); - Vector3f head = parent.getModelSpacePosition(); - Vector3f tail = head.add(bone.getModelSpaceRotation().mult(Vector3f.UNIT_Y.mult(parentContext.getLength()))); - result.getTranslation().subtractLocal(tail); - - } - break; - } - default: - throw new IllegalStateException("Unknown space type: " + space); - } - tempVars.release(); - return result; - } else { - switch (space) { - case CONSTRAINT_SPACE_LOCAL: - return feature.getLocalTransform(); - case CONSTRAINT_SPACE_WORLD: - return feature.getWorldTransform(); - case CONSTRAINT_SPACE_PARLOCAL: - case CONSTRAINT_SPACE_POSE: - throw new IllegalStateException("Nodes can have only Local and World spaces applied!"); - default: - throw new IllegalStateException("Unknown space type: " + space); - } - } - } - - /** - * Applies transform to a feature (bone or spatial). Computations transform - * the given transformation from the given space to the feature's local - * space. - * - * @param oma - * the OMA of the feature we apply transformation to - * @param subtargetName - * the name of the feature's subtarget (bone in case of armature) - * @param space - * the space in which the given transform is to be applied - * @param transform - * the transform we apply - */ - public void applyTransform(Long oma, String subtargetName, Space space, Transform transform) { - Spatial feature = (Spatial) blenderContext.getLoadedFeature(oma, LoadedDataType.FEATURE); - boolean isArmature = blenderContext.getMarkerValue(ObjectHelper.ARMATURE_NODE_MARKER, feature) != null; - if (isArmature) { - Skeleton skeleton = blenderContext.getSkeleton(oma); - BoneContext targetBoneContext = blenderContext.getBoneByName(oma, subtargetName); - Bone bone = targetBoneContext.getBone(); - - if (bone.getParent() == null && (space == Space.CONSTRAINT_SPACE_LOCAL || space == Space.CONSTRAINT_SPACE_PARLOCAL)) { - space = Space.CONSTRAINT_SPACE_POSE; - } - - TempVars tempVars = TempVars.get(); - switch (space) { - case CONSTRAINT_SPACE_LOCAL: - assert bone.getParent() != null : "CONSTRAINT_SPACE_LOCAL should be evaluated as CONSTRAINT_SPACE_POSE if the bone has no parent!"; - bone.setBindTransforms(transform.getTranslation(), transform.getRotation(), transform.getScale()); - break; - case CONSTRAINT_SPACE_WORLD: { - Matrix4f boneMatrixInWorldSpace = this.toMatrix(transform, tempVars.tempMat4); - Matrix4f modelWorldMatrix = this.toMatrix(this.getTransform(targetBoneContext.getSkeletonOwnerOma(), null, Space.CONSTRAINT_SPACE_WORLD), tempVars.tempMat42); - Matrix4f boneMatrixInModelSpace = modelWorldMatrix.invertLocal().multLocal(boneMatrixInWorldSpace); - Bone parent = bone.getParent(); - if (parent != null) { - Matrix4f parentMatrixInModelSpace = this.toMatrix(parent.getModelSpacePosition(), parent.getModelSpaceRotation(), parent.getModelSpaceScale(), tempVars.tempMat4); - boneMatrixInModelSpace = parentMatrixInModelSpace.invertLocal().multLocal(boneMatrixInModelSpace); - } - bone.setBindTransforms(boneMatrixInModelSpace.toTranslationVector(), boneMatrixInModelSpace.toRotationQuat(), boneMatrixInModelSpace.toScaleVector()); - break; - } - case CONSTRAINT_SPACE_POSE: { - Matrix4f armatureWorldMatrix = this.toMatrix(feature.getWorldTransform(), tempVars.tempMat4); - Matrix4f boneMatrixInWorldSpace = armatureWorldMatrix.multLocal(this.toMatrix(transform, tempVars.tempMat42)); - Matrix4f invertedModelMatrix = this.toMatrix(this.getTransform(targetBoneContext.getSkeletonOwnerOma(), null, Space.CONSTRAINT_SPACE_WORLD), tempVars.tempMat42).invertLocal(); - Matrix4f boneMatrixInModelSpace = invertedModelMatrix.multLocal(boneMatrixInWorldSpace); - Bone parent = bone.getParent(); - if (parent != null) { - Matrix4f parentMatrixInModelSpace = this.toMatrix(parent.getModelSpacePosition(), parent.getModelSpaceRotation(), parent.getModelSpaceScale(), tempVars.tempMat4); - boneMatrixInModelSpace = parentMatrixInModelSpace.invertLocal().multLocal(boneMatrixInModelSpace); - } - bone.setBindTransforms(boneMatrixInModelSpace.toTranslationVector(), boneMatrixInModelSpace.toRotationQuat(), boneMatrixInModelSpace.toScaleVector()); - break; - } - case CONSTRAINT_SPACE_PARLOCAL: - Matrix4f armatureWorldMatrix = this.toMatrix(feature.getWorldTransform(), tempVars.tempMat4); - Matrix4f boneMatrixInWorldSpace = armatureWorldMatrix.multLocal(this.toMatrix(transform, tempVars.tempMat42)); - Matrix4f invertedModelMatrix = this.toMatrix(this.getTransform(targetBoneContext.getSkeletonOwnerOma(), null, Space.CONSTRAINT_SPACE_WORLD), tempVars.tempMat42).invertLocal(); - Matrix4f boneMatrixInModelSpace = invertedModelMatrix.multLocal(boneMatrixInWorldSpace); - Bone parent = bone.getParent(); - if (parent != null) { - //first add the initial parent matrix to the bone's model matrix - BoneContext parentContext = blenderContext.getBoneContext(parent); - - Matrix4f initialParentMatrixInModelSpace = parentContext.getBoneMatrixInModelSpace(); - Matrix4f currentParentMatrixInModelSpace = this.toMatrix(parent.getModelSpacePosition(), parent.getModelSpaceRotation(), parent.getModelSpaceScale(), tempVars.tempMat4); - //the bone will now move with its parent in model space - - //now we need to subtract the difference between current parent's model matrix and its initial model matrix - boneMatrixInModelSpace = initialParentMatrixInModelSpace.mult(boneMatrixInModelSpace); - - Matrix4f diffMatrix = initialParentMatrixInModelSpace.mult(currentParentMatrixInModelSpace.invert()); - boneMatrixInModelSpace.multLocal(diffMatrix); - //now the bone will have its position in model space with initial parent's model matrix added - } - bone.setBindTransforms(boneMatrixInModelSpace.toTranslationVector(), boneMatrixInModelSpace.toRotationQuat(), boneMatrixInModelSpace.toScaleVector()); - break; - default: - tempVars.release(); - throw new IllegalStateException("Invalid space type for target object: " + space.toString()); - } - tempVars.release(); - skeleton.updateWorldVectors(); - } else { - switch (space) { - case CONSTRAINT_SPACE_LOCAL: - feature.getLocalTransform().set(transform); - break; - case CONSTRAINT_SPACE_WORLD: - if (feature.getParent() == null) { - feature.setLocalTransform(transform); - } else { - Transform parentWorldTransform = feature.getParent().getWorldTransform(); - - TempVars tempVars = TempVars.get(); - Matrix4f parentInverseMatrix = this.toMatrix(parentWorldTransform, tempVars.tempMat4).invertLocal(); - Matrix4f m = this.toMatrix(transform, tempVars.tempMat42); - m = m.multLocal(parentInverseMatrix); - tempVars.release(); - - transform.setTranslation(m.toTranslationVector()); - transform.setRotation(m.toRotationQuat()); - transform.setScale(m.toScaleVector()); - - feature.setLocalTransform(transform); - } - break; - default: - throw new IllegalStateException("Invalid space type for spatial object: " + space.toString()); - } - } - } - - /** - * Converts given transform to the matrix. - * - * @param transform - * the transform to be converted - * @param store - * the matrix where the result will be stored - * @return the store matrix - */ - public Matrix4f toMatrix(Transform transform, Matrix4f store) { - if (transform != null) { - return this.toMatrix(transform.getTranslation(), transform.getRotation(), transform.getScale(), store); - } - store.loadIdentity(); - return store; - } - - /** - * Converts given transformation parameters into the matrix. - * - * @param position - * the position of the feature - * @param rotation - * the rotation of the feature - * @param scale - * the scale of the feature - * @param store - * the matrix where the result will be stored - * @return the store matrix - */ - private Matrix4f toMatrix(Vector3f position, Quaternion rotation, Vector3f scale, Matrix4f store) { - store.loadIdentity(); - store.setTranslation(position); - store.setRotationQuaternion(rotation); - store.setScale(scale); - return store; - } - - /** - * The space of target or owner transformation. - * - * @author Marcin Roguski (Kaelthas) - */ - public static enum Space { - /** A transformation of the bone or spatial in the world space. */ - CONSTRAINT_SPACE_WORLD, - /** - * For spatial it is the transformation in its parent space or in WORLD space if it has no parent. - * For bone it is a transformation in its bone parent space or in armature space if it has no parent. - */ - CONSTRAINT_SPACE_LOCAL, - /** - * This space IS NOT applicable for spatials. - * For bone it is a transformation in the blender's armature object space. - */ - CONSTRAINT_SPACE_POSE, - - CONSTRAINT_SPACE_PARLOCAL; - - /** - * This method returns the enum instance when given the appropriate - * value from the blend file. - * - * @param c - * the blender's value of the space modifier - * @return the scape enum instance - */ - public static Space valueOf(byte c) { - switch (c) { - case 0: - return CONSTRAINT_SPACE_WORLD; - case 1: - return CONSTRAINT_SPACE_LOCAL; - case 2: - return CONSTRAINT_SPACE_POSE; - case 3: - return CONSTRAINT_SPACE_PARLOCAL; - default: - throw new IllegalArgumentException("Value: " + c + " cannot be converted to Space enum instance!"); - } - } - } -} diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/SimulationNode.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/SimulationNode.java deleted file mode 100644 index 279b5311d..000000000 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/SimulationNode.java +++ /dev/null @@ -1,397 +0,0 @@ -package com.jme3.scene.plugins.blender.constraints; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; -import java.util.Stack; -import java.util.logging.Logger; - -import com.jme3.animation.AnimChannel; -import com.jme3.animation.AnimControl; -import com.jme3.animation.Animation; -import com.jme3.animation.Bone; -import com.jme3.animation.BoneTrack; -import com.jme3.animation.Skeleton; -import com.jme3.animation.SpatialTrack; -import com.jme3.animation.Track; -import com.jme3.math.Quaternion; -import com.jme3.math.Transform; -import com.jme3.math.Vector3f; -import com.jme3.scene.Node; -import com.jme3.scene.Spatial; -import com.jme3.scene.plugins.blender.BlenderContext; -import com.jme3.scene.plugins.blender.BlenderContext.LoadedDataType; -import com.jme3.scene.plugins.blender.animations.BoneContext; -import com.jme3.scene.plugins.blender.objects.ObjectHelper; -import com.jme3.util.TempVars; - -/** - * A node that represents either spatial or bone in constraint simulation. The - * node is applied its translation, rotation and scale for each frame of its - * animation. Then the constraints are applied that will eventually alter it. - * After that the feature's transformation is stored in VirtualTrack which is - * converted to new bone or spatial track at the very end. - * - * @author Marcin Roguski (Kaelthas) - */ -public class SimulationNode { - private static final Logger LOGGER = Logger.getLogger(SimulationNode.class.getName()); - - private Long featureOMA; - /** The blender context. */ - private BlenderContext blenderContext; - /** The name of the node (for debugging purposes). */ - private String name; - /** A list of children for the node (either bones or child spatials). */ - private List children = new ArrayList(); - /** A list of node's animations. */ - private List animations; - - /** The nodes spatial (if null then the boneContext should be set). */ - private Spatial spatial; - /** The skeleton of the bone (not null if the node simulated the bone). */ - private Skeleton skeleton; - /** Animation controller for the node's feature. */ - private AnimControl animControl; - - /** - * The star transform of a spatial. Needed to properly reset the spatial to - * its start position. - */ - private Transform spatialStartTransform; - /** Star transformations for bones. Needed to properly reset the bones. */ - private Map boneStartTransforms; - - /** - * Builds the nodes tree for the given feature. The feature (bone or - * spatial) is found by its OMA. The feature must be a root bone or a root - * spatial. - * - * @param featureOMA - * the OMA of either bone or spatial - * @param blenderContext - * the blender context - */ - public SimulationNode(Long featureOMA, BlenderContext blenderContext) { - this(featureOMA, blenderContext, true); - } - - /** - * Creates the node for the feature. - * - * @param featureOMA - * the OMA of either bone or spatial - * @param blenderContext - * the blender context - * @param rootNode - * indicates if the feature is a root bone or root spatial or not - */ - private SimulationNode(Long featureOMA, BlenderContext blenderContext, boolean rootNode) { - this.featureOMA = featureOMA; - this.blenderContext = blenderContext; - Node spatial = (Node) blenderContext.getLoadedFeature(featureOMA, LoadedDataType.FEATURE); - if (blenderContext.getMarkerValue(ObjectHelper.ARMATURE_NODE_MARKER, spatial) != null) { - skeleton = blenderContext.getSkeleton(featureOMA); - - Node nodeWithAnimationControl = blenderContext.getControlledNode(skeleton); - animControl = nodeWithAnimationControl.getControl(AnimControl.class); - - boneStartTransforms = new HashMap(); - for (int i = 0; i < skeleton.getBoneCount(); ++i) { - Bone bone = skeleton.getBone(i); - boneStartTransforms.put(bone, new Transform(bone.getBindPosition(), bone.getBindRotation(), bone.getBindScale())); - } - } else { - if (rootNode && spatial.getParent() != null) { - throw new IllegalStateException("Given spatial must be a root node!"); - } - this.spatial = spatial; - spatialStartTransform = spatial.getLocalTransform().clone(); - } - - name = '>' + spatial.getName() + '<'; - - // add children nodes - if (skeleton != null) { - Node node = blenderContext.getControlledNode(skeleton); - Long animatedNodeOMA = ((Number) blenderContext.getMarkerValue(ObjectHelper.OMA_MARKER, node)).longValue(); - animations = blenderContext.getAnimations(animatedNodeOMA); - } else { - animations = blenderContext.getAnimations(featureOMA); - for (Spatial child : spatial.getChildren()) { - if (child instanceof Node) { - children.add(new SimulationNode((Long) blenderContext.getMarkerValue(ObjectHelper.OMA_MARKER, child), blenderContext, false)); - } - } - } - } - - /** - * Resets the node's feature to its starting transformation. - */ - private void reset() { - if (spatial != null) { - spatial.setLocalTransform(spatialStartTransform); - for (SimulationNode child : children) { - child.reset(); - } - } else if (skeleton != null) { - for (Entry entry : boneStartTransforms.entrySet()) { - Transform t = entry.getValue(); - entry.getKey().setBindTransforms(t.getTranslation(), t.getRotation(), t.getScale()); - entry.getKey().updateModelTransforms(); - } - skeleton.reset(); - } - } - - /** - * Simulates the spatial node. - */ - private void simulateSpatial() { - List constraints = blenderContext.getConstraints(featureOMA); - if (constraints != null && constraints.size() > 0) { - LOGGER.fine("Simulating spatial."); - boolean applyStaticConstraints = true; - if (animations != null) { - for (Animation animation : animations) { - float[] animationTimeBoundaries = this.computeAnimationTimeBoundaries(animation); - int maxFrame = (int) animationTimeBoundaries[0]; - float maxTime = animationTimeBoundaries[1]; - - VirtualTrack vTrack = new VirtualTrack(spatial.getName(), maxFrame, maxTime); - for (Track track : animation.getTracks()) { - for (int frame = 0; frame < maxFrame; ++frame) { - spatial.setLocalTranslation(((SpatialTrack) track).getTranslations()[frame]); - spatial.setLocalRotation(((SpatialTrack) track).getRotations()[frame]); - spatial.setLocalScale(((SpatialTrack) track).getScales()[frame]); - - for (Constraint constraint : constraints) { - constraint.apply(frame); - vTrack.setTransform(frame, spatial.getLocalTransform()); - } - } - Track newTrack = vTrack.getAsSpatialTrack(); - if (newTrack != null) { - animation.removeTrack(track); - animation.addTrack(newTrack); - } - applyStaticConstraints = false; - } - } - } - - // if there are no animations then just constraint the static - // object's transformation - if (applyStaticConstraints) { - for (Constraint constraint : constraints) { - constraint.apply(0); - } - } - } - - for (SimulationNode child : children) { - child.simulate(); - } - } - - /** - * Simulates the bone node. - */ - private void simulateSkeleton() { - LOGGER.fine("Simulating skeleton."); - Set alteredOmas = new HashSet(); - - if (animations != null) { - TempVars vars = TempVars.get(); - AnimChannel animChannel = animControl.createChannel(); - - for (Animation animation : animations) { - float[] animationTimeBoundaries = this.computeAnimationTimeBoundaries(animation); - int maxFrame = (int) animationTimeBoundaries[0]; - float maxTime = animationTimeBoundaries[1]; - - Map tracks = new HashMap(); - for (int frame = 0; frame < maxFrame; ++frame) { - // this MUST be done here, otherwise setting next frame of animation will - // lead to possible errors - this.reset(); - - // first set proper time for all bones in all the tracks ... - for (Track track : animation.getTracks()) { - float time = ((BoneTrack) track).getTimes()[frame]; - track.setTime(time, 1, animControl, animChannel, vars); - skeleton.updateWorldVectors(); - } - - // ... and then apply constraints from the root bone to the last child ... - Set applied = new HashSet(); - for (Bone rootBone : skeleton.getRoots()) { - // ignore the 0-indexed bone - if (skeleton.getBoneIndex(rootBone) > 0) { - this.applyConstraints(rootBone, alteredOmas, applied, frame, new Stack()); - } - } - - // ... add virtual tracks if necessary, for bones that were altered but had no tracks before ... - for (Long boneOMA : alteredOmas) { - BoneContext boneContext = blenderContext.getBoneContext(boneOMA); - int boneIndex = skeleton.getBoneIndex(boneContext.getBone()); - if (!tracks.containsKey(boneIndex)) { - tracks.put(boneIndex, new VirtualTrack(boneContext.getBone().getName(), maxFrame, maxTime)); - } - } - alteredOmas.clear(); - - // ... and fill in another frame in the result track - for (Entry trackEntry : tracks.entrySet()) { - Bone bone = skeleton.getBone(trackEntry.getKey()); - Transform startTransform = boneStartTransforms.get(bone); - - // track contains differences between the frame position and bind positions of bones/spatials - Vector3f bonePositionDifference = bone.getLocalPosition().subtract(startTransform.getTranslation()); - Quaternion boneRotationDifference = startTransform.getRotation().inverse().mult(bone.getLocalRotation()).normalizeLocal(); - Vector3f boneScaleDifference = bone.getLocalScale().divide(startTransform.getScale()); - - trackEntry.getValue().setTransform(frame, new Transform(bonePositionDifference, boneRotationDifference, boneScaleDifference)); - } - } - - for (Entry trackEntry : tracks.entrySet()) { - Track newTrack = trackEntry.getValue().getAsBoneTrack(trackEntry.getKey()); - if (newTrack != null) { - boolean trackReplaced = false; - for (Track track : animation.getTracks()) { - if (((BoneTrack) track).getTargetBoneIndex() == trackEntry.getKey().intValue()) { - animation.removeTrack(track); - animation.addTrack(newTrack); - trackReplaced = true; - break; - } - } - if (!trackReplaced) { - animation.addTrack(newTrack); - } - } - } - } - vars.release(); - animControl.clearChannels(); - this.reset(); - } - } - - /** - * Applies constraints to the given bone and its children. - * The goal is to apply constraint from root bone to the last child. - * @param bone - * the bone whose constraints will be applied - * @param alteredOmas - * the set of OMAS of the altered bones (is populated if necessary) - * @param frame - * the current frame of the animation - * @param bonesStack - * the stack of bones used to avoid infinite loops while applying constraints - */ - private void applyConstraints(Bone bone, Set alteredOmas, Set applied, int frame, Stack bonesStack) { - if (!bonesStack.contains(bone)) { - bonesStack.push(bone); - BoneContext boneContext = blenderContext.getBoneContext(bone); - if (!applied.contains(boneContext.getBoneOma())) { - List constraints = this.findConstraints(boneContext.getBoneOma(), blenderContext); - if (constraints != null && constraints.size() > 0) { - for (Constraint constraint : constraints) { - if (constraint.getTargetOMA() != null && constraint.getTargetOMA() > 0L) { - // first apply constraints of the target bone - BoneContext targetBone = blenderContext.getBoneContext(constraint.getTargetOMA()); - this.applyConstraints(targetBone.getBone(), alteredOmas, applied, frame, bonesStack); - } - constraint.apply(frame); - if (constraint.getAlteredOmas() != null) { - alteredOmas.addAll(constraint.getAlteredOmas()); - } - alteredOmas.add(boneContext.getBoneOma()); - } - } - applied.add(boneContext.getBoneOma()); - } - - List children = bone.getChildren(); - if (children != null && children.size() > 0) { - for (Bone child : bone.getChildren()) { - this.applyConstraints(child, alteredOmas, applied, frame, bonesStack); - } - } - bonesStack.pop(); - } - } - - /** - * Simulates the node. - */ - public void simulate() { - this.reset(); - if (spatial != null) { - this.simulateSpatial(); - } else { - this.simulateSkeleton(); - } - } - - /** - * Computes the maximum frame and time for the animation. Different tracks - * can have different lengths so here the maximum one is being found. - * - * @param animation - * the animation - * @return maximum frame and time of the animation - */ - private float[] computeAnimationTimeBoundaries(Animation animation) { - int maxFrame = Integer.MIN_VALUE; - float maxTime = -Float.MAX_VALUE; - for (Track track : animation.getTracks()) { - if (track instanceof BoneTrack) { - maxFrame = Math.max(maxFrame, ((BoneTrack) track).getTranslations().length); - maxTime = Math.max(maxTime, ((BoneTrack) track).getTimes()[((BoneTrack) track).getTimes().length - 1]); - } else if (track instanceof SpatialTrack) { - maxFrame = Math.max(maxFrame, ((SpatialTrack) track).getTranslations().length); - maxTime = Math.max(maxTime, ((SpatialTrack) track).getTimes()[((SpatialTrack) track).getTimes().length - 1]); - } else { - throw new IllegalStateException("Unsupported track type for simuation: " + track); - } - } - return new float[] { maxFrame, maxTime }; - } - - /** - * Finds constraints for the node's features. - * - * @param ownerOMA - * the feature's OMA - * @param blenderContext - * the blender context - * @return a list of feature's constraints or empty list if none were found - */ - private List findConstraints(Long ownerOMA, BlenderContext blenderContext) { - List result = new ArrayList(); - List constraints = blenderContext.getConstraints(ownerOMA); - if (constraints != null) { - for (Constraint constraint : constraints) { - if (constraint.isImplemented() && constraint.validate() && constraint.isTrackToBeChanged()) { - result.add(constraint); - } - // TODO: add proper warnings to some map or set so that they are not logged on every frame - } - } - return result.size() > 0 ? result : null; - } - - @Override - public String toString() { - return name; - } -} \ No newline at end of file diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/SkeletonConstraint.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/SkeletonConstraint.java deleted file mode 100644 index 5560bbd43..000000000 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/SkeletonConstraint.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.jme3.scene.plugins.blender.constraints; - -import java.util.logging.Logger; - -import com.jme3.scene.plugins.blender.BlenderContext; -import com.jme3.scene.plugins.blender.animations.Ipo; -import com.jme3.scene.plugins.blender.file.BlenderFileException; -import com.jme3.scene.plugins.blender.file.Structure; - -/** - * Constraint applied on the skeleton. This constraint is here only to make the - * application not crash when loads constraints applied to armature. But - * skeleton movement is not supported by jme so the constraint will never be - * applied. - * - * @author Marcin Roguski (Kaelthas) - */ -/* package */class SkeletonConstraint extends Constraint { - private static final Logger LOGGER = Logger.getLogger(SkeletonConstraint.class.getName()); - - public SkeletonConstraint(Structure constraintStructure, Long ownerOMA, Ipo influenceIpo, BlenderContext blenderContext) throws BlenderFileException { - super(constraintStructure, ownerOMA, influenceIpo, blenderContext); - } - - @Override - public boolean validate() { - LOGGER.warning("Constraints for skeleton are not supported."); - return false; - } - - @Override - public void apply(int frame) { - LOGGER.warning("Applying constraints to skeleton is not supported."); - } - - @Override - public Long getTargetOMA() { - LOGGER.warning("Constraints for skeleton are not supported."); - return null; - } -} diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/SpatialConstraint.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/SpatialConstraint.java deleted file mode 100644 index 91eb5c5c7..000000000 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/SpatialConstraint.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.jme3.scene.plugins.blender.constraints; - -import com.jme3.scene.plugins.blender.BlenderContext; -import com.jme3.scene.plugins.blender.BlenderContext.LoadedDataType; -import com.jme3.scene.plugins.blender.animations.Ipo; -import com.jme3.scene.plugins.blender.file.BlenderFileException; -import com.jme3.scene.plugins.blender.file.Structure; - -/** - * Constraint applied on the spatial objects. This includes: nodes, cameras - * nodes and light nodes. - * - * @author Marcin Roguski (Kaelthas) - */ -/* package */class SpatialConstraint extends Constraint { - public SpatialConstraint(Structure constraintStructure, Long ownerOMA, Ipo influenceIpo, BlenderContext blenderContext) throws BlenderFileException { - super(constraintStructure, ownerOMA, influenceIpo, blenderContext); - } - - @Override - public boolean validate() { - if (targetOMA != null) { - return blenderContext.getLoadedFeature(targetOMA, LoadedDataType.FEATURE) != null; - } - return constraintDefinition == null ? true : constraintDefinition.isTargetRequired(); - } - - @Override - public Long getTargetOMA() { - return targetOMA; - } -} diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/VirtualTrack.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/VirtualTrack.java deleted file mode 100644 index f18222795..000000000 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/VirtualTrack.java +++ /dev/null @@ -1,165 +0,0 @@ -package com.jme3.scene.plugins.blender.constraints; - -import java.util.ArrayList; - -import com.jme3.animation.BoneTrack; -import com.jme3.animation.SpatialTrack; -import com.jme3.math.Quaternion; -import com.jme3.math.Transform; -import com.jme3.math.Vector3f; - -/** - * A virtual track that stores computed frames after constraints are applied. - * Not all the frames need to be inserted. If there are lacks then the class - * will fill the gaps. - * - * @author Marcin Roguski (Kaelthas) - */ -/* package */class VirtualTrack { - /** The name of the track (for debugging purposes). */ - private String name; - /** The last frame for the track. */ - public int maxFrame; - /** The max time for the track. */ - public float maxTime; - /** Translations of the track. */ - public ArrayList translations; - /** Rotations of the track. */ - public ArrayList rotations; - /** Scales of the track. */ - public ArrayList scales; - - /** - * Constructs the object storing the maximum frame and time. - * - * @param maxFrame - * the last frame for the track - * @param maxTime - * the max time for the track - */ - public VirtualTrack(String name, int maxFrame, float maxTime) { - this.name = name; - this.maxFrame = maxFrame; - this.maxTime = maxTime; - } - - /** - * Sets the transform for the given frame. - * - * @param frameIndex - * the frame for which the transform will be set - * @param transform - * the transformation to be set - */ - public void setTransform(int frameIndex, Transform transform) { - if (translations == null) { - translations = this.createList(Vector3f.ZERO, frameIndex); - } - this.append(translations, Vector3f.ZERO, frameIndex - translations.size()); - translations.add(transform.getTranslation().clone()); - - if (rotations == null) { - rotations = this.createList(Quaternion.IDENTITY, frameIndex); - } - this.append(rotations, Quaternion.IDENTITY, frameIndex - rotations.size()); - rotations.add(transform.getRotation().clone()); - - if (scales == null) { - scales = this.createList(Vector3f.UNIT_XYZ, frameIndex); - } - this.append(scales, Vector3f.UNIT_XYZ, frameIndex - scales.size()); - scales.add(transform.getScale().clone()); - } - - /** - * Returns the track as a bone track. - * - * @param targetBoneIndex - * the bone index - * @return the bone track - */ - public BoneTrack getAsBoneTrack(int targetBoneIndex) { - if (translations == null && rotations == null && scales == null) { - return null; - } - return new BoneTrack(targetBoneIndex, this.createTimes(), translations.toArray(new Vector3f[maxFrame]), rotations.toArray(new Quaternion[maxFrame]), scales.toArray(new Vector3f[maxFrame])); - } - - /** - * Returns the track as a spatial track. - * - * @return the spatial track - */ - public SpatialTrack getAsSpatialTrack() { - if (translations == null && rotations == null && scales == null) { - return null; - } - return new SpatialTrack(this.createTimes(), translations.toArray(new Vector3f[maxFrame]), rotations.toArray(new Quaternion[maxFrame]), scales.toArray(new Vector3f[maxFrame])); - } - - /** - * The method creates times for the track based on the given maximum values. - * - * @return the times for the track - */ - private float[] createTimes() { - float[] times = new float[maxFrame]; - float dT = maxTime / maxFrame; - float t = 0; - for (int i = 0; i < maxFrame; ++i) { - times[i] = t; - t += dT; - } - return times; - } - - /** - * Helper method that creates a list of a given size filled with given - * elements. - * - * @param element - * the element to be put into the list - * @param count - * the list size - * @return the list - */ - private ArrayList createList(T element, int count) { - ArrayList result = new ArrayList(count); - for (int i = 0; i < count; ++i) { - result.add(element); - } - return result; - } - - /** - * Appends the element to the given list. - * - * @param list - * the list where the element will be appended - * @param element - * the element to be appended - * @param count - * how many times the element will be appended - */ - private void append(ArrayList list, T element, int count) { - for (int i = 0; i < count; ++i) { - list.add(element); - } - } - - @Override - public String toString() { - StringBuilder result = new StringBuilder(2048); - result.append("TRACK: ").append(name).append('\n'); - if (translations != null && translations.size() > 0) { - result.append("TRANSLATIONS: ").append(translations.toString()).append('\n'); - } - if (rotations != null && rotations.size() > 0) { - result.append("ROTATIONS: ").append(rotations.toString()).append('\n'); - } - if (scales != null && scales.size() > 0) { - result.append("SCALES: ").append(scales.toString()).append('\n'); - } - return result.toString(); - } -} \ No newline at end of file diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinition.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinition.java deleted file mode 100644 index 166b28c61..000000000 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinition.java +++ /dev/null @@ -1,162 +0,0 @@ -package com.jme3.scene.plugins.blender.constraints.definitions; - -import java.util.Set; - -import com.jme3.animation.Bone; -import com.jme3.math.Transform; -import com.jme3.scene.plugins.blender.BlenderContext; -import com.jme3.scene.plugins.blender.BlenderContext.LoadedDataType; -import com.jme3.scene.plugins.blender.animations.BoneContext; -import com.jme3.scene.plugins.blender.constraints.ConstraintHelper; -import com.jme3.scene.plugins.blender.constraints.ConstraintHelper.Space; -import com.jme3.scene.plugins.blender.file.Structure; - -/** - * A base class for all constraint definitions. - * - * @author Marcin Roguski (Kaelthas) - */ -public abstract class ConstraintDefinition { - protected ConstraintHelper constraintHelper; - /** Constraints flag. Used to load user's options applied to the constraint. */ - protected int flag; - /** The constraint's owner. Loaded during runtime. */ - private Object owner; - /** The blender context. */ - protected BlenderContext blenderContext; - /** The constraint's owner OMA. */ - protected Long ownerOMA; - /** Stores the OMA addresses of all features whose transform had been altered beside the constraint owner. */ - protected Set alteredOmas; - /** The variable that determines if the constraint will alter the track in any way. */ - protected boolean trackToBeChanged = true; - /** The name of the constraint. */ - protected String constraintName; - - /** - * Loads a constraint definition based on the constraint definition - * structure. - * - * @param constraintData - * the constraint definition structure - * @param ownerOMA - * the constraint's owner OMA - * @param blenderContext - * the blender context - */ - public ConstraintDefinition(Structure constraintData, Long ownerOMA, BlenderContext blenderContext) { - if (constraintData != null) {// Null constraint has no data - Number flag = (Number) constraintData.getFieldValue("flag"); - if (flag != null) { - this.flag = flag.intValue(); - } - } - this.blenderContext = blenderContext; - constraintHelper = (ConstraintHelper) (blenderContext == null ? null : blenderContext.getHelper(ConstraintHelper.class)); - this.ownerOMA = ownerOMA; - } - - public void setConstraintName(String constraintName) { - this.constraintName = constraintName; - } - - /** - * @return determines if the definition of the constraint will change the bone in any way; in most cases - * it is possible to tell that even before the constraint baking simulation is started, so we can discard such bones from constraint - * computing to improve the computation speed and lower the computations complexity - */ - public boolean isTrackToBeChanged() { - return trackToBeChanged; - } - - /** - * @return determines if this constraint definition requires a defined target or not - */ - public abstract boolean isTargetRequired(); - - /** - * This method is here because we have no guarantee that the owner is loaded - * when constraint is being created. So use it to get the owner when it is - * needed for computations. - * - * @return the owner of the constraint or null if none is set - */ - protected Object getOwner() { - if (ownerOMA != null && owner == null) { - owner = blenderContext.getLoadedFeature(ownerOMA, LoadedDataType.FEATURE); - if (owner == null) { - throw new IllegalStateException("Cannot load constraint's owner for constraint type: " + this.getClass().getName()); - } - } - return owner; - } - - /** - * The method gets the owner's transformation. The owner can be either bone or spatial. - * @param ownerSpace - * the space in which the computed transformation is given - * @return the constraint owner's transformation - */ - protected Transform getOwnerTransform(Space ownerSpace) { - if (this.getOwner() instanceof Bone) { - BoneContext boneContext = blenderContext.getBoneContext(ownerOMA); - return constraintHelper.getTransform(boneContext.getArmatureObjectOMA(), boneContext.getBone().getName(), ownerSpace); - } - return constraintHelper.getTransform(ownerOMA, null, ownerSpace); - } - - /** - * The method applies the given transformation to the owner. - * @param ownerTransform - * the transformation to apply to the owner - * @param ownerSpace - * the space that defines which owner's transformation (ie. global, local, etc. will be set) - */ - protected void applyOwnerTransform(Transform ownerTransform, Space ownerSpace) { - if (this.getOwner() instanceof Bone) { - BoneContext boneContext = blenderContext.getBoneContext(ownerOMA); - constraintHelper.applyTransform(boneContext.getArmatureObjectOMA(), boneContext.getBone().getName(), ownerSpace, ownerTransform); - } else { - constraintHelper.applyTransform(ownerOMA, null, ownerSpace, ownerTransform); - } - } - - /** - * @return true if the definition is implemented and false - * otherwise - */ - public boolean isImplemented() { - return true; - } - - /** - * @return a list of all OMAs of the features that the constraint had altered beside its owner - */ - public Set getAlteredOmas() { - return alteredOmas; - } - - /** - * @return the type name of the constraint - */ - public abstract String getConstraintTypeName(); - - /** - * Bakes the constraint for the current feature (bone or spatial) position. - * - * @param ownerSpace - * the space where owner transform will be evaluated in - * @param targetSpace - * the space where target transform will be evaluated in - * @param targetTransform - * the target transform used by some of the constraints - * @param influence - * the influence of the constraint from range [0; 1] - */ - public abstract void bake(Space ownerSpace, Space targetSpace, Transform targetTransform, float influence); - - @Override - public String toString() { - return this.getConstraintTypeName(); - } -} diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionDistLimit.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionDistLimit.java deleted file mode 100644 index aaa7e2d97..000000000 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionDistLimit.java +++ /dev/null @@ -1,84 +0,0 @@ -package com.jme3.scene.plugins.blender.constraints.definitions; - -import com.jme3.animation.Bone; -import com.jme3.math.Transform; -import com.jme3.math.Vector3f; -import com.jme3.scene.plugins.blender.BlenderContext; -import com.jme3.scene.plugins.blender.animations.BoneContext; -import com.jme3.scene.plugins.blender.constraints.ConstraintHelper.Space; -import com.jme3.scene.plugins.blender.file.Structure; - -/** - * This class represents 'Dist limit' constraint type in blender. - * - * @author Marcin Roguski (Kaelthas) - */ -/* package */class ConstraintDefinitionDistLimit extends ConstraintDefinition { - private static final int LIMITDIST_INSIDE = 0; - private static final int LIMITDIST_OUTSIDE = 1; - private static final int LIMITDIST_ONSURFACE = 2; - - protected int mode; - protected float dist; - - public ConstraintDefinitionDistLimit(Structure constraintData, Long ownerOMA, BlenderContext blenderContext) { - super(constraintData, ownerOMA, blenderContext); - mode = ((Number) constraintData.getFieldValue("mode")).intValue(); - dist = ((Number) constraintData.getFieldValue("dist")).floatValue(); - } - - @Override - public void bake(Space ownerSpace, Space targetSpace, Transform targetTransform, float influence) { - if (this.getOwner() instanceof Bone && ((Bone) this.getOwner()).getParent() != null && blenderContext.getBoneContext(ownerOMA).is(BoneContext.CONNECTED_TO_PARENT)) { - // distance limit does not work on bones who are connected to their parent - return; - } - if (influence == 0 || targetTransform == null) { - return;// no need to do anything - } - - Transform ownerTransform = this.getOwnerTransform(ownerSpace); - - Vector3f v = ownerTransform.getTranslation().subtract(targetTransform.getTranslation()); - float currentDistance = v.length(); - switch (mode) { - case LIMITDIST_INSIDE: - if (currentDistance >= dist) { - v.normalizeLocal(); - v.multLocal(dist + (currentDistance - dist) * (1.0f - influence)); - ownerTransform.getTranslation().set(v.addLocal(targetTransform.getTranslation())); - } - break; - case LIMITDIST_ONSURFACE: - if (currentDistance > dist) { - v.normalizeLocal(); - v.multLocal(dist + (currentDistance - dist) * (1.0f - influence)); - ownerTransform.getTranslation().set(v.addLocal(targetTransform.getTranslation())); - } else if (currentDistance < dist) { - v.normalizeLocal().multLocal(dist * influence); - ownerTransform.getTranslation().set(targetTransform.getTranslation().add(v)); - } - break; - case LIMITDIST_OUTSIDE: - if (currentDistance <= dist) { - v = targetTransform.getTranslation().subtract(ownerTransform.getTranslation()).normalizeLocal().multLocal(dist * influence); - ownerTransform.getTranslation().set(targetTransform.getTranslation().add(v)); - } - break; - default: - throw new IllegalStateException("Unknown distance limit constraint mode: " + mode); - } - - this.applyOwnerTransform(ownerTransform, ownerSpace); - } - - @Override - public boolean isTargetRequired() { - return true; - } - - @Override - public String getConstraintTypeName() { - return "Limit distance"; - } -} diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionFactory.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionFactory.java deleted file mode 100644 index c1d69fe06..000000000 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionFactory.java +++ /dev/null @@ -1,126 +0,0 @@ -/* - * 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.blender.constraints.definitions; - -import java.lang.reflect.InvocationTargetException; -import java.util.HashMap; -import java.util.Map; - -import com.jme3.scene.plugins.blender.BlenderContext; -import com.jme3.scene.plugins.blender.file.BlenderFileException; -import com.jme3.scene.plugins.blender.file.Structure; - -public class ConstraintDefinitionFactory { - private static final Map> CONSTRAINT_CLASSES = new HashMap>(); - static { - CONSTRAINT_CLASSES.put("bDistLimitConstraint", ConstraintDefinitionDistLimit.class); - CONSTRAINT_CLASSES.put("bLocateLikeConstraint", ConstraintDefinitionLocLike.class); - CONSTRAINT_CLASSES.put("bLocLimitConstraint", ConstraintDefinitionLocLimit.class); - CONSTRAINT_CLASSES.put("bNullConstraint", ConstraintDefinitionNull.class); - CONSTRAINT_CLASSES.put("bRotateLikeConstraint", ConstraintDefinitionRotLike.class); - CONSTRAINT_CLASSES.put("bRotLimitConstraint", ConstraintDefinitionRotLimit.class); - CONSTRAINT_CLASSES.put("bSizeLikeConstraint", ConstraintDefinitionSizeLike.class); - CONSTRAINT_CLASSES.put("bSizeLimitConstraint", ConstraintDefinitionSizeLimit.class); - CONSTRAINT_CLASSES.put("bKinematicConstraint", ConstraintDefinitionIK.class); - CONSTRAINT_CLASSES.put("bTransLikeConstraint", ConstraintDefinitionTransLike.class);// since blender 2.51 - CONSTRAINT_CLASSES.put("bSameVolumeConstraint", ConstraintDefinitionMaintainVolume.class);// since blender 2.53 - } - - private static final Map UNSUPPORTED_CONSTRAINTS = new HashMap(); - static { - UNSUPPORTED_CONSTRAINTS.put("bActionConstraint", "Action"); - UNSUPPORTED_CONSTRAINTS.put("bChildOfConstraint", "Child of"); - UNSUPPORTED_CONSTRAINTS.put("bClampToConstraint", "Clamp to"); - UNSUPPORTED_CONSTRAINTS.put("bFollowPathConstraint", "Follow path"); - UNSUPPORTED_CONSTRAINTS.put("bLockTrackConstraint", "Lock track"); - UNSUPPORTED_CONSTRAINTS.put("bMinMaxConstraint", "Min max"); - UNSUPPORTED_CONSTRAINTS.put("bPythonConstraint", "Python/Script"); - UNSUPPORTED_CONSTRAINTS.put("bRigidBodyJointConstraint", "Rigid body joint"); - UNSUPPORTED_CONSTRAINTS.put("bShrinkWrapConstraint", "Shrinkwrap"); - UNSUPPORTED_CONSTRAINTS.put("bStretchToConstraint", "Stretch to"); - UNSUPPORTED_CONSTRAINTS.put("bTransformConstraint", "Transform"); - // Blender 2.50+ - UNSUPPORTED_CONSTRAINTS.put("bSplineIKConstraint", "Spline inverse kinematics"); - UNSUPPORTED_CONSTRAINTS.put("bDampTrackConstraint", "Damp track"); - UNSUPPORTED_CONSTRAINTS.put("bPivotConstraint", "Pivot"); - // Blender 2.56+ - UNSUPPORTED_CONSTRAINTS.put("bTrackToConstraint", "Track to"); - // Blender 2.62+ - UNSUPPORTED_CONSTRAINTS.put("bCameraSolverConstraint", "Camera solver"); - UNSUPPORTED_CONSTRAINTS.put("bObjectSolverConstraint", "Object solver"); - UNSUPPORTED_CONSTRAINTS.put("bFollowTrackConstraint", "Follow track"); - } - - /** - * This method creates the constraint instance. - * - * @param constraintStructure - * the constraint's structure (bConstraint clss in blender 2.49). - * If the value is null the NullConstraint is created. - * @param blenderContext - * the blender context - * @throws BlenderFileException - * this exception is thrown when the blender file is somehow - * corrupted - */ - public static ConstraintDefinition createConstraintDefinition(Structure constraintStructure, String constraintName, Long ownerOMA, BlenderContext blenderContext) throws BlenderFileException { - if (constraintStructure == null) { - return new ConstraintDefinitionNull(null, ownerOMA, blenderContext); - } - String constraintClassName = constraintStructure.getType(); - Class constraintDefinitionClass = CONSTRAINT_CLASSES.get(constraintClassName); - if (constraintDefinitionClass != null) { - try { - ConstraintDefinition def = (ConstraintDefinition) constraintDefinitionClass.getDeclaredConstructors()[0].newInstance(constraintStructure, ownerOMA, blenderContext); - def.setConstraintName(constraintName); - return def; - } catch (IllegalArgumentException e) { - throw new BlenderFileException(e.getLocalizedMessage(), e); - } catch (SecurityException e) { - throw new BlenderFileException(e.getLocalizedMessage(), e); - } catch (InstantiationException e) { - throw new BlenderFileException(e.getLocalizedMessage(), e); - } catch (IllegalAccessException e) { - throw new BlenderFileException(e.getLocalizedMessage(), e); - } catch (InvocationTargetException e) { - throw new BlenderFileException(e.getLocalizedMessage(), e); - } - } else { - String unsupportedConstraintClassName = UNSUPPORTED_CONSTRAINTS.get(constraintClassName); - if (unsupportedConstraintClassName != null) { - return new UnsupportedConstraintDefinition(unsupportedConstraintClassName); - } else { - throw new BlenderFileException("Unknown constraint type: " + constraintClassName); - } - } - } -} diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionIK.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionIK.java deleted file mode 100644 index 69c0a6f50..000000000 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionIK.java +++ /dev/null @@ -1,236 +0,0 @@ -package com.jme3.scene.plugins.blender.constraints.definitions; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashSet; -import java.util.List; - -import org.ejml.simple.SimpleMatrix; - -import com.jme3.animation.Bone; -import com.jme3.math.Transform; -import com.jme3.scene.plugins.blender.BlenderContext; -import com.jme3.scene.plugins.blender.animations.BoneContext; -import com.jme3.scene.plugins.blender.constraints.ConstraintHelper; -import com.jme3.scene.plugins.blender.constraints.ConstraintHelper.Space; -import com.jme3.scene.plugins.blender.file.Structure; -import com.jme3.scene.plugins.blender.math.DQuaternion; -import com.jme3.scene.plugins.blender.math.DTransform; -import com.jme3.scene.plugins.blender.math.Matrix; -import com.jme3.scene.plugins.blender.math.Vector3d; - -/** - * A definiotion of a Inverse Kinematics constraint. This implementation uses Jacobian pseudoinverse algorithm. - * - * @author Marcin Roguski (Kaelthas) - */ -public class ConstraintDefinitionIK extends ConstraintDefinition { - private static final float MIN_DISTANCE = 0.001f; - private static final float MIN_ANGLE_CHANGE = 0.001f; - private static final int FLAG_USE_TAIL = 0x01; - private static final int FLAG_POSITION = 0x20; - - private BonesChain bones; - /** The number of affected bones. Zero means that all parent bones of the current bone should take part in baking. */ - private int bonesAffected; - /** Indicates if the tail of the bone should be used or not. */ - private boolean useTail; - /** The amount of iterations of the algorithm. */ - private int iterations; - /** The count of bones' chain. */ - private int bonesCount = -1; - - public ConstraintDefinitionIK(Structure constraintData, Long ownerOMA, BlenderContext blenderContext) { - super(constraintData, ownerOMA, blenderContext); - bonesAffected = ((Number) constraintData.getFieldValue("rootbone")).intValue(); - iterations = ((Number) constraintData.getFieldValue("iterations")).intValue(); - useTail = (flag & FLAG_USE_TAIL) != 0; - - if ((flag & FLAG_POSITION) == 0) { - trackToBeChanged = false; - } - - if (trackToBeChanged) { - alteredOmas = new HashSet(); - } - } - - /** - * Below are the variables that only need to be allocated once for IK constraint instance. - */ - /** Temporal quaternion. */ - private DQuaternion tempDQuaternion = new DQuaternion(); - /** Temporal matrix column. */ - private Vector3d col = new Vector3d(); - /** Effector's position change. */ - private Matrix deltaP = new Matrix(3, 1); - /** The current target position. */ - private Vector3d target = new Vector3d(); - /** Rotation vectors for each joint (allocated when we know the size of a bones' chain. */ - private Vector3d[] rotationVectors; - /** The Jacobian matrix. Allocated when the bones' chain size is known. */ - private Matrix J; - - @Override - public void bake(Space ownerSpace, Space targetSpace, Transform targetTransform, float influence) { - if (influence == 0 || !trackToBeChanged || targetTransform == null || bonesCount == 0) { - return;// no need to do anything - } - - if (bones == null) { - bones = new BonesChain((Bone) this.getOwner(), useTail, bonesAffected, alteredOmas, blenderContext); - } - if (bones.size() == 0) { - bonesCount = 0; - return;// no need to do anything - } - double distanceFromTarget = Double.MAX_VALUE; - target.set(targetTransform.getTranslation().x, targetTransform.getTranslation().y, targetTransform.getTranslation().z); - - if (bonesCount < 0) { - bonesCount = bones.size(); - rotationVectors = new Vector3d[bonesCount]; - for (int i = 0; i < bonesCount; ++i) { - rotationVectors[i] = new Vector3d(); - } - J = new Matrix(3, bonesCount); - } - - BoneContext topBone = bones.get(0); - for (int i = 0; i < iterations; ++i) { - DTransform topBoneTransform = bones.getWorldTransform(topBone); - Vector3d e = topBoneTransform.getTranslation().add(topBoneTransform.getRotation().mult(Vector3d.UNIT_Y).multLocal(topBone.getLength()));// effector - distanceFromTarget = e.distance(target); - if (distanceFromTarget <= MIN_DISTANCE) { - break; - } - - deltaP.setColumn(0, 0, target.x - e.x, target.y - e.y, target.z - e.z); - int column = 0; - for (BoneContext boneContext : bones) { - DTransform boneWorldTransform = bones.getWorldTransform(boneContext); - Vector3d j = boneWorldTransform.getTranslation(); // current join position - Vector3d vectorFromJointToEffector = e.subtract(j); - vectorFromJointToEffector.cross(target.subtract(j), rotationVectors[column]).normalizeLocal(); - rotationVectors[column].cross(vectorFromJointToEffector, col); - J.setColumn(col, column++); - } - Matrix J_1 = J.pseudoinverse(); - - SimpleMatrix deltaThetas = J_1.mult(deltaP); - if (deltaThetas.elementMaxAbs() < MIN_ANGLE_CHANGE) { - break; - } - for (int j = 0; j < deltaThetas.numRows(); ++j) { - double angle = deltaThetas.get(j, 0); - Vector3d rotationVector = rotationVectors[j]; - - tempDQuaternion.fromAngleAxis(angle, rotationVector); - BoneContext boneContext = bones.get(j); - Bone bone = boneContext.getBone(); - if (bone.equals(this.getOwner())) { - if (boneContext.isLockX()) { - tempDQuaternion.set(0, tempDQuaternion.getY(), tempDQuaternion.getZ(), tempDQuaternion.getW()); - } - if (boneContext.isLockY()) { - tempDQuaternion.set(tempDQuaternion.getX(), 0, tempDQuaternion.getZ(), tempDQuaternion.getW()); - } - if (boneContext.isLockZ()) { - tempDQuaternion.set(tempDQuaternion.getX(), tempDQuaternion.getY(), 0, tempDQuaternion.getW()); - } - } - - DTransform boneTransform = bones.getWorldTransform(boneContext); - boneTransform.getRotation().set(tempDQuaternion.mult(boneTransform.getRotation())); - bones.setWorldTransform(boneContext, boneTransform); - } - } - - // applying the results - for (int i = bonesCount - 1; i >= 0; --i) { - BoneContext boneContext = bones.get(i); - DTransform transform = bones.getWorldTransform(boneContext); - constraintHelper.applyTransform(boneContext.getArmatureObjectOMA(), boneContext.getBone().getName(), Space.CONSTRAINT_SPACE_WORLD, transform.toTransform()); - } - bones = null;// need to reload them again - } - - @Override - public String getConstraintTypeName() { - return "Inverse kinematics"; - } - - @Override - public boolean isTargetRequired() { - return true; - } - - /** - * Loaded bones' chain. This class allows to operate on transform matrices that use double precision in computations. - * Only the final result is being transformed to single precision numbers. - * - * @author Marcin Roguski (Kaelthas) - */ - private static class BonesChain extends ArrayList { - private static final long serialVersionUID = -1850524345643600718L; - - private List localBonesMatrices = new ArrayList(); - - public BonesChain(Bone bone, boolean useTail, int bonesAffected, Collection alteredOmas, BlenderContext blenderContext) { - if (bone != null) { - ConstraintHelper constraintHelper = blenderContext.getHelper(ConstraintHelper.class); - if (!useTail) { - bone = bone.getParent(); - } - while (bone != null && (bonesAffected <= 0 || this.size() < bonesAffected)) { - BoneContext boneContext = blenderContext.getBoneContext(bone); - this.add(boneContext); - alteredOmas.add(boneContext.getBoneOma()); - - Transform transform = constraintHelper.getTransform(boneContext.getArmatureObjectOMA(), boneContext.getBone().getName(), Space.CONSTRAINT_SPACE_WORLD); - localBonesMatrices.add(new DTransform(transform).toMatrix()); - - bone = bone.getParent(); - } - - if(localBonesMatrices.size() > 0) { - // making the matrices describe the local transformation - Matrix parentWorldMatrix = localBonesMatrices.get(localBonesMatrices.size() - 1); - for(int i=localBonesMatrices.size() - 2;i>=0;--i) { - SimpleMatrix m = parentWorldMatrix.invert().mult(localBonesMatrices.get(i)); - parentWorldMatrix = localBonesMatrices.get(i); - localBonesMatrices.set(i, new Matrix(m)); - } - } - } - } - - public DTransform getWorldTransform(BoneContext bone) { - int index = this.indexOf(bone); - return this.getWorldMatrix(index).toTransform(); - } - - public void setWorldTransform(BoneContext bone, DTransform transform) { - int index = this.indexOf(bone); - Matrix boneMatrix = transform.toMatrix(); - - if (index < this.size() - 1) { - // computing the current bone local transform - Matrix parentWorldMatrix = this.getWorldMatrix(index + 1); - SimpleMatrix m = parentWorldMatrix.invert().mult(boneMatrix); - boneMatrix = new Matrix(m); - } - localBonesMatrices.set(index, boneMatrix); - } - - public Matrix getWorldMatrix(int index) { - if (index == this.size() - 1) { - return new Matrix(localBonesMatrices.get(this.size() - 1)); - } - - SimpleMatrix result = this.getWorldMatrix(index + 1); - result = result.mult(localBonesMatrices.get(index)); - return new Matrix(result); - } - } -} diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionLocLike.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionLocLike.java deleted file mode 100644 index bf6fb39b1..000000000 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionLocLike.java +++ /dev/null @@ -1,107 +0,0 @@ -package com.jme3.scene.plugins.blender.constraints.definitions; - -import com.jme3.animation.Bone; -import com.jme3.math.Transform; -import com.jme3.math.Vector3f; -import com.jme3.scene.plugins.blender.BlenderContext; -import com.jme3.scene.plugins.blender.animations.BoneContext; -import com.jme3.scene.plugins.blender.constraints.ConstraintHelper.Space; -import com.jme3.scene.plugins.blender.file.Structure; - -/** - * This class represents 'Loc like' constraint type in blender. - * - * @author Marcin Roguski (Kaelthas) - */ -/* package */class ConstraintDefinitionLocLike extends ConstraintDefinition { - private static final int LOCLIKE_X = 0x01; - private static final int LOCLIKE_Y = 0x02; - private static final int LOCLIKE_Z = 0x04; - // protected static final int LOCLIKE_TIP = 0x08;//this is deprecated in - // blender - private static final int LOCLIKE_X_INVERT = 0x10; - private static final int LOCLIKE_Y_INVERT = 0x20; - private static final int LOCLIKE_Z_INVERT = 0x40; - private static final int LOCLIKE_OFFSET = 0x80; - - public ConstraintDefinitionLocLike(Structure constraintData, Long ownerOMA, BlenderContext blenderContext) { - super(constraintData, ownerOMA, blenderContext); - if (blenderContext.getBlenderKey().isFixUpAxis()) { - // swapping Y and X limits flag in the bitwise flag - int y = flag & LOCLIKE_Y; - int invY = flag & LOCLIKE_Y_INVERT; - int z = flag & LOCLIKE_Z; - int invZ = flag & LOCLIKE_Z_INVERT; - // clear the other flags to swap them - flag &= LOCLIKE_X | LOCLIKE_X_INVERT | LOCLIKE_OFFSET; - - flag |= y << 1; - flag |= invY << 1; - flag |= z >> 1; - flag |= invZ >> 1; - - trackToBeChanged = (flag & LOCLIKE_X) != 0 || (flag & LOCLIKE_Y) != 0 || (flag & LOCLIKE_Z) != 0; - } - } - - @Override - public boolean isTrackToBeChanged() { - // location copy does not work on bones who are connected to their parent - return trackToBeChanged && !(this.getOwner() instanceof Bone && ((Bone) this.getOwner()).getParent() != null && blenderContext.getBoneContext(ownerOMA).is(BoneContext.CONNECTED_TO_PARENT)); - } - - @Override - public void bake(Space ownerSpace, Space targetSpace, Transform targetTransform, float influence) { - if (influence == 0 || targetTransform == null || !this.isTrackToBeChanged()) { - return; - } - - Transform ownerTransform = this.getOwnerTransform(ownerSpace); - - Vector3f ownerLocation = ownerTransform.getTranslation(); - Vector3f targetLocation = targetTransform.getTranslation(); - - Vector3f startLocation = ownerTransform.getTranslation().clone(); - Vector3f offset = Vector3f.ZERO; - if ((flag & LOCLIKE_OFFSET) != 0) {// we add the original location to the copied location - offset = startLocation; - } - - if ((flag & LOCLIKE_X) != 0) { - ownerLocation.x = targetLocation.x; - if ((flag & LOCLIKE_X_INVERT) != 0) { - ownerLocation.x = -ownerLocation.x; - } - } - if ((flag & LOCLIKE_Y) != 0) { - ownerLocation.y = targetLocation.y; - if ((flag & LOCLIKE_Y_INVERT) != 0) { - ownerLocation.y = -ownerLocation.y; - } - } - if ((flag & LOCLIKE_Z) != 0) { - ownerLocation.z = targetLocation.z; - if ((flag & LOCLIKE_Z_INVERT) != 0) { - ownerLocation.z = -ownerLocation.z; - } - } - ownerLocation.addLocal(offset); - - if (influence < 1.0f) { - startLocation.subtractLocal(ownerLocation).normalizeLocal().mult(influence); - ownerLocation.addLocal(startLocation); - } - - this.applyOwnerTransform(ownerTransform, ownerSpace); - } - - @Override - public String getConstraintTypeName() { - return "Copy location"; - } - - @Override - public boolean isTargetRequired() { - return true; - } -} diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionLocLimit.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionLocLimit.java deleted file mode 100644 index 62e92e73b..000000000 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionLocLimit.java +++ /dev/null @@ -1,106 +0,0 @@ -package com.jme3.scene.plugins.blender.constraints.definitions; - -import com.jme3.animation.Bone; -import com.jme3.math.Transform; -import com.jme3.math.Vector3f; -import com.jme3.scene.plugins.blender.BlenderContext; -import com.jme3.scene.plugins.blender.animations.BoneContext; -import com.jme3.scene.plugins.blender.constraints.ConstraintHelper.Space; -import com.jme3.scene.plugins.blender.file.Structure; - -/** - * This class represents 'Loc limit' constraint type in blender. - * - * @author Marcin Roguski (Kaelthas) - */ -/* package */class ConstraintDefinitionLocLimit extends ConstraintDefinition { - private static final int LIMIT_XMIN = 0x01; - private static final int LIMIT_XMAX = 0x02; - private static final int LIMIT_YMIN = 0x04; - private static final int LIMIT_YMAX = 0x08; - private static final int LIMIT_ZMIN = 0x10; - private static final int LIMIT_ZMAX = 0x20; - - protected float[][] limits = new float[3][2]; - - public ConstraintDefinitionLocLimit(Structure constraintData, Long ownerOMA, BlenderContext blenderContext) { - super(constraintData, ownerOMA, blenderContext); - if (blenderContext.getBlenderKey().isFixUpAxis()) { - limits[0][0] = ((Number) constraintData.getFieldValue("xmin")).floatValue(); - limits[0][1] = ((Number) constraintData.getFieldValue("xmax")).floatValue(); - limits[2][0] = -((Number) constraintData.getFieldValue("ymin")).floatValue(); - limits[2][1] = -((Number) constraintData.getFieldValue("ymax")).floatValue(); - limits[1][0] = ((Number) constraintData.getFieldValue("zmin")).floatValue(); - limits[1][1] = ((Number) constraintData.getFieldValue("zmax")).floatValue(); - - // swapping Y and X limits flag in the bitwise flag - int ymin = flag & LIMIT_YMIN; - int ymax = flag & LIMIT_YMAX; - int zmin = flag & LIMIT_ZMIN; - int zmax = flag & LIMIT_ZMAX; - flag &= LIMIT_XMIN | LIMIT_XMAX;// clear the other flags to swap - // them - flag |= ymin << 2; - flag |= ymax << 2; - flag |= zmin >> 2; - flag |= zmax >> 2; - } else { - limits[0][0] = ((Number) constraintData.getFieldValue("xmin")).floatValue(); - limits[0][1] = ((Number) constraintData.getFieldValue("xmax")).floatValue(); - limits[1][0] = ((Number) constraintData.getFieldValue("ymin")).floatValue(); - limits[1][1] = ((Number) constraintData.getFieldValue("ymax")).floatValue(); - limits[2][0] = ((Number) constraintData.getFieldValue("zmin")).floatValue(); - limits[2][1] = ((Number) constraintData.getFieldValue("zmax")).floatValue(); - } - - trackToBeChanged = (flag & (LIMIT_XMIN | LIMIT_XMAX | LIMIT_YMIN | LIMIT_YMAX | LIMIT_ZMIN | LIMIT_ZMAX)) != 0; - } - - @Override - public boolean isTrackToBeChanged() { - // location limit does not work on bones who are connected to their parent - return trackToBeChanged && !(this.getOwner() instanceof Bone && ((Bone) this.getOwner()).getParent() != null && blenderContext.getBoneContext(ownerOMA).is(BoneContext.CONNECTED_TO_PARENT)); - } - - @Override - public void bake(Space ownerSpace, Space targetSpace, Transform targetTransform, float influence) { - if (influence == 0 || !this.isTrackToBeChanged()) { - return;// no need to do anything - } - - Transform ownerTransform = this.getOwnerTransform(ownerSpace); - - Vector3f translation = ownerTransform.getTranslation(); - - if ((flag & LIMIT_XMIN) != 0 && translation.x < limits[0][0]) { - translation.x -= (translation.x - limits[0][0]) * influence; - } - if ((flag & LIMIT_XMAX) != 0 && translation.x > limits[0][1]) { - translation.x -= (translation.x - limits[0][1]) * influence; - } - if ((flag & LIMIT_YMIN) != 0 && translation.y < limits[1][0]) { - translation.y -= (translation.y - limits[1][0]) * influence; - } - if ((flag & LIMIT_YMAX) != 0 && translation.y > limits[1][1]) { - translation.y -= (translation.y - limits[1][1]) * influence; - } - if ((flag & LIMIT_ZMIN) != 0 && translation.z < limits[2][0]) { - translation.z -= (translation.z - limits[2][0]) * influence; - } - if ((flag & LIMIT_ZMAX) != 0 && translation.z > limits[2][1]) { - translation.z -= (translation.z - limits[2][1]) * influence; - } - - this.applyOwnerTransform(ownerTransform, ownerSpace); - } - - @Override - public String getConstraintTypeName() { - return "Limit location"; - } - - @Override - public boolean isTargetRequired() { - return false; - } -} diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionMaintainVolume.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionMaintainVolume.java deleted file mode 100644 index c8756129e..000000000 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionMaintainVolume.java +++ /dev/null @@ -1,61 +0,0 @@ -package com.jme3.scene.plugins.blender.constraints.definitions; - -import com.jme3.animation.Bone; -import com.jme3.math.Transform; -import com.jme3.scene.plugins.blender.BlenderContext; -import com.jme3.scene.plugins.blender.constraints.ConstraintHelper.Space; -import com.jme3.scene.plugins.blender.file.Structure; - -/** - * This class represents 'Maintain volume' constraint type in blender. - * - * @author Marcin Roguski (Kaelthas) - */ -public class ConstraintDefinitionMaintainVolume extends ConstraintDefinition { - private static final int FLAG_MASK_X = 0; - private static final int FLAG_MASK_Y = 1; - private static final int FLAG_MASK_Z = 2; - - private float volume; - - public ConstraintDefinitionMaintainVolume(Structure constraintData, Long ownerOMA, BlenderContext blenderContext) { - super(constraintData, ownerOMA, blenderContext); - volume = (float) Math.sqrt(((Number) constraintData.getFieldValue("volume")).floatValue()); - trackToBeChanged = volume != 1 && (flag & (FLAG_MASK_X | FLAG_MASK_Y | FLAG_MASK_Z)) != 0; - } - - @Override - public void bake(Space ownerSpace, Space targetSpace, Transform targetTransform, float influence) { - if (trackToBeChanged && influence > 0) { - // the maintain volume constraint is applied directly to object's scale, so no need to do it again - // but in case of bones we need to make computations - if (this.getOwner() instanceof Bone) { - Transform ownerTransform = this.getOwnerTransform(ownerSpace); - switch (flag) { - case FLAG_MASK_X: - ownerTransform.getScale().multLocal(1, volume, volume); - break; - case FLAG_MASK_Y: - ownerTransform.getScale().multLocal(volume, 1, volume); - break; - case FLAG_MASK_Z: - ownerTransform.getScale().multLocal(volume, volume, 1); - break; - default: - throw new IllegalStateException("Unknown flag value: " + flag); - } - this.applyOwnerTransform(ownerTransform, ownerSpace); - } - } - } - - @Override - public String getConstraintTypeName() { - return "Maintain volume"; - } - - @Override - public boolean isTargetRequired() { - return false; - } -} diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionNull.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionNull.java deleted file mode 100644 index 032909c65..000000000 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionNull.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.jme3.scene.plugins.blender.constraints.definitions; - -import com.jme3.math.Transform; -import com.jme3.scene.plugins.blender.BlenderContext; -import com.jme3.scene.plugins.blender.constraints.ConstraintHelper.Space; -import com.jme3.scene.plugins.blender.file.Structure; - -/** - * This class represents 'Null' constraint type in blender. - * - * @author Marcin Roguski (Kaelthas) - */ -/* package */class ConstraintDefinitionNull extends ConstraintDefinition { - - public ConstraintDefinitionNull(Structure constraintData, Long ownerOMA, BlenderContext blenderContext) { - super(constraintData, ownerOMA, blenderContext); - trackToBeChanged = false; - } - - @Override - public void bake(Space ownerSpace, Space targetSpace, Transform targetTransform, float influence) { - // null constraint does nothing so no need to implement this one - } - - @Override - public String getConstraintTypeName() { - return "Null"; - } - - @Override - public boolean isTargetRequired() { - return false; - } -} diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionRotLike.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionRotLike.java deleted file mode 100644 index 280e4e77e..000000000 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionRotLike.java +++ /dev/null @@ -1,87 +0,0 @@ -package com.jme3.scene.plugins.blender.constraints.definitions; - -import com.jme3.math.Quaternion; -import com.jme3.math.Transform; -import com.jme3.scene.plugins.blender.BlenderContext; -import com.jme3.scene.plugins.blender.constraints.ConstraintHelper.Space; -import com.jme3.scene.plugins.blender.file.Structure; - -/** - * This class represents 'Rot like' constraint type in blender. - * - * @author Marcin Roguski (Kaelthas) - */ -/* package */class ConstraintDefinitionRotLike extends ConstraintDefinition { - private static final int ROTLIKE_X = 0x01; - private static final int ROTLIKE_Y = 0x02; - private static final int ROTLIKE_Z = 0x04; - private static final int ROTLIKE_X_INVERT = 0x10; - private static final int ROTLIKE_Y_INVERT = 0x20; - private static final int ROTLIKE_Z_INVERT = 0x40; - private static final int ROTLIKE_OFFSET = 0x80; - - private transient float[] ownerAngles = new float[3]; - private transient float[] targetAngles = new float[3]; - - public ConstraintDefinitionRotLike(Structure constraintData, Long ownerOMA, BlenderContext blenderContext) { - super(constraintData, ownerOMA, blenderContext); - trackToBeChanged = (flag & (ROTLIKE_X | ROTLIKE_Y | ROTLIKE_Z)) != 0; - } - - @Override - public void bake(Space ownerSpace, Space targetSpace, Transform targetTransform, float influence) { - if (influence == 0 || targetTransform == null || !trackToBeChanged) { - return;// no need to do anything - } - Transform ownerTransform = this.getOwnerTransform(ownerSpace); - - Quaternion ownerRotation = ownerTransform.getRotation(); - ownerAngles = ownerRotation.toAngles(ownerAngles); - targetAngles = targetTransform.getRotation().toAngles(targetAngles); - - Quaternion startRotation = ownerRotation.clone(); - Quaternion offset = Quaternion.IDENTITY; - if ((flag & ROTLIKE_OFFSET) != 0) {// we add the original rotation to - // the copied rotation - offset = startRotation; - } - - if ((flag & ROTLIKE_X) != 0) { - ownerAngles[0] = targetAngles[0]; - if ((flag & ROTLIKE_X_INVERT) != 0) { - ownerAngles[0] = -ownerAngles[0]; - } - } - if ((flag & ROTLIKE_Y) != 0) { - ownerAngles[1] = targetAngles[1]; - if ((flag & ROTLIKE_Y_INVERT) != 0) { - ownerAngles[1] = -ownerAngles[1]; - } - } - if ((flag & ROTLIKE_Z) != 0) { - ownerAngles[2] = targetAngles[2]; - if ((flag & ROTLIKE_Z_INVERT) != 0) { - ownerAngles[2] = -ownerAngles[2]; - } - } - ownerRotation.fromAngles(ownerAngles).multLocal(offset); - - if (influence < 1.0f) { - // startLocation.subtractLocal(ownerLocation).normalizeLocal().mult(influence); - // ownerLocation.addLocal(startLocation); - // TODO - } - - this.applyOwnerTransform(ownerTransform, ownerSpace); - } - - @Override - public String getConstraintTypeName() { - return "Copy rotation"; - } - - @Override - public boolean isTargetRequired() { - return true; - } -} diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionRotLimit.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionRotLimit.java deleted file mode 100644 index 3d569447f..000000000 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionRotLimit.java +++ /dev/null @@ -1,129 +0,0 @@ -package com.jme3.scene.plugins.blender.constraints.definitions; - -import com.jme3.math.FastMath; -import com.jme3.math.Transform; -import com.jme3.scene.plugins.blender.BlenderContext; -import com.jme3.scene.plugins.blender.constraints.ConstraintHelper.Space; -import com.jme3.scene.plugins.blender.file.Structure; - -/** - * This class represents 'Rot limit' constraint type in blender. - * - * @author Marcin Roguski (Kaelthas) - */ -/* package */class ConstraintDefinitionRotLimit extends ConstraintDefinition { - private static final int LIMIT_XROT = 0x01; - private static final int LIMIT_YROT = 0x02; - private static final int LIMIT_ZROT = 0x04; - - private transient float[][] limits = new float[3][2]; - private transient float[] angles = new float[3]; - - public ConstraintDefinitionRotLimit(Structure constraintData, Long ownerOMA, BlenderContext blenderContext) { - super(constraintData, ownerOMA, blenderContext); - if (blenderContext.getBlenderKey().isFixUpAxis()) { - limits[0][0] = ((Number) constraintData.getFieldValue("xmin")).floatValue(); - limits[0][1] = ((Number) constraintData.getFieldValue("xmax")).floatValue(); - limits[2][0] = ((Number) constraintData.getFieldValue("ymin")).floatValue(); - limits[2][1] = ((Number) constraintData.getFieldValue("ymax")).floatValue(); - limits[1][0] = ((Number) constraintData.getFieldValue("zmin")).floatValue(); - limits[1][1] = ((Number) constraintData.getFieldValue("zmax")).floatValue(); - - // swapping Y and X limits flag in the bitwise flag - int limitY = flag & LIMIT_YROT; - int limitZ = flag & LIMIT_ZROT; - flag &= LIMIT_XROT;// clear the other flags to swap them - flag |= limitY << 1; - flag |= limitZ >> 1; - } else { - limits[0][0] = ((Number) constraintData.getFieldValue("xmin")).floatValue(); - limits[0][1] = ((Number) constraintData.getFieldValue("xmax")).floatValue(); - limits[1][0] = ((Number) constraintData.getFieldValue("ymin")).floatValue(); - limits[1][1] = ((Number) constraintData.getFieldValue("ymax")).floatValue(); - limits[2][0] = ((Number) constraintData.getFieldValue("zmin")).floatValue(); - limits[2][1] = ((Number) constraintData.getFieldValue("zmax")).floatValue(); - } - - // until blender 2.49 the rotations values were stored in degrees - if (blenderContext.getBlenderVersion() <= 249) { - for (int i = 0; i < 3; ++i) { - limits[i][0] *= FastMath.DEG_TO_RAD; - limits[i][1] *= FastMath.DEG_TO_RAD; - } - } - - // make sure that the limits are always in range [0, 2PI) - // TODO: left it here because it is essential to make sure all cases - // work poperly - // but will do it a little bit later ;) - /* - * for (int i = 0; i < 3; ++i) { for (int j = 0; j < 2; ++j) { int - * multFactor = (int)Math.abs(limits[i][j] / FastMath.TWO_PI) ; if - * (limits[i][j] < 0) { limits[i][j] += FastMath.TWO_PI * (multFactor + - * 1); } else { limits[i][j] -= FastMath.TWO_PI * multFactor; } } //make - * sure the lower limit is not greater than the upper one - * if(limits[i][0] > limits[i][1]) { float temp = limits[i][0]; - * limits[i][0] = limits[i][1]; limits[i][1] = temp; } } - */ - - trackToBeChanged = (flag & (LIMIT_XROT | LIMIT_YROT | LIMIT_ZROT)) != 0; - } - - @Override - public void bake(Space ownerSpace, Space targetSpace, Transform targetTransform, float influence) { - if (influence == 0 || !trackToBeChanged) { - return; - } - Transform ownerTransform = this.getOwnerTransform(ownerSpace); - - ownerTransform.getRotation().toAngles(angles); - // make sure that the rotations are always in range [0, 2PI) - // TODO: same comment as in constructor - /* - * for (int i = 0; i < 3; ++i) { int multFactor = - * (int)Math.abs(angles[i] / FastMath.TWO_PI) ; if(angles[i] < 0) { - * angles[i] += FastMath.TWO_PI * (multFactor + 1); } else { angles[i] - * -= FastMath.TWO_PI * multFactor; } } - */ - if ((flag & LIMIT_XROT) != 0) { - float difference = 0.0f; - if (angles[0] < limits[0][0]) { - difference = (angles[0] - limits[0][0]) * influence; - } else if (angles[0] > limits[0][1]) { - difference = (angles[0] - limits[0][1]) * influence; - } - angles[0] -= difference; - } - if ((flag & LIMIT_YROT) != 0) { - float difference = 0.0f; - if (angles[1] < limits[1][0]) { - difference = (angles[1] - limits[1][0]) * influence; - } else if (angles[1] > limits[1][1]) { - difference = (angles[1] - limits[1][1]) * influence; - } - angles[1] -= difference; - } - if ((flag & LIMIT_ZROT) != 0) { - float difference = 0.0f; - if (angles[2] < limits[2][0]) { - difference = (angles[2] - limits[2][0]) * influence; - } else if (angles[2] > limits[2][1]) { - difference = (angles[2] - limits[2][1]) * influence; - } - angles[2] -= difference; - } - ownerTransform.getRotation().fromAngles(angles); - - this.applyOwnerTransform(ownerTransform, ownerSpace); - } - - @Override - public String getConstraintTypeName() { - return "Limit rotation"; - } - - @Override - public boolean isTargetRequired() { - return false; - } -} diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionSizeLike.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionSizeLike.java deleted file mode 100644 index b05048af6..000000000 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionSizeLike.java +++ /dev/null @@ -1,74 +0,0 @@ -package com.jme3.scene.plugins.blender.constraints.definitions; - -import com.jme3.math.Transform; -import com.jme3.math.Vector3f; -import com.jme3.scene.plugins.blender.BlenderContext; -import com.jme3.scene.plugins.blender.constraints.ConstraintHelper.Space; -import com.jme3.scene.plugins.blender.file.Structure; - -/** - * This class represents 'Size like' constraint type in blender. - * - * @author Marcin Roguski (Kaelthas) - */ -/* package */class ConstraintDefinitionSizeLike extends ConstraintDefinition { - private static final int SIZELIKE_X = 0x01; - private static final int SIZELIKE_Y = 0x02; - private static final int SIZELIKE_Z = 0x04; - private static final int LOCLIKE_OFFSET = 0x80; - - public ConstraintDefinitionSizeLike(Structure constraintData, Long ownerOMA, BlenderContext blenderContext) { - super(constraintData, ownerOMA, blenderContext); - if (blenderContext.getBlenderKey().isFixUpAxis()) { - // swapping Y and X limits flag in the bitwise flag - int y = flag & SIZELIKE_Y; - int z = flag & SIZELIKE_Z; - flag &= SIZELIKE_X | LOCLIKE_OFFSET;// clear the other flags to swap - // them - flag |= y << 1; - flag |= z >> 1; - - trackToBeChanged = (flag & (SIZELIKE_X | SIZELIKE_Y | SIZELIKE_Z)) != 0; - } - } - - @Override - public void bake(Space ownerSpace, Space targetSpace, Transform targetTransform, float influence) { - if (influence == 0 || targetTransform == null || !trackToBeChanged) { - return;// no need to do anything - } - Transform ownerTransform = this.getOwnerTransform(ownerSpace); - - Vector3f ownerScale = ownerTransform.getScale(); - Vector3f targetScale = targetTransform.getScale(); - - Vector3f offset = Vector3f.ZERO; - if ((flag & LOCLIKE_OFFSET) != 0) {// we add the original scale to the - // copied scale - offset = ownerScale.clone(); - } - - if ((flag & SIZELIKE_X) != 0) { - ownerScale.x = targetScale.x * influence + (1.0f - influence) * ownerScale.x; - } - if ((flag & SIZELIKE_Y) != 0) { - ownerScale.y = targetScale.y * influence + (1.0f - influence) * ownerScale.y; - } - if ((flag & SIZELIKE_Z) != 0) { - ownerScale.z = targetScale.z * influence + (1.0f - influence) * ownerScale.z; - } - ownerScale.addLocal(offset); - - this.applyOwnerTransform(ownerTransform, ownerSpace); - } - - @Override - public String getConstraintTypeName() { - return "Copy scale"; - } - - @Override - public boolean isTargetRequired() { - return true; - } -} diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionSizeLimit.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionSizeLimit.java deleted file mode 100644 index 6c133bf18..000000000 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionSizeLimit.java +++ /dev/null @@ -1,96 +0,0 @@ -package com.jme3.scene.plugins.blender.constraints.definitions; - -import com.jme3.math.Transform; -import com.jme3.math.Vector3f; -import com.jme3.scene.plugins.blender.BlenderContext; -import com.jme3.scene.plugins.blender.constraints.ConstraintHelper.Space; -import com.jme3.scene.plugins.blender.file.Structure; - -/** - * This class represents 'Size limit' constraint type in blender. - * - * @author Marcin Roguski (Kaelthas) - */ -/* package */class ConstraintDefinitionSizeLimit extends ConstraintDefinition { - private static final int LIMIT_XMIN = 0x01; - private static final int LIMIT_XMAX = 0x02; - private static final int LIMIT_YMIN = 0x04; - private static final int LIMIT_YMAX = 0x08; - private static final int LIMIT_ZMIN = 0x10; - private static final int LIMIT_ZMAX = 0x20; - - protected transient float[][] limits = new float[3][2]; - - public ConstraintDefinitionSizeLimit(Structure constraintData, Long ownerOMA, BlenderContext blenderContext) { - super(constraintData, ownerOMA, blenderContext); - if (blenderContext.getBlenderKey().isFixUpAxis()) { - limits[0][0] = ((Number) constraintData.getFieldValue("xmin")).floatValue(); - limits[0][1] = ((Number) constraintData.getFieldValue("xmax")).floatValue(); - limits[2][0] = -((Number) constraintData.getFieldValue("ymin")).floatValue(); - limits[2][1] = -((Number) constraintData.getFieldValue("ymax")).floatValue(); - limits[1][0] = ((Number) constraintData.getFieldValue("zmin")).floatValue(); - limits[1][1] = ((Number) constraintData.getFieldValue("zmax")).floatValue(); - - // swapping Y and X limits flag in the bitwise flag - int ymin = flag & LIMIT_YMIN; - int ymax = flag & LIMIT_YMAX; - int zmin = flag & LIMIT_ZMIN; - int zmax = flag & LIMIT_ZMAX; - flag &= LIMIT_XMIN | LIMIT_XMAX;// clear the other flags to swap - // them - flag |= ymin << 2; - flag |= ymax << 2; - flag |= zmin >> 2; - flag |= zmax >> 2; - } else { - limits[0][0] = ((Number) constraintData.getFieldValue("xmin")).floatValue(); - limits[0][1] = ((Number) constraintData.getFieldValue("xmax")).floatValue(); - limits[1][0] = ((Number) constraintData.getFieldValue("ymin")).floatValue(); - limits[1][1] = ((Number) constraintData.getFieldValue("ymax")).floatValue(); - limits[2][0] = ((Number) constraintData.getFieldValue("zmin")).floatValue(); - limits[2][1] = ((Number) constraintData.getFieldValue("zmax")).floatValue(); - } - - trackToBeChanged = (flag & (LIMIT_XMIN | LIMIT_XMAX | LIMIT_YMIN | LIMIT_YMAX | LIMIT_ZMIN | LIMIT_ZMAX)) != 0; - } - - @Override - public void bake(Space ownerSpace, Space targetSpace, Transform targetTransform, float influence) { - if (influence == 0 || !trackToBeChanged) { - return; - } - Transform ownerTransform = this.getOwnerTransform(ownerSpace); - - Vector3f scale = ownerTransform.getScale(); - if ((flag & LIMIT_XMIN) != 0 && scale.x < limits[0][0]) { - scale.x -= (scale.x - limits[0][0]) * influence; - } - if ((flag & LIMIT_XMAX) != 0 && scale.x > limits[0][1]) { - scale.x -= (scale.x - limits[0][1]) * influence; - } - if ((flag & LIMIT_YMIN) != 0 && scale.y < limits[1][0]) { - scale.y -= (scale.y - limits[1][0]) * influence; - } - if ((flag & LIMIT_YMAX) != 0 && scale.y > limits[1][1]) { - scale.y -= (scale.y - limits[1][1]) * influence; - } - if ((flag & LIMIT_ZMIN) != 0 && scale.z < limits[2][0]) { - scale.z -= (scale.z - limits[2][0]) * influence; - } - if ((flag & LIMIT_ZMAX) != 0 && scale.z > limits[2][1]) { - scale.z -= (scale.z - limits[2][1]) * influence; - } - - this.applyOwnerTransform(ownerTransform, ownerSpace); - } - - @Override - public String getConstraintTypeName() { - return "Limit scale"; - } - - @Override - public boolean isTargetRequired() { - return false; - } -} diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionTransLike.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionTransLike.java deleted file mode 100644 index b38857279..000000000 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionTransLike.java +++ /dev/null @@ -1,81 +0,0 @@ -package com.jme3.scene.plugins.blender.constraints.definitions; - -import com.jme3.animation.Bone; -import com.jme3.animation.Skeleton; -import com.jme3.math.Matrix4f; -import com.jme3.math.Transform; -import com.jme3.scene.plugins.blender.BlenderContext; -import com.jme3.scene.plugins.blender.BlenderContext.LoadedDataType; -import com.jme3.scene.plugins.blender.animations.BoneContext; -import com.jme3.scene.plugins.blender.constraints.ConstraintHelper; -import com.jme3.scene.plugins.blender.constraints.ConstraintHelper.Space; -import com.jme3.scene.plugins.blender.file.Pointer; -import com.jme3.scene.plugins.blender.file.Structure; -import com.jme3.scene.plugins.blender.objects.ObjectHelper; -import com.jme3.util.TempVars; - -/** - * This class represents 'Trans like' constraint type in blender. - * - * @author Marcin Roguski (Kaelthas) - */ -public class ConstraintDefinitionTransLike extends ConstraintDefinition { - private Long targetOMA; - private String subtargetName; - - public ConstraintDefinitionTransLike(Structure constraintData, Long ownerOMA, BlenderContext blenderContext) { - super(constraintData, ownerOMA, blenderContext); - Pointer pTarget = (Pointer) constraintData.getFieldValue("tar"); - targetOMA = pTarget.getOldMemoryAddress(); - Object subtarget = constraintData.getFieldValue("subtarget"); - if (subtarget != null) { - subtargetName = subtarget.toString(); - } - } - - @Override - public void bake(Space ownerSpace, Space targetSpace, Transform targetTransform, float influence) { - if (influence == 0 || targetTransform == null) { - return;// no need to do anything - } - Object target = this.getTarget();// Bone or Node - Object owner = this.getOwner();// Bone or Node - if (!target.getClass().equals(owner.getClass())) { - ConstraintHelper constraintHelper = blenderContext.getHelper(ConstraintHelper.class); - - TempVars tempVars = TempVars.get(); - Matrix4f m = constraintHelper.toMatrix(targetTransform, tempVars.tempMat4); - tempVars.tempMat42.set(BoneContext.BONE_ARMATURE_TRANSFORMATION_MATRIX); - if (target instanceof Bone) { - tempVars.tempMat42.invertLocal(); - } - m = m.multLocal(tempVars.tempMat42); - tempVars.release(); - - targetTransform = new Transform(m.toTranslationVector(), m.toRotationQuat(), m.toScaleVector()); - } - this.applyOwnerTransform(targetTransform, ownerSpace); - } - - /** - * @return the target feature; it is either Node or Bone (vertex group subtarger is not yet supported) - */ - private Object getTarget() { - Object target = blenderContext.getLoadedFeature(targetOMA, LoadedDataType.FEATURE); - if (subtargetName != null && blenderContext.getMarkerValue(ObjectHelper.ARMATURE_NODE_MARKER, target) != null) { - Skeleton skeleton = blenderContext.getSkeleton(targetOMA); - target = skeleton.getBone(subtargetName); - } - return target; - } - - @Override - public String getConstraintTypeName() { - return "Copy transforms"; - } - - @Override - public boolean isTargetRequired() { - return true; - } -} diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/definitions/UnsupportedConstraintDefinition.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/definitions/UnsupportedConstraintDefinition.java deleted file mode 100644 index e3fa82e9a..000000000 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/definitions/UnsupportedConstraintDefinition.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.jme3.scene.plugins.blender.constraints.definitions; - -import com.jme3.math.Transform; -import com.jme3.scene.plugins.blender.constraints.ConstraintHelper.Space; - -/** - * This class represents a constraint that is defined by blender but not - * supported by either importer ot jme. It only wirtes down a warning when - * baking is called. - * - * @author Marcin Roguski (Kaelthas) - */ -/* package */class UnsupportedConstraintDefinition extends ConstraintDefinition { - private String typeName; - - public UnsupportedConstraintDefinition(String typeName) { - super(null, null, null); - this.typeName = typeName; - trackToBeChanged = false; - } - - @Override - public void bake(Space ownerSpace, Space targetSpace, Transform targetTransform, float influence) { - } - - @Override - public boolean isImplemented() { - return false; - } - - @Override - public String getConstraintTypeName() { - return typeName; - } - - @Override - public boolean isTargetRequired() { - return false; - } -} diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/curves/BezierCurve.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/curves/BezierCurve.java deleted file mode 100644 index 96a91335d..000000000 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/curves/BezierCurve.java +++ /dev/null @@ -1,172 +0,0 @@ -package com.jme3.scene.plugins.blender.curves; - -import java.util.ArrayList; -import java.util.List; - -import com.jme3.math.Vector3f; -import com.jme3.scene.plugins.blender.file.DynamicArray; -import com.jme3.scene.plugins.blender.file.Structure; - -/** - * A class that helps to calculate the bezier curves calues. It uses doubles for performing calculations to minimize - * floating point operations errors. - * @author Marcin Roguski (Kaelthas) - */ -public class BezierCurve { - private static final int IPO_CONSTANT = 0; - private static final int IPO_LINEAR = 1; - private static final int IPO_BEZIER = 2; - - public static final int X_VALUE = 0; - public static final int Y_VALUE = 1; - public static final int Z_VALUE = 2; - /** - * The type of the curve. Describes the data it modifies. - * Used in ipos calculations. - */ - private int type; - /** The dimension of the curve. */ - private int dimension; - /** A table of the bezier points. */ - private double[][][] bezierPoints; - /** Array that stores a radius for each bezier triple. */ - private double[] radiuses; - /** Interpolation types of the bezier triples. */ - private int[] interpolations; - - public BezierCurve(final int type, final List bezTriples, final int dimension) { - this(type, bezTriples, dimension, false); - } - - @SuppressWarnings("unchecked") - public BezierCurve(final int type, final List bezTriples, final int dimension, boolean fixUpAxis) { - if (dimension != 2 && dimension != 3) { - throw new IllegalArgumentException("The dimension of the curve should be 2 or 3!"); - } - this.type = type; - this.dimension = dimension; - // first index of the bezierPoints table has the length of triples amount - // the second index points to a table od three points of a bezier triple (handle, point, handle) - // the third index specifies the coordinates of the specific point in a bezier triple - bezierPoints = new double[bezTriples.size()][3][dimension]; - radiuses = new double[bezTriples.size()]; - interpolations = new int[bezTriples.size()]; - int i = 0, j, k; - for (Structure bezTriple : bezTriples) { - DynamicArray vec = (DynamicArray) bezTriple.getFieldValue("vec"); - for (j = 0; j < 3; ++j) { - for (k = 0; k < dimension; ++k) { - bezierPoints[i][j][k] = vec.get(j, k).doubleValue(); - } - if (fixUpAxis && dimension == 3) { - double temp = bezierPoints[i][j][2]; - bezierPoints[i][j][2] = -bezierPoints[i][j][1]; - bezierPoints[i][j][1] = temp; - } - } - radiuses[i] = ((Number) bezTriple.getFieldValue("radius")).floatValue(); - interpolations[i++] = ((Number) bezTriple.getFieldValue("ipo", IPO_BEZIER)).intValue(); - } - } - - /** - * This method evaluates the data for the specified frame. The Y value is returned. - * @param frame - * the frame for which the value is being calculated - * @param valuePart - * this param specifies wheather we should return the X, Y or Z part of the result value; it should have - * one of the following values: X_VALUE - the X factor of the result Y_VALUE - the Y factor of the result - * Z_VALUE - the Z factor of the result - * @return the value of the curve - */ - public double evaluate(int frame, int valuePart) { - for (int i = 0; i < bezierPoints.length - 1; ++i) { - if (frame >= bezierPoints[i][1][0] && frame <= bezierPoints[i + 1][1][0]) { - double t = (frame - bezierPoints[i][1][0]) / (bezierPoints[i + 1][1][0] - bezierPoints[i][1][0]); - switch (interpolations[i]) { - case IPO_BEZIER: - double oneMinusT = 1.0f - t; - double oneMinusT2 = oneMinusT * oneMinusT; - double t2 = t * t; - return bezierPoints[i][1][valuePart] * oneMinusT2 * oneMinusT + 3.0f * bezierPoints[i][2][valuePart] * t * oneMinusT2 + 3.0f * bezierPoints[i + 1][0][valuePart] * t2 * oneMinusT + bezierPoints[i + 1][1][valuePart] * t2 * t; - case IPO_LINEAR: - return (1f - t) * bezierPoints[i][1][valuePart] + t * bezierPoints[i + 1][1][valuePart]; - case IPO_CONSTANT: - return bezierPoints[i][1][valuePart]; - default: - throw new IllegalStateException("Unknown interpolation type for curve: " + interpolations[i]); - } - } - } - if (frame < bezierPoints[0][1][0]) { - return bezierPoints[0][1][1]; - } else { // frame>bezierPoints[bezierPoints.length-1][1][0] - return bezierPoints[bezierPoints.length - 1][1][1]; - } - } - - /** - * This method returns the frame where last bezier triple center point of the bezier curve is located. - * @return the frame number of the last defined bezier triple point for the curve - */ - public int getLastFrame() { - return (int) bezierPoints[bezierPoints.length - 1][1][0]; - } - - /** - * This method returns the type of the bezier curve. The type describes the parameter that this curve modifies - * (ie. LocationX or rotationW of the feature). - * @return the type of the bezier curve - */ - public int getType() { - return type; - } - - /** - * The method returns the radius for the required bezier triple. - * - * @param bezierTripleIndex - * index of the bezier triple - * @return radius of the required bezier triple - */ - public double getRadius(int bezierTripleIndex) { - return radiuses[bezierTripleIndex]; - } - - /** - * This method returns a list of control points for this curve. - * @return a list of control points for this curve. - */ - public List getControlPoints() { - List controlPoints = new ArrayList(bezierPoints.length * 3); - for (int i = 0; i < bezierPoints.length; ++i) { - controlPoints.add(new Vector3f((float) bezierPoints[i][0][0], (float) bezierPoints[i][0][1], (float) bezierPoints[i][0][2])); - controlPoints.add(new Vector3f((float) bezierPoints[i][1][0], (float) bezierPoints[i][1][1], (float) bezierPoints[i][1][2])); - controlPoints.add(new Vector3f((float) bezierPoints[i][2][0], (float) bezierPoints[i][2][1], (float) bezierPoints[i][2][2])); - } - return controlPoints; - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder("Bezier curve: ").append(type).append('\n'); - for (int i = 0; i < bezierPoints.length; ++i) { - sb.append(this.toStringBezTriple(i)).append('\n'); - } - return sb.toString(); - } - - /** - * This method converts the bezier triple of a specified index into text. - * @param tripleIndex - * index of the triple - * @return text representation of the triple - */ - private String toStringBezTriple(int tripleIndex) { - if (dimension == 2) { - return "[(" + bezierPoints[tripleIndex][0][0] + ", " + bezierPoints[tripleIndex][0][1] + ") (" + bezierPoints[tripleIndex][1][0] + ", " + bezierPoints[tripleIndex][1][1] + ") (" + bezierPoints[tripleIndex][2][0] + ", " + bezierPoints[tripleIndex][2][1] + ")]"; - } else { - return "[(" + bezierPoints[tripleIndex][0][0] + ", " + bezierPoints[tripleIndex][0][1] + ", " + bezierPoints[tripleIndex][0][2] + ") (" + bezierPoints[tripleIndex][1][0] + ", " + bezierPoints[tripleIndex][1][1] + ", " + bezierPoints[tripleIndex][1][2] + ") (" + bezierPoints[tripleIndex][2][0] + ", " + bezierPoints[tripleIndex][2][1] + ", " + bezierPoints[tripleIndex][2][2] + ")]"; - } - } -} \ No newline at end of file diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/curves/CurvesHelper.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/curves/CurvesHelper.java deleted file mode 100644 index dd033d3dc..000000000 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/curves/CurvesHelper.java +++ /dev/null @@ -1,159 +0,0 @@ -/* - * 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.blender.curves; - -import java.util.logging.Logger; - -import com.jme3.math.FastMath; -import com.jme3.math.Matrix4f; -import com.jme3.math.Quaternion; -import com.jme3.math.Vector3f; -import com.jme3.scene.plugins.blender.AbstractBlenderHelper; -import com.jme3.scene.plugins.blender.BlenderContext; -import com.jme3.scene.plugins.blender.file.BlenderFileException; -import com.jme3.scene.plugins.blender.file.Structure; - -/** - * A class that is used in mesh calculations. - * - * @author Marcin Roguski (Kaelthas) - */ -public class CurvesHelper extends AbstractBlenderHelper { - private static final Logger LOGGER = Logger.getLogger(CurvesHelper.class.getName()); - - /** Minimum basis U function degree for NURBS curves and surfaces. */ - protected int minimumBasisUFunctionDegree = 4; - /** Minimum basis V function degree for NURBS curves and surfaces. */ - protected int minimumBasisVFunctionDegree = 4; - - /** - * This constructor parses the given blender version and stores the result. Some functionalities may differ in - * different blender versions. - * @param blenderVersion - * the version read from the blend file - * @param blenderContext - * the blender context - */ - public CurvesHelper(String blenderVersion, BlenderContext blenderContext) { - super(blenderVersion, blenderContext); - } - - public CurvesTemporalMesh toCurve(Structure curveStructure, BlenderContext blenderContext) throws BlenderFileException { - CurvesTemporalMesh result = new CurvesTemporalMesh(curveStructure, blenderContext); - - if (blenderContext.getBlenderKey().isLoadObjectProperties()) { - LOGGER.fine("Reading custom properties."); - result.setProperties(this.loadProperties(curveStructure, blenderContext)); - } - - return result; - } - - /** - * The method transforms the bevel along the curve. - * - * @param bevel - * the bevel to be transformed - * @param prevPos - * previous curve point - * @param currPos - * current curve point (here the center of the new bevel will be - * set) - * @param nextPos - * next curve point - * @return points of transformed bevel - */ - protected Vector3f[] transformBevel(Vector3f[] bevel, Vector3f prevPos, Vector3f currPos, Vector3f nextPos) { - bevel = bevel.clone(); - - // currPos and directionVector define the line in 3D space - Vector3f directionVector = prevPos != null ? currPos.subtract(prevPos) : nextPos.subtract(currPos); - directionVector.normalizeLocal(); - - // plane is described by equation: Ax + By + Cz + D = 0 where planeNormal = [A, B, C] and D = -(Ax + By + Cz) - Vector3f planeNormal = null; - if (prevPos != null) { - planeNormal = currPos.subtract(prevPos).normalizeLocal(); - if (nextPos != null) { - planeNormal.addLocal(nextPos.subtract(currPos).normalizeLocal()).normalizeLocal(); - } - } else { - planeNormal = nextPos.subtract(currPos).normalizeLocal(); - } - float D = -planeNormal.dot(currPos);// D = -(Ax + By + Cz) - - // now we need to compute paralell cast of each bevel point on the plane, the leading line is already known - // parametric equation of a line: x = px + vx * t; y = py + vy * t; z = pz + vz * t - // where p = currPos and v = directionVector - // using x, y and z in plane equation we get value of 't' that will allow us to compute the point where plane and line cross - float temp = planeNormal.dot(directionVector); - for (int i = 0; i < bevel.length; ++i) { - float t = -(planeNormal.dot(bevel[i]) + D) / temp; - if (fixUpAxis) { - bevel[i] = new Vector3f(bevel[i].x + directionVector.x * t, bevel[i].y + directionVector.y * t, bevel[i].z + directionVector.z * t); - } else { - bevel[i] = new Vector3f(bevel[i].x + directionVector.x * t, -bevel[i].z + directionVector.z * t, bevel[i].y + directionVector.y * t); - } - } - return bevel; - } - - /** - * This method transforms the first line of the bevel points positioning it - * on the first point of the curve. - * - * @param startingLinePoints - * the vbevel shape points - * @param firstCurvePoint - * the first curve's point - * @param secondCurvePoint - * the second curve's point - * @return points of transformed bevel - */ - protected Vector3f[] transformToFirstLineOfBevelPoints(Vector3f[] startingLinePoints, Vector3f firstCurvePoint, Vector3f secondCurvePoint) { - Vector3f planeNormal = secondCurvePoint.subtract(firstCurvePoint).normalizeLocal(); - - float angle = FastMath.acos(planeNormal.dot(Vector3f.UNIT_X)); - Vector3f rotationVector = Vector3f.UNIT_X.cross(planeNormal).normalizeLocal(); - - Matrix4f m = new Matrix4f(); - m.setRotationQuaternion(new Quaternion().fromAngleAxis(angle, rotationVector)); - m.setTranslation(firstCurvePoint); - - Vector3f temp = new Vector3f(); - Vector3f[] verts = new Vector3f[startingLinePoints.length]; - for (int i = 0; i < verts.length; ++i) { - verts[i] = m.mult(startingLinePoints[i], temp).clone(); - } - return verts; - } -} \ No newline at end of file diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/curves/CurvesTemporalMesh.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/curves/CurvesTemporalMesh.java deleted file mode 100644 index 2ad8d3ff2..000000000 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/curves/CurvesTemporalMesh.java +++ /dev/null @@ -1,890 +0,0 @@ -package com.jme3.scene.plugins.blender.curves; - -import java.nio.FloatBuffer; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.TreeMap; -import java.util.logging.Logger; - -import com.jme3.material.RenderState.FaceCullMode; -import com.jme3.math.FastMath; -import com.jme3.math.Spline; -import com.jme3.math.Spline.SplineType; -import com.jme3.math.Vector3f; -import com.jme3.math.Vector4f; -import com.jme3.scene.VertexBuffer.Type; -import com.jme3.scene.mesh.IndexBuffer; -import com.jme3.scene.plugins.blender.BlenderContext; -import com.jme3.scene.plugins.blender.file.BlenderFileException; -import com.jme3.scene.plugins.blender.file.BlenderInputStream; -import com.jme3.scene.plugins.blender.file.DynamicArray; -import com.jme3.scene.plugins.blender.file.FileBlockHeader; -import com.jme3.scene.plugins.blender.file.Pointer; -import com.jme3.scene.plugins.blender.file.Structure; -import com.jme3.scene.plugins.blender.materials.MaterialContext; -import com.jme3.scene.plugins.blender.materials.MaterialHelper; -import com.jme3.scene.plugins.blender.meshes.Edge; -import com.jme3.scene.plugins.blender.meshes.Face; -import com.jme3.scene.plugins.blender.meshes.TemporalMesh; -import com.jme3.scene.shape.Curve; -import com.jme3.scene.shape.Surface; -import com.jme3.util.BufferUtils; - -/** - * A temporal mesh for curves and surfaces. It works in similar way as TemporalMesh for meshes. - * It prepares all necessary lines and faces and allows to apply modifiers just like in regular temporal mesh. - * - * @author Marcin Roguski (Kaelthas) - */ -public class CurvesTemporalMesh extends TemporalMesh { - private static final Logger LOGGER = Logger.getLogger(CurvesTemporalMesh.class.getName()); - - private static final int TYPE_BEZIER = 0x0001; - private static final int TYPE_NURBS = 0x0004; - - private static final int FLAG_3D = 0x0001; - private static final int FLAG_FRONT = 0x0002; - private static final int FLAG_BACK = 0x0004; - private static final int FLAG_FILL_CAPS = 0x4000; - - private static final int FLAG_SMOOTH = 0x0001; - - protected CurvesHelper curvesHelper; - protected boolean is2D; - protected boolean isFront; - protected boolean isBack; - protected boolean fillCaps; - protected float bevelStart; - protected float bevelEnd; - protected List beziers = new ArrayList(); - protected CurvesTemporalMesh bevelObject; - protected CurvesTemporalMesh taperObject; - /** The scale that is used if the curve is a bevel or taper curve. */ - protected Vector3f scale = new Vector3f(1, 1, 1); - - /** - * The constructor creates an empty temporal mesh. - * @param blenderContext - * the blender context - * @throws BlenderFileException - * this will never be thrown here - */ - protected CurvesTemporalMesh(BlenderContext blenderContext) throws BlenderFileException { - super(null, blenderContext, false); - } - - /** - * Loads the temporal mesh from the given curve structure. The mesh can be either curve or surface. - * @param curveStructure - * the structure that contains the curve/surface data - * @param blenderContext - * the blender context - * @throws BlenderFileException - * an exception is thrown when problems with reading occur - */ - public CurvesTemporalMesh(Structure curveStructure, BlenderContext blenderContext) throws BlenderFileException { - this(curveStructure, new Vector3f(1, 1, 1), true, blenderContext); - } - - /** - * Loads the temporal mesh from the given curve structure. The mesh can be either curve or surface. - * @param curveStructure - * the structure that contains the curve/surface data - * @param scale - * the scale used if the current curve is used as a bevel curve - * @param loadBevelAndTaper indicates if bevel and taper should be loaded (this is not needed for curves that are loaded to be used as bevel and taper) - * @param blenderContext - * the blender context - * @throws BlenderFileException - * an exception is thrown when problems with reading occur - */ - @SuppressWarnings("unchecked") - private CurvesTemporalMesh(Structure curveStructure, Vector3f scale, boolean loadBevelAndTaper, BlenderContext blenderContext) throws BlenderFileException { - super(curveStructure, blenderContext, false); - name = curveStructure.getName(); - curvesHelper = blenderContext.getHelper(CurvesHelper.class); - this.scale = scale; - - int flag = ((Number) curveStructure.getFieldValue("flag")).intValue(); - is2D = (flag & FLAG_3D) == 0; - if (is2D) { - // TODO: add support for 3D flag - LOGGER.warning("2D flag not yet supported for curves!"); - } - isFront = (flag & FLAG_FRONT) != 0; - isBack = (flag & FLAG_BACK) != 0; - fillCaps = (flag & FLAG_FILL_CAPS) != 0; - bevelStart = ((Number) curveStructure.getFieldValue("bevfac1", 0)).floatValue(); - bevelEnd = ((Number) curveStructure.getFieldValue("bevfac2", 1)).floatValue(); - if (bevelStart > bevelEnd) { - float temp = bevelStart; - bevelStart = bevelEnd; - bevelEnd = temp; - } - - LOGGER.fine("Reading nurbs (and sorting them by material)."); - Map> nurbs = new HashMap>(); - List nurbStructures = ((Structure) curveStructure.getFieldValue("nurb")).evaluateListBase(); - for (Structure nurb : nurbStructures) { - Number matNumber = (Number) nurb.getFieldValue("mat_nr"); - List nurbList = nurbs.get(matNumber); - if (nurbList == null) { - nurbList = new ArrayList(); - nurbs.put(matNumber, nurbList); - } - nurbList.add(nurb); - } - - LOGGER.fine("Getting materials."); - MaterialHelper materialHelper = blenderContext.getHelper(MaterialHelper.class); - materials = materialHelper.getMaterials(curveStructure, blenderContext); - if (materials != null) { - for (MaterialContext materialContext : materials) { - materialContext.setFaceCullMode(FaceCullMode.Off); - } - } - - LOGGER.fine("Getting or creating bevel object."); - bevelObject = loadBevelAndTaper ? this.loadBevelObject(curveStructure) : null; - - LOGGER.fine("Getting taper object."); - Pointer pTaperObject = (Pointer) curveStructure.getFieldValue("taperobj"); - if (bevelObject != null && pTaperObject.isNotNull()) { - Structure taperObjectStructure = pTaperObject.fetchData().get(0); - DynamicArray scaleArray = (DynamicArray) taperObjectStructure.getFieldValue("size"); - scale = blenderContext.getBlenderKey().isFixUpAxis() ? new Vector3f(scaleArray.get(0).floatValue(), scaleArray.get(1).floatValue(), scaleArray.get(2).floatValue()) : new Vector3f(scaleArray.get(0).floatValue(), scaleArray.get(2).floatValue(), scaleArray.get(1).floatValue()); - Pointer pTaperStructure = (Pointer) taperObjectStructure.getFieldValue("data"); - Structure taperStructure = pTaperStructure.fetchData().get(0); - taperObject = new CurvesTemporalMesh(taperStructure, blenderContext); - } - - LOGGER.fine("Creating the result curves."); - for (Entry> nurbEntry : nurbs.entrySet()) { - for (Structure nurb : nurbEntry.getValue()) { - int type = ((Number) nurb.getFieldValue("type")).intValue(); - if ((type & TYPE_BEZIER) != 0) { - this.loadBezierCurve(nurb, nurbEntry.getKey().intValue()); - } else if ((type & TYPE_NURBS) != 0) { - this.loadNurbSurface(nurb, nurbEntry.getKey().intValue()); - } else { - throw new BlenderFileException("Unknown curve type: " + type); - } - } - } - - if (bevelObject != null && beziers.size() > 0) { - this.append(this.applyBevelAndTaper(this, bevelObject, taperObject, blenderContext)); - } else { - for (BezierLine bezierLine : beziers) { - int originalVerticesAmount = vertices.size(); - vertices.add(bezierLine.vertices[0]); - Vector3f v = bezierLine.vertices[1].subtract(bezierLine.vertices[0]).normalizeLocal(); - float temp = v.x; - v.x = -v.y; - v.y = temp; - v.z = 0; - normals.add(v);// this will be smoothed in the next iteration - - for (int i = 1; i < bezierLine.vertices.length; ++i) { - vertices.add(bezierLine.vertices[i]); - edges.add(new Edge(originalVerticesAmount + i - 1, originalVerticesAmount + i, 0, false, this)); - - // generating normal for vertex at 'i' - v = bezierLine.vertices[i].subtract(bezierLine.vertices[i - 1]).normalizeLocal(); - temp = v.x; - v.x = -v.y; - v.y = temp; - v.z = 0; - - // make the previous normal smooth - normals.get(i - 1).addLocal(v).multLocal(0.5f).normalizeLocal(); - normals.add(v);// this will be smoothed in the next iteration - } - } - } - } - - /** - * The method computes the value of a point at the certain relational distance from its beginning. - * @param alongRatio - * the relative distance along the curve; should be a value between 0 and 1 inclusive; - * if the value exceeds the boundaries it is truncated to them - * @return computed value along the curve - */ - private Vector3f getValueAlongCurve(float alongRatio) { - alongRatio = FastMath.clamp(alongRatio, 0, 1); - Vector3f result = new Vector3f(); - float probeLength = this.getLength() * alongRatio, length = 0; - for (BezierLine bezier : beziers) { - float edgeLength = bezier.getLength(); - if (length + edgeLength >= probeLength) { - float ratioAlongEdge = (probeLength - length) / edgeLength; - return bezier.getValueAlongCurve(ratioAlongEdge); - } - length += edgeLength; - } - return result; - } - - /** - * @return the length of the curve - */ - private float getLength() { - float result = 0; - for (BezierLine bezier : beziers) { - result += bezier.getLength(); - } - return result; - } - - /** - * The methods loads the bezier curve from the given structure. - * @param nurbStructure - * the structure containing a single curve definition - * @param materialIndex - * the index of this segment's material - * @throws BlenderFileException - * an exception is thrown when problems with reading occur - */ - private void loadBezierCurve(Structure nurbStructure, int materialIndex) throws BlenderFileException { - Pointer pBezierTriple = (Pointer) nurbStructure.getFieldValue("bezt"); - if (pBezierTriple.isNotNull()) { - int resolution = ((Number) nurbStructure.getFieldValue("resolu")).intValue(); - boolean cyclic = (((Number) nurbStructure.getFieldValue("flagu")).intValue() & 0x01) != 0; - boolean smooth = (((Number) nurbStructure.getFieldValue("flag")).intValue() & FLAG_SMOOTH) != 0; - - // creating the curve object - BezierCurve bezierCurve = new BezierCurve(0, pBezierTriple.fetchData(), 3, blenderContext.getBlenderKey().isFixUpAxis()); - List controlPoints = bezierCurve.getControlPoints(); - - if (cyclic) { - // copy the first three points at the end - for (int i = 0; i < 3; ++i) { - controlPoints.add(controlPoints.get(i)); - } - } - // removing the first and last handles - controlPoints.remove(0); - controlPoints.remove(controlPoints.size() - 1); - - // creating curve - Curve curve = new Curve(new Spline(SplineType.Bezier, controlPoints, 0, false), resolution); - - FloatBuffer vertsBuffer = (FloatBuffer) curve.getBuffer(Type.Position).getData(); - beziers.add(new BezierLine(BufferUtils.getVector3Array(vertsBuffer), materialIndex, smooth, cyclic)); - } - } - - /** - * This method loads the NURBS curve or surface. - * @param nurb - * the NURBS data structure - * @throws BlenderFileException - * an exception is thrown when problems with reading occur - */ - @SuppressWarnings("unchecked") - private void loadNurbSurface(Structure nurb, int materialIndex) throws BlenderFileException { - // loading the knots - List[] knots = new List[2]; - Pointer[] pKnots = new Pointer[] { (Pointer) nurb.getFieldValue("knotsu"), (Pointer) nurb.getFieldValue("knotsv") }; - for (int i = 0; i < knots.length; ++i) { - if (pKnots[i].isNotNull()) { - FileBlockHeader fileBlockHeader = blenderContext.getFileBlock(pKnots[i].getOldMemoryAddress()); - BlenderInputStream blenderInputStream = blenderContext.getInputStream(); - blenderInputStream.setPosition(fileBlockHeader.getBlockPosition()); - int knotsAmount = fileBlockHeader.getCount() * fileBlockHeader.getSize() / 4; - knots[i] = new ArrayList(knotsAmount); - for (int j = 0; j < knotsAmount; ++j) { - knots[i].add(Float.valueOf(blenderInputStream.readFloat())); - } - } - } - - // loading the flags and orders (basis functions degrees) - int flag = ((Number) nurb.getFieldValue("flag")).intValue(); - boolean smooth = (flag & FLAG_SMOOTH) != 0; - int flagU = ((Number) nurb.getFieldValue("flagu")).intValue(); - int flagV = ((Number) nurb.getFieldValue("flagv")).intValue(); - int orderU = ((Number) nurb.getFieldValue("orderu")).intValue(); - int orderV = ((Number) nurb.getFieldValue("orderv")).intValue(); - - // loading control points and their weights - int pntsU = ((Number) nurb.getFieldValue("pntsu")).intValue(); - int pntsV = ((Number) nurb.getFieldValue("pntsv")).intValue(); - List bPoints = ((Pointer) nurb.getFieldValue("bp")).fetchData(); - List> controlPoints = new ArrayList>(pntsV); - for (int i = 0; i < pntsV; ++i) { - List uControlPoints = new ArrayList(pntsU); - for (int j = 0; j < pntsU; ++j) { - DynamicArray vec = (DynamicArray) bPoints.get(j + i * pntsU).getFieldValue("vec"); - if (blenderContext.getBlenderKey().isFixUpAxis()) { - uControlPoints.add(new Vector4f(vec.get(0).floatValue(), vec.get(2).floatValue(), -vec.get(1).floatValue(), vec.get(3).floatValue())); - } else { - uControlPoints.add(new Vector4f(vec.get(0).floatValue(), vec.get(1).floatValue(), vec.get(2).floatValue(), vec.get(3).floatValue())); - } - } - if ((flagU & 0x01) != 0) { - for (int k = 0; k < orderU - 1; ++k) { - uControlPoints.add(uControlPoints.get(k)); - } - } - controlPoints.add(uControlPoints); - } - if ((flagV & 0x01) != 0) { - for (int k = 0; k < orderV - 1; ++k) { - controlPoints.add(controlPoints.get(k)); - } - } - - int originalVerticesAmount = vertices.size(); - int resolu = ((Number) nurb.getFieldValue("resolu")).intValue(); - if (knots[1] == null) {// creating the NURB curve - Curve curve = new Curve(new Spline(controlPoints.get(0), knots[0]), resolu); - FloatBuffer vertsBuffer = (FloatBuffer) curve.getBuffer(Type.Position).getData(); - beziers.add(new BezierLine(BufferUtils.getVector3Array(vertsBuffer), materialIndex, smooth, false)); - } else {// creating the NURB surface - int resolv = ((Number) nurb.getFieldValue("resolv")).intValue(); - int uSegments = resolu * controlPoints.get(0).size() - 1; - int vSegments = resolv * controlPoints.size() - 1; - Surface nurbSurface = Surface.createNurbsSurface(controlPoints, knots, uSegments, vSegments, orderU, orderV, smooth); - - FloatBuffer vertsBuffer = (FloatBuffer) nurbSurface.getBuffer(Type.Position).getData(); - vertices.addAll(Arrays.asList(BufferUtils.getVector3Array(vertsBuffer))); - FloatBuffer normalsBuffer = (FloatBuffer) nurbSurface.getBuffer(Type.Normal).getData(); - normals.addAll(Arrays.asList(BufferUtils.getVector3Array(normalsBuffer))); - - IndexBuffer indexBuffer = nurbSurface.getIndexBuffer(); - for (int i = 0; i < indexBuffer.size(); i += 3) { - int index1 = indexBuffer.get(i) + originalVerticesAmount; - int index2 = indexBuffer.get(i + 1) + originalVerticesAmount; - int index3 = indexBuffer.get(i + 2) + originalVerticesAmount; - faces.add(new Face(new Integer[] { index1, index2, index3 }, smooth, materialIndex, null, null, this)); - } - } - } - - /** - * The method loads the bevel object that should be applied to curve. It can either be another curve or a generated one - * based on the bevel generating parameters in blender. - * @param curveStructure - * the structure with the curve's data (the curve being loaded, NOT the bevel curve) - * @return the curve's bevel object - * @throws BlenderFileException - * an exception is thrown when problems with reading occur - */ - @SuppressWarnings("unchecked") - private CurvesTemporalMesh loadBevelObject(Structure curveStructure) throws BlenderFileException { - CurvesTemporalMesh bevelObject = null; - Pointer pBevelObject = (Pointer) curveStructure.getFieldValue("bevobj"); - boolean cyclic = false; - if (pBevelObject.isNotNull()) { - Structure bevelObjectStructure = pBevelObject.fetchData().get(0); - DynamicArray scaleArray = (DynamicArray) bevelObjectStructure.getFieldValue("size"); - Vector3f scale = blenderContext.getBlenderKey().isFixUpAxis() ? new Vector3f(scaleArray.get(0).floatValue(), scaleArray.get(1).floatValue(), scaleArray.get(2).floatValue()) : new Vector3f(scaleArray.get(0).floatValue(), scaleArray.get(2).floatValue(), scaleArray.get(1).floatValue()); - Pointer pBevelStructure = (Pointer) bevelObjectStructure.getFieldValue("data"); - Structure bevelStructure = pBevelStructure.fetchData().get(0); - bevelObject = new CurvesTemporalMesh(bevelStructure, scale, false, blenderContext); - - // transforming the bezier lines from plane XZ to plane YZ - for (BezierLine bl : bevelObject.beziers) { - for (Vector3f v : bl.vertices) { - // casting the bezier curve orthogonally on the plane XZ (making Y = 0) and then moving the plane XZ to ZY in a way that: - // -Z => +Y and +X => +Z and +Y => +X (but because casting would make Y = 0, then we simply set X = 0) - v.y = -v.z; - v.z = v.x; - v.x = 0; - } - - // bevel curves should not have repeated the first vertex at the end when they are cyclic (this is handled differently) - if (bl.isCyclic()) { - bl.removeLastVertex(); - } - } - } else { - fillCaps = false;// this option is inactive in blender when there is no bevel object applied - int bevResol = ((Number) curveStructure.getFieldValue("bevresol")).intValue(); - float extrude = ((Number) curveStructure.getFieldValue("ext1")).floatValue(); - float bevelDepth = ((Number) curveStructure.getFieldValue("ext2")).floatValue(); - float offset = ((Number) curveStructure.getFieldValue("offset", 0)).floatValue(); - if (offset != 0) { - // TODO: add support for offset parameter - LOGGER.warning("Offset parameter not yet supported."); - } - Curve bevelCurve = null; - if (bevelDepth > 0.0f) { - float handlerLength = bevelDepth / 2.0f; - cyclic = !isFront && !isBack; - List conrtolPoints = new ArrayList(); - - // blenders from 2.49 to 2.52 did not pay attention to fron and back faces - // so in order to draw the scene exactly as it is in different blender versions the blender version is checked here - // when neither fron and back face is selected all version behave the same and draw full bevel around the curve - if (cyclic || blenderContext.getBlenderVersion() < 253) { - conrtolPoints.add(new Vector3f(0, -extrude - bevelDepth, 0)); - conrtolPoints.add(new Vector3f(0, -extrude - bevelDepth, -handlerLength)); - - conrtolPoints.add(new Vector3f(0, -extrude - handlerLength, -bevelDepth)); - conrtolPoints.add(new Vector3f(0, -extrude, -bevelDepth)); - conrtolPoints.add(new Vector3f(0, -extrude + handlerLength, -bevelDepth)); - - if (extrude > 0) { - conrtolPoints.add(new Vector3f(0, extrude - handlerLength, -bevelDepth)); - conrtolPoints.add(new Vector3f(0, extrude, -bevelDepth)); - conrtolPoints.add(new Vector3f(0, extrude + handlerLength, -bevelDepth)); - } - - conrtolPoints.add(new Vector3f(0, extrude + bevelDepth, -handlerLength)); - conrtolPoints.add(new Vector3f(0, extrude + bevelDepth, 0)); - - if (cyclic) { - conrtolPoints.add(new Vector3f(0, extrude + bevelDepth, handlerLength)); - - conrtolPoints.add(new Vector3f(0, extrude + handlerLength, bevelDepth)); - conrtolPoints.add(new Vector3f(0, extrude, bevelDepth)); - conrtolPoints.add(new Vector3f(0, extrude - handlerLength, bevelDepth)); - - if (extrude > 0) { - conrtolPoints.add(new Vector3f(0, -extrude + handlerLength, bevelDepth)); - conrtolPoints.add(new Vector3f(0, -extrude, bevelDepth)); - conrtolPoints.add(new Vector3f(0, -extrude - handlerLength, bevelDepth)); - } - - conrtolPoints.add(new Vector3f(0, -extrude - bevelDepth, handlerLength)); - conrtolPoints.add(new Vector3f(0, -extrude - bevelDepth, 0)); - } - } else { - if (extrude > 0) { - if (isBack) { - conrtolPoints.add(new Vector3f(0, -extrude - bevelDepth, 0)); - conrtolPoints.add(new Vector3f(0, -extrude - bevelDepth, -handlerLength)); - - conrtolPoints.add(new Vector3f(0, -extrude - handlerLength, -bevelDepth)); - } - - conrtolPoints.add(new Vector3f(0, -extrude, -bevelDepth)); - conrtolPoints.add(new Vector3f(0, -extrude + handlerLength, -bevelDepth)); - conrtolPoints.add(new Vector3f(0, extrude - handlerLength, -bevelDepth)); - conrtolPoints.add(new Vector3f(0, extrude, -bevelDepth)); - - if (isFront) { - conrtolPoints.add(new Vector3f(0, extrude + handlerLength, -bevelDepth)); - - conrtolPoints.add(new Vector3f(0, extrude + bevelDepth, -handlerLength)); - conrtolPoints.add(new Vector3f(0, extrude + bevelDepth, 0)); - } - } else { - if (isFront && isBack) { - conrtolPoints.add(new Vector3f(0, -bevelDepth, 0)); - conrtolPoints.add(new Vector3f(0, -bevelDepth, -handlerLength)); - - conrtolPoints.add(new Vector3f(0, -handlerLength, -bevelDepth)); - conrtolPoints.add(new Vector3f(0, 0, -bevelDepth)); - conrtolPoints.add(new Vector3f(0, handlerLength, -bevelDepth)); - - conrtolPoints.add(new Vector3f(0, bevelDepth, -handlerLength)); - conrtolPoints.add(new Vector3f(0, bevelDepth, 0)); - } else { - if (isBack) { - conrtolPoints.add(new Vector3f(0, -bevelDepth, 0)); - conrtolPoints.add(new Vector3f(0, -bevelDepth, -handlerLength)); - - conrtolPoints.add(new Vector3f(0, -handlerLength, -bevelDepth)); - conrtolPoints.add(new Vector3f(0, 0, -bevelDepth)); - } else { - conrtolPoints.add(new Vector3f(0, 0, -bevelDepth)); - conrtolPoints.add(new Vector3f(0, handlerLength, -bevelDepth)); - - conrtolPoints.add(new Vector3f(0, bevelDepth, -handlerLength)); - conrtolPoints.add(new Vector3f(0, bevelDepth, 0)); - } - } - } - } - - bevelCurve = new Curve(new Spline(SplineType.Bezier, conrtolPoints, 0, false), bevResol); - } else if (extrude > 0.0f) { - Spline bevelSpline = new Spline(SplineType.Linear, new Vector3f[] { new Vector3f(0, extrude, 0), new Vector3f(0, -extrude, 0) }, 1, false); - bevelCurve = new Curve(bevelSpline, bevResol); - } - if (bevelCurve != null) { - bevelObject = new CurvesTemporalMesh(blenderContext); - FloatBuffer vertsBuffer = (FloatBuffer) bevelCurve.getBuffer(Type.Position).getData(); - Vector3f[] verts = BufferUtils.getVector3Array(vertsBuffer); - if (cyclic) {// get rid of the last vertex which is identical to the first one - verts = Arrays.copyOf(verts, verts.length - 1); - } - bevelObject.beziers.add(new BezierLine(verts, 0, false, cyclic)); - } - } - return bevelObject; - } - - private List getScaledBeziers() { - if (scale.equals(Vector3f.UNIT_XYZ)) { - return beziers; - } - List result = new ArrayList(); - for (BezierLine bezierLine : beziers) { - result.add(bezierLine.scale(scale)); - } - return result; - } - - /** - * This method applies bevel and taper objects to the curve. - * @param curve - * the curve we apply the objects to - * @param bevelObject - * the bevel object - * @param taperObject - * the taper object - * @param blenderContext - * the blender context - * @return a list of geometries representing the beveled and/or tapered curve - * @throws BlenderFileException - * an exception is thrown when problems with reading occur - */ - private CurvesTemporalMesh applyBevelAndTaper(CurvesTemporalMesh curve, CurvesTemporalMesh bevelObject, CurvesTemporalMesh taperObject, BlenderContext blenderContext) throws BlenderFileException { - List bevelBezierLines = bevelObject.getScaledBeziers(); - List curveLines = curve.beziers; - if (bevelBezierLines.size() == 0 || curveLines.size() == 0) { - return null; - } - - CurvesTemporalMesh result = new CurvesTemporalMesh(blenderContext); - for (BezierLine curveLine : curveLines) { - Vector3f[] curveLineVertices = curveLine.getVertices(bevelStart, bevelEnd); - - for (BezierLine bevelBezierLine : bevelBezierLines) { - CurvesTemporalMesh partResult = new CurvesTemporalMesh(blenderContext); - - Vector3f[] bevelLineVertices = bevelBezierLine.getVertices(); - List bevels = new ArrayList(); - - Vector3f[] bevelPoints = curvesHelper.transformToFirstLineOfBevelPoints(bevelLineVertices, curveLineVertices[0], curveLineVertices[1]); - bevels.add(bevelPoints); - for (int i = 1; i < curveLineVertices.length - 1; ++i) { - bevelPoints = curvesHelper.transformBevel(bevelPoints, curveLineVertices[i - 1], curveLineVertices[i], curveLineVertices[i + 1]); - bevels.add(bevelPoints); - } - bevelPoints = curvesHelper.transformBevel(bevelPoints, curveLineVertices[curveLineVertices.length - 2], curveLineVertices[curveLineVertices.length - 1], null); - bevels.add(bevelPoints); - - Vector3f subtractResult = new Vector3f(); - if (bevels.size() > 2) { - // changing the first and last bevel so that they are parallel to their neighbours (blender works this way) - // notice this implicates that the distances of every corresponding point in the two bevels must be identical and - // equal to the distance between the points on curve that define the bevel position - // so instead doing complicated rotations on each point we will simply properly translate each of them - int[][] pointIndexes = new int[][] { { 0, 1 }, { curveLineVertices.length - 1, curveLineVertices.length - 2 } }; - for (int[] indexes : pointIndexes) { - float distance = curveLineVertices[indexes[1]].subtract(curveLineVertices[indexes[0]], subtractResult).length(); - Vector3f[] bevel = bevels.get(indexes[0]); - Vector3f[] nextBevel = bevels.get(indexes[1]); - for (int i = 0; i < bevel.length; ++i) { - float d = bevel[i].subtract(nextBevel[i], subtractResult).length(); - subtractResult.normalizeLocal().multLocal(distance - d); - bevel[i].addLocal(subtractResult); - } - } - } - - if (taperObject != null) { - float curveLength = curveLine.getLength(), lengthAlongCurve = bevelStart; - for (int i = 0; i < curveLineVertices.length; ++i) { - if (i > 0) { - lengthAlongCurve += curveLineVertices[i].subtract(curveLineVertices[i - 1], subtractResult).length(); - } - float taperScale = -taperObject.getValueAlongCurve(lengthAlongCurve / curveLength).z * taperObject.scale.z; - if (taperScale != 1) { - this.applyScale(bevels.get(i), curveLineVertices[i], taperScale); - } - } - } - - // adding vertices to the part result - for (Vector3f[] bevel : bevels) { - for (Vector3f d : bevel) { - partResult.getVertices().add(d); - } - } - - // preparing faces for the part result (each face is a quad) - int bevelVertCount = bevelPoints.length; - for (int i = 0; i < bevels.size() - 1; ++i) { - for (int j = 0; j < bevelVertCount - 1; ++j) { - Integer[] indexes = new Integer[] { i * bevelVertCount + j + 1, (i + 1) * bevelVertCount + j + 1, (i + 1) * bevelVertCount + j, i * bevelVertCount + j }; - partResult.getFaces().add(new Face(indexes, curveLine.isSmooth(), curveLine.getMaterialNumber(), null, null, partResult)); - partResult.getEdges().add(new Edge(indexes[0], indexes[1], 0, true, partResult)); - partResult.getEdges().add(new Edge(indexes[1], indexes[2], 0, true, partResult)); - partResult.getEdges().add(new Edge(indexes[2], indexes[3], 0, true, partResult)); - partResult.getEdges().add(new Edge(indexes[3], indexes[0], 0, true, partResult)); - } - if (bevelBezierLine.isCyclic()) { - int j = bevelVertCount - 1; - Integer[] indexes = new Integer[] { i * bevelVertCount, (i + 1) * bevelVertCount, (i + 1) * bevelVertCount + j, i * bevelVertCount + j }; - partResult.getFaces().add(new Face(indexes, curveLine.isSmooth(), curveLine.getMaterialNumber(), null, null, partResult)); - partResult.getEdges().add(new Edge(indexes[0], indexes[1], 0, true, partResult)); - partResult.getEdges().add(new Edge(indexes[1], indexes[2], 0, true, partResult)); - partResult.getEdges().add(new Edge(indexes[2], indexes[3], 0, true, partResult)); - partResult.getEdges().add(new Edge(indexes[3], indexes[0], 0, true, partResult)); - } - } - - partResult.generateNormals(); - - if (fillCaps) {// caps in blender behave as if they weren't affected by the smooth factor - // START CAP - Vector3f[] cap = bevels.get(0); - List capIndexes = new ArrayList(cap.length); - Vector3f capNormal = curveLineVertices[0].subtract(curveLineVertices[1]).normalizeLocal(); - for (int i = 0; i < cap.length; ++i) { - capIndexes.add(partResult.getVertices().size()); - partResult.getVertices().add(cap[i]); - partResult.getNormals().add(capNormal); - } - Collections.reverse(capIndexes);// the indexes ned to be reversed for the face to have fron face outside the beveled line - partResult.getFaces().add(new Face(capIndexes.toArray(new Integer[capIndexes.size()]), false, curveLine.getMaterialNumber(), null, null, partResult)); - for (int i = 1; i < capIndexes.size(); ++i) { - partResult.getEdges().add(new Edge(capIndexes.get(i - 1), capIndexes.get(i), 0, true, partResult)); - } - - // END CAP - cap = bevels.get(bevels.size() - 1); - capIndexes.clear(); - capNormal = curveLineVertices[curveLineVertices.length - 1].subtract(curveLineVertices[curveLineVertices.length - 2]).normalizeLocal(); - for (int i = 0; i < cap.length; ++i) { - capIndexes.add(partResult.getVertices().size()); - partResult.getVertices().add(cap[i]); - partResult.getNormals().add(capNormal); - } - partResult.getFaces().add(new Face(capIndexes.toArray(new Integer[capIndexes.size()]), false, curveLine.getMaterialNumber(), null, null, partResult)); - for (int i = 1; i < capIndexes.size(); ++i) { - partResult.getEdges().add(new Edge(capIndexes.get(i - 1), capIndexes.get(i), 0, true, partResult)); - } - } - - result.append(partResult); - } - } - - return result; - } - - /** - * The method generates normals for the curve. If any normals were already stored they are discarded. - */ - private void generateNormals() { - Map normalMap = new TreeMap(); - for (Face face : faces) { - // the first 3 verts are enough here (all faces are triangles except for the caps, but those are fully flat anyway) - int index1 = face.getIndexes().get(0); - int index2 = face.getIndexes().get(1); - int index3 = face.getIndexes().get(2); - - Vector3f n = FastMath.computeNormal(vertices.get(index1), vertices.get(index2), vertices.get(index3)); - for (int index : face.getIndexes()) { - Vector3f normal = normalMap.get(index); - if (normal == null) { - normalMap.put(index, n.clone()); - } else { - normal.addLocal(n).normalizeLocal(); - } - } - } - - normals.clear(); - Collections.addAll(normals, new Vector3f[normalMap.size()]); - for (Entry entry : normalMap.entrySet()) { - normals.set(entry.getKey(), entry.getValue()); - } - } - - /** - * the method applies scale for the given bevel points. The points table is - * being modified so expect your result there. - * - * @param points - * the bevel points - * @param centerPoint - * the center point of the bevel - * @param scale - * the scale to be applied - */ - private void applyScale(Vector3f[] points, Vector3f centerPoint, float scale) { - Vector3f taperScaleVector = new Vector3f(); - for (Vector3f p : points) { - taperScaleVector.set(centerPoint).subtractLocal(p).multLocal(1 - scale); - p.addLocal(taperScaleVector); - } - } - - /** - * A helper class that represents a single bezier line. It consists of Edge's and allows to - * get a subline of a length of the line. - * - * @author Marcin Roguski (Kaelthas) - */ - public static class BezierLine { - /** The edges of the bezier line. */ - private Vector3f[] vertices; - /** The material number of the line. */ - private int materialNumber; - /** Indicates if the line is smooth of flat. */ - private boolean smooth; - /** The length of the line. */ - private float length; - /** Indicates if the current line is cyclic or not. */ - private boolean cyclic; - - public BezierLine(Vector3f[] vertices, int materialNumber, boolean smooth, boolean cyclik) { - this.vertices = vertices; - this.materialNumber = materialNumber; - this.smooth = smooth; - cyclic = cyclik; - this.recomputeLength(); - } - - public BezierLine scale(Vector3f scale) { - BezierLine result = new BezierLine(vertices, materialNumber, smooth, cyclic); - result.vertices = new Vector3f[vertices.length]; - for (int i = 0; i < vertices.length; ++i) { - result.vertices[i] = vertices[i].mult(scale); - } - result.recomputeLength(); - return result; - } - - public void removeLastVertex() { - Vector3f[] newVertices = new Vector3f[vertices.length - 1]; - for (int i = 0; i < vertices.length - 1; ++i) { - newVertices[i] = vertices[i]; - } - vertices = newVertices; - this.recomputeLength(); - } - - private void recomputeLength() { - length = 0; - for (int i = 1; i < vertices.length; ++i) { - length += vertices[i - 1].distance(vertices[i]); - } - if (cyclic) { - // if the first vertex is repeated at the end the distance will be = 0 so it won't affect the result, and if it is not repeated - // then it is necessary to add the length between the last and the first vertex - length += vertices[vertices.length - 1].distance(vertices[0]); - } - } - - public Vector3f[] getVertices() { - return this.getVertices(0, 1); - } - - public Vector3f[] getVertices(float startSlice, float endSlice) { - if (startSlice == 0 && endSlice == 1) { - return vertices; - } - List result = new ArrayList(); - float length = this.getLength(), temp = 0; - float startSliceLength = length * startSlice; - float endSliceLength = length * endSlice; - int index = 1; - - if (startSlice > 0) { - while (temp < startSliceLength) { - Vector3f v1 = vertices[index - 1]; - Vector3f v2 = vertices[index++]; - float edgeLength = v1.distance(v2); - temp += edgeLength; - if (temp == startSliceLength) { - result.add(v2); - } else if (temp > startSliceLength) { - result.add(v1.subtract(v2).normalizeLocal().multLocal(temp - startSliceLength).addLocal(v2)); - } - } - } - - if (endSlice < 1) { - if (index == vertices.length) { - Vector3f v1 = vertices[vertices.length - 2]; - Vector3f v2 = vertices[vertices.length - 1]; - result.add(v1.subtract(v2).normalizeLocal().multLocal(length - endSliceLength).addLocal(v2)); - } else { - for (int i = index; i < vertices.length && temp < endSliceLength; ++i) { - Vector3f v1 = vertices[index - 1]; - Vector3f v2 = vertices[index++]; - temp += v1.distance(v2); - if (temp == endSliceLength) { - result.add(v2); - } else if (temp > endSliceLength) { - result.add(v1.subtract(v2).normalizeLocal().multLocal(temp - startSliceLength).addLocal(v2)); - } - } - } - } else { - result.addAll(Arrays.asList(Arrays.copyOfRange(vertices, index, vertices.length))); - } - - return result.toArray(new Vector3f[result.size()]); - } - - /** - * The method computes the value of a point at the certain relational distance from its beginning. - * @param alongRatio - * the relative distance along the curve; should be a value between 0 and 1 inclusive; - * if the value exceeds the boundaries it is truncated to them - * @return computed value along the curve - */ - public Vector3f getValueAlongCurve(float alongRatio) { - alongRatio = FastMath.clamp(alongRatio, 0, 1); - Vector3f result = new Vector3f(); - float probeLength = this.getLength() * alongRatio; - float length = 0; - for (int i = 1; i < vertices.length; ++i) { - float edgeLength = vertices[i].distance(vertices[i - 1]); - if (length + edgeLength > probeLength) { - float ratioAlongEdge = (probeLength - length) / edgeLength; - return FastMath.interpolateLinear(ratioAlongEdge, vertices[i - 1], vertices[i]); - } else if (length + edgeLength == probeLength) { - return vertices[i]; - } - length += edgeLength; - } - - return result; - } - - /** - * @return the material number of this bezier line - */ - public int getMaterialNumber() { - return materialNumber; - } - - /** - * @return indicates if the line is smooth of flat - */ - public boolean isSmooth() { - return smooth; - } - - /** - * @return the length of this bezier line - */ - public float getLength() { - return length; - } - - /** - * @return indicates if the current line is cyclic or not - */ - public boolean isCyclic() { - return cyclic; - } - } -} diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/file/BlenderFileException.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/file/BlenderFileException.java deleted file mode 100644 index 9deeb8fa7..000000000 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/file/BlenderFileException.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (c) 2009-2019 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.blender.file; - -/** - * This exception is thrown when blend file data is somehow invalid. - * @author Marcin Roguski - */ -public class BlenderFileException extends Exception { - - private static final long serialVersionUID = 7573482836437866767L; - - /** - * Constructor. Creates an exception with no description. - */ - public BlenderFileException() { - // this constructor has no message - } - - /** - * Constructor. Creates an exception containing the given message. - * @param message - * the message describing the problem that occurred - */ - public BlenderFileException(String message) { - super(message); - } - - /** - * Constructor. Creates an exception that is based upon other thrown object. It contains the whole stacktrace then. - * @param throwable - * an exception/error that occurred - */ - public BlenderFileException(Throwable throwable) { - super(throwable); - } - - /** - * Constructor. Creates an exception with both a message and stacktrace. - * @param message - * the message describing the problem that occurred - * @param throwable - * an exception/error that occurred - */ - public BlenderFileException(String message, Throwable throwable) { - super(message, throwable); - } -} diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/file/BlenderInputStream.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/file/BlenderInputStream.java deleted file mode 100644 index 01d103753..000000000 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/file/BlenderInputStream.java +++ /dev/null @@ -1,371 +0,0 @@ -/* - * Copyright (c) 2009-2019 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.blender.file; - -import java.io.BufferedInputStream; -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.util.logging.Logger; -import java.util.zip.GZIPInputStream; - -/** - * An input stream with random access to data. - * @author Marcin Roguski - */ -public class BlenderInputStream extends InputStream { - - private static final Logger LOGGER = Logger.getLogger(BlenderInputStream.class.getName()); - /** The default size of the blender buffer. */ - private static final int DEFAULT_BUFFER_SIZE = 1048576; // 1MB - /** - * Size of a pointer; all pointers in the file are stored in this format. '_' means 4 bytes and '-' means 8 bytes. - */ - private int pointerSize; - /** - * Type of byte ordering used; 'v' means little endian and 'V' means big endian. - */ - private char endianess; - /** Version of Blender the file was created in; '248' means version 2.48. */ - private String versionNumber; - /** The buffer we store the read data to. */ - protected byte[] cachedBuffer; - /** The total size of the stored data. */ - protected int size; - /** The current position of the read cursor. */ - protected int position; - - /** - * Constructor. The input stream is stored and used to read data. - * @param inputStream - * the stream we read data from - * @throws BlenderFileException - * this exception is thrown if the file header has some invalid data - */ - public BlenderInputStream(InputStream inputStream) throws BlenderFileException { - // the size value will canche while reading the file; the available() method cannot be counted on - try { - size = inputStream.available(); - } catch (IOException e) { - size = 0; - } - if (size <= 0) { - size = BlenderInputStream.DEFAULT_BUFFER_SIZE; - } - - // buffered input stream is used here for much faster file reading - BufferedInputStream bufferedInputStream; - if (inputStream instanceof BufferedInputStream) { - bufferedInputStream = (BufferedInputStream) inputStream; - } else { - bufferedInputStream = new BufferedInputStream(inputStream); - } - - try { - this.readStreamToCache(bufferedInputStream); - } catch (IOException e) { - throw new BlenderFileException("Problems occurred while caching the file!", e); - } finally { - try { - inputStream.close(); - } catch (IOException e) { - LOGGER.warning("Unable to close stream with blender file."); - } - } - - try { - this.readFileHeader(); - } catch (BlenderFileException e) {// the file might be packed, don't panic, try one more time ;) - this.decompressFile(); - position = 0; - this.readFileHeader(); - } - } - - /** - * This method reads the whole stream into a buffer. - * @param inputStream - * the stream to read the file data from - * @throws IOException - * an exception is thrown when data read from the stream is invalid or there are problems with i/o - * operations - */ - private void readStreamToCache(InputStream inputStream) throws IOException { - int data = inputStream.read(); - cachedBuffer = new byte[size]; - size = 0;// this will count the actual size - while (data != -1) { - if (size >= cachedBuffer.length) {// widen the cached array - byte[] newBuffer = new byte[cachedBuffer.length + (cachedBuffer.length >> 1)]; - System.arraycopy(cachedBuffer, 0, newBuffer, 0, cachedBuffer.length); - cachedBuffer = newBuffer; - } - cachedBuffer[size++] = (byte) data; - data = inputStream.read(); - } - } - - /** - * This method is used when the blender file is gzipped. It decompresses the data and stores it back into the - * cachedBuffer field. - */ - private void decompressFile() { - GZIPInputStream gis = null; - try { - gis = new GZIPInputStream(new ByteArrayInputStream(cachedBuffer)); - this.readStreamToCache(gis); - } catch (IOException e) { - throw new IllegalStateException("IO errors occurred where they should NOT! " + "The data is already buffered at this point!", e); - } finally { - try { - if (gis != null) { - gis.close(); - } - } catch (IOException e) { - LOGGER.warning(e.getMessage()); - } - } - } - - /** - * This method loads the header from the given stream during instance creation. - * @param inputStream - * the stream we read the header from - * @throws BlenderFileException - * this exception is thrown if the file header has some invalid data - */ - private void readFileHeader() throws BlenderFileException { - byte[] identifier = new byte[7]; - int bytesRead = this.readBytes(identifier); - if (bytesRead != 7) { - throw new BlenderFileException("Error reading header identifier. Only " + bytesRead + " bytes read and there should be 7!"); - } - String strIdentifier = new String(identifier); - if (!"BLENDER".equals(strIdentifier)) { - throw new BlenderFileException("Wrong file identifier: " + strIdentifier + "! Should be 'BLENDER'!"); - } - char pointerSizeSign = (char) this.readByte(); - if (pointerSizeSign == '-') { - pointerSize = 8; - } else if (pointerSizeSign == '_') { - pointerSize = 4; - } else { - throw new BlenderFileException("Invalid pointer size character! Should be '_' or '-' and there is: " + pointerSizeSign); - } - endianess = (char) this.readByte(); - if (endianess != 'v' && endianess != 'V') { - throw new BlenderFileException("Unknown endianess value! 'v' or 'V' expected and found: " + endianess); - } - byte[] versionNumber = new byte[3]; - bytesRead = this.readBytes(versionNumber); - if (bytesRead != 3) { - throw new BlenderFileException("Error reading version numberr. Only " + bytesRead + " bytes read and there should be 3!"); - } - this.versionNumber = new String(versionNumber); - } - - @Override - public int read() throws IOException { - return this.readByte(); - } - - /** - * This method reads 1 byte from the stream. - * It works just in the way the read method does. - * It just not throw an exception because at this moment the whole file - * is loaded into buffer, so no need for IOException to be thrown. - * @return a byte from the stream (1 bytes read) - */ - public int readByte() { - return cachedBuffer[position++] & 0xFF; - } - - /** - * This method reads a bytes number big enough to fill the table. - * It does not throw exceptions so it is for internal use only. - * @param bytes - * an array to be filled with data - * @return number of read bytes (a length of array actually) - */ - private int readBytes(byte[] bytes) { - for (int i = 0; i < bytes.length; ++i) { - bytes[i] = (byte) this.readByte(); - } - return bytes.length; - } - - /** - * This method reads 2-byte number from the stream. - * @return a number from the stream (2 bytes read) - */ - public int readShort() { - int part1 = this.readByte(); - int part2 = this.readByte(); - if (endianess == 'v') { - return (part2 << 8) + part1; - } else { - return (part1 << 8) + part2; - } - } - - /** - * This method reads 4-byte number from the stream. - * @return a number from the stream (4 bytes read) - */ - public int readInt() { - int part1 = this.readByte(); - int part2 = this.readByte(); - int part3 = this.readByte(); - int part4 = this.readByte(); - if (endianess == 'v') { - return (part4 << 24) + (part3 << 16) + (part2 << 8) + part1; - } else { - return (part1 << 24) + (part2 << 16) + (part3 << 8) + part4; - } - } - - /** - * This method reads 4-byte floating point number (float) from the stream. - * @return a number from the stream (4 bytes read) - */ - public float readFloat() { - int intValue = this.readInt(); - return Float.intBitsToFloat(intValue); - } - - /** - * This method reads 8-byte number from the stream. - * @return a number from the stream (8 bytes read) - */ - public long readLong() { - long part1 = this.readInt(); - long part2 = this.readInt(); - long result = -1; - if (endianess == 'v') { - result = part2 << 32 | part1; - } else { - result = part1 << 32 | part2; - } - return result; - } - - /** - * This method reads 8-byte floating point number (double) from the stream. - * @return a number from the stream (8 bytes read) - */ - public double readDouble() { - long longValue = this.readLong(); - return Double.longBitsToDouble(longValue); - } - - /** - * This method reads the pointer value. Depending on the pointer size defined in the header, the stream reads either - * 4 or 8 bytes of data. - * @return the pointer value - */ - public long readPointer() { - if (pointerSize == 4) { - return this.readInt(); - } - return this.readLong(); - } - - /** - * This method reads the string. It assumes the string is terminated with zero in the stream. - * @return the string read from the stream - */ - public String readString() { - StringBuilder stringBuilder = new StringBuilder(); - int data = this.readByte(); - while (data != 0) { - stringBuilder.append((char) data); - data = this.readByte(); - } - return stringBuilder.toString(); - } - - /** - * This method sets the current position of the read cursor. - * @param position - * the position of the read cursor - */ - public void setPosition(int position) { - this.position = position; - } - - /** - * This method returns the position of the read cursor. - * @return the position of the read cursor - */ - public int getPosition() { - return position; - } - - /** - * This method returns the blender version number where the file was created. - * @return blender version number - */ - public String getVersionNumber() { - return versionNumber; - } - - /** - * This method returns the size of the pointer. - * @return the size of the pointer - */ - public int getPointerSize() { - return pointerSize; - } - - /** - * This method aligns cursor position forward to a given amount of bytes. - * @param bytesAmount - * the byte amount to which we aligh the cursor - */ - public void alignPosition(int bytesAmount) { - if (bytesAmount <= 0) { - throw new IllegalArgumentException("Alignment byte number shoulf be positivbe!"); - } - long move = position % bytesAmount; - if (move > 0) { - position += bytesAmount - move; - } - } - - @Override - public void close() throws IOException { - // this method is unimplemented because some loaders (ie. TGALoader) tend close the stream given from the outside - // because the images can be stored directly in the blender file then this stream is properly positioned and given to the loader - // to read the image file, that is why we do not want it to be closed before the reading is done - // and anyway this stream is only a cached buffer, so it does not hold any open connection to anything - } -} diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/file/DnaBlockData.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/file/DnaBlockData.java deleted file mode 100644 index 9fae86160..000000000 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/file/DnaBlockData.java +++ /dev/null @@ -1,203 +0,0 @@ -/* - * Copyright (c) 2009-2018 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.blender.file; - -import com.jme3.scene.plugins.blender.BlenderContext; - -import java.util.HashMap; -import java.util.Map; - -/** - * The data block containing the description of the file. - * @author Marcin Roguski (Kaelthas) - */ -public class DnaBlockData { - - private static final int SDNA_ID = 'S' << 24 | 'D' << 16 | 'N' << 8 | 'A'; // SDNA - private static final int NAME_ID = 'N' << 24 | 'A' << 16 | 'M' << 8 | 'E'; // NAME - private static final int TYPE_ID = 'T' << 24 | 'Y' << 16 | 'P' << 8 | 'E'; // TYPE - private static final int TLEN_ID = 'T' << 24 | 'L' << 16 | 'E' << 8 | 'N'; // TLEN - private static final int STRC_ID = 'S' << 24 | 'T' << 16 | 'R' << 8 | 'C'; // STRC - /** Structures available inside the file. */ - private final Structure[] structures; - /** A map that helps finding a structure by type. */ - private final Map structuresMap; - - /** - * Constructor. Loads the block from the given stream during instance creation. - * @param inputStream - * the stream we read the block from - * @param blenderContext - * the blender context - * @throws BlenderFileException - * this exception is throw if the blend file is invalid or somehow corrupted - */ - public DnaBlockData(BlenderInputStream inputStream, BlenderContext blenderContext) throws BlenderFileException { - int identifier; - - // reading 'SDNA' identifier - identifier = inputStream.readByte() << 24 | inputStream.readByte() << 16 | inputStream.readByte() << 8 | inputStream.readByte(); - - if (identifier != SDNA_ID) { - throw new BlenderFileException("Invalid identifier! '" + this.toString(SDNA_ID) + "' expected and found: " + this.toString(identifier)); - } - - // reading names - identifier = inputStream.readByte() << 24 | inputStream.readByte() << 16 | inputStream.readByte() << 8 | inputStream.readByte(); - if (identifier != NAME_ID) { - throw new BlenderFileException("Invalid identifier! '" + this.toString(NAME_ID) + "' expected and found: " + this.toString(identifier)); - } - int amount = inputStream.readInt(); - if (amount <= 0) { - throw new BlenderFileException("The names amount number should be positive!"); - } - String[] names = new String[amount]; - for (int i = 0; i < amount; ++i) { - names[i] = inputStream.readString(); - } - - // reading types - inputStream.alignPosition(4); - identifier = inputStream.readByte() << 24 | inputStream.readByte() << 16 | inputStream.readByte() << 8 | inputStream.readByte(); - if (identifier != TYPE_ID) { - throw new BlenderFileException("Invalid identifier! '" + this.toString(TYPE_ID) + "' expected and found: " + this.toString(identifier)); - } - amount = inputStream.readInt(); - if (amount <= 0) { - throw new BlenderFileException("The types amount number should be positive!"); - } - String[] types = new String[amount]; - for (int i = 0; i < amount; ++i) { - types[i] = inputStream.readString(); - } - - // reading lengths - inputStream.alignPosition(4); - identifier = inputStream.readByte() << 24 | inputStream.readByte() << 16 | inputStream.readByte() << 8 | inputStream.readByte(); - if (identifier != TLEN_ID) { - throw new BlenderFileException("Invalid identifier! '" + this.toString(TLEN_ID) + "' expected and found: " + this.toString(identifier)); - } - int[] lengths = new int[amount];// theamount is the same as int types - for (int i = 0; i < amount; ++i) { - lengths[i] = inputStream.readShort(); - } - - // reading structures - inputStream.alignPosition(4); - identifier = inputStream.readByte() << 24 | inputStream.readByte() << 16 | inputStream.readByte() << 8 | inputStream.readByte(); - if (identifier != STRC_ID) { - throw new BlenderFileException("Invalid identifier! '" + this.toString(STRC_ID) + "' expected and found: " + this.toString(identifier)); - } - amount = inputStream.readInt(); - if (amount <= 0) { - throw new BlenderFileException("The structures amount number should be positive!"); - } - structures = new Structure[amount]; - structuresMap = new HashMap(amount); - for (int i = 0; i < amount; ++i) { - structures[i] = new Structure(inputStream, names, types, blenderContext); - if (structuresMap.containsKey(structures[i].getType())) { - throw new BlenderFileException("Blend file seems to be corrupted! The type " + structures[i].getType() + " is defined twice!"); - } - structuresMap.put(structures[i].getType(), structures[i]); - } - } - - /** - * This method returns the amount of the structures. - * @return the amount of the structures - */ - public int getStructuresCount() { - return structures.length; - } - - /** - * This method returns the structure of the given index. - * @param index - * the index of the structure - * @return the structure of the given index - */ - public Structure getStructure(int index) { - try { - return (Structure) structures[index].clone(); - } catch (CloneNotSupportedException e) { - throw new IllegalStateException("Structure should be clonable!!!", e); - } - } - - /** - * This method returns a structure of the given name. If the name does not exists then null is returned. - * @param name - * the name of the structure - * @return the required structure or null if the given name is inapropriate - */ - public Structure getStructure(String name) { - try { - return (Structure) structuresMap.get(name).clone(); - } catch (CloneNotSupportedException e) { - throw new IllegalStateException(e.getMessage(), e); - } - } - - /** - * This method indicates if the structure of the given name exists. - * @param name - * the name of the structure - * @return true if the structure exists and false otherwise - */ - public boolean hasStructure(String name) { - return structuresMap.containsKey(name); - } - - /** - * This method converts the given identifier code to string. - * @param code - * the code that is to be converted - * @return the string value of the identifier - */ - private String toString(int code) { - char c1 = (char) ((code & 0xFF000000) >> 24); - char c2 = (char) ((code & 0xFF0000) >> 16); - char c3 = (char) ((code & 0xFF00) >> 8); - char c4 = (char) (code & 0xFF); - return String.valueOf(c1) + c2 + c3 + c4; - } - - @Override - public String toString() { - StringBuilder stringBuilder = new StringBuilder("=============== ").append(SDNA_ID).append('\n'); - for (Structure structure : structures) { - stringBuilder.append(structure.toString()).append('\n'); - } - return stringBuilder.append("===============").toString(); - } -} diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/file/DynamicArray.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/file/DynamicArray.java deleted file mode 100644 index 748757ab0..000000000 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/file/DynamicArray.java +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Copyright (c) 2009-2018 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.blender.file; - -/** - * An array that can be dynamically modified - * @author Marcin Roguski - * @param - * the type of stored data in the array - */ -public class DynamicArray implements Cloneable { - - /** An array object that holds the required data. */ - private T[] array; - /** - * This table holds the sizes of dimensions of the dynamic table. Its length specifies the table dimension or a - * pointer level. For example: if tableSizes.length == 3 then it either specifies a dynamic table of fixed lengths: - * dynTable[a][b][c], where a,b,c are stored in the tableSizes table. - */ - private int[] tableSizes; - - /** - * Constructor. Builds an empty array of the specified sizes. - * @param tableSizes - * the sizes of the table - * @throws IllegalArgumentException - * an exception is thrown if one of the sizes is not a positive number - */ - public DynamicArray(int[] tableSizes, T[] data) { - this.tableSizes = tableSizes; - int totalSize = 1; - for (int size : tableSizes) { - if (size <= 0) { - throw new IllegalArgumentException("The size of the table must be positive!"); - } - totalSize *= size; - } - if (totalSize != data.length) { - throw new IllegalArgumentException("The size of the table does not match the size of the given data!"); - } - this.array = data; - } - - @Override - public Object clone() throws CloneNotSupportedException { - return super.clone(); - } - - /** - * This method returns a value on the specified position. The dimension of the table is not taken into - * consideration. - * @param position - * the position of the data - * @return required data - */ - public T get(int position) { - return array[position]; - } - - /** - * This method returns a value on the specified position in multidimensional array. Be careful not to exceed the - * table boundaries. Check the table's dimension first. - * @param position - * the position of the data indices of data position - * @return required data required data - */ - public T get(int... position) { - if (position.length != tableSizes.length) { - throw new ArrayIndexOutOfBoundsException("The table accepts " + tableSizes.length + " indexing number(s)!"); - } - int index = 0; - for (int i = 0; i < position.length - 1; ++i) { - index += position[i] * tableSizes[i + 1]; - } - index += position[position.length - 1]; - return array[index]; - } - - /** - * This method returns the total amount of data stored in the array. - * @return the total amount of data stored in the array - */ - public int getTotalSize() { - return array.length; - } - - @Override - public String toString() { - StringBuilder result = new StringBuilder(); - if (array instanceof Character[]) {// in case of character array we convert it to String - for (int i = 0; i < array.length && (Character) array[i] != '\0'; ++i) {// strings are terminater with '0' - result.append(array[i]); - } - } else { - result.append('['); - for (int i = 0; i < array.length; ++i) { - result.append(array[i].toString()); - if (i + 1 < array.length) { - result.append(','); - } - } - result.append(']'); - } - return result.toString(); - } -} \ No newline at end of file diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/file/Field.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/file/Field.java deleted file mode 100644 index 88c33467f..000000000 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/file/Field.java +++ /dev/null @@ -1,327 +0,0 @@ -package com.jme3.scene.plugins.blender.file; - -import com.jme3.scene.plugins.blender.BlenderContext; -import com.jme3.scene.plugins.blender.file.Structure.DataType; - -import java.util.ArrayList; -import java.util.List; - -/** - * This class represents a single field in the structure. It can be either a primitive type or a table or a reference to - * another structure. - * @author Marcin Roguski - */ -/* package */ -class Field implements Cloneable { - - private static final int NAME_LENGTH = 24; - private static final int TYPE_LENGTH = 16; - /** The blender context. */ - public BlenderContext blenderContext; - /** The type of the field. */ - public String type; - /** The name of the field. */ - public String name; - /** The value of the field. Filled during data reading. */ - public Object value; - /** This variable indicates the level of the pointer. */ - public int pointerLevel; - /** - * This variable determines the sizes of the array. If the value is null then the field is not an array. - */ - public int[] tableSizes; - /** This variable indicates if the field is a function pointer. */ - public boolean function; - - /** - * Constructor. Saves the field data and parses its name. - * @param name - * the name of the field - * @param type - * the type of the field - * @param blenderContext - * the blender context - * @throws BlenderFileException - * this exception is thrown if the names contain errors - */ - public Field(String name, String type, BlenderContext blenderContext) throws BlenderFileException { - this.type = type; - this.blenderContext = blenderContext; - this.parseField(new StringBuilder(name)); - } - - /** - * Copy constructor. Used in clone method. Copying is not full. The value in the new object is not set so that we - * have a clean empty copy of the field to fill with data. - * @param field - * the object that we copy - */ - private Field(Field field) { - type = field.type; - name = field.name; - blenderContext = field.blenderContext; - pointerLevel = field.pointerLevel; - if (field.tableSizes != null) { - tableSizes = field.tableSizes.clone(); - } - function = field.function; - } - - @Override - public Object clone() throws CloneNotSupportedException { - return new Field(this); - } - - /** - * This method fills the field with data read from the input stream. - * @param blenderInputStream - * the stream we read data from - * @throws BlenderFileException - * an exception is thrown when the blend file is somehow invalid or corrupted - */ - public void fill(BlenderInputStream blenderInputStream) throws BlenderFileException { - int dataToRead = 1; - if (tableSizes != null && tableSizes.length > 0) { - for (int size : tableSizes) { - if (size <= 0) { - throw new BlenderFileException("The field " + name + " has invalid table size: " + size); - } - dataToRead *= size; - } - } - DataType dataType = pointerLevel == 0 ? DataType.getDataType(type, blenderContext) : DataType.POINTER; - switch (dataType) { - case POINTER: - if (dataToRead == 1) { - Pointer pointer = new Pointer(pointerLevel, function, blenderContext); - pointer.fill(blenderInputStream); - value = pointer; - } else { - Pointer[] data = new Pointer[dataToRead]; - for (int i = 0; i < dataToRead; ++i) { - Pointer pointer = new Pointer(pointerLevel, function, blenderContext); - pointer.fill(blenderInputStream); - data[i] = pointer; - } - value = new DynamicArray(tableSizes, data); - } - break; - case CHARACTER: - // character is also stored as a number, because sometimes the new blender version uses - // other number type instead of character as a field type - // and characters are very often used as byte number stores instead of real chars - if (dataToRead == 1) { - value = Byte.valueOf((byte) blenderInputStream.readByte()); - } else { - Character[] data = new Character[dataToRead]; - for (int i = 0; i < dataToRead; ++i) { - data[i] = Character.valueOf((char) blenderInputStream.readByte()); - } - value = new DynamicArray(tableSizes, data); - } - break; - case SHORT: - if (dataToRead == 1) { - value = Integer.valueOf(blenderInputStream.readShort()); - } else { - Number[] data = new Number[dataToRead]; - for (int i = 0; i < dataToRead; ++i) { - data[i] = Integer.valueOf(blenderInputStream.readShort()); - } - value = new DynamicArray(tableSizes, data); - } - break; - case INTEGER: - if (dataToRead == 1) { - value = Integer.valueOf(blenderInputStream.readInt()); - } else { - Number[] data = new Number[dataToRead]; - for (int i = 0; i < dataToRead; ++i) { - data[i] = Integer.valueOf(blenderInputStream.readInt()); - } - value = new DynamicArray(tableSizes, data); - } - break; - case LONG: - if (dataToRead == 1) { - value = Long.valueOf(blenderInputStream.readLong()); - } else { - Number[] data = new Number[dataToRead]; - for (int i = 0; i < dataToRead; ++i) { - data[i] = Long.valueOf(blenderInputStream.readLong()); - } - value = new DynamicArray(tableSizes, data); - } - break; - case FLOAT: - if (dataToRead == 1) { - value = Float.valueOf(blenderInputStream.readFloat()); - } else { - Number[] data = new Number[dataToRead]; - for (int i = 0; i < dataToRead; ++i) { - data[i] = Float.valueOf(blenderInputStream.readFloat()); - } - value = new DynamicArray(tableSizes, data); - } - break; - case DOUBLE: - if (dataToRead == 1) { - value = Double.valueOf(blenderInputStream.readDouble()); - } else { - Number[] data = new Number[dataToRead]; - for (int i = 0; i < dataToRead; ++i) { - data[i] = Double.valueOf(blenderInputStream.readDouble()); - } - value = new DynamicArray(tableSizes, data); - } - break; - case VOID: - break; - case STRUCTURE: - if (dataToRead == 1) { - Structure structure = blenderContext.getDnaBlockData().getStructure(type); - structure.fill(blenderContext.getInputStream()); - value = structure; - } else { - Structure[] data = new Structure[dataToRead]; - for (int i = 0; i < dataToRead; ++i) { - Structure structure = blenderContext.getDnaBlockData().getStructure(type); - structure.fill(blenderContext.getInputStream()); - data[i] = structure; - } - value = new DynamicArray(tableSizes, data); - } - break; - default: - throw new IllegalStateException("Unimplemented filling of type: " + type); - } - } - - /** - * This method parses the field name to determine how the field should be used. - * @param nameBuilder - * the name of the field (given as StringBuilder) - * @throws BlenderFileException - * this exception is thrown if the names contain errors - */ - private void parseField(StringBuilder nameBuilder) throws BlenderFileException { - this.removeWhitespaces(nameBuilder); - // veryfying if the name is a pointer - int pointerIndex = nameBuilder.indexOf("*"); - while (pointerIndex >= 0) { - ++pointerLevel; - nameBuilder.deleteCharAt(pointerIndex); - pointerIndex = nameBuilder.indexOf("*"); - } - // veryfying if the name is a function pointer - if (nameBuilder.indexOf("(") >= 0) { - function = true; - this.removeCharacter(nameBuilder, '('); - this.removeCharacter(nameBuilder, ')'); - } else { - // veryfying if the name is a table - int tableStartIndex = 0; - List lengths = new ArrayList(3);// 3 dimensions will be enough in most cases - do { - tableStartIndex = nameBuilder.indexOf("["); - if (tableStartIndex > 0) { - int tableStopIndex = nameBuilder.indexOf("]"); - if (tableStopIndex < 0) { - throw new BlenderFileException("Invalid structure name: " + name); - } - try { - lengths.add(Integer.valueOf(nameBuilder.substring(tableStartIndex + 1, tableStopIndex))); - } catch (NumberFormatException e) { - throw new BlenderFileException("Invalid structure name caused by invalid table length: " + name, e); - } - nameBuilder.delete(tableStartIndex, tableStopIndex + 1); - } - } while (tableStartIndex > 0); - if (!lengths.isEmpty()) { - tableSizes = new int[lengths.size()]; - for (int i = 0; i < tableSizes.length; ++i) { - tableSizes[i] = lengths.get(i).intValue(); - } - } - } - name = nameBuilder.toString(); - } - - /** - * This method removes the required character from the text. - * @param text - * the text we remove characters from - * @param toRemove - * the character to be removed - */ - private void removeCharacter(StringBuilder text, char toRemove) { - for (int i = 0; i < text.length(); ++i) { - if (text.charAt(i) == toRemove) { - text.deleteCharAt(i); - --i; - } - } - } - - /** - * This method removes all whitespace from the text. - * @param text - * the text we remove whitespace from - */ - private void removeWhitespaces(StringBuilder text) { - for (int i = 0; i < text.length(); ++i) { - if (Character.isWhitespace(text.charAt(i))) { - text.deleteCharAt(i); - --i; - } - } - } - - /** - * This method builds the full name of the field (with function, pointer and table indications). - * @return the full name of the field - */ - /*package*/ String getFullName() { - StringBuilder result = new StringBuilder(); - if (function) { - result.append('('); - } - for (int i = 0; i < pointerLevel; ++i) { - result.append('*'); - } - result.append(name); - if (tableSizes != null) { - for (int i = 0; i < tableSizes.length; ++i) { - result.append('[').append(tableSizes[i]).append(']'); - } - } - if (function) { - result.append(")()"); - } - return result.toString(); - } - - @Override - public String toString() { - StringBuilder result = new StringBuilder(); - result.append(this.getFullName()); - - // insert appropriate number of spaces to format the output corrently - int nameLength = result.length(); - result.append(' ');// at least one space is a must - for (int i = 1; i < NAME_LENGTH - nameLength; ++i) {// we start from i=1 because one space is already added - result.append(' '); - } - result.append(type); - nameLength = result.length(); - for (int i = 0; i < NAME_LENGTH + TYPE_LENGTH - nameLength; ++i) { - result.append(' '); - } - if (value instanceof Character) { - result.append(" = ").append((int) ((Character) value).charValue()); - } else { - result.append(" = ").append(value != null ? value.toString() : "null"); - } - return result.toString(); - } -} \ No newline at end of file diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/file/FileBlockHeader.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/file/FileBlockHeader.java deleted file mode 100644 index 6a5222a9c..000000000 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/file/FileBlockHeader.java +++ /dev/null @@ -1,213 +0,0 @@ -/* - * 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.blender.file; - -import java.util.logging.Level; -import java.util.logging.Logger; - -import com.jme3.scene.plugins.blender.BlenderContext; - -/** - * A class that holds the header data of a file block. The file block itself is not implemented. This class holds its - * start position in the stream and using this the structure can fill itself with the proper data. - * @author Marcin Roguski - */ -public class FileBlockHeader { - private static final Logger LOGGER = Logger.getLogger(FileBlockHeader.class.getName()); - - /** Identifier of the file-block [4 bytes]. */ - private BlockCode code; - /** Total length of the data after the file-block-header [4 bytes]. */ - private int size; - /** - * Memory address the structure was located when written to disk [4 or 8 bytes (defined in file header as a pointer - * size)]. - */ - private long oldMemoryAddress; - /** Index of the SDNA structure [4 bytes]. */ - private int sdnaIndex; - /** Number of structure located in this file-block [4 bytes]. */ - private int count; - /** Start position of the block's data in the stream. */ - private int blockPosition; - - /** - * Constructor. Loads the block header from the given stream during instance creation. - * @param inputStream - * the stream we read the block header from - * @param blenderContext - * the blender context - * @throws BlenderFileException - * this exception is thrown when the pointer size is neither 4 nor 8 - */ - public FileBlockHeader(BlenderInputStream inputStream, BlenderContext blenderContext) throws BlenderFileException { - inputStream.alignPosition(4); - code = BlockCode.valueOf(inputStream.readByte() << 24 | inputStream.readByte() << 16 | inputStream.readByte() << 8 | inputStream.readByte()); - size = inputStream.readInt(); - oldMemoryAddress = inputStream.readPointer(); - sdnaIndex = inputStream.readInt(); - count = inputStream.readInt(); - blockPosition = inputStream.getPosition(); - if (BlockCode.BLOCK_DNA1 == code) { - blenderContext.setBlockData(new DnaBlockData(inputStream, blenderContext)); - } else { - inputStream.setPosition(blockPosition + size); - blenderContext.addFileBlockHeader(Long.valueOf(oldMemoryAddress), this); - } - } - - /** - * This method returns the structure described by the header filled with appropriate data. - * @param blenderContext - * the blender context - * @return structure filled with data - * @throws BlenderFileException - */ - public Structure getStructure(BlenderContext blenderContext) throws BlenderFileException { - blenderContext.getInputStream().setPosition(blockPosition); - Structure structure = blenderContext.getDnaBlockData().getStructure(sdnaIndex); - structure.fill(blenderContext.getInputStream()); - return structure; - } - - /** - * This method returns the code of this data block. - * @return the code of this data block - */ - public BlockCode getCode() { - return code; - } - - /** - * This method returns the size of the data stored in this block. - * @return the size of the data stored in this block - */ - public int getSize() { - return size; - } - - /** - * This method returns the sdna index. - * @return the sdna index - */ - public int getSdnaIndex() { - return sdnaIndex; - } - - /** - * This data returns the number of structure stored in the data block after this header. - * @return the number of structure stored in the data block after this header - */ - public int getCount() { - return count; - } - - /** - * This method returns the start position of the data block in the blend file stream. - * @return the start position of the data block - */ - public int getBlockPosition() { - return blockPosition; - } - - /** - * This method indicates if the block is the last block in the file. - * @return true if this block is the last one in the file nad false otherwise - */ - public boolean isLastBlock() { - return BlockCode.BLOCK_ENDB == code; - } - - /** - * This method indicates if the block is the SDNA block. - * @return true if this block is the SDNA block and false otherwise - */ - public boolean isDnaBlock() { - return BlockCode.BLOCK_DNA1 == code; - } - - @Override - public String toString() { - return "FILE BLOCK HEADER [" + code.toString() + " : " + size + " : " + oldMemoryAddress + " : " + sdnaIndex + " : " + count + "]"; - } - - public static enum BlockCode { - BLOCK_ME00('M' << 24 | 'E' << 16), // mesh - BLOCK_CA00('C' << 24 | 'A' << 16), // camera - BLOCK_LA00('L' << 24 | 'A' << 16), // lamp - BLOCK_OB00('O' << 24 | 'B' << 16), // object - BLOCK_MA00('M' << 24 | 'A' << 16), // material - BLOCK_SC00('S' << 24 | 'C' << 16), // scene - BLOCK_WO00('W' << 24 | 'O' << 16), // world - BLOCK_TX00('T' << 24 | 'X' << 16), // texture - BLOCK_IP00('I' << 24 | 'P' << 16), // ipo - BLOCK_AC00('A' << 24 | 'C' << 16), // action - BLOCK_IM00('I' << 24 | 'M' << 16), // image - BLOCK_TE00('T' << 24 | 'E' << 16), - BLOCK_WM00('W' << 24 | 'M' << 16), - BLOCK_SR00('S' << 24 | 'R' << 16), - BLOCK_SN00('S' << 24 | 'N' << 16), - BLOCK_BR00('B' << 24 | 'R' << 16), - BLOCK_LS00('L' << 24 | 'S' << 16), - BLOCK_GR00('G' << 24 | 'R' << 16), - BLOCK_AR00('A' << 24 | 'R' << 16), - BLOCK_GLOB('G' << 24 | 'L' << 16 | 'O' << 8 | 'B'), - BLOCK_REND('R' << 24 | 'E' << 16 | 'N' << 8 | 'D'), - BLOCK_DATA('D' << 24 | 'A' << 16 | 'T' << 8 | 'A'), - BLOCK_DNA1('D' << 24 | 'N' << 16 | 'A' << 8 | '1'), - BLOCK_ENDB('E' << 24 | 'N' << 16 | 'D' << 8 | 'B'), - BLOCK_TEST('T' << 24 | 'E' << 16 | 'S' << 8 | 'T'), - BLOCK_UNKN(0); - - private int code; - - private BlockCode(int code) { - this.code = code; - } - - public static BlockCode valueOf(int code) { - for (BlockCode blockCode : BlockCode.values()) { - if (blockCode.code == code) { - return blockCode; - } - } - byte[] codeBytes = new byte[] { (byte) (code >> 24 & 0xFF), (byte) (code >> 16 & 0xFF), (byte) (code >> 8 & 0xFF), (byte) (code & 0xFF) }; - for (int i = 0; i < codeBytes.length; ++i) { - if (codeBytes[i] == 0) { - codeBytes[i] = '0'; - } - } - LOGGER.log(Level.WARNING, "Unknown block header: {0}", new String(codeBytes)); - return BLOCK_UNKN; - } - } -} diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/file/Pointer.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/file/Pointer.java deleted file mode 100644 index c5a662325..000000000 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/file/Pointer.java +++ /dev/null @@ -1,189 +0,0 @@ -/* - * 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.blender.file; - -import java.util.ArrayList; -import java.util.List; - -import com.jme3.scene.plugins.blender.BlenderContext; - -/** - * A class that represents a pointer of any level that can be stored in the file. - * @author Marcin Roguski - */ -public class Pointer { - - /** The blender context. */ - private BlenderContext blenderContext; - /** The level of the pointer. */ - private int pointerLevel; - /** The address in file it points to. */ - private long oldMemoryAddress; - /** This variable indicates if the field is a function pointer. */ - public boolean function; - - /** - * Constructr. Stores the basic data about the pointer. - * @param pointerLevel - * the level of the pointer - * @param function - * this variable indicates if the field is a function pointer - * @param blenderContext - * the repository f data; used in fetching the value that the pointer points - */ - public Pointer(int pointerLevel, boolean function, BlenderContext blenderContext) { - this.pointerLevel = pointerLevel; - this.function = function; - this.blenderContext = blenderContext; - } - - /** - * This method fills the pointer with its address value (it doesn't get the actual data yet. Use the 'fetch' method - * for this. - * @param inputStream - * the stream we read the pointer value from - */ - public void fill(BlenderInputStream inputStream) { - oldMemoryAddress = inputStream.readPointer(); - } - - /** - * This method fetches the data stored under the given address. - * @return the data read from the file - * @throws BlenderFileException - * this exception is thrown when the blend file structure is somehow invalid or corrupted - */ - public List fetchData() throws BlenderFileException { - if (oldMemoryAddress == 0) { - throw new NullPointerException("The pointer points to nothing!"); - } - List structures = null; - FileBlockHeader dataFileBlock = blenderContext.getFileBlock(oldMemoryAddress); - if (dataFileBlock == null) { - throw new BlenderFileException("No data stored for address: " + oldMemoryAddress + ". Make sure you did not open the newer blender file with older blender version."); - } - BlenderInputStream inputStream = blenderContext.getInputStream(); - if (pointerLevel > 1) { - int pointersAmount = dataFileBlock.getSize() / inputStream.getPointerSize() * dataFileBlock.getCount(); - for (int i = 0; i < pointersAmount; ++i) { - inputStream.setPosition(dataFileBlock.getBlockPosition() + inputStream.getPointerSize() * i); - long oldMemoryAddress = inputStream.readPointer(); - if (oldMemoryAddress != 0L) { - Pointer p = new Pointer(pointerLevel - 1, function, blenderContext); - p.oldMemoryAddress = oldMemoryAddress; - if (structures == null) { - structures = p.fetchData(); - } else { - structures.addAll(p.fetchData()); - } - } else { - // it is necessary to put null's if the pointer is null, ie. in materials array that is attached to the mesh, the index - // of the material is important, that is why we need null's to indicate that some materials' slots are empty - if (structures == null) { - structures = new ArrayList(); - } - structures.add(null); - } - } - } else { - inputStream.setPosition(dataFileBlock.getBlockPosition()); - structures = new ArrayList(dataFileBlock.getCount()); - for (int i = 0; i < dataFileBlock.getCount(); ++i) { - Structure structure = blenderContext.getDnaBlockData().getStructure(dataFileBlock.getSdnaIndex()); - structure.fill(blenderContext.getInputStream()); - structures.add(structure); - } - return structures; - } - return structures; - } - - /** - * This method indicates if this pointer points to a function. - * @return true if this is a function pointer and false otherwise - */ - public boolean isFunction() { - return function; - } - - /** - * This method indicates if this is a null-pointer or not. - * @return true if the pointer is null and false otherwise - */ - public boolean isNull() { - return oldMemoryAddress == 0; - } - - /** - * This method indicates if this is a null-pointer or not. - * @return true if the pointer is not null and false otherwise - */ - public boolean isNotNull() { - return oldMemoryAddress != 0; - } - - /** - * This method returns the old memory address of the structure pointed by the pointer. - * @return the old memory address of the structure pointed by the pointer - */ - public long getOldMemoryAddress() { - return oldMemoryAddress; - } - - @Override - public String toString() { - return oldMemoryAddress == 0 ? "{$null$}" : "{$" + oldMemoryAddress + "$}"; - } - - @Override - public int hashCode() { - return 31 + (int) (oldMemoryAddress ^ oldMemoryAddress >>> 32); - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (this.getClass() != obj.getClass()) { - return false; - } - Pointer other = (Pointer) obj; - if (oldMemoryAddress != other.oldMemoryAddress) { - return false; - } - return true; - } -} diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/file/Structure.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/file/Structure.java deleted file mode 100644 index da3076e2a..000000000 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/file/Structure.java +++ /dev/null @@ -1,328 +0,0 @@ -/* - * Copyright (c) 2009-2020 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.blender.file; - -import java.util.Collection; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; - -import com.jme3.scene.plugins.blender.BlenderContext; - -/** - * A class representing a single structure in the file. - * @author Marcin Roguski - */ -public class Structure implements Cloneable { - - /** The address of the block that fills the structure. */ - private transient Long oldMemoryAddress; - /** The type of the structure. */ - private String type; - /** - * The fields of the structure. Each field consists of a pair: name-type. - */ - private Field[] fields; - - /** - * Constructor that copies the data of the structure. - * @param structure - * the structure to copy. - * @throws CloneNotSupportedException - * this exception should never be thrown - */ - private Structure(Structure structure) throws CloneNotSupportedException { - type = structure.type; - fields = new Field[structure.fields.length]; - for (int i = 0; i < fields.length; ++i) { - fields[i] = (Field) structure.fields[i].clone(); - } - oldMemoryAddress = structure.oldMemoryAddress; - } - - /** - * Constructor. Loads the structure from the given stream during instance creation. - * @param inputStream - * the stream we read the structure from - * @param names - * the names from which the name of structure and its fields will be taken - * @param types - * the names of types for the structure - * @param blenderContext - * the blender context - * @throws BlenderFileException - * this exception occurs if the amount of fields, defined in the file, is negative - */ - public Structure(BlenderInputStream inputStream, String[] names, String[] types, BlenderContext blenderContext) throws BlenderFileException { - int nameIndex = inputStream.readShort(); - type = types[nameIndex]; - int fieldsAmount = inputStream.readShort(); - if (fieldsAmount < 0) { - throw new BlenderFileException("The amount of fields of " + type + " structure cannot be negative!"); - } - if (fieldsAmount > 0) { - fields = new Field[fieldsAmount]; - for (int i = 0; i < fieldsAmount; ++i) { - int typeIndex = inputStream.readShort(); - nameIndex = inputStream.readShort(); - fields[i] = new Field(names[nameIndex], types[typeIndex], blenderContext); - } - } - oldMemoryAddress = Long.valueOf(-1L); - } - - /** - * This method fills the structure with data. - * @param inputStream - * the stream we read data from, its read cursor should be placed at the start position of the data for the - * structure - * @throws BlenderFileException - * an exception is thrown when the blend file is somehow invalid or corrupted - */ - public void fill(BlenderInputStream inputStream) throws BlenderFileException { - int position = inputStream.getPosition(); - inputStream.setPosition(position - 8 - inputStream.getPointerSize()); - oldMemoryAddress = Long.valueOf(inputStream.readPointer()); - inputStream.setPosition(position); - for (Field field : fields) { - field.fill(inputStream); - } - } - - /** - * This method returns the value of the filed with a given name. - * @param fieldName - * the name of the field - * @return the value of the field or null if no field with a given name is found - */ - public Object getFieldValue(String fieldName) { - return this.getFieldValue(fieldName, null); - } - - /** - * This method returns the value of the filed with a given name. - * @param fieldName - * the name of the field - * @param defaultValue - * the value that is being returned when no field of a given name is found - * @return the value of the field or the given default value if no field with a given name is found - */ - public Object getFieldValue(String fieldName, Object defaultValue) { - for (Field field : fields) { - if (field.name.equalsIgnoreCase(fieldName)) { - return field.value; - } - } - return defaultValue; - } - - /** - * This method returns the value of the filed with a given name. The structure is considered to have flat fields - * only (no substructures). - * @param fieldName - * the name of the field - * @return the value of the field or null if no field with a given name is found - */ - public Object getFlatFieldValue(String fieldName) { - for (Field field : fields) { - Object value = field.value; - if (field.name.equalsIgnoreCase(fieldName)) { - return value; - } else if (value instanceof Structure) { - value = ((Structure) value).getFlatFieldValue(fieldName); - if (value != null) {// we can compare references here, since we use one static object as a NULL field value - return value; - } - } - } - return null; - } - - /** - * This method should be used on structures that are of a 'ListBase' type. It creates a List of structures that are - * held by this structure within the blend file. - * @return a list of filled structures - * @throws BlenderFileException - * this exception is thrown when the blend file structure is somehow invalid or corrupted - * @throws IllegalArgumentException - * this exception is thrown if the type of the structure is not 'ListBase' - */ - public List evaluateListBase() throws BlenderFileException { - if (!"ListBase".equals(type)) { - throw new IllegalStateException("This structure is not of type: 'ListBase'"); - } - Pointer first = (Pointer) this.getFieldValue("first"); - Pointer last = (Pointer) this.getFieldValue("last"); - long currentAddress = 0; - long lastAddress = last.getOldMemoryAddress(); - List result = new LinkedList(); - while (currentAddress != lastAddress) { - currentAddress = first.getOldMemoryAddress(); - Structure structure = first.fetchData().get(0); - result.add(structure); - first = (Pointer) structure.getFlatFieldValue("next"); - } - return result; - } - - /** - * This method returns the type of the structure. - * @return the type of the structure - */ - public String getType() { - return type; - } - - /** - * This method returns the amount of fields for the current structure. - * @return the amount of fields for the current structure - */ - public int getFieldsAmount() { - return fields.length; - } - - /** - * This method returns the full field name of the given index. - * @param fieldIndex - * the index of the field - * @return the full field name of the given index - */ - public String getFieldFullName(int fieldIndex) { - return fields[fieldIndex].getFullName(); - } - - /** - * This method returns the field type of the given index. - * @param fieldIndex - * the index of the field - * @return the field type of the given index - */ - public String getFieldType(int fieldIndex) { - return fields[fieldIndex].type; - } - - /** - * This method returns the address of the structure. The structure should be filled with data otherwise an exception - * is thrown. - * @return the address of the feature stored in this structure - */ - public Long getOldMemoryAddress() { - if (oldMemoryAddress.longValue() == -1L) { - throw new IllegalStateException("Call the 'fill' method and fill the structure with data first!"); - } - return oldMemoryAddress; - } - - /** - * This method returns the name of the structure. If the structure has an ID field then the name is returned. - * Otherwise the name does not exists and the method returns null. - * @return the name of the structure read from the ID field or null - */ - public String getName() { - Object fieldValue = this.getFieldValue("ID"); - if (fieldValue instanceof Structure) { - Structure id = (Structure) fieldValue; - return id.getFieldValue("name").toString().substring(2);// blender adds 2-charactes as a name prefix - } - Object name = this.getFieldValue("name", null); - return name == null ? null : name.toString().substring(2); - } - - @Override - public String toString() { - StringBuilder result = new StringBuilder("struct ").append(type).append(" {\n"); - for (int i = 0; i < fields.length; ++i) { - result.append(fields[i].toString()).append('\n'); - } - return result.append('}').toString(); - } - - @Override - public Object clone() throws CloneNotSupportedException { - return new Structure(this); - } - - /** - * This enum enumerates all known data types that can be found in the blend file. - * @author Marcin Roguski (Kaelthas) - */ - /* package */static enum DataType { - - CHARACTER, SHORT, INTEGER, LONG, FLOAT, DOUBLE, VOID, STRUCTURE, POINTER; - /** The map containing the known primary types. */ - private static final Map PRIMARY_TYPES = new HashMap(10); - - static { - PRIMARY_TYPES.put("char", CHARACTER); - PRIMARY_TYPES.put("uchar", CHARACTER); - PRIMARY_TYPES.put("short", SHORT); - PRIMARY_TYPES.put("ushort", SHORT); - PRIMARY_TYPES.put("int", INTEGER); - PRIMARY_TYPES.put("long", LONG); - PRIMARY_TYPES.put("ulong", LONG); - PRIMARY_TYPES.put("uint64_t", LONG); - PRIMARY_TYPES.put("float", FLOAT); - PRIMARY_TYPES.put("double", DOUBLE); - PRIMARY_TYPES.put("void", VOID); - } - - /** - * This method returns the data type that is appropriate to the given type name. WARNING! The type recognition - * is case sensitive! - * @param type - * the type name of the data - * @param blenderContext - * the blender context - * @return appropriate enum value to the given type name - * @throws BlenderFileException - * this exception is thrown if the given type name does not exist in the blend file - */ - public static DataType getDataType(String type, BlenderContext blenderContext) throws BlenderFileException { - DataType result = PRIMARY_TYPES.get(type); - if (result != null) { - return result; - } - if (blenderContext.getDnaBlockData().hasStructure(type)) { - return STRUCTURE; - } - throw new BlenderFileException("Unknown data type: " + type); - } - - /** - * @return a collection of known primary types names - */ - /* package */static Collection getKnownPrimaryTypesNames() { - return PRIMARY_TYPES.keySet(); - } - } -} diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/landscape/LandscapeHelper.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/landscape/LandscapeHelper.java deleted file mode 100644 index 32160c13a..000000000 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/landscape/LandscapeHelper.java +++ /dev/null @@ -1,214 +0,0 @@ -package com.jme3.scene.plugins.blender.landscape; - -import java.util.ArrayList; -import java.util.List; -import java.util.logging.Level; -import java.util.logging.Logger; - -import com.jme3.light.AmbientLight; -import com.jme3.light.Light; -import com.jme3.math.ColorRGBA; -import com.jme3.post.filters.FogFilter; -import com.jme3.scene.Spatial; -import com.jme3.scene.plugins.blender.AbstractBlenderHelper; -import com.jme3.scene.plugins.blender.BlenderContext; -import com.jme3.scene.plugins.blender.file.BlenderFileException; -import com.jme3.scene.plugins.blender.file.Structure; -import com.jme3.scene.plugins.blender.textures.ColorBand; -import com.jme3.scene.plugins.blender.textures.CombinedTexture; -import com.jme3.scene.plugins.blender.textures.ImageUtils; -import com.jme3.scene.plugins.blender.textures.TextureHelper; -import com.jme3.scene.plugins.blender.textures.TexturePixel; -import com.jme3.scene.plugins.blender.textures.io.PixelIOFactory; -import com.jme3.scene.plugins.blender.textures.io.PixelInputOutput; -import com.jme3.texture.Image; -import com.jme3.texture.Image.Format; -import com.jme3.texture.TextureCubeMap; -import com.jme3.util.SkyFactory; - -/** - * The class that allows to load the following:
  • the ambient light of the scene
  • the sky of the scene (with or without texture) - * - * @author Marcin Roguski (Kaelthas) - */ -public class LandscapeHelper extends AbstractBlenderHelper { - private static final Logger LOGGER = Logger.getLogger(LandscapeHelper.class.getName()); - - private static final int SKYTYPE_BLEND = 1; - private static final int SKYTYPE_REAL = 2; - private static final int SKYTYPE_PAPER = 4; - - private static final int MODE_MIST = 0x01; - - public LandscapeHelper(String blenderVersion, BlenderContext blenderContext) { - super(blenderVersion, blenderContext); - } - - /** - * Loads scene ambient light. - * @param worldStructure - * the world's blender structure - * @return the scene's ambient light - */ - public Light toAmbientLight(Structure worldStructure) { - LOGGER.fine("Loading ambient light."); - AmbientLight ambientLight = null; - float ambr = ((Number) worldStructure.getFieldValue("ambr")).floatValue(); - float ambg = ((Number) worldStructure.getFieldValue("ambg")).floatValue(); - float ambb = ((Number) worldStructure.getFieldValue("ambb")).floatValue(); - if (ambr > 0 || ambg > 0 || ambb > 0) { - ambientLight = new AmbientLight(); - ColorRGBA ambientLightColor = new ColorRGBA(ambr, ambg, ambb, 0.0f); - ambientLight.setColor(ambientLightColor); - LOGGER.log(Level.FINE, "Loaded ambient light: {0}.", ambientLightColor); - } else { - LOGGER.finer("Ambient light is set to BLACK which means: no ambient light! The ambient light node will not be included in the result."); - } - return ambientLight; - } - - /** - * The method loads fog for the scene. - * NOTICE! Remember to manually set the distance and density of the fog. - * Unfortunately blender's fog parameters in no way fit to the JME. - * @param worldStructure - * the world's structure - * @return fog filter or null if scene does not define it - */ - public FogFilter toFog(Structure worldStructure) { - FogFilter result = null; - int mode = ((Number) worldStructure.getFieldValue("mode")).intValue(); - if ((mode & MODE_MIST) != 0) { - LOGGER.fine("Loading fog."); - result = new FogFilter(); - result.setName("FIfog"); - result.setFogColor(this.toBackgroundColor(worldStructure)); - } - return result; - } - - /** - * Loads the background color. - * @param worldStructure - * the world's structure - * @return the horizon color of the world which is used as a background color. - */ - public ColorRGBA toBackgroundColor(Structure worldStructure) { - float horr = ((Number) worldStructure.getFieldValue("horr")).floatValue(); - float horg = ((Number) worldStructure.getFieldValue("horg")).floatValue(); - float horb = ((Number) worldStructure.getFieldValue("horb")).floatValue(); - return new ColorRGBA(horr, horg, horb, 1); - } - - /** - * Loads scene's sky. Sky can be plain or textured. - * If no sky type is selected in blender then no sky is loaded. - * @param worldStructure - * the world's structure - * @return the scene's sky - * @throws BlenderFileException - * blender exception is thrown when problems with blender file occur - */ - public Spatial toSky(Structure worldStructure) throws BlenderFileException { - int skytype = ((Number) worldStructure.getFieldValue("skytype")).intValue(); - if (skytype == 0) { - return null; - } - - LOGGER.fine("Loading sky."); - ColorRGBA horizontalColor = this.toBackgroundColor(worldStructure); - - float zenr = ((Number) worldStructure.getFieldValue("zenr")).floatValue(); - float zeng = ((Number) worldStructure.getFieldValue("zeng")).floatValue(); - float zenb = ((Number) worldStructure.getFieldValue("zenb")).floatValue(); - ColorRGBA zenithColor = new ColorRGBA(zenr, zeng, zenb, 1); - - // jutr for this case load generated textures wheather user had set it or not because those might be needed to properly load the sky - boolean loadGeneratedTextures = blenderContext.getBlenderKey().isLoadGeneratedTextures(); - blenderContext.getBlenderKey().setLoadGeneratedTextures(true); - - TextureHelper textureHelper = blenderContext.getHelper(TextureHelper.class); - List loadedTextures = null; - try { - loadedTextures = textureHelper.readTextureData(worldStructure, new float[] { horizontalColor.r, horizontalColor.g, horizontalColor.b, horizontalColor.a }, true); - } finally { - blenderContext.getBlenderKey().setLoadGeneratedTextures(loadGeneratedTextures); - } - - TextureCubeMap texture = null; - if (loadedTextures != null && loadedTextures.size() > 0) { - if (loadedTextures.size() > 1) { - throw new IllegalStateException("There should be only one combined texture for sky!"); - } - CombinedTexture combinedTexture = loadedTextures.get(0); - texture = combinedTexture.generateSkyTexture(horizontalColor, zenithColor, blenderContext); - } else { - LOGGER.fine("Preparing colors for colorband."); - int colorbandType = ColorBand.IPO_CARDINAL; - List colorbandColors = new ArrayList(3); - colorbandColors.add(horizontalColor); - if ((skytype & SKYTYPE_BLEND) != 0) { - if ((skytype & SKYTYPE_PAPER) != 0) { - colorbandType = ColorBand.IPO_LINEAR; - } - if ((skytype & SKYTYPE_REAL) != 0) { - colorbandColors.add(0, zenithColor); - } - colorbandColors.add(zenithColor); - } - - int size = blenderContext.getBlenderKey().getSkyGeneratedTextureSize(); - - List positions = new ArrayList(colorbandColors.size()); - positions.add(0); - if (colorbandColors.size() == 2) { - positions.add(size - 1); - } else if (colorbandColors.size() == 3) { - positions.add(size / 2); - positions.add(size - 1); - } - - LOGGER.fine("Generating sky texture."); - float[][] values = new ColorBand(colorbandType, colorbandColors, positions, size).computeValues(); - - Image image = ImageUtils.createEmptyImage(Format.RGB8, size, size, 6); - PixelInputOutput pixelIO = PixelIOFactory.getPixelIO(image.getFormat()); - TexturePixel pixel = new TexturePixel(); - - LOGGER.fine("Creating side textures."); - int[] sideImagesIndexes = new int[] { 0, 1, 4, 5 }; - for (int i : sideImagesIndexes) { - for (int y = 0; y < size; ++y) { - pixel.red = values[y][0]; - pixel.green = values[y][1]; - pixel.blue = values[y][2]; - - for (int x = 0; x < size; ++x) { - pixelIO.write(image, i, pixel, x, y); - } - } - } - - LOGGER.fine("Creating top texture."); - pixelIO.read(image, 0, pixel, 0, image.getHeight() - 1); - for (int y = 0; y < size; ++y) { - for (int x = 0; x < size; ++x) { - pixelIO.write(image, 3, pixel, x, y); - } - } - - LOGGER.fine("Creating bottom texture."); - pixelIO.read(image, 0, pixel, 0, 0); - for (int y = 0; y < size; ++y) { - for (int x = 0; x < size; ++x) { - pixelIO.write(image, 2, pixel, x, y); - } - } - - texture = new TextureCubeMap(image); - } - - LOGGER.fine("Sky texture created. Creating sky."); - return SkyFactory.createSky(blenderContext.getAssetManager(), texture, SkyFactory.EnvMapType.CubeMap); - } -} diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/lights/LightHelper.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/lights/LightHelper.java deleted file mode 100644 index 3ae866b7c..000000000 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/lights/LightHelper.java +++ /dev/null @@ -1,116 +0,0 @@ -/* - * 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.blender.lights; - -import java.util.logging.Level; -import java.util.logging.Logger; - -import com.jme3.light.DirectionalLight; -import com.jme3.light.Light; -import com.jme3.light.PointLight; -import com.jme3.light.SpotLight; -import com.jme3.math.ColorRGBA; -import com.jme3.math.FastMath; -import com.jme3.scene.plugins.blender.AbstractBlenderHelper; -import com.jme3.scene.plugins.blender.BlenderContext; -import com.jme3.scene.plugins.blender.BlenderContext.LoadedDataType; -import com.jme3.scene.plugins.blender.file.BlenderFileException; -import com.jme3.scene.plugins.blender.file.Structure; - -/** - * A class that is used in light calculations. - * @author Marcin Roguski - */ -public class LightHelper extends AbstractBlenderHelper { - - private static final Logger LOGGER = Logger.getLogger(LightHelper.class.getName()); - - /** - * This constructor parses the given blender version and stores the result. Some functionalities may differ in - * different blender versions. - * @param blenderVersion - * the version read from the blend file - * @param blenderContext - * the blender context - */ - public LightHelper(String blenderVersion, BlenderContext blenderContext) { - super(blenderVersion, blenderContext); - } - - public Light toLight(Structure structure, BlenderContext blenderContext) throws BlenderFileException { - Light result = (Light) blenderContext.getLoadedFeature(structure.getOldMemoryAddress(), LoadedDataType.FEATURE); - if (result != null) { - return result; - } - Light light = null; - int type = ((Number) structure.getFieldValue("type")).intValue(); - switch (type) { - case 0:// Lamp - light = new PointLight(); - float distance = ((Number) structure.getFieldValue("dist")).floatValue(); - ((PointLight) light).setRadius(distance); - break; - case 1:// Sun - LOGGER.log(Level.WARNING, "'Sun' lamp is not supported in jMonkeyEngine. Using PointLight with radius = Float.MAX_VALUE."); - light = new PointLight(); - ((PointLight) light).setRadius(Float.MAX_VALUE); - break; - case 2:// Spot - light = new SpotLight(); - // range - ((SpotLight) light).setSpotRange(((Number) structure.getFieldValue("dist")).floatValue()); - // outer angle - float outerAngle = ((Number) structure.getFieldValue("spotsize")).floatValue() * FastMath.DEG_TO_RAD * 0.5f; - ((SpotLight) light).setSpotOuterAngle(outerAngle); - - // inner angle - float spotblend = ((Number) structure.getFieldValue("spotblend")).floatValue(); - spotblend = FastMath.clamp(spotblend, 0, 1); - float innerAngle = outerAngle * (1 - spotblend); - ((SpotLight) light).setSpotInnerAngle(innerAngle); - break; - case 3:// Hemi - LOGGER.log(Level.WARNING, "'Hemi' lamp is not supported in jMonkeyEngine. Using DirectionalLight instead."); - case 4:// Area - light = new DirectionalLight(); - break; - default: - throw new BlenderFileException("Unknown light source type: " + type); - } - float r = ((Number) structure.getFieldValue("r")).floatValue(); - float g = ((Number) structure.getFieldValue("g")).floatValue(); - float b = ((Number) structure.getFieldValue("b")).floatValue(); - light.setColor(new ColorRGBA(r, g, b, 1.0f)); - light.setName(structure.getName()); - return light; - } -} diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/materials/IAlphaMask.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/materials/IAlphaMask.java deleted file mode 100644 index 7fddd3d41..000000000 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/materials/IAlphaMask.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.jme3.scene.plugins.blender.materials; - -/** - * An interface used in calculating alpha mask during particles' texture calculations. - * @author Marcin Roguski (Kaelthas) - */ -/* package */interface IAlphaMask { - /** - * This method sets the size of the texture's image. - * @param width - * the width of the image - * @param height - * the height of the image - */ - void setImageSize(int width, int height); - - /** - * This method returns the alpha value for the specified texture position. - * @param x - * the X coordinate of the texture position - * @param y - * the Y coordinate of the texture position - * @return the alpha value for the specified texture position - */ - byte getAlpha(float x, float y); -} \ No newline at end of file diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/materials/MaterialContext.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/materials/MaterialContext.java deleted file mode 100644 index 42da1c9d2..000000000 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/materials/MaterialContext.java +++ /dev/null @@ -1,366 +0,0 @@ -package com.jme3.scene.plugins.blender.materials; - -import java.io.IOException; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.logging.Level; -import java.util.logging.Logger; - -import com.jme3.export.JmeExporter; -import com.jme3.export.JmeImporter; -import com.jme3.export.Savable; -import com.jme3.material.Material; -import com.jme3.material.RenderState.BlendMode; -import com.jme3.material.RenderState.FaceCullMode; -import com.jme3.math.ColorRGBA; -import com.jme3.math.Vector2f; -import com.jme3.renderer.queue.RenderQueue.Bucket; -import com.jme3.scene.Geometry; -import com.jme3.scene.VertexBuffer; -import com.jme3.scene.VertexBuffer.Format; -import com.jme3.scene.VertexBuffer.Usage; -import com.jme3.scene.plugins.blender.BlenderContext; -import com.jme3.scene.plugins.blender.file.BlenderFileException; -import com.jme3.scene.plugins.blender.file.Structure; -import com.jme3.scene.plugins.blender.materials.MaterialHelper.DiffuseShader; -import com.jme3.scene.plugins.blender.materials.MaterialHelper.SpecularShader; -import com.jme3.scene.plugins.blender.textures.CombinedTexture; -import com.jme3.scene.plugins.blender.textures.TextureHelper; -import com.jme3.texture.Texture; -import com.jme3.util.BufferUtils; - -/** - * This class holds the data about the material. - * @author Marcin Roguski (Kaelthas) - */ -public final class MaterialContext implements Savable { - private static final Logger LOGGER = Logger.getLogger(MaterialContext.class.getName()); - - // texture mapping types - public static final int MTEX_COL = 0x01; - public static final int MTEX_NOR = 0x02; - public static final int MTEX_SPEC = 0x04; - public static final int MTEX_EMIT = 0x40; - public static final int MTEX_ALPHA = 0x80; - public static final int MTEX_AMB = 0x800; - - public static final int FLAG_TRANSPARENT = 0x10000; - - /* package */final String name; - /* package */final List loadedTextures; - - /* package */final ColorRGBA diffuseColor; - /* package */final DiffuseShader diffuseShader; - /* package */final SpecularShader specularShader; - /* package */final ColorRGBA specularColor; - /* package */final float ambientFactor; - /* package */final float shininess; - /* package */final boolean shadeless; - /* package */final boolean vertexColor; - /* package */final boolean transparent; - /* package */final boolean vTangent; - /* package */FaceCullMode faceCullMode; - - /* package */MaterialContext(Structure structure, BlenderContext blenderContext) throws BlenderFileException { - name = structure.getName(); - - int mode = ((Number) structure.getFieldValue("mode")).intValue(); - shadeless = (mode & 0x4) != 0; - vertexColor = (mode & 0x80) != 0; - vTangent = (mode & 0x4000000) != 0; // NOTE: Requires tangents - - int diff_shader = ((Number) structure.getFieldValue("diff_shader")).intValue(); - diffuseShader = DiffuseShader.values()[diff_shader]; - ambientFactor = ((Number) structure.getFieldValue("amb")).floatValue(); - - if (shadeless) { - float r = ((Number) structure.getFieldValue("r")).floatValue(); - float g = ((Number) structure.getFieldValue("g")).floatValue(); - float b = ((Number) structure.getFieldValue("b")).floatValue(); - float alpha = ((Number) structure.getFieldValue("alpha")).floatValue(); - - diffuseColor = new ColorRGBA(r, g, b, alpha); - specularShader = null; - specularColor = null; - shininess = 0.0f; - } else { - diffuseColor = this.readDiffuseColor(structure, diffuseShader); - - int spec_shader = ((Number) structure.getFieldValue("spec_shader")).intValue(); - specularShader = SpecularShader.values()[spec_shader]; - specularColor = this.readSpecularColor(structure); - float shininess = ((Number) structure.getFieldValue("har")).floatValue();// this is (probably) the specular hardness in blender - this.shininess = shininess > 0.0f ? shininess : MaterialHelper.DEFAULT_SHININESS; - } - - TextureHelper textureHelper = blenderContext.getHelper(TextureHelper.class); - loadedTextures = textureHelper.readTextureData(structure, new float[] { diffuseColor.r, diffuseColor.g, diffuseColor.b, diffuseColor.a }, false); - - long flag = ((Number)structure.getFieldValue("flag")).longValue(); - if((flag & FLAG_TRANSPARENT) != 0) { - // veryfying if the transparency is present - // (in blender transparent mask is 0x10000 but it's better to verify it because blender can indicate transparency when - // it is not required - boolean transparent = false; - if (diffuseColor != null) { - transparent = diffuseColor.a < 1.0f; - if (loadedTextures.size() > 0) {// texture covers the material color - diffuseColor.set(1, 1, 1, 1); - } - } - if (specularColor != null) { - transparent = transparent || specularColor.a < 1.0f; - } - this.transparent = transparent; - } else { - transparent = false; - } - } - - /** - * @return the name of the material - */ - public String getName() { - return name; - } - - /** - * Applies material to a given geometry. - * - * @param geometry - * the geometry - * @param geometriesOMA - * the geometries OMA - * @param userDefinedUVCoordinates - * UV coords defined by user - * @param blenderContext - * the blender context - */ - public void applyMaterial(Geometry geometry, Long geometriesOMA, Map> userDefinedUVCoordinates, BlenderContext blenderContext) { - Material material = null; - if (shadeless) { - material = new Material(blenderContext.getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md"); - - if (!transparent) { - diffuseColor.a = 1; - } - - material.setColor("Color", diffuseColor); - } else { - material = new Material(blenderContext.getAssetManager(), "Common/MatDefs/Light/Lighting.j3md"); - material.setBoolean("UseMaterialColors", Boolean.TRUE); - - // setting the colors - if (!transparent) { - diffuseColor.a = 1; - } - material.setColor("Diffuse", diffuseColor); - - material.setColor("Specular", specularColor); - material.setFloat("Shininess", shininess); - - material.setColor("Ambient", new ColorRGBA(ambientFactor, ambientFactor, ambientFactor, 1f)); - } - - // initializing unused "user-defined UV coords" to all available - Map> unusedUserDefinedUVCoords = Collections.emptyMap(); - if(userDefinedUVCoordinates != null && !userDefinedUVCoordinates.isEmpty()) { - unusedUserDefinedUVCoords = new HashMap<>(userDefinedUVCoordinates); - } - - // applying textures - int textureIndex = 0; - if (loadedTextures != null && loadedTextures.size() > 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) { - String usedUserUVSet = combinedTexture.flatten(geometry, geometriesOMA, userDefinedUVCoordinates, blenderContext); - - this.setTexture(material, combinedTexture.getMappingType(), combinedTexture.getResultTexture()); - - if(usedUserUVSet == null || unusedUserDefinedUVCoords.containsKey(usedUserUVSet)) { - List uvs = combinedTexture.getResultUVS(); - if(uvs != null && uvs.size() > 0) { - VertexBuffer uvCoordsBuffer = new VertexBuffer(TextureHelper.TEXCOORD_TYPES[textureIndex++]); - uvCoordsBuffer.setupData(Usage.Static, 2, Format.Float, BufferUtils.createFloatBuffer(uvs.toArray(new Vector2f[uvs.size()]))); - geometry.getMesh().setBuffer(uvCoordsBuffer); - }//uvs might be null if the user assigned non existing UV coordinates group name to the mesh (this should be fixed in blender file) - - // Remove used "user-defined UV coords" from the unused collection - if(usedUserUVSet != null) { - unusedUserDefinedUVCoords.remove(usedUserUVSet); - } - } - } else { - LOGGER.log(Level.WARNING, "The texture could not be applied because JME only supports up to {0} different UV's.", TextureHelper.TEXCOORD_TYPES.length); - } - } - } - - if (unusedUserDefinedUVCoords != null && unusedUserDefinedUVCoords.size() > 0) { - LOGGER.fine("Storing unused, user defined UV coordinates sets."); - if (unusedUserDefinedUVCoords.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> entry : unusedUserDefinedUVCoords.entrySet()) { - if (textureIndex < TextureHelper.TEXCOORD_TYPES.length) { - List uvs = entry.getValue(); - VertexBuffer uvCoordsBuffer = new VertexBuffer(TextureHelper.TEXCOORD_TYPES[textureIndex++]); - uvCoordsBuffer.setupData(Usage.Static, 2, Format.Float, BufferUtils.createFloatBuffer(uvs.toArray(new Vector2f[uvs.size()]))); - geometry.getMesh().setBuffer(uvCoordsBuffer); - } else { - LOGGER.log(Level.WARNING, "The user's UV set named: '{0}' could not be stored because JME only supports up to {1} different UV's.", new Object[] { - entry.getKey(), TextureHelper.TEXCOORD_TYPES.length - }); - } - } - } - - // applying additional data - material.setName(name); - if (vertexColor) { - material.setBoolean(shadeless ? "VertexColor" : "UseVertexColor", true); - } - material.getAdditionalRenderState().setFaceCullMode(faceCullMode != null ? faceCullMode : blenderContext.getBlenderKey().getFaceCullMode()); - if (transparent) { - material.setTransparent(true); - material.getAdditionalRenderState().setBlendMode(BlendMode.Alpha); - geometry.setQueueBucket(Bucket.Transparent); - } - - geometry.setMaterial(material); - } - - /** - * Sets the texture to the given material. - * - * @param material - * the material that we add texture to - * @param mapTo - * the texture mapping type - * @param texture - * the added texture - */ - private void setTexture(Material material, int mapTo, Texture texture) { - switch (mapTo) { - case MTEX_COL: - material.setTexture(shadeless ? MaterialHelper.TEXTURE_TYPE_COLOR : MaterialHelper.TEXTURE_TYPE_DIFFUSE, texture); - break; - case MTEX_NOR: - material.setTexture(MaterialHelper.TEXTURE_TYPE_NORMAL, texture); - break; - case MTEX_SPEC: - material.setTexture(MaterialHelper.TEXTURE_TYPE_SPECULAR, texture); - break; - case MTEX_EMIT: - material.setTexture(MaterialHelper.TEXTURE_TYPE_GLOW, texture); - break; - case MTEX_ALPHA: - if (!shadeless) { - material.setTexture(MaterialHelper.TEXTURE_TYPE_ALPHA, texture); - } else { - LOGGER.warning("JME does not support alpha map on unshaded material. Material name is " + name); - } - break; - case MTEX_AMB: - material.setTexture(MaterialHelper.TEXTURE_TYPE_LIGHTMAP, texture); - break; - default: - LOGGER.severe("Unknown mapping type: " + mapTo); - } - } - - /** - * @return true if the material has at least one generated texture and false otherwise - */ - public boolean hasGeneratedTextures() { - if (loadedTextures != null) { - for (CombinedTexture generatedTextures : loadedTextures) { - if (generatedTextures.hasGeneratedTextures()) { - return true; - } - } - } - return false; - } - - /** - * This method sets the face cull mode. - * @param faceCullMode - * the face cull mode - */ - public void setFaceCullMode(FaceCullMode faceCullMode) { - this.faceCullMode = faceCullMode; - } - - /** - * This method returns the diffuse color. - * - * @param materialStructure - * the material structure - * @param diffuseShader - * the diffuse shader - * @return the diffuse color - */ - private ColorRGBA readDiffuseColor(Structure materialStructure, DiffuseShader diffuseShader) { - // bitwise 'or' of all textures mappings - int commonMapto = ((Number) materialStructure.getFieldValue("mapto")).intValue(); - - // diffuse color - float r = ((Number) materialStructure.getFieldValue("r")).floatValue(); - float g = ((Number) materialStructure.getFieldValue("g")).floatValue(); - float b = ((Number) materialStructure.getFieldValue("b")).floatValue(); - float alpha = ((Number) materialStructure.getFieldValue("alpha")).floatValue(); - if ((commonMapto & 0x01) == 0x01) {// Col - return new ColorRGBA(r, g, b, alpha); - } else { - switch (diffuseShader) { - case FRESNEL: - case ORENNAYAR: - case TOON: - break;// TODO: find what is the proper modification - case MINNAERT: - case LAMBERT:// TODO: check if that is correct - float ref = ((Number) materialStructure.getFieldValue("ref")).floatValue(); - r *= ref; - g *= ref; - b *= ref; - break; - default: - throw new IllegalStateException("Unknown diffuse shader type: " + diffuseShader.toString()); - } - return new ColorRGBA(r, g, b, alpha); - } - } - - /** - * This method returns a specular color used by the material. - * - * @param materialStructure - * the material structure filled with data - * @return a specular color used by the material - */ - private ColorRGBA readSpecularColor(Structure materialStructure) { - float specularIntensity = ((Number) materialStructure.getFieldValue("spec")).floatValue(); - float r = ((Number) materialStructure.getFieldValue("specr")).floatValue() * specularIntensity; - float g = ((Number) materialStructure.getFieldValue("specg")).floatValue() * specularIntensity; - float b = ((Number) materialStructure.getFieldValue("specb")).floatValue() * specularIntensity; - float alpha = ((Number) materialStructure.getFieldValue("alpha")).floatValue(); - return new ColorRGBA(r, g, b, alpha); - } - - @Override - public void write(JmeExporter e) throws IOException { - throw new IOException("Material context is not for saving! It implements savable only to be passed to another blend file as a Savable in user data!"); - } - - @Override - public void read(JmeImporter e) throws IOException { - throw new IOException("Material context is not for loading! It implements savable only to be passed to another blend file as a Savable in user data!"); - } -} diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/materials/MaterialHelper.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/materials/MaterialHelper.java deleted file mode 100644 index 50aa6275e..000000000 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/materials/MaterialHelper.java +++ /dev/null @@ -1,387 +0,0 @@ -/* - * Copyright (c) 2009-2020 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.blender.materials; - -import java.nio.ByteBuffer; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.logging.Level; -import java.util.logging.Logger; - -import com.jme3.material.MatParam; -import com.jme3.material.Material; -import com.jme3.math.ColorRGBA; -import com.jme3.math.FastMath; -import com.jme3.scene.plugins.blender.AbstractBlenderHelper; -import com.jme3.scene.plugins.blender.BlenderContext; -import com.jme3.scene.plugins.blender.BlenderContext.LoadedDataType; -import com.jme3.scene.plugins.blender.file.BlenderFileException; -import com.jme3.scene.plugins.blender.file.Pointer; -import com.jme3.scene.plugins.blender.file.Structure; -import com.jme3.shader.VarType; -import com.jme3.texture.Image; -import com.jme3.texture.Image.Format; -import com.jme3.texture.image.ColorSpace; -import com.jme3.texture.Texture; -import com.jme3.util.BufferUtils; - -public class MaterialHelper extends AbstractBlenderHelper { - private static final Logger LOGGER = Logger.getLogger(MaterialHelper.class.getName()); - protected static final float DEFAULT_SHININESS = 20.0f; - - public static final String TEXTURE_TYPE_COLOR = "ColorMap"; - public static final String TEXTURE_TYPE_DIFFUSE = "DiffuseMap"; - public static final String TEXTURE_TYPE_NORMAL = "NormalMap"; - public static final String TEXTURE_TYPE_SPECULAR = "SpecularMap"; - public static final String TEXTURE_TYPE_GLOW = "GlowMap"; - public static final String TEXTURE_TYPE_ALPHA = "AlphaMap"; - public static final String TEXTURE_TYPE_LIGHTMAP = "LightMap"; - - public static final Integer ALPHA_MASK_NONE = Integer.valueOf(0); - public static final Integer ALPHA_MASK_CIRCLE = Integer.valueOf(1); - public static final Integer ALPHA_MASK_CONE = Integer.valueOf(2); - public static final Integer ALPHA_MASK_HYPERBOLE = Integer.valueOf(3); - protected final Map alphaMasks = new HashMap(); - - /** - * The type of the material's diffuse shader. - */ - public static enum DiffuseShader { - LAMBERT, ORENNAYAR, TOON, MINNAERT, FRESNEL - } - - /** - * The type of the material's specular shader. - */ - public static enum SpecularShader { - COOKTORRENCE, PHONG, BLINN, TOON, WARDISO - } - - /** - * This constructor parses the given blender version and stores the result. Some functionalities may differ in different blender - * versions. - * - * @param blenderVersion - * the version read from the blend file - * @param blenderContext - * the blender context - */ - public MaterialHelper(String blenderVersion, BlenderContext blenderContext) { - super(blenderVersion, blenderContext); - // setting alpha masks - alphaMasks.put(ALPHA_MASK_NONE, new IAlphaMask() { - @Override - public void setImageSize(int width, int height) { - } - - @Override - public byte getAlpha(float x, float y) { - return (byte) 255; - } - }); - alphaMasks.put(ALPHA_MASK_CIRCLE, new IAlphaMask() { - private float r; - private float[] center; - - @Override - public void setImageSize(int width, int height) { - r = Math.min(width, height) * 0.5f; - center = new float[] { width * 0.5f, height * 0.5f }; - } - - @Override - public byte getAlpha(float x, float y) { - float d = FastMath.abs(FastMath.sqrt((x - center[0]) * (x - center[0]) + (y - center[1]) * (y - center[1]))); - return (byte) (d >= r ? 0 : 255); - } - }); - alphaMasks.put(ALPHA_MASK_CONE, new IAlphaMask() { - private float r; - private float[] center; - - @Override - public void setImageSize(int width, int height) { - r = Math.min(width, height) * 0.5f; - center = new float[] { width * 0.5f, height * 0.5f }; - } - - @Override - public byte getAlpha(float x, float y) { - float d = FastMath.abs(FastMath.sqrt((x - center[0]) * (x - center[0]) + (y - center[1]) * (y - center[1]))); - return (byte) (d >= r ? 0 : -255.0f * d / r + 255.0f); - } - }); - alphaMasks.put(ALPHA_MASK_HYPERBOLE, new IAlphaMask() { - private float r; - private float[] center; - - @Override - public void setImageSize(int width, int height) { - r = Math.min(width, height) * 0.5f; - center = new float[] { width * 0.5f, height * 0.5f }; - } - - @Override - public byte getAlpha(float x, float y) { - float d = FastMath.abs(FastMath.sqrt((x - center[0]) * (x - center[0]) + (y - center[1]) * (y - center[1]))) / r; - return d >= 1.0f ? 0 : (byte) ((-FastMath.sqrt((2.0f - d) * d) + 1.0f) * 255.0f); - } - }); - } - - /** - * This method converts the material structure to jme Material. - * @param structure - * structure with material data - * @param blenderContext - * the blender context - * @return jme material - * @throws BlenderFileException - * an exception is throw when problems with blend file occur - */ - public MaterialContext toMaterialContext(Structure structure, BlenderContext blenderContext) throws BlenderFileException { - MaterialContext result = (MaterialContext) blenderContext.getLoadedFeature(structure.getOldMemoryAddress(), LoadedDataType.FEATURE); - if (result != null) { - return result; - } - - if ("ID".equals(structure.getType())) { - LOGGER.fine("Loading material from external blend file."); - return (MaterialContext) this.loadLibrary(structure); - } - - LOGGER.fine("Loading material."); - result = new MaterialContext(structure, blenderContext); - LOGGER.log(Level.FINE, "Material''s name: {0}", result.name); - Long oma = structure.getOldMemoryAddress(); - blenderContext.addLoadedFeatures(oma, LoadedDataType.STRUCTURE, structure); - blenderContext.addLoadedFeatures(oma, LoadedDataType.FEATURE, result); - return result; - } - - /** - * This method converts the given material into particles-usable material. - * The texture and glow color are being copied. - * The method assumes it receives the Lighting type of material. - * @param material - * the source material - * @param blenderContext - * the blender context - * @return material converted into particles-usable material - */ - public Material getParticlesMaterial(Material material, Integer alphaMaskIndex, BlenderContext blenderContext) { - Material result = new Material(blenderContext.getAssetManager(), "Common/MatDefs/Misc/Particle.j3md"); - - // copying texture - MatParam diffuseMap = material.getParam("DiffuseMap"); - if (diffuseMap != null) { - Texture texture = ((Texture) diffuseMap.getValue()).clone(); - - // applying alpha mask to the texture - Image image = texture.getImage(); - ByteBuffer sourceBB = image.getData(0); - sourceBB.rewind(); - int w = image.getWidth(); - int h = image.getHeight(); - ByteBuffer bb = BufferUtils.createByteBuffer(w * h * 4); - IAlphaMask iAlphaMask = alphaMasks.get(alphaMaskIndex); - iAlphaMask.setImageSize(w, h); - - for (int x = 0; x < w; ++x) { - for (int y = 0; y < h; ++y) { - bb.put(sourceBB.get()); - bb.put(sourceBB.get()); - bb.put(sourceBB.get()); - bb.put(iAlphaMask.getAlpha(x, y)); - } - } - - image = new Image(Format.RGBA8, w, h, bb, ColorSpace.Linear); - texture.setImage(image); - - result.setTextureParam("Texture", VarType.Texture2D, texture); - } - - // copying glow color - MatParam glowColor = material.getParam("GlowColor"); - if (glowColor != null) { - ColorRGBA color = (ColorRGBA) glowColor.getValue(); - result.setParam("GlowColor", VarType.Vector3, color); - } - return result; - } - - /** - * This method returns the table of materials connected to the specified structure. The given structure can be of any type (ie. mesh or - * curve) but needs to have 'mat' field/ - * - * @param structureWithMaterials - * the structure containing the mesh data - * @param blenderContext - * the blender context - * @return a list of vertices colors, each color belongs to a single vertex - * @throws BlenderFileException - * this exception is thrown when the blend file structure is somehow invalid or corrupted - */ - public MaterialContext[] getMaterials(Structure structureWithMaterials, BlenderContext blenderContext) throws BlenderFileException { - Pointer ppMaterials = (Pointer) structureWithMaterials.getFieldValue("mat"); - MaterialContext[] materials = null; - if (ppMaterials.isNotNull()) { - List materialStructures = ppMaterials.fetchData(); - if (materialStructures != null && materialStructures.size() > 0) { - MaterialHelper materialHelper = blenderContext.getHelper(MaterialHelper.class); - materials = new MaterialContext[materialStructures.size()]; - int i = 0; - for (Structure s : materialStructures) { - materials[i++] = s == null ? null : materialHelper.toMaterialContext(s, blenderContext); - } - } - } - return materials; - } - - /** - * This method converts rgb values to hsv values. - * - * @param r - * red value of the color - * @param g - * green value of the color - * @param b - * blue value of the color - * @param hsv - * hsv values of a color (this table contains the result of the transformation) - */ - public void rgbToHsv(float r, float g, float b, float[] hsv) { - float cmax = r; - float cmin = r; - cmax = g > cmax ? g : cmax; - cmin = g < cmin ? g : cmin; - cmax = b > cmax ? b : cmax; - cmin = b < cmin ? b : cmin; - - hsv[2] = cmax; /* value */ - if (cmax != 0.0) { - hsv[1] = (cmax - cmin) / cmax; - } else { - hsv[1] = 0.0f; - hsv[0] = 0.0f; - } - if (hsv[1] == 0.0) { - hsv[0] = -1.0f; - } else { - float cdelta = cmax - cmin; - float rc = (cmax - r) / cdelta; - float gc = (cmax - g) / cdelta; - float bc = (cmax - b) / cdelta; - if (r == cmax) { - hsv[0] = bc - gc; - } else if (g == cmax) { - hsv[0] = 2.0f + rc - bc; - } else { - hsv[0] = 4.0f + gc - rc; - } - hsv[0] *= 60.0f; - if (hsv[0] < 0.0f) { - hsv[0] += 360.0f; - } - } - - hsv[0] /= 360.0f; - if (hsv[0] < 0.0f) { - hsv[0] = 0.0f; - } - } - - /** - * This method converts rgb values to hsv values. - * - * @param h - * hue - * @param s - * saturation - * @param v - * value - * @param rgb - * rgb result vector (should have 3 elements) - */ - public void hsvToRgb(float h, float s, float v, float[] rgb) { - h *= 360.0f; - if (s == 0.0) { - rgb[0] = rgb[1] = rgb[2] = v; - } else { - if (h == 360) { - h = 0; - } else { - h /= 60; - } - int i = (int) Math.floor(h); - float f = h - i; - float p = v * (1.0f - s); - float q = v * (1.0f - s * f); - float t = v * (1.0f - s * (1.0f - f)); - switch (i) { - case 0: - rgb[0] = v; - rgb[1] = t; - rgb[2] = p; - break; - case 1: - rgb[0] = q; - rgb[1] = v; - rgb[2] = p; - break; - case 2: - rgb[0] = p; - rgb[1] = v; - rgb[2] = t; - break; - case 3: - rgb[0] = p; - rgb[1] = q; - rgb[2] = v; - break; - case 4: - rgb[0] = t; - rgb[1] = p; - rgb[2] = v; - break; - case 5: - rgb[0] = v; - rgb[1] = p; - rgb[2] = q; - break; - } - } - } -} diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/math/DQuaternion.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/math/DQuaternion.java deleted file mode 100644 index 11715434d..000000000 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/math/DQuaternion.java +++ /dev/null @@ -1,583 +0,0 @@ -/* - * Copyright (c) 2009-2020 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.blender.math; - -import java.io.IOException; - -import com.jme3.export.InputCapsule; -import com.jme3.export.JmeExporter; -import com.jme3.export.JmeImporter; -import com.jme3.export.OutputCapsule; -import com.jme3.export.Savable; -import com.jme3.math.Quaternion; - -/** - * DQuaternion defines a single example of a more general class of - * hypercomplex numbers. DQuaternions extends a rotation in three dimensions to a - * rotation in four dimensions. This avoids "gimbal lock" and allows for smooth - * continuous rotation. - * - * DQuaternion is defined by four double point numbers: {x y z w}. - * - * This class's only purpose is to give better accuracy in floating point operations during computations. - * This is made by copying the original Quaternion class from jme3 core and leaving only required methods and basic computation methods, so that - * the class is smaller and easier to maintain. - * Should any other methods be needed, they will be added. - * - * @author Mark Powell - * @author Joshua Slack - * @author Marcin Roguski (Kaelthas) - */ -public final class DQuaternion implements Savable, Cloneable, java.io.Serializable { - private static final long serialVersionUID = 5009180713885017539L; - - /** - * Represents the identity quaternion rotation (0, 0, 0, 1). - */ - public static final DQuaternion IDENTITY = new DQuaternion(); - public static final DQuaternion DIRECTION_Z = new DQuaternion(); - public static final DQuaternion ZERO = new DQuaternion(0, 0, 0, 0); - protected double x, y, z, w = 1; - - /** - * Constructor instantiates a new DQuaternion object - * initializing all values to zero, except w which is initialized to 1. - * - */ - public DQuaternion() { - } - - /** - * Constructor instantiates a new DQuaternion object from the - * given list of parameters. - * - * @param x - * the x value of the quaternion. - * @param y - * the y value of the quaternion. - * @param z - * the z value of the quaternion. - * @param w - * the w value of the quaternion. - */ - public DQuaternion(double x, double y, double z, double w) { - this.set(x, y, z, w); - } - - public DQuaternion(Quaternion q) { - this(q.getX(), q.getY(), q.getZ(), q.getW()); - } - - public Quaternion toQuaternion() { - return new Quaternion((float) x, (float) y, (float) z, (float) w); - } - - public double getX() { - return x; - } - - public double getY() { - return y; - } - - public double getZ() { - return z; - } - - public double getW() { - return w; - } - - /** - * sets the data in a DQuaternion object from the given list - * of parameters. - * - * @param x - * the x value of the quaternion. - * @param y - * the y value of the quaternion. - * @param z - * the z value of the quaternion. - * @param w - * the w value of the quaternion. - * @return this - */ - public DQuaternion set(double x, double y, double z, double w) { - this.x = x; - this.y = y; - this.z = z; - this.w = w; - return this; - } - - /** - * Sets the data in this DQuaternion object to be equal to the - * passed DQuaternion object. The values are copied producing - * a new object. - * - * @param q - * The DQuaternion to copy values from. - * @return this - */ - public DQuaternion set(DQuaternion q) { - x = q.x; - y = q.y; - z = q.z; - w = q.w; - return this; - } - - /** - * Sets this DQuaternion to {0, 0, 0, 1}. Same as calling set(0,0,0,1). - */ - public void loadIdentity() { - x = y = z = 0; - w = 1; - } - - /** - * norm returns the norm of this quaternion. This is the dot - * product of this quaternion with itself. - * - * @return the norm of the quaternion. - */ - public double norm() { - return w * w + x * x + y * y + z * z; - } - - public DQuaternion fromRotationMatrix(double m00, double m01, double m02, - double m10, double m11, double m12, double m20, double m21, double m22) { - // first normalize the forward (F), up (U) and side (S) vectors of the rotation matrix - // so that the scale does not affect the rotation - double lengthSquared = m00 * m00 + m10 * m10 + m20 * m20; - if (lengthSquared != 1f && lengthSquared != 0f) { - lengthSquared = 1.0 / Math.sqrt(lengthSquared); - m00 *= lengthSquared; - m10 *= lengthSquared; - m20 *= lengthSquared; - } - lengthSquared = m01 * m01 + m11 * m11 + m21 * m21; - if (lengthSquared != 1 && lengthSquared != 0f) { - lengthSquared = 1.0 / Math.sqrt(lengthSquared); - m01 *= lengthSquared; - m11 *= lengthSquared; - m21 *= lengthSquared; - } - lengthSquared = m02 * m02 + m12 * m12 + m22 * m22; - if (lengthSquared != 1f && lengthSquared != 0f) { - lengthSquared = 1.0 / Math.sqrt(lengthSquared); - m02 *= lengthSquared; - m12 *= lengthSquared; - m22 *= lengthSquared; - } - - // Use the Graphics Gems code, from - // ftp://ftp.cis.upenn.edu/pub/graphics/shoemake/quatut.ps.Z - // *NOT* the "Matrix and Quaternions FAQ", which has errors! - - // the trace is the sum of the diagonal elements; see - // http://mathworld.wolfram.com/MatrixTrace.html - double t = m00 + m11 + m22; - - // we protect the division by s by ensuring that s>=1 - if (t >= 0) { // |w| >= .5 - double s = Math.sqrt(t + 1); // |s|>=1 ... - w = 0.5f * s; - s = 0.5f / s; // so this division isn't bad - x = (m21 - m12) * s; - y = (m02 - m20) * s; - z = (m10 - m01) * s; - } else if (m00 > m11 && m00 > m22) { - double s = Math.sqrt(1.0 + m00 - m11 - m22); // |s|>=1 - x = s * 0.5f; // |x| >= .5 - s = 0.5f / s; - y = (m10 + m01) * s; - z = (m02 + m20) * s; - w = (m21 - m12) * s; - } else if (m11 > m22) { - double s = Math.sqrt(1.0 + m11 - m00 - m22); // |s|>=1 - y = s * 0.5f; // |y| >= .5 - s = 0.5f / s; - x = (m10 + m01) * s; - z = (m21 + m12) * s; - w = (m02 - m20) * s; - } else { - double s = Math.sqrt(1.0 + m22 - m00 - m11); // |s|>=1 - z = s * 0.5f; // |z| >= .5 - s = 0.5f / s; - x = (m02 + m20) * s; - y = (m21 + m12) * s; - w = (m10 - m01) * s; - } - - return this; - } - - /** - * toRotationMatrix converts this quaternion to a rotational - * matrix. The result is stored in result. 4th row and 4th column values are - * untouched. Note: the result is created from a normalized version of this quat. - * - * @param result - * The Matrix4f to store the result in. - * @return the rotation matrix representation of this quaternion. - */ - public Matrix toRotationMatrix(Matrix result) { - Vector3d originalScale = new Vector3d(); - - result.toScaleVector(originalScale); - result.setScale(1, 1, 1); - double norm = this.norm(); - // we explicitly test norm against one here, saving a division - // at the cost of a test and branch. Is it worth it? - double s = norm == 1f ? 2f : norm > 0f ? 2f / norm : 0; - - // compute xs/ys/zs first to save 6 multiplications, since xs/ys/zs - // will be used 2-4 times each. - double xs = x * s; - double ys = y * s; - double zs = z * s; - double xx = x * xs; - double xy = x * ys; - double xz = x * zs; - double xw = w * xs; - double yy = y * ys; - double yz = y * zs; - double yw = w * ys; - double zz = z * zs; - double zw = w * zs; - - // using s=2/norm (instead of 1/norm) saves 9 multiplications by 2 here - result.set(0, 0, 1 - (yy + zz)); - result.set(0, 1, xy - zw); - result.set(0, 2, xz + yw); - result.set(1, 0, xy + zw); - result.set(1, 1, 1 - (xx + zz)); - result.set(1, 2, yz - xw); - result.set(2, 0, xz - yw); - result.set(2, 1, yz + xw); - result.set(2, 2, 1 - (xx + yy)); - - result.setScale(originalScale); - - return result; - } - - /** - * fromAngleAxis sets this quaternion to the values specified - * by an angle and an axis of rotation. This method creates an object, so - * use fromAngleNormalAxis if your axis is already normalized. - * - * @param angle - * the angle to rotate (in radians). - * @param axis - * the axis of rotation. - * @return this quaternion - */ - public DQuaternion fromAngleAxis(double angle, Vector3d axis) { - Vector3d normAxis = axis.normalize(); - this.fromAngleNormalAxis(angle, normAxis); - return this; - } - - /** - * fromAngleNormalAxis sets this quaternion to the values - * specified by an angle and a normalized axis of rotation. - * - * @param angle - * the angle to rotate (in radians). - * @param axis - * the axis of rotation (already normalized). - */ - public DQuaternion fromAngleNormalAxis(double angle, Vector3d axis) { - if (axis.x == 0 && axis.y == 0 && axis.z == 0) { - this.loadIdentity(); - } else { - double halfAngle = 0.5f * angle; - double sin = Math.sin(halfAngle); - w = Math.cos(halfAngle); - x = sin * axis.x; - y = sin * axis.y; - z = sin * axis.z; - } - return this; - } - - /** - * add adds the values of this quaternion to those of the - * parameter quaternion. The result is returned as a new quaternion. - * - * @param q - * the quaternion to add to this. - * @return the new quaternion. - */ - public DQuaternion add(DQuaternion q) { - return new DQuaternion(x + q.x, y + q.y, z + q.z, w + q.w); - } - - /** - * add adds the values of this quaternion to those of the - * parameter quaternion. The result is stored in this DQuaternion. - * - * @param q - * the quaternion to add to this. - * @return This DQuaternion after addition. - */ - public DQuaternion addLocal(DQuaternion q) { - x += q.x; - y += q.y; - z += q.z; - w += q.w; - return this; - } - - /** - * subtract subtracts the values of the parameter quaternion - * from those of this quaternion. The result is returned as a new - * quaternion. - * - * @param q - * the quaternion to subtract from this. - * @return the new quaternion. - */ - public DQuaternion subtract(DQuaternion q) { - return new DQuaternion(x - q.x, y - q.y, z - q.z, w - q.w); - } - - /** - * subtract subtracts the values of the parameter quaternion - * from those of this quaternion. The result is stored in this DQuaternion. - * - * @param q - * the quaternion to subtract from this. - * @return This DQuaternion after subtraction. - */ - public DQuaternion subtractLocal(DQuaternion q) { - x -= q.x; - y -= q.y; - z -= q.z; - w -= q.w; - return this; - } - - /** - * mult multiplies this quaternion by a parameter quaternion. - * The result is returned as a new quaternion. It should be noted that - * quaternion multiplication is not commutative so q * p != p * q. - * - * @param q - * the quaternion to multiply this quaternion by. - * @return the new quaternion. - */ - public DQuaternion mult(DQuaternion q) { - return this.mult(q, null); - } - - /** - * mult multiplies this quaternion by a parameter quaternion. - * The result is returned as a new quaternion. It should be noted that - * quaternion multiplication is not commutative so q * p != p * q. - * - * It IS safe for q and res to be the same object. - * It IS NOT safe for this and res to be the same object. - * - * @param q - * the quaternion to multiply this quaternion by. - * @param res - * the quaternion to store the result in. - * @return the new quaternion. - */ - public DQuaternion mult(DQuaternion q, DQuaternion res) { - if (res == null) { - res = new DQuaternion(); - } - double qw = q.w, qx = q.x, qy = q.y, qz = q.z; - res.x = x * qw + y * qz - z * qy + w * qx; - res.y = -x * qz + y * qw + z * qx + w * qy; - res.z = x * qy - y * qx + z * qw + w * qz; - res.w = -x * qx - y * qy - z * qz + w * qw; - return res; - } - - /** - * mult multiplies this quaternion by a parameter vector. The - * result is returned as a new vector. - * - * @param v - * the vector to multiply this quaternion by. - * @return the new vector. - */ - public Vector3d mult(Vector3d v) { - return this.mult(v, null); - } - - /** - * Multiplies this DQuaternion by the supplied quaternion. The result is - * stored in this DQuaternion, which is also returned for chaining. Similar - * to this *= q. - * - * @param q - * The DQuaternion to multiply this one by. - * @return This DQuaternion, after multiplication. - */ - public DQuaternion multLocal(DQuaternion q) { - double x1 = x * q.w + y * q.z - z * q.y + w * q.x; - double y1 = -x * q.z + y * q.w + z * q.x + w * q.y; - double z1 = x * q.y - y * q.x + z * q.w + w * q.z; - w = -x * q.x - y * q.y - z * q.z + w * q.w; - x = x1; - y = y1; - z = z1; - return this; - } - - /** - * mult multiplies this quaternion by a parameter vector. The - * result is returned as a new vector. - * - * @param v - * the vector to multiply this quaternion by. - * @param store - * the vector to store the result in. It IS safe for v and store - * to be the same object. - * @return the result vector. - */ - public Vector3d mult(Vector3d v, Vector3d store) { - if (store == null) { - store = new Vector3d(); - } - if (v.x == 0 && v.y == 0 && v.z == 0) { - store.set(0, 0, 0); - } else { - double vx = v.x, vy = v.y, vz = v.z; - store.x = w * w * vx + 2 * y * w * vz - 2 * z * w * vy + x * x * vx + 2 * y * x * vy + 2 * z * x * vz - z * z * vx - y * y * vx; - store.y = 2 * x * y * vx + y * y * vy + 2 * z * y * vz + 2 * w * z * vx - z * z * vy + w * w * vy - 2 * x * w * vz - x * x * vy; - store.z = 2 * x * z * vx + 2 * y * z * vy + z * z * vz - 2 * w * y * vx - y * y * vz + 2 * w * x * vy - x * x * vz + w * w * vz; - } - return store; - } - - /** - * - * toString creates the string representation of this DQuaternion. The values of the quaternion are displaced (x, - * y, z, w), in the following manner:
    - * (x, y, z, w) - * - * @return the string representation of this object. - * @see java.lang.Object#toString() - */ - @Override - public String toString() { - return "(" + x + ", " + y + ", " + z + ", " + w + ")"; - } - - /** - * equals determines if two quaternions are logically equal, - * that is, if the values of (x, y, z, w) are the same for both quaternions. - * - * @param o - * the object to compare for equality - * @return true if they are equal, false otherwise. - */ - @Override - public boolean equals(Object o) { - if (!(o instanceof DQuaternion)) { - return false; - } - - if (this == o) { - return true; - } - - DQuaternion comp = (DQuaternion) o; - if (Double.compare(x, comp.x) != 0) { - return false; - } - if (Double.compare(y, comp.y) != 0) { - return false; - } - if (Double.compare(z, comp.z) != 0) { - return false; - } - if (Double.compare(w, comp.w) != 0) { - return false; - } - return true; - } - - /** - * - * hashCode returns the hash code value as an integer and is - * supported for the benefit of hashing based collection classes such as - * Hashtable, HashMap, HashSet etc. - * - * @return the hashcode for this instance of DQuaternion. - * @see java.lang.Object#hashCode() - */ - @Override - public int hashCode() { - long hash = 37; - hash = 37 * hash + Double.doubleToLongBits(x); - hash = 37 * hash + Double.doubleToLongBits(y); - hash = 37 * hash + Double.doubleToLongBits(z); - hash = 37 * hash + Double.doubleToLongBits(w); - return (int) hash; - - } - - @Override - public void write(JmeExporter e) throws IOException { - OutputCapsule cap = e.getCapsule(this); - cap.write(x, "x", 0); - cap.write(y, "y", 0); - cap.write(z, "z", 0); - cap.write(w, "w", 1); - } - - @Override - public void read(JmeImporter e) throws IOException { - InputCapsule cap = e.getCapsule(this); - x = cap.readFloat("x", 0); - y = cap.readFloat("y", 0); - z = cap.readFloat("z", 0); - w = cap.readFloat("w", 1); - } - - @Override - public DQuaternion clone() { - try { - return (DQuaternion) super.clone(); - } catch (CloneNotSupportedException e) { - throw new AssertionError(); // can not happen - } - } -} diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/math/DTransform.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/math/DTransform.java deleted file mode 100644 index ea2440e58..000000000 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/math/DTransform.java +++ /dev/null @@ -1,190 +0,0 @@ -/* - * Copyright (c) 2009-2020 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.blender.math; - -import java.io.IOException; - -import com.jme3.export.InputCapsule; -import com.jme3.export.JmeExporter; -import com.jme3.export.JmeImporter; -import com.jme3.export.OutputCapsule; -import com.jme3.export.Savable; -import com.jme3.math.Transform; - -/** - * Started Date: Jul 16, 2004
    - *
    - * Represents a translation, rotation and scale in one object. - * - * This class's only purpose is to give better accuracy in floating point operations during computations. - * This is made by copying the original Transfrom class from jme3 core and removing unnecessary methods so that - * the class is smaller and easier to maintain. - * Should any other methods be needed, they will be added. - * - * @author Jack Lindamood - * @author Joshua Slack - * @author Marcin Roguski (Kaelthas) - */ -public final class DTransform implements Savable, Cloneable, java.io.Serializable { - private static final long serialVersionUID = 7812915425940606722L; - - private DQuaternion rotation; - private Vector3d translation; - private Vector3d scale; - - public DTransform() { - translation = new Vector3d(); - rotation = new DQuaternion(); - scale = new Vector3d(); - } - - public DTransform(Transform transform) { - translation = new Vector3d(transform.getTranslation()); - rotation = new DQuaternion(transform.getRotation()); - scale = new Vector3d(transform.getScale()); - } - - public Transform toTransform() { - return new Transform(translation.toVector3f(), rotation.toQuaternion(), scale.toVector3f()); - } - - public Matrix toMatrix() { - Matrix m = Matrix.identity(4); - m.setTranslation(translation); - m.setRotationQuaternion(rotation); - m.setScale(scale); - return m; - } - - /** - * Sets this translation to the given value. - * @param trans - * The new translation for this matrix. - * @return this - */ - public DTransform setTranslation(Vector3d trans) { - translation.set(trans); - return this; - } - - /** - * Sets this rotation to the given DQuaternion value. - * @param rot - * The new rotation for this matrix. - * @return this - */ - public DTransform setRotation(DQuaternion rot) { - rotation.set(rot); - return this; - } - - /** - * Sets this scale to the given value. - * @param scale - * The new scale for this matrix. - * @return this - */ - public DTransform setScale(Vector3d scale) { - this.scale.set(scale); - return this; - } - - /** - * Sets this scale to the given value. - * @param scale - * The new scale for this matrix. - * @return this - */ - public DTransform setScale(float scale) { - this.scale.set(scale, scale, scale); - return this; - } - - /** - * Return the translation vector in this matrix. - * @return translation vector. - */ - public Vector3d getTranslation() { - return translation; - } - - /** - * Return the rotation quaternion in this matrix. - * @return rotation quaternion. - */ - public DQuaternion getRotation() { - return rotation; - } - - /** - * Return the scale vector in this matrix. - * @return scale vector. - */ - public Vector3d getScale() { - return scale; - } - - @Override - public String toString() { - return this.getClass().getSimpleName() + "[ " + translation.x + ", " + translation.y + ", " + translation.z + "]\n" + "[ " + rotation.x + ", " + rotation.y + ", " + rotation.z + ", " + rotation.w + "]\n" + "[ " + scale.x + " , " + scale.y + ", " + scale.z + "]"; - } - - @Override - public void write(JmeExporter e) throws IOException { - OutputCapsule capsule = e.getCapsule(this); - capsule.write(rotation, "rot", new DQuaternion()); - capsule.write(translation, "translation", Vector3d.ZERO); - capsule.write(scale, "scale", Vector3d.UNIT_XYZ); - } - - @Override - public void read(JmeImporter e) throws IOException { - InputCapsule capsule = e.getCapsule(this); - - rotation = (DQuaternion) capsule.readSavable("rot", new DQuaternion()); - translation = (Vector3d) capsule.readSavable("translation", Vector3d.ZERO); - scale = (Vector3d) capsule.readSavable("scale", Vector3d.UNIT_XYZ); - } - - @Override - public DTransform clone() { - try { - DTransform tq = (DTransform) super.clone(); - tq.rotation = rotation.clone(); - tq.scale = scale.clone(); - tq.translation = translation.clone(); - return tq; - } catch (CloneNotSupportedException e) { - throw new AssertionError(); - } - } -} diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/math/Matrix.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/math/Matrix.java deleted file mode 100644 index 0b05f2f1f..000000000 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/math/Matrix.java +++ /dev/null @@ -1,210 +0,0 @@ -package com.jme3.scene.plugins.blender.math; - -import java.text.DecimalFormat; - -import org.ejml.ops.CommonOps; -import org.ejml.simple.SimpleMatrix; -import org.ejml.simple.SimpleSVD; - -import com.jme3.math.FastMath; - -/** - * Encapsulates a 4x4 matrix - * - * - */ -public class Matrix extends SimpleMatrix { - private static final long serialVersionUID = 2396600537315902559L; - - public Matrix(int rows, int cols) { - super(rows, cols); - } - - /** - * Copy constructor - */ - public Matrix(SimpleMatrix m) { - super(m); - } - - public Matrix(double[][] data) { - super(data); - } - - public static Matrix identity(int size) { - Matrix result = new Matrix(size, size); - CommonOps.setIdentity(result.mat); - return result; - } - - public Matrix pseudoinverse() { - return this.pseudoinverse(1); - } - - @SuppressWarnings("unchecked") - public Matrix pseudoinverse(double lambda) { - SimpleSVD simpleSVD = this.svd(); - - SimpleMatrix U = simpleSVD.getU(); - SimpleMatrix S = simpleSVD.getW(); - SimpleMatrix V = simpleSVD.getV(); - - int N = Math.min(this.numRows(),this.numCols()); - double maxSingular = 0; - for( int i = 0; i < N; ++i ) { - if( S.get(i, i) > maxSingular ) { - maxSingular = S.get(i, i); - } - } - - double tolerance = FastMath.DBL_EPSILON * Math.max(this.numRows(),this.numCols()) * maxSingular; - for(int i=0;isetRotationQuaternion builds a rotation from a - * Quaternion. - * - * @param quat - * the quaternion to build the rotation from. - * @throws NullPointerException - * if quat is null. - */ - public void setRotationQuaternion(DQuaternion quat) { - quat.toRotationMatrix(this); - } - - public DTransform toTransform() { - DTransform result = new DTransform(); - result.setTranslation(this.toTranslationVector()); - result.setRotation(this.toRotationQuat()); - result.setScale(this.toScaleVector()); - return result; - } - - public Vector3d toTranslationVector() { - return new Vector3d(this.get(0, 3), this.get(1, 3), this.get(2, 3)); - } - - public DQuaternion toRotationQuat() { - DQuaternion quat = new DQuaternion(); - quat.fromRotationMatrix(this.get(0, 0), this.get(0, 1), this.get(0, 2), this.get(1, 0), this.get(1, 1), this.get(1, 2), this.get(2, 0), this.get(2, 1), this.get(2, 2)); - return quat; - } - - /** - * Retrieves the scale vector from the matrix and stores it into a given - * vector. - */ - public Vector3d toScaleVector() { - Vector3d result = new Vector3d(); - this.toScaleVector(result); - return result; - } - - /** - * Retrieves the scale vector from the matrix and stores it into a given - * vector. - * - * @param vector the vector where the scale will be stored - */ - public void toScaleVector(Vector3d vector) { - double scaleX = Math.sqrt(this.get(0, 0) * this.get(0, 0) + this.get(1, 0) * this.get(1, 0) + this.get(2, 0) * this.get(2, 0)); - double scaleY = Math.sqrt(this.get(0, 1) * this.get(0, 1) + this.get(1, 1) * this.get(1, 1) + this.get(2, 1) * this.get(2, 1)); - double scaleZ = Math.sqrt(this.get(0, 2) * this.get(0, 2) + this.get(1, 2) * this.get(1, 2) + this.get(2, 2) * this.get(2, 2)); - vector.set(scaleX, scaleY, scaleZ); - } -} diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/math/Vector3d.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/math/Vector3d.java deleted file mode 100644 index ed3fca0f6..000000000 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/math/Vector3d.java +++ /dev/null @@ -1,869 +0,0 @@ -/* - * Copyright (c) 2009-2020 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.blender.math; - -import java.io.IOException; -import java.io.Serializable; -import java.util.logging.Logger; - -import com.jme3.export.InputCapsule; -import com.jme3.export.JmeExporter; -import com.jme3.export.JmeImporter; -import com.jme3.export.OutputCapsule; -import com.jme3.export.Savable; -import com.jme3.math.FastMath; -import com.jme3.math.Vector3f; - -/* - * -- Added *Local methods to cut down on object creation - JS - */ - -/** - * Vector3d defines a Vector for a three float value tuple. Vector3d can represent any three dimensional value, such as a - * vertex, a normal, etc. Utility methods are also included to aid in - * mathematical calculations. - * - * This class's only purpose is to give better accuracy in floating point operations during computations. - * This is made by copying the original Vector3f class from jme3 core and leaving only required methods and basic computation methods, so that - * the class is smaller and easier to maintain. - * Should any other methods be needed, they will be added. - * - * @author Mark Powell - * @author Joshua Slack - * @author Marcin Roguski (Kaelthas) - */ -public final class Vector3d implements Savable, Cloneable, Serializable { - private static final long serialVersionUID = 3090477054277293078L; - - private static final Logger LOGGER = Logger.getLogger(Vector3d.class.getName()); - - public final static Vector3d ZERO = new Vector3d(); - public final static Vector3d UNIT_XYZ = new Vector3d(1, 1, 1); - public final static Vector3d UNIT_X = new Vector3d(1, 0, 0); - public final static Vector3d UNIT_Y = new Vector3d(0, 1, 0); - public final static Vector3d UNIT_Z = new Vector3d(0, 0, 1); - - /** - * the x value of the vector. - */ - public double x; - - /** - * the y value of the vector. - */ - public double y; - - /** - * the z value of the vector. - */ - public double z; - - /** - * Constructor instantiates a new Vector3d with default - * values of (0,0,0). - * - */ - public Vector3d() { - } - - /** - * Constructor instantiates a new Vector3d with provides - * values. - * - * @param x - * the x value of the vector. - * @param y - * the y value of the vector. - * @param z - * the z value of the vector. - */ - public Vector3d(double x, double y, double z) { - this.x = x; - this.y = y; - this.z = z; - } - - /** - * Constructor instantiates a new Vector3d that is a copy - * of the provided vector - * @param vector3f - * The Vector3f to copy - */ - public Vector3d(Vector3f vector3f) { - this(vector3f.x, vector3f.y, vector3f.z); - } - - public Vector3f toVector3f() { - return new Vector3f((float) x, (float) y, (float) z); - } - - /** - * set sets the x,y,z values of the vector based on passed - * parameters. - * - * @param x - * the x value of the vector. - * @param y - * the y value of the vector. - * @param z - * the z value of the vector. - * @return this vector - */ - public Vector3d set(double x, double y, double z) { - this.x = x; - this.y = y; - this.z = z; - return this; - } - - /** - * set sets the x,y,z values of the vector by copying the - * supplied vector. - * - * @param vect - * the vector to copy. - * @return this vector - */ - public Vector3d set(Vector3d vect) { - return this.set(vect.x, vect.y, vect.z); - } - - /** - * - * add adds a provided vector to this vector creating a - * resultant vector which is returned. If the provided vector is null, null - * is returned. - * - * @param vec - * the vector to add to this. - * @return the resultant vector. - */ - public Vector3d add(Vector3d vec) { - if (null == vec) { - LOGGER.warning("Provided vector is null, null returned."); - return null; - } - return new Vector3d(x + vec.x, y + vec.y, z + vec.z); - } - - /** - * - * add adds the values of a provided vector storing the - * values in the supplied vector. - * - * @param vec - * the vector to add to this - * @param result - * the vector to store the result in - * @return result returns the supplied result vector. - */ - public Vector3d add(Vector3d vec, Vector3d result) { - result.x = x + vec.x; - result.y = y + vec.y; - result.z = z + vec.z; - return result; - } - - /** - * addLocal adds a provided vector to this vector internally, - * and returns a handle to this vector for easy chaining of calls. If the - * provided vector is null, null is returned. - * - * @param vec - * the vector to add to this vector. - * @return this - */ - public Vector3d addLocal(Vector3d vec) { - if (null == vec) { - LOGGER.warning("Provided vector is null, null returned."); - return null; - } - x += vec.x; - y += vec.y; - z += vec.z; - return this; - } - - /** - * - * add adds the provided values to this vector, creating a - * new vector that is then returned. - * - * @param addX - * the x value to add. - * @param addY - * the y value to add. - * @param addZ - * the z value to add. - * @return the result vector. - */ - public Vector3d add(double addX, double addY, double addZ) { - return new Vector3d(x + addX, y + addY, z + addZ); - } - - /** - * addLocal adds the provided values to this vector - * internally, and returns a handle to this vector for easy chaining of - * calls. - * - * @param addX - * value to add to x - * @param addY - * value to add to y - * @param addZ - * value to add to z - * @return this - */ - public Vector3d addLocal(double addX, double addY, double addZ) { - x += addX; - y += addY; - z += addZ; - return this; - } - - /** - * - * scaleAdd multiplies this vector by a scalar then adds the - * given Vector3d. - * - * @param scalar - * the value to multiply this vector by. - * @param add - * the value to add - */ - public Vector3d scaleAdd(double scalar, Vector3d add) { - x = x * scalar + add.x; - y = y * scalar + add.y; - z = z * scalar + add.z; - return this; - } - - /** - * - * scaleAdd multiplies the given vector by a scalar then adds - * the given vector. - * - * @param scalar - * the value to multiply this vector by. - * @param mult - * the value to multiply the scalar by - * @param add - * the value to add - */ - public Vector3d scaleAdd(double scalar, Vector3d mult, Vector3d add) { - x = mult.x * scalar + add.x; - y = mult.y * scalar + add.y; - z = mult.z * scalar + add.z; - return this; - } - - /** - * - * dot calculates the dot product of this vector with a - * provided vector. If the provided vector is null, 0 is returned. - * - * @param vec - * the vector to dot with this vector. - * @return the resultant dot product of this vector and a given vector. - */ - public double dot(Vector3d vec) { - if (null == vec) { - LOGGER.warning("Provided vector is null, 0 returned."); - return 0; - } - return x * vec.x + y * vec.y + z * vec.z; - } - - /** - * cross calculates the cross product of this vector with a - * parameter vector v. - * - * @param v - * the vector to take the cross product of with this. - * @return the cross product vector. - */ - public Vector3d cross(Vector3d v) { - return this.cross(v, null); - } - - /** - * cross calculates the cross product of this vector with a - * parameter vector v. The result is stored in result - * - * @param v - * the vector to take the cross product of with this. - * @param result - * the vector to store the cross product result. - * @return result, after receiving the cross product vector. - */ - public Vector3d cross(Vector3d v, Vector3d result) { - return this.cross(v.x, v.y, v.z, result); - } - - /** - * cross calculates the cross product of this vector with a - * parameter vector v. The result is stored in result - * - * @param otherX - * x component of the vector to take the cross product of with this. - * @param otherY - * y component of the vector to take the cross product of with this. - * @param otherZ - * z component of the vector to take the cross product of with this. - * @param result - * the vector to store the cross product result. - * @return result, after receiving the cross product vector. - */ - public Vector3d cross(double otherX, double otherY, double otherZ, Vector3d result) { - if (result == null) { - result = new Vector3d(); - } - double resX = y * otherZ - z * otherY; - double resY = z * otherX - x * otherZ; - double resZ = x * otherY - y * otherX; - result.set(resX, resY, resZ); - return result; - } - - /** - * crossLocal calculates the cross product of this vector - * with a parameter vector v. - * - * @param v - * the vector to take the cross product of with this. - * @return this. - */ - public Vector3d crossLocal(Vector3d v) { - return this.crossLocal(v.x, v.y, v.z); - } - - /** - * crossLocal calculates the cross product of this vector - * with a parameter vector v. - * - * @param otherX - * x component of the vector to take the cross product of with this. - * @param otherY - * y component of the vector to take the cross product of with this. - * @param otherZ - * z component of the vector to take the cross product of with this. - * @return this. - */ - public Vector3d crossLocal(double otherX, double otherY, double otherZ) { - double tempx = y * otherZ - z * otherY; - double tempy = z * otherX - x * otherZ; - z = x * otherY - y * otherX; - x = tempx; - y = tempy; - return this; - } - - /** - * length calculates the magnitude of this vector. - * - * @return the length or magnitude of the vector. - */ - public double length() { - return Math.sqrt(this.lengthSquared()); - } - - /** - * lengthSquared calculates the squared value of the - * magnitude of the vector. - * - * @return the magnitude squared of the vector. - */ - public double lengthSquared() { - return x * x + y * y + z * z; - } - - /** - * distanceSquared calculates the distance squared between - * this vector and vector v. - * - * @param v - * the second vector to determine the distance squared. - * @return the distance squared between the two vectors. - */ - public double distanceSquared(Vector3d v) { - double dx = x - v.x; - double dy = y - v.y; - double dz = z - v.z; - return dx * dx + dy * dy + dz * dz; - } - - /** - * distance calculates the distance between this vector and - * vector v. - * - * @param v - * the second vector to determine the distance. - * @return the distance between the two vectors. - */ - public double distance(Vector3d v) { - return Math.sqrt(this.distanceSquared(v)); - } - - /** - * - * mult multiplies this vector by a scalar. The resultant - * vector is returned. - * - * @param scalar - * the value to multiply this vector by. - * @return the new vector. - */ - public Vector3d mult(double scalar) { - return new Vector3d(x * scalar, y * scalar, z * scalar); - } - - /** - * - * mult multiplies this vector by a scalar. The resultant - * vector is supplied as the second parameter and returned. - * - * @param scalar - * the scalar to multiply this vector by. - * @param product - * the product to store the result in. - * @return product - */ - public Vector3d mult(double scalar, Vector3d product) { - if (null == product) { - product = new Vector3d(); - } - - product.x = x * scalar; - product.y = y * scalar; - product.z = z * scalar; - return product; - } - - /** - * multLocal multiplies this vector by a scalar internally, - * and returns a handle to this vector for easy chaining of calls. - * - * @param scalar - * the value to multiply this vector by. - * @return this - */ - public Vector3d multLocal(double scalar) { - x *= scalar; - y *= scalar; - z *= scalar; - return this; - } - - /** - * multLocal multiplies a provided vector by this vector - * internally, and returns a handle to this vector for easy chaining of - * calls. If the provided vector is null, null is returned. - * - * @param vec - * the vector to multiply by this vector. - * @return this - */ - public Vector3d multLocal(Vector3d vec) { - if (null == vec) { - LOGGER.warning("Provided vector is null, null returned."); - return null; - } - x *= vec.x; - y *= vec.y; - z *= vec.z; - return this; - } - - /** - * multLocal multiplies this vector by 3 scalars - * internally, and returns a handle to this vector for easy chaining of - * calls. - * - * @param x - * @param y - * @param z - * @return this - */ - public Vector3d multLocal(double x, double y, double z) { - this.x *= x; - this.y *= y; - this.z *= z; - return this; - } - - /** - * multLocal multiplies a provided vector by this vector - * internally, and returns a handle to this vector for easy chaining of - * calls. If the provided vector is null, null is returned. - * - * @param vec - * the vector to mult to this vector. - * @return this - */ - public Vector3d mult(Vector3d vec) { - if (null == vec) { - LOGGER.warning("Provided vector is null, null returned."); - return null; - } - return this.mult(vec, null); - } - - /** - * multLocal multiplies a provided vector by this vector - * internally, and returns a handle to this vector for easy chaining of - * calls. If the provided vector is null, null is returned. - * - * @param vec - * the vector to mult to this vector. - * @param store - * result vector (null to create a new vector) - * @return this - */ - public Vector3d mult(Vector3d vec, Vector3d store) { - if (null == vec) { - LOGGER.warning("Provided vector is null, null returned."); - return null; - } - if (store == null) { - store = new Vector3d(); - } - return store.set(x * vec.x, y * vec.y, z * vec.z); - } - - /** - * divide divides the values of this vector by a scalar and - * returns the result. The values of this vector remain untouched. - * - * @param scalar - * the value to divide this vectors attributes by. - * @return the result Vector. - */ - public Vector3d divide(double scalar) { - scalar = 1f / scalar; - return new Vector3d(x * scalar, y * scalar, z * scalar); - } - - /** - * divideLocal divides this vector by a scalar internally, - * and returns a handle to this vector for easy chaining of calls. Dividing - * by zero will result in an exception. - * - * @param scalar - * the value to divides this vector by. - * @return this - */ - public Vector3d divideLocal(double scalar) { - scalar = 1f / scalar; - x *= scalar; - y *= scalar; - z *= scalar; - return this; - } - - /** - * divide divides the values of this vector by a scalar and - * returns the result. The values of this vector remain untouched. - * - * @param scalar - * the value to divide this vectors attributes by. - * @return the result Vector. - */ - public Vector3d divide(Vector3d scalar) { - return new Vector3d(x / scalar.x, y / scalar.y, z / scalar.z); - } - - /** - * divideLocal divides this vector by a scalar internally, - * and returns a handle to this vector for easy chaining of calls. Dividing - * by zero will result in an exception. - * - * @param scalar - * the value to divides this vector by. - * @return this - */ - public Vector3d divideLocal(Vector3d scalar) { - x /= scalar.x; - y /= scalar.y; - z /= scalar.z; - return this; - } - - /** - * - * negate returns the negative of this vector. All values are - * negated and set to a new vector. - * - * @return the negated vector. - */ - public Vector3d negate() { - return new Vector3d(-x, -y, -z); - } - - /** - * - * negateLocal negates the internal values of this vector. - * - * @return this. - */ - public Vector3d negateLocal() { - x = -x; - y = -y; - z = -z; - return this; - } - - /** - * - * subtract subtracts the values of a given vector from those - * of this vector creating a new vector object. If the provided vector is - * null, null is returned. - * - * @param vec - * the vector to subtract from this vector. - * @return the result vector. - */ - public Vector3d subtract(Vector3d vec) { - return new Vector3d(x - vec.x, y - vec.y, z - vec.z); - } - - /** - * subtractLocal subtracts a provided vector from this vector - * internally, and returns a handle to this vector for easy chaining of - * calls. If the provided vector is null, null is returned. - * - * @param vec - * the vector to subtract - * @return this - */ - public Vector3d subtractLocal(Vector3d vec) { - if (null == vec) { - LOGGER.warning("Provided vector is null, null returned."); - return null; - } - x -= vec.x; - y -= vec.y; - z -= vec.z; - return this; - } - - /** - * - * subtract - * - * @param vec - * the vector to subtract from this - * @param result - * the vector to store the result in - * @return result - */ - public Vector3d subtract(Vector3d vec, Vector3d result) { - if (result == null) { - result = new Vector3d(); - } - result.x = x - vec.x; - result.y = y - vec.y; - result.z = z - vec.z; - return result; - } - - /** - * - * subtract subtracts the provided values from this vector, - * creating a new vector that is then returned. - * - * @param subtractX - * the x value to subtract. - * @param subtractY - * the y value to subtract. - * @param subtractZ - * the z value to subtract. - * @return the result vector. - */ - public Vector3d subtract(double subtractX, double subtractY, double subtractZ) { - return new Vector3d(x - subtractX, y - subtractY, z - subtractZ); - } - - /** - * subtractLocal subtracts the provided values from this vector - * internally, and returns a handle to this vector for easy chaining of - * calls. - * - * @param subtractX - * the x value to subtract. - * @param subtractY - * the y value to subtract. - * @param subtractZ - * the z value to subtract. - * @return this - */ - public Vector3d subtractLocal(double subtractX, double subtractY, double subtractZ) { - x -= subtractX; - y -= subtractY; - z -= subtractZ; - return this; - } - - /** - * normalize returns the unit vector of this vector. - * - * @return unit vector of this vector. - */ - public Vector3d normalize() { - double length = x * x + y * y + z * z; - if (length != 1f && length != 0f) { - length = 1.0f / Math.sqrt(length); - return new Vector3d(x * length, y * length, z * length); - } - return this.clone(); - } - - /** - * normalizeLocal makes this vector into a unit vector of - * itself. - * - * @return this. - */ - public Vector3d normalizeLocal() { - // NOTE: this implementation is more optimized - // than the old jme normalize as this method - // is commonly used. - double length = x * x + y * y + z * z; - if (length != 1f && length != 0f) { - length = 1.0f / Math.sqrt(length); - x *= length; - y *= length; - z *= length; - } - return this; - } - - /** - * angleBetween returns (in radians) the angle between two vectors. - * It is assumed that both this vector and the given vector are unit vectors (iow, normalized). - * - * @param otherVector - * a unit vector to find the angle against - * @return the angle in radians. - */ - public double angleBetween(Vector3d otherVector) { - double dot = this.dot(otherVector); - // the vectors are normalized, but if they are parallel then the dot product migh get a value like: 1.000000000000000002 - // which is caused by floating point operations; in such case, the acos function will return NaN so we need to clamp this value - dot = FastMath.clamp((float) dot, -1, 1); - return Math.acos(dot); - } - - @Override - public Vector3d clone() { - try { - return (Vector3d) super.clone(); - } catch (CloneNotSupportedException e) { - throw new AssertionError(); // can not happen - } - } - - /** - * are these two vectors the same? they are is they both have the same x,y, - * and z values. - * - * @param o - * the object to compare for equality - * @return true if they are equal - */ - @Override - public boolean equals(Object o) { - if (!(o instanceof Vector3d)) { - return false; - } - - if (this == o) { - return true; - } - - Vector3d comp = (Vector3d) o; - if (Double.compare(x, comp.x) != 0) { - return false; - } - if (Double.compare(y, comp.y) != 0) { - return false; - } - if (Double.compare(z, comp.z) != 0) { - return false; - } - return true; - } - - /** - * hashCode returns a unique code for this vector object based - * on its values. If two vectors are logically equivalent, they will return - * the same hash code value. - * @return the hash code value of this vector. - */ - @Override - public int hashCode() { - long hash = 37; - hash += 37 * hash + Double.doubleToLongBits(x); - hash += 37 * hash + Double.doubleToLongBits(y); - hash += 37 * hash + Double.doubleToLongBits(z); - return (int) hash; - } - - /** - * toString returns the string representation of this vector. - * The format is: - * - * org.jme.math.Vector3d [X=XX.XXXX, Y=YY.YYYY, Z=ZZ.ZZZZ] - * - * @return the string representation of this vector. - */ - @Override - public String toString() { - return "(" + x + ", " + y + ", " + z + ")"; - } - - @Override - public void write(JmeExporter e) throws IOException { - OutputCapsule capsule = e.getCapsule(this); - capsule.write(x, "x", 0); - capsule.write(y, "y", 0); - capsule.write(z, "z", 0); - } - - @Override - public void read(JmeImporter e) throws IOException { - InputCapsule capsule = e.getCapsule(this); - x = capsule.readDouble("x", 0); - y = capsule.readDouble("y", 0); - z = capsule.readDouble("z", 0); - } -} diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/meshes/Edge.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/meshes/Edge.java deleted file mode 100644 index 12aff2f4f..000000000 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/meshes/Edge.java +++ /dev/null @@ -1,349 +0,0 @@ -package com.jme3.scene.plugins.blender.meshes; - -import java.util.ArrayList; -import java.util.List; -import java.util.logging.Level; -import java.util.logging.Logger; - -import com.jme3.math.FastMath; -import com.jme3.math.Vector3f; -import com.jme3.scene.plugins.blender.file.BlenderFileException; -import com.jme3.scene.plugins.blender.file.Pointer; -import com.jme3.scene.plugins.blender.file.Structure; -import com.jme3.scene.plugins.blender.math.Vector3d; -import com.jme3.scene.plugins.blender.meshes.IndexesLoop.IndexPredicate; - -/** - * A class that represents a single edge between two vertices. - * - * @author Marcin Roguski (Kaelthas) - */ -public class Edge { - private static final Logger LOGGER = Logger.getLogger(Edge.class.getName()); - - private static final int FLAG_EDGE_NOT_IN_FACE = 0x80; - - /** The vertices indexes. */ - private int index1, index2; - /** The vertices that can be set if we need and abstract edge outside the mesh (for computations). */ - private Vector3f v1, v2; - /** The weight of the edge. */ - private float crease; - /** A variable that indicates if this edge belongs to any face or not. */ - private boolean inFace; - /** The mesh that owns the edge. */ - private TemporalMesh temporalMesh; - - public Edge(Vector3f v1, Vector3f v2) { - this.v1 = v1 == null ? new Vector3f() : v1; - this.v2 = v2 == null ? new Vector3f() : v2; - index1 = 0; - index2 = 1; - } - - /** - * This constructor only stores the indexes of the vertices. The position vertices should be stored - * outside this class. - * @param index1 - * the first index of the edge - * @param index2 - * the second index of the edge - * @param crease - * the weight of the face - * @param inFace - * a variable that indicates if this edge belongs to any face or not - */ - public Edge(int index1, int index2, float crease, boolean inFace, TemporalMesh temporalMesh) { - this.index1 = index1; - this.index2 = index2; - this.crease = crease; - this.inFace = inFace; - this.temporalMesh = temporalMesh; - } - - @Override - public Edge clone() { - return new Edge(index1, index2, crease, inFace, temporalMesh); - } - - /** - * @return the first index of the edge - */ - public int getFirstIndex() { - return index1; - } - - /** - * @return the second index of the edge - */ - public int getSecondIndex() { - return index2; - } - - /** - * @return the first vertex of the edge - */ - public Vector3f getFirstVertex() { - return temporalMesh == null ? v1 : temporalMesh.getVertices().get(index1); - } - - /** - * @return the second vertex of the edge - */ - public Vector3f getSecondVertex() { - return temporalMesh == null ? v2 : temporalMesh.getVertices().get(index2); - } - - /** - * Returns the index other than the given. - * @param index - * index of the edge - * @return the remaining index number - */ - public int getOtherIndex(int index) { - if (index == index1) { - return index2; - } - if (index == index2) { - return index1; - } - throw new IllegalArgumentException("Cannot give the other index for [" + index + "] because this index does not exist in edge: " + this); - } - - /** - * @return the crease value of the edge (its weight) - */ - public float getCrease() { - return crease; - } - - /** - * @return true if the edge is used by at least one face and false otherwise - */ - public boolean isInFace() { - return inFace; - } - - /** - * @return the length of the edge - */ - public float getLength() { - return this.getFirstVertex().distance(this.getSecondVertex()); - } - - /** - * @return the mesh this edge belongs to - */ - public TemporalMesh getTemporalMesh() { - return temporalMesh; - } - - /** - * @return the centroid of the edge - */ - public Vector3f computeCentroid() { - return this.getFirstVertex().add(this.getSecondVertex()).divideLocal(2); - } - - /** - * Shifts indexes by a given amount. - * @param shift - * how much the indexes should be shifted - * @param predicate - * the predicate that verifies which indexes should be shifted; if null then all will be shifted - */ - public void shiftIndexes(int shift, IndexPredicate predicate) { - if (predicate == null) { - index1 += shift; - index2 += shift; - } else { - index1 += predicate.execute(index1) ? shift : 0; - index2 += predicate.execute(index2) ? shift : 0; - } - } - - /** - * Flips the order of the indexes. - */ - public void flipIndexes() { - int temp = index1; - index1 = index2; - index2 = temp; - } - - /** - * The crossing method first computes the points on both lines (that contain the edges) - * who are closest in distance. If the distance between points is smaller than FastMath.FLT_EPSILON - * the we consider them to be the same point (the lines cross). - * The second step is to check if both points are contained within the edges. - * - * The method of computing the crossing point is as follows: - * Let's assume that: - * (P0, P1) are the points of the first edge - * (Q0, Q1) are the points of the second edge - * - * u = P1 - P0 - * v = Q1 - Q0 - * - * This gives us the equations of two lines: - * L1: (x = P1x + ux*t1; y = P1y + uy*t1; z = P1z + uz*t1) - * L2: (x = P2x + vx*t2; y = P2y + vy*t2; z = P2z + vz*t2) - * - * Comparing the x and y of the first two equations for each line will allow us to compute t1 and t2 - * (which is implemented below). - * Using t1 and t2 we can compute (x, y, z) of each line and that will give us two points that we need to compare. - * - * @param edge - * the edge we check against crossing - * @return true if the edges cross and false otherwise - */ - public boolean cross(Edge edge) { - return this.getCrossPoint(edge) != null; - } - - /** - * The method computes the crossing pint of this edge and another edge. If - * there is no crossing then null is returned. - * - * @param edge - * the edge to compute corss point with - * @return cross point on null if none exist - */ - public Vector3f getCrossPoint(Edge edge) { - return this.getCrossPoint(edge, false, false); - } - - /** - * The method computes the crossing pint of this edge and another edge. If - * there is no crossing then null is returned. Also null is returned if the edges are parallel. - * This method also allows to get the crossing point of the straight lines that contain these edges if - * you set the 'extend' parameter to true. - * - * @param edge - * the edge to compute corss point with - * @param extendThisEdge - * set to true to find a crossing point along the whole - * straight that contains the current edge - * @param extendSecondEdge - * set to true to find a crossing point along the whole - * straight that contains the given edge - * @return cross point on null if none exist or the edges are parallel - */ - public Vector3f getCrossPoint(Edge edge, boolean extendThisEdge, boolean extendSecondEdge) { - Vector3d P1 = new Vector3d(this.getFirstVertex()); - Vector3d P2 = new Vector3d(edge.getFirstVertex()); - Vector3d u = new Vector3d(this.getSecondVertex()).subtract(P1).normalizeLocal(); - Vector3d v = new Vector3d(edge.getSecondVertex()).subtract(P2).normalizeLocal(); - - if(Math.abs(u.dot(v)) >= 1 - FastMath.DBL_EPSILON) { - // the edges are parallel; do not care about the crossing point - return null; - } - - double t1 = 0, t2 = 0; - if(u.x == 0 && v.x == 0) { - t2 = (u.z * (P2.y - P1.y) - u.y * (P2.z - P1.z)) / (u.y * v.z - u.z * v.y); - t1 = (P2.z - P1.z + v.z * t2) / u.z; - } else if(u.y == 0 && v.y == 0) { - t2 = (u.x * (P2.z - P1.z) - u.z * (P2.x - P1.x)) / (u.z * v.x - u.x * v.z); - t1 = (P2.x - P1.x + v.x * t2) / u.x; - } else if(u.z == 0 && v.z == 0) { - t2 = (u.x * (P2.y - P1.y) - u.y * (P2.x - P1.x)) / (u.y * v.x - u.x * v.y); - t1 = (P2.x - P1.x + v.x * t2) / u.x; - } else { - t2 = (P1.y * u.x - P1.x * u.y + P2.x * u.y - P2.y * u.x) / (v.y * u.x - u.y * v.x); - t1 = (P2.x - P1.x + v.x * t2) / u.x; - if(Math.abs(P1.z - P2.z + u.z * t1 - v.z * t2) > FastMath.FLT_EPSILON) { - return null; - } - } - Vector3d p1 = P1.add(u.mult(t1)); - Vector3d p2 = P2.add(v.mult(t2)); - - if (p1.distance(p2) <= FastMath.FLT_EPSILON) { - if(extendThisEdge && extendSecondEdge) { - return p1.toVector3f(); - } - // the lines cross, check if p1 and p2 are within the edges - Vector3d p = p1.subtract(P1); - double cos = p.dot(u) / p.length(); - if (extendThisEdge || p.length()<= FastMath.FLT_EPSILON || cos >= 1 - FastMath.FLT_EPSILON && p.length() - this.getLength() <= FastMath.FLT_EPSILON) { - // p1 is inside the first edge, lets check the other edge now - p = p2.subtract(P2); - cos = p.dot(v) / p.length(); - if(extendSecondEdge || p.length()<= FastMath.FLT_EPSILON || cos >= 1 - FastMath.FLT_EPSILON && p.length() - edge.getLength() <= FastMath.FLT_EPSILON) { - return p1.toVector3f(); - } - } - } - - return null; - } - - @Override - public String toString() { - String result = "Edge [" + index1 + ", " + index2 + "] {" + crease + "}"; - result += " (" + this.getFirstVertex() + " -> " + this.getSecondVertex() + ")"; - if (inFace) { - result += "[F]"; - } - return result; - } - - @Override - public int hashCode() { - // The hash code must be identical for the same two indexes, no matter their order. - final int prime = 31; - int result = 1; - int lowerIndex = Math.min(index1, index2); - int higherIndex = Math.max(index1, index2); - result = prime * result + lowerIndex; - result = prime * result + higherIndex; - return result; - } - - @Override - public boolean equals(Object obj) { - if (!(obj instanceof Edge)) { - return false; - } - if (this == obj) { - return true; - } - Edge other = (Edge) obj; - return Math.min(index1, index2) == Math.min(other.index1, other.index2) && Math.max(index1, index2) == Math.max(other.index1, other.index2); - } - - /** - * The method loads all edges from the given mesh structure that does not belong to any face. - * @param meshStructure - * the mesh structure - * @param temporalMesh - * the owner of the edges - * @return all edges without faces - * @throws BlenderFileException - * an exception is thrown when problems with file reading occur - */ - public static List loadAll(Structure meshStructure, TemporalMesh temporalMesh) throws BlenderFileException { - LOGGER.log(Level.FINE, "Loading all edges that do not belong to any face from mesh: {0}", meshStructure.getName()); - List result = new ArrayList(); - - Pointer pMEdge = (Pointer) meshStructure.getFieldValue("medge"); - - if (pMEdge.isNotNull()) { - List edges = pMEdge.fetchData(); - for (Structure edge : edges) { - int flag = ((Number) edge.getFieldValue("flag")).intValue(); - - int v1 = ((Number) edge.getFieldValue("v1")).intValue(); - int v2 = ((Number) edge.getFieldValue("v2")).intValue(); - // I do not know why, but blender stores (possibly only sometimes) crease as negative values and shows positive in the editor - float crease = Math.abs(((Number) edge.getFieldValue("crease")).floatValue()); - boolean edgeInFace = (flag & Edge.FLAG_EDGE_NOT_IN_FACE) == 0; - result.add(new Edge(v1, v2, crease, edgeInFace, temporalMesh)); - } - } - LOGGER.log(Level.FINE, "Loaded {0} edges.", result.size()); - return result; - } -} diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/meshes/Face.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/meshes/Face.java deleted file mode 100644 index 009e3c908..000000000 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/meshes/Face.java +++ /dev/null @@ -1,613 +0,0 @@ -package com.jme3.scene.plugins.blender.meshes; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.logging.Level; -import java.util.logging.Logger; - -import com.jme3.math.FastMath; -import com.jme3.math.Vector2f; -import com.jme3.math.Vector3f; -import com.jme3.scene.plugins.blender.BlenderContext; -import com.jme3.scene.plugins.blender.file.BlenderFileException; -import com.jme3.scene.plugins.blender.file.Pointer; -import com.jme3.scene.plugins.blender.file.Structure; - -/** - * A class that represents a single face in the mesh. The face is a polygon. Its minimum count of - * vertices is = 3. - * - * @author Marcin Roguski (Kaelthas) - */ -public class Face implements Comparator { - private static final Logger LOGGER = Logger.getLogger(Face.class.getName()); - - /** The indexes loop of the face. */ - private IndexesLoop indexes; - - private List triangulatedFaces; - /** Indicates if the face is smooth or solid. */ - private boolean smooth; - /** The material index of the face. */ - private int materialNumber; - /** UV coordinate sets attached to the face. The key is the set name and value are the UV coords. */ - private Map> faceUVCoords; - /** The vertex colors of the face. */ - private List vertexColors; - /** The temporal mesh the face belongs to. */ - private TemporalMesh temporalMesh; - - /** - * Creates a complete face with all available data. - * @param indexes - * the indexes of the face (required) - * @param smooth - * indicates if the face is smooth or solid - * @param materialNumber - * the material index of the face - * @param faceUVCoords - * UV coordinate sets of the face (optional) - * @param vertexColors - * the vertex colors of the face (optional) - * @param temporalMesh - * the temporal mesh the face belongs to (required) - */ - public Face(Integer[] indexes, boolean smooth, int materialNumber, Map> faceUVCoords, List vertexColors, TemporalMesh temporalMesh) { - this.setTemporalMesh(temporalMesh); - this.indexes = new IndexesLoop(indexes); - this.smooth = smooth; - this.materialNumber = materialNumber; - this.faceUVCoords = faceUVCoords; - this.temporalMesh = temporalMesh; - this.vertexColors = vertexColors; - } - - /** - * Default constructor. Used by the clone method. - */ - private Face() { - } - - @Override - public Face clone() { - Face result = new Face(); - result.indexes = indexes.clone(); - result.smooth = smooth; - result.materialNumber = materialNumber; - if (faceUVCoords != null) { - result.faceUVCoords = new HashMap>(faceUVCoords.size()); - for (Entry> entry : faceUVCoords.entrySet()) { - List uvs = new ArrayList(entry.getValue().size()); - for (Vector2f v : entry.getValue()) { - uvs.add(v.clone()); - } - result.faceUVCoords.put(entry.getKey(), uvs); - } - } - if (vertexColors != null) { - result.vertexColors = new ArrayList(vertexColors.size()); - for (byte[] colors : vertexColors) { - result.vertexColors.add(colors.clone()); - } - } - result.temporalMesh = temporalMesh; - return result; - } - - /** - * Returns the index at the given position in the index loop. If the given position is negative or exceeds - * the amount of vertices - it is being looped properly so that it always hits an index. - * For example getIndex(-1) will return the index before the 0 - in this case it will be the last one. - * @param indexPosition - * the index position - * @return index value at the given position - */ - private Integer getIndex(int indexPosition) { - if (indexPosition >= indexes.size()) { - indexPosition = indexPosition % indexes.size(); - } else if (indexPosition < 0) { - indexPosition = indexes.size() - -indexPosition % indexes.size(); - } - return indexes.get(indexPosition); - } - - /** - * @return the mesh this face belongs to - */ - public TemporalMesh getTemporalMesh() { - return temporalMesh; - } - - /** - * @return the original indexes of the face - */ - public IndexesLoop getIndexes() { - return indexes; - } - - /** - * @return the centroid of the face - */ - public Vector3f computeCentroid() { - Vector3f result = new Vector3f(); - List vertices = temporalMesh.getVertices(); - for (Integer index : indexes) { - result.addLocal(vertices.get(index)); - } - return result.divideLocal(indexes.size()); - } - - /** - * @return current indexes of the face (if it is already triangulated then more than one index group will be in the result list) - */ - public List> getCurrentIndexes() { - if (triangulatedFaces == null) { - return Arrays.asList(indexes.getAll()); - } - List> result = new ArrayList>(triangulatedFaces.size()); - for (IndexesLoop loop : triangulatedFaces) { - result.add(loop.getAll()); - } - return result; - } - - /** - * The method detaches the triangle from the face. This method keeps the indexes loop normalized - every index - * has only two neighbours. So if detaching the triangle causes a vertex to have more than two neighbours - it is - * also detached and returned as a result. - * The result is an empty list if no such situation happens. - * @param triangleIndexes - * the indexes of a triangle to be detached - * @return a list of faces that need to be detached as well in order to keep them normalized - * @throws BlenderFileException - * an exception is thrown when vertices of a face create more than one loop; this is found during path finding - */ - private List detachTriangle(Integer[] triangleIndexes) throws BlenderFileException { - LOGGER.fine("Detaching triangle."); - if (triangleIndexes.length != 3) { - throw new IllegalArgumentException("Cannot detach triangle with that does not have 3 indexes!"); - } - MeshHelper meshHelper = temporalMesh.getBlenderContext().getHelper(MeshHelper.class); - List detachedFaces = new ArrayList(); - List path = new ArrayList(indexes.size()); - - boolean[] edgeRemoved = new boolean[] { indexes.removeEdge(triangleIndexes[0], triangleIndexes[1]), indexes.removeEdge(triangleIndexes[0], triangleIndexes[2]), indexes.removeEdge(triangleIndexes[1], triangleIndexes[2]) }; - Integer[][] indexesPairs = new Integer[][] { new Integer[] { triangleIndexes[0], triangleIndexes[1] }, new Integer[] { triangleIndexes[0], triangleIndexes[2] }, new Integer[] { triangleIndexes[1], triangleIndexes[2] } }; - - for (int i = 0; i < 3; ++i) { - if (!edgeRemoved[i]) { - indexes.findPath(indexesPairs[i][0], indexesPairs[i][1], path); - if (path.size() == 0) { - indexes.findPath(indexesPairs[i][1], indexesPairs[i][0], path); - } - if (path.size() == 0) { - throw new IllegalStateException("Triangulation failed. Cannot find path between two indexes. Please apply triangulation in Blender as a workaround."); - } - if (detachedFaces.size() == 0 && path.size() < indexes.size()) { - Integer[] indexesSublist = path.toArray(new Integer[path.size()]); - detachedFaces.add(new Face(indexesSublist, smooth, materialNumber, meshHelper.selectUVSubset(this, indexesSublist), meshHelper.selectVertexColorSubset(this, indexesSublist), temporalMesh)); - for (int j = 0; j < path.size() - 1; ++j) { - indexes.removeEdge(path.get(j), path.get(j + 1)); - } - indexes.removeEdge(path.get(path.size() - 1), path.get(0)); - } else { - indexes.addEdge(path.get(path.size() - 1), path.get(0)); - } - } - } - - return detachedFaces; - } - - /** - * Sets the temporal mesh for the face. The given mesh cannot be null. - * @param temporalMesh - * the temporal mesh of the face - * @throws IllegalArgumentException - * thrown if given temporal mesh is null - */ - public void setTemporalMesh(TemporalMesh temporalMesh) { - if (temporalMesh == null) { - throw new IllegalArgumentException("No temporal mesh for the face given!"); - } - this.temporalMesh = temporalMesh; - } - - /** - * Flips the order of the indexes. - */ - public void flipIndexes() { - indexes.reverse(); - if (faceUVCoords != null) { - for (Entry> entry : faceUVCoords.entrySet()) { - Collections.reverse(entry.getValue()); - } - } - } - - /** - * Flips UV coordinates. - * @param u - * indicates if U coords should be flipped - * @param v - * indicates if V coords should be flipped - */ - public void flipUV(boolean u, boolean v) { - if (faceUVCoords != null) { - for (Entry> entry : faceUVCoords.entrySet()) { - for (Vector2f uv : entry.getValue()) { - uv.set(u ? 1 - uv.x : uv.x, v ? 1 - uv.y : uv.y); - } - } - } - } - - /** - * @return the UV sets of the face - */ - public Map> getUvSets() { - return faceUVCoords; - } - - /** - * @return current vertex count of the face - */ - public int vertexCount() { - return indexes.size(); - } - - /** - * The method triangulates the face. - */ - public TriangulationWarning triangulate() { - LOGGER.fine("Triangulating face."); - assert indexes.size() >= 3 : "Invalid indexes amount for face. 3 is the required minimum!"; - triangulatedFaces = new ArrayList(indexes.size() - 2); - Integer[] indexes = new Integer[3]; - TriangulationWarning warning = TriangulationWarning.NONE; - - try { - List facesToTriangulate = new ArrayList(Arrays.asList(this.clone())); - while (facesToTriangulate.size() > 0 && warning == TriangulationWarning.NONE) { - Face face = facesToTriangulate.remove(0); - // two special cases will improve the computations speed - if(face.getIndexes().size() == 3) { - triangulatedFaces.add(face.getIndexes().clone()); - } else { - int previousIndex1 = -1, previousIndex2 = -1, previousIndex3 = -1; - while (face.vertexCount() > 0) { - indexes[0] = face.getIndex(0); - indexes[1] = face.findClosestVertex(indexes[0], -1); - indexes[2] = face.findClosestVertex(indexes[0], indexes[1]); - - LOGGER.finer("Veryfying improper triangulation of the temporal mesh."); - if (indexes[0] < 0 || indexes[1] < 0 || indexes[2] < 0) { - warning = TriangulationWarning.CLOSEST_VERTS; - break; - } - if (previousIndex1 == indexes[0] && previousIndex2 == indexes[1] && previousIndex3 == indexes[2]) { - warning = TriangulationWarning.INFINITE_LOOP; - break; - } - previousIndex1 = indexes[0]; - previousIndex2 = indexes[1]; - previousIndex3 = indexes[2]; - - Arrays.sort(indexes, this); - facesToTriangulate.addAll(face.detachTriangle(indexes)); - triangulatedFaces.add(new IndexesLoop(indexes)); - } - } - } - } catch (BlenderFileException e) { - LOGGER.log(Level.WARNING, "Errors occurred during face triangulation: {0}. The face will be triangulated with the most direct algorithm, but the results might not be identical to blender.", e.getLocalizedMessage()); - warning = TriangulationWarning.UNKNOWN; - } - if(warning != TriangulationWarning.NONE) { - LOGGER.finest("Triangulation the face using the most direct algorithm."); - indexes[0] = this.getIndex(0); - for (int i = 1; i < this.vertexCount() - 1; ++i) { - indexes[1] = this.getIndex(i); - indexes[2] = this.getIndex(i + 1); - triangulatedFaces.add(new IndexesLoop(indexes)); - } - } - return warning; - } - - /** - * A warning that indicates a problem with face triangulation. The warnings are collected and displayed once for each type for a mesh to - * avoid multiple warning loggings during triangulation. The amount of iterations can be really huge and logging every single failure would - * really slow down the importing process and make logs unreadable. - * - * @author Marcin Roguski (Kaelthas) - */ - public static enum TriangulationWarning { - NONE(null), - CLOSEST_VERTS("Unable to find two closest vertices while triangulating face."), - INFINITE_LOOP("Infinite loop detected during triangulation."), - UNKNOWN("There was an unknown problem with face triangulation. Please see log for details."); - - private String description; - - private TriangulationWarning(String description) { - this.description = description; - } - - @Override - public String toString() { - return description; - } - } - - /** - * @return true if the face is smooth and false otherwise - */ - public boolean isSmooth() { - return smooth; - } - - /** - * @return the material index of the face - */ - public int getMaterialNumber() { - return materialNumber; - } - - /** - * @return the vertices colord of the face - */ - public List getVertexColors() { - return vertexColors; - } - - @Override - public String toString() { - return "Face " + indexes; - } - - /** - * The method finds the closest vertex to the one specified by index. - * If the vertexToIgnore is positive than it will be ignored in the result. - * The closest vertex must be able to create an edge that is fully contained - * within the face and does not cross any other edges. Also if the - * vertexToIgnore is not negative then the condition that the edge between - * the found index and the one to ignore is inside the face must also be - * met. - * - * @param index - * the index of the vertex that needs to have found the nearest - * neighbour - * @param indexToIgnore - * the index to ignore in the result (pass -1 if none is to be - * ignored) - * @return the index of the closest vertex to the given one - */ - private int findClosestVertex(int index, int indexToIgnore) { - int result = -1; - List vertices = temporalMesh.getVertices(); - Vector3f v1 = vertices.get(index); - float distance = Float.MAX_VALUE; - for (int i : indexes) { - if (i != index && i != indexToIgnore) { - Vector3f v2 = vertices.get(i); - float d = v2.distance(v1); - if (d < distance && this.contains(new Edge(index, i, 0, true, temporalMesh)) && (indexToIgnore < 0 || this.contains(new Edge(indexToIgnore, i, 0, true, temporalMesh)))) { - result = i; - distance = d; - } - } - } - return result; - } - - /** - * The method verifies if the edge is contained within the face. - * It means it cannot cross any other edge and it must be inside the face and not outside of it. - * @param edge - * the edge to be checked - * @return true if the given edge is contained within the face and false otherwise - */ - private boolean contains(Edge edge) { - int index1 = edge.getFirstIndex(); - int index2 = edge.getSecondIndex(); - // check if the line between the vertices is not a border edge of the face - if (!indexes.areNeighbours(index1, index2)) { - for (int i = 0; i < indexes.size(); ++i) { - int i1 = this.getIndex(i - 1); - int i2 = this.getIndex(i); - // check if the edges have no common verts (because if they do, they cannot cross) - if (i1 != index1 && i1 != index2 && i2 != index1 && i2 != index2) { - if (edge.cross(new Edge(i1, i2, 0, false, temporalMesh))) { - return false; - } - } - } - - // computing the edge's middle point - Vector3f edgeMiddlePoint = edge.computeCentroid(); - // computing the edge that is perpendicular to the given edge and has a length of 1 (length actually does not matter) - Vector3f edgeVector = edge.getSecondVertex().subtract(edge.getFirstVertex()); - Vector3f edgeNormal = temporalMesh.getNormals().get(index1).cross(edgeVector).normalizeLocal(); - Edge e = new Edge(edgeMiddlePoint, edgeNormal.add(edgeMiddlePoint)); - // compute the vectors from the middle point to the crossing between the extended edge 'e' and other edges of the face - List crossingVectors = new ArrayList(); - for (int i = 0; i < indexes.size(); ++i) { - int i1 = this.getIndex(i); - int i2 = this.getIndex(i + 1); - Vector3f crossPoint = e.getCrossPoint(new Edge(i1, i2, 0, false, temporalMesh), true, false); - if(crossPoint != null) { - crossingVectors.add(crossPoint.subtractLocal(edgeMiddlePoint)); - } - } - if(crossingVectors.size() == 0) { - return false;// edges do not cross - } - - // use only distinct vertices (doubles may appear if the crossing point is a vertex) - List distinctCrossingVectors = new ArrayList(); - for(Vector3f cv : crossingVectors) { - double minDistance = Double.MAX_VALUE; - for(Vector3f dcv : distinctCrossingVectors) { - minDistance = Math.min(minDistance, dcv.distance(cv)); - } - if(minDistance > FastMath.FLT_EPSILON) { - distinctCrossingVectors.add(cv); - } - } - - if(distinctCrossingVectors.size() == 0) { - throw new IllegalStateException("There MUST be at least 2 crossing vertices!"); - } - // checking if all crossing vectors point to the same direction (if yes then the edge is outside the face) - float direction = Math.signum(distinctCrossingVectors.get(0).dot(edgeNormal));// if at least one vector has different direction that this - it means that the edge is inside the face - for(int i=1;i loadAll(Structure meshStructure, Map> userUVGroups, List verticesColors, TemporalMesh temporalMesh, BlenderContext blenderContext) throws BlenderFileException { - LOGGER.log(Level.FINE, "Loading all faces from mesh: {0}", meshStructure.getName()); - List result = new ArrayList(); - MeshHelper meshHelper = blenderContext.getHelper(MeshHelper.class); - if (meshHelper.isBMeshCompatible(meshStructure)) { - LOGGER.fine("Reading BMesh."); - Pointer pMLoop = (Pointer) meshStructure.getFieldValue("mloop"); - Pointer pMPoly = (Pointer) meshStructure.getFieldValue("mpoly"); - - if (pMPoly.isNotNull() && pMLoop.isNotNull()) { - List polys = pMPoly.fetchData(); - List loops = pMLoop.fetchData(); - for (Structure poly : polys) { - int materialNumber = ((Number) poly.getFieldValue("mat_nr")).intValue(); - int loopStart = ((Number) poly.getFieldValue("loopstart")).intValue(); - int totLoop = ((Number) poly.getFieldValue("totloop")).intValue(); - boolean smooth = (((Number) poly.getFieldValue("flag")).byteValue() & 0x01) != 0x00; - Integer[] vertexIndexes = new Integer[totLoop]; - - for (int i = loopStart; i < loopStart + totLoop; ++i) { - vertexIndexes[i - loopStart] = ((Number) loops.get(i).getFieldValue("v")).intValue(); - } - - // uvs always must be added wheater we have texture or not - Map> uvCoords = new HashMap>(); - for (Entry> entry : userUVGroups.entrySet()) { - List uvs = entry.getValue().subList(loopStart, loopStart + totLoop); - uvCoords.put(entry.getKey(), new ArrayList(uvs)); - } - - List vertexColors = null; - if (verticesColors != null && verticesColors.size() > 0) { - vertexColors = new ArrayList(totLoop); - for (int i = loopStart; i < loopStart + totLoop; ++i) { - vertexColors.add(verticesColors.get(i)); - } - } - - result.add(new Face(vertexIndexes, smooth, materialNumber, uvCoords, vertexColors, temporalMesh)); - } - } - } else { - LOGGER.fine("Reading traditional faces."); - Pointer pMFace = (Pointer) meshStructure.getFieldValue("mface"); - List mFaces = pMFace.isNotNull() ? pMFace.fetchData() : null; - if (mFaces != null && mFaces.size() > 0) { - // indicates if the material with the specified number should have a texture attached - for (int i = 0; i < mFaces.size(); ++i) { - Structure mFace = mFaces.get(i); - int materialNumber = ((Number) mFace.getFieldValue("mat_nr")).intValue(); - boolean smooth = (((Number) mFace.getFieldValue("flag")).byteValue() & 0x01) != 0x00; - - int v1 = ((Number) mFace.getFieldValue("v1")).intValue(); - int v2 = ((Number) mFace.getFieldValue("v2")).intValue(); - int v3 = ((Number) mFace.getFieldValue("v3")).intValue(); - int v4 = ((Number) mFace.getFieldValue("v4")).intValue(); - - int vertCount = v4 == 0 ? 3 : 4; - - // uvs always must be added wheater we have texture or not - Map> faceUVCoords = new HashMap>(); - for (Entry> entry : userUVGroups.entrySet()) { - List uvCoordsForASingleFace = new ArrayList(vertCount); - for (int j = 0; j < vertCount; ++j) { - uvCoordsForASingleFace.add(entry.getValue().get(i * 4 + j)); - } - faceUVCoords.put(entry.getKey(), uvCoordsForASingleFace); - } - - List vertexColors = null; - if (verticesColors != null && verticesColors.size() > 0) { - vertexColors = new ArrayList(vertCount); - - vertexColors.add(verticesColors.get(v1)); - vertexColors.add(verticesColors.get(v2)); - vertexColors.add(verticesColors.get(v3)); - if (vertCount == 4) { - vertexColors.add(verticesColors.get(v4)); - } - } - - result.add(new Face(vertCount == 4 ? new Integer[] { v1, v2, v3, v4 } : new Integer[] { v1, v2, v3 }, smooth, materialNumber, faceUVCoords, vertexColors, temporalMesh)); - } - } - } - LOGGER.log(Level.FINE, "Loaded {0} faces.", result.size()); - return result; - } - - @Override - public int compare(Integer index1, Integer index2) { - return indexes.indexOf(index1) - indexes.indexOf(index2); - } -} diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/meshes/IndexesLoop.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/meshes/IndexesLoop.java deleted file mode 100644 index edec9b489..000000000 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/meshes/IndexesLoop.java +++ /dev/null @@ -1,305 +0,0 @@ -package com.jme3.scene.plugins.blender.meshes; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; - -import com.jme3.scene.plugins.blender.file.BlenderFileException; - -/** - * This class represents the Face's indexes loop. It is a simplified implementation of directed graph. - * - * @author Marcin Roguski (Kaelthas) - */ -public class IndexesLoop implements Comparator, Iterable { - public static final IndexPredicate INDEX_PREDICATE_USE_ALL = new IndexPredicate() { - @Override - public boolean execute(Integer index) { - return true; - } - }; - - /** The indexes. */ - private List nodes; - /** The edges of the indexes graph. The key is the 'from' index and 'value' is - 'to' index. */ - private Map> edges = new HashMap>(); - - /** - * The constructor uses the given nodes in their give order. Each neighbour indexes will form an edge. - * @param nodes - * the nodes for the loop - */ - public IndexesLoop(Integer[] nodes) { - this.nodes = new ArrayList(Arrays.asList(nodes)); - this.prepareEdges(this.nodes); - } - - @Override - public IndexesLoop clone() { - return new IndexesLoop(nodes.toArray(new Integer[nodes.size()])); - } - - /** - * The method prepares edges for the given indexes. - * @param nodes - * the indexes - */ - private void prepareEdges(List nodes) { - for (int i = 0; i < nodes.size() - 1; ++i) { - if (edges.containsKey(nodes.get(i))) { - edges.get(nodes.get(i)).add(nodes.get(i + 1)); - } else { - edges.put(nodes.get(i), new ArrayList(Arrays.asList(nodes.get(i + 1)))); - } - } - edges.put(nodes.get(nodes.size() - 1), new ArrayList(Arrays.asList(nodes.get(0)))); - } - - /** - * @return the count of indexes - */ - public int size() { - return nodes.size(); - } - - /** - * Adds edge to the loop. - * @param from - * the start index - * @param to - * the end index - */ - public void addEdge(Integer from, Integer to) { - if (nodes.contains(from) && nodes.contains(to)) { - if (edges.containsKey(from) && !edges.get(from).contains(to)) { - edges.get(from).add(to); - } - } - } - - /** - * Removes edge from the face. The edge is removed if it already exists in the face. - * @param node1 - * the first index of the edge to be removed - * @param node2 - * the second index of the edge to be removed - * @return true if the edge was removed and false otherwise - */ - public boolean removeEdge(Integer node1, Integer node2) { - boolean edgeRemoved = false; - if (nodes.contains(node1) && nodes.contains(node2)) { - if (edges.containsKey(node1)) { - edgeRemoved |= edges.get(node1).remove(node2); - } - if (edges.containsKey(node2)) { - edgeRemoved |= edges.get(node2).remove(node1); - } - if (edgeRemoved) { - if (this.getNeighbourCount(node1) == 0) { - this.removeIndexes(node1); - } - if (this.getNeighbourCount(node2) == 0) { - this.removeIndexes(node2); - } - } - } - return edgeRemoved; - } - - /** - * Tells if the given indexes are neighbours. - * @param index1 - * the first index - * @param index2 - * the second index - * @return true if the given indexes are neighbours and false otherwise - */ - public boolean areNeighbours(Integer index1, Integer index2) { - if (index1.equals(index2) || !edges.containsKey(index1) || !edges.containsKey(index2)) { - return false; - } - return edges.get(index1).contains(index2) || edges.get(index2).contains(index1); - } - - /** - * Returns the value of the index located after the given one. Pointint the last index will return the first one. - * @param index - * the index value - * @return the value of 'next' index - */ - public Integer getNextIndex(Integer index) { - int i = nodes.indexOf(index); - return i == nodes.size() - 1 ? nodes.get(0) : nodes.get(i + 1); - } - - /** - * Returns the value of the index located before the given one. Pointint the first index will return the last one. - * @param index - * the index value - * @return the value of 'previous' index - */ - public Integer getPreviousIndex(Integer index) { - int i = nodes.indexOf(index); - return i == 0 ? nodes.get(nodes.size() - 1) : nodes.get(i - 1); - } - - /** - * The method shifts all indexes by a given value. - * @param shift - * the value to shift all indexes - * @param predicate - * the predicate that verifies which indexes should be shifted; if null then all will be shifted - */ - public void shiftIndexes(int shift, IndexPredicate predicate) { - if (predicate == null) { - predicate = INDEX_PREDICATE_USE_ALL; - } - List nodes = new ArrayList(this.nodes.size()); - for (Integer node : this.nodes) { - nodes.add(node + (predicate.execute(node) ? shift : 0)); - } - - Map> edges = new HashMap>(); - for (Entry> entry : this.edges.entrySet()) { - List neighbours = new ArrayList(entry.getValue().size()); - for (Integer neighbour : entry.getValue()) { - neighbours.add(neighbour + (predicate.execute(neighbour) ? shift : 0)); - } - edges.put(entry.getKey() + shift, neighbours); - } - - this.nodes = nodes; - this.edges = edges; - } - - /** - * Reverses the order of the indexes. - */ - public void reverse() { - Collections.reverse(nodes); - edges.clear(); - this.prepareEdges(nodes); - } - - /** - * Returns the neighbour count of the given index. - * @param index - * the index whose neighbour count will be checked - * @return the count of neighbours of the given index - */ - private int getNeighbourCount(Integer index) { - int result = 0; - if (edges.containsKey(index)) { - result = edges.get(index).size(); - for (List neighbours : edges.values()) { - if (neighbours.contains(index)) { - ++result; - } - } - } - return result; - } - - /** - * Returns the position of the given index in the loop. - * @param index - * the index of the face - * @return the indexe's position in the loop - */ - public int indexOf(Integer index) { - return nodes.indexOf(index); - } - - /** - * Returns the index at the given position. - * @param indexPosition - * the position of the index - * @return the index at a given position - */ - public Integer get(int indexPosition) { - return nodes.get(indexPosition); - } - - /** - * @return all indexes of the face - */ - public List getAll() { - return new ArrayList(nodes); - } - - /** - * The method removes all given indexes. - * @param indexes - * the indexes to be removed - */ - public void removeIndexes(Integer... indexes) { - for (Integer index : indexes) { - nodes.remove(index); - edges.remove(index); - for (List neighbours : edges.values()) { - neighbours.remove(index); - } - } - } - - /** - * The method finds the path between the given indexes. - * @param start - * the start index - * @param end - * the end index - * @param result - * a list containing indexes on the path from start to end (inclusive) - * @throws IllegalStateException - * an exception is thrown when the loop is not normalized (at least one - * index has more than 2 neighbours) - * @throws BlenderFileException - * an exception is thrown if the vertices of a face create more than one loop; this is thrown - * to prevent lack of memory errors during triangulation - */ - public void findPath(Integer start, Integer end, List result) throws BlenderFileException { - result.clear(); - Integer node = start; - while (!node.equals(end)) { - if (result.contains(node)) { - throw new BlenderFileException("Indexes of face have infinite loops!"); - } - result.add(node); - List nextSteps = edges.get(node); - if (nextSteps == null || nextSteps.size() == 0) { - result.clear();// no directed path from start to end - return; - } else if (nextSteps.size() == 1) { - node = nextSteps.get(0); - } else { - throw new BlenderFileException("Triangulation failed. Face has ambiguous indexes loop. Please triangulate your model in Blender as a workaround."); - } - } - result.add(end); - } - - @Override - public String toString() { - return "IndexesLoop " + nodes.toString(); - } - - @Override - public int compare(Integer i1, Integer i2) { - return nodes.indexOf(i1) - nodes.indexOf(i2); - } - - @Override - public Iterator iterator() { - return nodes.iterator(); - } - - public static interface IndexPredicate { - boolean execute(Integer index); - } -} diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/meshes/MeshBuffers.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/meshes/MeshBuffers.java deleted file mode 100644 index a14d77004..000000000 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/meshes/MeshBuffers.java +++ /dev/null @@ -1,364 +0,0 @@ -package com.jme3.scene.plugins.blender.meshes; - -import java.nio.Buffer; -import java.nio.ByteBuffer; -import java.nio.FloatBuffer; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.NavigableMap; -import java.util.TreeMap; - -import com.jme3.math.FastMath; -import com.jme3.math.Vector2f; -import com.jme3.math.Vector3f; -import com.jme3.scene.VertexBuffer; -import com.jme3.scene.VertexBuffer.Format; -import com.jme3.scene.VertexBuffer.Type; -import com.jme3.scene.VertexBuffer.Usage; -import com.jme3.util.BufferUtils; - -/** - * A class that aggregates the mesh data to prepare proper buffers. The buffers refer only to ONE material. - * - * @author Marcin Roguski (Kaelthas) - */ -/* package */class MeshBuffers { - private static final int MAXIMUM_WEIGHTS_PER_VERTEX = 4; - - /** The material index. */ - private final int materialIndex; - /** The vertices. */ - private List verts = new ArrayList(); - /** The normals. */ - private List normals = new ArrayList(); - /** The UV coordinate sets. */ - private Map> uvCoords = new HashMap>(); - /** The vertex colors. */ - private List vertColors = new ArrayList(); - /** The indexes. */ - private List indexes = new ArrayList(); - /** The maximum weights count assigned to a single vertex. Used during weights normalization. */ - private int maximumWeightsPerVertex; - /** A list of mapping between weights and indexes. Each entry for the proper vertex. */ - private List> boneWeightAndIndexes = new ArrayList>(); - - /** - * Constructor stores only the material index value. - * @param materialIndex - * the material index - */ - public MeshBuffers(int materialIndex) { - this.materialIndex = materialIndex; - } - - /** - * @return the material index - */ - public int getMaterialIndex() { - return materialIndex; - } - - /** - * @return indexes buffer - */ - public Buffer getIndexBuffer() { - if (indexes.size() <= Short.MAX_VALUE) { - short[] indices = new short[indexes.size()]; - for (int i = 0; i < indexes.size(); ++i) { - indices[i] = indexes.get(i).shortValue(); - } - return BufferUtils.createShortBuffer(indices); - } else { - int[] indices = new int[indexes.size()]; - for (int i = 0; i < indexes.size(); ++i) { - indices[i] = indexes.get(i).intValue(); - } - return BufferUtils.createIntBuffer(indices); - } - } - - /** - * @return positions buffer - */ - public VertexBuffer getPositionsBuffer() { - VertexBuffer positionBuffer = new VertexBuffer(Type.Position); - Vector3f[] data = verts.toArray(new Vector3f[verts.size()]); - positionBuffer.setupData(Usage.Static, 3, Format.Float, BufferUtils.createFloatBuffer(data)); - return positionBuffer; - } - - /** - * @return normals buffer - */ - public VertexBuffer getNormalsBuffer() { - VertexBuffer positionBuffer = new VertexBuffer(Type.Normal); - Vector3f[] data = normals.toArray(new Vector3f[normals.size()]); - positionBuffer.setupData(Usage.Static, 3, Format.Float, BufferUtils.createFloatBuffer(data)); - return positionBuffer; - } - - /** - * @return bone buffers - */ - public BoneBuffersData getBoneBuffers() { - BoneBuffersData result = null; - if (maximumWeightsPerVertex > 0) { - this.normalizeBoneBuffers(MAXIMUM_WEIGHTS_PER_VERTEX); - maximumWeightsPerVertex = MAXIMUM_WEIGHTS_PER_VERTEX; - - FloatBuffer weightsFloatData = BufferUtils.createFloatBuffer(boneWeightAndIndexes.size() * MAXIMUM_WEIGHTS_PER_VERTEX); - ByteBuffer indicesData = BufferUtils.createByteBuffer(boneWeightAndIndexes.size() * MAXIMUM_WEIGHTS_PER_VERTEX); - int index = 0; - for (Map boneBuffersData : boneWeightAndIndexes) { - if (boneBuffersData.size() > 0) { - int count = 0; - for (Entry entry : boneBuffersData.entrySet()) { - weightsFloatData.put(index * MAXIMUM_WEIGHTS_PER_VERTEX + count, entry.getKey()); - indicesData.put(index * MAXIMUM_WEIGHTS_PER_VERTEX + count, entry.getValue().byteValue()); - ++count; - } - } else { - // if no bone is assigned to this vertex then attach it to the 0-indexed root bone - weightsFloatData.put(index * MAXIMUM_WEIGHTS_PER_VERTEX, 1.0f); - indicesData.put(index * MAXIMUM_WEIGHTS_PER_VERTEX, (byte) 0); - } - ++index; - } - VertexBuffer verticesWeights = new VertexBuffer(Type.BoneWeight); - verticesWeights.setupData(Usage.CpuOnly, maximumWeightsPerVertex, Format.Float, weightsFloatData); - - VertexBuffer verticesWeightsIndices = new VertexBuffer(Type.BoneIndex); - verticesWeightsIndices.setupData(Usage.CpuOnly, maximumWeightsPerVertex, Format.UnsignedByte, indicesData); - - result = new BoneBuffersData(maximumWeightsPerVertex, verticesWeights, verticesWeightsIndices); - } - - return result; - } - - /** - * @return UV coordinates sets - */ - public Map> getUvCoords() { - return uvCoords; - } - - /** - * @return true if vertex colors are used and false otherwise - */ - public boolean areVertexColorsUsed() { - return vertColors.size() > 0; - } - - /** - * @return vertex colors buffer - */ - public ByteBuffer getVertexColorsBuffer() { - ByteBuffer result = null; - if (vertColors.size() > 0) { - result = BufferUtils.createByteBuffer(4 * vertColors.size()); - for (byte[] v : vertColors) { - if (v != null) { - result.put(v[0]).put(v[1]).put(v[2]).put(v[3]); - } else { - result.put((byte) 0).put((byte) 0).put((byte) 0).put((byte) 0); - } - } - result.flip(); - } - return result; - } - - /** - * @return true if indexes can be shorts' and false if they need to be ints' - */ - public boolean isShortIndexBuffer() { - return indexes.size() <= Short.MAX_VALUE; - } - - /** - * Appends a vertex and normal to the buffers. - * @param vert - * vertex - * @param normal - * normal vector - */ - public void append(Vector3f vert, Vector3f normal) { - int index = this.indexOf(vert, normal, null); - if (index >= 0) { - indexes.add(index); - } else { - indexes.add(verts.size()); - verts.add(vert); - normals.add(normal); - } - } - - /** - * Appends the face data to the buffers. - * @param smooth - * tells if the face is smooth or flat - * @param verts - * the vertices - * @param normals - * the normals - * @param uvCoords - * the UV coordinates - * @param vertColors - * the vertex colors - * @param vertexGroups - * the vertex groups - */ - public void append(boolean smooth, Vector3f[] verts, Vector3f[] normals, Map> uvCoords, byte[][] vertColors, List> vertexGroups) { - if (verts.length != normals.length) { - throw new IllegalArgumentException("The amount of verts and normals MUST be equal!"); - } - if (vertColors != null && vertColors.length != verts.length) { - throw new IllegalArgumentException("The amount of vertex colors and vertices MUST be equal!"); - } - if (vertexGroups.size() != 0 && vertexGroups.size() != verts.length) { - throw new IllegalArgumentException("The amount of (if given) vertex groups and vertices MUST be equal!"); - } - - if (!smooth) { - // make the normals perpendicular to the face - normals[0] = normals[1] = normals[2] = FastMath.computeNormal(verts[0], verts[1], verts[2]); - } - - for (int i = 0; i < verts.length; ++i) { - int index = -1; - Map uvCoordsForVertex = this.getUVsForVertex(i, uvCoords); - if (smooth && (index = this.indexOf(verts[i], normals[i], uvCoordsForVertex)) >= 0) { - indexes.add(index); - } else { - indexes.add(this.verts.size()); - this.verts.add(verts[i]); - this.normals.add(normals[i]); - this.vertColors.add(vertColors[i]); - - if (uvCoords != null && uvCoords.size() > 0) { - for (Entry> entry : uvCoords.entrySet()) { - if (this.uvCoords.containsKey(entry.getKey())) { - this.uvCoords.get(entry.getKey()).add(entry.getValue().get(i)); - } else { - List uvs = new ArrayList(); - uvs.add(entry.getValue().get(i)); - this.uvCoords.put(entry.getKey(), uvs); - } - } - } - - if (vertexGroups.size() > 0) { - Map group = vertexGroups.get(i); - maximumWeightsPerVertex = Math.max(maximumWeightsPerVertex, group.size()); - boneWeightAndIndexes.add(new TreeMap(group)); - } - } - } - } - - /** - * Returns UV coordinates assigned for the vertex with the proper index. - * @param vertexIndex - * the index of the vertex - * @param uvs - * all UV coordinates we search in - * @return a set of UV coordinates assigned to the given vertex - */ - private Map getUVsForVertex(int vertexIndex, Map> uvs) { - if (uvs == null || uvs.size() == 0) { - return null; - } - Map result = new HashMap(uvs.size()); - for (Entry> entry : uvs.entrySet()) { - result.put(entry.getKey(), entry.getValue().get(vertexIndex)); - } - return result; - } - - /** - * The method returns an index of a vertex described by the given data. - * The method tries to find a vertex that mathes the given data. If it does it means - * that such vertex is already used. - * @param vert - * the vertex position coordinates - * @param normal - * the vertex's normal vector - * @param uvCoords - * the UV coords of the vertex - * @return index of the found vertex of -1 - */ - private int indexOf(Vector3f vert, Vector3f normal, Map uvCoords) { - for (int i = 0; i < verts.size(); ++i) { - if (verts.get(i).equals(vert) && normals.get(i).equals(normal)) { - if (uvCoords != null && uvCoords.size() > 0) { - for (Entry entry : uvCoords.entrySet()) { - List uvs = this.uvCoords.get(entry.getKey()); - if (uvs == null) { - return -1; - } - if (!uvs.get(i).equals(entry.getValue())) { - return -1; - } - } - } - return i; - } - } - return -1; - } - - /** - * The method normalizes the weights and bone indexes data. - * First it truncates the amount to MAXIMUM_WEIGHTS_PER_VERTEX because this is how many weights JME can handle. - * Next it normalizes the weights so that the sum of all verts is 1. - * @param maximumSize - * the maximum size that the data will be truncated to (usually: MAXIMUM_WEIGHTS_PER_VERTEX) - */ - private void normalizeBoneBuffers(int maximumSize) { - for (TreeMap group : boneWeightAndIndexes) { - if (group.size() > maximumSize) { - NavigableMap descendingWeights = group.descendingMap(); - while (descendingWeights.size() > maximumSize) { - descendingWeights.pollLastEntry(); - } - } - - // normalizing the weights so that the sum of the values is equal to '1' - TreeMap normalizedGroup = new TreeMap(); - float sum = 0; - for (Entry entry : group.entrySet()) { - sum += entry.getKey(); - } - - if (sum != 0 && sum != 1) { - for (Entry entry : group.entrySet()) { - normalizedGroup.put(entry.getKey() / sum, entry.getValue()); - } - group.clear(); - group.putAll(normalizedGroup); - } - } - } - - /** - * A class that gathers the data for mesh bone buffers. - * Added to increase code readability. - * - * @author Marcin Roguski (Kaelthas) - */ - public static class BoneBuffersData { - public final int maximumWeightsPerVertex; - public final VertexBuffer verticesWeights; - public final VertexBuffer verticesWeightsIndices; - - public BoneBuffersData(int maximumWeightsPerVertex, VertexBuffer verticesWeights, VertexBuffer verticesWeightsIndices) { - this.maximumWeightsPerVertex = maximumWeightsPerVertex; - this.verticesWeights = verticesWeights; - this.verticesWeightsIndices = verticesWeightsIndices; - } - } -} diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/meshes/MeshHelper.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/meshes/MeshHelper.java deleted file mode 100644 index 6a86ffecf..000000000 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/meshes/MeshHelper.java +++ /dev/null @@ -1,381 +0,0 @@ -/* - * Copyright (c) 2009-2019 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.blender.meshes; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.logging.Level; -import java.util.logging.Logger; - -import com.jme3.material.Material; -import com.jme3.math.ColorRGBA; -import com.jme3.math.Vector2f; -import com.jme3.math.Vector3f; -import com.jme3.scene.plugins.blender.AbstractBlenderHelper; -import com.jme3.scene.plugins.blender.BlenderContext; -import com.jme3.scene.plugins.blender.BlenderContext.LoadedDataType; -import com.jme3.scene.plugins.blender.file.BlenderFileException; -import com.jme3.scene.plugins.blender.file.DynamicArray; -import com.jme3.scene.plugins.blender.file.Pointer; -import com.jme3.scene.plugins.blender.file.Structure; -import com.jme3.scene.plugins.blender.materials.MaterialHelper; -import com.jme3.scene.plugins.blender.objects.Properties; - -/** - * A class that is used in mesh calculations. - * - * @author Marcin Roguski (Kaelthas) - */ -public class MeshHelper extends AbstractBlenderHelper { - private static final Logger LOGGER = Logger.getLogger(MeshHelper.class.getName()); - - /** A type of UV data layer in traditional faced mesh (triangles or quads). */ - public static final int UV_DATA_LAYER_TYPE_FMESH = 5; - /** A type of UV data layer in bmesh type. */ - public static final int UV_DATA_LAYER_TYPE_BMESH = 16; - - /** A material used for single lines and points. */ - private Material blackUnshadedMaterial; - - /** - * This constructor parses the given blender version and stores the result. Some functionalities may differ in different blender - * versions. - * - * @param blenderVersion - * the version read from the blend file - * @param blenderContext - * the blender context - */ - public MeshHelper(String blenderVersion, BlenderContext blenderContext) { - super(blenderVersion, blenderContext); - } - - /** - * Converts the mesh structure into temporal mesh. - * The temporal mesh is stored in blender context and here always a clone is being returned because the mesh might - * be modified by modifiers. - * - * @param meshStructure - * the mesh structure - * @param blenderContext - * the blender context - * @return temporal mesh read from the given structure - * @throws BlenderFileException - * an exception is thrown when problems with reading blend file occur - */ - public TemporalMesh toTemporalMesh(Structure meshStructure, BlenderContext blenderContext) throws BlenderFileException { - LOGGER.log(Level.FINE, "Loading temporal mesh named: {0}.", meshStructure.getName()); - TemporalMesh temporalMesh = (TemporalMesh) blenderContext.getLoadedFeature(meshStructure.getOldMemoryAddress(), LoadedDataType.TEMPORAL_MESH); - if (temporalMesh != null) { - LOGGER.fine("The mesh is already loaded. Returning its clone."); - return temporalMesh.clone(); - } - - if ("ID".equals(meshStructure.getType())) { - LOGGER.fine("Loading mesh from external blend file."); - return (TemporalMesh) this.loadLibrary(meshStructure); - } - - String name = meshStructure.getName(); - LOGGER.log(Level.FINE, "Reading mesh: {0}.", name); - temporalMesh = new TemporalMesh(meshStructure, blenderContext); - - LOGGER.fine("Loading materials."); - MaterialHelper materialHelper = blenderContext.getHelper(MaterialHelper.class); - temporalMesh.setMaterials(materialHelper.getMaterials(meshStructure, blenderContext)); - - LOGGER.fine("Reading custom properties."); - Properties properties = this.loadProperties(meshStructure, blenderContext); - temporalMesh.setProperties(properties); - - blenderContext.addLoadedFeatures(meshStructure.getOldMemoryAddress(), LoadedDataType.STRUCTURE, meshStructure); - blenderContext.addLoadedFeatures(meshStructure.getOldMemoryAddress(), LoadedDataType.TEMPORAL_MESH, temporalMesh); - return temporalMesh.clone(); - } - - /** - * Tells if the given mesh structure supports BMesh. - * - * @param meshStructure - * the mesh structure - * @return true if BMesh is supported and false otherwise - */ - public boolean isBMeshCompatible(Structure meshStructure) { - Pointer pMLoop = (Pointer) meshStructure.getFieldValue("mloop"); - Pointer pMPoly = (Pointer) meshStructure.getFieldValue("mpoly"); - return pMLoop != null && pMPoly != null && pMLoop.isNotNull() && pMPoly.isNotNull(); - } - - /** - * This method returns the vertices: a list of vertex positions and a list - * of vertex normals. - * - * @param meshStructure - * the structure containing the mesh data - * @throws BlenderFileException - * this exception is thrown when the blend file structure is somehow invalid or corrupted - */ - @SuppressWarnings("unchecked") - public void loadVerticesAndNormals(Structure meshStructure, List vertices, List normals) throws BlenderFileException { - LOGGER.log(Level.FINE, "Loading vertices and normals from mesh: {0}.", meshStructure.getName()); - int count = ((Number) meshStructure.getFieldValue("totvert")).intValue(); - if (count > 0) { - Pointer pMVert = (Pointer) meshStructure.getFieldValue("mvert"); - List mVerts = pMVert.fetchData(); - Vector3f co = null, no = null; - if (fixUpAxis) { - for (int i = 0; i < count; ++i) { - DynamicArray coordinates = (DynamicArray) mVerts.get(i).getFieldValue("co"); - co = new Vector3f(coordinates.get(0).floatValue(), coordinates.get(2).floatValue(), -coordinates.get(1).floatValue()); - vertices.add(co); - - DynamicArray norm = (DynamicArray) mVerts.get(i).getFieldValue("no"); - no = new Vector3f(norm.get(0).shortValue() / 32767.0f, norm.get(2).shortValue() / 32767.0f, -norm.get(1).shortValue() / 32767.0f); - normals.add(no); - } - } else { - for (int i = 0; i < count; ++i) { - DynamicArray coordinates = (DynamicArray) mVerts.get(i).getFieldValue("co"); - co = new Vector3f(coordinates.get(0).floatValue(), coordinates.get(1).floatValue(), coordinates.get(2).floatValue()); - vertices.add(co); - - DynamicArray norm = (DynamicArray) mVerts.get(i).getFieldValue("no"); - no = new Vector3f(norm.get(0).shortValue() / 32767.0f, norm.get(1).shortValue() / 32767.0f, norm.get(2).shortValue() / 32767.0f); - normals.add(no); - } - } - } - LOGGER.log(Level.FINE, "Loaded {0} vertices and normals.", vertices.size()); - } - - /** - * This method returns the vertices colors. Each vertex is stored in byte[4] array. - * - * @param meshStructure - * the structure containing the mesh data - * @param blenderContext - * the blender context - * @return a list of vertices colors, each color belongs to a single vertex or empty list of colors are not specified - * @throws BlenderFileException - * this exception is thrown when the blend file structure is somehow invalid or corrupted - */ - public List loadVerticesColors(Structure meshStructure, BlenderContext blenderContext) throws BlenderFileException { - LOGGER.log(Level.FINE, "Loading vertices colors from mesh: {0}.", meshStructure.getName()); - MeshHelper meshHelper = blenderContext.getHelper(MeshHelper.class); - Pointer pMCol = (Pointer) meshStructure.getFieldValue(meshHelper.isBMeshCompatible(meshStructure) ? "mloopcol" : "mcol"); - List verticesColors = new ArrayList(); - // it was likely a bug in blender untill version 2.63 (the blue and red factors were misplaced in their structure) - // so we need to put them right - boolean useBGRA = blenderContext.getBlenderVersion() < 263; - if (pMCol.isNotNull()) { - List mCol = pMCol.fetchData(); - for (Structure color : mCol) { - byte r = ((Number) color.getFieldValue("r")).byteValue(); - byte g = ((Number) color.getFieldValue("g")).byteValue(); - byte b = ((Number) color.getFieldValue("b")).byteValue(); - byte a = ((Number) color.getFieldValue("a")).byteValue(); - verticesColors.add(useBGRA ? new byte[] { b, g, r, a } : new byte[] { r, g, b, a }); - } - } - return verticesColors; - } - - /** - * The method loads the UV coordinates. The result is a map where the key is the user's UV set name and the values are UV coordinates. - * But depending on the mesh type (triangle/quads or bmesh) the lists in the map have different meaning. - * For bmesh they are enlisted just like they are stored in the blend file (in loops). - * For traditional faces every 4 UV's should be assigned for a single face. - * @param meshStructure - * the mesh structure - * @return a map that sorts UV coordinates between different UV sets - * @throws BlenderFileException - * an exception is thrown when problems with blend file occur - */ - @SuppressWarnings("unchecked") - public LinkedHashMap> loadUVCoordinates(Structure meshStructure) throws BlenderFileException { - LOGGER.log(Level.FINE, "Loading UV coordinates from mesh: {0}.", meshStructure.getName()); - LinkedHashMap> result = new LinkedHashMap>(); - if (this.isBMeshCompatible(meshStructure)) { - // in this case the UV's are assigned to vertices (an array is the same length as the vertex array) - Structure loopData = (Structure) meshStructure.getFieldValue("ldata"); - Pointer pLoopDataLayers = (Pointer) loopData.getFieldValue("layers"); - List loopDataLayers = pLoopDataLayers.fetchData(); - for (Structure structure : loopDataLayers) { - Pointer p = (Pointer) structure.getFieldValue("data"); - if (p.isNotNull() && ((Number) structure.getFieldValue("type")).intValue() == MeshHelper.UV_DATA_LAYER_TYPE_BMESH) { - String uvSetName = structure.getFieldValue("name").toString(); - List uvsStructures = p.fetchData(); - List uvs = new ArrayList(uvsStructures.size()); - for (Structure uvStructure : uvsStructures) { - DynamicArray loopUVS = (DynamicArray) uvStructure.getFieldValue("uv"); - uvs.add(new Vector2f(loopUVS.get(0).floatValue(), loopUVS.get(1).floatValue())); - } - result.put(uvSetName, uvs); - } - } - } else { - // in this case UV's are assigned to faces (the array has the same length as the faces count) - Structure facesData = (Structure) meshStructure.getFieldValue("fdata"); - Pointer pFacesDataLayers = (Pointer) facesData.getFieldValue("layers"); - if (pFacesDataLayers.isNotNull()) { - List facesDataLayers = pFacesDataLayers.fetchData(); - for (Structure structure : facesDataLayers) { - Pointer p = (Pointer) structure.getFieldValue("data"); - if (p.isNotNull() && ((Number) structure.getFieldValue("type")).intValue() == MeshHelper.UV_DATA_LAYER_TYPE_FMESH) { - String uvSetName = structure.getFieldValue("name").toString(); - List uvsStructures = p.fetchData(); - List uvs = new ArrayList(uvsStructures.size()); - for (Structure uvStructure : uvsStructures) { - DynamicArray mFaceUVs = (DynamicArray) uvStructure.getFieldValue("uv"); - uvs.add(new Vector2f(mFaceUVs.get(0).floatValue(), mFaceUVs.get(1).floatValue())); - uvs.add(new Vector2f(mFaceUVs.get(2).floatValue(), mFaceUVs.get(3).floatValue())); - uvs.add(new Vector2f(mFaceUVs.get(4).floatValue(), mFaceUVs.get(5).floatValue())); - uvs.add(new Vector2f(mFaceUVs.get(6).floatValue(), mFaceUVs.get(7).floatValue())); - } - result.put(uvSetName, uvs); - } - } - } - } - return result; - } - - /** - * Loads all vertices groups. - * @param meshStructure - * the mesh structure - * @return a list of vertex groups for every vertex in the mesh - * @throws BlenderFileException - * an exception is thrown when problems with blend file occur - */ - public List> loadVerticesGroups(Structure meshStructure) throws BlenderFileException { - LOGGER.log(Level.FINE, "Loading vertices groups from mesh: {0}.", meshStructure.getName()); - List> result = new ArrayList>(); - - Structure parent = blenderContext.peekParent(); - if(parent != null) { - // the mesh might be saved without its parent (it is then unused) - Structure defbase = (Structure) parent.getFieldValue("defbase"); - List groupNames = new ArrayList(); - List defs = defbase.evaluateListBase(); - - if(!defs.isEmpty()) { - for (Structure def : defs) { - groupNames.add(def.getFieldValue("name").toString()); - } - - Pointer pDvert = (Pointer) meshStructure.getFieldValue("dvert");// dvert = DeformVERTices - if (pDvert.isNotNull()) {// assigning weights and bone indices - List dverts = pDvert.fetchData(); - for (Structure dvert : dverts) { - Map weightsForVertex = new HashMap(); - Pointer pDW = (Pointer) dvert.getFieldValue("dw"); - if (pDW.isNotNull()) { - List dw = pDW.fetchData(); - for (Structure deformWeight : dw) { - int groupIndex = ((Number) deformWeight.getFieldValue("def_nr")).intValue(); - float weight = ((Number) deformWeight.getFieldValue("weight")).floatValue(); - String groupName = groupNames.get(groupIndex); - - weightsForVertex.put(groupName, weight); - } - } - result.add(weightsForVertex); - } - } - } - } - return result; - } - - /** - * Selects the proper subsets of UV coordinates for the given sublist of indexes. - * @param face - * the face with the original UV sets - * @param indexesSublist - * the sub list of indexes - * @return a map of UV coordinates subsets - */ - public Map> selectUVSubset(Face face, Integer... indexesSublist) { - Map> result = null; - if (face.getUvSets() != null) { - result = new HashMap>(); - for (Entry> entry : face.getUvSets().entrySet()) { - List uvs = new ArrayList(indexesSublist.length); - for (Integer index : indexesSublist) { - uvs.add(entry.getValue().get(face.getIndexes().indexOf(index))); - } - result.put(entry.getKey(), uvs); - } - } - return result; - } - - /** - * Selects the proper subsets of vertex colors for the given sublist of indexes. - * @param face - * the face with the original vertex colors - * @param indexesSublist - * the sub list of indexes - * @return a sublist of vertex colors - */ - public List selectVertexColorSubset(Face face, Integer... indexesSublist) { - List result = null; - List vertexColors = face.getVertexColors(); - if (vertexColors != null) { - result = new ArrayList(indexesSublist.length); - for (Integer index : indexesSublist) { - result.add(vertexColors.get(face.getIndexes().indexOf(index))); - } - } - return result; - } - - /** - * Returns the black unshaded material. It is used for lines and points because that is how blender - * renders it. - * @param blenderContext - * the blender context - * @return black unshaded material - */ - public synchronized Material getBlackUnshadedMaterial(BlenderContext blenderContext) { - if (blackUnshadedMaterial == null) { - blackUnshadedMaterial = new Material(blenderContext.getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md"); - blackUnshadedMaterial.setColor("Color", ColorRGBA.Black); - } - return blackUnshadedMaterial; - } -} diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/meshes/Point.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/meshes/Point.java deleted file mode 100644 index 04f705e5b..000000000 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/meshes/Point.java +++ /dev/null @@ -1,93 +0,0 @@ -package com.jme3.scene.plugins.blender.meshes; - -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.logging.Level; -import java.util.logging.Logger; - -import com.jme3.scene.plugins.blender.file.BlenderFileException; -import com.jme3.scene.plugins.blender.file.Pointer; -import com.jme3.scene.plugins.blender.file.Structure; -import com.jme3.scene.plugins.blender.meshes.IndexesLoop.IndexPredicate; - -/** - * A class that represents a single point on the scene that is not a part of an edge. - * - * @author Marcin Roguski (Kaelthas) - */ -public class Point { - private static final Logger LOGGER = Logger.getLogger(Point.class.getName()); - - /** The point's index. */ - private int index; - - /** - * Constructs a point for a given index. - * @param index - * the index of the point - */ - public Point(int index) { - this.index = index; - } - - @Override - public Point clone() { - return new Point(index); - } - - /** - * @return the index of the point - */ - public int getIndex() { - return index; - } - - /** - * The method shifts the index by a given value. - * @param shift - * the value to shift the index - * @param predicate - * the predicate that verifies which indexes should be shifted; if null then all will be shifted - */ - public void shiftIndexes(int shift, IndexPredicate predicate) { - if (predicate == null || predicate.execute(index)) { - index += shift; - } - } - - /** - * Loads all points of the mesh that do not belong to any edge. - * @param meshStructure - * the mesh structure - * @return a list of points - * @throws BlenderFileException - * an exception is thrown when problems with file reading occur - */ - public static List loadAll(Structure meshStructure) throws BlenderFileException { - LOGGER.log(Level.FINE, "Loading all points that do not belong to any edge from mesh: {0}", meshStructure.getName()); - List result = new ArrayList(); - - Pointer pMEdge = (Pointer) meshStructure.getFieldValue("medge"); - if (pMEdge.isNotNull()) { - int count = ((Number) meshStructure.getFieldValue("totvert")).intValue(); - Set unusedVertices = new HashSet(count); - for (int i = 0; i < count; ++i) { - unusedVertices.add(i); - } - - List edges = pMEdge.fetchData(); - for (Structure edge : edges) { - unusedVertices.remove(((Number) edge.getFieldValue("v1")).intValue()); - unusedVertices.remove(((Number) edge.getFieldValue("v2")).intValue()); - } - - for (Integer unusedIndex : unusedVertices) { - result.add(new Point(unusedIndex)); - } - } - LOGGER.log(Level.FINE, "Loaded {0} points.", result.size()); - return result; - } -} diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/meshes/TemporalMesh.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/meshes/TemporalMesh.java deleted file mode 100644 index 1b42b0c39..000000000 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/meshes/TemporalMesh.java +++ /dev/null @@ -1,767 +0,0 @@ -package com.jme3.scene.plugins.blender.meshes; - -import java.nio.IntBuffer; -import java.nio.ShortBuffer; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; -import java.util.logging.Level; -import java.util.logging.Logger; - -import com.jme3.bounding.BoundingBox; -import com.jme3.bounding.BoundingVolume; -import com.jme3.material.Material; -import com.jme3.material.RenderState.FaceCullMode; -import com.jme3.math.FastMath; -import com.jme3.math.Vector2f; -import com.jme3.math.Vector3f; -import com.jme3.scene.Geometry; -import com.jme3.scene.Mesh; -import com.jme3.scene.Mesh.Mode; -import com.jme3.scene.Node; -import com.jme3.scene.VertexBuffer; -import com.jme3.scene.VertexBuffer.Type; -import com.jme3.scene.VertexBuffer.Usage; -import com.jme3.scene.plugins.blender.BlenderContext; -import com.jme3.scene.plugins.blender.BlenderContext.LoadedDataType; -import com.jme3.scene.plugins.blender.file.BlenderFileException; -import com.jme3.scene.plugins.blender.file.Structure; -import com.jme3.scene.plugins.blender.materials.MaterialContext; -import com.jme3.scene.plugins.blender.meshes.Face.TriangulationWarning; -import com.jme3.scene.plugins.blender.meshes.MeshBuffers.BoneBuffersData; -import com.jme3.scene.plugins.blender.modifiers.Modifier; -import com.jme3.scene.plugins.blender.objects.Properties; - -/** - * The class extends Geometry so that it can be temporalily added to the object's node. - * Later each such node's child will be transformed into a list of geometries. - * - * @author Marcin Roguski (Kaelthas) - */ -public class TemporalMesh extends Geometry { - private static final Logger LOGGER = Logger.getLogger(TemporalMesh.class.getName()); - /** A minimum weight value. */ - private static final double MINIMUM_BONE_WEIGHT = FastMath.DBL_EPSILON; - - /** The blender context. */ - protected final BlenderContext blenderContext; - - /** The mesh's structure. */ - protected final Structure meshStructure; - - /** Loaded vertices. */ - protected List vertices = new ArrayList(); - /** Loaded normals. */ - protected List normals = new ArrayList(); - /** Loaded vertex groups. */ - protected List> vertexGroups = new ArrayList>(); - /** Loaded vertex colors. */ - protected List verticesColors = new ArrayList(); - - /** Materials used by the mesh. */ - protected MaterialContext[] materials; - /** The properties of the mesh. */ - protected Properties properties; - /** The bone indexes. */ - protected Map boneIndexes = new HashMap(); - /** The modifiers that should be applied after the mesh has been created. */ - protected List postMeshCreationModifiers = new ArrayList(); - - /** The faces of the mesh. */ - protected List faces = new ArrayList(); - /** The edges of the mesh. */ - protected List edges = new ArrayList(); - /** The points of the mesh. */ - protected List points = new ArrayList(); - /** A map between index and faces that contain it (for faster index - face queries). */ - protected Map> indexToFaceMapping = new HashMap>(); - /** A map between index and edges that contain it (for faster index - edge queries). */ - protected Map> indexToEdgeMapping = new HashMap>(); - - /** The bounding box of the temporal mesh. */ - protected BoundingBox boundingBox; - - /** - * Creates a temporal mesh based on the given mesh structure. - * @param meshStructure - * the mesh structure - * @param blenderContext - * the blender context - * @throws BlenderFileException - * an exception is thrown when problems with file reading occur - */ - public TemporalMesh(Structure meshStructure, BlenderContext blenderContext) throws BlenderFileException { - this(meshStructure, blenderContext, true); - } - - /** - * Creates a temporal mesh based on the given mesh structure. - * @param meshStructure - * the mesh structure - * @param blenderContext - * the blender context - * @param loadData - * tells if the data should be loaded from the mesh structure - * @throws BlenderFileException - * an exception is thrown when problems with file reading occur - */ - protected TemporalMesh(Structure meshStructure, BlenderContext blenderContext, boolean loadData) throws BlenderFileException { - this.blenderContext = blenderContext; - this.meshStructure = meshStructure; - - if (loadData) { - name = meshStructure.getName(); - - MeshHelper meshHelper = blenderContext.getHelper(MeshHelper.class); - - meshHelper.loadVerticesAndNormals(meshStructure, vertices, normals); - verticesColors = meshHelper.loadVerticesColors(meshStructure, blenderContext); - LinkedHashMap> userUVGroups = meshHelper.loadUVCoordinates(meshStructure); - vertexGroups = meshHelper.loadVerticesGroups(meshStructure); - - faces = Face.loadAll(meshStructure, userUVGroups, verticesColors, this, blenderContext); - edges = Edge.loadAll(meshStructure, this); - points = Point.loadAll(meshStructure); - - this.rebuildIndexesMappings(); - } - } - - /** - * @return the blender context - */ - public BlenderContext getBlenderContext() { - return blenderContext; - } - - /** - * @return the vertices of the mesh - */ - public List getVertices() { - return vertices; - } - - /** - * @return the normals of the mesh - */ - public List getNormals() { - return normals; - } - - /** - * @return all faces - */ - public List getFaces() { - return faces; - } - - /** - * @return all edges - */ - public List getEdges() { - return edges; - } - - /** - * @return all points (do not mistake it with vertices) - */ - public List getPoints() { - return points; - } - - /** - * @return all vertices colors - */ - public List getVerticesColors() { - return verticesColors; - } - - /** - * @return all vertex groups for the vertices (each map has groups for the proper vertex) - */ - public List> getVertexGroups() { - return vertexGroups; - } - - /** - * @return the faces that contain the given index or null if none contain it - */ - public Collection getAdjacentFaces(Integer index) { - return indexToFaceMapping.get(index); - } - - /** - * @param edge the edge of the mesh - * @return a list of faces that contain the given edge or an empty list - */ - public Collection getAdjacentFaces(Edge edge) { - List result = new ArrayList(indexToFaceMapping.get(edge.getFirstIndex())); - Set secondIndexAdjacentFaces = indexToFaceMapping.get(edge.getSecondIndex()); - if (secondIndexAdjacentFaces != null) { - result.retainAll(indexToFaceMapping.get(edge.getSecondIndex())); - } - return result; - } - - /** - * @param index the index of the mesh - * @return a list of edges that contain the index - */ - public Collection getAdjacentEdges(Integer index) { - return indexToEdgeMapping.get(index); - } - - /** - * Tells if the given edge is a boundary edge. The boundary edge means that it belongs to a single - * face or to none. - * @param edge the edge of the mesh - * @return true if the edge is a boundary one and false otherwise - */ - public boolean isBoundary(Edge edge) { - return this.getAdjacentFaces(edge).size() <= 1; - } - - /** - * The method tells if the given index is a boundary index. A boundary index belongs to at least - * one boundary edge. - * @param index - * the index of the mesh - * @return true if the index is a boundary one and false otherwise - */ - public boolean isBoundary(Integer index) { - Collection adjacentEdges = this.getAdjacentEdges(index); - for (Edge edge : adjacentEdges) { - if (this.isBoundary(edge)) { - return true; - } - } - return false; - } - - @Override - public TemporalMesh clone() { - try { - TemporalMesh result = new TemporalMesh(meshStructure, blenderContext, false); - result.name = name; - for (Vector3f v : vertices) { - result.vertices.add(v.clone()); - } - for (Vector3f n : normals) { - result.normals.add(n.clone()); - } - for (Map group : vertexGroups) { - result.vertexGroups.add(new HashMap(group)); - } - for (byte[] vertColor : verticesColors) { - result.verticesColors.add(vertColor.clone()); - } - result.materials = materials; - result.properties = properties; - result.boneIndexes.putAll(boneIndexes); - result.postMeshCreationModifiers.addAll(postMeshCreationModifiers); - for (Face face : faces) { - result.faces.add(face.clone()); - } - for (Edge edge : edges) { - result.edges.add(edge.clone()); - } - for (Point point : points) { - result.points.add(point.clone()); - } - result.rebuildIndexesMappings(); - return result; - } catch (BlenderFileException e) { - LOGGER.log(Level.SEVERE, "Error while cloning the temporal mesh: {0}. Returning null.", e.getLocalizedMessage()); - } - return null; - } - - /** - * The method rebuilds the mappings between faces and edges. Should be called after - * every major change of the temporal mesh done outside it. - *

    - * Note: I will remove this method soon and cause the mappings to be done - * automatically when the mesh is modified. - */ - public void rebuildIndexesMappings() { - indexToEdgeMapping.clear(); - indexToFaceMapping.clear(); - for (Face face : faces) { - for (Integer index : face.getIndexes()) { - Set faces = indexToFaceMapping.get(index); - if (faces == null) { - faces = new HashSet(); - indexToFaceMapping.put(index, faces); - } - faces.add(face); - } - } - for (Edge edge : edges) { - Set edges = indexToEdgeMapping.get(edge.getFirstIndex()); - if (edges == null) { - edges = new HashSet(); - indexToEdgeMapping.put(edge.getFirstIndex(), edges); - } - edges.add(edge); - edges = indexToEdgeMapping.get(edge.getSecondIndex()); - if (edges == null) { - edges = new HashSet(); - indexToEdgeMapping.put(edge.getSecondIndex(), edges); - } - edges.add(edge); - } - } - - @Override - public void updateModelBound() { - if (boundingBox == null) { - boundingBox = new BoundingBox(); - } - Vector3f min = new Vector3f(Float.MAX_VALUE, Float.MAX_VALUE, Float.MAX_VALUE); - Vector3f max = new Vector3f(Float.MIN_VALUE, Float.MIN_VALUE, Float.MIN_VALUE); - for (Vector3f v : vertices) { - min.set(Math.min(min.x, v.x), Math.min(min.y, v.y), Math.min(min.z, v.z)); - max.set(Math.max(max.x, v.x), Math.max(max.y, v.y), Math.max(max.z, v.z)); - } - boundingBox.setMinMax(min, max); - } - - @Override - public BoundingVolume getModelBound() { - this.updateModelBound(); - return boundingBox; - } - - @Override - public BoundingVolume getWorldBound() { - this.updateModelBound(); - Node parent = this.getParent(); - if (parent != null) { - BoundingVolume bv = boundingBox.clone(); - bv.setCenter(parent.getWorldTranslation()); - return bv; - } else { - return boundingBox; - } - } - - /** - * Triangulates the mesh. - */ - public void triangulate() { - Set warnings = new HashSet<>(TriangulationWarning.values().length - 1); - LOGGER.fine("Triangulating temporal mesh."); - for (Face face : faces) { - TriangulationWarning warning = face.triangulate(); - if(warning != TriangulationWarning.NONE) { - warnings.add(warning); - } - } - - if(warnings.size() > 0 && LOGGER.isLoggable(Level.WARNING)) { - StringBuilder sb = new StringBuilder(512); - sb.append("There were problems with triangulating the faces of a mesh: ").append(name); - for(TriangulationWarning w : warnings) { - sb.append("\n\t").append(w); - } - LOGGER.warning(sb.toString()); - } - } - - /** - * The method appends the given mesh to the current one. New faces and vertices and indexes are added. - * @param mesh - * the mesh to be appended - */ - public void append(TemporalMesh mesh) { - if (mesh != null) { - // we need to shift the indexes in faces, lines and points - int shift = vertices.size(); - if (shift > 0) { - for (Face face : mesh.faces) { - face.getIndexes().shiftIndexes(shift, null); - face.setTemporalMesh(this); - } - for (Edge edge : mesh.edges) { - edge.shiftIndexes(shift, null); - } - for (Point point : mesh.points) { - point.shiftIndexes(shift, null); - } - } - - faces.addAll(mesh.faces); - edges.addAll(mesh.edges); - points.addAll(mesh.points); - - vertices.addAll(mesh.vertices); - normals.addAll(mesh.normals); - vertexGroups.addAll(mesh.vertexGroups); - verticesColors.addAll(mesh.verticesColors); - boneIndexes.putAll(mesh.boneIndexes); - - this.rebuildIndexesMappings(); - } - } - - /** - * Sets the properties of the mesh. - * @param properties - * the properties of the mesh - */ - public void setProperties(Properties properties) { - this.properties = properties; - } - - /** - * Sets the materials of the mesh. - * @param materials - * the materials of the mesh - */ - public void setMaterials(MaterialContext[] materials) { - this.materials = materials; - } - - /** - * Adds bone index to the mesh. - * @param boneName - * the name of the bone - * @param boneIndex - * the index of the bone - */ - public void addBoneIndex(String boneName, Integer boneIndex) { - boneIndexes.put(boneName, boneIndex); - } - - /** - * The modifier to be applied after the geometries are created. - * @param modifier - * the modifier to be applied - */ - public void applyAfterMeshCreate(Modifier modifier) { - postMeshCreationModifiers.add(modifier); - } - - @Override - public int getVertexCount() { - return vertices.size(); - } - - /** - * Removes all vertices from the mesh. - */ - public void clear() { - vertices.clear(); - normals.clear(); - vertexGroups.clear(); - verticesColors.clear(); - faces.clear(); - edges.clear(); - points.clear(); - indexToEdgeMapping.clear(); - indexToFaceMapping.clear(); - } - - /** - * The mesh builds geometries from the mesh. The result is stored in the blender context - * under the mesh's OMA. - */ - public void toGeometries() { - LOGGER.log(Level.FINE, "Converting temporal mesh {0} to jme geometries.", name); - List result = new ArrayList(); - MeshHelper meshHelper = blenderContext.getHelper(MeshHelper.class); - Node parent = this.getParent(); - parent.detachChild(this); - - this.prepareFacesGeometry(result, meshHelper); - this.prepareLinesGeometry(result, meshHelper); - this.preparePointsGeometry(result, meshHelper); - - blenderContext.addLoadedFeatures(meshStructure.getOldMemoryAddress(), LoadedDataType.FEATURE, result); - - for (Geometry geometry : result) { - parent.attachChild(geometry); - } - - for (Modifier modifier : postMeshCreationModifiers) { - modifier.postMeshCreationApply(parent, blenderContext); - } - } - - /** - * The method creates geometries from faces. - * @param result - * the list where new geometries will be appended - * @param meshHelper - * the mesh helper - */ - protected void prepareFacesGeometry(List result, MeshHelper meshHelper) { - LOGGER.fine("Preparing faces geometries."); - this.triangulate(); - - Vector3f[] tempVerts = new Vector3f[3]; - Vector3f[] tempNormals = new Vector3f[3]; - byte[][] tempVertColors = new byte[3][]; - List> boneBuffers = new ArrayList>(3); - - LOGGER.log(Level.FINE, "Appending {0} faces to mesh buffers.", faces.size()); - Map faceMeshes = new HashMap(); - for (Face face : faces) { - MeshBuffers meshBuffers = faceMeshes.get(face.getMaterialNumber()); - if (meshBuffers == null) { - meshBuffers = new MeshBuffers(face.getMaterialNumber()); - faceMeshes.put(face.getMaterialNumber(), meshBuffers); - } - - List> triangulatedIndexes = face.getCurrentIndexes(); - List vertexColors = face.getVertexColors(); - - for (List indexes : triangulatedIndexes) { - assert indexes.size() == 3 : "The mesh has not been properly triangulated!"; - - Vector3f normal = null; - if(!face.isSmooth()) { - normal = FastMath.computeNormal(vertices.get(indexes.get(0)), vertices.get(indexes.get(1)), vertices.get(indexes.get(2))); - } - - boneBuffers.clear(); - for (int i = 0; i < 3; ++i) { - int vertIndex = indexes.get(i); - tempVerts[i] = vertices.get(vertIndex); - tempNormals[i] = normal != null ? normal : normals.get(vertIndex); - tempVertColors[i] = vertexColors != null ? vertexColors.get(face.getIndexes().indexOf(vertIndex)) : null; - - if (boneIndexes.size() > 0 && vertexGroups.size() > 0) { - Map boneBuffersForVertex = new HashMap(); - Map vertexGroupsForVertex = vertexGroups.get(vertIndex); - for (Entry entry : boneIndexes.entrySet()) { - if (vertexGroupsForVertex.containsKey(entry.getKey())) { - float weight = vertexGroupsForVertex.get(entry.getKey()); - if (weight > MINIMUM_BONE_WEIGHT) { - // only values of weight greater than MINIMUM_BONE_WEIGHT are used - // if all non zero weights were used, and they were samm enough, problems with normalisation would occur - // because adding a very small value to 1.0 will give 1.0 - // so in order to avoid such errors, which can cause severe animation artifacts we need to use some minimum weight value - boneBuffersForVertex.put(weight, entry.getValue()); - } - } - } - if (boneBuffersForVertex.size() == 0) {// attach the vertex to zero-indexed bone so that it does not collapse to (0, 0, 0) - boneBuffersForVertex.put(1.0f, 0); - } - boneBuffers.add(boneBuffersForVertex); - } - } - - Map> uvs = meshHelper.selectUVSubset(face, indexes.toArray(new Integer[indexes.size()])); - meshBuffers.append(face.isSmooth(), tempVerts, tempNormals, uvs, tempVertColors, boneBuffers); - } - } - - LOGGER.fine("Converting mesh buffers to geometries."); - Map geometryToBuffersMap = new HashMap(); - for (Entry entry : faceMeshes.entrySet()) { - MeshBuffers meshBuffers = entry.getValue(); - - Mesh mesh = new Mesh(); - - if (meshBuffers.isShortIndexBuffer()) { - mesh.setBuffer(Type.Index, 1, (ShortBuffer) meshBuffers.getIndexBuffer()); - } else { - mesh.setBuffer(Type.Index, 1, (IntBuffer) meshBuffers.getIndexBuffer()); - } - mesh.setBuffer(meshBuffers.getPositionsBuffer()); - mesh.setBuffer(meshBuffers.getNormalsBuffer()); - if (meshBuffers.areVertexColorsUsed()) { - mesh.setBuffer(Type.Color, 4, meshBuffers.getVertexColorsBuffer()); - mesh.getBuffer(Type.Color).setNormalized(true); - } - - BoneBuffersData boneBuffersData = meshBuffers.getBoneBuffers(); - if (boneBuffersData != null) { - mesh.setMaxNumWeights(boneBuffersData.maximumWeightsPerVertex); - mesh.setBuffer(boneBuffersData.verticesWeights); - mesh.setBuffer(boneBuffersData.verticesWeightsIndices); - - LOGGER.fine("Generating bind pose and normal buffers."); - mesh.generateBindPose(true); - - // change the usage type of vertex and normal buffers from Static to Stream - mesh.getBuffer(Type.Position).setUsage(Usage.Stream); - mesh.getBuffer(Type.Normal).setUsage(Usage.Stream); - - // creating empty buffers for HW skinning; the buffers will be setup if ever used - VertexBuffer verticesWeightsHW = new VertexBuffer(Type.HWBoneWeight); - VertexBuffer verticesWeightsIndicesHW = new VertexBuffer(Type.HWBoneIndex); - mesh.setBuffer(verticesWeightsHW); - mesh.setBuffer(verticesWeightsIndicesHW); - } - - Geometry geometry = new Geometry(name + (result.size() + 1), mesh); - if (properties != null && properties.getValue() != null) { - meshHelper.applyProperties(geometry, properties); - } - result.add(geometry); - - geometryToBuffersMap.put(geometry, meshBuffers); - } - - LOGGER.fine("Applying materials to geometries."); - for (Entry entry : geometryToBuffersMap.entrySet()) { - int materialIndex = entry.getValue().getMaterialIndex(); - Geometry geometry = entry.getKey(); - if (materialIndex >= 0 && materials != null && materials.length > materialIndex && materials[materialIndex] != null) { - materials[materialIndex].applyMaterial(geometry, meshStructure.getOldMemoryAddress(), entry.getValue().getUvCoords(), blenderContext); - } else { - Material defaultMaterial = blenderContext.getDefaultMaterial().clone(); - defaultMaterial.getAdditionalRenderState().setFaceCullMode(FaceCullMode.Off); - geometry.setMaterial(defaultMaterial); - } - } - } - - /** - * The method creates geometries from lines. - * @param result - * the list where new geometries will be appended - * @param meshHelper - * the mesh helper - */ - protected void prepareLinesGeometry(List result, MeshHelper meshHelper) { - if (edges.size() > 0) { - LOGGER.fine("Preparing lines geometries."); - - List> separateEdges = new ArrayList>(); - List edges = new ArrayList(this.edges.size()); - for (Edge edge : this.edges) { - if (!edge.isInFace()) { - edges.add(edge); - } - } - while (edges.size() > 0) { - boolean edgeAppended = false; - int edgeIndex = 0; - for (List list : separateEdges) { - for (edgeIndex = 0; edgeIndex < edges.size() && !edgeAppended; ++edgeIndex) { - Edge edge = edges.get(edgeIndex); - if (list.get(0).equals(edge.getFirstIndex())) { - list.add(0, edge.getSecondIndex()); - --edgeIndex; - edgeAppended = true; - } else if (list.get(0).equals(edge.getSecondIndex())) { - list.add(0, edge.getFirstIndex()); - --edgeIndex; - edgeAppended = true; - } else if (list.get(list.size() - 1).equals(edge.getFirstIndex())) { - list.add(edge.getSecondIndex()); - --edgeIndex; - edgeAppended = true; - } else if (list.get(list.size() - 1).equals(edge.getSecondIndex())) { - list.add(edge.getFirstIndex()); - --edgeIndex; - edgeAppended = true; - } - } - if (edgeAppended) { - break; - } - } - Edge edge = edges.remove(edgeAppended ? edgeIndex : 0); - if (!edgeAppended) { - separateEdges.add(new ArrayList(Arrays.asList(edge.getFirstIndex(), edge.getSecondIndex()))); - } - } - - for (List list : separateEdges) { - MeshBuffers meshBuffers = new MeshBuffers(0); - for (int index : list) { - meshBuffers.append(vertices.get(index), normals.get(index)); - } - Mesh mesh = new Mesh(); - mesh.setLineWidth(blenderContext.getBlenderKey().getLinesWidth()); - mesh.setMode(Mode.LineStrip); - if (meshBuffers.isShortIndexBuffer()) { - mesh.setBuffer(Type.Index, 1, (ShortBuffer) meshBuffers.getIndexBuffer()); - } else { - mesh.setBuffer(Type.Index, 1, (IntBuffer) meshBuffers.getIndexBuffer()); - } - mesh.setBuffer(meshBuffers.getPositionsBuffer()); - mesh.setBuffer(meshBuffers.getNormalsBuffer()); - - Geometry geometry = new Geometry(meshStructure.getName() + (result.size() + 1), mesh); - geometry.setMaterial(meshHelper.getBlackUnshadedMaterial(blenderContext)); - if (properties != null && properties.getValue() != null) { - meshHelper.applyProperties(geometry, properties); - } - result.add(geometry); - } - } - } - - /** - * The method creates geometries from points. - * @param result - * the list where new geometries will be appended - * @param meshHelper - * the mesh helper - */ - protected void preparePointsGeometry(List result, MeshHelper meshHelper) { - if (points.size() > 0) { - LOGGER.fine("Preparing point geometries."); - - MeshBuffers pointBuffers = new MeshBuffers(0); - for (Point point : points) { - pointBuffers.append(vertices.get(point.getIndex()), normals.get(point.getIndex())); - } - Mesh pointsMesh = new Mesh(); - pointsMesh.setMode(Mode.Points); - pointsMesh.setPointSize(blenderContext.getBlenderKey().getPointsSize()); - if (pointBuffers.isShortIndexBuffer()) { - pointsMesh.setBuffer(Type.Index, 1, (ShortBuffer) pointBuffers.getIndexBuffer()); - } else { - pointsMesh.setBuffer(Type.Index, 1, (IntBuffer) pointBuffers.getIndexBuffer()); - } - pointsMesh.setBuffer(pointBuffers.getPositionsBuffer()); - pointsMesh.setBuffer(pointBuffers.getNormalsBuffer()); - - Geometry pointsGeometry = new Geometry(meshStructure.getName() + (result.size() + 1), pointsMesh); - pointsGeometry.setMaterial(meshHelper.getBlackUnshadedMaterial(blenderContext)); - if (properties != null && properties.getValue() != null) { - meshHelper.applyProperties(pointsGeometry, properties); - } - result.add(pointsGeometry); - } - } - - @Override - public String toString() { - return "TemporalMesh [name=" + name + ", vertices.size()=" + vertices.size() + "]"; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + (meshStructure == null ? 0 : meshStructure.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (!(obj instanceof TemporalMesh)) { - return false; - } - TemporalMesh other = (TemporalMesh) obj; - return meshStructure.getOldMemoryAddress().equals(other.meshStructure.getOldMemoryAddress()); - } -} diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/modifiers/ArmatureModifier.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/modifiers/ArmatureModifier.java deleted file mode 100644 index b59f3d05c..000000000 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/modifiers/ArmatureModifier.java +++ /dev/null @@ -1,113 +0,0 @@ -package com.jme3.scene.plugins.blender.modifiers; - -import java.util.ArrayList; -import java.util.List; -import java.util.logging.Level; -import java.util.logging.Logger; - -import com.jme3.animation.Bone; -import com.jme3.animation.Skeleton; -import com.jme3.scene.Node; -import com.jme3.scene.plugins.blender.BlenderContext; -import com.jme3.scene.plugins.blender.animations.AnimationHelper; -import com.jme3.scene.plugins.blender.animations.BoneContext; -import com.jme3.scene.plugins.blender.file.BlenderFileException; -import com.jme3.scene.plugins.blender.file.Pointer; -import com.jme3.scene.plugins.blender.file.Structure; -import com.jme3.scene.plugins.blender.meshes.TemporalMesh; - -/** - * This modifier allows to add bone animation to the object. - * - * @author Marcin Roguski (Kaelthas) - */ -/* package */class ArmatureModifier extends Modifier { - private static final Logger LOGGER = Logger.getLogger(ArmatureModifier.class.getName()); - - private static final int FLAG_VERTEX_GROUPS = 0x01; - private static final int FLAG_BONE_ENVELOPES = 0x02; - - private Skeleton skeleton; - - /** - * This constructor reads animation data from the object structore. The - * stored data is the AnimData and additional data is armature's OMA. - * - * @param objectStructure - * the structure of the object - * @param modifierStructure - * the structure of the modifier - * @param blenderContext - * the blender context - * @throws BlenderFileException - * this exception is thrown when the blender file is somehow - * corrupted - */ - public ArmatureModifier(Structure objectStructure, Structure modifierStructure, BlenderContext blenderContext) throws BlenderFileException { - if (this.validate(modifierStructure, blenderContext)) { - Pointer pArmatureObject = (Pointer) modifierStructure.getFieldValue("object"); - if (pArmatureObject.isNotNull()) { - int deformflag = ((Number) modifierStructure.getFieldValue("deformflag")).intValue(); - boolean useVertexGroups = (deformflag & FLAG_VERTEX_GROUPS) != 0; - boolean useBoneEnvelopes = (deformflag & FLAG_BONE_ENVELOPES) != 0; - modifying = useBoneEnvelopes || useVertexGroups; - if (modifying) {// if neither option is used the modifier will not modify anything anyway - Structure armatureObject = pArmatureObject.fetchData().get(0); - if(blenderContext.getSkeleton(armatureObject.getOldMemoryAddress()) == null) { - LOGGER.fine("Creating new skeleton for armature modifier."); - Structure armatureStructure = ((Pointer) armatureObject.getFieldValue("data")).fetchData().get(0); - List bonebase = ((Structure) armatureStructure.getFieldValue("bonebase")).evaluateListBase(); - List bonesList = new ArrayList(); - for (int i = 0; i < bonebase.size(); ++i) { - this.buildBones(armatureObject.getOldMemoryAddress(), bonebase.get(i), null, bonesList, objectStructure.getOldMemoryAddress(), blenderContext); - } - bonesList.add(0, new Bone("")); - Bone[] bones = bonesList.toArray(new Bone[bonesList.size()]); - skeleton = new Skeleton(bones); - blenderContext.setSkeleton(armatureObject.getOldMemoryAddress(), skeleton); - } else { - skeleton = blenderContext.getSkeleton(armatureObject.getOldMemoryAddress()); - } - } - } else { - modifying = false; - } - } - } - - private void buildBones(Long armatureObjectOMA, Structure boneStructure, Bone parent, List result, Long spatialOMA, BlenderContext blenderContext) throws BlenderFileException { - BoneContext bc = new BoneContext(armatureObjectOMA, boneStructure, blenderContext); - bc.buildBone(result, spatialOMA, blenderContext); - } - - @Override - public void postMeshCreationApply(Node node, BlenderContext blenderContext) { - LOGGER.fine("Applying armature modifier after mesh has been created."); - AnimationHelper animationHelper = blenderContext.getHelper(AnimationHelper.class); - animationHelper.applyAnimations(node, skeleton, blenderContext.getBlenderKey().getAnimationMatchMethod()); - node.updateModelBound(); - } - - @Override - public void apply(Node node, BlenderContext blenderContext) { - if (invalid) { - LOGGER.log(Level.WARNING, "Armature modifier is invalid! Cannot be applied to: {0}", node.getName()); - } - - if (modifying) { - TemporalMesh temporalMesh = this.getTemporalMesh(node); - if (temporalMesh != null) { - LOGGER.log(Level.FINE, "Applying armature modifier to: {0}", temporalMesh); - - LOGGER.fine("Creating map between bone name and its index."); - for (int i = 0; i < skeleton.getBoneCount(); ++i) { - Bone bone = skeleton.getBone(i); - temporalMesh.addBoneIndex(bone.getName(), i); - } - temporalMesh.applyAfterMeshCreate(this); - } else { - LOGGER.log(Level.WARNING, "Cannot find temporal mesh for node: {0}. The modifier will NOT be applied!", node); - } - } - } -} diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/modifiers/ArrayModifier.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/modifiers/ArrayModifier.java deleted file mode 100644 index e00c22640..000000000 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/modifiers/ArrayModifier.java +++ /dev/null @@ -1,241 +0,0 @@ -package com.jme3.scene.plugins.blender.modifiers; - -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.logging.Level; -import java.util.logging.Logger; - -import com.jme3.bounding.BoundingBox; -import com.jme3.bounding.BoundingSphere; -import com.jme3.bounding.BoundingVolume; -import com.jme3.math.Vector3f; -import com.jme3.scene.Geometry; -import com.jme3.scene.Mesh; -import com.jme3.scene.Node; -import com.jme3.scene.Spatial; -import com.jme3.scene.plugins.blender.BlenderContext; -import com.jme3.scene.plugins.blender.file.BlenderFileException; -import com.jme3.scene.plugins.blender.file.DynamicArray; -import com.jme3.scene.plugins.blender.file.FileBlockHeader; -import com.jme3.scene.plugins.blender.file.Pointer; -import com.jme3.scene.plugins.blender.file.Structure; -import com.jme3.scene.plugins.blender.meshes.MeshHelper; -import com.jme3.scene.plugins.blender.meshes.TemporalMesh; -import com.jme3.scene.plugins.blender.objects.ObjectHelper; -import com.jme3.scene.shape.Curve; - -/** - * This modifier allows to array modifier to the object. - * - * @author Marcin Roguski (Kaelthas) - */ -/* package */class ArrayModifier extends Modifier { - private static final Logger LOGGER = Logger.getLogger(ArrayModifier.class.getName()); - - private int fittype; - private int count; - private float length; - private float[] offset; - private float[] scale; - private Pointer pOffsetObject; - private Pointer pStartCap; - private Pointer pEndCap; - - /** - * This constructor reads array data from the modifier structure. The - * stored data is a map of parameters for array modifier. No additional data - * is loaded. - * - * @param objectStructure - * the structure of the object - * @param modifierStructure - * the structure of the modifier - * @param blenderContext - * the blender context - * @throws BlenderFileException - * this exception is thrown when the blender file is somehow - * corrupted - */ - @SuppressWarnings("unchecked") - public ArrayModifier(Structure modifierStructure, BlenderContext blenderContext) throws BlenderFileException { - if (this.validate(modifierStructure, blenderContext)) { - fittype = ((Number) modifierStructure.getFieldValue("fit_type")).intValue(); - switch (fittype) { - case 0:// FIXED COUNT - count = ((Number) modifierStructure.getFieldValue("count")).intValue(); - break; - case 1:// FIXED LENGTH - length = ((Number) modifierStructure.getFieldValue("length")).floatValue(); - break; - case 2:// FITCURVE - Pointer pCurveOb = (Pointer) modifierStructure.getFieldValue("curve_ob"); - if (pCurveOb.isNotNull()) { - Structure curveStructure = pCurveOb.fetchData().get(0); - ObjectHelper objectHelper = blenderContext.getHelper(ObjectHelper.class); - Node curveObject = (Node) objectHelper.toObject(curveStructure, blenderContext); - Set referencesToCurveLengths = new HashSet(curveObject.getChildren().size()); - for (Spatial spatial : curveObject.getChildren()) { - if (spatial instanceof Geometry) { - Mesh mesh = ((Geometry) spatial).getMesh(); - if (mesh instanceof Curve) { - length += ((Curve) mesh).getLength(); - } else { - // if bevel object has several parts then each mesh will have the same reference - // to length value (and we should use only one) - Number curveLength = spatial.getUserData("curveLength"); - if (curveLength != null && !referencesToCurveLengths.contains(curveLength)) { - length += curveLength.floatValue(); - referencesToCurveLengths.add(curveLength); - } - } - } - } - } - fittype = 1;// treat it like FIXED LENGTH - break; - default: - assert false : "Unknown array modifier fit type: " + fittype; - } - - // offset parameters - int offsettype = ((Number) modifierStructure.getFieldValue("offset_type")).intValue(); - if ((offsettype & 0x01) != 0) {// Constant offset - DynamicArray offsetArray = (DynamicArray) modifierStructure.getFieldValue("offset"); - offset = new float[] { offsetArray.get(0).floatValue(), offsetArray.get(1).floatValue(), offsetArray.get(2).floatValue() }; - } - if ((offsettype & 0x02) != 0) {// Relative offset - DynamicArray scaleArray = (DynamicArray) modifierStructure.getFieldValue("scale"); - scale = new float[] { scaleArray.get(0).floatValue(), scaleArray.get(1).floatValue(), scaleArray.get(2).floatValue() }; - } - if ((offsettype & 0x04) != 0) {// Object offset - pOffsetObject = (Pointer) modifierStructure.getFieldValue("offset_ob"); - } - - // start cap and end cap - pStartCap = (Pointer) modifierStructure.getFieldValue("start_cap"); - pEndCap = (Pointer) modifierStructure.getFieldValue("end_cap"); - } - } - - @Override - public void apply(Node node, BlenderContext blenderContext) { - if (invalid) { - LOGGER.log(Level.WARNING, "Array modifier is invalid! Cannot be applied to: {0}", node.getName()); - } else { - TemporalMesh temporalMesh = this.getTemporalMesh(node); - if (temporalMesh != null) { - LOGGER.log(Level.FINE, "Applying array modifier to: {0}", temporalMesh); - if (offset == null) {// the node will be repeated several times in the same place - offset = new float[] { 0.0f, 0.0f, 0.0f }; - } - if (scale == null) {// the node will be repeated several times in the same place - scale = new float[] { 0.0f, 0.0f, 0.0f }; - } else { - // getting bounding box - temporalMesh.updateModelBound(); - BoundingVolume boundingVolume = temporalMesh.getWorldBound(); - if (boundingVolume instanceof BoundingBox) { - scale[0] *= ((BoundingBox) boundingVolume).getXExtent() * 2.0f; - scale[1] *= ((BoundingBox) boundingVolume).getYExtent() * 2.0f; - scale[2] *= ((BoundingBox) boundingVolume).getZExtent() * 2.0f; - } else if (boundingVolume instanceof BoundingSphere) { - float radius = ((BoundingSphere) boundingVolume).getRadius(); - scale[0] *= radius * 2.0f; - scale[1] *= radius * 2.0f; - scale[2] *= radius * 2.0f; - } else { - throw new IllegalStateException("Unknown bounding volume type: " + boundingVolume.getClass().getName()); - } - } - - // adding object's offset - float[] objectOffset = new float[] { 0.0f, 0.0f, 0.0f }; - if (pOffsetObject != null && pOffsetObject.isNotNull()) { - FileBlockHeader offsetObjectBlock = blenderContext.getFileBlock(pOffsetObject.getOldMemoryAddress()); - ObjectHelper objectHelper = blenderContext.getHelper(ObjectHelper.class); - try {// we take the structure in case the object was not yet loaded - Structure offsetStructure = offsetObjectBlock.getStructure(blenderContext); - Vector3f translation = objectHelper.getTransformation(offsetStructure, blenderContext).getTranslation(); - objectOffset[0] = translation.x; - objectOffset[1] = translation.y; - objectOffset[2] = translation.z; - } catch (BlenderFileException e) { - LOGGER.log(Level.WARNING, "Problems in blender file structure! Object offset cannot be applied! The problem: {0}", e.getMessage()); - } - } - - // getting start and end caps - MeshHelper meshHelper = blenderContext.getHelper(MeshHelper.class); - TemporalMesh[] caps = new TemporalMesh[] { null, null }; - Pointer[] pCaps = new Pointer[] { pStartCap, pEndCap }; - for (int i = 0; i < pCaps.length; ++i) { - if (pCaps[i].isNotNull()) { - FileBlockHeader capBlock = blenderContext.getFileBlock(pCaps[i].getOldMemoryAddress()); - try {// we take the structure in case the object was not yet loaded - Structure capStructure = capBlock.getStructure(blenderContext); - Pointer pMesh = (Pointer) capStructure.getFieldValue("data"); - List meshesArray = pMesh.fetchData(); - caps[i] = meshHelper.toTemporalMesh(meshesArray.get(0), blenderContext); - } catch (BlenderFileException e) { - LOGGER.log(Level.WARNING, "Problems in blender file structure! Cap object cannot be applied! The problem: {0}", e.getMessage()); - } - } - } - - Vector3f translationVector = new Vector3f(offset[0] + scale[0] + objectOffset[0], offset[1] + scale[1] + objectOffset[1], offset[2] + scale[2] + objectOffset[2]); - if (blenderContext.getBlenderKey().isFixUpAxis()) { - float y = translationVector.y; - translationVector.y = translationVector.z; - translationVector.z = y == 0 ? 0 : -y; - } - - // getting/calculating repeats amount - int count = 0; - if (fittype == 0) {// Fixed count - count = this.count - 1; - } else if (fittype == 1) {// Fixed length - float length = this.length; - if (translationVector.length() > 0.0f) { - count = (int) (length / translationVector.length()) - 1; - } - } else if (fittype == 2) {// Fit curve - throw new IllegalStateException("Fit curve should be transformed to Fixed Length array type!"); - } else { - throw new IllegalStateException("Unknown fit type: " + fittype); - } - - // adding translated nodes and caps - Vector3f totalTranslation = new Vector3f(translationVector); - if (count > 0) { - TemporalMesh originalMesh = temporalMesh.clone(); - for (int i = 0; i < count; ++i) { - TemporalMesh clone = originalMesh.clone(); - for (Vector3f v : clone.getVertices()) { - v.addLocal(totalTranslation); - } - temporalMesh.append(clone); - totalTranslation.addLocal(translationVector); - } - } - if (caps[0] != null) { - translationVector.multLocal(-1); - TemporalMesh capsClone = caps[0].clone(); - for (Vector3f v : capsClone.getVertices()) { - v.addLocal(translationVector); - } - temporalMesh.append(capsClone); - } - if (caps[1] != null) { - TemporalMesh capsClone = caps[1].clone(); - for (Vector3f v : capsClone.getVertices()) { - v.addLocal(totalTranslation); - } - temporalMesh.append(capsClone); - } - } else { - LOGGER.log(Level.WARNING, "Cannot find temporal mesh for node: {0}. The modifier will NOT be applied!", node); - } - } - } -} diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/modifiers/MaskModifier.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/modifiers/MaskModifier.java deleted file mode 100644 index 9201800e6..000000000 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/modifiers/MaskModifier.java +++ /dev/null @@ -1,200 +0,0 @@ -package com.jme3.scene.plugins.blender.modifiers; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.logging.Level; -import java.util.logging.Logger; - -import com.jme3.scene.Node; -import com.jme3.scene.plugins.blender.BlenderContext; -import com.jme3.scene.plugins.blender.animations.BoneContext; -import com.jme3.scene.plugins.blender.file.BlenderFileException; -import com.jme3.scene.plugins.blender.file.Pointer; -import com.jme3.scene.plugins.blender.file.Structure; -import com.jme3.scene.plugins.blender.meshes.Edge; -import com.jme3.scene.plugins.blender.meshes.Face; -import com.jme3.scene.plugins.blender.meshes.IndexesLoop.IndexPredicate; -import com.jme3.scene.plugins.blender.meshes.Point; -import com.jme3.scene.plugins.blender.meshes.TemporalMesh; - -/** - * This modifier allows to use mask modifier on the object. - * - * @author Marcin Roguski (Kaelthas) - */ -/* package */class MaskModifier extends Modifier { - private static final Logger LOGGER = Logger.getLogger(MaskModifier.class.getName()); - - private static final int FLAG_INVERT_MASK = 0x01; - - private static final int MODE_VERTEX_GROUP = 0; - private static final int MODE_ARMATURE = 1; - - private Pointer pArmatureObject; - private String vertexGroupName; - private boolean invertMask; - - public MaskModifier(Structure modifierStructure, BlenderContext blenderContext) { - if (this.validate(modifierStructure, blenderContext)) { - int flag = ((Number) modifierStructure.getFieldValue("flag")).intValue(); - invertMask = (flag & FLAG_INVERT_MASK) != 0; - - int mode = ((Number) modifierStructure.getFieldValue("mode")).intValue(); - if (mode == MODE_VERTEX_GROUP) { - vertexGroupName = modifierStructure.getFieldValue("vgroup").toString(); - if (vertexGroupName != null && vertexGroupName.length() == 0) { - vertexGroupName = null; - } - } else if (mode == MODE_ARMATURE) { - pArmatureObject = (Pointer) modifierStructure.getFieldValue("ob_arm"); - } else { - LOGGER.log(Level.SEVERE, "Unknown mode type: {0}. Cannot apply modifier: {1}.", new Object[] { mode, modifierStructure.getName() }); - invalid = true; - } - } - } - - @Override - public void apply(Node node, BlenderContext blenderContext) { - if (invalid) { - LOGGER.log(Level.WARNING, "Mirror modifier is invalid! Cannot be applied to: {0}", node.getName()); - } else { - TemporalMesh temporalMesh = this.getTemporalMesh(node); - if (temporalMesh != null) { - List vertexGroupsToRemove = new ArrayList(); - if (vertexGroupName != null) { - vertexGroupsToRemove.add(vertexGroupName); - } else if (pArmatureObject != null && pArmatureObject.isNotNull()) { - try { - Structure armatureObject = pArmatureObject.fetchData().get(0); - - Structure armatureStructure = ((Pointer) armatureObject.getFieldValue("data")).fetchData().get(0); - List bonebase = ((Structure) armatureStructure.getFieldValue("bonebase")).evaluateListBase(); - vertexGroupsToRemove.addAll(this.readBoneNames(bonebase)); - } catch (BlenderFileException e) { - LOGGER.log(Level.SEVERE, "Cannot load armature object for the mask modifier. Cause: {0}", e.getLocalizedMessage()); - LOGGER.log(Level.SEVERE, "Mask modifier will NOT be applied to node named: {0}", node.getName()); - } - } else { - // if the mesh has no vertex groups then remove all verts - // if the mesh has at least one vertex group - then do nothing - // I have no idea why we should do that, but blender works this way - Set vertexGroupNames = new HashSet(); - for (Map groups : temporalMesh.getVertexGroups()) { - vertexGroupNames.addAll(groups.keySet()); - } - if (vertexGroupNames.size() == 0 && !invertMask || vertexGroupNames.size() > 0 && invertMask) { - temporalMesh.clear(); - } - } - - if (vertexGroupsToRemove.size() > 0) { - List vertsToBeRemoved = new ArrayList(); - for (int i = 0; i < temporalMesh.getVertexCount(); ++i) { - Map vertexGroups = temporalMesh.getVertexGroups().get(i); - boolean hasVertexGroup = false; - if (vertexGroups != null) { - for (String groupName : vertexGroupsToRemove) { - Float weight = vertexGroups.get(groupName); - if (weight != null && weight > 0) { - hasVertexGroup = true; - break; - } - } - } - - if (!hasVertexGroup && !invertMask || hasVertexGroup && invertMask) { - vertsToBeRemoved.add(i); - } - } - - Collections.reverse(vertsToBeRemoved); - for (Integer vertexIndex : vertsToBeRemoved) { - this.removeVertexAt(vertexIndex, temporalMesh); - } - } - } else { - LOGGER.log(Level.WARNING, "Cannot find temporal mesh for node: {0}. The modifier will NOT be applied!", node); - } - } - } - - /** - * Every face, edge and point that contains - * the vertex will be removed. - * @param index - * the index of a vertex to be removed - * @throws IndexOutOfBoundsException - * thrown when given index is negative or beyond the count of vertices - */ - private void removeVertexAt(final int index, TemporalMesh temporalMesh) { - if (index < 0 || index >= temporalMesh.getVertexCount()) { - throw new IndexOutOfBoundsException("The given index is out of bounds: " + index); - } - - temporalMesh.getVertices().remove(index); - temporalMesh.getNormals().remove(index); - if (temporalMesh.getVertexGroups().size() > 0) { - temporalMesh.getVertexGroups().remove(index); - } - if (temporalMesh.getVerticesColors().size() > 0) { - temporalMesh.getVerticesColors().remove(index); - } - - IndexPredicate shiftPredicate = new IndexPredicate() { - @Override - public boolean execute(Integer i) { - return i > index; - } - }; - for (int i = temporalMesh.getFaces().size() - 1; i >= 0; --i) { - Face face = temporalMesh.getFaces().get(i); - if (face.getIndexes().indexOf(index) >= 0) { - temporalMesh.getFaces().remove(i); - } else { - face.getIndexes().shiftIndexes(-1, shiftPredicate); - } - } - for (int i = temporalMesh.getEdges().size() - 1; i >= 0; --i) { - Edge edge = temporalMesh.getEdges().get(i); - if (edge.getFirstIndex() == index || edge.getSecondIndex() == index) { - temporalMesh.getEdges().remove(i); - } else { - edge.shiftIndexes(-1, shiftPredicate); - } - } - for (int i = temporalMesh.getPoints().size() - 1; i >= 0; --i) { - Point point = temporalMesh.getPoints().get(i); - if (point.getIndex() == index) { - temporalMesh.getPoints().remove(i); - } else { - point.shiftIndexes(-1, shiftPredicate); - } - } - } - - /** - * Reads the names of the bones from the given bone base. - * @param boneBase - * the list of bone base structures - * @return a list of bones' names - * @throws BlenderFileException - * is thrown if problems with reading the child bones' bases occur - */ - private List readBoneNames(List boneBase) throws BlenderFileException { - List result = new ArrayList(); - for (Structure boneStructure : boneBase) { - int flag = ((Number) boneStructure.getFieldValue("flag")).intValue(); - if ((flag & BoneContext.SELECTED) != 0) { - result.add(boneStructure.getFieldValue("name").toString()); - } - List childbase = ((Structure) boneStructure.getFieldValue("childbase")).evaluateListBase(); - result.addAll(this.readBoneNames(childbase)); - } - return result; - } -} diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/modifiers/MirrorModifier.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/modifiers/MirrorModifier.java deleted file mode 100644 index 36538afc3..000000000 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/modifiers/MirrorModifier.java +++ /dev/null @@ -1,196 +0,0 @@ -package com.jme3.scene.plugins.blender.modifiers; - -import java.util.Collections; -import java.util.logging.Level; -import java.util.logging.Logger; - -import com.jme3.math.Matrix4f; -import com.jme3.math.Vector3f; -import com.jme3.scene.Node; -import com.jme3.scene.plugins.blender.BlenderContext; -import com.jme3.scene.plugins.blender.file.BlenderFileException; -import com.jme3.scene.plugins.blender.file.Pointer; -import com.jme3.scene.plugins.blender.file.Structure; -import com.jme3.scene.plugins.blender.meshes.Edge; -import com.jme3.scene.plugins.blender.meshes.Face; -import com.jme3.scene.plugins.blender.meshes.TemporalMesh; -import com.jme3.scene.plugins.blender.objects.ObjectHelper; - -/** - * This modifier allows to array modifier to the object. - * - * @author Marcin Roguski (Kaelthas) - */ -/* package */class MirrorModifier extends Modifier { - private static final Logger LOGGER = Logger.getLogger(MirrorModifier.class.getName()); - - private static final int FLAG_MIRROR_X = 0x08; - private static final int FLAG_MIRROR_Y = 0x10; - private static final int FLAG_MIRROR_Z = 0x20; - private static final int FLAG_MIRROR_U = 0x02; - private static final int FLAG_MIRROR_V = 0x04; - private static final int FLAG_MIRROR_VERTEX_GROUP = 0x40; - private static final int FLAG_MIRROR_MERGE = 0x80; - - private boolean[] isMirrored; - private boolean mirrorU, mirrorV; - private boolean merge; - private float tolerance; - private Pointer pMirrorObject; - private boolean mirrorVGroup; - - /** - * This constructor reads mirror data from the modifier structure. The - * stored data is a map of parameters for mirror modifier. No additional data - * is loaded. - * When the modifier is applied it is necessary to get the newly created node. - * - * @param objectStructure - * the structure of the object - * @param modifierStructure - * the structure of the modifier - * @param blenderContext - * the blender context - * @throws BlenderFileException - * this exception is thrown when the blender file is somehow - * corrupted - */ - public MirrorModifier(Structure modifierStructure, BlenderContext blenderContext) { - if (this.validate(modifierStructure, blenderContext)) { - int flag = ((Number) modifierStructure.getFieldValue("flag")).intValue(); - - isMirrored = new boolean[] { (flag & FLAG_MIRROR_X) != 0, (flag & FLAG_MIRROR_Y) != 0, (flag & FLAG_MIRROR_Z) != 0 }; - if (blenderContext.getBlenderKey().isFixUpAxis()) { - boolean temp = isMirrored[1]; - isMirrored[1] = isMirrored[2]; - isMirrored[2] = temp; - } - mirrorU = (flag & FLAG_MIRROR_U) != 0; - mirrorV = (flag & FLAG_MIRROR_V) != 0; - mirrorVGroup = (flag & FLAG_MIRROR_VERTEX_GROUP) != 0; - merge = (flag & FLAG_MIRROR_MERGE) == 0;// in this case we use == instead of != (this is not a mistake) - - tolerance = ((Number) modifierStructure.getFieldValue("tolerance")).floatValue(); - pMirrorObject = (Pointer) modifierStructure.getFieldValue("mirror_ob"); - - if (mirrorVGroup) { - LOGGER.warning("Mirroring vertex groups is currently not supported."); - } - } - } - - @Override - public void apply(Node node, BlenderContext blenderContext) { - if (invalid) { - LOGGER.log(Level.WARNING, "Mirror modifier is invalid! Cannot be applied to: {0}", node.getName()); - } else { - TemporalMesh temporalMesh = this.getTemporalMesh(node); - if (temporalMesh != null) { - LOGGER.log(Level.FINE, "Applying mirror modifier to: {0}", temporalMesh); - Vector3f mirrorPlaneCenter = new Vector3f(); - if (pMirrorObject.isNotNull()) { - Structure objectStructure; - try { - objectStructure = pMirrorObject.fetchData().get(0); - ObjectHelper objectHelper = blenderContext.getHelper(ObjectHelper.class); - Node object = (Node) objectHelper.toObject(objectStructure, blenderContext); - if (object != null) { - // compute the mirror object coordinates in node's local space - mirrorPlaneCenter = this.getWorldMatrix(node).invertLocal().mult(object.getWorldTranslation()); - } - } catch (BlenderFileException e) { - LOGGER.log(Level.SEVERE, "Cannot load mirror''s reference object. Cause: {0}", e.getLocalizedMessage()); - LOGGER.log(Level.SEVERE, "Mirror modifier will not be applied to node named: {0}", node.getName()); - return; - } - } - - LOGGER.finest("Allocating temporal variables."); - float d; - Vector3f mirrorPlaneNormal = new Vector3f(); - Vector3f shiftVector = new Vector3f(); - - LOGGER.fine("Mirroring mesh."); - for (int mirrorIndex = 0; mirrorIndex < 3; ++mirrorIndex) { - if (isMirrored[mirrorIndex]) { - boolean mirrorAtPoint0 = mirrorPlaneCenter.get(mirrorIndex) == 0; - if (!mirrorAtPoint0) {// compute mirror's plane normal vector in node's space - mirrorPlaneNormal.set(0, 0, 0).set(mirrorIndex, Math.signum(mirrorPlaneCenter.get(mirrorIndex))); - } - - TemporalMesh mirror = temporalMesh.clone(); - for (int i = 0; i < mirror.getVertexCount(); ++i) { - Vector3f vertex = mirror.getVertices().get(i); - Vector3f normal = mirror.getNormals().get(i); - - if (mirrorAtPoint0) { - d = Math.abs(vertex.get(mirrorIndex)); - shiftVector.set(0, 0, 0).set(mirrorIndex, -vertex.get(mirrorIndex)); - } else { - d = this.computeDistanceFromPlane(vertex, mirrorPlaneCenter, mirrorPlaneNormal); - mirrorPlaneNormal.mult(d, shiftVector); - } - - if (merge && d <= tolerance) { - vertex.addLocal(shiftVector); - normal.set(mirrorIndex, 0); - temporalMesh.getVertices().get(i).addLocal(shiftVector); - temporalMesh.getNormals().get(i).set(mirrorIndex, 0); - } else { - vertex.addLocal(shiftVector.multLocal(2)); - normal.set(mirrorIndex, -normal.get(mirrorIndex)); - } - } - - // flipping the indexes - for (Face face : mirror.getFaces()) { - face.flipIndexes(); - } - for (Edge edge : mirror.getEdges()) { - edge.flipIndexes(); - } - Collections.reverse(mirror.getPoints()); - - if (mirrorU || mirrorV) { - for (Face face : mirror.getFaces()) { - face.flipUV(mirrorU, mirrorV); - } - } - - temporalMesh.append(mirror); - } - } - } else { - LOGGER.log(Level.WARNING, "Cannot find temporal mesh for node: {0}. The modifier will NOT be applied!", node); - } - } - } - - /** - * Fetches the world matrix transformation of the given node. - * @param node - * the node - * @return the node's world transformation matrix - */ - private Matrix4f getWorldMatrix(Node node) { - Matrix4f result = new Matrix4f(); - result.setTranslation(node.getWorldTranslation()); - result.setRotationQuaternion(node.getWorldRotation()); - result.setScale(node.getWorldScale()); - return result; - } - - /** - * The method computes the distance between a point and a plane (described by point in space and normal vector). - * @param p - * the point in the space - * @param c - * mirror's plane center - * @param n - * mirror's plane normal (should be normalized) - * @return the minimum distance from point to plane - */ - private float computeDistanceFromPlane(Vector3f p, Vector3f c, Vector3f n) { - return Math.abs(n.dot(p) - c.dot(n)); - } -} diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/modifiers/Modifier.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/modifiers/Modifier.java deleted file mode 100644 index 889000056..000000000 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/modifiers/Modifier.java +++ /dev/null @@ -1,92 +0,0 @@ -package com.jme3.scene.plugins.blender.modifiers; - -import java.util.List; - -import com.jme3.scene.Node; -import com.jme3.scene.Spatial; -import com.jme3.scene.plugins.blender.BlenderContext; -import com.jme3.scene.plugins.blender.file.Pointer; -import com.jme3.scene.plugins.blender.file.Structure; -import com.jme3.scene.plugins.blender.meshes.TemporalMesh; - -/** - * This class represents an object's modifier. The modifier object can be varied - * and the user needs to know what is the type of it for the specified type - * name. For example "ArmatureModifierData" type specified in blender is - * represented by AnimData object from jMonkeyEngine. - * - * @author Marcin Roguski (Kaelthas) - */ -public abstract class Modifier { - public static final String ARRAY_MODIFIER_DATA = "ArrayModifierData"; - public static final String ARMATURE_MODIFIER_DATA = "ArmatureModifierData"; - public static final String PARTICLE_MODIFIER_DATA = "ParticleSystemModifierData"; - public static final String MIRROR_MODIFIER_DATA = "MirrorModifierData"; - public static final String SUBSURF_MODIFIER_DATA = "SubsurfModifierData"; - public static final String OBJECT_ANIMATION_MODIFIER_DATA = "ObjectAnimationModifierData"; - - /** This variable indicates if the modifier is invalid (true) or not (false). */ - protected boolean invalid; - /** - * A variable that tells if the modifier causes modification. Some modifiers like ArmatureModifier might have no - * Armature object attached and thus not really modifying the feature. In such cases it is good to know if it is - * sense to add the modifier to the list of object's modifiers. - */ - protected boolean modifying = true; - - /** - * This method applies the modifier to the given node. - * - * @param node - * the node that will have modifier applied - * @param blenderContext - * the blender context - */ - public abstract void apply(Node node, BlenderContext blenderContext); - - /** - * The method that is called when geometries are already created. - * @param node - * the node that will have the modifier applied - * @param blenderContext - * the blender context - */ - public void postMeshCreationApply(Node node, BlenderContext blenderContext) { - } - - /** - * Determines if the modifier can be applied multiple times over one mesh. - * At this moment only armature and object animation modifiers cannot be - * applied multiple times. - * - * @param modifierType - * the type name of the modifier - * @return true if the modifier can be applied many times and - * false otherwise - */ - public static boolean canBeAppliedMultipleTimes(String modifierType) { - return !(ARMATURE_MODIFIER_DATA.equals(modifierType) || OBJECT_ANIMATION_MODIFIER_DATA.equals(modifierType)); - } - - protected boolean validate(Structure modifierStructure, BlenderContext blenderContext) { - Structure modifierData = (Structure) modifierStructure.getFieldValue("modifier"); - Pointer pError = (Pointer) modifierData.getFieldValue("error"); - invalid = pError.isNotNull(); - return !invalid; - } - - /** - * @return true if the modifier causes feature's modification or false if not - */ - public boolean isModifying() { - return modifying; - } - - protected TemporalMesh getTemporalMesh(Node node) { - List children = node.getChildren(); - if (children != null && children.size() == 1 && children.get(0) instanceof TemporalMesh) { - return (TemporalMesh) children.get(0); - } - return null; - } -} diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/modifiers/ModifierHelper.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/modifiers/ModifierHelper.java deleted file mode 100644 index 4f1fae3bd..000000000 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/modifiers/ModifierHelper.java +++ /dev/null @@ -1,117 +0,0 @@ -/* - * 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.blender.modifiers; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.logging.Level; -import java.util.logging.Logger; - -import com.jme3.scene.plugins.blender.AbstractBlenderHelper; -import com.jme3.scene.plugins.blender.BlenderContext; -import com.jme3.scene.plugins.blender.file.BlenderFileException; -import com.jme3.scene.plugins.blender.file.Structure; - -/** - * A class that is used in modifiers calculations. - * - * @author Marcin Roguski - */ -public class ModifierHelper extends AbstractBlenderHelper { - - private static final Logger LOGGER = Logger.getLogger(ModifierHelper.class.getName()); - - /** - * This constructor parses the given blender version and stores the result. - * Some functionalities may differ in different blender versions. - * - * @param blenderVersion - * the version read from the blend file - * @param blenderContext - * the blender context - */ - public ModifierHelper(String blenderVersion, BlenderContext blenderContext) { - super(blenderVersion, blenderContext); - } - - /** - * This method reads the given object's modifiers. - * - * @param objectStructure - * the object structure - * @param blenderContext - * the blender context - * @throws BlenderFileException - * this exception is thrown when the blender file is somehow - * corrupted - */ - public Collection readModifiers(Structure objectStructure, BlenderContext blenderContext) throws BlenderFileException { - Set alreadyReadModifiers = new HashSet(); - Collection result = new ArrayList(); - Structure modifiersListBase = (Structure) objectStructure.getFieldValue("modifiers"); - List modifiers = modifiersListBase.evaluateListBase(); - for (Structure modifierStructure : modifiers) { - String modifierType = modifierStructure.getType(); - if (!Modifier.canBeAppliedMultipleTimes(modifierType) && alreadyReadModifiers.contains(modifierType)) { - LOGGER.log(Level.WARNING, "Modifier {0} can only be applied once to object: {1}", new Object[] { modifierType, objectStructure.getName() }); - } else { - Modifier modifier = null; - if (Modifier.ARRAY_MODIFIER_DATA.equals(modifierStructure.getType())) { - modifier = new ArrayModifier(modifierStructure, blenderContext); - } else if (Modifier.MIRROR_MODIFIER_DATA.equals(modifierStructure.getType())) { - modifier = new MirrorModifier(modifierStructure, blenderContext); - } else if (Modifier.ARMATURE_MODIFIER_DATA.equals(modifierStructure.getType())) { - modifier = new ArmatureModifier(objectStructure, modifierStructure, blenderContext); - } else if (Modifier.PARTICLE_MODIFIER_DATA.equals(modifierStructure.getType())) { - modifier = new ParticlesModifier(modifierStructure, blenderContext); - } else if(Modifier.SUBSURF_MODIFIER_DATA.equals(modifierStructure.getType())) { - modifier = new SubdivisionSurfaceModifier(modifierStructure, blenderContext); - } - - if (modifier != null) { - if (modifier.isModifying()) { - result.add(modifier); - alreadyReadModifiers.add(modifierType); - } else { - LOGGER.log(Level.WARNING, "The modifier {0} will cause no changes in the model. It will be ignored!", modifierStructure.getName()); - } - } else { - LOGGER.log(Level.WARNING, "Unsupported modifier type: {0}", modifierStructure.getType()); - } - } - } - return result; - } -} diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/modifiers/ParticlesModifier.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/modifiers/ParticlesModifier.java deleted file mode 100644 index c01d269bb..000000000 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/modifiers/ParticlesModifier.java +++ /dev/null @@ -1,107 +0,0 @@ -package com.jme3.scene.plugins.blender.modifiers; - -import java.util.ArrayList; -import java.util.List; -import java.util.logging.Level; -import java.util.logging.Logger; - -import com.jme3.effect.ParticleEmitter; -import com.jme3.effect.shapes.EmitterMeshVertexShape; -import com.jme3.effect.shapes.EmitterShape; -import com.jme3.material.Material; -import com.jme3.scene.Geometry; -import com.jme3.scene.Mesh; -import com.jme3.scene.Node; -import com.jme3.scene.Spatial; -import com.jme3.scene.plugins.blender.BlenderContext; -import com.jme3.scene.plugins.blender.file.BlenderFileException; -import com.jme3.scene.plugins.blender.file.Pointer; -import com.jme3.scene.plugins.blender.file.Structure; -import com.jme3.scene.plugins.blender.materials.MaterialHelper; -import com.jme3.scene.plugins.blender.meshes.TemporalMesh; -import com.jme3.scene.plugins.blender.particles.ParticlesHelper; - -/** - * This modifier allows to add particles to the object. - * - * @author Marcin Roguski (Kaelthas) - */ -/* package */class ParticlesModifier extends Modifier { - private static final Logger LOGGER = Logger.getLogger(MirrorModifier.class.getName()); - - /** Loaded particles emitter. */ - private ParticleEmitter particleEmitter; - - /** - * This constructor reads the particles system structure and stores it in - * order to apply it later to the node. - * - * @param modifierStructure - * the structure of the modifier - * @param blenderContext - * the blender context - * @throws BlenderFileException - * an exception is throw wneh there are problems with the - * blender file - */ - public ParticlesModifier(Structure modifierStructure, BlenderContext blenderContext) throws BlenderFileException { - if (this.validate(modifierStructure, blenderContext)) { - Pointer pParticleSystem = (Pointer) modifierStructure.getFieldValue("psys"); - if (pParticleSystem.isNotNull()) { - ParticlesHelper particlesHelper = blenderContext.getHelper(ParticlesHelper.class); - Structure particleSystem = pParticleSystem.fetchData().get(0); - particleEmitter = particlesHelper.toParticleEmitter(particleSystem); - } - } - } - - @Override - public void postMeshCreationApply(Node node, BlenderContext blenderContext) { - LOGGER.log(Level.FINE, "Applying particles modifier to: {0}", node); - - MaterialHelper materialHelper = blenderContext.getHelper(MaterialHelper.class); - ParticleEmitter emitter = particleEmitter.clone(); - - // veryfying the alpha function for particles' texture - Integer alphaFunction = MaterialHelper.ALPHA_MASK_HYPERBOLE; - char nameSuffix = emitter.getName().charAt(emitter.getName().length() - 1); - if (nameSuffix == 'B' || nameSuffix == 'N') { - alphaFunction = MaterialHelper.ALPHA_MASK_NONE; - } - // removing the type suffix from the name - emitter.setName(emitter.getName().substring(0, emitter.getName().length() - 1)); - - // applying emitter shape - EmitterShape emitterShape = emitter.getShape(); - List meshes = new ArrayList(); - for (Spatial spatial : node.getChildren()) { - if (spatial instanceof Geometry) { - Mesh mesh = ((Geometry) spatial).getMesh(); - if (mesh != null) { - meshes.add(mesh); - Material material = materialHelper.getParticlesMaterial(((Geometry) spatial).getMaterial(), alphaFunction, blenderContext); - emitter.setMaterial(material);// TODO: divide into several pieces - } - } - } - if (meshes.size() > 0 && emitterShape instanceof EmitterMeshVertexShape) { - ((EmitterMeshVertexShape) emitterShape).setMeshes(meshes); - } - - node.attachChild(emitter); - } - - @Override - public void apply(Node node, BlenderContext blenderContext) { - if (invalid) { - LOGGER.log(Level.WARNING, "Particles modifier is invalid! Cannot be applied to: {0}", node.getName()); - } else { - TemporalMesh temporalMesh = this.getTemporalMesh(node); - if(temporalMesh != null) { - temporalMesh.applyAfterMeshCreate(this); - } else { - LOGGER.log(Level.WARNING, "Cannot find temporal mesh for node: {0}. The modifier will NOT be applied!", node); - } - } - } -} diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/modifiers/SubdivisionSurfaceModifier.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/modifiers/SubdivisionSurfaceModifier.java deleted file mode 100644 index 402efc3d3..000000000 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/modifiers/SubdivisionSurfaceModifier.java +++ /dev/null @@ -1,642 +0,0 @@ -package com.jme3.scene.plugins.blender.modifiers; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; -import java.util.logging.Level; -import java.util.logging.Logger; - -import com.jme3.math.Vector2f; -import com.jme3.math.Vector3f; -import com.jme3.scene.Node; -import com.jme3.scene.plugins.blender.BlenderContext; -import com.jme3.scene.plugins.blender.file.BlenderFileException; -import com.jme3.scene.plugins.blender.file.Structure; -import com.jme3.scene.plugins.blender.meshes.Edge; -import com.jme3.scene.plugins.blender.meshes.Face; -import com.jme3.scene.plugins.blender.meshes.TemporalMesh; -import com.jme3.scene.plugins.blender.textures.TexturePixel; - -/** - * A modifier that subdivides the mesh using either simple or catmull-clark subdivision. - * - * @author Marcin Roguski (Kaelthas) - */ -public class SubdivisionSurfaceModifier extends Modifier { - private static final Logger LOGGER = Logger.getLogger(SubdivisionSurfaceModifier.class.getName()); - - private static final int TYPE_CATMULLCLARK = 0; - private static final int TYPE_SIMPLE = 1; - - private static final int FLAG_SUBDIVIDE_UVS = 0x8; - - /** The subdivision type. */ - private int subdivType; - /** The amount of subdivision levels. */ - private int levels; - /** Indicates if the UV's should also be subdivided. */ - private boolean subdivideUVS; - /** Stores the vertices that are located on original edges of the mesh. */ - private Set verticesOnOriginalEdges = new HashSet(); - - /** - * Constructor loads all necessary modifier data. - * @param modifierStructure - * the modifier structure - * @param blenderContext - * the blender context - */ - public SubdivisionSurfaceModifier(Structure modifierStructure, BlenderContext blenderContext) { - if (this.validate(modifierStructure, blenderContext)) { - subdivType = ((Number) modifierStructure.getFieldValue("subdivType")).intValue(); - levels = ((Number) modifierStructure.getFieldValue("levels")).intValue(); - int flag = ((Number) modifierStructure.getFieldValue("flags")).intValue(); - subdivideUVS = (flag & FLAG_SUBDIVIDE_UVS) != 0 && subdivType == TYPE_CATMULLCLARK; - - if (subdivType != TYPE_CATMULLCLARK && subdivType != TYPE_SIMPLE) { - LOGGER.log(Level.SEVERE, "Unknown subdivision type: {0}.", subdivType); - invalid = true; - } - if (levels < 0) { - LOGGER.severe("The amount of subdivision levels cannot be negative."); - invalid = true; - } - } - } - - @Override - public void apply(Node node, BlenderContext blenderContext) { - if (invalid) { - LOGGER.log(Level.WARNING, "Subdivision surface modifier is invalid! Cannot be applied to: {0}", node.getName()); - } else if (levels > 0) {// no need to do anything if the levels is set to zero - TemporalMesh temporalMesh = this.getTemporalMesh(node); - if (temporalMesh != null) { - LOGGER.log(Level.FINE, "Applying subdivision surface modifier to: {0}", temporalMesh); - verticesOnOriginalEdges.clear();//in case the instance of this class was used more than once - - for (Edge edge : temporalMesh.getEdges()) { - verticesOnOriginalEdges.add(edge.getFirstIndex()); - verticesOnOriginalEdges.add(edge.getSecondIndex()); - } - - if (subdivType == TYPE_CATMULLCLARK) { - for (int i = 0; i < levels; ++i) { - this.subdivideSimple(temporalMesh);// first do simple subdivision ... - this.subdivideCatmullClark(temporalMesh);// ... and then apply Catmull-Clark algorithm - if (subdivideUVS) {// UV's can be subdivided only for Catmull-Clark subdivision algorithm - this.subdivideUVs(temporalMesh); - } - } - } else { - for (int i = 0; i < levels; ++i) { - this.subdivideSimple(temporalMesh); - } - } - } else { - LOGGER.log(Level.WARNING, "Cannot find temporal mesh for node: {0}. The modifier will NOT be applied!", node); - } - } - } - - /** - * Catmull-Clark subdivision. It assumes that the mesh was already simple-subdivided. - * @param temporalMesh - * the mesh whose vertices will be transformed to form Catmull-Clark subdivision - */ - private void subdivideCatmullClark(TemporalMesh temporalMesh) { - Set boundaryVertices = new HashSet(); - for (Edge edge : temporalMesh.getEdges()) { - if (!edge.isInFace()) { - boundaryVertices.add(edge.getFirstIndex()); - boundaryVertices.add(edge.getSecondIndex()); - } else { - if (temporalMesh.isBoundary(edge.getFirstIndex())) { - boundaryVertices.add(edge.getFirstIndex()); - } - if (temporalMesh.isBoundary(edge.getSecondIndex())) { - boundaryVertices.add(edge.getSecondIndex()); - } - } - } - - List creasePoints = new ArrayList(temporalMesh.getVertexCount()); - for (int i = 0; i < temporalMesh.getVertexCount(); ++i) { - // finding adjacent edges that were created by dividing original edges - List adjacentOriginalEdges = new ArrayList(); - Collection adjacentEdges = temporalMesh.getAdjacentEdges(i); - if(adjacentEdges != null) {// this can be null if a vertex with index 'i' is not connected to any face nor edge - for (Edge edge : temporalMesh.getAdjacentEdges(i)) { - if (verticesOnOriginalEdges.contains(edge.getFirstIndex()) || verticesOnOriginalEdges.contains(edge.getSecondIndex())) { - adjacentOriginalEdges.add(edge); - } - } - - creasePoints.add(new CreasePoint(i, boundaryVertices.contains(i), adjacentOriginalEdges, temporalMesh)); - } else { - creasePoints.add(null);//the count of crease points must be equal to vertex count; otherwise we'll get IndexOutofBoundsException later - } - } - - Vector3f[] averageVert = new Vector3f[temporalMesh.getVertexCount()]; - int[] averageCount = new int[temporalMesh.getVertexCount()]; - - for (Face face : temporalMesh.getFaces()) { - Vector3f centroid = face.computeCentroid(); - - for (Integer index : face.getIndexes()) { - if (boundaryVertices.contains(index)) { - Edge edge = this.findEdge(temporalMesh, index, face.getIndexes().getNextIndex(index)); - if (temporalMesh.isBoundary(edge)) { - averageVert[index] = averageVert[index] == null ? edge.computeCentroid() : averageVert[index].addLocal(edge.computeCentroid()); - averageCount[index] += 1; - } - edge = this.findEdge(temporalMesh, face.getIndexes().getPreviousIndex(index), index); - if (temporalMesh.isBoundary(edge)) { - averageVert[index] = averageVert[index] == null ? edge.computeCentroid() : averageVert[index].addLocal(edge.computeCentroid()); - averageCount[index] += 1; - } - } else { - averageVert[index] = averageVert[index] == null ? centroid.clone() : averageVert[index].addLocal(centroid); - averageCount[index] += 1; - } - } - } - for (Edge edge : temporalMesh.getEdges()) { - if (!edge.isInFace()) { - Vector3f centroid = temporalMesh.getVertices().get(edge.getFirstIndex()).add(temporalMesh.getVertices().get(edge.getSecondIndex())).divideLocal(2); - - averageVert[edge.getFirstIndex()] = averageVert[edge.getFirstIndex()] == null ? centroid.clone() : averageVert[edge.getFirstIndex()].addLocal(centroid); - averageVert[edge.getSecondIndex()] = averageVert[edge.getSecondIndex()] == null ? centroid.clone() : averageVert[edge.getSecondIndex()].addLocal(centroid); - averageCount[edge.getFirstIndex()] += 1; - averageCount[edge.getSecondIndex()] += 1; - } - } - - for (int i = 0; i < averageVert.length; ++i) { - if(averageVert[i] != null && averageCount[i] > 0) { - Vector3f v = temporalMesh.getVertices().get(i); - averageVert[i].divideLocal(averageCount[i]); - - // computing translation vector - Vector3f t = averageVert[i].subtract(v); - if (!boundaryVertices.contains(i)) { - t.multLocal(4 / (float) averageCount[i]); - } - - // moving the vertex - v.addLocal(t); - - // applying crease weight if necessary - CreasePoint creasePoint = creasePoints.get(i); - if (creasePoint.getTarget() != null && creasePoint.getWeight() != 0) { - t = creasePoint.getTarget().subtractLocal(v).multLocal(creasePoint.getWeight()); - v.addLocal(t); - } - } - } - } - - /** - * The method performs a simple subdivision of the mesh. - * - * @param temporalMesh - * the mesh to be subdivided - */ - private void subdivideSimple(TemporalMesh temporalMesh) { - Map edgePoints = new HashMap(); - Map facePoints = new HashMap(); - Set newFaces = new LinkedHashSet(); - Set newEdges = new LinkedHashSet(temporalMesh.getEdges().size() * 4); - - int originalFacesCount = temporalMesh.getFaces().size(); - - List> vertexGroups = temporalMesh.getVertexGroups(); - // the result vertex array will have verts in the following order [[original_verts], [face_verts], [edge_verts]] - List vertices = temporalMesh.getVertices(); - List edgeVertices = new ArrayList(); - List faceVertices = new ArrayList(); - // the same goes for normals - List normals = temporalMesh.getNormals(); - List edgeNormals = new ArrayList(); - List faceNormals = new ArrayList(); - - List faces = temporalMesh.getFaces(); - for (Face face : faces) { - Map> uvSets = face.getUvSets(); - - Vector3f facePoint = face.computeCentroid(); - Integer facePointIndex = vertices.size() + faceVertices.size(); - facePoints.put(face, facePointIndex); - faceVertices.add(facePoint); - faceNormals.add(this.computeFaceNormal(face)); - Map faceUV = this.computeFaceUVs(face); - byte[] faceVertexColor = this.computeFaceVertexColor(face); - Map faceVertexGroups = this.computeFaceVertexGroups(face); - if (vertexGroups.size() > 0) { - vertexGroups.add(faceVertexGroups); - } - - for (int i = 0; i < face.getIndexes().size(); ++i) { - int vIndex = face.getIndexes().get(i); - int vPrevIndex = i == 0 ? face.getIndexes().get(face.getIndexes().size() - 1) : face.getIndexes().get(i - 1); - int vNextIndex = i == face.getIndexes().size() - 1 ? face.getIndexes().get(0) : face.getIndexes().get(i + 1); - - Edge prevEdge = this.findEdge(temporalMesh, vPrevIndex, vIndex); - Edge nextEdge = this.findEdge(temporalMesh, vIndex, vNextIndex); - int vPrevEdgeVertIndex = edgePoints.containsKey(prevEdge) ? edgePoints.get(prevEdge) : -1; - int vNextEdgeVertIndex = edgePoints.containsKey(nextEdge) ? edgePoints.get(nextEdge) : -1; - - Vector3f v = temporalMesh.getVertices().get(vIndex); - if (vPrevEdgeVertIndex < 0) { - vPrevEdgeVertIndex = vertices.size() + originalFacesCount + edgeVertices.size(); - verticesOnOriginalEdges.add(vPrevEdgeVertIndex); - edgeVertices.add(vertices.get(vPrevIndex).add(v).divideLocal(2)); - edgeNormals.add(normals.get(vPrevIndex).add(normals.get(vIndex)).normalizeLocal()); - edgePoints.put(prevEdge, vPrevEdgeVertIndex); - if (vertexGroups.size() > 0) { - vertexGroups.add(this.interpolateVertexGroups(Arrays.asList(vertexGroups.get(vPrevIndex), vertexGroups.get(vIndex)))); - } - } - if (vNextEdgeVertIndex < 0) { - vNextEdgeVertIndex = vertices.size() + originalFacesCount + edgeVertices.size(); - verticesOnOriginalEdges.add(vNextEdgeVertIndex); - edgeVertices.add(vertices.get(vNextIndex).add(v).divideLocal(2)); - edgeNormals.add(normals.get(vNextIndex).add(normals.get(vIndex)).normalizeLocal()); - edgePoints.put(nextEdge, vNextEdgeVertIndex); - if (vertexGroups.size() > 0) { - vertexGroups.add(this.interpolateVertexGroups(Arrays.asList(vertexGroups.get(vNextIndex), vertexGroups.get(vIndex)))); - } - } - - Integer[] indexes = new Integer[] { vIndex, vNextEdgeVertIndex, facePointIndex, vPrevEdgeVertIndex }; - - Map> newUVSets = null; - if (uvSets != null) { - newUVSets = new HashMap>(uvSets.size()); - for (Entry> uvset : uvSets.entrySet()) { - int indexOfvIndex = i; - int indexOfvPrevIndex = face.getIndexes().indexOf(vPrevIndex); - int indexOfvNextIndex = face.getIndexes().indexOf(vNextIndex); - - Vector2f uv1 = uvset.getValue().get(indexOfvIndex); - Vector2f uv2 = uvset.getValue().get(indexOfvNextIndex).add(uv1).divideLocal(2); - Vector2f uv3 = faceUV.get(uvset.getKey()); - Vector2f uv4 = uvset.getValue().get(indexOfvPrevIndex).add(uv1).divideLocal(2); - List uvList = Arrays.asList(uv1, uv2, uv3, uv4); - newUVSets.put(uvset.getKey(), new ArrayList(uvList)); - } - } - - List vertexColors = null; - if (face.getVertexColors() != null) { - - int indexOfvIndex = i; - int indexOfvPrevIndex = face.getIndexes().indexOf(vPrevIndex); - int indexOfvNextIndex = face.getIndexes().indexOf(vNextIndex); - - byte[] vCol1 = face.getVertexColors().get(indexOfvIndex); - byte[] vCol2 = this.interpolateVertexColors(face.getVertexColors().get(indexOfvNextIndex), vCol1); - byte[] vCol3 = faceVertexColor; - byte[] vCol4 = this.interpolateVertexColors(face.getVertexColors().get(indexOfvPrevIndex), vCol1); - vertexColors = new ArrayList(Arrays.asList(vCol1, vCol2, vCol3, vCol4)); - } - - newFaces.add(new Face(indexes, face.isSmooth(), face.getMaterialNumber(), newUVSets, vertexColors, temporalMesh)); - - newEdges.add(new Edge(vIndex, vNextEdgeVertIndex, nextEdge.getCrease(), true, temporalMesh)); - newEdges.add(new Edge(vNextEdgeVertIndex, facePointIndex, 0, true, temporalMesh)); - newEdges.add(new Edge(facePointIndex, vPrevEdgeVertIndex, 0, true, temporalMesh)); - newEdges.add(new Edge(vPrevEdgeVertIndex, vIndex, prevEdge.getCrease(), true, temporalMesh)); - } - } - - vertices.addAll(faceVertices); - vertices.addAll(edgeVertices); - normals.addAll(faceNormals); - normals.addAll(edgeNormals); - - for (Edge edge : temporalMesh.getEdges()) { - if (!edge.isInFace()) { - int newVertexIndex = vertices.size(); - vertices.add(vertices.get(edge.getFirstIndex()).add(vertices.get(edge.getSecondIndex())).divideLocal(2)); - normals.add(normals.get(edge.getFirstIndex()).add(normals.get(edge.getSecondIndex())).normalizeLocal()); - - newEdges.add(new Edge(edge.getFirstIndex(), newVertexIndex, edge.getCrease(), false, temporalMesh)); - newEdges.add(new Edge(newVertexIndex, edge.getSecondIndex(), edge.getCrease(), false, temporalMesh)); - verticesOnOriginalEdges.add(newVertexIndex); - } - } - - temporalMesh.getFaces().clear(); - temporalMesh.getFaces().addAll(newFaces); - temporalMesh.getEdges().clear(); - temporalMesh.getEdges().addAll(newEdges); - - temporalMesh.rebuildIndexesMappings(); - } - - /** - * The method subdivides mesh's UV coordinates. It actually performs only Catmull-Clark modifications because if any UV's are present then they are - * automatically subdivided by the simple algorithm. - * @param temporalMesh - * the mesh whose UV coordinates will be applied Catmull-Clark algorithm - */ - private void subdivideUVs(TemporalMesh temporalMesh) { - List faces = temporalMesh.getFaces(); - Map subdividedUVS = new HashMap(); - for (Face face : faces) { - if (face.getUvSets() != null) { - for (Entry> uvset : face.getUvSets().entrySet()) { - UvCoordsSubdivideTemporalMesh uvCoordsSubdivideTemporalMesh = subdividedUVS.get(uvset.getKey()); - if (uvCoordsSubdivideTemporalMesh == null) { - try { - uvCoordsSubdivideTemporalMesh = new UvCoordsSubdivideTemporalMesh(temporalMesh.getBlenderContext()); - } catch (BlenderFileException e) { - assert false : "Something went really wrong! The UvCoordsSubdivideTemporalMesh class should NOT throw exceptions here!"; - } - subdividedUVS.put(uvset.getKey(), uvCoordsSubdivideTemporalMesh); - } - uvCoordsSubdivideTemporalMesh.addFace(uvset.getValue()); - } - } - } - - for (Entry entry : subdividedUVS.entrySet()) { - entry.getValue().rebuildIndexesMappings(); - this.subdivideCatmullClark(entry.getValue()); - - for (int i = 0; i < faces.size(); ++i) { - List uvs = faces.get(i).getUvSets().get(entry.getKey()); - if (uvs != null) { - uvs.clear(); - uvs.addAll(entry.getValue().faceToUVs(i)); - } - } - } - } - - /** - * The method computes the face's normal vector. - * @param face - * the face of the mesh - * @return face's normal vector - */ - private Vector3f computeFaceNormal(Face face) { - Vector3f result = new Vector3f(); - for (Integer index : face.getIndexes()) { - result.addLocal(face.getTemporalMesh().getNormals().get(index)); - } - result.divideLocal(face.getIndexes().size()); - return result; - } - - /** - * The method computes the UV coordinates of the face middle point. - * @param face - * the face of the mesh - * @return a map whose key is the name of the UV set and value is the UV coordinate of the face's middle point - */ - private Map computeFaceUVs(Face face) { - Map result = null; - - Map> uvSets = face.getUvSets(); - if (uvSets != null && uvSets.size() > 0) { - result = new HashMap(uvSets.size()); - - for (Entry> entry : uvSets.entrySet()) { - Vector2f faceUV = new Vector2f(); - for (Vector2f uv : entry.getValue()) { - faceUV.addLocal(uv); - } - faceUV.divideLocal(entry.getValue().size()); - result.put(entry.getKey(), faceUV); - } - } - - return result; - } - - /** - * The mesh interpolates the values of vertex groups weights for new vertices. - * @param vertexGroups - * the vertex groups - * @return interpolated weights of given vertex groups' weights - */ - private Map interpolateVertexGroups(List> vertexGroups) { - Map weightSums = new HashMap(); - if (vertexGroups.size() > 0) { - for (Map vGroup : vertexGroups) { - for (Entry entry : vGroup.entrySet()) { - if (weightSums.containsKey(entry.getKey())) { - weightSums.put(entry.getKey(), weightSums.get(entry.getKey()) + entry.getValue()); - } else { - weightSums.put(entry.getKey(), entry.getValue()); - } - } - } - } - - Map result = new HashMap(weightSums.size()); - for (Entry entry : weightSums.entrySet()) { - result.put(entry.getKey(), entry.getValue() / vertexGroups.size()); - } - - return result; - } - - /** - * The method computes the vertex groups values for face's middle point. - * @param face - * the face of the mesh - * @return face's middle point interpolated vertex groups' weights - */ - private Map computeFaceVertexGroups(Face face) { - if (face.getTemporalMesh().getVertexGroups().size() > 0) { - List> vertexGroups = new ArrayList>(face.getIndexes().size()); - for (Integer index : face.getIndexes()) { - vertexGroups.add(face.getTemporalMesh().getVertexGroups().get(index)); - } - return this.interpolateVertexGroups(vertexGroups); - } - return new HashMap(); - } - - /** - * The method computes face's middle point vertex color. - * @param face - * the face of the mesh - * @return face's middle point vertex color - */ - private byte[] computeFaceVertexColor(Face face) { - if (face.getVertexColors() != null) { - return this.interpolateVertexColors(face.getVertexColors().toArray(new byte[face.getVertexColors().size()][])); - } - return null; - } - - /** - * The method computes the average value for the given vertex colors. - * @param colors - * the vertex colors - * @return vertex colors' average value - */ - private byte[] interpolateVertexColors(byte[]... colors) { - TexturePixel pixel = new TexturePixel(); - TexturePixel temp = new TexturePixel(); - for (int i = 0; i < colors.length; ++i) { - temp.fromARGB8(colors[i][3], colors[i][0], colors[i][1], colors[i][2]); - pixel.add(temp); - } - pixel.divide(colors.length); - byte[] result = new byte[4]; - pixel.toRGBA8(result); - return result; - } - - /** - * The method finds an edge between the given vertices in the mesh. - * @param temporalMesh - * the mesh - * @param index1 - * first index of the edge - * @param index2 - * second index of the edge - * @return found edge or null - */ - private Edge findEdge(TemporalMesh temporalMesh, int index1, int index2) { - for (Edge edge : temporalMesh.getEdges()) { - if (edge.getFirstIndex() == index1 && edge.getSecondIndex() == index2 || edge.getFirstIndex() == index2 && edge.getSecondIndex() == index1) { - return edge; - } - } - return null; - } - - /** - * This is a helper class for UV coordinates subdivision. UV's form a mesh that is being applied the same algorithms as a regular mesh. - * This way one code handles two issues. After applying Catmull-Clark algorithm the UV-mesh is transformed back into UV coordinates. - * - * @author Marcin Roguski (Kaelthas) - */ - private static class UvCoordsSubdivideTemporalMesh extends TemporalMesh { - private static final Vector3f NORMAL = new Vector3f(0, 0, 1); - - public UvCoordsSubdivideTemporalMesh(BlenderContext blenderContext) throws BlenderFileException { - super(null, blenderContext, false); - } - - /** - * Adds a UV-face to the mesh. - * @param uvs - * the UV coordinates - */ - public void addFace(List uvs) { - Integer[] indexes = new Integer[uvs.size()]; - int i = 0; - - for (Vector2f uv : uvs) { - Vector3f v = new Vector3f(uv.x, uv.y, 0); - int index = vertices.indexOf(v); - if (index >= 0) { - indexes[i++] = index; - } else { - indexes[i++] = vertices.size(); - vertices.add(v); - normals.add(NORMAL); - } - } - faces.add(new Face(indexes, false, 0, null, null, this)); - for (i = 1; i < indexes.length; ++i) { - edges.add(new Edge(indexes[i - 1], indexes[i], 0, true, this)); - } - edges.add(new Edge(indexes[indexes.length - 1], indexes[0], 0, true, this)); - } - - /** - * Converts the mesh back into UV coordinates for the given face. - * @param faceIndex - * the index of the face - * @return UV coordinates - */ - public List faceToUVs(int faceIndex) { - Face face = faces.get(faceIndex); - List result = new ArrayList(face.getIndexes().size()); - for (Integer index : face.getIndexes()) { - Vector3f v = vertices.get(index); - result.add(new Vector2f(v.x, v.y)); - } - return result; - } - } - - /** - * A point computed for each vertex before applying CC subdivision and after simple subdivision. - * This class has a target where the vertices will be drawn to with a proper strength (value from 0 to 1). - * - * The algorithm of computing the target point was made by observing how blender behaves. - * If a vertex has one or less creased edges (means edges that have non zero crease value) the target will not exist. - * If a vertex is a border vertex and has two creased edges - the target will be the original simple subdivided vertex. - * If a vertex is not a border vertex and have two creased edges - then it will be drawned to the plane defined by those - * two edges. - * If a vertex has 3 or more creased edges it will be drawn to its original vertex before CC subdivision with average strength - * computed from edges' crease values. - * - * @author Marcin Roguski (Kaelthas) - */ - private static class CreasePoint { - private Vector3f target = new Vector3f(); - private float weight; - private int index; - - public CreasePoint(int index, boolean borderIndex, List creaseEdges, TemporalMesh temporalMesh) { - this.index = index; - if (creaseEdges == null || creaseEdges.size() <= 1) { - target = null;// crease is used when vertex belongs to at least 2 creased edges - } else { - int creasedEdgesCount = 0; - for (Edge edge : creaseEdges) { - if (edge.getCrease() > 0) { - ++creasedEdgesCount; - weight += edge.getCrease(); - target.addLocal(temporalMesh.getVertices().get(edge.getOtherIndex(index))); - } - } - - if (creasedEdgesCount <= 1) { - target = null;// crease is used when vertex belongs to at least 2 creased edges - } else if (creasedEdgesCount == 2) { - if (borderIndex) { - target.set(temporalMesh.getVertices().get(index)); - } else { - target.addLocal(temporalMesh.getVertices().get(index)).divideLocal(creasedEdgesCount + 1); - } - } else { - target.set(temporalMesh.getVertices().get(index)); - } - if (creasedEdgesCount > 0) { - weight /= creasedEdgesCount; - } - } - } - - public Vector3f getTarget() { - return target; - } - - public float getWeight() { - return weight; - } - - @Override - public String toString() { - return "CreasePoint [index = " + index + ", target=" + target + ", weight=" + weight + "]"; - } - } -} diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/modifiers/TriangulateModifier.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/modifiers/TriangulateModifier.java deleted file mode 100644 index 375565975..000000000 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/modifiers/TriangulateModifier.java +++ /dev/null @@ -1,54 +0,0 @@ -package com.jme3.scene.plugins.blender.modifiers; - -import java.util.logging.Level; -import java.util.logging.Logger; - -import com.jme3.scene.Node; -import com.jme3.scene.plugins.blender.BlenderContext; -import com.jme3.scene.plugins.blender.file.BlenderFileException; -import com.jme3.scene.plugins.blender.file.Structure; -import com.jme3.scene.plugins.blender.meshes.TemporalMesh; - -/** - * The triangulation modifier. It does not take any settings into account so if the result is different than - * in blender then please apply the modifier before importing. - * - * @author Marcin Roguski - */ -public class TriangulateModifier extends Modifier { - private static final Logger LOGGER = Logger.getLogger(TriangulateModifier.class.getName()); - - /** - * This constructor reads animation data from the object structore. The - * stored data is the AnimData and additional data is armature's OMA. - * - * @param objectStructure - * the structure of the object - * @param modifierStructure - * the structure of the modifier - * @param blenderContext - * the blender context - * @throws BlenderFileException - * this exception is thrown when the blender file is somehow - * corrupted - */ - public TriangulateModifier(Structure objectStructure, Structure modifierStructure, BlenderContext blenderContext) throws BlenderFileException { - if (this.validate(modifierStructure, blenderContext)) { - LOGGER.warning("Triangulation modifier does not take modifier options into account. If triangulation result is different" + " than the model in blender please apply the modifier before importing!"); - } - } - - @Override - public void apply(Node node, BlenderContext blenderContext) { - if (invalid) { - LOGGER.log(Level.WARNING, "Triangulate modifier is invalid! Cannot be applied to: {0}", node.getName()); - } - TemporalMesh temporalMesh = this.getTemporalMesh(node); - if (temporalMesh != null) { - LOGGER.log(Level.FINE, "Applying triangulation modifier to: {0}", temporalMesh); - temporalMesh.triangulate(); - } else { - LOGGER.log(Level.WARNING, "Cannot find temporal mesh for node: {0}. The modifier will NOT be applied!", node); - } - } -} diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/objects/ObjectHelper.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/objects/ObjectHelper.java deleted file mode 100644 index 17c0d421e..000000000 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/objects/ObjectHelper.java +++ /dev/null @@ -1,520 +0,0 @@ -/* - * 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.blender.objects; - -import java.nio.Buffer; -import java.nio.FloatBuffer; -import java.nio.IntBuffer; -import java.nio.ShortBuffer; -import java.util.Collection; -import java.util.List; -import java.util.logging.Level; -import java.util.logging.Logger; - -import com.jme3.light.Light; -import com.jme3.math.FastMath; -import com.jme3.math.Matrix4f; -import com.jme3.math.Transform; -import com.jme3.math.Vector3f; -import com.jme3.renderer.Camera; -import com.jme3.scene.CameraNode; -import com.jme3.scene.Geometry; -import com.jme3.scene.LightNode; -import com.jme3.scene.Mesh.Mode; -import com.jme3.scene.Node; -import com.jme3.scene.Spatial; -import com.jme3.scene.Spatial.CullHint; -import com.jme3.scene.VertexBuffer.Type; -import com.jme3.scene.plugins.blender.AbstractBlenderHelper; -import com.jme3.scene.plugins.blender.BlenderContext; -import com.jme3.scene.plugins.blender.BlenderContext.LoadedDataType; -import com.jme3.scene.plugins.blender.animations.AnimationHelper; -import com.jme3.scene.plugins.blender.cameras.CameraHelper; -import com.jme3.scene.plugins.blender.constraints.ConstraintHelper; -import com.jme3.scene.plugins.blender.curves.CurvesHelper; -import com.jme3.scene.plugins.blender.file.BlenderFileException; -import com.jme3.scene.plugins.blender.file.DynamicArray; -import com.jme3.scene.plugins.blender.file.Pointer; -import com.jme3.scene.plugins.blender.file.Structure; -import com.jme3.scene.plugins.blender.lights.LightHelper; -import com.jme3.scene.plugins.blender.meshes.MeshHelper; -import com.jme3.scene.plugins.blender.meshes.TemporalMesh; -import com.jme3.scene.plugins.blender.modifiers.Modifier; -import com.jme3.scene.plugins.blender.modifiers.ModifierHelper; -import com.jme3.util.TempVars; - -/** - * A class that is used in object calculations. - * - * @author Marcin Roguski (Kaelthas) - */ -public class ObjectHelper extends AbstractBlenderHelper { - private static final Logger LOGGER = Logger.getLogger(ObjectHelper.class.getName()); - - public static final String OMA_MARKER = "oma"; - public static final String ARMATURE_NODE_MARKER = "armature-node"; - - /** - * This constructor parses the given blender version and stores the result. - * Some functionalities may differ in different blender versions. - * - * @param blenderVersion - * the version read from the blend file - * @param blenderContext - * the blender context - */ - public ObjectHelper(String blenderVersion, BlenderContext blenderContext) { - super(blenderVersion, blenderContext); - } - - /** - * This method reads the given structure and createn an object that - * represents the data. - * - * @param objectStructure - * the object's structure - * @param blenderContext - * the blender context - * @return blener's object representation or null if its type is excluded from loading - * @throws BlenderFileException - * an exception is thrown when the given data is inapropriate - */ - public Object toObject(Structure objectStructure, BlenderContext blenderContext) throws BlenderFileException { - Object loadedResult = blenderContext.getLoadedFeature(objectStructure.getOldMemoryAddress(), LoadedDataType.FEATURE); - if (loadedResult != null) { - return loadedResult; - } - - LOGGER.fine("Loading blender object."); - if ("ID".equals(objectStructure.getType())) { - Node object = (Node) this.loadLibrary(objectStructure); - if (object.getParent() != null) { - LOGGER.log(Level.FINEST, "Detaching object {0}, loaded from external file, from its parent.", object); - object.getParent().detachChild(object); - } - return object; - } - int type = ((Number) objectStructure.getFieldValue("type")).intValue(); - ObjectType objectType = ObjectType.valueOf(type); - LOGGER.log(Level.FINE, "Type of the object: {0}.", objectType); - - int lay = ((Number) objectStructure.getFieldValue("lay")).intValue(); - if ((lay & blenderContext.getBlenderKey().getLayersToLoad()) == 0) { - LOGGER.fine("The layer this object is located in is not included in loading."); - return null; - } - - blenderContext.pushParent(objectStructure); - String name = objectStructure.getName(); - LOGGER.log(Level.FINE, "Loading obejct: {0}", name); - - int restrictflag = ((Number) objectStructure.getFieldValue("restrictflag")).intValue(); - boolean visible = (restrictflag & 0x01) != 0; - - Pointer pParent = (Pointer) objectStructure.getFieldValue("parent"); - Object parent = blenderContext.getLoadedFeature(pParent.getOldMemoryAddress(), LoadedDataType.FEATURE); - if (parent == null && pParent.isNotNull()) { - Structure parentStructure = pParent.fetchData().get(0); - parent = this.toObject(parentStructure, blenderContext); - } - - Transform t = this.getTransformation(objectStructure, blenderContext); - LOGGER.log(Level.FINE, "Importing object of type: {0}", objectType); - Node result = null; - try { - switch (objectType) { - case LATTICE: - case METABALL: - case TEXT: - case WAVE: - LOGGER.log(Level.WARNING, "{0} type is not supported but the node will be returned in order to keep parent - child relationship.", objectType); - case EMPTY: - case ARMATURE: - // need to use an empty node to properly create - // parent-children relationships between nodes - result = new Node(name); - break; - case MESH: - result = new Node(name); - MeshHelper meshHelper = blenderContext.getHelper(MeshHelper.class); - Pointer pMesh = (Pointer) objectStructure.getFieldValue("data"); - List meshesArray = pMesh.fetchData(); - TemporalMesh temporalMesh = meshHelper.toTemporalMesh(meshesArray.get(0), blenderContext); - if (temporalMesh != null) { - result.attachChild(temporalMesh); - } - break; - case SURF: - case CURVE: - result = new Node(name); - Pointer pCurve = (Pointer) objectStructure.getFieldValue("data"); - if (pCurve.isNotNull()) { - CurvesHelper curvesHelper = blenderContext.getHelper(CurvesHelper.class); - Structure curveData = pCurve.fetchData().get(0); - TemporalMesh curvesTemporalMesh = curvesHelper.toCurve(curveData, blenderContext); - if (curvesTemporalMesh != null) { - result.attachChild(curvesTemporalMesh); - } - } - break; - case LAMP: - Pointer pLamp = (Pointer) objectStructure.getFieldValue("data"); - if (pLamp.isNotNull()) { - LightHelper lightHelper = blenderContext.getHelper(LightHelper.class); - List lampsArray = pLamp.fetchData(); - Light light = lightHelper.toLight(lampsArray.get(0), blenderContext); - if (light == null) { - // probably some light type is not supported, just create a node so that we can maintain child-parent relationship for nodes - result = new Node(name); - } else { - result = new LightNode(name, light); - } - } - break; - case CAMERA: - Pointer pCamera = (Pointer) objectStructure.getFieldValue("data"); - if (pCamera.isNotNull()) { - CameraHelper cameraHelper = blenderContext.getHelper(CameraHelper.class); - List camerasArray = pCamera.fetchData(); - Camera camera = cameraHelper.toCamera(camerasArray.get(0), blenderContext); - if (camera == null) { - // just create a node so that we can maintain child-parent relationship for nodes - result = new Node(name); - } else { - result = new CameraNode(name, camera); - } - } - break; - default: - LOGGER.log(Level.WARNING, "Unsupported object type: {0}", type); - } - - if (result != null) { - LOGGER.fine("Storing loaded feature in blender context and applying markers (those will be removed before the final result is released)."); - Long oma = objectStructure.getOldMemoryAddress(); - blenderContext.addLoadedFeatures(oma, LoadedDataType.STRUCTURE, objectStructure); - blenderContext.addLoadedFeatures(oma, LoadedDataType.FEATURE, result); - - blenderContext.addMarker(OMA_MARKER, result, objectStructure.getOldMemoryAddress()); - if (objectType == ObjectType.ARMATURE) { - blenderContext.addMarker(ARMATURE_NODE_MARKER, result, Boolean.TRUE); - } - - result.setLocalTransform(t); - result.setCullHint(visible ? CullHint.Always : CullHint.Inherit); - if (parent instanceof Node) { - ((Node) parent).attachChild(result); - } - - LOGGER.fine("Reading and applying object's modifiers."); - ModifierHelper modifierHelper = blenderContext.getHelper(ModifierHelper.class); - Collection modifiers = modifierHelper.readModifiers(objectStructure, blenderContext); - for (Modifier modifier : modifiers) { - modifier.apply(result, blenderContext); - } - - if (result.getChildren() != null && result.getChildren().size() > 0) { - if (result.getChildren().size() == 1 && result.getChild(0) instanceof TemporalMesh) { - LOGGER.fine("Converting temporal mesh into jme geometries."); - ((TemporalMesh) result.getChild(0)).toGeometries(); - } - - LOGGER.fine("Applying proper scale to the geometries."); - for (Spatial child : result.getChildren()) { - if (child instanceof Geometry) { - this.flipMeshIfRequired((Geometry) child, child.getWorldScale()); - } - } - } - - // I prefer do compute bounding box here than read it from the file - result.updateModelBound(); - - LOGGER.fine("Applying animations to the object if such are defined."); - AnimationHelper animationHelper = blenderContext.getHelper(AnimationHelper.class); - animationHelper.applyAnimations(result, blenderContext.getBlenderKey().getAnimationMatchMethod()); - - LOGGER.fine("Loading constraints connected with this object."); - ConstraintHelper constraintHelper = blenderContext.getHelper(ConstraintHelper.class); - constraintHelper.loadConstraints(objectStructure, blenderContext); - - LOGGER.fine("Loading custom properties."); - if (blenderContext.getBlenderKey().isLoadObjectProperties()) { - Properties properties = this.loadProperties(objectStructure, blenderContext); - // the loaded property is a group property, so we need to get - // each value and set it to Spatial - if (properties != null && properties.getValue() != null) { - this.applyProperties(result, properties); - } - } - } - } finally { - blenderContext.popParent(); - } - return result; - } - - /** - * The method flips the mesh if the scale is mirroring it. Mirroring scale has either 1 or all 3 factors negative. - * If two factors are negative then there is no mirroring because a rotation and translation can be found that will - * lead to the same transform when all scales are positive. - * - * @param geometry - * the geometry that is being flipped if necessary - * @param scale - * the scale vector of the given geometry - */ - private void flipMeshIfRequired(Geometry geometry, Vector3f scale) { - float s = scale.x * scale.y * scale.z; - - if (s < 0 && geometry.getMesh() != null) {// negative s means that the scale is mirroring the object - FloatBuffer normals = geometry.getMesh().getFloatBuffer(Type.Normal); - if (normals != null) { - for (int i = 0; i < normals.limit(); i += 3) { - if (scale.x < 0) { - normals.put(i, -normals.get(i)); - } - if (scale.y < 0) { - normals.put(i + 1, -normals.get(i + 1)); - } - if (scale.z < 0) { - normals.put(i + 2, -normals.get(i + 2)); - } - } - } - - if (geometry.getMesh().getMode() == Mode.Triangles) {// there is no need to flip the indexes for lines and points - LOGGER.finer("Flipping index order in triangle mesh."); - Buffer indexBuffer = geometry.getMesh().getBuffer(Type.Index).getData(); - for (int i = 0; i < indexBuffer.limit(); i += 3) { - if (indexBuffer instanceof ShortBuffer) { - short index = ((ShortBuffer) indexBuffer).get(i + 1); - ((ShortBuffer) indexBuffer).put(i + 1, ((ShortBuffer) indexBuffer).get(i + 2)); - ((ShortBuffer) indexBuffer).put(i + 2, index); - } else { - int index = ((IntBuffer) indexBuffer).get(i + 1); - ((IntBuffer) indexBuffer).put(i + 1, ((IntBuffer) indexBuffer).get(i + 2)); - ((IntBuffer) indexBuffer).put(i + 2, index); - } - } - } - } - } - - /** - * Checks if the first given OMA points to a parent of the second one. - * The parent need not to be the direct one. This method should be called when we are sure - * that both of the features are alred loaded because it does not check it. - * The OMA's should point to a spatials, otherwise the function will throw ClassCastException. - * @param supposedParentOMA - * the OMA of the node that we suppose might be a parent of the second one - * @param spatialOMA - * the OMA of the scene's node - * @return true if the first given OMA points to a parent of the second one and false otherwise - */ - public boolean isParent(Long supposedParentOMA, Long spatialOMA) { - Spatial supposedParent = (Spatial) blenderContext.getLoadedFeature(supposedParentOMA, LoadedDataType.FEATURE); - Spatial spatial = (Spatial) blenderContext.getLoadedFeature(spatialOMA, LoadedDataType.FEATURE); - - Spatial parent = spatial.getParent(); - while (parent != null) { - if (parent.equals(supposedParent)) { - return true; - } - parent = parent.getParent(); - } - return false; - } - - /** - * This method calculates local transformation for the object. Parentage is - * taken under consideration. - * - * @param objectStructure - * the object's structure - * @return objects transformation relative to its parent - */ - public Transform getTransformation(Structure objectStructure, BlenderContext blenderContext) { - TempVars tempVars = TempVars.get(); - - Matrix4f parentInv = tempVars.tempMat4; - Pointer pParent = (Pointer) objectStructure.getFieldValue("parent"); - if (pParent.isNotNull()) { - Structure parentObjectStructure = (Structure) blenderContext.getLoadedFeature(pParent.getOldMemoryAddress(), LoadedDataType.STRUCTURE); - this.getMatrix(parentObjectStructure, "obmat", fixUpAxis, parentInv).invertLocal(); - } else { - parentInv.loadIdentity(); - } - - Matrix4f globalMatrix = this.getMatrix(objectStructure, "obmat", fixUpAxis, tempVars.tempMat42); - Matrix4f localMatrix = parentInv.multLocal(globalMatrix); - - this.getSizeSignums(objectStructure, tempVars.vect1); - - localMatrix.toTranslationVector(tempVars.vect2); - localMatrix.toRotationQuat(tempVars.quat1); - localMatrix.toScaleVector(tempVars.vect3); - - Transform t = new Transform(tempVars.vect2, tempVars.quat1.normalizeLocal(), tempVars.vect3.multLocal(tempVars.vect1)); - tempVars.release(); - return t; - } - - /** - * The method gets the signs of the scale factors and stores them properly in the given vector. - * @param objectStructure - * the object's structure - * @param store - * the vector where the result will be stored - */ - @SuppressWarnings("unchecked") - private void getSizeSignums(Structure objectStructure, Vector3f store) { - DynamicArray size = (DynamicArray) objectStructure.getFieldValue("size"); - if (fixUpAxis) { - store.x = Math.signum(size.get(0).floatValue()); - store.y = Math.signum(size.get(2).floatValue()); - store.z = Math.signum(size.get(1).floatValue()); - } else { - store.x = Math.signum(size.get(0).floatValue()); - store.y = Math.signum(size.get(1).floatValue()); - store.z = Math.signum(size.get(2).floatValue()); - } - } - - /** - * This method returns the matrix of a given name for the given structure. - * It takes up axis into consideration. - * - * The method that moves the matrix from Z-up axis to Y-up axis space is as follows: - * - load the matrix directly from blender (it has the Z-up axis orientation) - * - switch the second and third rows in the matrix - * - switch the second and third column in the matrix - * - multiply the values in the third row by -1 - * - multiply the values in the third column by -1 - * - * The result matrix is now in Y-up axis orientation. - * The procedure was discovered by experimenting but it looks like it's working :) - * The previous procedure transformet the loaded matrix into component (loc, rot, scale), - * switched several values and pu the back into the matrix. - * It worked fine until models with negative scale are used. - * The current method is not touched by that flaw. - * - * @param structure - * the structure with matrix data - * @param matrixName - * the name of the matrix - * @param fixUpAxis - * tells if the Y axis is a UP axis - * @param store - * the matrix where the result will pe placed - * @return the required matrix - */ - @SuppressWarnings("unchecked") - private Matrix4f getMatrix(Structure structure, String matrixName, boolean fixUpAxis, Matrix4f store) { - DynamicArray obmat = (DynamicArray) structure.getFieldValue(matrixName); - // the matrix must be square - int rowAndColumnSize = Math.abs((int) Math.sqrt(obmat.getTotalSize())); - for (int i = 0; i < rowAndColumnSize; ++i) { - for (int j = 0; j < rowAndColumnSize; ++j) { - float value = obmat.get(j, i).floatValue(); - if (Math.abs(value) <= FastMath.FLT_EPSILON) { - value = 0; - } - store.set(i, j, value); - } - } - if (fixUpAxis) { - // first switch the second and third row - for (int i = 0; i < 4; ++i) { - float temp = store.get(1, i); - store.set(1, i, store.get(2, i)); - store.set(2, i, temp); - } - - // then switch the second and third column - for (int i = 0; i < 4; ++i) { - float temp = store.get(i, 1); - store.set(i, 1, store.get(i, 2)); - store.set(i, 2, temp); - } - - // multiply the values in the third row by -1 - store.m20 *= -1; - store.m21 *= -1; - store.m22 *= -1; - store.m23 *= -1; - - // multiply the values in the third column by -1 - store.m02 *= -1; - store.m12 *= -1; - store.m22 *= -1; - store.m32 *= -1; - } - - return store; - } - - /** - * This method returns the matrix of a given name for the given structure. - * It takes up axis into consideration. - * - * @param structure - * the structure with matrix data - * @param matrixName - * the name of the matrix - * @param fixUpAxis - * tells if the Y axis is a UP axis - * @return the required matrix - */ - public Matrix4f getMatrix(Structure structure, String matrixName, boolean fixUpAxis) { - return this.getMatrix(structure, matrixName, fixUpAxis, new Matrix4f()); - } - - private static enum ObjectType { - EMPTY(0), MESH(1), CURVE(2), SURF(3), TEXT(4), METABALL(5), LAMP(10), CAMERA(11), WAVE(21), LATTICE(22), ARMATURE(25); - - private int blenderTypeValue; - - private ObjectType(int blenderTypeValue) { - this.blenderTypeValue = blenderTypeValue; - } - - public static ObjectType valueOf(int blenderTypeValue) throws BlenderFileException { - for (ObjectType type : ObjectType.values()) { - if (type.blenderTypeValue == blenderTypeValue) { - return type; - } - } - throw new BlenderFileException("Unknown type value: " + blenderTypeValue); - } - } -} diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/objects/Properties.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/objects/Properties.java deleted file mode 100644 index c1f867543..000000000 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/objects/Properties.java +++ /dev/null @@ -1,365 +0,0 @@ -package com.jme3.scene.plugins.blender.objects; - -import com.jme3.scene.plugins.blender.BlenderContext; -import com.jme3.scene.plugins.blender.file.BlenderFileException; -import com.jme3.scene.plugins.blender.file.BlenderInputStream; -import com.jme3.scene.plugins.blender.file.FileBlockHeader; -import com.jme3.scene.plugins.blender.file.Pointer; -import com.jme3.scene.plugins.blender.file.Structure; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -/** - * The blender object's custom properties. - * This class is valid for all versions of blender. - * @author Marcin Roguski (Kaelthas) - */ -public class Properties implements Cloneable { - // property type - public static final int IDP_STRING = 0; - public static final int IDP_INT = 1; - public static final int IDP_FLOAT = 2; - public static final int IDP_ARRAY = 5; - public static final int IDP_GROUP = 6; - // public static final int IDP_ID = 7;//this is not implemented in blender (yet) - public static final int IDP_DOUBLE = 8; - // the following are valid for blender 2.5x+ - public static final int IDP_IDPARRAY = 9; - public static final int IDP_NUMTYPES = 10; - - protected static final String RNA_PROPERTY_NAME = "_RNA_UI"; - /** Default name of the property (used if the name is not specified in blender file). */ - protected static final String DEFAULT_NAME = "Unnamed property"; - - /** The name of the property. */ - private String name; - /** The type of the property. */ - private int type; - /** The subtype of the property. Defines the type of array's elements. */ - private int subType; - /** The value of the property. */ - private Object value; - /** The description of the property. */ - private String description; - - /** - * This method loads the property from the belnder file. - * @param idPropertyStructure - * the ID structure constining the property - * @param blenderContext - * the blender context - * @throws BlenderFileException - * an exception is thrown when the belnder file is somehow invalid - */ - public void load(Structure idPropertyStructure, BlenderContext blenderContext) throws BlenderFileException { - name = idPropertyStructure.getFieldValue("name").toString(); - if (name == null || name.length() == 0) { - name = DEFAULT_NAME; - } - subType = ((Number) idPropertyStructure.getFieldValue("subtype")).intValue(); - type = ((Number) idPropertyStructure.getFieldValue("type")).intValue(); - - // reading the data - Structure data = (Structure) idPropertyStructure.getFieldValue("data"); - int len = ((Number) idPropertyStructure.getFieldValue("len")).intValue(); - switch (type) { - case IDP_STRING: { - Pointer pointer = (Pointer) data.getFieldValue("pointer"); - BlenderInputStream bis = blenderContext.getInputStream(); - FileBlockHeader dataFileBlock = blenderContext.getFileBlock(pointer.getOldMemoryAddress()); - bis.setPosition(dataFileBlock.getBlockPosition()); - value = bis.readString(); - break; - } - case IDP_INT: - int intValue = ((Number) data.getFieldValue("val")).intValue(); - value = Integer.valueOf(intValue); - break; - case IDP_FLOAT: - int floatValue = ((Number) data.getFieldValue("val")).intValue(); - value = Float.valueOf(Float.intBitsToFloat(floatValue)); - break; - case IDP_ARRAY: { - Pointer pointer = (Pointer) data.getFieldValue("pointer"); - BlenderInputStream bis = blenderContext.getInputStream(); - FileBlockHeader dataFileBlock = blenderContext.getFileBlock(pointer.getOldMemoryAddress()); - bis.setPosition(dataFileBlock.getBlockPosition()); - int elementAmount = dataFileBlock.getSize(); - switch (subType) { - case IDP_INT: - elementAmount /= 4; - int[] intList = new int[elementAmount]; - for (int i = 0; i < elementAmount; ++i) { - intList[i] = bis.readInt(); - } - value = intList; - break; - case IDP_FLOAT: - elementAmount /= 4; - float[] floatList = new float[elementAmount]; - for (int i = 0; i < elementAmount; ++i) { - floatList[i] = bis.readFloat(); - } - value = floatList; - break; - case IDP_DOUBLE: - elementAmount /= 8; - double[] doubleList = new double[elementAmount]; - for (int i = 0; i < elementAmount; ++i) { - doubleList[i] = bis.readDouble(); - } - value = doubleList; - break; - default: - throw new IllegalStateException("Invalid array subtype: " + subType); - } - } - case IDP_GROUP: - Structure group = (Structure) data.getFieldValue("group"); - List dataList = group.evaluateListBase(); - List subProperties = new ArrayList(len); - for (Structure d : dataList) { - Properties properties = new Properties(); - properties.load(d, blenderContext); - subProperties.add(properties); - } - value = subProperties; - break; - case IDP_DOUBLE: - int doublePart1 = ((Number) data.getFieldValue("val")).intValue(); - int doublePart2 = ((Number) data.getFieldValue("val2")).intValue(); - long doubleVal = (long) doublePart2 << 32 | doublePart1; - value = Double.valueOf(Double.longBitsToDouble(doubleVal)); - break; - case IDP_IDPARRAY: { - Pointer pointer = (Pointer) data.getFieldValue("pointer"); - List arrays = pointer.fetchData(); - List result = new ArrayList(arrays.size()); - Properties temp = new Properties(); - for (Structure array : arrays) { - temp.load(array, blenderContext); - result.add(temp.value); - } - value = result; - break; - } - case IDP_NUMTYPES: - throw new UnsupportedOperationException(); - // case IDP_ID://not yet implemented in blender - // return null; - default: - throw new IllegalStateException("Unknown custom property type: " + type); - } - this.completeLoading(); - } - - /** - * This method returns the name of the property. - * @return the name of the property - */ - public String getName() { - return name; - } - - /** - * This method returns the value of the property. - * The type of the value depends on the type of the property. - * @return the value of the property - */ - public Object getValue() { - return value; - } - - /** - * @return the names of properties that are stored withing this property - * (assuming this property is of IDP_GROUP type) - */ - @SuppressWarnings("unchecked") - public List getSubPropertiesNames() { - List result = null; - if (type == IDP_GROUP) { - List properties = (List) value; - if (properties != null && properties.size() > 0) { - result = new ArrayList(properties.size()); - for (Properties property : properties) { - result.add(property.getName()); - } - } - } - return result; - } - - /** - * This method returns the same as getValue if the current property is of - * other type than IDP_GROUP and its name matches 'propertyName' param. If - * this property is a group property the method tries to find subproperty - * value of the given name. The first found value is returnes os use this - * method wisely. If no property of a given name is foung - null - * is returned. - * - * @param propertyName - * the name of the property - * @return found property value or null - */ - @SuppressWarnings("unchecked") - public Object findValue(String propertyName) { - if (name.equals(propertyName)) { - return value; - } else { - if (type == IDP_GROUP) { - List props = (List) value; - for (Properties p : props) { - Object v = p.findValue(propertyName); - if (v != null) { - return v; - } - } - } - } - return null; - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - this.append(sb, new StringBuilder()); - return sb.toString(); - } - - /** - * This method appends the data of the property to the given string buffer. - * @param sb - * string buffer - * @param indent - * indent buffer - */ - @SuppressWarnings("unchecked") - private void append(StringBuilder sb, StringBuilder indent) { - sb.append(indent).append("name: ").append(name).append("\n\r"); - sb.append(indent).append("type: ").append(type).append("\n\r"); - sb.append(indent).append("subType: ").append(subType).append("\n\r"); - sb.append(indent).append("description: ").append(description).append("\n\r"); - indent.append('\t'); - sb.append(indent).append("value: "); - if (value instanceof Properties) { - ((Properties) value).append(sb, indent); - } else if (value instanceof List) { - for (Object v : (List) value) { - if (v instanceof Properties) { - sb.append(indent).append("{\n\r"); - indent.append('\t'); - ((Properties) v).append(sb, indent); - indent.deleteCharAt(indent.length() - 1); - sb.append(indent).append("}\n\r"); - } else { - sb.append(v); - } - } - } else { - sb.append(value); - } - sb.append("\n\r"); - indent.deleteCharAt(indent.length() - 1); - } - - /** - * This method should be called after the properties loading. - * It loads the properties from the _RNA_UI property and removes this property from the - * result list. - */ - @SuppressWarnings("unchecked") - protected void completeLoading() { - if (type == IDP_GROUP) { - List groupProperties = (List) value; - Properties rnaUI = null; - for (Properties properties : groupProperties) { - if (properties.name.equals(RNA_PROPERTY_NAME) && properties.type == IDP_GROUP) { - rnaUI = properties; - break; - } - } - if (rnaUI != null) { - // removing the RNA from the result list - groupProperties.remove(rnaUI); - - // loading the descriptions - Map descriptions = new HashMap(groupProperties.size()); - List propertiesRNA = (List) rnaUI.value; - for (Properties properties : propertiesRNA) { - String name = properties.name; - String description = null; - List rnaData = (List) properties.value; - for (Properties rna : rnaData) { - if ("description".equalsIgnoreCase(rna.name)) { - description = (String) rna.value; - break; - } - } - descriptions.put(name, description); - } - - // applying the descriptions - for (Properties properties : groupProperties) { - properties.description = descriptions.get(properties.name); - } - } - } - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + (description == null ? 0 : description.hashCode()); - result = prime * result + (name == null ? 0 : name.hashCode()); - result = prime * result + subType; - result = prime * result + type; - result = prime * result + (value == null ? 0 : value.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (this.getClass() != obj.getClass()) { - return false; - } - Properties other = (Properties) obj; - if (description == null) { - if (other.description != null) { - return false; - } - } else if (!description.equals(other.description)) { - return false; - } - if (name == null) { - if (other.name != null) { - return false; - } - } else if (!name.equals(other.name)) { - return false; - } - if (subType != other.subType) { - return false; - } - if (type != other.type) { - return false; - } - if (value == null) { - if (other.value != null) { - return false; - } - } else if (!value.equals(other.value)) { - return false; - } - return true; - } -} diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/particles/ParticlesHelper.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/particles/ParticlesHelper.java deleted file mode 100644 index 85c093f66..000000000 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/particles/ParticlesHelper.java +++ /dev/null @@ -1,192 +0,0 @@ -package com.jme3.scene.plugins.blender.particles; - -import com.jme3.effect.ParticleEmitter; -import com.jme3.effect.ParticleMesh.Type; -import com.jme3.effect.influencers.EmptyParticleInfluencer; -import com.jme3.effect.influencers.NewtonianParticleInfluencer; -import com.jme3.effect.influencers.ParticleInfluencer; -import com.jme3.effect.shapes.EmitterMeshConvexHullShape; -import com.jme3.effect.shapes.EmitterMeshFaceShape; -import com.jme3.effect.shapes.EmitterMeshVertexShape; -import com.jme3.math.ColorRGBA; -import com.jme3.scene.plugins.blender.AbstractBlenderHelper; -import com.jme3.scene.plugins.blender.BlenderContext; -import com.jme3.scene.plugins.blender.file.BlenderFileException; -import com.jme3.scene.plugins.blender.file.DynamicArray; -import com.jme3.scene.plugins.blender.file.Pointer; -import com.jme3.scene.plugins.blender.file.Structure; - -import java.util.logging.Logger; - -public class ParticlesHelper extends AbstractBlenderHelper { - private static final Logger LOGGER = Logger.getLogger(ParticlesHelper.class.getName()); - - // part->type - public static final int PART_EMITTER = 0; - public static final int PART_REACTOR = 1; - public static final int PART_HAIR = 2; - public static final int PART_FLUID = 3; - - // part->flag - public static final int PART_REACT_STA_END = 1; - public static final int PART_REACT_MULTIPLE = 2; - public static final int PART_LOOP = 4; - // public static final int PART_LOOP_INSTANT =8; - public static final int PART_HAIR_GEOMETRY = 16; - public static final int PART_UNBORN = 32; // show unborn particles - public static final int PART_DIED = 64; // show died particles - public static final int PART_TRAND = 128; - public static final int PART_EDISTR = 256; // particle/face from face areas - public static final int PART_STICKY = 512; // collided particles can stick to collider - public static final int PART_DIE_ON_COL = 1 << 12; - public static final int PART_SIZE_DEFL = 1 << 13; // swept sphere deflections - public static final int PART_ROT_DYN = 1 << 14; // dynamic rotation - public static final int PART_SIZEMASS = 1 << 16; - public static final int PART_ABS_LENGTH = 1 << 15; - public static final int PART_ABS_TIME = 1 << 17; - public static final int PART_GLOB_TIME = 1 << 18; - public static final int PART_BOIDS_2D = 1 << 19; - public static final int PART_BRANCHING = 1 << 20; - public static final int PART_ANIM_BRANCHING = 1 << 21; - public static final int PART_SELF_EFFECT = 1 << 22; - public static final int PART_SYMM_BRANCHING = 1 << 24; - public static final int PART_HAIR_BSPLINE = 1024; - public static final int PART_GRID_INVERT = 1 << 26; - public static final int PART_CHILD_EFFECT = 1 << 27; - public static final int PART_CHILD_SEAMS = 1 << 28; - public static final int PART_CHILD_RENDER = 1 << 29; - public static final int PART_CHILD_GUIDE = 1 << 30; - - // part->from - public static final int PART_FROM_VERT = 0; - public static final int PART_FROM_FACE = 1; - public static final int PART_FROM_VOLUME = 2; - public static final int PART_FROM_PARTICLE = 3; - public static final int PART_FROM_CHILD = 4; - - // part->phystype - public static final int PART_PHYS_NO = 0; - public static final int PART_PHYS_NEWTON = 1; - public static final int PART_PHYS_KEYED = 2; - public static final int PART_PHYS_BOIDS = 3; - - // part->draw_as - public static final int PART_DRAW_NOT = 0; - public static final int PART_DRAW_DOT = 1; - public static final int PART_DRAW_CIRC = 2; - public static final int PART_DRAW_CROSS = 3; - public static final int PART_DRAW_AXIS = 4; - public static final int PART_DRAW_LINE = 5; - public static final int PART_DRAW_PATH = 6; - public static final int PART_DRAW_OB = 7; - public static final int PART_DRAW_GR = 8; - public static final int PART_DRAW_BB = 9; - - /** - * This constructor parses the given blender version and stores the result. Some functionalities may differ in - * different blender versions. - * @param blenderVersion - * the version read from the blend file - * @param blenderContext - * the blender context - */ - public ParticlesHelper(String blenderVersion, BlenderContext blenderContext) { - super(blenderVersion, blenderContext); - } - - @SuppressWarnings("unchecked") - public ParticleEmitter toParticleEmitter(Structure particleSystem) throws BlenderFileException { - ParticleEmitter result = null; - Pointer pParticleSettings = (Pointer) particleSystem.getFieldValue("part"); - if (pParticleSettings.isNotNull()) { - Structure particleSettings = pParticleSettings.fetchData().get(0); - - int totPart = ((Number) particleSettings.getFieldValue("totpart")).intValue(); - - // draw type will be stored temporarily in the name (it is used during modifier applying operation) - int drawAs = ((Number) particleSettings.getFieldValue("draw_as")).intValue(); - char nameSuffix;// P - point, L - line, N - None, B - Bilboard - switch (drawAs) { - case PART_DRAW_NOT: - nameSuffix = 'N'; - totPart = 0;// no need to generate particles in this case - break; - case PART_DRAW_BB: - nameSuffix = 'B'; - break; - case PART_DRAW_OB: - case PART_DRAW_GR: - nameSuffix = 'P'; - LOGGER.warning("Neither object nor group particles supported yet! Using point representation instead!");// TODO: support groups and aobjects - break; - case PART_DRAW_LINE: - nameSuffix = 'L'; - LOGGER.warning("Lines not yet supported! Using point representation instead!");// TODO: support lines - default:// all others are rendered as points in blender - nameSuffix = 'P'; - } - result = new ParticleEmitter(particleSettings.getName() + nameSuffix, Type.Triangle, totPart); - if (nameSuffix == 'N') { - return result;// no need to set anything else - } - - // setting the emitters shape (the shapes meshes will be set later during modifier applying operation) - int from = ((Number) particleSettings.getFieldValue("from")).intValue(); - switch (from) { - case PART_FROM_VERT: - result.setShape(new EmitterMeshVertexShape()); - break; - case PART_FROM_FACE: - result.setShape(new EmitterMeshFaceShape()); - break; - case PART_FROM_VOLUME: - result.setShape(new EmitterMeshConvexHullShape()); - break; - default: - LOGGER.warning("Default shape used! Unknown emitter shape value ('from' parameter: " + from + ')'); - } - - // reading acceleration - DynamicArray acc = (DynamicArray) particleSettings.getFieldValue("acc"); - result.setGravity(-acc.get(0).floatValue(), -acc.get(1).floatValue(), -acc.get(2).floatValue()); - - // setting the colors - result.setEndColor(new ColorRGBA(1f, 1f, 1f, 1f)); - result.setStartColor(new ColorRGBA(1f, 1f, 1f, 1f)); - - // reading size - float sizeFactor = nameSuffix == 'B' ? 1.0f : 0.3f; - float size = ((Number) particleSettings.getFieldValue("size")).floatValue() * sizeFactor; - result.setStartSize(size); - result.setEndSize(size); - - // reading lifetime - int fps = blenderContext.getBlenderKey().getFps(); - float lifetime = ((Number) particleSettings.getFieldValue("lifetime")).floatValue() / fps; - float randlife = ((Number) particleSettings.getFieldValue("randlife")).floatValue() / fps; - result.setLowLife(lifetime * (1.0f - randlife)); - result.setHighLife(lifetime); - - // preparing influencer - ParticleInfluencer influencer; - int phystype = ((Number) particleSettings.getFieldValue("phystype")).intValue(); - switch (phystype) { - case PART_PHYS_NEWTON: - influencer = new NewtonianParticleInfluencer(); - ((NewtonianParticleInfluencer) influencer).setNormalVelocity(((Number) particleSettings.getFieldValue("normfac")).floatValue()); - ((NewtonianParticleInfluencer) influencer).setVelocityVariation(((Number) particleSettings.getFieldValue("randfac")).floatValue()); - ((NewtonianParticleInfluencer) influencer).setSurfaceTangentFactor(((Number) particleSettings.getFieldValue("tanfac")).floatValue()); - ((NewtonianParticleInfluencer) influencer).setSurfaceTangentRotation(((Number) particleSettings.getFieldValue("tanphase")).floatValue()); - break; - case PART_PHYS_BOIDS: - case PART_PHYS_KEYED:// TODO: support other influencers - LOGGER.warning("Boids and Keyed particles physic not yet supported! Empty influencer used!"); - case PART_PHYS_NO: - default: - influencer = new EmptyParticleInfluencer(); - } - result.setParticleInfluencer(influencer); - } - return result; - } -} diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/ColorBand.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/ColorBand.java deleted file mode 100644 index d8298e867..000000000 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/ColorBand.java +++ /dev/null @@ -1,401 +0,0 @@ -/* - * 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.blender.textures; - -import com.jme3.math.ColorRGBA; -import com.jme3.math.FastMath; -import com.jme3.scene.plugins.blender.BlenderContext; -import com.jme3.scene.plugins.blender.file.BlenderFileException; -import com.jme3.scene.plugins.blender.file.DynamicArray; -import com.jme3.scene.plugins.blender.file.Pointer; -import com.jme3.scene.plugins.blender.file.Structure; - -import java.util.List; -import java.util.Map; -import java.util.TreeMap; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * A class constaining the colorband data. - * - * @author Marcin Roguski (Kaelthas) - */ -public class ColorBand { - private static final Logger LOGGER = Logger.getLogger(ColorBand.class.getName()); - - // interpolation types - public static final int IPO_LINEAR = 0; - public static final int IPO_EASE = 1; - public static final int IPO_BSPLINE = 2; - public static final int IPO_CARDINAL = 3; - public static final int IPO_CONSTANT = 4; - - private int cursorsAmount, ipoType; - /** The default amount of possible cursor positions. */ - private int resultSize = 1001; - private ColorBandData[] data; - - /** - * A constructor used to instantiate color band by hand instead of reading it from the blend file. - * @param ipoType - * the interpolation type - * @param colors - * the colorband colors - * @param positions - * the positions for colors' cursors - * @param resultSize - * the size of the result table - */ - public ColorBand(int ipoType, List colors, List positions, int resultSize) { - if (colors == null || colors.size() < 1) { - throw new IllegalArgumentException("The amount of colorband's colors must be at least 1."); - } - if (ipoType < IPO_LINEAR || ipoType > IPO_CONSTANT) { - throw new IllegalArgumentException("Unknown colorband interpolation type: " + ipoType); - } - if (positions == null || positions.size() != colors.size()) { - throw new IllegalArgumentException("The size of positions and colors list should be equal!"); - } - for (Integer position : positions) { - if (position.intValue() < 0 || position.intValue() >= resultSize) { - throw new IllegalArgumentException("Invalid position value: " + position + "! Should be from range: [0, " + resultSize + "]!"); - } - } - - cursorsAmount = colors.size(); - this.ipoType = ipoType; - this.resultSize = resultSize; - data = new ColorBandData[cursorsAmount]; - for (int i = 0; i < cursorsAmount; ++i) { - data[i] = new ColorBandData(colors.get(i), positions.get(i)); - } - } - - /** - * Constructor. Loads the data from the given structure. - * @param tex - * @param blenderContext - */ - @SuppressWarnings("unchecked") - public ColorBand(Structure tex, BlenderContext blenderContext) { - int flag = ((Number) tex.getFieldValue("flag")).intValue(); - if ((flag & GeneratedTexture.TEX_COLORBAND) != 0) { - Pointer pColorband = (Pointer) tex.getFieldValue("coba"); - try { - Structure colorbandStructure = pColorband.fetchData().get(0); - cursorsAmount = ((Number) colorbandStructure.getFieldValue("tot")).intValue(); - ipoType = ((Number) colorbandStructure.getFieldValue("ipotype")).intValue(); - data = new ColorBandData[cursorsAmount]; - DynamicArray data = (DynamicArray) colorbandStructure.getFieldValue("data"); - for (int i = 0; i < cursorsAmount; ++i) { - this.data[i] = new ColorBandData(data.get(i)); - } - } catch (BlenderFileException e) { - LOGGER.log(Level.WARNING, "Cannot fetch the colorband structure. The reason: {0}", e.getLocalizedMessage()); - } - } - } - - /** - * This method determines if the colorband has any transparencies or is not - * transparent at all. - * - * @return true if the colorband has transparencies and false - * otherwise - */ - public boolean hasTransparencies() { - if (data != null) { - for (ColorBandData colorBandData : data) { - if (colorBandData.a < 1.0f) { - return true; - } - } - } - return false; - } - - /** - * This method computes the values of the colorband. - * - * @return an array of 1001 elements and each element is float[4] object - * containing rgba values - */ - public float[][] computeValues() { - float[][] result = null; - if (data != null) { - result = new float[resultSize][4];// resultSize - amount of possible cursor positions; 4 = [r, g, b, a] - if (data.length == 1) {// special case; use only one color for all types of colorband interpolation - for (int i = 0; i < result.length; ++i) { - result[i][0] = data[0].r; - result[i][1] = data[0].g; - result[i][2] = data[0].b; - result[i][3] = data[0].a; - } - } else { - int currentCursor = 0; - ColorBandData currentData = data[0]; - ColorBandData nextData = data[0]; - switch (ipoType) { - case ColorBand.IPO_LINEAR: - float rDiff = 0, - gDiff = 0, - bDiff = 0, - aDiff = 0, - posDiff; - for (int i = 0; i < result.length; ++i) { - posDiff = i - currentData.pos; - result[i][0] = currentData.r + rDiff * posDiff; - result[i][1] = currentData.g + gDiff * posDiff; - result[i][2] = currentData.b + bDiff * posDiff; - result[i][3] = currentData.a + aDiff * posDiff; - if (nextData.pos == i) { - currentData = data[currentCursor++]; - if (currentCursor < data.length) { - nextData = data[currentCursor]; - // calculate differences - int d = nextData.pos - currentData.pos; - rDiff = (nextData.r - currentData.r) / d; - gDiff = (nextData.g - currentData.g) / d; - bDiff = (nextData.b - currentData.b) / d; - aDiff = (nextData.a - currentData.a) / d; - } else { - rDiff = gDiff = bDiff = aDiff = 0; - } - } - } - break; - case ColorBand.IPO_BSPLINE: - case ColorBand.IPO_CARDINAL: - Map cbDataMap = new TreeMap(); - for (int i = 0; i < data.length; ++i) { - cbDataMap.put(Integer.valueOf(i), data[i]); - } - - if (data[0].pos == 0) { - cbDataMap.put(Integer.valueOf(-1), data[0]); - } else { - ColorBandData cbData = new ColorBandData(data[0]); - cbData.pos = 0; - cbDataMap.put(Integer.valueOf(-1), cbData); - cbDataMap.put(Integer.valueOf(-2), cbData); - } - - if (data[data.length - 1].pos == 1000) { - cbDataMap.put(Integer.valueOf(data.length), data[data.length - 1]); - } else { - ColorBandData cbData = new ColorBandData(data[data.length - 1]); - cbData.pos = 1000; - cbDataMap.put(Integer.valueOf(data.length), cbData); - cbDataMap.put(Integer.valueOf(data.length + 1), cbData); - } - - float[] ipoFactors = new float[4]; - float f; - - ColorBandData data0 = this.getColorbandData(currentCursor - 2, cbDataMap); - ColorBandData data1 = this.getColorbandData(currentCursor - 1, cbDataMap); - ColorBandData data2 = this.getColorbandData(currentCursor, cbDataMap); - ColorBandData data3 = this.getColorbandData(currentCursor + 1, cbDataMap); - - for (int i = 0; i < result.length; ++i) { - if (data2.pos != data1.pos) { - f = (i - data2.pos) / (float) (data1.pos - data2.pos); - f = FastMath.clamp(f, 0.0f, 1.0f); - } else { - f = 0.0f; - } - this.getIpoData(f, ipoFactors); - result[i][0] = ipoFactors[3] * data0.r + ipoFactors[2] * data1.r + ipoFactors[1] * data2.r + ipoFactors[0] * data3.r; - result[i][1] = ipoFactors[3] * data0.g + ipoFactors[2] * data1.g + ipoFactors[1] * data2.g + ipoFactors[0] * data3.g; - result[i][2] = ipoFactors[3] * data0.b + ipoFactors[2] * data1.b + ipoFactors[1] * data2.b + ipoFactors[0] * data3.b; - result[i][3] = ipoFactors[3] * data0.a + ipoFactors[2] * data1.a + ipoFactors[1] * data2.a + ipoFactors[0] * data3.a; - result[i][0] = FastMath.clamp(result[i][0], 0.0f, 1.0f); - result[i][1] = FastMath.clamp(result[i][1], 0.0f, 1.0f); - result[i][2] = FastMath.clamp(result[i][2], 0.0f, 1.0f); - result[i][3] = FastMath.clamp(result[i][3], 0.0f, 1.0f); - - if (nextData.pos == i) { - ++currentCursor; - data0 = cbDataMap.get(currentCursor - 2); - data1 = cbDataMap.get(currentCursor - 1); - data2 = cbDataMap.get(currentCursor); - data3 = cbDataMap.get(currentCursor + 1); - } - } - break; - case ColorBand.IPO_EASE: - float d, - a, - b, - d2; - for (int i = 0; i < result.length; ++i) { - if (nextData.pos != currentData.pos) { - d = (i - currentData.pos) / (float) (nextData.pos - currentData.pos); - d2 = d * d; - a = 3.0f * d2 - 2.0f * d * d2; - b = 1.0f - a; - } else { - d = a = 0.0f; - b = 1.0f; - } - - result[i][0] = b * currentData.r + a * nextData.r; - result[i][1] = b * currentData.g + a * nextData.g; - result[i][2] = b * currentData.b + a * nextData.b; - result[i][3] = b * currentData.a + a * nextData.a; - if (nextData.pos == i) { - currentData = data[currentCursor++]; - if (currentCursor < data.length) { - nextData = data[currentCursor]; - } - } - } - break; - case ColorBand.IPO_CONSTANT: - for (int i = 0; i < result.length; ++i) { - result[i][0] = currentData.r; - result[i][1] = currentData.g; - result[i][2] = currentData.b; - result[i][3] = currentData.a; - if (nextData.pos == i) { - currentData = data[currentCursor++]; - if (currentCursor < data.length) { - nextData = data[currentCursor]; - } - } - } - break; - default: - throw new IllegalStateException("Unknown interpolation type: " + ipoType); - } - } - } - return result; - } - - private ColorBandData getColorbandData(int index, Map cbDataMap) { - ColorBandData result = cbDataMap.get(index); - if (result == null) { - result = new ColorBandData(); - } - return result; - } - - /** - * This method returns the data for either B-spline of Cardinal - * interpolation. - * - * @param d - * distance factor for the current intensity - * @param ipoFactors - * table to store the results (size of the table must be at least - * 4) - */ - private void getIpoData(float d, float[] ipoFactors) { - float d2 = d * d; - float d3 = d2 * d; - if (ipoType == ColorBand.IPO_BSPLINE) { - ipoFactors[0] = -0.71f * d3 + 1.42f * d2 - 0.71f * d; - ipoFactors[1] = 1.29f * d3 - 2.29f * d2 + 1.0f; - ipoFactors[2] = -1.29f * d3 + 1.58f * d2 + 0.71f * d; - ipoFactors[3] = 0.71f * d3 - 0.71f * d2; - } else if (ipoType == ColorBand.IPO_CARDINAL) { - ipoFactors[0] = -0.16666666f * d3 + 0.5f * d2 - 0.5f * d + 0.16666666f; - ipoFactors[1] = 0.5f * d3 - d2 + 0.6666666f; - ipoFactors[2] = -0.5f * d3 + 0.5f * d2 + 0.5f * d + 0.16666666f; - ipoFactors[3] = 0.16666666f * d3; - } else { - throw new IllegalStateException("Cannot get interpolation data for other colorband types than B-spline and Cardinal!"); - } - } - - /** - * Class to store the single colorband cursor data. - * - * @author Marcin Roguski (Kaelthas) - */ - private static class ColorBandData { - public final float r, g, b, a; - public int pos; - - public ColorBandData() { - r = g = b = 0; - a = 1; - } - - /** - * Constructor that stores the color and position of the cursor. - * @param color - * the cursor's color - * @param pos - * the cursor's position - */ - public ColorBandData(ColorRGBA color, int pos) { - r = color.r; - g = color.g; - b = color.b; - a = color.a; - this.pos = pos; - } - - /** - * Copy constructor. - */ - private ColorBandData(ColorBandData data) { - r = data.r; - g = data.g; - b = data.b; - a = data.a; - pos = data.pos; - } - - /** - * Constructor. Loads the data from the given structure. - * - * @param cbdataStructure - * the structure containing the CBData object - */ - public ColorBandData(Structure cbdataStructure) { - r = ((Number) cbdataStructure.getFieldValue("r")).floatValue(); - g = ((Number) cbdataStructure.getFieldValue("g")).floatValue(); - b = ((Number) cbdataStructure.getFieldValue("b")).floatValue(); - a = ((Number) cbdataStructure.getFieldValue("a")).floatValue(); - pos = (int) (((Number) cbdataStructure.getFieldValue("pos")).floatValue() * 1000.0f); - } - - @Override - public String toString() { - return "P: " + pos + " [" + r + ", " + g + ", " + b + ", " + a + "]"; - } - } -} diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/CombinedTexture.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/CombinedTexture.java deleted file mode 100644 index 6c40542e2..000000000 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/CombinedTexture.java +++ /dev/null @@ -1,543 +0,0 @@ -package com.jme3.scene.plugins.blender.textures; - -import java.awt.Graphics2D; -import java.awt.RenderingHints; -import java.awt.image.BufferedImage; -import java.nio.ByteBuffer; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.logging.Level; -import java.util.logging.Logger; - -import jme3tools.converters.ImageToAwt; - -import com.jme3.math.ColorRGBA; -import com.jme3.math.Vector2f; -import com.jme3.scene.Geometry; -import com.jme3.scene.Mesh; -import com.jme3.scene.plugins.blender.BlenderContext; -import com.jme3.scene.plugins.blender.BlenderContext.LoadedDataType; -import com.jme3.scene.plugins.blender.file.Structure; -import com.jme3.scene.plugins.blender.materials.MaterialContext; -import com.jme3.scene.plugins.blender.meshes.TemporalMesh; -import com.jme3.scene.plugins.blender.textures.TriangulatedTexture.TriangleTextureElement; -import com.jme3.scene.plugins.blender.textures.UVCoordinatesGenerator.UVCoordinatesType; -import com.jme3.scene.plugins.blender.textures.UVProjectionGenerator.UVProjectionType; -import com.jme3.scene.plugins.blender.textures.blending.TextureBlender; -import com.jme3.scene.plugins.blender.textures.blending.TextureBlenderFactory; -import com.jme3.scene.plugins.blender.textures.io.PixelIOFactory; -import com.jme3.scene.plugins.blender.textures.io.PixelInputOutput; -import com.jme3.texture.Image; -import com.jme3.texture.Texture; -import com.jme3.texture.Texture.MagFilter; -import com.jme3.texture.Texture.MinFilter; -import com.jme3.texture.Texture.WrapMode; -import com.jme3.texture.Texture2D; -import com.jme3.texture.TextureCubeMap; -import com.jme3.texture.image.ColorSpace; -import com.jme3.util.BufferUtils; - -/** - * This class represents a texture that is defined for the material. It can be - * made of several textures (both 2D and 3D) that are merged together and - * returned as a single texture. - * - * @author Marcin Roguski (Kaelthas) - */ -public class CombinedTexture { - private static final Logger LOGGER = Logger.getLogger(CombinedTexture.class.getName()); - - /** The mapping type of the texture. Defined bu MaterialContext.MTEX_COL, MTEX_NOR etc. */ - private final int mappingType; - /** - * If set to true then if a texture without alpha is added then all textures below are discarded because - * the new one will cover them anyway. If set to false then all textures are stored. - */ - private boolean discardCoveredTextures; - /** The data for each of the textures. */ - private List textureDatas = new ArrayList(); - /** The result texture. */ - private Texture resultTexture; - /** The UV values for the result texture. */ - private List resultUVS; - - /** - * Constructor. Stores the texture mapping type (ie. color map, normal map). - * - * @param mappingType - * texture mapping type - * @param discardCoveredTextures - * if set to true then if a texture without alpha is added then all textures below are discarded because - * the new one will cover them anyway, if set to false then all textures are stored - */ - public CombinedTexture(int mappingType, boolean discardCoveredTextures) { - this.mappingType = mappingType; - this.discardCoveredTextures = discardCoveredTextures; - } - - /** - * This method adds a texture data to the resulting texture. - * - * @param texture - * the source texture - * @param textureBlender - * the texture blender (to mix the texture with its material - * color) - * @param uvCoordinatesType - * the type of UV coordinates - * @param projectionType - * the type of UV coordinates projection (for flat textures) - * @param textureStructure - * the texture sructure - * @param uvCoordinatesName - * the name of the used user's UV coordinates for this texture - * @param blenderContext - * the blender context - */ - public void add(Texture texture, TextureBlender textureBlender, int uvCoordinatesType, int projectionType, Structure textureStructure, String uvCoordinatesName, BlenderContext blenderContext) { - if (!(texture instanceof GeneratedTexture) && !(texture instanceof Texture2D)) { - throw new IllegalArgumentException("Unsupported texture type: " + (texture == null ? "null" : texture.getClass())); - } - if (!(texture instanceof GeneratedTexture) || blenderContext.getBlenderKey().isLoadGeneratedTextures()) { - if (UVCoordinatesGenerator.isTextureCoordinateTypeSupported(UVCoordinatesType.valueOf(uvCoordinatesType))) { - TextureData textureData = new TextureData(); - textureData.texture = texture; - textureData.textureBlender = textureBlender; - textureData.uvCoordinatesType = UVCoordinatesType.valueOf(uvCoordinatesType); - textureData.projectionType = UVProjectionType.valueOf(projectionType); - textureData.textureStructure = textureStructure; - textureData.uvCoordinatesName = uvCoordinatesName; - - if (discardCoveredTextures && textureDatas.size() > 0 && this.isWithoutAlpha(textureData, blenderContext)) { - textureDatas.clear();// clear previous textures, they will be covered anyway - } - textureDatas.add(textureData); - } else { - LOGGER.warning("The texture coordinates type is not supported: " + UVCoordinatesType.valueOf(uvCoordinatesType) + ". The texture '" + textureStructure.getName() + "'."); - } - } - } - - /** - * This method flattens the texture and creates a single result of Texture2D - * type. - * - * @param geometry - * the geometry the texture is created for - * @param geometriesOMA - * the old memory address of the geometries list that the given - * geometry belongs to (needed for bounding box creation) - * @param userDefinedUVCoordinates - * the UV's defined by user (null or zero length table if none - * were defined) - * @param blenderContext - * the blender context - * @return the name of the user UV coordinates used (null if the UV's were - * generated) - */ - public String flatten(Geometry geometry, Long geometriesOMA, Map> userDefinedUVCoordinates, BlenderContext blenderContext) { - Mesh mesh = geometry.getMesh(); - Texture previousTexture = null; - UVCoordinatesType masterUVCoordinatesType = null; - String masterUserUVSetName = null; - for (TextureData textureData : textureDatas) { - // decompress compressed textures (all will be merged into one texture anyway) - if (textureDatas.size() > 1 && textureData.texture.getImage().getFormat().isCompressed()) { - textureData.texture.setImage(ImageUtils.decompress(textureData.texture.getImage())); - textureData.textureBlender = TextureBlenderFactory.alterTextureType(textureData.texture.getImage().getFormat(), textureData.textureBlender); - } - - if (previousTexture == null) {// the first texture will lead the others to its shape - if (textureData.texture instanceof GeneratedTexture) { - resultTexture = ((GeneratedTexture) textureData.texture).triangulate(mesh, geometriesOMA, textureData.uvCoordinatesType, blenderContext); - } else if (textureData.texture instanceof Texture2D) { - resultTexture = textureData.texture; - - if (textureData.uvCoordinatesType == UVCoordinatesType.TEXCO_UV && userDefinedUVCoordinates != null && userDefinedUVCoordinates.size() > 0) { - if (textureData.uvCoordinatesName == null) { - resultUVS = userDefinedUVCoordinates.values().iterator().next();// get the first UV available - } else { - resultUVS = userDefinedUVCoordinates.get(textureData.uvCoordinatesName); - } - if(resultUVS == null && LOGGER.isLoggable(Level.WARNING)) { - LOGGER.warning("The texture " + textureData.texture.getName() + " has assigned non existing UV coordinates group: " + textureData.uvCoordinatesName + "."); - } - masterUserUVSetName = textureData.uvCoordinatesName; - } else { - TemporalMesh temporalMesh = (TemporalMesh) blenderContext.getLoadedFeature(geometriesOMA, LoadedDataType.TEMPORAL_MESH); - resultUVS = UVCoordinatesGenerator.generateUVCoordinatesFor2DTexture(mesh, textureData.uvCoordinatesType, textureData.projectionType, temporalMesh); - } - } - this.blend(resultTexture, textureData.textureBlender, blenderContext); - - previousTexture = resultTexture; - masterUVCoordinatesType = textureData.uvCoordinatesType; - } else { - if (textureData.texture instanceof GeneratedTexture) { - if (!(resultTexture instanceof TriangulatedTexture)) { - resultTexture = new TriangulatedTexture((Texture2D) resultTexture, resultUVS, blenderContext); - resultUVS = null; - previousTexture = resultTexture; - } - - TriangulatedTexture triangulatedTexture = ((GeneratedTexture) textureData.texture).triangulate(mesh, geometriesOMA, textureData.uvCoordinatesType, blenderContext); - triangulatedTexture.castToUVS((TriangulatedTexture) resultTexture, blenderContext); - triangulatedTexture.blend(textureData.textureBlender, (TriangulatedTexture) resultTexture, blenderContext); - resultTexture = previousTexture = triangulatedTexture; - } else if (textureData.texture instanceof Texture2D) { - if (this.isUVTypesMatch(masterUVCoordinatesType, masterUserUVSetName, textureData.uvCoordinatesType, textureData.uvCoordinatesName) && resultTexture instanceof Texture2D) { - this.scale((Texture2D) textureData.texture, resultTexture.getImage().getWidth(), resultTexture.getImage().getHeight()); - ImageUtils.merge(resultTexture.getImage(), textureData.texture.getImage()); - previousTexture = resultTexture; - } else { - if (!(resultTexture instanceof TriangulatedTexture)) { - resultTexture = new TriangulatedTexture((Texture2D) resultTexture, resultUVS, blenderContext); - resultUVS = null; - } - // first triangulate the current texture - List textureUVS = null; - if (textureData.uvCoordinatesType == UVCoordinatesType.TEXCO_UV && userDefinedUVCoordinates != null && userDefinedUVCoordinates.size() > 0) { - if (textureData.uvCoordinatesName == null) { - textureUVS = userDefinedUVCoordinates.values().iterator().next();// get the first UV available - } else { - textureUVS = userDefinedUVCoordinates.get(textureData.uvCoordinatesName); - } - } else { - TemporalMesh geometries = (TemporalMesh) blenderContext.getLoadedFeature(geometriesOMA, LoadedDataType.TEMPORAL_MESH); - textureUVS = UVCoordinatesGenerator.generateUVCoordinatesFor2DTexture(mesh, textureData.uvCoordinatesType, textureData.projectionType, geometries); - } - TriangulatedTexture triangulatedTexture = new TriangulatedTexture((Texture2D) textureData.texture, textureUVS, blenderContext); - // then move the texture to different UV's - triangulatedTexture.castToUVS((TriangulatedTexture) resultTexture, blenderContext); - // merge triangulated textures - for (int i = 0; i < ((TriangulatedTexture) resultTexture).getFaceTextureCount(); ++i) { - ImageUtils.merge(((TriangulatedTexture) resultTexture).getFaceTextureElement(i).image, triangulatedTexture.getFaceTextureElement(i).image); - } - } - } - } - } - - if (resultTexture instanceof TriangulatedTexture) { - if (mappingType == MaterialContext.MTEX_NOR) { - for (int i = 0; i < ((TriangulatedTexture) resultTexture).getFaceTextureCount(); ++i) { - TriangleTextureElement triangleTextureElement = ((TriangulatedTexture) resultTexture).getFaceTextureElement(i); - triangleTextureElement.image = ImageUtils.convertToNormalMapTexture(triangleTextureElement.image, 1);// TODO: get proper strength factor - } - } - resultUVS = ((TriangulatedTexture) resultTexture).getResultUVS(); - resultTexture = ((TriangulatedTexture) resultTexture).getResultTexture(); - masterUserUVSetName = null; - } - - // setting additional data - resultTexture.setWrap(WrapMode.Repeat); - // the filters are required if generated textures are used because - // otherwise ugly lines appear between the mesh faces - resultTexture.setMagFilter(MagFilter.Nearest); - resultTexture.setMinFilter(MinFilter.NearestNoMipMaps); - - return masterUserUVSetName; - } - - /** - * Generates a texture that will be used by the sky spatial. - * The result texture has 6 layers. Every image in each layer has equal size and its shape is a square. - * The size of each image is the maximum size (width or height) of the textures given. - * The default sky generated texture size is used (this value is set in the BlenderKey) if no picture textures - * are present or their sizes is lower than the generated texture size. - * The textures of lower sizes are properly scaled. - * All the textures are mixed into one and put as layers in the result texture. - * - * @param horizontalColor - * the horizon color - * @param zenithColor - * the zenith color - * @param blenderContext - * the blender context - * @return texture for the sky - */ - public TextureCubeMap generateSkyTexture(ColorRGBA horizontalColor, ColorRGBA zenithColor, BlenderContext blenderContext) { - LOGGER.log(Level.FINE, "Preparing sky texture from {0} applied textures.", textureDatas.size()); - - LOGGER.fine("Computing the texture size."); - int size = -1; - for (TextureData textureData : textureDatas) { - if (textureData.texture instanceof Texture2D) { - size = Math.max(textureData.texture.getImage().getWidth(), size); - size = Math.max(textureData.texture.getImage().getHeight(), size); - } - } - if (size < 0) { - size = blenderContext.getBlenderKey().getSkyGeneratedTextureSize(); - } - LOGGER.log(Level.FINE, "The sky texture size will be: {0}x{0}.", size); - - TextureCubeMap result = null; - for (TextureData textureData : textureDatas) { - TextureCubeMap texture = null; - if (textureData.texture instanceof GeneratedTexture) { - texture = ((GeneratedTexture) textureData.texture).generateSkyTexture(size, horizontalColor, zenithColor, blenderContext); - } else { - // first create a grayscale version of the image - Image image = textureData.texture.getImage(); - if (image.getWidth() != image.getHeight() || image.getWidth() != size) { - image = ImageUtils.resizeTo(image, size, size); - } - Image grayscaleImage = ImageUtils.convertToGrayscaleTexture(image); - - // add the sky colors to the image - PixelInputOutput sourcePixelIO = PixelIOFactory.getPixelIO(grayscaleImage.getFormat()); - PixelInputOutput targetPixelIO = PixelIOFactory.getPixelIO(image.getFormat()); - TexturePixel texturePixel = new TexturePixel(); - for (int x = 0; x < image.getWidth(); ++x) { - for (int y = 0; y < image.getHeight(); ++y) { - sourcePixelIO.read(grayscaleImage, 0, texturePixel, x, y); - texturePixel.intensity = texturePixel.red;// no matter which factor we use here, in grayscale they are all equal - ImageUtils.color(texturePixel, horizontalColor, zenithColor); - targetPixelIO.write(image, 0, texturePixel, x, y); - } - } - - // create the cubemap texture from the coloured image - ByteBuffer sourceData = image.getData(0); - ArrayList data = new ArrayList(6); - for (int i = 0; i < 6; ++i) { - data.add(BufferUtils.clone(sourceData)); - } - texture = new TextureCubeMap(new Image(image.getFormat(), image.getWidth(), image.getHeight(), 6, data, ColorSpace.Linear)); - } - - if (result == null) { - result = texture; - } else { - ImageUtils.mix(result.getImage(), texture.getImage()); - } - } - return result; - } - - /** - * The method checks if the texture UV coordinates match. - * It the types are equal and different then UVCoordinatesType.TEXCO_UV then we consider them a match. - * If they are both UVCoordinatesType.TEXCO_UV then they match only when their UV sets names are equal. - * In other cases they are considered NOT a match. - * @param type1 - * the UV coord type - * @param uvSetName1 - * the user's UV coords set name (considered only for UVCoordinatesType.TEXCO_UV) - * @param type2 - * the UV coord type - * @param uvSetName2 - * the user's UV coords set name (considered only for UVCoordinatesType.TEXCO_UV) - * @return true if the types match and false otherwise - */ - private boolean isUVTypesMatch(UVCoordinatesType type1, String uvSetName1, UVCoordinatesType type2, String uvSetName2) { - if (type1 == type2) { - if (type1 == UVCoordinatesType.TEXCO_UV) { - if (uvSetName1 != null && uvSetName2 != null && uvSetName1.equals(uvSetName2)) { - return true; - } - } else { - return true; - } - } - return false; - } - - /** - * This method blends the texture. - * - * @param texture - * the texture to be blended - * @param textureBlender - * blending definition for the texture - * @param blenderContext - * the blender context - */ - private void blend(Texture texture, TextureBlender textureBlender, BlenderContext blenderContext) { - if (texture instanceof TriangulatedTexture) { - ((TriangulatedTexture) texture).blend(textureBlender, null, blenderContext); - } else if (texture instanceof Texture2D) { - Image blendedImage = textureBlender.blend(texture.getImage(), null, blenderContext); - texture.setImage(blendedImage); - } else { - throw new IllegalArgumentException("Invalid type for texture to blend!"); - } - } - - /** - * @return the result texture - */ - public Texture getResultTexture() { - return resultTexture; - } - - /** - * @return the result UV coordinates - */ - public List getResultUVS() { - return resultUVS; - } - - /** - * @return the amount of added textures - */ - public int getTexturesCount() { - return textureDatas.size(); - } - - /** - * @return the texture's mapping type - */ - public int getMappingType() { - return mappingType; - } - - /** - * @return true if the texture has at least one generated texture component and false otherwise - */ - public boolean hasGeneratedTextures() { - if (textureDatas != null) { - for (TextureData textureData : textureDatas) { - if (textureData.texture instanceof GeneratedTexture) { - return true; - } - } - } - return false; - } - - /** - * This method determines if the given texture has no alpha channel. - * - * @param texture - * the texture to check for alpha channel - * @return true if the texture has no alpha channel and false - * otherwise - */ - private boolean isWithoutAlpha(TextureData textureData, BlenderContext blenderContext) { - ColorBand colorBand = new ColorBand(textureData.textureStructure, blenderContext); - if (!colorBand.hasTransparencies()) { - int type = ((Number) textureData.textureStructure.getFieldValue("type")).intValue(); - if (type == TextureHelper.TEX_MAGIC) { - return true; - } - if (type == TextureHelper.TEX_VORONOI) { - int voronoiColorType = ((Number) textureData.textureStructure.getFieldValue("vn_coltype")).intValue(); - return voronoiColorType != 0;// voronoiColorType == 0: - // intensity, voronoiColorType - // != 0: col1, col2 or col3 - } - if (type == TextureHelper.TEX_CLOUDS) { - int sType = ((Number) textureData.textureStructure.getFieldValue("stype")).intValue(); - return sType == 1;// sType==0: without colors, sType==1: with - // colors - } - - // checking the flat textures for alpha values presence - if (type == TextureHelper.TEX_IMAGE) { - Image image = textureData.texture.getImage(); - switch (image.getFormat()) { - case BGR8: - case DXT1: - case Luminance16F: - case Luminance32F: - case Luminance8: - case RGB111110F: - case RGB16F: - case RGB32F: - case RGB565: - case RGB8: - return true;// these types have no alpha by definition - case ABGR8: - case DXT1A: - case DXT3: - case DXT5: - case Luminance16FAlpha16F: - case Luminance8Alpha8: - case RGBA16F: - case RGBA32F: - case RGBA8: - case ARGB8: - case BGRA8: - case RGB5A1:// with these types it is better to make sure if the texture is or is not transparent - PixelInputOutput pixelInputOutput = PixelIOFactory.getPixelIO(image.getFormat()); - TexturePixel pixel = new TexturePixel(); - int depth = image.getDepth() == 0 ? 1 : image.getDepth(); - for (int layerIndex = 0; layerIndex < depth; ++layerIndex) { - for (int x = 0; x < image.getWidth(); ++x) { - for (int y = 0; y < image.getHeight(); ++y) { - pixelInputOutput.read(image, layerIndex, pixel, x, y); - if (pixel.alpha < 1.0f) { - return false; - } - } - } - } - return true; - default: - throw new IllegalStateException("Unknown image format: " + image.getFormat()); - } - } - } - return false; - } - - /** - * This method scales the given texture to the given size. - * - * @param texture - * the texture to be scaled - * @param width - * new width of the texture - * @param height - * new height of the texture - */ - private void scale(Texture2D texture, int width, int height) { - // first determine if scaling is required - boolean scaleRequired = texture.getImage().getWidth() != width || texture.getImage().getHeight() != height; - - if (scaleRequired) { - Image image = texture.getImage(); - BufferedImage sourceImage = ImageToAwt.convert(image, false, true, 0); - - int sourceWidth = sourceImage.getWidth(); - int sourceHeight = sourceImage.getHeight(); - - BufferedImage targetImage = new BufferedImage(width, height, sourceImage.getType()); - - Graphics2D g = targetImage.createGraphics(); - g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); - g.drawImage(sourceImage, 0, 0, width, height, 0, 0, sourceWidth, sourceHeight, null); - g.dispose(); - - Image output = new ImageLoader().load(targetImage, false); - image.setWidth(width); - image.setHeight(height); - image.setData(output.getData(0)); - image.setFormat(output.getFormat()); - } - } - - /** - * A simple class to aggregate the texture data (improves code quality). - * - * @author Marcin Roguski (Kaelthas) - */ - private static class TextureData { - /** The texture. */ - public Texture texture; - /** The texture blender (to mix the texture with its material color). */ - public TextureBlender textureBlender; - /** The type of UV coordinates. */ - public UVCoordinatesType uvCoordinatesType; - /** The type of UV coordinates projection (for flat textures). */ - public UVProjectionType projectionType; - /** The texture sructure. */ - public Structure textureStructure; - /** The name of the user's UV coordinates that are used for this texture. */ - public String uvCoordinatesName; - } -} diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/DDSTexelData.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/DDSTexelData.java deleted file mode 100644 index 5537f1e0b..000000000 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/DDSTexelData.java +++ /dev/null @@ -1,157 +0,0 @@ -package com.jme3.scene.plugins.blender.textures; - -import com.jme3.math.FastMath; -import com.jme3.texture.Image.Format; - -/** - * The data that helps in bytes calculations for the result image. - * - * @author Marcin Roguski (Kaelthas) - */ -/* package */class DDSTexelData { - /** The colors of the texes. */ - private TexturePixel[][] colors; - /** The indexes of the texels. */ - private long[] indexes; - /** The alphas of the texels (might be null). */ - private float[][] alphas; - /** The indexels of texels alpha values (might be null). */ - private long[] alphaIndexes; - /** The counter of texel x column. */ - private int xCounter; - /** The counter of texel y row. */ - private int yCounter; - /** The width of the image in pixels. */ - private int widthInPixels; - /** The height of the image in pixels. */ - private int heightInPixels; - /** The total texel count. */ - private int xTexelCount; - - /** - * Constructor. Allocates memory for data structures. - * - * @param compressedSize - * the size of compressed image (or its mipmap) - * @param widthToHeightRatio - * width/height ratio for the image - * @param format - * the format of the image - */ - public DDSTexelData(int compressedSize, float widthToHeightRatio, Format format) { - int texelsCount = compressedSize * 8 / format.getBitsPerPixel() / 16; - this.colors = new TexturePixel[texelsCount][]; - this.indexes = new long[texelsCount]; - this.widthInPixels = (int) (0.5f * (float) Math.sqrt(this.getSizeInBytes() / widthToHeightRatio)); - this.heightInPixels = (int) (this.widthInPixels / widthToHeightRatio); - this.xTexelCount = widthInPixels >> 2; - this.yCounter = (heightInPixels >> 2) - 1;// xCounter is 0 for now - if (format == Format.DXT3 || format == Format.DXT5) { - this.alphas = new float[texelsCount][]; - this.alphaIndexes = new long[texelsCount]; - } - } - - /** - * This method adds a color and indexes for a texel. - * - * @param colors - * the colors of the texel - * @param indexes - * the indexes of the texel - */ - public void add(TexturePixel[] colors, int indexes) { - this.add(colors, indexes, null, 0); - } - - /** - * This method adds a color, color indexes and alha values (with their - * indexes) for a texel. - * - * @param colors - * the colors of the texel - * @param indexes - * the indexes of the texel - * @param alphas - * the alpha values - * @param alphaIndexes - * the indexes of the given alpha values - */ - public void add(TexturePixel[] colors, int indexes, float[] alphas, long alphaIndexes) { - int index = yCounter * xTexelCount + xCounter; - this.colors[index] = colors; - this.indexes[index] = indexes; - if (alphas != null) { - this.alphas[index] = alphas; - this.alphaIndexes[index] = alphaIndexes; - } - ++this.xCounter; - if (this.xCounter >= this.xTexelCount) { - this.xCounter = 0; - --this.yCounter; - } - } - - /** - * This method returns the values of the pixel located on the given - * coordinates on the result image. - * - * @param x - * the x coordinate of the pixel - * @param y - * the y coordinate of the pixel - * @param result - * the table where the result is stored - * @return true if the pixel was correctly read and false if - * the position was outside the image sizes - */ - public boolean getRGBA8(int x, int y, byte[] result) { - int xTexetlIndex = x % widthInPixels / 4; - int yTexelIndex = y % heightInPixels / 4; - - int texelIndex = yTexelIndex * xTexelCount + xTexetlIndex; - if (texelIndex < colors.length) { - TexturePixel[] colors = this.colors[texelIndex]; - - // coordinates of the pixel in the selected texel - x = x - 4 * xTexetlIndex;// pixels are arranged from left to right - y = 3 - y - 4 * yTexelIndex;// pixels are arranged from bottom to top (that is why '3 - ...' is at the start) - - int pixelIndexInTexel = (y * 4 + x) * (int) FastMath.log(colors.length, 2); - int alphaIndexInTexel = alphas != null ? (y * 4 + x) * (int) FastMath.log(alphas.length, 2) : 0; - - // getting the pixel - int indexMask = colors.length - 1; - int colorIndex = (int) (this.indexes[texelIndex] >> pixelIndexInTexel & indexMask); - float alpha = this.alphas != null ? this.alphas[texelIndex][(int) (this.alphaIndexes[texelIndex] >> alphaIndexInTexel & 0x07)] : colors[colorIndex].alpha; - result[0] = (byte) (colors[colorIndex].red * 255.0f); - result[1] = (byte) (colors[colorIndex].green * 255.0f); - result[2] = (byte) (colors[colorIndex].blue * 255.0f); - result[3] = (byte) (alpha * 255.0f); - return true; - } - return false; - } - - /** - * @return the size of the decompressed texel (in bytes) - */ - public int getSizeInBytes() { - // indexes.length == count of texels - return indexes.length * 16 * 4; - } - - /** - * @return image (mipmap) width - */ - public int getPixelWidth() { - return widthInPixels; - } - - /** - * @return image (mipmap) height - */ - public int getPixelHeight() { - return heightInPixels; - } -} diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/GeneratedTexture.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/GeneratedTexture.java deleted file mode 100644 index 2eb5430e5..000000000 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/GeneratedTexture.java +++ /dev/null @@ -1,282 +0,0 @@ -package com.jme3.scene.plugins.blender.textures; - -import java.util.Comparator; -import java.util.List; -import java.util.Set; -import java.util.TreeSet; - -import com.jme3.bounding.BoundingBox; -import com.jme3.math.ColorRGBA; -import com.jme3.math.FastMath; -import com.jme3.math.Vector3f; -import com.jme3.scene.Mesh; -import com.jme3.scene.plugins.blender.BlenderContext; -import com.jme3.scene.plugins.blender.BlenderContext.LoadedDataType; -import com.jme3.scene.plugins.blender.file.Structure; -import com.jme3.scene.plugins.blender.meshes.TemporalMesh; -import com.jme3.scene.plugins.blender.textures.TriangulatedTexture.TriangleTextureElement; -import com.jme3.scene.plugins.blender.textures.UVCoordinatesGenerator.UVCoordinatesType; -import com.jme3.scene.plugins.blender.textures.generating.TextureGenerator; -import com.jme3.scene.plugins.blender.textures.io.PixelIOFactory; -import com.jme3.scene.plugins.blender.textures.io.PixelInputOutput; -import com.jme3.texture.Image; -import com.jme3.texture.Image.Format; -import com.jme3.texture.Texture; -import com.jme3.texture.TextureCubeMap; -import com.jme3.util.TempVars; - -/** - * The generated texture loaded from blender file. The texture is not generated - * after being read. This class rather stores all required data and can compute - * a pixel in the required 3D space position. - * - * @author Marcin Roguski (Kaelthas) - */ -/* package */class GeneratedTexture extends Texture { - private static final int POSITIVE_X = 0; - private static final int NEGATIVE_X = 1; - private static final int POSITIVE_Y = 2; - private static final int NEGATIVE_Y = 3; - private static final int POSITIVE_Z = 4; - private static final int NEGATIVE_Z = 5; - - // flag values - public static final int TEX_COLORBAND = 1; - public static final int TEX_FLIPBLEND = 2; - public static final int TEX_NEGALPHA = 4; - public static final int TEX_CHECKER_ODD = 8; - public static final int TEX_CHECKER_EVEN = 16; - public static final int TEX_PRV_ALPHA = 32; - public static final int TEX_PRV_NOR = 64; - public static final int TEX_REPEAT_XMIR = 128; - public static final int TEX_REPEAT_YMIR = 256; - public static final int TEX_FLAG_MASK = TEX_COLORBAND | TEX_FLIPBLEND | TEX_NEGALPHA | TEX_CHECKER_ODD | TEX_CHECKER_EVEN | TEX_PRV_ALPHA | TEX_PRV_NOR | TEX_REPEAT_XMIR | TEX_REPEAT_YMIR; - - /** Material-texture link structure. */ - private final Structure mTex; - /** Texture generateo for the specified texture type. */ - private final TextureGenerator textureGenerator; - /** - * The generated texture cast functions. They are used to cas a given point on a plane to a specified shape in 3D space. - * The functions should be ordered as the ordinal of a BlenderKey.CastFunction enums. - */ - private final static CastFunction[] CAST_FUNCTIONS = new CastFunction[] { - /** - * The cube casting function (does nothing except scaling if needed because the given points are already on a cube). - */ - new CastFunction() { - @Override - public void cast(Vector3f pointToCast, float radius) { - //computed using the Thales' theorem - float length = 2 * pointToCast.subtractLocal(0.5f, 0.5f, 0.5f).length() * radius; - pointToCast.normalizeLocal().addLocal(0.5f, 0.5f, 0.5f).multLocal(length); - } - }, - - /** - * The sphere casting function. - */ - new CastFunction() { - /** - * The method casts a point on a plane to a sphere. - * The plane is one of the faces of a cube that has a edge of length 1 and center in (0.5 0.5, 0.5). This cube is a basic 3d area where generated texture - * is created. - * To cast a point on a cube face to a sphere that is inside the cube we perform several easy vector operations. - * 1. create a vector from the cube's center to the point - * 2. setting its length to 0.5 (the radius of the sphere) - * 3. adding the value of the cube's center to get a point on the sphere - * - * The result is stored in the given vector. - * - * @param pointToCast - * the point on a plane that will be cast to a sphere - * @param radius - * the radius of the sphere - */ - @Override - public void cast(Vector3f pointToCast, float radius) { - pointToCast.subtractLocal(0.5f, 0.5f, 0.5f).normalizeLocal().multLocal(radius).addLocal(0.5f, 0.5f, 0.5f); - } - } - }; - - /** - * Constructor. Reads the required data from the 'tex' structure. - * - * @param tex - * the texture structure - * @param mTex - * the material-texture link data structure - * @param textureGenerator - * the generator for the required texture type - * @param blenderContext - * the blender context - */ - public GeneratedTexture(Structure tex, Structure mTex, TextureGenerator textureGenerator, BlenderContext blenderContext) { - this.mTex = mTex; - this.textureGenerator = textureGenerator; - this.textureGenerator.readData(tex, blenderContext); - super.setImage(new GeneratedTextureImage(textureGenerator.getImageFormat())); - } - - /** - * This method computes the textyre color/intensity at the specified (u, v, - * s) position in 3D space. - * - * @param pixel - * the pixel where the result is stored - * @param u - * the U factor - * @param v - * the V factor - * @param s - * the S factor - */ - public void getPixel(TexturePixel pixel, float u, float v, float s) { - textureGenerator.getPixel(pixel, u, v, s); - } - - /** - * This method triangulates the texture. In the result we get a set of small - * flat textures for each face of the given mesh. This can be later merged - * into one flat texture. - * - * @param mesh - * the mesh we create the texture for - * @param geometriesOMA - * the old memory address of the geometries group that the given - * mesh belongs to (required for bounding box calculations) - * @param coordinatesType - * the types of UV coordinates - * @param blenderContext - * the blender context - * @return triangulated texture - */ - public TriangulatedTexture triangulate(Mesh mesh, Long geometriesOMA, UVCoordinatesType coordinatesType, BlenderContext blenderContext) { - TemporalMesh geometries = (TemporalMesh) blenderContext.getLoadedFeature(geometriesOMA, LoadedDataType.TEMPORAL_MESH); - - int[] coordinatesSwappingIndexes = new int[] { ((Number) mTex.getFieldValue("projx")).intValue(), ((Number) mTex.getFieldValue("projy")).intValue(), ((Number) mTex.getFieldValue("projz")).intValue() }; - List uvs = UVCoordinatesGenerator.generateUVCoordinatesFor3DTexture(mesh, coordinatesType, coordinatesSwappingIndexes, geometries); - Vector3f[] uvsArray = uvs.toArray(new Vector3f[uvs.size()]); - BoundingBox boundingBox = UVCoordinatesGenerator.getBoundingBox(geometries); - Set triangleTextureElements = new TreeSet(new Comparator() { - @Override - public int compare(TriangleTextureElement o1, TriangleTextureElement o2) { - return o1.faceIndex - o2.faceIndex; - } - }); - int[] indices = new int[3]; - for (int i = 0; i < mesh.getTriangleCount(); ++i) { - mesh.getTriangle(i, indices); - triangleTextureElements.add(new TriangleTextureElement(i, boundingBox, this, uvsArray, indices, blenderContext)); - } - return new TriangulatedTexture(triangleTextureElements, blenderContext); - } - - /** - * Creates a texture for the sky. The result texture has 6 layers. - * @param size - * the size of the texture (width and height are equal) - * @param horizontalColor - * the horizon color - * @param zenithColor - * the zenith color - * @param blenderContext - * the blender context - * @return the sky texture - */ - public TextureCubeMap generateSkyTexture(int size, ColorRGBA horizontalColor, ColorRGBA zenithColor, BlenderContext blenderContext) { - Image image = ImageUtils.createEmptyImage(Format.RGB8, size, size, 6); - PixelInputOutput pixelIO = PixelIOFactory.getPixelIO(image.getFormat()); - TexturePixel pixel = new TexturePixel(); - - float delta = 1 / (float) (size - 1); - float sideV, sideS = 1, forwardU = 1, forwardV, upS; - TempVars tempVars = TempVars.get(); - CastFunction castFunction = CAST_FUNCTIONS[blenderContext.getBlenderKey().getSkyGeneratedTextureShape().ordinal()]; - float castRadius = blenderContext.getBlenderKey().getSkyGeneratedTextureRadius(); - - for (int x = 0; x < size; ++x) { - sideV = 1; - forwardV = 1; - upS = 0; - for (int y = 0; y < size; ++y) { - castFunction.cast(tempVars.vect1.set(1, sideV, sideS), castRadius); - textureGenerator.getPixel(pixel, tempVars.vect1.x, tempVars.vect1.y, tempVars.vect1.z); - pixelIO.write(image, NEGATIVE_X, ImageUtils.color(pixel, horizontalColor, zenithColor), x, y);// right - - castFunction.cast(tempVars.vect1.set(0, sideV, 1 - sideS), castRadius); - textureGenerator.getPixel(pixel, tempVars.vect1.x, tempVars.vect1.y, tempVars.vect1.z); - pixelIO.write(image, POSITIVE_X, ImageUtils.color(pixel, horizontalColor, zenithColor), x, y);// left - - castFunction.cast(tempVars.vect1.set(forwardU, forwardV, 0), castRadius); - textureGenerator.getPixel(pixel, tempVars.vect1.x, tempVars.vect1.y, tempVars.vect1.z); - pixelIO.write(image, POSITIVE_Z, ImageUtils.color(pixel, horizontalColor, zenithColor), x, y);// front - - castFunction.cast(tempVars.vect1.set(1 - forwardU, forwardV, 1), castRadius); - textureGenerator.getPixel(pixel, tempVars.vect1.x, tempVars.vect1.y, tempVars.vect1.z); - pixelIO.write(image, NEGATIVE_Z, ImageUtils.color(pixel, horizontalColor, zenithColor), x, y);// back - - castFunction.cast(tempVars.vect1.set(forwardU, 0, upS), castRadius); - textureGenerator.getPixel(pixel, tempVars.vect1.x, tempVars.vect1.y, tempVars.vect1.z); - pixelIO.write(image, NEGATIVE_Y, ImageUtils.color(pixel, horizontalColor, zenithColor), x, y);// top - - castFunction.cast(tempVars.vect1.set(forwardU, 1, 1 - upS), castRadius); - textureGenerator.getPixel(pixel, tempVars.vect1.x, tempVars.vect1.y, tempVars.vect1.z); - pixelIO.write(image, POSITIVE_Y, ImageUtils.color(pixel, horizontalColor, zenithColor), x, y);// bottom - - sideV = FastMath.clamp(sideV - delta, 0, 1); - forwardV = FastMath.clamp(forwardV - delta, 0, 1); - upS = FastMath.clamp(upS + delta, 0, 1); - } - sideS = FastMath.clamp(sideS - delta, 0, 1); - forwardU = FastMath.clamp(forwardU - delta, 0, 1); - } - tempVars.release(); - - return new TextureCubeMap(image); - } - - @Override - public void setWrap(WrapAxis axis, WrapMode mode) { - } - - @Override - public void setWrap(WrapMode mode) { - } - - @Override - public WrapMode getWrap(WrapAxis axis) { - return null; - } - - @Override - public Type getType() { - return Type.ThreeDimensional; - } - - @Override - public Texture createSimpleClone() { - return null; - } - - /** - * Private class to give the format of the 'virtual' 3D texture image. - * - * @author Marcin Roguski (Kaelthas) - */ - private static class GeneratedTextureImage extends Image { - public GeneratedTextureImage(Format imageFormat) { - super.format = imageFormat; - } - } - - /** - * The casting functions to create a sky generated texture against selected shape of a selected size. - * - * @author Marcin Roguski (Kaelthas) - */ - private static interface CastFunction { - void cast(Vector3f pointToCast, float radius); - } -} diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/ImageLoader.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/ImageLoader.java deleted file mode 100644 index 806de78f5..000000000 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/ImageLoader.java +++ /dev/null @@ -1,136 +0,0 @@ -/* - * 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.blender.textures; - -import com.jme3.asset.AssetManager; -import com.jme3.asset.TextureKey; -import com.jme3.scene.plugins.blender.file.BlenderInputStream; -import com.jme3.texture.Image; -import com.jme3.texture.Texture; -import com.jme3.texture.plugins.AWTLoader; -import com.jme3.texture.plugins.HDRLoader; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * An image loader class. It uses three loaders (AWTLoader, TGALoader and DDSLoader) in an attempt to load the image from the given - * input stream. - * - * @author Marcin Roguski (Kaelthas) - */ -/* package */class ImageLoader extends AWTLoader { - private static final Logger LOGGER = Logger.getLogger(ImageLoader.class.getName()); - private static final Logger hdrLogger = Logger.getLogger(HDRLoader.class.getName()); // Used to silence HDR Errors - - /** - * List of Blender-Supported Texture Extensions (we have to guess them, so - * the AssetLoader can find them. Not good, but better than nothing. - * Source: https://docs.blender.org/manual/en/dev/data_system/files/media/image_formats.html - */ - private static final String[] extensions = new String[] - { /* Windows Bitmap */".bmp", - /* Iris */ ".sgi", ".rgb", ".bw", - /* PNG */ ".png", - /* JPEG */ ".jpg", ".jpeg", - /* JPEG 2000 */ ".jp2", ".j2c", - /* Targa */".tga", - /* Cineon & DPX */".cin", ".dpx", - /* OpenEXR */ ".exr", - /* Radiance HDR */ ".hdr", - /* TIFF */ ".tif", ".tiff", - /* DDS (Direct X) */ ".dds" }; - - /** - * This method loads a image which is packed into the blender file. - * It makes use of all the registered AssetLoaders - * - * @param inputStream - * blender input stream - * @param startPosition - * position in the stream where the image data starts - * @param flipY - * if the image should be flipped (does not work with DirectX image) - * @return loaded image or null if it could not be loaded - * @deprecated This method has only been left in for API compability. - * Use loadTexture instead - */ - public Image loadImage(AssetManager assetManager, BlenderInputStream inputStream, int startPosition, boolean flipY) { - Texture tex = loadTexture(assetManager, inputStream, startPosition, flipY); - - if (tex == null) { - return null; - } else { - return tex.getImage(); - } - } - - /** - * This method loads a texture which is packed into the blender file. - * It makes use of all the registered AssetLoaders - * - * @param inputStream - * blender input stream - * @param startPosition - * position in the stream where the image data starts - * @param flipY - * if the image should be flipped (does not work with DirectX image) - * @return loaded texture or null if it could not be loaded - */ - public Texture loadTexture(AssetManager assetManager, BlenderInputStream inputStream, int startPosition, boolean flipY) { - inputStream.setPosition(startPosition); - TextureKey tKey; - Texture result = null; - - hdrLogger.setLevel(Level.SEVERE); // When we bruteforce try HDR on a non hdr file, it prints unreadable chars - - for (String ext: extensions) { - tKey = new TextureKey("dummy" + ext, flipY); - try { - result = assetManager.loadAssetFromStream(tKey, inputStream); - } catch (Exception e) { - continue; - } - - if (result != null) { - break; // Could locate a possible asset - } - } - - if (result == null) { - LOGGER.warning("Texture could not be loaded by any of the available loaders!\n" - + "Since the file has been packed into the blender file, there is no" - + "way for us to tell you which texture it was."); - } - - return result; - } -} diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/ImageUtils.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/ImageUtils.java deleted file mode 100644 index 8ca4f6330..000000000 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/ImageUtils.java +++ /dev/null @@ -1,473 +0,0 @@ -package com.jme3.scene.plugins.blender.textures; - -import java.awt.color.ColorSpace; -import java.awt.geom.AffineTransform; -import java.awt.image.AffineTransformOp; -import java.awt.image.BufferedImage; -import java.awt.image.ColorConvertOp; -import java.nio.ByteBuffer; -import java.util.ArrayList; -import java.util.List; - -import jme3tools.converters.ImageToAwt; -import jme3tools.converters.RGB565; - -import com.jme3.math.ColorRGBA; -import com.jme3.math.Vector3f; -import com.jme3.scene.plugins.blender.textures.io.PixelIOFactory; -import com.jme3.scene.plugins.blender.textures.io.PixelInputOutput; -import com.jme3.texture.Image; -import com.jme3.texture.Image.Format; -import com.jme3.util.BufferUtils; - -/** - * This utility class has the methods that deal with images. - * - * @author Marcin Roguski (Kaelthas) - */ -public final class ImageUtils { - /** - * Creates an image of the given size and depth. - * @param format - * the image format - * @param width - * the image width - * @param height - * the image height - * @param depth - * the image depth - * @return the new image instance - */ - public static Image createEmptyImage(Format format, int width, int height, int depth) { - int bufferSize = width * height * (format.getBitsPerPixel() >> 3); - if (depth < 2) { - return new Image(format, width, height, BufferUtils.createByteBuffer(bufferSize), com.jme3.texture.image.ColorSpace.Linear); - } - ArrayList data = new ArrayList(depth); - for (int i = 0; i < depth; ++i) { - data.add(BufferUtils.createByteBuffer(bufferSize)); - } - return new Image(Format.RGB8, width, height, depth, data, com.jme3.texture.image.ColorSpace.Linear); - } - - /** - * The method sets a color for the given pixel by merging the two given colors. - * The lowIntensityColor will be most visible when the pixel has low intensity. - * The highIntensityColor will be most visible when the pixel has high intensity. - * - * @param pixel - * the pixel that will have the colors altered - * @param lowIntensityColor - * the low intensity color - * @param highIntensityColor - * the high intensity color - * @return the altered pixel (the same instance) - */ - public static TexturePixel color(TexturePixel pixel, ColorRGBA lowIntensityColor, ColorRGBA highIntensityColor) { - float intensity = pixel.intensity; - pixel.fromColor(lowIntensityColor); - pixel.mult(1 - pixel.intensity); - pixel.add(highIntensityColor.mult(intensity)); - return pixel; - } - - /** - * This method merges two given images. The result is stored in the - * 'target' image. - * - * @param targetImage - * the target image - * @param sourceImage - * the source image - */ - public static void merge(Image targetImage, Image sourceImage) { - if (sourceImage.getDepth() != targetImage.getDepth()) { - throw new IllegalArgumentException("The given images should have the same depth to merge them!"); - } - if (sourceImage.getWidth() != targetImage.getWidth()) { - throw new IllegalArgumentException("The given images should have the same width to merge them!"); - } - if (sourceImage.getHeight() != targetImage.getHeight()) { - throw new IllegalArgumentException("The given images should have the same height to merge them!"); - } - - PixelInputOutput sourceIO = PixelIOFactory.getPixelIO(sourceImage.getFormat()); - PixelInputOutput targetIO = PixelIOFactory.getPixelIO(targetImage.getFormat()); - TexturePixel sourcePixel = new TexturePixel(); - TexturePixel targetPixel = new TexturePixel(); - int depth = targetImage.getDepth() == 0 ? 1 : targetImage.getDepth(); - - for (int layerIndex = 0; layerIndex < depth; ++layerIndex) { - for (int x = 0; x < sourceImage.getWidth(); ++x) { - for (int y = 0; y < sourceImage.getHeight(); ++y) { - sourceIO.read(sourceImage, layerIndex, sourcePixel, x, y); - targetIO.read(targetImage, layerIndex, targetPixel, x, y); - targetPixel.merge(sourcePixel); - targetIO.write(targetImage, layerIndex, targetPixel, x, y); - } - } - } - } - - /** - * This method merges two given images. The result is stored in the - * 'target' image. - * - * @param targetImage - * the target image - * @param sourceImage - * the source image - */ - public static void mix(Image targetImage, Image sourceImage) { - if (sourceImage.getDepth() != targetImage.getDepth()) { - throw new IllegalArgumentException("The given images should have the same depth to merge them!"); - } - if (sourceImage.getWidth() != targetImage.getWidth()) { - throw new IllegalArgumentException("The given images should have the same width to merge them!"); - } - if (sourceImage.getHeight() != targetImage.getHeight()) { - throw new IllegalArgumentException("The given images should have the same height to merge them!"); - } - - PixelInputOutput sourceIO = PixelIOFactory.getPixelIO(sourceImage.getFormat()); - PixelInputOutput targetIO = PixelIOFactory.getPixelIO(targetImage.getFormat()); - TexturePixel sourcePixel = new TexturePixel(); - TexturePixel targetPixel = new TexturePixel(); - int depth = targetImage.getDepth() == 0 ? 1 : targetImage.getDepth(); - - for (int layerIndex = 0; layerIndex < depth; ++layerIndex) { - for (int x = 0; x < sourceImage.getWidth(); ++x) { - for (int y = 0; y < sourceImage.getHeight(); ++y) { - sourceIO.read(sourceImage, layerIndex, sourcePixel, x, y); - targetIO.read(targetImage, layerIndex, targetPixel, x, y); - targetPixel.mix(sourcePixel); - targetIO.write(targetImage, layerIndex, targetPixel, x, y); - } - } - } - } - - /** - * Resizes the image to the given width and height. - * @param source - * the source image (this remains untouched, the new image instance is created) - * @param width - * the target image width - * @param height - * the target image height - * @return the resized image - */ - public static Image resizeTo(Image source, int width, int height) { - BufferedImage sourceImage = ImageToAwt.convert(source, false, false, 0); - - double scaleX = width / (double) sourceImage.getWidth(); - double scaleY = height / (double) sourceImage.getHeight(); - AffineTransform scaleTransform = AffineTransform.getScaleInstance(scaleX, scaleY); - AffineTransformOp bilinearScaleOp = new AffineTransformOp(scaleTransform, AffineTransformOp.TYPE_BILINEAR); - - BufferedImage scaledImage = bilinearScaleOp.filter(sourceImage, new BufferedImage(width, height, sourceImage.getType())); - return ImageUtils.toJmeImage(scaledImage, source.getFormat()); - } - - /** - * This method converts the given texture into normal-map texture. - * - * @param source - * the source texture - * @param strengthFactor - * the normal strength factor - * @return normal-map texture - */ - public static Image convertToNormalMapTexture(Image source, float strengthFactor) { - BufferedImage sourceImage = ImageToAwt.convert(source, false, false, 0); - - BufferedImage heightMap = new BufferedImage(sourceImage.getWidth(), sourceImage.getHeight(), BufferedImage.TYPE_INT_ARGB); - BufferedImage bumpMap = new BufferedImage(sourceImage.getWidth(), sourceImage.getHeight(), BufferedImage.TYPE_INT_ARGB); - ColorConvertOp gscale = new ColorConvertOp(ColorSpace.getInstance(ColorSpace.CS_GRAY), null); - gscale.filter(sourceImage, heightMap); - - Vector3f S = new Vector3f(); - Vector3f T = new Vector3f(); - Vector3f N = new Vector3f(); - - for (int x = 0; x < bumpMap.getWidth(); ++x) { - for (int y = 0; y < bumpMap.getHeight(); ++y) { - // generating bump pixel - S.x = 1; - S.y = 0; - S.z = strengthFactor * ImageUtils.getHeight(heightMap, x + 1, y) - strengthFactor * ImageUtils.getHeight(heightMap, x - 1, y); - T.x = 0; - T.y = 1; - T.z = strengthFactor * ImageUtils.getHeight(heightMap, x, y + 1) - strengthFactor * ImageUtils.getHeight(heightMap, x, y - 1); - - float den = (float) Math.sqrt(S.z * S.z + T.z * T.z + 1); - N.x = -S.z; - N.y = -T.z; - N.z = 1; - N.divideLocal(den); - - // setting the pixel in the result image - bumpMap.setRGB(x, y, ImageUtils.vectorToColor(N.x, N.y, N.z)); - } - } - return ImageUtils.toJmeImage(bumpMap, source.getFormat()); - } - - /** - * This method converts the given texture into black and whit (grayscale) texture. - * - * @param source - * the source texture - * @return grayscale texture - */ - public static Image convertToGrayscaleTexture(Image source) { - BufferedImage sourceImage = ImageToAwt.convert(source, false, false, 0); - ColorConvertOp op = new ColorConvertOp(ColorSpace.getInstance(ColorSpace.CS_GRAY), null); - op.filter(sourceImage, sourceImage); - return ImageUtils.toJmeImage(sourceImage, source.getFormat()); - } - - /** - * This method decompresses the given image. If the given image is already - * decompressed nothing happens and it is simply returned. - * - * @param image - * the image to decompress - * @return the decompressed image - */ - public static Image decompress(Image image) { - Format format = image.getFormat(); - int depth = image.getDepth(); - if (depth == 0) { - depth = 1; - } - ArrayList dataArray = new ArrayList(depth); - int[] sizes = image.getMipMapSizes() != null ? image.getMipMapSizes() : new int[1]; - int[] newMipmapSizes = image.getMipMapSizes() != null ? new int[image.getMipMapSizes().length] : null; - - for (int dataLayerIndex = 0; dataLayerIndex < depth; ++dataLayerIndex) { - ByteBuffer data = image.getData(dataLayerIndex); - data.rewind(); - if (sizes.length == 1) { - sizes[0] = data.remaining(); - } - float widthToHeightRatio = image.getWidth() / image.getHeight();// this should always be constant for each mipmap - List texelDataList = new ArrayList(sizes.length); - int maxPosition = 0, resultSize = 0; - - for (int sizeIndex = 0; sizeIndex < sizes.length; ++sizeIndex) { - maxPosition += sizes[sizeIndex]; - DDSTexelData texelData = new DDSTexelData(sizes[sizeIndex], widthToHeightRatio, format); - texelDataList.add(texelData); - switch (format) { - case DXT1:// BC1 - case DXT1A: - while (data.position() < maxPosition) { - TexturePixel[] colors = new TexturePixel[] { new TexturePixel(), new TexturePixel(), new TexturePixel(), new TexturePixel() }; - short c0 = data.getShort(); - short c1 = data.getShort(); - int col0 = RGB565.RGB565_to_ARGB8(c0); - int col1 = RGB565.RGB565_to_ARGB8(c1); - colors[0].fromARGB8(col0); - colors[1].fromARGB8(col1); - - if (col0 > col1) { - // creating color2 = 2/3color0 + 1/3color1 - colors[2].fromPixel(colors[0]); - colors[2].mult(2); - colors[2].add(colors[1]); - colors[2].divide(3); - - // creating color3 = 1/3color0 + 2/3color1; - colors[3].fromPixel(colors[1]); - colors[3].mult(2); - colors[3].add(colors[0]); - colors[3].divide(3); - } else { - // creating color2 = 1/2color0 + 1/2color1 - colors[2].fromPixel(colors[0]); - colors[2].add(colors[1]); - colors[2].mult(0.5f); - - colors[3].fromARGB8(0); - } - int indexes = data.getInt();// 4-byte table with color indexes in decompressed table - texelData.add(colors, indexes); - } - break; - case DXT3:// BC2 - while (data.position() < maxPosition) { - TexturePixel[] colors = new TexturePixel[] { new TexturePixel(), new TexturePixel(), new TexturePixel(), new TexturePixel() }; - long alpha = data.getLong(); - float[] alphas = new float[16]; - long alphasIndex = 0; - for (int i = 0; i < 16; ++i) { - alphasIndex |= i << i * 4; - byte a = (byte) ((alpha >> i * 4 & 0x0F) << 4); - alphas[i] = a >= 0 ? a / 255.0f : 1.0f - ~a / 255.0f; - } - - short c0 = data.getShort(); - short c1 = data.getShort(); - int col0 = RGB565.RGB565_to_ARGB8(c0); - int col1 = RGB565.RGB565_to_ARGB8(c1); - colors[0].fromARGB8(col0); - colors[1].fromARGB8(col1); - - // creating color2 = 2/3color0 + 1/3color1 - colors[2].fromPixel(colors[0]); - colors[2].mult(2); - colors[2].add(colors[1]); - colors[2].divide(3); - - // creating color3 = 1/3color0 + 2/3color1; - colors[3].fromPixel(colors[1]); - colors[3].mult(2); - colors[3].add(colors[0]); - colors[3].divide(3); - - int indexes = data.getInt();// 4-byte table with color indexes in decompressed table - texelData.add(colors, indexes, alphas, alphasIndex); - } - break; - case DXT5:// BC3 - float[] alphas = new float[8]; - while (data.position() < maxPosition) { - TexturePixel[] colors = new TexturePixel[] { new TexturePixel(), new TexturePixel(), new TexturePixel(), new TexturePixel() }; - alphas[0] = data.get() * 255.0f; - alphas[1] = data.get() * 255.0f; - //the casts to long must be done here because otherwise 32-bit integers would be shifetd by 32 and 40 bits which would result in improper values - long alphaIndices = data.get() | (long)data.get() << 8 | (long)data.get() << 16 | (long)data.get() << 24 | (long)data.get() << 32 | (long)data.get() << 40; - if (alphas[0] > alphas[1]) {// 6 interpolated alpha values. - alphas[2] = (6 * alphas[0] + alphas[1]) / 7; - alphas[3] = (5 * alphas[0] + 2 * alphas[1]) / 7; - alphas[4] = (4 * alphas[0] + 3 * alphas[1]) / 7; - alphas[5] = (3 * alphas[0] + 4 * alphas[1]) / 7; - alphas[6] = (2 * alphas[0] + 5 * alphas[1]) / 7; - alphas[7] = (alphas[0] + 6 * alphas[1]) / 7; - } else { - alphas[2] = (4 * alphas[0] + alphas[1]) * 0.2f; - alphas[3] = (3 * alphas[0] + 2 * alphas[1]) * 0.2f; - alphas[4] = (2 * alphas[0] + 3 * alphas[1]) * 0.2f; - alphas[5] = (alphas[0] + 4 * alphas[1]) * 0.2f; - alphas[6] = 0; - alphas[7] = 1; - } - - short c0 = data.getShort(); - short c1 = data.getShort(); - int col0 = RGB565.RGB565_to_ARGB8(c0); - int col1 = RGB565.RGB565_to_ARGB8(c1); - colors[0].fromARGB8(col0); - colors[1].fromARGB8(col1); - - // creating color2 = 2/3color0 + 1/3color1 - colors[2].fromPixel(colors[0]); - colors[2].mult(2); - colors[2].add(colors[1]); - colors[2].divide(3); - - // creating color3 = 1/3color0 + 2/3color1; - colors[3].fromPixel(colors[1]); - colors[3].mult(2); - colors[3].add(colors[0]); - colors[3].divide(3); - - int indexes = data.getInt();// 4-byte table with color indexes in decompressed table - texelData.add(colors, indexes, alphas, alphaIndices); - } - break; - default: - throw new IllegalStateException("Unknown compressed format: " + format); - } - newMipmapSizes[sizeIndex] = texelData.getSizeInBytes(); - resultSize += texelData.getSizeInBytes(); - } - byte[] bytes = new byte[resultSize]; - int offset = 0; - byte[] pixelBytes = new byte[4]; - for (DDSTexelData texelData : texelDataList) { - for (int i = 0; i < texelData.getPixelWidth(); ++i) { - for (int j = 0; j < texelData.getPixelHeight(); ++j) { - if (texelData.getRGBA8(i, j, pixelBytes)) { - bytes[offset + (j * texelData.getPixelWidth() + i) * 4] = pixelBytes[0]; - bytes[offset + (j * texelData.getPixelWidth() + i) * 4 + 1] = pixelBytes[1]; - bytes[offset + (j * texelData.getPixelWidth() + i) * 4 + 2] = pixelBytes[2]; - bytes[offset + (j * texelData.getPixelWidth() + i) * 4 + 3] = pixelBytes[3]; - } else { - break; - } - } - } - offset += texelData.getSizeInBytes(); - } - dataArray.add(BufferUtils.createByteBuffer(bytes)); - } - - Image result = depth > 1 ? new Image(Format.RGBA8, image.getWidth(), image.getHeight(), depth, dataArray, com.jme3.texture.image.ColorSpace.Linear) : - new Image(Format.RGBA8, image.getWidth(), image.getHeight(), dataArray.get(0), com.jme3.texture.image.ColorSpace.Linear); - if (newMipmapSizes != null) { - result.setMipMapSizes(newMipmapSizes); - } - return result; - } - - /** - * This method returns the height represented by the specified pixel in the - * given texture. The given texture should be a height-map. - * - * @param image - * the height-map texture - * @param x - * pixel's X coordinate - * @param y - * pixel's Y coordinate - * @return height represented by the given texture in the specified location - */ - private static int getHeight(BufferedImage image, int x, int y) { - if (x < 0) { - x = 0; - } else if (x >= image.getWidth()) { - x = image.getWidth() - 1; - } - if (y < 0) { - y = 0; - } else if (y >= image.getHeight()) { - y = image.getHeight() - 1; - } - return image.getRGB(x, y) & 0xff; - } - - /** - * This method transforms given vector's coordinates into ARGB color (A is - * always = 255). - * - * @param x - * X factor of the vector - * @param y - * Y factor of the vector - * @param z - * Z factor of the vector - * @return color representation of the given vector - */ - private static int vectorToColor(float x, float y, float z) { - int r = Math.round(255 * (x + 1f) / 2f); - int g = Math.round(255 * (y + 1f) / 2f); - int b = Math.round(255 * (z + 1f) / 2f); - return (255 << 24) + (r << 16) + (g << 8) + b; - } - - /** - * Converts java awt image to jme image. - * @param bufferedImage - * the java awt image - * @param format - * the result image format - * @return the jme image - */ - private static Image toJmeImage(BufferedImage bufferedImage, Format format) { - ByteBuffer byteBuffer = BufferUtils.createByteBuffer(bufferedImage.getWidth() * bufferedImage.getHeight() * 3); - ImageToAwt.convert(bufferedImage, format, byteBuffer); - return new Image(format, bufferedImage.getWidth(), bufferedImage.getHeight(), byteBuffer, com.jme3.texture.image.ColorSpace.Linear); - } -} diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/TextureHelper.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/TextureHelper.java deleted file mode 100644 index 5b7ccfc9b..000000000 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/TextureHelper.java +++ /dev/null @@ -1,691 +0,0 @@ -/* - * Copyright (c) 2009-2018 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.blender.textures; - -import java.awt.geom.AffineTransform; -import java.nio.ByteBuffer; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.logging.Level; -import java.util.logging.Logger; - -import com.jme3.asset.AssetInfo; -import com.jme3.asset.AssetLoadException; -import com.jme3.asset.AssetManager; -import com.jme3.asset.AssetNotFoundException; -import com.jme3.asset.BlenderKey; -import com.jme3.asset.GeneratedTextureKey; -import com.jme3.asset.TextureKey; -import com.jme3.math.Vector2f; -import com.jme3.scene.VertexBuffer.Type; -import com.jme3.scene.plugins.blender.AbstractBlenderHelper; -import com.jme3.scene.plugins.blender.BlenderContext; -import com.jme3.scene.plugins.blender.BlenderContext.LoadedDataType; -import com.jme3.scene.plugins.blender.file.BlenderFileException; -import com.jme3.scene.plugins.blender.file.DynamicArray; -import com.jme3.scene.plugins.blender.file.FileBlockHeader; -import com.jme3.scene.plugins.blender.file.Pointer; -import com.jme3.scene.plugins.blender.file.Structure; -import com.jme3.scene.plugins.blender.materials.MaterialContext; -import com.jme3.scene.plugins.blender.textures.UVCoordinatesGenerator.UVCoordinatesType; -import com.jme3.scene.plugins.blender.textures.blending.TextureBlender; -import com.jme3.scene.plugins.blender.textures.blending.TextureBlenderFactory; -import com.jme3.scene.plugins.blender.textures.generating.TextureGeneratorFactory; -import com.jme3.scene.plugins.blender.textures.io.PixelIOFactory; -import com.jme3.scene.plugins.blender.textures.io.PixelInputOutput; -import com.jme3.texture.Image; -import com.jme3.texture.Texture; -import com.jme3.texture.Texture.MinFilter; -import com.jme3.texture.Texture.WrapMode; -import com.jme3.texture.Texture2D; -import com.jme3.texture.image.ColorSpace; -import com.jme3.util.BufferUtils; -import com.jme3.util.PlaceholderAssets; - -/** - * A class that is used in texture calculations. - * - * @author Marcin Roguski - */ -public class TextureHelper extends AbstractBlenderHelper { - private static final Logger LOGGER = Logger.getLogger(TextureHelper.class.getName()); - - // texture types - public static final int TEX_NONE = 0; - public static final int TEX_CLOUDS = 1; - public static final int TEX_WOOD = 2; - public static final int TEX_MARBLE = 3; - public static final int TEX_MAGIC = 4; - public static final int TEX_BLEND = 5; - public static final int TEX_STUCCI = 6; - public static final int TEX_NOISE = 7; - public static final int TEX_IMAGE = 8; - public static final int TEX_PLUGIN = 9; - public static final int TEX_ENVMAP = 10; - public static final int TEX_MUSGRAVE = 11; - public static final int TEX_VORONOI = 12; - public static final int TEX_DISTNOISE = 13; - public static final int TEX_POINTDENSITY = 14; // v. 25+ - public static final int TEX_VOXELDATA = 15; // v. 25+ - public static final int TEX_OCEAN = 16; // v. 26+ - - public static final Type[] TEXCOORD_TYPES = new Type[] { Type.TexCoord, Type.TexCoord2, Type.TexCoord3, Type.TexCoord4, Type.TexCoord5, Type.TexCoord6, Type.TexCoord7, Type.TexCoord8 }; - - private TextureGeneratorFactory textureGeneratorFactory = new TextureGeneratorFactory(); - - /** - * This constructor parses the given blender version and stores the result. - * It creates noise generator and texture generators. - * - * @param blenderVersion - * the version read from the blend file - * @param blenderContext - * the blender context - */ - public TextureHelper(String blenderVersion, BlenderContext blenderContext) { - super(blenderVersion, blenderContext); - } - - /** - * This class returns a texture read from the file or from packed blender - * data. The returned texture has the name set to the value of its blender - * type. - * - * @param textureStructure - * texture structure filled with data - * @param blenderContext - * the blender context - * @return the texture that can be used by JME engine - * @throws BlenderFileException - * this exception is thrown when the blend file structure is - * somehow invalid or corrupted - */ - public Texture getTexture(Structure textureStructure, Structure mTex, BlenderContext blenderContext) throws BlenderFileException { - Texture result = (Texture) blenderContext.getLoadedFeature(textureStructure.getOldMemoryAddress(), LoadedDataType.FEATURE); - if (result != null) { - return result; - } - - if ("ID".equals(textureStructure.getType())) { - LOGGER.fine("Loading texture from external blend file."); - return (Texture) this.loadLibrary(textureStructure); - } - - int type = ((Number) textureStructure.getFieldValue("type")).intValue(); - int imaflag = ((Number) textureStructure.getFieldValue("imaflag")).intValue(); - - switch (type) { - case TEX_IMAGE:// (it is first because probably this will be most commonly used) - Pointer pImage = (Pointer) textureStructure.getFieldValue("ima"); - if (pImage.isNotNull()) { - Structure image = pImage.fetchData().get(0); - Texture loadedTexture = this.loadImageAsTexture(image, imaflag, blenderContext); - if (loadedTexture != null) { - result = loadedTexture; - this.applyColorbandAndColorFactors(textureStructure, result.getImage(), blenderContext); - } - } - break; - case TEX_CLOUDS: - case TEX_WOOD: - case TEX_MARBLE: - case TEX_MAGIC: - case TEX_BLEND: - case TEX_STUCCI: - case TEX_NOISE: - case TEX_MUSGRAVE: - case TEX_VORONOI: - case TEX_DISTNOISE: - result = new GeneratedTexture(textureStructure, mTex, textureGeneratorFactory.createTextureGenerator(type), blenderContext); - break; - case TEX_NONE:// No texture, do nothing - break; - case TEX_POINTDENSITY: - case TEX_VOXELDATA: - case TEX_PLUGIN: - case TEX_ENVMAP: - case TEX_OCEAN: - LOGGER.log(Level.WARNING, "Unsupported texture type: {0} for texture: {1}", new Object[] { type, textureStructure.getName() }); - break; - default: - throw new BlenderFileException("Unknown texture type: " + type + " for texture: " + textureStructure.getName()); - } - if (result != null) { - result.setName(textureStructure.getName()); - result.setWrap(WrapMode.Repeat); - - // decide if the mipmaps will be generated - switch (blenderContext.getBlenderKey().getMipmapGenerationMethod()) { - case ALWAYS_GENERATE: - result.setMinFilter(MinFilter.Trilinear); - break; - case NEVER_GENERATE: - break; - case GENERATE_WHEN_NEEDED: - if ((imaflag & 0x04) != 0) { - result.setMinFilter(MinFilter.Trilinear); - } - break; - default: - throw new IllegalStateException("Unknown mipmap generation method: " + blenderContext.getBlenderKey().getMipmapGenerationMethod()); - } - - if (type != TEX_IMAGE) {// only generated textures should have this key - result.setKey(new GeneratedTextureKey(textureStructure.getName())); - } - - if (LOGGER.isLoggable(Level.FINE)) { - LOGGER.log(Level.FINE, "Adding texture {0} to the loaded features with OMA = {1}", new Object[] { result.getName(), textureStructure.getOldMemoryAddress() }); - } - blenderContext.addLoadedFeatures(textureStructure.getOldMemoryAddress(), LoadedDataType.STRUCTURE, textureStructure); - blenderContext.addLoadedFeatures(textureStructure.getOldMemoryAddress(), LoadedDataType.FEATURE, result); - } - return result; - } - - /** - * This class returns a texture read from the file or from packed blender - * data. - * - * @param imageStructure - * image structure filled with data - * @param imaflag - * the image flag - * @param blenderContext - * the blender context - * @return the texture that can be used by JME engine - * @throws BlenderFileException - * this exception is thrown when the blend file structure is - * somehow invalid or corrupted - */ - public Texture loadImageAsTexture(Structure imageStructure, int imaflag, BlenderContext blenderContext) throws BlenderFileException { - LOGGER.log(Level.FINE, "Fetching texture with OMA = {0}", imageStructure.getOldMemoryAddress()); - Texture result = null; - Image im = (Image) blenderContext.getLoadedFeature(imageStructure.getOldMemoryAddress(), LoadedDataType.FEATURE); - // if (im == null) { HACK force reaload always, as constructor in else case is destroying the TextureKeys! - if ("ID".equals(imageStructure.getType())) { - LOGGER.fine("Loading texture from external blend file."); - result = (Texture) this.loadLibrary(imageStructure); - } else { - String texturePath = imageStructure.getFieldValue("name").toString(); - Pointer pPackedFile = (Pointer) imageStructure.getFieldValue("packedfile"); - if (pPackedFile.isNull()) { - LOGGER.log(Level.FINE, "Reading texture from file: {0}", texturePath); - result = this.loadImageFromFile(texturePath, imaflag, blenderContext); - } else { - LOGGER.fine("Packed texture. Reading directly from the blend file!"); - Structure packedFile = pPackedFile.fetchData().get(0); - Pointer pData = (Pointer) packedFile.getFieldValue("data"); - FileBlockHeader dataFileBlock = blenderContext.getFileBlock(pData.getOldMemoryAddress()); - blenderContext.getInputStream().setPosition(dataFileBlock.getBlockPosition()); - - // Should the texture be flipped? It works for sinbad .. - result = new ImageLoader().loadTexture(blenderContext.getAssetManager(), blenderContext.getInputStream(), dataFileBlock.getBlockPosition(), true); - if (result == null) { - result = new Texture2D(PlaceholderAssets.getPlaceholderImage(blenderContext.getAssetManager())); - LOGGER.fine("ImageLoader returned null. It probably failed to load the packed texture, using placeholder asset"); - } - } - } - //} else { - // result = new Texture2D(im); - // } - - if (result != null) {// render result is not being loaded - blenderContext.addLoadedFeatures(imageStructure.getOldMemoryAddress(), LoadedDataType.STRUCTURE, imageStructure); - blenderContext.addLoadedFeatures(imageStructure.getOldMemoryAddress(), LoadedDataType.FEATURE, result.getImage()); - result.setName(imageStructure.getName()); - } - return result; - } - - /** - * This method creates the affine transform that is used to transform a - * triangle defined by one UV coordinates into a triangle defined by - * different UV's. - * - * @param source - * source UV coordinates - * @param dest - * target UV coordinates - * @param sourceSize - * the width and height of the source image - * @param targetSize - * the width and height of the target image - * @return affine transform to transform one triangle to another - */ - public AffineTransform createAffineTransform(Vector2f[] source, Vector2f[] dest, int[] sourceSize, int[] targetSize) { - float x11 = source[0].getX() * sourceSize[0]; - float x12 = source[0].getY() * sourceSize[1]; - float x21 = source[1].getX() * sourceSize[0]; - float x22 = source[1].getY() * sourceSize[1]; - float x31 = source[2].getX() * sourceSize[0]; - float x32 = source[2].getY() * sourceSize[1]; - float y11 = dest[0].getX() * targetSize[0]; - float y12 = dest[0].getY() * targetSize[1]; - float y21 = dest[1].getX() * targetSize[0]; - float y22 = dest[1].getY() * targetSize[1]; - float y31 = dest[2].getX() * targetSize[0]; - float y32 = dest[2].getY() * targetSize[1]; - - float a1 = ((y11 - y21) * (x12 - x32) - (y11 - y31) * (x12 - x22)) / ((x11 - x21) * (x12 - x32) - (x11 - x31) * (x12 - x22)); - float a2 = ((y11 - y21) * (x11 - x31) - (y11 - y31) * (x11 - x21)) / ((x12 - x22) * (x11 - x31) - (x12 - x32) * (x11 - x21)); - float a3 = y11 - a1 * x11 - a2 * x12; - float a4 = ((y12 - y22) * (x12 - x32) - (y12 - y32) * (x12 - x22)) / ((x11 - x21) * (x12 - x32) - (x11 - x31) * (x12 - x22)); - float a5 = ((y12 - y22) * (x11 - x31) - (y12 - y32) * (x11 - x21)) / ((x12 - x22) * (x11 - x31) - (x12 - x32) * (x11 - x21)); - float a6 = y12 - a4 * x11 - a5 * x12; - return new AffineTransform(a1, a4, a2, a5, a3, a6); - } - - /** - * This method returns the proper pixel position on the image. - * - * @param pos - * the relative position (value of range [0, 1] (both inclusive)) - * @param size - * the size of the line the pixel lies on (width, height or - * depth) - * @return the integer index of the pixel on the line of the specified width - */ - public int getPixelPosition(float pos, int size) { - float pixelWidth = 1 / (float) size; - pos *= size; - int result = (int) pos; - // here is where we repair floating point operations errors :) - if (Math.abs(result - pos) > pixelWidth) { - ++result; - } - return result; - } - - /** - * This method returns subimage of the give image. The subimage is - * constrained by the rectangle coordinates. The source image is unchanged. - * - * @param image - * the image to be subimaged - * @param minX - * minimum X position - * @param minY - * minimum Y position - * @param maxX - * maximum X position - * @param maxY - * maximum Y position - * @return a part of the given image - */ - public Image getSubimage(Image image, int minX, int minY, int maxX, int maxY) { - if (minY > maxY) { - throw new IllegalArgumentException("Minimum Y value is higher than maximum Y value!"); - } - if (minX > maxX) { - throw new IllegalArgumentException("Minimum Y value is higher than maximum Y value!"); - } - if (image.getData().size() > 1) { - throw new IllegalArgumentException("Only flat images are allowed for subimage operation!"); - } - if (image.getMipMapSizes() != null) { - LOGGER.warning("Subimaging image with mipmaps is not yet supported!"); - } - - int width = maxX - minX; - int height = maxY - minY; - ByteBuffer data = BufferUtils.createByteBuffer(width * height * (image.getFormat().getBitsPerPixel() >> 3)); - - Image result = new Image(image.getFormat(), width, height, data, ColorSpace.sRGB); - PixelInputOutput pixelIO = PixelIOFactory.getPixelIO(image.getFormat()); - TexturePixel pixel = new TexturePixel(); - - for (int x = minX; x < maxX; ++x) { - for (int y = minY; y < maxY; ++y) { - pixelIO.read(image, 0, pixel, x, y); - pixelIO.write(result, 0, pixel, x - minX, y - minY); - } - } - return result; - } - - /** - * This method applies the colorband and color factors to image type - * textures. If there is no colorband defined for the texture or the color - * factors are all equal to 1.0f then no changes are made. - * - * @param tex - * the texture structure - * @param image - * the image that will be altered if necessary - * @param blenderContext - * the blender context - */ - private void applyColorbandAndColorFactors(Structure tex, Image image, BlenderContext blenderContext) { - float rfac = ((Number) tex.getFieldValue("rfac")).floatValue(); - float gfac = ((Number) tex.getFieldValue("gfac")).floatValue(); - float bfac = ((Number) tex.getFieldValue("bfac")).floatValue(); - float[][] colorBand = new ColorBand(tex, blenderContext).computeValues(); - int depth = image.getDepth() == 0 ? 1 : image.getDepth(); - - if (colorBand != null) { - TexturePixel pixel = new TexturePixel(); - PixelInputOutput imageIO = PixelIOFactory.getPixelIO(image.getFormat()); - for (int layerIndex = 0; layerIndex < depth; ++layerIndex) { - for (int x = 0; x < image.getWidth(); ++x) { - for (int y = 0; y < image.getHeight(); ++y) { - imageIO.read(image, layerIndex, pixel, x, y); - - int colorbandIndex = (int) (pixel.alpha * 1000.0f); - pixel.red = colorBand[colorbandIndex][0] * rfac; - pixel.green = colorBand[colorbandIndex][1] * gfac; - pixel.blue = colorBand[colorbandIndex][2] * bfac; - pixel.alpha = colorBand[colorbandIndex][3]; - - imageIO.write(image, layerIndex, pixel, x, y); - } - } - } - } else if (rfac != 1.0f || gfac != 1.0f || bfac != 1.0f) { - TexturePixel pixel = new TexturePixel(); - PixelInputOutput imageIO = PixelIOFactory.getPixelIO(image.getFormat()); - for (int layerIndex = 0; layerIndex < depth; ++layerIndex) { - for (int x = 0; x < image.getWidth(); ++x) { - for (int y = 0; y < image.getHeight(); ++y) { - imageIO.read(image, layerIndex, pixel, x, y); - - pixel.red *= rfac; - pixel.green *= gfac; - pixel.blue *= bfac; - - imageIO.write(image, layerIndex, pixel, x, y); - } - } - } - } - } - - /** - * This method loads the texture from outside the blend file using the - * AssetManager that the blend file was loaded with. It returns a texture - * with a full assetKey that references the original texture so it later - * doesn't need to be packed when the model data is serialized. It searches - * the AssetManager for the full path if the model file is a relative path - * and will attempt to truncate the path if it is an absolute file path - * until the path can be found in the AssetManager. If the texture can not - * be found, it will issue a load attempt for the initial path anyway so the - * failed load can be reported by the AssetManagers callback methods for - * failed assets. - * - * @param name - * the path to the image - * @param imaflag - * the image flag - * @param blenderContext - * the blender context - * @return the loaded image or null if the image cannot be found - */ - protected Texture loadImageFromFile(String name, int imaflag, BlenderContext blenderContext) { - if (!name.contains(".")) { - return null; // no extension means not a valid image - } - - // decide if the mipmaps will be generated - boolean generateMipmaps = false; - switch (blenderContext.getBlenderKey().getMipmapGenerationMethod()) { - case ALWAYS_GENERATE: - generateMipmaps = true; - break; - case NEVER_GENERATE: - break; - case GENERATE_WHEN_NEEDED: - generateMipmaps = (imaflag & 0x04) != 0; - break; - default: - throw new IllegalStateException("Unknown mipmap generation method: " + blenderContext.getBlenderKey().getMipmapGenerationMethod()); - } - - AssetManager assetManager = blenderContext.getAssetManager(); - name = name.replace('\\', '/'); - Texture result = null; - - if (name.startsWith("//")) { - // This is a relative path, so try to find it relative to the .blend file - String relativePath = name.substring(2); - // Augument the path with blender key path - BlenderKey blenderKey = blenderContext.getBlenderKey(); - int idx = blenderKey.getName().lastIndexOf('/'); - String blenderAssetFolder = blenderKey.getName().substring(0, idx != -1 ? idx : 0); - String absoluteName = blenderAssetFolder + '/' + relativePath; - // Directly try to load texture so AssetManager can report missing textures - try { - TextureKey key = new TextureKey(absoluteName); - key.setFlipY(true); - key.setGenerateMips(generateMipmaps); - result = assetManager.loadTexture(key); - result.setKey(key); - } catch (AssetNotFoundException | AssetLoadException e) { - LOGGER.fine(e.getLocalizedMessage()); - } - } else { - // This is a full path, try to truncate it until the file can be found - // this works as the assetManager root is most probably a part of the - // image path. E.g. AssetManager has a locator at c:/Files/ and the - // texture path is c:/Files/Textures/Models/Image.jpg. - // For this we create a list with every possible full path name from - // the asset name to the root. Image.jpg, Models/Image.jpg, - // Textures/Models/Image.jpg (bingo) etc. - List assetNames = new ArrayList(); - String[] paths = name.split("\\/"); - StringBuilder sb = new StringBuilder(paths[paths.length - 1]);// the asset name - assetNames.add(paths[paths.length - 1]); - - for (int i = paths.length - 2; i >= 0; --i) { - sb.insert(0, '/'); - sb.insert(0, paths[i]); - assetNames.add(0, sb.toString()); - } - // Now try to locate the asset - for (String assetName : assetNames) { - try { - TextureKey key = new TextureKey(assetName); - key.setFlipY(true); - key.setGenerateMips(generateMipmaps); - AssetInfo info = assetManager.locateAsset(key); - if (info != null) { - Texture texture = assetManager.loadTexture(key); - result = texture; - // Set key explicitly here if other ways fail - texture.setKey(key); - // If texture is found return it; - return result; - } - } catch (AssetNotFoundException | AssetLoadException e) { - LOGGER.fine(e.getLocalizedMessage()); - } - } - // The asset was not found in the loop above, call loadTexture with - // the original path once anyway so that the AssetManager can report - // the missing asset to subsystems. - try { - TextureKey key = new TextureKey(name); - assetManager.loadTexture(key); - } catch (AssetNotFoundException | AssetLoadException e) { - LOGGER.fine(e.getLocalizedMessage()); - } - } - - return result; - } - - /** - * Reads the texture data from the given material or sky structure. - * @param structure - * the structure of material or sky - * @param diffuseColorArray - * array of diffuse colors - * @param skyTexture - * indicates it we're going to read sky texture or not - * @return a list of combined textures - * @throws BlenderFileException - * an exception is thrown when problems with reading the blend file occur - */ - @SuppressWarnings("unchecked") - public List readTextureData(Structure structure, float[] diffuseColorArray, boolean skyTexture) throws BlenderFileException { - DynamicArray mtexsArray = (DynamicArray) structure.getFieldValue("mtex"); - int separatedTextures = skyTexture ? 0 : ((Number) structure.getFieldValue("septex")).intValue(); - List texturesList = new ArrayList(); - for (int i = 0; i < mtexsArray.getTotalSize(); ++i) { - Pointer p = mtexsArray.get(i); - if (p.isNotNull() && (separatedTextures & 1 << i) == 0) { - TextureData textureData = new TextureData(); - textureData.mtex = p.fetchData().get(0); - textureData.uvCoordinatesType = skyTexture ? UVCoordinatesType.TEXCO_ORCO.blenderValue : ((Number) textureData.mtex.getFieldValue("texco")).intValue(); - textureData.projectionType = ((Number) textureData.mtex.getFieldValue("mapping")).intValue(); - textureData.uvCoordinatesName = textureData.mtex.getFieldValue("uvName").toString(); - if (textureData.uvCoordinatesName != null && textureData.uvCoordinatesName.trim().length() == 0) { - textureData.uvCoordinatesName = null; - } - - Pointer pTex = (Pointer) textureData.mtex.getFieldValue("tex"); - if (pTex.isNotNull()) { - Structure tex = pTex.fetchData().get(0); - textureData.textureStructure = tex; - texturesList.add(textureData); - } - } - } - - LOGGER.info("Loading model's textures."); - List loadedTextures = new ArrayList(); - if (blenderContext.getBlenderKey().isOptimiseTextures()) { - LOGGER.fine("Optimising the useage of model's textures."); - Map> textureDataMap = this.sortTextures(texturesList); - for (Entry> entry : textureDataMap.entrySet()) { - if (entry.getValue().size() > 0) { - CombinedTexture combinedTexture = new CombinedTexture(entry.getKey().intValue(), !skyTexture); - for (TextureData textureData : entry.getValue()) { - int texflag = ((Number) textureData.mtex.getFieldValue("texflag")).intValue(); - boolean negateTexture = (texflag & 0x04) != 0; - Texture texture = this.getTexture(textureData.textureStructure, textureData.mtex, blenderContext); - if (texture != null) { - int blendType = ((Number) textureData.mtex.getFieldValue("blendtype")).intValue(); - float[] color = new float[] { ((Number) textureData.mtex.getFieldValue("r")).floatValue(), ((Number) textureData.mtex.getFieldValue("g")).floatValue(), ((Number) textureData.mtex.getFieldValue("b")).floatValue() }; - float colfac = ((Number) textureData.mtex.getFieldValue("colfac")).floatValue(); - TextureBlender textureBlender = TextureBlenderFactory.createTextureBlender(texture.getImage().getFormat(), texflag, negateTexture, blendType, diffuseColorArray, color, colfac); - combinedTexture.add(texture, textureBlender, textureData.uvCoordinatesType, textureData.projectionType, textureData.textureStructure, textureData.uvCoordinatesName, blenderContext); - } - } - if (combinedTexture.getTexturesCount() > 0) { - loadedTextures.add(combinedTexture); - } - } - } - } else { - LOGGER.fine("No textures optimisation applied."); - int[] mappings = new int[] { MaterialContext.MTEX_COL, MaterialContext.MTEX_NOR, MaterialContext.MTEX_EMIT, MaterialContext.MTEX_SPEC, MaterialContext.MTEX_ALPHA, MaterialContext.MTEX_AMB }; - for (TextureData textureData : texturesList) { - Texture texture = this.getTexture(textureData.textureStructure, textureData.mtex, blenderContext); - if (texture != null) { - Number mapto = (Number) textureData.mtex.getFieldValue("mapto"); - int texflag = ((Number) textureData.mtex.getFieldValue("texflag")).intValue(); - boolean negateTexture = (texflag & 0x04) != 0; - - boolean colorSet = false; - for (int i = 0; i < mappings.length; ++i) { - if ((mappings[i] & mapto.intValue()) != 0) { - if(mappings[i] == MaterialContext.MTEX_COL) { - colorSet = true; - } else if(colorSet && mappings[i] == MaterialContext.MTEX_ALPHA) { - continue; - } - - CombinedTexture combinedTexture = new CombinedTexture(mappings[i], !skyTexture); - int blendType = ((Number) textureData.mtex.getFieldValue("blendtype")).intValue(); - float[] color = new float[] { ((Number) textureData.mtex.getFieldValue("r")).floatValue(), ((Number) textureData.mtex.getFieldValue("g")).floatValue(), ((Number) textureData.mtex.getFieldValue("b")).floatValue() }; - float colfac = ((Number) textureData.mtex.getFieldValue("colfac")).floatValue(); - TextureBlender textureBlender = TextureBlenderFactory.createTextureBlender(texture.getImage().getFormat(), texflag, negateTexture, blendType, diffuseColorArray, color, colfac); - combinedTexture.add(texture, textureBlender, textureData.uvCoordinatesType, textureData.projectionType, textureData.textureStructure, textureData.uvCoordinatesName, blenderContext); - if (combinedTexture.getTexturesCount() > 0) {// the added texture might not have been accepted (if for example loading generated textures is disabled) - loadedTextures.add(combinedTexture); - } - } - } - } - - } - } - - return loadedTextures; - } - - /** - * This method sorts the textures by their mapping type. In each group only - * textures of one type are put (either two- or three-dimensional). - * - * @return a map with sorted textures - */ - private Map> sortTextures(List textures) { - int[] mappings = new int[] { MaterialContext.MTEX_COL, MaterialContext.MTEX_NOR, MaterialContext.MTEX_EMIT, MaterialContext.MTEX_SPEC, MaterialContext.MTEX_ALPHA, MaterialContext.MTEX_AMB }; - Map> result = new HashMap>(); - for (TextureData data : textures) { - Number mapto = (Number) data.mtex.getFieldValue("mapto"); - - boolean colorSet = false; - for (int i = 0; i < mappings.length; ++i) { - if ((mappings[i] & mapto.intValue()) != 0) { - if(mappings[i] == MaterialContext.MTEX_COL) { - colorSet = true; - } else if(colorSet && mappings[i] == MaterialContext.MTEX_ALPHA) { - continue; - } - - List datas = result.get(mappings[i]); - if (datas == null) { - datas = new ArrayList(); - result.put(mappings[i], datas); - } - datas.add(data); - } - } - } - return result; - } - - private static class TextureData { - public Structure mtex; - public Structure textureStructure; - public int uvCoordinatesType; - public int projectionType; - /** The name of the user's UV coordinates that are used for this texture. */ - public String uvCoordinatesName; - } -} diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/TexturePixel.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/TexturePixel.java deleted file mode 100644 index def3462ed..000000000 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/TexturePixel.java +++ /dev/null @@ -1,392 +0,0 @@ -package com.jme3.scene.plugins.blender.textures; - -import com.jme3.math.ColorRGBA; -import com.jme3.math.FastMath; - -/** - * The class that stores the pixel values of a texture. - * - * @author Marcin Roguski (Kaelthas) - */ -public class TexturePixel implements Cloneable { - /** The pixel data. */ - public float intensity, red, green, blue, alpha; - - /** - * Copies the values from the given pixel. - * - * @param pixel - * the pixel that we read from - */ - public void fromPixel(TexturePixel pixel) { - this.intensity = pixel.intensity; - this.red = pixel.red; - this.green = pixel.green; - this.blue = pixel.blue; - this.alpha = pixel.alpha; - } - - /** - * Copies the values from the given color. - * - * @param colorRGBA - * the color that we read from - */ - public void fromColor(ColorRGBA colorRGBA) { - this.red = colorRGBA.r; - this.green = colorRGBA.g; - this.blue = colorRGBA.b; - this.alpha = colorRGBA.a; - } - - /** - * Copies the values from the given values. - * - * @param a - * the alpha value - * @param r - * the red value - * @param g - * the green value - * @param b - * the blue value - */ - public void fromARGB(float a, float r, float g, float b) { - this.alpha = a; - this.red = r; - this.green = g; - this.blue = b; - } - - /** - * Copies the values from the given values. - * - * @param a - * the alpha value - * @param r - * the red value - * @param g - * the green value - * @param b - * the blue value - */ - public void fromARGB8(byte a, byte r, byte g, byte b) { - this.alpha = a >= 0 ? a / 255.0f : 1.0f - ~a / 255.0f; - this.red = r >= 0 ? r / 255.0f : 1.0f - ~r / 255.0f; - this.green = g >= 0 ? g / 255.0f : 1.0f - ~g / 255.0f; - this.blue = b >= 0 ? b / 255.0f : 1.0f - ~b / 255.0f; - } - - /** - * Copies the values from the given values. - * - * @param a - * the alpha value - * @param r - * the red value - * @param g - * the green value - * @param b - * the blue value - */ - public void fromARGB16(short a, short r, short g, short b) { - this.alpha = a >= 0 ? a / 65535.0f : 1.0f - ~a / 65535.0f; - this.red = r >= 0 ? r / 65535.0f : 1.0f - ~r / 65535.0f; - this.green = g >= 0 ? g / 65535.0f : 1.0f - ~g / 65535.0f; - this.blue = b >= 0 ? b / 65535.0f : 1.0f - ~b / 65535.0f; - } - - /** - * Copies the intensity from the given value. - * - * @param intensity - * the intensity value - */ - public void fromIntensity(byte intensity) { - this.intensity = intensity >= 0 ? intensity / 255.0f : 1.0f - ~intensity / 255.0f; - } - - /** - * Copies the intensity from the given value. - * - * @param intensity - * the intensity value - */ - public void fromIntensity(short intensity) { - this.intensity = intensity >= 0 ? intensity / 65535.0f : 1.0f - ~intensity / 65535.0f; - } - - /** - * This method sets the alpha value (converts it to float number from range - * [0, 1]). - * - * @param alpha - * the alpha value - */ - public void setAlpha(byte alpha) { - this.alpha = alpha >= 0 ? alpha / 255.0f : 1.0f - ~alpha / 255.0f; - } - - /** - * This method sets the alpha value (converts it to float number from range - * [0, 1]). - * - * @param alpha - * the alpha value - */ - public void setAlpha(short alpha) { - this.alpha = alpha >= 0 ? alpha / 65535.0f : 1.0f - ~alpha / 65535.0f; - } - - /** - * Copies the values from the given integer that stores the ARGB8 data. - * - * @param argb8 - * the data stored in an integer - */ - public void fromARGB8(int argb8) { - byte pixelValue = (byte) ((argb8 & 0xFF000000) >> 24); - this.alpha = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - ~pixelValue / 255.0f; - pixelValue = (byte) ((argb8 & 0xFF0000) >> 16); - this.red = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - ~pixelValue / 255.0f; - pixelValue = (byte) ((argb8 & 0xFF00) >> 8); - this.green = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - ~pixelValue / 255.0f; - pixelValue = (byte) (argb8 & 0xFF); - this.blue = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - ~pixelValue / 255.0f; - } - - /** - * Stores RGBA values in the given array. - * - * @param result - * the array to store values - */ - public void toRGBA(float[] result) { - result[0] = this.red; - result[1] = this.green; - result[2] = this.blue; - result[3] = this.alpha; - } - - /** - * Stores the data in the given table. - * - * @param result - * the result table - */ - public void toRGBA8(byte[] result) { - result[0] = (byte) (this.red * 255.0f); - result[1] = (byte) (this.green * 255.0f); - result[2] = (byte) (this.blue * 255.0f); - result[3] = (byte) (this.alpha * 255.0f); - } - - /** - * Stores the pixel values in the integer. - * - * @return the integer that stores the pixel values - */ - public int toARGB8() { - int result = 0; - int b = (int) (this.alpha * 255.0f); - result |= b << 24; - b = (int) (this.red * 255.0f); - result |= b << 16; - b = (int) (this.green * 255.0f); - result |= b << 8; - b = (int) (this.blue * 255.0f); - result |= b; - return result; - } - - /** - * @return the intensity of the pixel - */ - public byte getInt() { - return (byte) (this.intensity * 255.0f); - } - - /** - * @return the alpha value of the pixel - */ - public byte getA8() { - return (byte) (this.alpha * 255.0f); - } - - /** - * @return the alpha red of the pixel - */ - public byte getR8() { - return (byte) (this.red * 255.0f); - } - - /** - * @return the green value of the pixel - */ - public byte getG8() { - return (byte) (this.green * 255.0f); - } - - /** - * @return the blue value of the pixel - */ - public byte getB8() { - return (byte) (this.blue * 255.0f); - } - - /** - * @return the alpha value of the pixel - */ - public short getA16() { - return (byte) (this.alpha * 65535.0f); - } - - /** - * @return the alpha red of the pixel - */ - public short getR16() { - return (byte) (this.red * 65535.0f); - } - - /** - * @return the green value of the pixel - */ - public short getG16() { - return (byte) (this.green * 65535.0f); - } - - /** - * @return the blue value of the pixel - */ - public short getB16() { - return (byte) (this.blue * 65535.0f); - } - - /** - * Merges two pixels (adds the values of each color). - * - * @param pixel - * the pixel we merge with - */ - public void merge(TexturePixel pixel) { - float oneMinusAlpha = 1 - pixel.alpha; - this.red = oneMinusAlpha * this.red + pixel.alpha * pixel.red; - this.green = oneMinusAlpha * this.green + pixel.alpha * pixel.green; - this.blue = oneMinusAlpha * this.blue + pixel.alpha * pixel.blue; - this.alpha = (this.alpha + pixel.alpha) * 0.5f; - } - - /** - * Mixes two pixels. - * - * @param pixel - * the pixel we mix with - */ - public void mix(TexturePixel pixel) { - this.red = 0.5f * (this.red + pixel.red); - this.green = 0.5f * (this.green + pixel.green); - this.blue = 0.5f * (this.blue + pixel.blue); - this.alpha = 0.5f * (this.alpha + pixel.alpha); - this.intensity = 0.5f * (this.intensity + pixel.intensity); - } - - /** - * This method negates the colors. - */ - public void negate() { - this.red = 1.0f - this.red; - this.green = 1.0f - this.green; - this.blue = 1.0f - this.blue; - this.alpha = 1.0f - this.alpha; - } - - /** - * This method clears the pixel values. - */ - public void clear() { - this.intensity = this.blue = this.red = this.green = this.alpha = 0.0f; - } - - /** - * This method adds the calues of the given pixel to the current pixel. - * - * @param pixel - * the pixel we add - */ - public void add(TexturePixel pixel) { - this.red += pixel.red; - this.green += pixel.green; - this.blue += pixel.blue; - this.alpha += pixel.alpha; - this.intensity += pixel.intensity; - } - - /** - * This method adds the calues of the given pixel to the current pixel. - * - * @param pixel - * the pixel we add - */ - public void add(ColorRGBA pixel) { - this.red += pixel.r; - this.green += pixel.g; - this.blue += pixel.b; - this.alpha += pixel.a; - } - - /** - * This method multiplies the values of the given pixel by the given value. - * - * @param value - * multiplication factor - */ - public void mult(float value) { - this.red *= value; - this.green *= value; - this.blue *= value; - this.alpha *= value; - this.intensity *= value; - } - - /** - * This method divides the values of the given pixel by the given value. - * ATTENTION! Beware of the zero value. This will cause you NaN's in the - * pixel values. - * - * @param value - * division factor - */ - public void divide(float value) { - this.red /= value; - this.green /= value; - this.blue /= value; - this.alpha /= value; - this.intensity /= value; - } - - /** - * This method clamps the pixel values to the given borders. - * - * @param min - * the minimum value - * @param max - * the maximum value - */ - public void clamp(float min, float max) { - this.red = FastMath.clamp(this.red, min, max); - this.green = FastMath.clamp(this.green, min, max); - this.blue = FastMath.clamp(this.blue, min, max); - this.alpha = FastMath.clamp(this.alpha, min, max); - this.intensity = FastMath.clamp(this.intensity, min, max); - } - - @Override - public Object clone() throws CloneNotSupportedException { - return super.clone(); - } - - @Override - public String toString() { - return "[" + red + ", " + green + ", " + blue + ", " + alpha + " {" + intensity + "}]"; - } -} diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/TriangulatedTexture.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/TriangulatedTexture.java deleted file mode 100644 index bb63f37cc..000000000 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/TriangulatedTexture.java +++ /dev/null @@ -1,662 +0,0 @@ -package com.jme3.scene.plugins.blender.textures; - -import java.awt.Graphics2D; -import java.awt.RenderingHints; -import java.awt.geom.AffineTransform; -import java.awt.image.BufferedImage; -import java.nio.ByteBuffer; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; -import java.util.TreeSet; - -import jme3tools.converters.ImageToAwt; - -import com.jme3.bounding.BoundingBox; -import com.jme3.math.FastMath; -import com.jme3.math.Vector2f; -import com.jme3.math.Vector3f; -import com.jme3.scene.plugins.blender.BlenderContext; -import com.jme3.scene.plugins.blender.textures.blending.TextureBlender; -import com.jme3.scene.plugins.blender.textures.io.PixelIOFactory; -import com.jme3.scene.plugins.blender.textures.io.PixelInputOutput; -import com.jme3.texture.Image; -import com.jme3.texture.Image.Format; -import com.jme3.texture.Texture; -import com.jme3.texture.Texture2D; -import com.jme3.texture.image.ColorSpace; -import com.jme3.util.BufferUtils; - -/** - * This texture holds a set of images for each face in the specified mesh. It - * helps to flatten 3D texture, merge 3D and 2D textures and merge 2D textures - * with different UV coordinates. - * - * @author Marcin Roguski (Kaelthas) - */ -/* package */class TriangulatedTexture extends Texture2D { - /** The result image format. */ - private Format format; - /** The collection of images for each face. */ - private Collection faceTextures; - /** - * The maximum texture size (width/height). This is taken from the blender - * key. - */ - private int maxTextureSize; - /** A variable that can prevent removing identical textures. */ - private boolean keepIdenticalTextures = false; - /** The result texture. */ - private Texture2D resultTexture; - /** The result texture's UV coordinates. */ - private List resultUVS; - - /** - * This method triangulates the given flat texture. The given texture is not - * changed. - * - * @param texture2d - * the texture to be triangulated - * @param uvs - * the UV coordinates for each face - */ - public TriangulatedTexture(Texture2D texture2d, List uvs, BlenderContext blenderContext) { - maxTextureSize = blenderContext.getBlenderKey().getMaxTextureSize(); - faceTextures = new TreeSet(new Comparator() { - @Override - public int compare(TriangleTextureElement o1, TriangleTextureElement o2) { - return o1.faceIndex - o2.faceIndex; - } - }); - int facesCount = uvs.size() / 3; - for (int i = 0; i < facesCount; ++i) { - faceTextures.add(new TriangleTextureElement(i, texture2d.getImage(), uvs, true, blenderContext)); - } - format = texture2d.getImage().getFormat(); - } - - /** - * Constructor that simply stores precalculated images. - * - * @param faceTextures - * a collection of images for the mesh's faces - * @param blenderContext - * the blender context - */ - public TriangulatedTexture(Collection faceTextures, BlenderContext blenderContext) { - maxTextureSize = blenderContext.getBlenderKey().getMaxTextureSize(); - this.faceTextures = faceTextures; - for (TriangleTextureElement faceTextureElement : faceTextures) { - if (format == null) { - format = faceTextureElement.image.getFormat(); - } else if (format != faceTextureElement.image.getFormat()) { - throw new IllegalArgumentException("Face texture element images MUST have the same image format!"); - } - } - } - - /** - * This method blends the each image using the given blender and taking base - * texture into consideration. - * - * @param textureBlender - * the texture blender that holds the blending definition - * @param baseTexture - * the texture that is 'below' the current texture (can be null) - * @param blenderContext - * the blender context - */ - public void blend(TextureBlender textureBlender, TriangulatedTexture baseTexture, BlenderContext blenderContext) { - Format newFormat = null; - for (TriangleTextureElement triangleTextureElement : faceTextures) { - Image baseImage = baseTexture == null ? null : baseTexture.getFaceTextureElement(triangleTextureElement.faceIndex).image; - triangleTextureElement.image = textureBlender.blend(triangleTextureElement.image, baseImage, blenderContext); - if (newFormat == null) { - newFormat = triangleTextureElement.image.getFormat(); - } else if (newFormat != triangleTextureElement.image.getFormat()) { - throw new IllegalArgumentException("Face texture element images MUST have the same image format!"); - } - } - format = newFormat; - } - - /** - * This method alters the images to fit them into UV coordinates of the - * given target texture. - * - * @param targetTexture - * the texture to whose UV coordinates we fit current images - * @param blenderContext - * the blender context - */ - public void castToUVS(TriangulatedTexture targetTexture, BlenderContext blenderContext) { - int[] sourceSize = new int[2], targetSize = new int[2]; - ImageLoader imageLoader = new ImageLoader(); - TextureHelper textureHelper = blenderContext.getHelper(TextureHelper.class); - for (TriangleTextureElement entry : faceTextures) { - TriangleTextureElement targetFaceTextureElement = targetTexture.getFaceTextureElement(entry.faceIndex); - Vector2f[] dest = targetFaceTextureElement.uv; - - // get the sizes of the source and target images - sourceSize[0] = entry.image.getWidth(); - sourceSize[1] = entry.image.getHeight(); - targetSize[0] = targetFaceTextureElement.image.getWidth(); - targetSize[1] = targetFaceTextureElement.image.getHeight(); - - // create triangle transformation - AffineTransform affineTransform = textureHelper.createAffineTransform(entry.uv, dest, sourceSize, targetSize); - - // compute the result texture - BufferedImage sourceImage = ImageToAwt.convert(entry.image, false, true, 0); - - BufferedImage targetImage = new BufferedImage(targetSize[0], targetSize[1], sourceImage.getType()); - Graphics2D g = targetImage.createGraphics(); - g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); - g.drawImage(sourceImage, affineTransform, null); - g.dispose(); - - Image output = imageLoader.load(targetImage, false); - entry.image = output; - entry.uv[0].set(dest[0]); - entry.uv[1].set(dest[1]); - entry.uv[2].set(dest[2]); - } - } - - /** - * This method returns the flat texture. It is calculated if required or if - * it was not created before. Images that are identical are discarded to - * reduce the texture size. - * - * @param rebuild - * a variable that forces texture recomputation (even if it was - * computed vefore) - * @return flat result texture (all images merged into one) - */ - public Texture2D getResultTexture(boolean rebuild) { - if (resultTexture == null || rebuild) { - // sorting the parts by their height (from highest to the lowest) - List list = new ArrayList(faceTextures); - Collections.sort(list, new Comparator() { - @Override - public int compare(TriangleTextureElement o1, TriangleTextureElement o2) { - return o2.image.getHeight() - o1.image.getHeight(); - } - }); - - // arraging the images on the resulting image (calculating the result image width and height) - Set duplicatedFaceIndexes = new HashSet(); - int resultImageHeight = list.get(0).image.getHeight(); - int resultImageWidth = 0; - int currentXPos = 0, currentYPos = 0; - Map imageLayoutData = new HashMap(list.size()); - while (list.size() > 0) { - TriangleTextureElement currentElement = list.remove(0); - if (currentXPos + currentElement.image.getWidth() > maxTextureSize) { - currentXPos = 0; - currentYPos = resultImageHeight; - resultImageHeight += currentElement.image.getHeight(); - } - Integer[] currentPositions = new Integer[] { currentXPos, currentYPos }; - imageLayoutData.put(currentElement, currentPositions); - - if (keepIdenticalTextures) {// removing identical images - for (int i = 0; i < list.size(); ++i) { - if (currentElement.image.equals(list.get(i).image)) { - duplicatedFaceIndexes.add(list.get(i).faceIndex); - imageLayoutData.put(list.remove(i--), currentPositions); - } - } - } - - currentXPos += currentElement.image.getWidth(); - resultImageWidth = Math.max(resultImageWidth, currentXPos); - // currentYPos += currentElement.image.getHeight(); - - // TODO: implement that to compact the result image - // try to add smaller images below the current one - // int remainingHeight = resultImageHeight - - // currentElement.image.getHeight(); - // while(remainingHeight > 0) { - // for(int i=list.size() - 1;i>=0;--i) { - // - // } - // } - } - - // computing the result UV coordinates - resultUVS = new ArrayList(imageLayoutData.size() * 3); - for (int i = 0; i < imageLayoutData.size() * 3; ++i) { - resultUVS.add(null); - } - Vector2f[] uvs = new Vector2f[3]; - for (Entry entry : imageLayoutData.entrySet()) { - Integer[] position = entry.getValue(); - entry.getKey().computeFinalUVCoordinates(resultImageWidth, resultImageHeight, position[0], position[1], uvs); - resultUVS.set(entry.getKey().faceIndex * 3, uvs[0]); - resultUVS.set(entry.getKey().faceIndex * 3 + 1, uvs[1]); - resultUVS.set(entry.getKey().faceIndex * 3 + 2, uvs[2]); - } - - Image resultImage = new Image(format, resultImageWidth, resultImageHeight, BufferUtils.createByteBuffer(resultImageWidth * resultImageHeight * (format.getBitsPerPixel() >> 3)), ColorSpace.Linear); - resultTexture = new Texture2D(resultImage); - for (Entry entry : imageLayoutData.entrySet()) { - if (!duplicatedFaceIndexes.contains(entry.getKey().faceIndex)) { - this.draw(resultImage, entry.getKey().image, entry.getValue()[0], entry.getValue()[1]); - } - } - - // setting additional data - resultTexture.setWrap(WrapAxis.S, this.getWrap(WrapAxis.S)); - resultTexture.setWrap(WrapAxis.T, this.getWrap(WrapAxis.T)); - resultTexture.setMagFilter(this.getMagFilter()); - resultTexture.setMinFilter(this.getMinFilter()); - } - return resultTexture; - } - - /** - * @return the result flat texture - */ - public Texture2D getResultTexture() { - return this.getResultTexture(false); - } - - /** - * @return the result texture's UV coordinates - */ - public List getResultUVS() { - this.getResultTexture();// this is called here to make sure that the result UVS are computed - return resultUVS; - } - - /** - * This method returns a single image element for the given face index. - * - * @param faceIndex - * the face index - * @return image element for the required face index - * @throws IllegalStateException - * this exception is thrown if the current image set does not - * contain an image for the given face index - */ - public TriangleTextureElement getFaceTextureElement(int faceIndex) { - for (TriangleTextureElement textureElement : faceTextures) { - if (textureElement.faceIndex == faceIndex) { - return textureElement; - } - } - throw new IllegalStateException("No face texture element found for index: " + faceIndex); - } - - /** - * @return the amount of texture faces - */ - public int getFaceTextureCount() { - return faceTextures.size(); - } - - /** - * Tells the object wheather to keep or reduce identical face textures. - * - * @param keepIdenticalTextures - * keeps or discards identical textures - */ - public void setKeepIdenticalTextures(boolean keepIdenticalTextures) { - this.keepIdenticalTextures = keepIdenticalTextures; - } - - /** - * This method draws the source image on the target image starting with the - * specified positions. - * - * @param target - * the target image - * @param source - * the source image - * @param targetXPos - * start X position on the target image - * @param targetYPos - * start Y position on the target image - */ - private void draw(Image target, Image source, int targetXPos, int targetYPos) { - PixelInputOutput sourceIO = PixelIOFactory.getPixelIO(source.getFormat()); - PixelInputOutput targetIO = PixelIOFactory.getPixelIO(target.getFormat()); - TexturePixel pixel = new TexturePixel(); - - for (int x = 0; x < source.getWidth(); ++x) { - for (int y = 0; y < source.getHeight(); ++y) { - sourceIO.read(source, 0, pixel, x, y); - targetIO.write(target, 0, pixel, targetXPos + x, targetYPos + y); - } - } - } - - /** - * A class that represents an image for a single face of the mesh. - * - * @author Marcin Roguski (Kaelthas) - */ - /* package */static class TriangleTextureElement { - /** The image for the face. */ - public Image image; - /** The UV coordinates for the image. */ - public final Vector2f[] uv; - /** The index of the face this image refers to. */ - public final int faceIndex; - - /** - * Constructor that creates the image element from the given texture and - * UV coordinates (it cuts out the smallest rectasngle possible from the - * given image that will hold the triangle defined by the given UV - * coordinates). After the image is cut out the UV coordinates are - * recalculated to be fit for the new image. - * - * @param faceIndex - * the index of mesh's face this image refers to - * @param sourceImage - * the source image - * @param uvCoordinates - * the UV coordinates that define the image - */ - public TriangleTextureElement(int faceIndex, Image sourceImage, List uvCoordinates, boolean wholeUVList, BlenderContext blenderContext) { - TextureHelper textureHelper = blenderContext.getHelper(TextureHelper.class); - this.faceIndex = faceIndex; - - uv = wholeUVList ? new Vector2f[] { uvCoordinates.get(faceIndex * 3).clone(), uvCoordinates.get(faceIndex * 3 + 1).clone(), uvCoordinates.get(faceIndex * 3 + 2).clone() } : new Vector2f[] { uvCoordinates.get(0).clone(), uvCoordinates.get(1).clone(), uvCoordinates.get(2).clone() }; - - // be careful here, floating point operations might cause the - // texture positions to be inapropriate - int[][] texturePosition = new int[3][2]; - for (int i = 0; i < texturePosition.length; ++i) { - texturePosition[i][0] = textureHelper.getPixelPosition(uv[i].x, sourceImage.getWidth()); - texturePosition[i][1] = textureHelper.getPixelPosition(uv[i].y, sourceImage.getHeight()); - } - - // calculating the extent of the texture - int minX = Integer.MAX_VALUE, minY = Integer.MAX_VALUE; - int maxX = Integer.MIN_VALUE, maxY = Integer.MIN_VALUE; - float minUVX = Float.MAX_VALUE, minUVY = Float.MAX_VALUE; - float maxUVX = Float.MIN_VALUE, maxUVY = Float.MIN_VALUE; - - for (int i = 0; i < texturePosition.length; ++i) { - minX = Math.min(texturePosition[i][0], minX); - minY = Math.min(texturePosition[i][1], minY); - - maxX = Math.max(texturePosition[i][0], maxX); - maxY = Math.max(texturePosition[i][1], maxY); - - minUVX = Math.min(uv[i].x, minUVX); - minUVY = Math.min(uv[i].y, minUVY); - maxUVX = Math.max(uv[i].x, maxUVX); - maxUVY = Math.max(uv[i].y, maxUVY); - } - int width = maxX - minX; - int height = maxY - minY; - - if (width == 0) { - width = 1; - } - if (height == 0) { - height = 1; - } - - // copy the pixel from the texture to the result image - PixelInputOutput pixelReader = PixelIOFactory.getPixelIO(sourceImage.getFormat()); - TexturePixel pixel = new TexturePixel(); - ByteBuffer data = BufferUtils.createByteBuffer(width * height * 4); - for (int y = minY; y < maxY; ++y) { - for (int x = minX; x < maxX; ++x) { - int xPos = x >= sourceImage.getWidth() ? x - sourceImage.getWidth() : x; - int yPos = y >= sourceImage.getHeight() ? y - sourceImage.getHeight() : y; - pixelReader.read(sourceImage, 0, pixel, xPos, yPos); - data.put(pixel.getR8()); - data.put(pixel.getG8()); - data.put(pixel.getB8()); - data.put(pixel.getA8()); - } - } - image = new Image(Format.RGBA8, width, height, data, ColorSpace.Linear); - - // modify the UV values so that they fit the new image - float heightUV = maxUVY - minUVY; - float widthUV = maxUVX - minUVX; - for (int i = 0; i < uv.length; ++i) { - // first translate it to the image borders - uv[i].x -= minUVX; - uv[i].y -= minUVY; - // then scale so that it fills the whole area - uv[i].x /= widthUV; - uv[i].y /= heightUV; - } - } - - /** - * Constructor that creates an image element from the 3D texture - * (generated texture). It computes a flat smallest rectangle that can - * hold a (3D) triangle defined by the given UV coordinates. Then it - * defines the image pixels for points in 3D space that define the - * calculated rectangle. - * - * @param faceIndex - * the face index this image refers to - * @param boundingBox - * the bounding box of the mesh - * @param texture - * the texture that allows to compute a pixel value in 3D - * space - * @param uv - * the UV coordinates of the mesh - * @param blenderContext - * the blender context - */ - public TriangleTextureElement(int faceIndex, BoundingBox boundingBox, GeneratedTexture texture, Vector3f[] uv, int[] uvIndices, BlenderContext blenderContext) { - this.faceIndex = faceIndex; - - // compute the face vertices from the UV coordinates - float width = boundingBox.getXExtent() * 2; - float height = boundingBox.getYExtent() * 2; - float depth = boundingBox.getZExtent() * 2; - - Vector3f min = boundingBox.getMin(null); - Vector3f v1 = min.add(uv[uvIndices[0]].x * width, uv[uvIndices[0]].y * height, uv[uvIndices[0]].z * depth); - Vector3f v2 = min.add(uv[uvIndices[1]].x * width, uv[uvIndices[1]].y * height, uv[uvIndices[1]].z * depth); - Vector3f v3 = min.add(uv[uvIndices[2]].x * width, uv[uvIndices[2]].y * height, uv[uvIndices[2]].z * depth); - - // get the rectangle envelope for the triangle - RectangleEnvelope envelope = this.getTriangleEnvelope(v1, v2, v3); - - // create the result image - Format imageFormat = texture.getImage().getFormat(); - int imageWidth = (int) (envelope.width * blenderContext.getBlenderKey().getGeneratedTexturePPU()); - if (imageWidth == 0) { - imageWidth = 1; - } - int imageHeight = (int) (envelope.height * blenderContext.getBlenderKey().getGeneratedTexturePPU()); - if (imageHeight == 0) { - imageHeight = 1; - } - ByteBuffer data = BufferUtils.createByteBuffer(imageWidth * imageHeight * (imageFormat.getBitsPerPixel() >> 3)); - image = new Image(texture.getImage().getFormat(), imageWidth, imageHeight, data, ColorSpace.Linear); - - // computing the pixels - PixelInputOutput pixelWriter = PixelIOFactory.getPixelIO(imageFormat); - TexturePixel pixel = new TexturePixel(); - float[] uvs = new float[3]; - Vector3f point = new Vector3f(envelope.min); - Vector3f vecY = new Vector3f(); - Vector3f wDelta = new Vector3f(envelope.w).multLocal(1.0f / imageWidth); - Vector3f hDelta = new Vector3f(envelope.h).multLocal(1.0f / imageHeight); - for (int x = 0; x < imageWidth; ++x) { - for (int y = 0; y < imageHeight; ++y) { - this.toTextureUV(boundingBox, point, uvs); - texture.getPixel(pixel, uvs[0], uvs[1], uvs[2]); - pixelWriter.write(image, 0, pixel, x, y); - point.addLocal(hDelta); - } - - vecY.addLocal(wDelta); - point.set(envelope.min).addLocal(vecY); - } - - // preparing UV coordinates for the flatted texture - this.uv = new Vector2f[3]; - this.uv[0] = new Vector2f(FastMath.clamp(v1.subtract(envelope.min).length(), 0, Float.MAX_VALUE) / envelope.height, 0); - Vector3f heightDropPoint = v2.subtract(envelope.w);// w is directed from the base to v2 - this.uv[1] = new Vector2f(1, heightDropPoint.subtractLocal(envelope.min).length() / envelope.height); - this.uv[2] = new Vector2f(0, 1); - } - - /** - * This method computes the final UV coordinates for the image (after it - * is combined with other images and drawed on the result image). - * - * @param totalImageWidth - * the result image width - * @param totalImageHeight - * the result image height - * @param xPos - * the most left x coordinate of the image - * @param yPos - * the most top y coordinate of the image - * @param result - * a vector where the result is stored - */ - public void computeFinalUVCoordinates(int totalImageWidth, int totalImageHeight, int xPos, int yPos, Vector2f[] result) { - for (int i = 0; i < 3; ++i) { - result[i] = new Vector2f(); - result[i].x = xPos / (float) totalImageWidth + uv[i].x * (image.getWidth() / (float) totalImageWidth); - result[i].y = yPos / (float) totalImageHeight + uv[i].y * (image.getHeight() / (float) totalImageHeight); - } - } - - /** - * This method converts the given point into 3D UV coordinates. - * - * @param boundingBox - * the bounding box of the mesh - * @param point - * the point to be transformed - * @param uvs - * the result UV coordinates - */ - private void toTextureUV(BoundingBox boundingBox, Vector3f point, float[] uvs) { - uvs[0] = (point.x - boundingBox.getCenter().x) / (boundingBox.getXExtent() == 0 ? 1 : boundingBox.getXExtent()); - uvs[1] = (point.y - boundingBox.getCenter().y) / (boundingBox.getYExtent() == 0 ? 1 : boundingBox.getYExtent()); - uvs[2] = (point.z - boundingBox.getCenter().z) / (boundingBox.getZExtent() == 0 ? 1 : boundingBox.getZExtent()); - // UVS cannot go outside <0, 1> range, but since we are generating texture for triangle envelope it might happen that - // some points of the envelope will exceet the bounding box of the mesh thus generating uvs outside the range - for (int i = 0; i < 3; ++i) { - uvs[i] = FastMath.clamp(uvs[i], 0, 1); - } - } - - /** - * This method returns an envelope of a minimal rectangle, that is set - * in 3D space, and contains the given triangle. - * - * @param triangle - * the triangle - * @return a rectangle minimum and maximum point and height and width - */ - private RectangleEnvelope getTriangleEnvelope(Vector3f v1, Vector3f v2, Vector3f v3) { - Vector3f h = v3.subtract(v1);// the height of the resulting rectangle - Vector3f temp = v2.subtract(v1); - - float field = 0.5f * h.cross(temp).length();// the field of the rectangle: Field = 0.5 * ||h x temp|| - if (field <= 0.0f) { - return new RectangleEnvelope(v1);// return single point envelope - } - - float cosAlpha = h.dot(temp) / (h.length() * temp.length());// the cosinus of angle betweenh and temp - - float triangleHeight = 2 * field / h.length();// the base of the height is the h vector - // now calculate the distance between v1 vertex and the point where - // the above calculated height 'touches' the base line (it can be - // settled outside the h vector) - float x = Math.abs((float) Math.sqrt(FastMath.clamp(temp.lengthSquared() - triangleHeight * triangleHeight, 0, Float.MAX_VALUE))) * Math.signum(cosAlpha); - // now get the height base point - Vector3f xPoint = v1.add(h.normalize().multLocal(x)); - - // get the minimum point of the envelope - Vector3f min = x < 0 ? xPoint : v1; - if (x < 0) { - h = v3.subtract(min); - } else if (x > h.length()) { - h = xPoint.subtract(min); - } - - Vector3f envelopeWidth = v2.subtract(xPoint); - return new RectangleEnvelope(min, envelopeWidth, h); - } - } - - /** - * A class that represents a flat rectangle in 3D space that is built on a - * triangle in 3D space. - * - * @author Marcin Roguski (Kaelthas) - */ - private static class RectangleEnvelope { - /** The minimum point of the rectangle. */ - public final Vector3f min; - /** The width vector. */ - public final Vector3f w; - /** The height vector. */ - public final Vector3f h; - /** The width of the rectangle. */ - public final float width; - /** The height of the rectangle. */ - public final float height; - - /** - * Constructs a rectangle that actually holds a point, not a triangle. - * This is a special case that is sometimes used when generating a - * texture where UV coordinates are defined by normals instead of - * vertices. - * - * @param pointPosition - * a position in 3D space - */ - public RectangleEnvelope(Vector3f pointPosition) { - min = pointPosition; - h = w = Vector3f.ZERO; - width = height = 1; - } - - /** - * Constructs a rectangle envelope. - * - * @param min - * the minimum rectangle point - * @param w - * the width vector - * @param h - * the height vector - */ - public RectangleEnvelope(Vector3f min, Vector3f w, Vector3f h) { - this.min = min; - this.h = h; - this.w = w; - width = w.length(); - height = h.length(); - } - - @Override - public String toString() { - return "Envelope[min = " + min + ", w = " + w + ", h = " + h + "]"; - } - } - - @Override - public Texture createSimpleClone() { - return null; - } -} diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/UVCoordinatesGenerator.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/UVCoordinatesGenerator.java deleted file mode 100644 index a922b2588..000000000 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/UVCoordinatesGenerator.java +++ /dev/null @@ -1,420 +0,0 @@ -/* - * 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.blender.textures; - -import java.util.ArrayList; -import java.util.List; -import java.util.logging.Logger; - -import com.jme3.bounding.BoundingBox; -import com.jme3.bounding.BoundingSphere; -import com.jme3.bounding.BoundingVolume; -import com.jme3.math.Vector2f; -import com.jme3.math.Vector3f; -import com.jme3.scene.Geometry; -import com.jme3.scene.Mesh; -import com.jme3.scene.VertexBuffer; -import com.jme3.scene.plugins.blender.textures.UVProjectionGenerator.UVProjectionType; -import com.jme3.util.BufferUtils; - -/** - * This class is used for UV coordinates generation. - * - * @author Marcin Roguski (Kaelthas) - */ -public class UVCoordinatesGenerator { - private static final Logger LOGGER = Logger.getLogger(UVCoordinatesGenerator.class.getName()); - - public static enum UVCoordinatesType { - TEXCO_ORCO(1), TEXCO_REFL(2), TEXCO_NORM(4), TEXCO_GLOB(8), TEXCO_UV(16), TEXCO_OBJECT(32), TEXCO_LAVECTOR(64), TEXCO_VIEW(128), - TEXCO_STICKY(256), TEXCO_OSA(512), TEXCO_WINDOW(1024), NEED_UV(2048), TEXCO_TANGENT(4096), - TEXCO_PARTICLE_OR_STRAND(8192), //TEXCO_PARTICLE (since blender 2.6x) has also the value of: 8192 but is used for halo materials instead of normal materials - TEXCO_STRESS(16384), TEXCO_SPEED(32768); - - public final int blenderValue; - - private UVCoordinatesType(int blenderValue) { - this.blenderValue = blenderValue; - } - - public static UVCoordinatesType valueOf(int blenderValue) { - for (UVCoordinatesType coordinatesType : UVCoordinatesType.values()) { - if (coordinatesType.blenderValue == blenderValue) { - return coordinatesType; - } - } - return null; - } - } - - /** - * Generates a UV coordinates for 2D texture. - * - * @param mesh - * the mesh we generate UV's for - * @param texco - * UV coordinates type - * @param projection - * projection type - * @param geometries - * the geometris the given mesh belongs to (required to compute - * bounding box) - * @return UV coordinates for the given mesh - */ - public static List generateUVCoordinatesFor2DTexture(Mesh mesh, UVCoordinatesType texco, UVProjectionType projection, Geometry geometries) { - List result = new ArrayList(); - BoundingBox bb = UVCoordinatesGenerator.getBoundingBox(geometries); - float[] inputData = null;// positions, normals, reflection vectors, etc. - - switch (texco) { - case TEXCO_ORCO: - inputData = BufferUtils.getFloatArray(mesh.getFloatBuffer(VertexBuffer.Type.Position)); - break; - case TEXCO_UV:// this should be used if not defined by user explicitly - Vector2f[] data = new Vector2f[] { new Vector2f(0, 1), new Vector2f(0, 0), new Vector2f(1, 0) }; - for (int i = 0; i < mesh.getVertexCount(); ++i) { - result.add(data[i % 3]); - } - break; - case TEXCO_NORM: - inputData = BufferUtils.getFloatArray(mesh.getFloatBuffer(VertexBuffer.Type.Normal)); - break; - case TEXCO_REFL: - case TEXCO_GLOB: - case TEXCO_TANGENT: - case TEXCO_STRESS: - case TEXCO_LAVECTOR: - case TEXCO_OBJECT: - case TEXCO_OSA: - case TEXCO_PARTICLE_OR_STRAND: - case TEXCO_SPEED: - case TEXCO_STICKY: - case TEXCO_VIEW: - case TEXCO_WINDOW: - LOGGER.warning("Texture coordinates type not currently supported: " + texco); - break; - default: - throw new IllegalStateException("Unknown texture coordinates value: " + texco); - } - - if (inputData != null) {// make projection calculations - switch (projection) { - case PROJECTION_FLAT: - inputData = UVProjectionGenerator.flatProjection(inputData, bb); - break; - case PROJECTION_CUBE: - inputData = UVProjectionGenerator.cubeProjection(inputData, bb); - break; - case PROJECTION_TUBE: - BoundingTube bt = UVCoordinatesGenerator.getBoundingTube(geometries); - inputData = UVProjectionGenerator.tubeProjection(inputData, bt); - break; - case PROJECTION_SPHERE: - BoundingSphere bs = UVCoordinatesGenerator.getBoundingSphere(geometries); - inputData = UVProjectionGenerator.sphereProjection(inputData, bs); - break; - default: - throw new IllegalStateException("Unknown projection type: " + projection); - } - for (int i = 0; i < inputData.length; i += 2) { - result.add(new Vector2f(inputData[i], inputData[i + 1])); - } - } - return result; - } - - /** - * Generates a UV coordinates for 3D texture. - * - * @param mesh - * the mesh we generate UV's for - * @param texco - * UV coordinates type - * @param coordinatesSwappingIndexes - * coordinates swapping indexes - * @param geometries - * the geometris the given mesh belongs to (required to compute - * bounding box) - * @return UV coordinates for the given mesh - */ - public static List generateUVCoordinatesFor3DTexture(Mesh mesh, UVCoordinatesType texco, int[] coordinatesSwappingIndexes, Geometry... geometries) { - List result = new ArrayList(); - BoundingBox bb = UVCoordinatesGenerator.getBoundingBox(geometries); - float[] inputData = null;// positions, normals, reflection vectors, etc. - - switch (texco) { - case TEXCO_ORCO: - inputData = BufferUtils.getFloatArray(mesh.getFloatBuffer(VertexBuffer.Type.Position)); - break; - case TEXCO_UV: - Vector2f[] data = new Vector2f[] { new Vector2f(0, 1), new Vector2f(0, 0), new Vector2f(1, 0) }; - for (int i = 0; i < mesh.getVertexCount(); ++i) { - Vector2f uv = data[i % 3]; - result.add(new Vector3f(uv.x, uv.y, 0)); - } - break; - case TEXCO_NORM: - inputData = BufferUtils.getFloatArray(mesh.getFloatBuffer(VertexBuffer.Type.Normal)); - break; - case TEXCO_REFL: - case TEXCO_GLOB: - case TEXCO_TANGENT: - case TEXCO_STRESS: - case TEXCO_LAVECTOR: - case TEXCO_OBJECT: - case TEXCO_OSA: - case TEXCO_PARTICLE_OR_STRAND: - case TEXCO_SPEED: - case TEXCO_STICKY: - case TEXCO_VIEW: - case TEXCO_WINDOW: - LOGGER.warning("Texture coordinates type not currently supported: " + texco); - break; - default: - throw new IllegalStateException("Unknown texture coordinates value: " + texco); - } - - if (inputData != null) {// make calculations - Vector3f min = bb.getMin(null); - float[] uvCoordsResults = new float[4];// used for coordinates swapping - float[] ext = new float[] { bb.getXExtent() * 2, bb.getYExtent() * 2, bb.getZExtent() * 2 }; - for (int i = 0; i < ext.length; ++i) { - if (ext[i] == 0) { - ext[i] = 1; - } - } - // now transform the coordinates so that they are in the range of - // <0; 1> - for (int i = 0; i < inputData.length; i += 3) { - uvCoordsResults[1] = (inputData[i] - min.x) / ext[0]; - uvCoordsResults[2] = (inputData[i + 1] - min.y) / ext[1]; - uvCoordsResults[3] = (inputData[i + 2] - min.z) / ext[2]; - result.add(new Vector3f(uvCoordsResults[coordinatesSwappingIndexes[0]], uvCoordsResults[coordinatesSwappingIndexes[1]], uvCoordsResults[coordinatesSwappingIndexes[2]])); - } - } - return result; - } - - /** - * This method should be used to determine if the texture will ever be - * computed. If the texture coordinates are not supported then the try of - * flattening the texture might result in runtime exceptions occurence. - * - * @param texco - * the texture coordinates type - * @return true if the type is supported and false otherwise - */ - public static boolean isTextureCoordinateTypeSupported(UVCoordinatesType texco) { - switch (texco) { - case TEXCO_ORCO: - case TEXCO_UV: - case TEXCO_NORM: - return true; - case TEXCO_REFL: - case TEXCO_GLOB: - case TEXCO_TANGENT: - case TEXCO_STRESS: - case TEXCO_LAVECTOR: - case TEXCO_OBJECT: - case TEXCO_OSA: - case TEXCO_PARTICLE_OR_STRAND: - case TEXCO_SPEED: - case TEXCO_STICKY: - case TEXCO_VIEW: - case TEXCO_WINDOW: - return false; - default: - throw new IllegalStateException("Unknown texture coordinates value: " + texco); - } - } - - /** - * This method returns the bounding box of the given geometries. - * - * @param geometries - * the list of geometries - * @return bounding box of the given geometries - */ - public static BoundingBox getBoundingBox(Geometry... geometries) { - BoundingBox result = null; - for (Geometry geometry : geometries) { - geometry.updateModelBound(); - BoundingVolume bv = geometry.getModelBound(); - if (bv instanceof BoundingBox) { - return (BoundingBox) bv; - } else if (bv instanceof BoundingSphere) { - BoundingSphere bs = (BoundingSphere) bv; - float r = bs.getRadius(); - return new BoundingBox(bs.getCenter(), r, r, r); - } else { - throw new IllegalStateException("Unknown bounding volume type: " + bv.getClass().getName()); - } - } - return result; - } - - /** - * This method returns the bounding sphere of the given geometries. - * - * @param geometries - * the list of geometries - * @return bounding sphere of the given geometries - */ - /* package */static BoundingSphere getBoundingSphere(Geometry... geometries) { - BoundingSphere result = null; - for (Geometry geometry : geometries) { - geometry.updateModelBound(); - BoundingVolume bv = geometry.getModelBound(); - if (bv instanceof BoundingBox) { - BoundingBox bb = (BoundingBox) bv; - float r = Math.max(bb.getXExtent(), bb.getYExtent()); - r = Math.max(r, bb.getZExtent()); - return new BoundingSphere(r, bb.getCenter()); - } else if (bv instanceof BoundingSphere) { - return (BoundingSphere) bv; - } else { - throw new IllegalStateException("Unknown bounding volume type: " + bv.getClass().getName()); - } - } - return result; - } - - /** - * This method returns the bounding tube of the given geometries. - * - * @param geometries - * the list of geometries - * @return bounding tube of the given geometries - */ - /* package */static BoundingTube getBoundingTube(Geometry... geometries) { - BoundingTube result = null; - for (Geometry geometry : geometries) { - BoundingBox bb = UVCoordinatesGenerator.getBoundingBox(geometry); - Vector3f max = bb.getMax(null); - Vector3f min = bb.getMin(null); - float radius = Math.max(max.x - min.x, max.y - min.y) * 0.5f; - - BoundingTube bt = new BoundingTube(radius, max.z - min.z, bb.getCenter()); - if (result == null) { - result = bt; - } else { - result.merge(bt); - } - } - return result; - } - - /** - * A very simple bounding tube. It holds only the basic data bout the - * bounding tube and does not provide full functionality of a - * BoundingVolume. Should be replaced with a bounding tube that extends the - * BoundingVolume if it is ever created. - * - * @author Marcin Roguski (Kaelthas) - */ - /* package */static class BoundingTube { - private float radius; - private float height; - private Vector3f center; - - /** - * Constructor creates the tube with the given params. - * - * @param radius - * the radius of the tube - * @param height - * the height of the tube - * @param center - * the center of the tube - */ - public BoundingTube(float radius, float height, Vector3f center) { - this.radius = radius; - this.height = height; - this.center = center; - } - - /** - * This method merges two bounding tubes. - * - * @param boundingTube - * bounding tube to be merged woth the current one - * @return new instance of bounding tube representing the tubes' merge - */ - public BoundingTube merge(BoundingTube boundingTube) { - // get tubes (tube1.radius >= tube2.radius) - BoundingTube tube1, tube2; - if (radius >= boundingTube.radius) { - tube1 = this; - tube2 = boundingTube; - } else { - tube1 = boundingTube; - tube2 = this; - } - float r1 = tube1.radius; - float r2 = tube2.radius; - - float minZ = Math.min(tube1.center.z - tube1.height * 0.5f, tube2.center.z - tube2.height * 0.5f); - float maxZ = Math.max(tube1.center.z + tube1.height * 0.5f, tube2.center.z + tube2.height * 0.5f); - float height = maxZ - minZ; - Vector3f distance = tube2.center.subtract(tube1.center); - Vector3f center = tube1.center.add(distance.mult(0.5f)); - distance.z = 0;// projecting this vector on XY plane - float d = distance.length(); - // d <= r1 - r2: tube2 is inside tube1 or touches tube1 from the - // inside - // d > r1 - r2: tube2 is outside or touches tube1 or crosses tube1 - float radius = d <= r1 - r2 ? tube1.radius : (d + r1 + r2) * 0.5f; - return new BoundingTube(radius, height, center); - } - - /** - * @return the radius of the tube - */ - public float getRadius() { - return radius; - } - - /** - * @return the height of the tube - */ - public float getHeight() { - return height; - } - - /** - * @return the center of the tube - */ - public Vector3f getCenter() { - return center; - } - } -} diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/UVProjectionGenerator.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/UVProjectionGenerator.java deleted file mode 100644 index a213bc626..000000000 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/UVProjectionGenerator.java +++ /dev/null @@ -1,240 +0,0 @@ -package com.jme3.scene.plugins.blender.textures; - -import com.jme3.bounding.BoundingBox; -import com.jme3.bounding.BoundingSphere; -import com.jme3.math.FastMath; -import com.jme3.math.Triangle; -import com.jme3.math.Vector3f; -import com.jme3.scene.plugins.blender.textures.UVCoordinatesGenerator.BoundingTube; - -/** - * This class helps with projection calculations. - * - * @author Marcin Roguski (Kaelthas) - */ -/* package */class UVProjectionGenerator { - /** - * 2D texture mapping (projection) - * @author Marcin Roguski (Kaelthas) - */ - public static enum UVProjectionType { - PROJECTION_FLAT(0), PROJECTION_CUBE(1), PROJECTION_TUBE(2), PROJECTION_SPHERE(3); - - public final int blenderValue; - - private UVProjectionType(int blenderValue) { - this.blenderValue = blenderValue; - } - - public static UVProjectionType valueOf(int blenderValue) { - for (UVProjectionType projectionType : UVProjectionType.values()) { - if (projectionType.blenderValue == blenderValue) { - return projectionType; - } - } - return null; - } - } - - /** - * Flat projection for 2D textures. - * - * @param mesh - * mesh that is to be projected - * @param bb - * the bounding box for projecting - * @return UV coordinates after the projection - */ - public static float[] flatProjection(float[] positions, BoundingBox bb) { - Vector3f min = bb.getMin(null); - float[] ext = new float[] { bb.getXExtent() * 2.0f, bb.getZExtent() * 2.0f }; - float[] uvCoordinates = new float[positions.length / 3 * 2]; - for (int i = 0, j = 0; i < positions.length; i += 3, j += 2) { - uvCoordinates[j] = (positions[i] - min.x) / ext[0]; - // skip the Y-coordinate - uvCoordinates[j + 1] = (positions[i + 2] - min.z) / ext[1]; - } - return uvCoordinates; - } - - /** - * Cube projection for 2D textures. - * - * @param positions - * points to be projected - * @param bb - * the bounding box for projecting - * @return UV coordinates after the projection - */ - public static float[] cubeProjection(float[] positions, BoundingBox bb) { - Triangle triangle = new Triangle(); - Vector3f x = new Vector3f(1, 0, 0); - Vector3f y = new Vector3f(0, 1, 0); - Vector3f z = new Vector3f(0, 0, 1); - Vector3f min = bb.getMin(null); - float[] ext = new float[] { bb.getXExtent() * 2.0f, bb.getYExtent() * 2.0f, bb.getZExtent() * 2.0f }; - - float[] uvCoordinates = new float[positions.length / 3 * 2]; - float borderAngle = (float) Math.sqrt(2.0f) / 2.0f; - for (int i = 0, pointIndex = 0; i < positions.length; i += 9) { - triangle.set(0, positions[i], positions[i + 1], positions[i + 2]); - triangle.set(1, positions[i + 3], positions[i + 4], positions[i + 5]); - triangle.set(2, positions[i + 6], positions[i + 7], positions[i + 8]); - Vector3f n = triangle.getNormal(); - float dotNX = Math.abs(n.dot(x)); - float dorNY = Math.abs(n.dot(y)); - float dotNZ = Math.abs(n.dot(z)); - if (dotNX > borderAngle) { - if (dotNZ < borderAngle) {// discard X-coordinate - uvCoordinates[pointIndex++] = (triangle.get1().y - min.y) / ext[1]; - uvCoordinates[pointIndex++] = (triangle.get1().z - min.z) / ext[2]; - uvCoordinates[pointIndex++] = (triangle.get2().y - min.y) / ext[1]; - uvCoordinates[pointIndex++] = (triangle.get2().z - min.z) / ext[2]; - uvCoordinates[pointIndex++] = (triangle.get3().y - min.y) / ext[1]; - uvCoordinates[pointIndex++] = (triangle.get3().z - min.z) / ext[2]; - } else {// discard Z-coordinate - uvCoordinates[pointIndex++] = (triangle.get1().x - min.x) / ext[0]; - uvCoordinates[pointIndex++] = (triangle.get1().y - min.y) / ext[1]; - uvCoordinates[pointIndex++] = (triangle.get2().x - min.x) / ext[0]; - uvCoordinates[pointIndex++] = (triangle.get2().y - min.y) / ext[1]; - uvCoordinates[pointIndex++] = (triangle.get3().x - min.x) / ext[0]; - uvCoordinates[pointIndex++] = (triangle.get3().y - min.y) / ext[1]; - } - } else { - if (dorNY > borderAngle) {// discard Y-coordinate - uvCoordinates[pointIndex++] = (triangle.get1().x - min.x) / ext[0]; - uvCoordinates[pointIndex++] = (triangle.get1().z - min.z) / ext[2]; - uvCoordinates[pointIndex++] = (triangle.get2().x - min.x) / ext[0]; - uvCoordinates[pointIndex++] = (triangle.get2().z - min.z) / ext[2]; - uvCoordinates[pointIndex++] = (triangle.get3().x - min.x) / ext[0]; - uvCoordinates[pointIndex++] = (triangle.get3().z - min.z) / ext[2]; - } else {// discard Z-coordinate - uvCoordinates[pointIndex++] = (triangle.get1().x - min.x) / ext[0]; - uvCoordinates[pointIndex++] = (triangle.get1().y - min.y) / ext[1]; - uvCoordinates[pointIndex++] = (triangle.get2().x - min.x) / ext[0]; - uvCoordinates[pointIndex++] = (triangle.get2().y - min.y) / ext[1]; - uvCoordinates[pointIndex++] = (triangle.get3().x - min.x) / ext[0]; - uvCoordinates[pointIndex++] = (triangle.get3().y - min.y) / ext[1]; - } - } - triangle.setNormal(null);// clear the previous normal vector - } - return uvCoordinates; - } - - /** - * Tube projection for 2D textures. - * - * @param positions - * points to be projected - * @param bt - * the bounding tube for projecting - * @return UV coordinates after the projection - */ - public static float[] tubeProjection(float[] positions, BoundingTube bt) { - float[] uvCoordinates = new float[positions.length / 3 * 2]; - Vector3f v = new Vector3f(); - float cx = bt.getCenter().x, cz = bt.getCenter().z; - Vector3f uBase = new Vector3f(0, 0, -1); - - float vBase = bt.getCenter().y - bt.getHeight() * 0.5f; - for (int i = 0, j = 0; i < positions.length; i += 3, j += 2) { - // calculating U - v.set(positions[i] - cx, 0, positions[i + 2] - cz); - v.normalizeLocal(); - float angle = v.angleBetween(uBase);// result between [0; PI] - if (v.x < 0) {// the angle should be greater than PI, we're on the other part of the image then - angle = FastMath.TWO_PI - angle; - } - uvCoordinates[j] = angle / FastMath.TWO_PI; - - // calculating V - float y = positions[i + 1]; - uvCoordinates[j + 1] = (y - vBase) / bt.getHeight(); - } - - // looking for splitted triangles - Triangle triangle = new Triangle(); - for (int i = 0; i < positions.length; i += 9) { - triangle.set(0, positions[i], positions[i + 1], positions[i + 2]); - triangle.set(1, positions[i + 3], positions[i + 4], positions[i + 5]); - triangle.set(2, positions[i + 6], positions[i + 7], positions[i + 8]); - float sgn1 = Math.signum(triangle.get1().x - cx); - float sgn2 = Math.signum(triangle.get2().x - cx); - float sgn3 = Math.signum(triangle.get3().x - cx); - float xSideFactor = sgn1 + sgn2 + sgn3; - float ySideFactor = Math.signum(triangle.get1().z - cz) + Math.signum(triangle.get2().z - cz) + Math.signum(triangle.get3().z - cz); - if ((xSideFactor > -3 || xSideFactor < 3) && ySideFactor < 0) {// the triangle is on the splitting plane - if (sgn1 == 1.0f) { - uvCoordinates[i / 3 * 2] += 1.0f; - } - if (sgn2 == 1.0f) { - uvCoordinates[(i / 3 + 1) * 2] += 1.0f; - } - if (sgn3 == 1.0f) { - uvCoordinates[(i / 3 + 2) * 2] += 1.0f; - } - } - } - return uvCoordinates; - } - - /** - * Sphere projection for 2D textures. - * - * @param positions - * points to be projected - * @param bb - * the bounding box for projecting - * @return UV coordinates after the projection - */ - public static float[] sphereProjection(float[] positions, BoundingSphere bs) {// TODO: rotate it to be vertical - float[] uvCoordinates = new float[positions.length / 3 * 2]; - Vector3f v = new Vector3f(); - float cx = bs.getCenter().x, cy = bs.getCenter().y, cz = bs.getCenter().z; - Vector3f uBase = new Vector3f(0, -1, 0); - Vector3f vBase = new Vector3f(0, 0, -1); - - for (int i = 0, j = 0; i < positions.length; i += 3, j += 2) { - // calculating U - v.set(positions[i] - cx, positions[i + 1] - cy, 0); - v.normalizeLocal(); - float angle = v.angleBetween(uBase);// result between [0; PI] - if (v.x < 0) {// the angle should be greater than PI, we're on the other part of the image then - angle = FastMath.TWO_PI - angle; - } - uvCoordinates[j] = angle / FastMath.TWO_PI; - - // calculating V - v.set(positions[i] - cx, positions[i + 1] - cy, positions[i + 2] - cz); - v.normalizeLocal(); - angle = v.angleBetween(vBase);// result between [0; PI] - uvCoordinates[j + 1] = angle / FastMath.PI; - } - - // looking for splitted triangles - Triangle triangle = new Triangle(); - for (int i = 0; i < positions.length; i += 9) { - triangle.set(0, positions[i], positions[i + 1], positions[i + 2]); - triangle.set(1, positions[i + 3], positions[i + 4], positions[i + 5]); - triangle.set(2, positions[i + 6], positions[i + 7], positions[i + 8]); - float sgn1 = Math.signum(triangle.get1().x - cx); - float sgn2 = Math.signum(triangle.get2().x - cx); - float sgn3 = Math.signum(triangle.get3().x - cx); - float xSideFactor = sgn1 + sgn2 + sgn3; - float ySideFactor = Math.signum(triangle.get1().y - cy) + Math.signum(triangle.get2().y - cy) + Math.signum(triangle.get3().y - cy); - if ((xSideFactor > -3 || xSideFactor < 3) && ySideFactor < 0) {// the triangle is on the splitting plane - if (sgn1 == 1.0f) { - uvCoordinates[i / 3 * 2] += 1.0f; - } - if (sgn2 == 1.0f) { - uvCoordinates[(i / 3 + 1) * 2] += 1.0f; - } - if (sgn3 == 1.0f) { - uvCoordinates[(i / 3 + 2) * 2] += 1.0f; - } - } - } - return uvCoordinates; - } -} diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/UserUVCollection.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/UserUVCollection.java deleted file mode 100644 index ea276af56..000000000 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/UserUVCollection.java +++ /dev/null @@ -1,86 +0,0 @@ -package com.jme3.scene.plugins.blender.textures; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; - -import com.jme3.math.Vector2f; - -/** - * A collection of UV coordinates. The coords are stored in groups defined by the material index and their UV set name. - * - * @author Kaelthas (Marcin Roguski) - */ -public class UserUVCollection { - /** A map between material number and UV coordinates of mesh that has this material applied. */ - private Map>> uvCoordinates = new HashMap>>(); - /** A map between vertex index and its UV coordinates. */ - private Map> uvsMap = new HashMap>(); - - /** - * Adds a single UV coordinates for a specified vertex index. - * @param materialIndex - * the material index - * @param uvSetName - * the UV set name - * @param uv - * the added UV coordinates - * @param jmeVertexIndex - * the index of the vertex in result jme mesh - */ - public void addUV(int materialIndex, String uvSetName, Vector2f uv, int jmeVertexIndex) { - // first get all UV sets for the specified material ... - LinkedHashMap> uvsForMaterial = uvCoordinates.get(materialIndex); - if (uvsForMaterial == null) { - uvsForMaterial = new LinkedHashMap>(); - uvCoordinates.put(materialIndex, uvsForMaterial); - } - - // ... then fetch the UVS for the specified UV set name ... - List uvsForName = uvsForMaterial.get(uvSetName); - if (uvsForName == null) { - uvsForName = new ArrayList(); - uvsForMaterial.put(uvSetName, uvsForName); - } - - // ... add the UV coordinates to the proper list ... - uvsForName.add(uv); - - // ... and add the mapping of the UV coordinates to a vertex index for the specified UV set - Map uvToVertexIndexMapping = uvsMap.get(uvSetName); - if (uvToVertexIndexMapping == null) { - uvToVertexIndexMapping = new HashMap(); - uvsMap.put(uvSetName, uvToVertexIndexMapping); - } - uvToVertexIndexMapping.put(jmeVertexIndex, uv); - } - - /** - * @param uvSetName - * the name of the UV set - * @param vertexIndex - * the vertex index corresponds to the index in jme mesh and not the original one in blender - * @return a pre-existing coordinate vector - */ - public Vector2f getUVForVertex(String uvSetName, int vertexIndex) { - return uvsMap.get(uvSetName).get(vertexIndex); - } - - /** - * @param materialNumber - * the material number that is appied to the mesh - * @return UV coordinates of vertices that belong to the required mesh part - */ - public LinkedHashMap> getUVCoordinates(int materialNumber) { - return uvCoordinates.get(materialNumber); - } - - /** - * @return indicates if the mesh has UV coordinates - */ - public boolean hasUVCoordinates() { - return uvCoordinates.size() > 0; - } -} diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/blending/AbstractTextureBlender.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/blending/AbstractTextureBlender.java deleted file mode 100644 index d1e0e31ae..000000000 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/blending/AbstractTextureBlender.java +++ /dev/null @@ -1,139 +0,0 @@ -package com.jme3.scene.plugins.blender.textures.blending; - -import java.util.logging.Logger; - -import jme3tools.converters.MipMapGenerator; - -import com.jme3.scene.plugins.blender.BlenderContext; -import com.jme3.scene.plugins.blender.materials.MaterialHelper; -import com.jme3.texture.Image; - -/** - * An abstract class that contains the basic methods used by the classes that - * will derive from it. - * - * @author Marcin Roguski (Kaelthas) - */ -/* package */abstract class AbstractTextureBlender implements TextureBlender { - private static final Logger LOGGER = Logger.getLogger(AbstractTextureBlender.class.getName()); - - protected int flag; - protected boolean negateTexture; - protected int blendType; - protected float[] materialColor; - protected float[] color; - protected float blendFactor; - - public AbstractTextureBlender(int flag, boolean negateTexture, int blendType, float[] materialColor, float[] color, float blendFactor) { - this.flag = flag; - this.negateTexture = negateTexture; - this.blendType = blendType; - this.materialColor = materialColor; - this.color = color; - this.blendFactor = blendFactor; - } - - /** - * The method that performs the ramp blending. - * - * @param type - * the blend type - * @param materialRGB - * the rgb value of the material, here the result is stored too - * @param fac - * color affection factor - * @param pixelColor - * the texture color - * @param blenderContext - * the blender context - */ - protected void blendHSV(int type, float[] materialRGB, float fac, float[] pixelColor, BlenderContext blenderContext) { - float oneMinusFactor = 1.0f - fac; - MaterialHelper materialHelper = blenderContext.getHelper(MaterialHelper.class); - - switch (type) { - case MTEX_BLEND_HUE: {// FIXME: not working well for image textures (works fine for generated textures) - float[] colorTransformResult = new float[3]; - materialHelper.rgbToHsv(pixelColor[0], pixelColor[1], pixelColor[2], colorTransformResult); - if (colorTransformResult[0] != 0.0f) { - float colH = colorTransformResult[0]; - materialHelper.rgbToHsv(materialRGB[0], materialRGB[1], materialRGB[2], colorTransformResult); - materialHelper.hsvToRgb(colH, colorTransformResult[1], colorTransformResult[2], colorTransformResult); - materialRGB[0] = oneMinusFactor * materialRGB[0] + fac * colorTransformResult[0]; - materialRGB[1] = oneMinusFactor * materialRGB[1] + fac * colorTransformResult[1]; - materialRGB[2] = oneMinusFactor * materialRGB[2] + fac * colorTransformResult[2]; - } - break; - } - case MTEX_BLEND_SAT: { - float[] colorTransformResult = new float[3]; - materialHelper.rgbToHsv(materialRGB[0], materialRGB[1], materialRGB[2], colorTransformResult); - float h = colorTransformResult[0]; - float s = colorTransformResult[1]; - float v = colorTransformResult[2]; - if (s != 0.0f) { - materialHelper.rgbToHsv(pixelColor[0], pixelColor[1], pixelColor[2], colorTransformResult); - materialHelper.hsvToRgb(h, oneMinusFactor * s + fac * colorTransformResult[1], v, materialRGB); - } - break; - } - case MTEX_BLEND_VAL: { - float[] rgbToHsv = new float[3]; - float[] colToHsv = new float[3]; - materialHelper.rgbToHsv(materialRGB[0], materialRGB[1], materialRGB[2], rgbToHsv); - materialHelper.rgbToHsv(pixelColor[0], pixelColor[1], pixelColor[2], colToHsv); - materialHelper.hsvToRgb(rgbToHsv[0], rgbToHsv[1], oneMinusFactor * rgbToHsv[2] + fac * colToHsv[2], materialRGB); - break; - } - case MTEX_BLEND_COLOR: {// FIXME: not working well for image textures (works fine for generated textures) - float[] rgbToHsv = new float[3]; - float[] colToHsv = new float[3]; - materialHelper.rgbToHsv(pixelColor[0], pixelColor[1], pixelColor[2], colToHsv); - if (colToHsv[2] != 0) { - materialHelper.rgbToHsv(materialRGB[0], materialRGB[1], materialRGB[2], rgbToHsv); - materialHelper.hsvToRgb(colToHsv[0], colToHsv[1], rgbToHsv[2], rgbToHsv); - materialRGB[0] = oneMinusFactor * materialRGB[0] + fac * rgbToHsv[0]; - materialRGB[1] = oneMinusFactor * materialRGB[1] + fac * rgbToHsv[1]; - materialRGB[2] = oneMinusFactor * materialRGB[2] + fac * rgbToHsv[2]; - } - break; - } - default: - throw new IllegalStateException("Unknown ramp type: " + type); - } - } - - @Override - public void copyBlendingData(TextureBlender textureBlender) { - if (textureBlender instanceof AbstractTextureBlender) { - flag = ((AbstractTextureBlender) textureBlender).flag; - negateTexture = ((AbstractTextureBlender) textureBlender).negateTexture; - blendType = ((AbstractTextureBlender) textureBlender).blendType; - materialColor = ((AbstractTextureBlender) textureBlender).materialColor.clone(); - color = ((AbstractTextureBlender) textureBlender).color.clone(); - blendFactor = ((AbstractTextureBlender) textureBlender).blendFactor; - } else { - LOGGER.warning("Cannot copy blending data from other types than " + this.getClass()); - } - } - - /** - * The method prepares images for blending. It generates mipmaps if one of - * the images has them defined and the other one has not. - * - * @param target - * the image where the blending result is stored - * @param source - * the image that is being read only - */ - protected void prepareImagesForBlending(Image target, Image source) { - LOGGER.fine("Generating mipmaps if needed!"); - boolean targetHasMipmaps = target == null ? false : target.getMipMapSizes() != null && target.getMipMapSizes().length > 0; - boolean sourceHasMipmaps = source == null ? false : source.getMipMapSizes() != null && source.getMipMapSizes().length > 0; - if (target != null && !targetHasMipmaps && sourceHasMipmaps) { - MipMapGenerator.generateMipMaps(target); - } else if (source != null && !sourceHasMipmaps && targetHasMipmaps) { - MipMapGenerator.generateMipMaps(source); - } - } -} diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/blending/TextureBlender.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/blending/TextureBlender.java deleted file mode 100644 index 7ba2b98f2..000000000 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/blending/TextureBlender.java +++ /dev/null @@ -1,53 +0,0 @@ -package com.jme3.scene.plugins.blender.textures.blending; - -import com.jme3.scene.plugins.blender.BlenderContext; -import com.jme3.texture.Image; - -/** - * An interface for texture blending classes (the classes that mix the texture - * pixels with the material colors). - * - * @author Marcin Roguski (Kaelthas) - */ -public interface TextureBlender { - // types of blending - int MTEX_BLEND = 0; - int MTEX_MUL = 1; - int MTEX_ADD = 2; - int MTEX_SUB = 3; - int MTEX_DIV = 4; - int MTEX_DARK = 5; - int MTEX_DIFF = 6; - int MTEX_LIGHT = 7; - int MTEX_SCREEN = 8; - int MTEX_OVERLAY = 9; - int MTEX_BLEND_HUE = 10; - int MTEX_BLEND_SAT = 11; - int MTEX_BLEND_VAL = 12; - int MTEX_BLEND_COLOR = 13; - int MTEX_NUM_BLENDTYPES = 14; - - /** - * This method blends the given texture with material color and the defined - * color in 'map to' panel. As a result of this method a new texture is - * created. The input texture is NOT. - * - * @param image - * the image we use in blending - * @param baseImage - * the texture that is underneath the current texture (its pixels - * will be used instead of material color) - * @param blenderContext - * the blender context - * @return new image that was created after the blending - */ - Image blend(Image image, Image baseImage, BlenderContext blenderContext); - - /** - * Copies blending data. Used for blending type format changing. - * - * @param textureBlender - * the blend data that should be copied - */ - void copyBlendingData(TextureBlender textureBlender); -} diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/blending/TextureBlenderAWT.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/blending/TextureBlenderAWT.java deleted file mode 100644 index 41e3e9475..000000000 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/blending/TextureBlenderAWT.java +++ /dev/null @@ -1,275 +0,0 @@ -/* - * 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.blender.textures.blending; - -import com.jme3.math.FastMath; -import com.jme3.scene.plugins.blender.BlenderContext; -import com.jme3.scene.plugins.blender.textures.TexturePixel; -import com.jme3.scene.plugins.blender.textures.io.PixelIOFactory; -import com.jme3.scene.plugins.blender.textures.io.PixelInputOutput; -import com.jme3.texture.Image; -import com.jme3.texture.Image.Format; -import com.jme3.texture.image.ColorSpace; -import com.jme3.util.BufferUtils; - -import java.nio.ByteBuffer; -import java.util.ArrayList; - -/** - *

    - * The class that is responsible for blending the following texture types: - *

    - *
      - *
    • RGBA8
    • - *
    • ABGR8
    • - *
    • BGR8
    • - *
    • RGB8
    • - *
    - * - *

    - * Not yet supported (but will be): - *

    - *
      - *
    • ARGB4444
    • - *
    • RGB10
    • - *
    • RGB111110F
    • - *
    • RGB16
    • - *
    • RGB16F
    • - *
    • RGB16F_to_RGB111110F
    • - *
    • RGB16F_to_RGB9E5
    • - *
    • RGB32F
    • - *
    • RGB565
    • - *
    • RGB5A1
    • - *
    • RGB9E5
    • - *
    • RGBA16
    • - *
    • RGBA16F
    • - *
    - * - * @author Marcin Roguski (Kaelthas) - */ -public class TextureBlenderAWT extends AbstractTextureBlender { - public TextureBlenderAWT(int flag, boolean negateTexture, int blendType, float[] materialColor, float[] color, float blendFactor) { - super(flag, negateTexture, blendType, materialColor, color, blendFactor); - } - - @Override - public Image blend(Image image, Image baseImage, BlenderContext blenderContext) { - this.prepareImagesForBlending(image, baseImage); - - float[] pixelColor = new float[] { color[0], color[1], color[2], 1.0f }; - Format format = image.getFormat(); - - PixelInputOutput basePixelIO = null, pixelReader = PixelIOFactory.getPixelIO(format); - TexturePixel basePixel = null, pixel = new TexturePixel(); - float[] materialColor = this.materialColor; - if (baseImage != null) { - basePixelIO = PixelIOFactory.getPixelIO(baseImage.getFormat()); - materialColor = new float[this.materialColor.length]; - basePixel = new TexturePixel(); - } - - int width = image.getWidth(); - int height = image.getHeight(); - int depth = image.getDepth(); - if (depth == 0) { - depth = 1; - } - int bytesPerPixel = image.getFormat().getBitsPerPixel() >> 3; - ArrayList dataArray = new ArrayList(depth); - - float[] resultPixel = new float[4]; - for (int dataLayerIndex = 0; dataLayerIndex < depth; ++dataLayerIndex) { - ByteBuffer data = image.getData(dataLayerIndex); - data.rewind(); - int imagePixelCount = data.limit() / bytesPerPixel; - ByteBuffer newData = BufferUtils.createByteBuffer(imagePixelCount * 4); - - int dataIndex = 0, x = 0, y = 0, index = 0; - while (index < data.limit()) { - // getting the proper material color if the base texture is applied - if (basePixelIO != null) { - basePixelIO.read(baseImage, dataLayerIndex, basePixel, x, y); - basePixel.toRGBA(materialColor); - ++x; - if (x >= width) { - x = 0; - ++y; - } - } - - // reading the current texture's pixel - pixelReader.read(image, dataLayerIndex, pixel, index); - index += bytesPerPixel; - pixel.toRGBA(pixelColor); - if (negateTexture) { - pixel.negate(); - } - - this.blendPixel(resultPixel, materialColor, pixelColor, blenderContext); - newData.put(dataIndex++, (byte) (resultPixel[0] * 255.0f)); - newData.put(dataIndex++, (byte) (resultPixel[1] * 255.0f)); - newData.put(dataIndex++, (byte) (resultPixel[2] * 255.0f)); - newData.put(dataIndex++, (byte) (pixelColor[3] * 255.0f)); - } - dataArray.add(newData); - } - - ColorSpace colorSpace; - if (baseImage != null) { - colorSpace = baseImage.getColorSpace() != null ? baseImage.getColorSpace() : ColorSpace.Linear; - } else { - colorSpace = image.getColorSpace(); - } - - Image result = depth > 1 ? new Image(Format.RGBA8, width, height, depth, dataArray, colorSpace) : new Image(Format.RGBA8, width, height, dataArray.get(0), colorSpace); - if (image.getMipMapSizes() != null) { - result.setMipMapSizes(image.getMipMapSizes().clone()); - } - return result; - } - - /** - * This method blends the single pixel depending on the blending type. - * - * @param result - * the result pixel - * @param materialColor - * the material color - * @param pixelColor - * the pixel color - * @param blenderContext - * the blender context - */ - protected void blendPixel(float[] result, float[] materialColor, float[] pixelColor, BlenderContext blenderContext) { - // We calculate first the importance of the texture (colFactor * texAlphaValue) - float blendFactor = this.blendFactor * pixelColor[3]; - // Then, we get the object material factor ((1 - texture importance) * matAlphaValue) - float oneMinusFactor = (1f - blendFactor) * materialColor[3]; - // Finally, we can get the final blendFactor, which is 1 - the material factor. - blendFactor = 1f - oneMinusFactor; - - // --- Compact method --- - // float blendFactor = this.blendFactor * (1f - ((1f - pixelColor[3]) * materialColor[3])); - // float oneMinusFactor = 1f - blendFactor; - - float col; - - switch (blendType) { - case MTEX_BLEND: - result[0] = blendFactor * pixelColor[0] + oneMinusFactor * materialColor[0]; - result[1] = blendFactor * pixelColor[1] + oneMinusFactor * materialColor[1]; - result[2] = blendFactor * pixelColor[2] + oneMinusFactor * materialColor[2]; - break; - case MTEX_MUL: - result[0] = (oneMinusFactor + blendFactor * materialColor[0]) * pixelColor[0]; - result[1] = (oneMinusFactor + blendFactor * materialColor[1]) * pixelColor[1]; - result[2] = (oneMinusFactor + blendFactor * materialColor[2]) * pixelColor[2]; - break; - case MTEX_DIV: - if (pixelColor[0] != 0.0) { - result[0] = (oneMinusFactor * materialColor[0] + blendFactor * materialColor[0] / pixelColor[0]) * 0.5f; - } - if (pixelColor[1] != 0.0) { - result[1] = (oneMinusFactor * materialColor[1] + blendFactor * materialColor[1] / pixelColor[1]) * 0.5f; - } - if (pixelColor[2] != 0.0) { - result[2] = (oneMinusFactor * materialColor[2] + blendFactor * materialColor[2] / pixelColor[2]) * 0.5f; - } - break; - case MTEX_SCREEN: - result[0] = 1.0f - (oneMinusFactor + blendFactor * (1.0f - materialColor[0])) * (1.0f - pixelColor[0]); - result[1] = 1.0f - (oneMinusFactor + blendFactor * (1.0f - materialColor[1])) * (1.0f - pixelColor[1]); - result[2] = 1.0f - (oneMinusFactor + blendFactor * (1.0f - materialColor[2])) * (1.0f - pixelColor[2]); - break; - case MTEX_OVERLAY: - if (materialColor[0] < 0.5f) { - result[0] = pixelColor[0] * (oneMinusFactor + 2.0f * blendFactor * materialColor[0]); - } else { - result[0] = 1.0f - (oneMinusFactor + 2.0f * blendFactor * (1.0f - materialColor[0])) * (1.0f - pixelColor[0]); - } - if (materialColor[1] < 0.5f) { - result[1] = pixelColor[1] * (oneMinusFactor + 2.0f * blendFactor * materialColor[1]); - } else { - result[1] = 1.0f - (oneMinusFactor + 2.0f * blendFactor * (1.0f - materialColor[1])) * (1.0f - pixelColor[1]); - } - if (materialColor[2] < 0.5f) { - result[2] = pixelColor[2] * (oneMinusFactor + 2.0f * blendFactor * materialColor[2]); - } else { - result[2] = 1.0f - (oneMinusFactor + 2.0f * blendFactor * (1.0f - materialColor[2])) * (1.0f - pixelColor[2]); - } - break; - case MTEX_SUB: - result[0] = materialColor[0] - blendFactor * pixelColor[0]; - result[1] = materialColor[1] - blendFactor * pixelColor[1]; - result[2] = materialColor[2] - blendFactor * pixelColor[2]; - result[0] = FastMath.clamp(result[0], 0.0f, 1.0f); - result[1] = FastMath.clamp(result[1], 0.0f, 1.0f); - result[2] = FastMath.clamp(result[2], 0.0f, 1.0f); - break; - case MTEX_ADD: - result[0] = (blendFactor * pixelColor[0] + materialColor[0]) * 0.5f; - result[1] = (blendFactor * pixelColor[1] + materialColor[1]) * 0.5f; - result[2] = (blendFactor * pixelColor[2] + materialColor[2]) * 0.5f; - break; - case MTEX_DIFF: - result[0] = oneMinusFactor * materialColor[0] + blendFactor * Math.abs(materialColor[0] - pixelColor[0]); - result[1] = oneMinusFactor * materialColor[1] + blendFactor * Math.abs(materialColor[1] - pixelColor[1]); - result[2] = oneMinusFactor * materialColor[2] + blendFactor * Math.abs(materialColor[2] - pixelColor[2]); - break; - case MTEX_DARK: - col = blendFactor * pixelColor[0]; - result[0] = col < materialColor[0] ? col : materialColor[0]; - col = blendFactor * pixelColor[1]; - result[1] = col < materialColor[1] ? col : materialColor[1]; - col = blendFactor * pixelColor[2]; - result[2] = col < materialColor[2] ? col : materialColor[2]; - break; - case MTEX_LIGHT: - col = blendFactor * pixelColor[0]; - result[0] = col > materialColor[0] ? col : materialColor[0]; - col = blendFactor * pixelColor[1]; - result[1] = col > materialColor[1] ? col : materialColor[1]; - col = blendFactor * pixelColor[2]; - result[2] = col > materialColor[2] ? col : materialColor[2]; - break; - case MTEX_BLEND_HUE: - case MTEX_BLEND_SAT: - case MTEX_BLEND_VAL: - case MTEX_BLEND_COLOR: - System.arraycopy(materialColor, 0, result, 0, 3); - this.blendHSV(blendType, result, blendFactor, pixelColor, blenderContext); - break; - default: - throw new IllegalStateException("Unknown blend type: " + blendType); - } - } -} diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/blending/TextureBlenderDDS.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/blending/TextureBlenderDDS.java deleted file mode 100644 index b40e2cc8f..000000000 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/blending/TextureBlenderDDS.java +++ /dev/null @@ -1,131 +0,0 @@ -package com.jme3.scene.plugins.blender.textures.blending; - -import com.jme3.scene.plugins.blender.BlenderContext; -import com.jme3.scene.plugins.blender.textures.TexturePixel; -import com.jme3.scene.plugins.blender.textures.io.PixelIOFactory; -import com.jme3.scene.plugins.blender.textures.io.PixelInputOutput; -import com.jme3.texture.Image; -import com.jme3.texture.Image.Format; -import com.jme3.texture.image.ColorSpace; -import com.jme3.util.BufferUtils; - -import java.nio.ByteBuffer; -import java.util.ArrayList; - -import jme3tools.converters.RGB565; - -/** - *

    - * The class that is responsible for blending the following texture types: - *

    - *
      - *
    • DXT1
    • - *
    • DXT1A
    • - *
    • DXT3
    • - *
    • DXT5
    • - *
    - * @author Marcin Roguski (Kaelthas) - */ -public class TextureBlenderDDS extends TextureBlenderAWT { - public TextureBlenderDDS(int flag, boolean negateTexture, int blendType, float[] materialColor, float[] color, float blendFactor) { - super(flag, negateTexture, blendType, materialColor, color, blendFactor); - } - - @Override - public Image blend(Image image, Image baseImage, BlenderContext blenderContext) { - this.prepareImagesForBlending(image, baseImage); - - Format format = image.getFormat(); - int width = image.getWidth(); - int height = image.getHeight(); - int depth = image.getDepth(); - if (depth == 0) { - depth = 1; - } - ArrayList dataArray = new ArrayList(depth); - - PixelInputOutput basePixelIO = null; - float[][] compressedMaterialColor = null; - TexturePixel[] baseTextureColors = null; - if (baseImage != null) { - basePixelIO = PixelIOFactory.getPixelIO(baseImage.getFormat()); - compressedMaterialColor = new float[2][4]; - baseTextureColors = new TexturePixel[] { new TexturePixel(), new TexturePixel() }; - } - - float[] resultPixel = new float[4]; - float[] pixelColor = new float[4]; - TexturePixel[] colors = new TexturePixel[] { new TexturePixel(), new TexturePixel() }; - int baseXTexelIndex = 0, baseYTexelIndex = 0; - float[] alphas = new float[] { 1, 1 }; - for (int dataLayerIndex = 0; dataLayerIndex < depth; ++dataLayerIndex) { - ByteBuffer data = image.getData(dataLayerIndex); - data.rewind(); - ByteBuffer newData = BufferUtils.createByteBuffer(data.remaining()); - while (data.hasRemaining()) { - if (format == Format.DXT3) { - long alpha = data.getLong(); - // get alpha for first and last pixel that is compressed in the texel - byte alpha0 = (byte) (alpha << 4 & 0xFF); - byte alpha1 = (byte) (alpha >> 60 & 0xFF); - alphas[0] = alpha0 >= 0 ? alpha0 / 255.0f : 1.0f - ~alpha0 / 255.0f; - alphas[1] = alpha1 >= 0 ? alpha1 / 255.0f : 1.0f - ~alpha1 / 255.0f; - newData.putLong(alpha); - } else if (format == Format.DXT5) { - byte alpha0 = data.get(); - byte alpha1 = data.get(); - alphas[0] = alpha0 >= 0 ? alpha0 / 255.0f : 1.0f - ~alpha0 / 255.0f; - alphas[1] = alpha1 >= 0 ? alpha0 / 255.0f : 1.0f - ~alpha0 / 255.0f; - newData.put(alpha0); - newData.put(alpha1); - // only read the next 6 bytes (these are alpha indexes) - newData.putInt(data.getInt()); - newData.putShort(data.getShort()); - } - int col0 = RGB565.RGB565_to_ARGB8(data.getShort()); - int col1 = RGB565.RGB565_to_ARGB8(data.getShort()); - colors[0].fromARGB8(col0); - colors[1].fromARGB8(col1); - - // compressing 16 pixels from the base texture as if they belonged to a texel - if (baseImage != null) { - // reading pixels (first and last of the 16 colors array) - basePixelIO.read(baseImage, dataLayerIndex, baseTextureColors[0], baseXTexelIndex << 2, baseYTexelIndex << 2);// first pixel - basePixelIO.read(baseImage, dataLayerIndex, baseTextureColors[1], baseXTexelIndex << 2 + 4, baseYTexelIndex << 2 + 4);// last pixel - baseTextureColors[0].toRGBA(compressedMaterialColor[0]); - baseTextureColors[1].toRGBA(compressedMaterialColor[1]); - } - - // blending colors - for (int i = 0; i < colors.length; ++i) { - if (negateTexture) { - colors[i].negate(); - } - colors[i].toRGBA(pixelColor); - pixelColor[3] = alphas[i]; - this.blendPixel(resultPixel, compressedMaterialColor != null ? compressedMaterialColor[i] : materialColor, pixelColor, blenderContext); - colors[i].fromARGB(1, resultPixel[0], resultPixel[1], resultPixel[2]); - int argb8 = colors[i].toARGB8(); - short rgb565 = RGB565.ARGB8_to_RGB565(argb8); - newData.putShort(rgb565); - } - - // just copy the remaining 4 bytes of the current texel - newData.putInt(data.getInt()); - - ++baseXTexelIndex; - if (baseXTexelIndex > image.getWidth() >> 2) { - baseXTexelIndex = 0; - ++baseYTexelIndex; - } - } - dataArray.add(newData); - } - - Image result = dataArray.size() > 1 ? new Image(format, width, height, depth, dataArray, ColorSpace.Linear) : new Image(format, width, height, dataArray.get(0), ColorSpace.Linear); - if (image.getMipMapSizes() != null) { - result.setMipMapSizes(image.getMipMapSizes().clone()); - } - return result; - } -} diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/blending/TextureBlenderFactory.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/blending/TextureBlenderFactory.java deleted file mode 100644 index f487e240d..000000000 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/blending/TextureBlenderFactory.java +++ /dev/null @@ -1,117 +0,0 @@ -/* - * 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.blender.textures.blending; - -import java.util.logging.Level; -import java.util.logging.Logger; - -import com.jme3.scene.plugins.blender.BlenderContext; -import com.jme3.texture.Image; -import com.jme3.texture.Image.Format; - -/** - * This class creates the texture blending class depending on the texture type. - * - * @author Marcin Roguski (Kaelthas) - */ -public class TextureBlenderFactory { - private static final Logger LOGGER = Logger.getLogger(TextureBlenderFactory.class.getName()); - - /** - * A blender that does not change the image. Used for none supported image types. - */ - private static final TextureBlender NON_CHANGING_BLENDER = new TextureBlender() { - @Override - public Image blend(Image image, Image baseImage, BlenderContext blenderContext) { - return image; - } - - @Override - public void copyBlendingData(TextureBlender textureBlender) { } - }; - - /** - * This method creates the blending class. - * - * @param format - * the texture format - * @return texture blending class - */ - public static TextureBlender createTextureBlender(Format format, int flag, boolean negate, int blendType, float[] materialColor, float[] color, float colfac) { - switch (format) { - case Luminance8: - case Luminance8Alpha8: - case Luminance16F: - case Luminance16FAlpha16F: - case Luminance32F: - return new TextureBlenderLuminance(flag, negate, blendType, materialColor, color, colfac); - case RGBA8: - case ABGR8: - case BGR8: - case RGB8: - case RGB111110F: - case RGB16F: - case RGB16F_to_RGB111110F: - case RGB16F_to_RGB9E5: - case RGB32F: - case RGB565: - case RGB5A1: - case RGB9E5: - case RGBA16F: - case RGBA32F: - return new TextureBlenderAWT(flag, negate, blendType, materialColor, color, colfac); - case DXT1: - case DXT1A: - case DXT3: - case DXT5: - return new TextureBlenderDDS(flag, negate, blendType, materialColor, color, colfac); - default: - LOGGER.log(Level.WARNING, "Image type not yet supported for blending: {0}. Returning a blender that does not change the texture.", format); - return NON_CHANGING_BLENDER; - } - } - - /** - * This method changes the image format in the texture blender. - * - * @param format - * the new image format - * @param textureBlender - * the texture blender that will be altered - * @return altered texture blender - */ - public static TextureBlender alterTextureType(Format format, TextureBlender textureBlender) { - TextureBlender result = TextureBlenderFactory.createTextureBlender(format, 0, false, 0, null, null, 0); - result.copyBlendingData(textureBlender); - return result; - } -} diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/blending/TextureBlenderLuminance.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/blending/TextureBlenderLuminance.java deleted file mode 100644 index 24349c809..000000000 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/blending/TextureBlenderLuminance.java +++ /dev/null @@ -1,259 +0,0 @@ -package com.jme3.scene.plugins.blender.textures.blending; - -import com.jme3.math.FastMath; -import com.jme3.scene.plugins.blender.BlenderContext; -import com.jme3.scene.plugins.blender.textures.TexturePixel; -import com.jme3.scene.plugins.blender.textures.io.PixelIOFactory; -import com.jme3.scene.plugins.blender.textures.io.PixelInputOutput; -import com.jme3.texture.Image; -import com.jme3.texture.Image.Format; -import com.jme3.texture.image.ColorSpace; -import com.jme3.util.BufferUtils; - -import java.nio.ByteBuffer; -import java.util.ArrayList; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - *

    - * The class that is responsible for blending the following texture types: - *

    - *
      - *
    • Luminance8
    • - *
    • Luminance8Alpha8
    • - *
    - *

    - * Not yet supported (but will be): - *

    - *
      - *
    • Luminance16
    • - *
    • Luminance16Alpha16
    • - *
    • Luminance16F
    • - *
    • Luminance16FAlpha16F
    • - *
    • Luminance32F
    • - *
    - * - * @author Marcin Roguski (Kaelthas) - */ -public class TextureBlenderLuminance extends AbstractTextureBlender { - private static final Logger LOGGER = Logger.getLogger(TextureBlenderLuminance.class.getName()); - - public TextureBlenderLuminance(int flag, boolean negateTexture, int blendType, float[] materialColor, float[] color, float blendFactor) { - super(flag, negateTexture, blendType, materialColor, color, blendFactor); - } - - @Override - public Image blend(Image image, Image baseImage, BlenderContext blenderContext) { - this.prepareImagesForBlending(image, baseImage); - - Format format = image.getFormat(); - PixelInputOutput basePixelIO = null; - TexturePixel basePixel = null; - float[] materialColor = this.materialColor; - if (baseImage != null) { - basePixelIO = PixelIOFactory.getPixelIO(baseImage.getFormat()); - materialColor = new float[this.materialColor.length]; - basePixel = new TexturePixel(); - } - - int width = image.getWidth(); - int height = image.getHeight(); - int depth = image.getDepth(); - if (depth == 0) { - depth = 1; - } - ArrayList dataArray = new ArrayList(depth); - - float[] resultPixel = new float[4]; - float[] tinAndAlpha = new float[2]; - for (int dataLayerIndex = 0; dataLayerIndex < depth; ++dataLayerIndex) { - ByteBuffer data = image.getData(dataLayerIndex); - data.rewind(); - ByteBuffer newData = BufferUtils.createByteBuffer(data.limit() * 4); - - int dataIndex = 0, x = 0, y = 0; - while (data.hasRemaining()) { - // getting the proper material color if the base texture is applied - if (basePixelIO != null) { - basePixelIO.read(baseImage, dataLayerIndex, basePixel, x, y); - basePixel.toRGBA(materialColor); - - ++x; - if (x >= width) { - x = 0; - ++y; - } - } - - this.getTinAndAlpha(data, format, negateTexture, tinAndAlpha); - this.blendPixel(resultPixel, materialColor, color, tinAndAlpha[0], blendFactor, blendType, blenderContext); - newData.put(dataIndex++, (byte) (resultPixel[0] * 255.0f)); - newData.put(dataIndex++, (byte) (resultPixel[1] * 255.0f)); - newData.put(dataIndex++, (byte) (resultPixel[2] * 255.0f)); - newData.put(dataIndex++, (byte) (tinAndAlpha[1] * 255.0f)); - } - dataArray.add(newData); - } - - Image result = depth > 1 ? new Image(Format.RGBA8, width, height, depth, dataArray, ColorSpace.Linear) : new Image(Format.RGBA8, width, height, dataArray.get(0), ColorSpace.Linear); - if (image.getMipMapSizes() != null) { - result.setMipMapSizes(image.getMipMapSizes().clone()); - } - return result; - } - - /** - * This method return texture intensity and alpha value. - * - * @param data - * the texture data - * @param imageFormat - * the image format - * @param neg - * indicates if the texture is negated - * @param result - * the table (2 elements) where the result is being stored - */ - protected void getTinAndAlpha(ByteBuffer data, Format imageFormat, boolean neg, float[] result) { - byte pixelValue = data.get();// at least one byte is always taken - float firstPixelValue = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - ~pixelValue / 255.0f; - switch (imageFormat) { - case Luminance8: - result[0] = neg ? 1.0f - firstPixelValue : firstPixelValue; - result[1] = 1.0f; - break; - case Luminance8Alpha8: - result[0] = neg ? 1.0f - firstPixelValue : firstPixelValue; - pixelValue = data.get(); - result[1] = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - ~pixelValue / 255.0f; - break; - case Luminance16F: - case Luminance16FAlpha16F: - case Luminance32F: - LOGGER.log(Level.WARNING, "Image type not yet supported for blending: {0}", imageFormat); - break; - default: - throw new IllegalStateException("Invalid image format type for Luminance texture blender: " + imageFormat); - } - } - - /** - * This method blends the texture with an appropriate color. - * - * @param result - * the result color (variable 'in' in blender source code) - * @param materialColor - * the texture color (variable 'out' in blender source coude) - * @param color - * the previous color (variable 'tex' in blender source code) - * @param textureIntensity - * texture intensity (variable 'fact' in blender source code) - * @param textureFactor - * texture affection factor (variable 'facg' in blender source - * code) - * @param blendtype - * the blend type - * @param blenderContext - * the blender context - */ - protected void blendPixel(float[] result, float[] materialColor, float[] color, float textureIntensity, float textureFactor, int blendtype, BlenderContext blenderContext) { - float oneMinusFactor, col; - textureIntensity *= textureFactor; - - switch (blendtype) { - case MTEX_BLEND: - oneMinusFactor = 1.0f - textureIntensity; - result[0] = textureIntensity * color[0] + oneMinusFactor * materialColor[0]; - result[1] = textureIntensity * color[1] + oneMinusFactor * materialColor[1]; - result[2] = textureIntensity * color[2] + oneMinusFactor * materialColor[2]; - break; - case MTEX_MUL: - oneMinusFactor = 1.0f - textureFactor; - result[0] = (oneMinusFactor + textureIntensity * materialColor[0]) * color[0]; - result[1] = (oneMinusFactor + textureIntensity * materialColor[1]) * color[1]; - result[2] = (oneMinusFactor + textureIntensity * materialColor[2]) * color[2]; - break; - case MTEX_DIV: - oneMinusFactor = 1.0f - textureIntensity; - if (color[0] != 0.0) { - result[0] = (oneMinusFactor * materialColor[0] + textureIntensity * materialColor[0] / color[0]) * 0.5f; - } - if (color[1] != 0.0) { - result[1] = (oneMinusFactor * materialColor[1] + textureIntensity * materialColor[1] / color[1]) * 0.5f; - } - if (color[2] != 0.0) { - result[2] = (oneMinusFactor * materialColor[2] + textureIntensity * materialColor[2] / color[2]) * 0.5f; - } - break; - case MTEX_SCREEN: - oneMinusFactor = 1.0f - textureFactor; - result[0] = 1.0f - (oneMinusFactor + textureIntensity * (1.0f - materialColor[0])) * (1.0f - color[0]); - result[1] = 1.0f - (oneMinusFactor + textureIntensity * (1.0f - materialColor[1])) * (1.0f - color[1]); - result[2] = 1.0f - (oneMinusFactor + textureIntensity * (1.0f - materialColor[2])) * (1.0f - color[2]); - break; - case MTEX_OVERLAY: - oneMinusFactor = 1.0f - textureFactor; - if (materialColor[0] < 0.5f) { - result[0] = color[0] * (oneMinusFactor + 2.0f * textureIntensity * materialColor[0]); - } else { - result[0] = 1.0f - (oneMinusFactor + 2.0f * textureIntensity * (1.0f - materialColor[0])) * (1.0f - color[0]); - } - if (materialColor[1] < 0.5f) { - result[1] = color[1] * (oneMinusFactor + 2.0f * textureIntensity * materialColor[1]); - } else { - result[1] = 1.0f - (oneMinusFactor + 2.0f * textureIntensity * (1.0f - materialColor[1])) * (1.0f - color[1]); - } - if (materialColor[2] < 0.5f) { - result[2] = color[2] * (oneMinusFactor + 2.0f * textureIntensity * materialColor[2]); - } else { - result[2] = 1.0f - (oneMinusFactor + 2.0f * textureIntensity * (1.0f - materialColor[2])) * (1.0f - color[2]); - } - break; - case MTEX_SUB: - result[0] = materialColor[0] - textureIntensity * color[0]; - result[1] = materialColor[1] - textureIntensity * color[1]; - result[2] = materialColor[2] - textureIntensity * color[2]; - result[0] = FastMath.clamp(result[0], 0.0f, 1.0f); - result[1] = FastMath.clamp(result[1], 0.0f, 1.0f); - result[2] = FastMath.clamp(result[2], 0.0f, 1.0f); - break; - case MTEX_ADD: - result[0] = (textureIntensity * color[0] + materialColor[0]) * 0.5f; - result[1] = (textureIntensity * color[1] + materialColor[1]) * 0.5f; - result[2] = (textureIntensity * color[2] + materialColor[2]) * 0.5f; - break; - case MTEX_DIFF: - oneMinusFactor = 1.0f - textureIntensity; - result[0] = oneMinusFactor * materialColor[0] + textureIntensity * Math.abs(materialColor[0] - color[0]); - result[1] = oneMinusFactor * materialColor[1] + textureIntensity * Math.abs(materialColor[1] - color[1]); - result[2] = oneMinusFactor * materialColor[2] + textureIntensity * Math.abs(materialColor[2] - color[2]); - break; - case MTEX_DARK: - col = textureIntensity * color[0]; - result[0] = col < materialColor[0] ? col : materialColor[0]; - col = textureIntensity * color[1]; - result[1] = col < materialColor[1] ? col : materialColor[1]; - col = textureIntensity * color[2]; - result[2] = col < materialColor[2] ? col : materialColor[2]; - break; - case MTEX_LIGHT: - col = textureIntensity * color[0]; - result[0] = col > materialColor[0] ? col : materialColor[0]; - col = textureIntensity * color[1]; - result[1] = col > materialColor[1] ? col : materialColor[1]; - col = textureIntensity * color[2]; - result[2] = col > materialColor[2] ? col : materialColor[2]; - break; - case MTEX_BLEND_HUE: - case MTEX_BLEND_SAT: - case MTEX_BLEND_VAL: - case MTEX_BLEND_COLOR: - System.arraycopy(materialColor, 0, result, 0, 3); - this.blendHSV(blendtype, result, textureIntensity, color, blenderContext); - break; - default: - throw new IllegalStateException("Unknown blend type: " + blendtype); - } - } -} diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/generating/NoiseGenerator.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/generating/NoiseGenerator.java deleted file mode 100644 index 7847f255f..000000000 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/generating/NoiseGenerator.java +++ /dev/null @@ -1,814 +0,0 @@ -/* - * Copyright (c) 2009-2020 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.blender.textures.generating; - -import java.io.IOException; -import java.io.InputStream; -import java.io.ObjectInputStream; -import java.util.HashMap; -import java.util.Map; -import java.util.logging.Level; -import java.util.logging.Logger; - -import com.jme3.math.FastMath; -import com.jme3.scene.plugins.blender.textures.generating.TextureGeneratorMusgrave.MusgraveData; - -/** - * This generator is responsible for creating various noises used to create - * generated textures loaded from blender. - * It is only used by TextureHelper. - * Take note that these functions are not thread safe. - * @author Marcin Roguski (Kaelthas) - */ -/* package */class NoiseGenerator { - private static final Logger LOGGER = Logger.getLogger(NoiseGenerator.class.getName()); - - // tex->stype - protected static final int TEX_PLASTIC = 0; - protected static final int TEX_WALLIN = 1; - protected static final int TEX_WALLOUT = 2; - - // musgrave stype - protected static final int TEX_MFRACTAL = 0; - protected static final int TEX_RIDGEDMF = 1; - protected static final int TEX_HYBRIDMF = 2; - protected static final int TEX_FBM = 3; - protected static final int TEX_HTERRAIN = 4; - - // keyblock->type - protected static final int KEY_LINEAR = 0; - protected static final int KEY_CARDINAL = 1; - protected static final int KEY_BSPLINE = 2; - - // CONSTANTS (read from file) - protected static float[] hashpntf; - protected static short[] hash; - protected static float[] hashvectf; - protected static short[] p; - protected static float[][] g; - - /** - * Constructor. Loads the constants needed for computations. They are exactly like the ones the blender uses. Each - * deriving class should override this method and load its own constraints. - */ - public NoiseGenerator() { - LOGGER.fine("Loading noise constants."); - InputStream is = NoiseGenerator.class.getResourceAsStream("noiseconstants.dat"); - try { - ObjectInputStream ois = new ObjectInputStream(is); - hashpntf = (float[]) ois.readObject(); - hash = (short[]) ois.readObject(); - hashvectf = (float[]) ois.readObject(); - p = (short[]) ois.readObject(); - g = (float[][]) ois.readObject(); - } catch (IOException e) { - LOGGER.log(Level.SEVERE, e.getLocalizedMessage(), e); - } catch (ClassNotFoundException e) { - assert false : "Constants' classes should be arrays of primitive types, so they are ALWAYS known!"; - } finally { - if (is != null) { - try { - is.close(); - } catch (IOException e) { - LOGGER.log(Level.WARNING, e.getLocalizedMessage()); - } - } - } - } - - protected static Map noiseFunctions = new HashMap(); - static { - noiseFunctions.put(Integer.valueOf(0), new NoiseFunction() { - // originalBlenderNoise - @Override - public float execute(float x, float y, float z) { - return NoiseFunctions.originalBlenderNoise(x, y, z); - } - - @Override - public float executeSigned(float x, float y, float z) { - return 2.0f * NoiseFunctions.originalBlenderNoise(x, y, z) - 1.0f; - } - }); - noiseFunctions.put(Integer.valueOf(1), new NoiseFunction() { - // orgPerlinNoise - @Override - public float execute(float x, float y, float z) { - return 0.5f + 0.5f * NoiseFunctions.noise3Perlin(x, y, z); - } - - @Override - public float executeSigned(float x, float y, float z) { - return NoiseFunctions.noise3Perlin(x, y, z); - } - }); - noiseFunctions.put(Integer.valueOf(2), new NoiseFunction() { - // newPerlin - @Override - public float execute(float x, float y, float z) { - return 0.5f + 0.5f * NoiseFunctions.newPerlin(x, y, z); - } - - @Override - public float executeSigned(float x, float y, float z) { - return this.execute(x, y, z); - } - }); - noiseFunctions.put(Integer.valueOf(3), new NoiseFunction() { - private final float[] da = new float[4]; - private final float[] pa = new float[12]; - - // voronoi_F1 - @Override - public float execute(float x, float y, float z) { - NoiseFunctions.voronoi(x, y, z, da, pa, 1, NATURAL_DISTANCE_FUNCTION); - return da[0]; - } - - @Override - public float executeSigned(float x, float y, float z) { - NoiseFunctions.voronoi(x, y, z, da, pa, 1, NATURAL_DISTANCE_FUNCTION); - return 2.0f * da[0] - 1.0f; - } - }); - noiseFunctions.put(Integer.valueOf(4), new NoiseFunction() { - private final float[] da = new float[4]; - private final float[] pa = new float[12]; - - // voronoi_F2 - @Override - public float execute(float x, float y, float z) { - NoiseFunctions.voronoi(x, y, z, da, pa, 1, NATURAL_DISTANCE_FUNCTION); - return da[1]; - } - - @Override - public float executeSigned(float x, float y, float z) { - NoiseFunctions.voronoi(x, y, z, da, pa, 1, NATURAL_DISTANCE_FUNCTION); - return 2.0f * da[1] - 1.0f; - } - }); - noiseFunctions.put(Integer.valueOf(5), new NoiseFunction() { - private final float[] da = new float[4]; - private final float[] pa = new float[12]; - - // voronoi_F3 - @Override - public float execute(float x, float y, float z) { - NoiseFunctions.voronoi(x, y, z, da, pa, 1, NATURAL_DISTANCE_FUNCTION); - return da[2]; - } - - @Override - public float executeSigned(float x, float y, float z) { - NoiseFunctions.voronoi(x, y, z, da, pa, 1, NATURAL_DISTANCE_FUNCTION); - return 2.0f * da[2] - 1.0f; - } - }); - noiseFunctions.put(Integer.valueOf(6), new NoiseFunction() { - private final float[] da = new float[4]; - private final float[] pa = new float[12]; - - // voronoi_F4 - @Override - public float execute(float x, float y, float z) { - NoiseFunctions.voronoi(x, y, z, da, pa, 1, NATURAL_DISTANCE_FUNCTION); - return da[3]; - } - - @Override - public float executeSigned(float x, float y, float z) { - NoiseFunctions.voronoi(x, y, z, da, pa, 1, NATURAL_DISTANCE_FUNCTION); - return 2.0f * da[3] - 1.0f; - } - }); - noiseFunctions.put(Integer.valueOf(7), new NoiseFunction() { - private final float[] da = new float[4]; - private final float[] pa = new float[12]; - - // voronoi_F1F2 - @Override - public float execute(float x, float y, float z) { - NoiseFunctions.voronoi(x, y, z, da, pa, 1, NATURAL_DISTANCE_FUNCTION); - return da[1] - da[0]; - } - - @Override - public float executeSigned(float x, float y, float z) { - NoiseFunctions.voronoi(x, y, z, da, pa, 1, NATURAL_DISTANCE_FUNCTION); - return 2.0f * (da[1] - da[0]) - 1.0f; - } - }); - noiseFunctions.put(Integer.valueOf(8), new NoiseFunction() { - private final NoiseFunction voronoiF1F2NoiseFunction = noiseFunctions.get(Integer.valueOf(7)); - - // voronoi_Cr - @Override - public float execute(float x, float y, float z) { - float t = 10 * voronoiF1F2NoiseFunction.execute(x, y, z); - return t > 1.0f ? 1.0f : t; - } - - @Override - public float executeSigned(float x, float y, float z) { - float t = 10.0f * voronoiF1F2NoiseFunction.execute(x, y, z); - return t > 1.0f ? 1.0f : 2.0f * t - 1.0f; - } - }); - noiseFunctions.put(Integer.valueOf(14), new NoiseFunction() { - // cellNoise - @Override - public float execute(float x, float y, float z) { - int xi = (int) FastMath.floor(x); - int yi = (int) FastMath.floor(y); - int zi = (int) FastMath.floor(z); - long n = xi + yi * 1301 + zi * 314159; - n ^= n << 13; - return (n * (n * n * 15731 + 789221) + 1376312589) / 4294967296.0f; - } - - @Override - public float executeSigned(float x, float y, float z) { - return 2.0f * this.execute(x, y, z) - 1.0f; - } - }); - } - /** Distance metrics for voronoi. e parameter only used in Minkovsky. */ - protected static Map distanceFunctions = new HashMap(); - private static DistanceFunction NATURAL_DISTANCE_FUNCTION; // often used in noise functions, se I store it here to avoid fetching it every time - static { - distanceFunctions.put(Integer.valueOf(0), new DistanceFunction() { - // real distance - @Override - public float execute(float x, float y, float z, float e) { - return (float) Math.sqrt(x * x + y * y + z * z); - } - }); - distanceFunctions.put(Integer.valueOf(1), new DistanceFunction() { - // distance squared - @Override - public float execute(float x, float y, float z, float e) { - return x * x + y * y + z * z; - } - }); - distanceFunctions.put(Integer.valueOf(2), new DistanceFunction() { - // manhattan/taxicab/cityblock distance - @Override - public float execute(float x, float y, float z, float e) { - return FastMath.abs(x) + FastMath.abs(y) + FastMath.abs(z); - } - }); - distanceFunctions.put(Integer.valueOf(3), new DistanceFunction() { - // Chebychev - @Override - public float execute(float x, float y, float z, float e) { - x = FastMath.abs(x); - y = FastMath.abs(y); - z = FastMath.abs(z); - float t = x > y ? x : y; - return z > t ? z : t; - } - }); - distanceFunctions.put(Integer.valueOf(4), new DistanceFunction() { - // Minkovsky, preset exponent 0.5 (MinkovskyH) - @Override - public float execute(float x, float y, float z, float e) { - float d = (float) (Math.sqrt(FastMath.abs(x)) + Math.sqrt(FastMath.abs(y)) + Math.sqrt(FastMath.abs(z))); - return d * d; - } - }); - distanceFunctions.put(Integer.valueOf(5), new DistanceFunction() { - // Minkovsky, preset exponent 0.25 (Minkovsky4) - @Override - public float execute(float x, float y, float z, float e) { - x *= x; - y *= y; - z *= z; - return (float) Math.sqrt(Math.sqrt(x * x + y * y + z * z)); - } - }); - distanceFunctions.put(Integer.valueOf(6), new DistanceFunction() { - // Minkovsky, general case - @Override - public float execute(float x, float y, float z, float e) { - return (float) Math.pow(Math.pow(FastMath.abs(x), e) + Math.pow(FastMath.abs(y), e) + Math.pow(FastMath.abs(z), e), 1.0f / e); - } - }); - NATURAL_DISTANCE_FUNCTION = distanceFunctions.get(Integer.valueOf(0)); - } - - protected static Map musgraveFunctions = new HashMap(); - static { - musgraveFunctions.put(Integer.valueOf(TEX_MFRACTAL), new MusgraveFunction() { - - @Override - public float execute(MusgraveData musgraveData, float x, float y, float z) { - float rmd, value = 1.0f, pwr = 1.0f, pwHL = (float) Math.pow(musgraveData.lacunarity, -musgraveData.h); - - for (int i = 0; i < (int) musgraveData.octaves; ++i) { - value *= pwr * musgraveData.noiseFunction.executeSigned(x, y, z) + 1.0f; - pwr *= pwHL; - x *= musgraveData.lacunarity; - y *= musgraveData.lacunarity; - z *= musgraveData.lacunarity; - } - rmd = musgraveData.octaves - FastMath.floor(musgraveData.octaves); - if (rmd != 0.0f) { - value *= rmd * musgraveData.noiseFunction.executeSigned(x, y, z) * pwr + 1.0f; - } - return value; - } - }); - musgraveFunctions.put(Integer.valueOf(TEX_RIDGEDMF), new MusgraveFunction() { - - @Override - public float execute(MusgraveData musgraveData, float x, float y, float z) { - float result, signal, weight; - float pwHL = (float) Math.pow(musgraveData.lacunarity, -musgraveData.h); - float pwr = pwHL; - - signal = musgraveData.offset - FastMath.abs(musgraveData.noiseFunction.executeSigned(x, y, z)); - signal *= signal; - result = signal; - weight = 1.0f; - - for (int i = 1; i < (int) musgraveData.octaves; ++i) { - x *= musgraveData.lacunarity; - y *= musgraveData.lacunarity; - z *= musgraveData.lacunarity; - weight = signal * musgraveData.gain; - if (weight > 1.0f) { - weight = 1.0f; - } else if (weight < 0.0) { - weight = 0.0f; - } - signal = musgraveData.offset - FastMath.abs(musgraveData.noiseFunction.executeSigned(x, y, z)); - signal *= signal; - signal *= weight; - result += signal * pwr; - pwr *= pwHL; - } - return result; - } - }); - musgraveFunctions.put(Integer.valueOf(TEX_HYBRIDMF), new MusgraveFunction() { - - @Override - public float execute(MusgraveData musgraveData, float x, float y, float z) { - float result, signal, weight, rmd; - float pwHL = (float) Math.pow(musgraveData.lacunarity, -musgraveData.h); - float pwr = pwHL; - - result = musgraveData.noiseFunction.executeSigned(x, y, z) + musgraveData.offset; - weight = musgraveData.gain * result; - x *= musgraveData.lacunarity; - y *= musgraveData.lacunarity; - z *= musgraveData.lacunarity; - - for (int i = 1; weight > 0.001f && i < (int) musgraveData.octaves; ++i) { - if (weight > 1.0f) { - weight = 1.0f; - } - signal = (musgraveData.noiseFunction.executeSigned(x, y, z) + musgraveData.offset) * pwr; - pwr *= pwHL; - result += weight * signal; - weight *= musgraveData.gain * signal; - x *= musgraveData.lacunarity; - y *= musgraveData.lacunarity; - z *= musgraveData.lacunarity; - } - - rmd = musgraveData.octaves - FastMath.floor(musgraveData.octaves); - if (rmd != 0.0f) { - result += rmd * (musgraveData.noiseFunction.executeSigned(x, y, z) + musgraveData.offset) * pwr; - } - return result; - } - }); - musgraveFunctions.put(Integer.valueOf(TEX_FBM), new MusgraveFunction() { - - @Override - public float execute(MusgraveData musgraveData, float x, float y, float z) { - float rmd, value = 0.0f, pwr = 1.0f, pwHL = (float) Math.pow(musgraveData.lacunarity, -musgraveData.h); - - for (int i = 0; i < (int) musgraveData.octaves; ++i) { - value += musgraveData.noiseFunction.executeSigned(x, y, z) * pwr; - pwr *= pwHL; - x *= musgraveData.lacunarity; - y *= musgraveData.lacunarity; - z *= musgraveData.lacunarity; - } - - rmd = musgraveData.octaves - FastMath.floor(musgraveData.octaves); - if (rmd != 0.f) { - value += rmd * musgraveData.noiseFunction.executeSigned(x, y, z) * pwr; - } - return value; - } - }); - musgraveFunctions.put(Integer.valueOf(TEX_HTERRAIN), new MusgraveFunction() { - - @Override - public float execute(MusgraveData musgraveData, float x, float y, float z) { - float value, increment, rmd; - float pwHL = (float) Math.pow(musgraveData.lacunarity, -musgraveData.h); - float pwr = pwHL; - - value = musgraveData.offset + musgraveData.noiseFunction.executeSigned(x, y, z); - x *= musgraveData.lacunarity; - y *= musgraveData.lacunarity; - z *= musgraveData.lacunarity; - - for (int i = 1; i < (int) musgraveData.octaves; ++i) { - increment = (musgraveData.noiseFunction.executeSigned(x, y, z) + musgraveData.offset) * pwr * value; - value += increment; - pwr *= pwHL; - x *= musgraveData.lacunarity; - y *= musgraveData.lacunarity; - z *= musgraveData.lacunarity; - } - - rmd = musgraveData.octaves - FastMath.floor(musgraveData.octaves); - if (rmd != 0.0) { - increment = (musgraveData.noiseFunction.executeSigned(x, y, z) + musgraveData.offset) * pwr * value; - value += rmd * increment; - } - return value; - } - }); - } - - public static class NoiseFunctions { - public static float noise(float x, float y, float z, float noiseSize, int noiseDepth, NoiseFunction noiseFunction, boolean isHard) { - if (noiseSize != 0.0) { - noiseSize = 1.0f / noiseSize; - x *= noiseSize; - y *= noiseSize; - z *= noiseSize; - } - float result = noiseFunction.execute(x, y, z); - return isHard ? FastMath.abs(2.0f * result - 1.0f) : result; - } - - public static float turbulence(float x, float y, float z, float noiseSize, int noiseDepth, NoiseFunction noiseFunction, boolean isHard) { - if (noiseSize != 0.0) { - noiseSize = 1.0f / noiseSize; - x *= noiseSize; - y *= noiseSize; - z *= noiseSize; - } - - float sum = 0, t, amp = 1, fscale = 1; - for (int i = 0; i <= noiseDepth; ++i, amp *= 0.5f, fscale *= 2f) { - t = noiseFunction.execute(fscale * x, fscale * y, fscale * z); - if (isHard) { - t = FastMath.abs(2.0f * t - 1.0f); - } - sum += t * amp; - } - - sum *= (1 << noiseDepth) / (float) ((1 << noiseDepth + 1) - 1); - return sum; - } - - private static final float[] voronoiP = new float[3]; - - public static void voronoi(float x, float y, float z, float[] da, float[] pa, float distanceExponent, DistanceFunction distanceFunction) { - float xd, yd, zd, d; - - int xi = (int) FastMath.floor(x); - int yi = (int) FastMath.floor(y); - int zi = (int) FastMath.floor(z); - da[0] = da[1] = da[2] = da[3] = Float.MAX_VALUE;// 1e10f; - for (int i = xi - 1; i <= xi + 1; ++i) { - for (int j = yi - 1; j <= yi + 1; ++j) { - for (int k = zi - 1; k <= zi + 1; ++k) { - NoiseMath.hash(i, j, k, voronoiP); - xd = x - (voronoiP[0] + i); - yd = y - (voronoiP[1] + j); - zd = z - (voronoiP[2] + k); - d = distanceFunction.execute(xd, yd, zd, distanceExponent); - if (d < da[0]) { - da[3] = da[2]; - da[2] = da[1]; - da[1] = da[0]; - da[0] = d; - pa[9] = pa[6]; - pa[10] = pa[7]; - pa[11] = pa[8]; - pa[6] = pa[3]; - pa[7] = pa[4]; - pa[8] = pa[5]; - pa[3] = pa[0]; - pa[4] = pa[1]; - pa[5] = pa[2]; - pa[0] = voronoiP[0] + i; - pa[1] = voronoiP[1] + j; - pa[2] = voronoiP[2] + k; - } else if (d < da[1]) { - da[3] = da[2]; - da[2] = da[1]; - da[1] = d; - pa[9] = pa[6]; - pa[10] = pa[7]; - pa[11] = pa[8]; - pa[6] = pa[3]; - pa[7] = pa[4]; - pa[8] = pa[5]; - pa[3] = voronoiP[0] + i; - pa[4] = voronoiP[1] + j; - pa[5] = voronoiP[2] + k; - } else if (d < da[2]) { - da[3] = da[2]; - da[2] = d; - pa[9] = pa[6]; - pa[10] = pa[7]; - pa[11] = pa[8]; - pa[6] = voronoiP[0] + i; - pa[7] = voronoiP[1] + j; - pa[8] = voronoiP[2] + k; - } else if (d < da[3]) { - da[3] = d; - pa[9] = voronoiP[0] + i; - pa[10] = voronoiP[1] + j; - pa[11] = voronoiP[2] + k; - } - } - } - } - } - - // instead of adding another permutation array, just use hash table defined above - public static float newPerlin(float x, float y, float z) { - int A, AA, AB, B, BA, BB; - float floorX = FastMath.floor(x), floorY = FastMath.floor(y), floorZ = FastMath.floor(z); - int intX = (int) floorX & 0xFF, intY = (int) floorY & 0xFF, intZ = (int) floorZ & 0xFF; - x -= floorX; - y -= floorY; - z -= floorZ; - // computing fading curves - floorX = NoiseMath.npfade(x); - floorY = NoiseMath.npfade(y); - floorZ = NoiseMath.npfade(z); - A = hash[intX] + intY; - AA = hash[A] + intZ; - AB = hash[A + 1] + intZ; - B = hash[intX + 1] + intY; - BA = hash[B] + intZ; - BB = hash[B + 1] + intZ; - return NoiseMath.lerp(floorZ, NoiseMath.lerp(floorY, NoiseMath.lerp(floorX, NoiseMath.grad(hash[AA], x, y, z), NoiseMath.grad(hash[BA], x - 1, y, z)), NoiseMath.lerp(floorX, NoiseMath.grad(hash[AB], x, y - 1, z), NoiseMath.grad(hash[BB], x - 1, y - 1, z))), - NoiseMath.lerp(floorY, NoiseMath.lerp(floorX, NoiseMath.grad(hash[AA + 1], x, y, z - 1), NoiseMath.grad(hash[BA + 1], x - 1, y, z - 1)), NoiseMath.lerp(floorX, NoiseMath.grad(hash[AB + 1], x, y - 1, z - 1), NoiseMath.grad(hash[BB + 1], x - 1, y - 1, z - 1)))); - } - - public static float noise3Perlin(float x, float y, float z) { - float t = x + 10000.0f; - int bx0 = (int) t & 0xFF; - int bx1 = bx0 + 1 & 0xFF; - float rx0 = t - (int) t; - float rx1 = rx0 - 1.0f; - - t = y + 10000.0f; - int by0 = (int) t & 0xFF; - int by1 = by0 + 1 & 0xFF; - float ry0 = t - (int) t; - float ry1 = ry0 - 1.0f; - - t = z + 10000.0f; - int bz0 = (int) t & 0xFF; - int bz1 = bz0 + 1 & 0xFF; - float rz0 = t - (int) t; - float rz1 = rz0 - 1.0f; - - int i = p[bx0]; - int j = p[bx1]; - - int b00 = p[i + by0]; - int b10 = p[j + by0]; - int b01 = p[i + by1]; - int b11 = p[j + by1]; - - float sx = NoiseMath.surve(rx0); - float sy = NoiseMath.surve(ry0); - float sz = NoiseMath.surve(rz0); - - float[] q = g[b00 + bz0]; - float u = NoiseMath.at(rx0, ry0, rz0, q); - q = g[b10 + bz0]; - float v = NoiseMath.at(rx1, ry0, rz0, q); - float a = NoiseMath.lerp(sx, u, v); - - q = g[b01 + bz0]; - u = NoiseMath.at(rx0, ry1, rz0, q); - q = g[b11 + bz0]; - v = NoiseMath.at(rx1, ry1, rz0, q); - float b = NoiseMath.lerp(sx, u, v); - - float c = NoiseMath.lerp(sy, a, b); - - q = g[b00 + bz1]; - u = NoiseMath.at(rx0, ry0, rz1, q); - q = g[b10 + bz1]; - v = NoiseMath.at(rx1, ry0, rz1, q); - a = NoiseMath.lerp(sx, u, v); - - q = g[b01 + bz1]; - u = NoiseMath.at(rx0, ry1, rz1, q); - q = g[b11 + bz1]; - v = NoiseMath.at(rx1, ry1, rz1, q); - b = NoiseMath.lerp(sx, u, v); - - float d = NoiseMath.lerp(sy, a, b); - return 1.5f * NoiseMath.lerp(sz, c, d); - } - - private static final float[] cn = new float[8]; - private static final int[] b1 = new int[8]; - private static final int[] b2 = new int[2]; - private static final float[] xFactor = new float[8]; - private static final float[] yFactor = new float[8]; - private static final float[] zFactor = new float[8]; - - public static float originalBlenderNoise(float x, float y, float z) { - float n = 0.5f; - - int ix = (int) FastMath.floor(x); - int iy = (int) FastMath.floor(y); - int iz = (int) FastMath.floor(z); - - float ox = x - ix; - float oy = y - iy; - float oz = z - iz; - - float jx = ox - 1; - float jy = oy - 1; - float jz = oz - 1; - - float cn1 = ox * ox; - float cn2 = oy * oy; - float cn3 = oz * oz; - float cn4 = jx * jx; - float cn5 = jy * jy; - float cn6 = jz * jz; - - cn1 = 1.0f - 3.0f * cn1 + 2.0f * cn1 * ox; - cn2 = 1.0f - 3.0f * cn2 + 2.0f * cn2 * oy; - cn3 = 1.0f - 3.0f * cn3 + 2.0f * cn3 * oz; - cn4 = 1.0f - 3.0f * cn4 - 2.0f * cn4 * jx; - cn5 = 1.0f - 3.0f * cn5 - 2.0f * cn5 * jy; - cn6 = 1.0f - 3.0f * cn6 - 2.0f * cn6 * jz; - - cn[0] = cn1 * cn2 * cn3; - cn[1] = cn1 * cn2 * cn6; - cn[2] = cn1 * cn5 * cn3; - cn[3] = cn1 * cn5 * cn6; - cn[4] = cn4 * cn2 * cn3; - cn[5] = cn4 * cn2 * cn6; - cn[6] = cn4 * cn5 * cn3; - cn[7] = cn4 * cn5 * cn6; - - b1[0] = b1[1] = hash[hash[ix & 0xFF] + (iy & 0xFF)]; - b1[2] = b1[3] = hash[hash[ix & 0xFF] + (iy + 1 & 0xFF)]; - b1[4] = b1[5] = hash[hash[ix + 1 & 0xFF] + (iy & 0xFF)]; - b1[6] = b1[7] = hash[hash[ix + 1 & 0xFF] + (iy + 1 & 0xFF)]; - - b2[0] = iz & 0xFF; - b2[1] = iz + 1 & 0xFF; - - xFactor[0] = xFactor[1] = xFactor[2] = xFactor[3] = ox; - xFactor[4] = xFactor[5] = xFactor[6] = xFactor[7] = jx; - yFactor[0] = yFactor[1] = yFactor[4] = yFactor[5] = oy; - yFactor[2] = yFactor[3] = yFactor[6] = yFactor[7] = jy; - zFactor[0] = zFactor[2] = zFactor[4] = zFactor[6] = oz; - zFactor[1] = zFactor[3] = zFactor[5] = zFactor[7] = jz; - - for (int i = 0; i < cn.length; ++i) { - int hIndex = 3 * hash[b1[i] + b2[i % 2]]; - n += cn[i] * (hashvectf[hIndex] * xFactor[i] + hashvectf[hIndex + 1] * yFactor[i] + hashvectf[hIndex + 2] * zFactor[i]); - } - - if (n < 0.0f) { - n = 0.0f; - } else if (n > 1.0f) { - n = 1.0f; - } - return n; - } - } - - /** - * This class is abstract to the noise functions computations. It has two methods. One calculates the Signed (with - * 'S' at the end) and the other Unsigned value. - * @author Marcin Roguski (Kaelthas) - */ - interface NoiseFunction { - - /** - * This method calculates the unsigned value of the noise. - * @param x - * the x texture coordinate - * @param y - * the y texture coordinate - * @param z - * the z texture coordinate - * @return value of the noise - */ - float execute(float x, float y, float z); - - /** - * This method calculates the signed value of the noise. - * @param x - * the x texture coordinate - * @param y - * the y texture coordinate - * @param z - * the z texture coordinate - * @return value of the noise - */ - float executeSigned(float x, float y, float z); - } - - public static class NoiseMath { - public static float lerp(float t, float a, float b) { - return a + t * (b - a); - } - - public static float npfade(float t) { - return t * t * t * (t * (t * 6.0f - 15.0f) + 10.0f); - } - - public static float grad(int hash, float x, float y, float z) { - int h = hash & 0x0F; - float u = h < 8 ? x : y, v = h < 4 ? y : h == 12 || h == 14 ? x : z; - return ((h & 1) == 0 ? u : -u) + ((h & 2) == 0 ? v : -v); - } - - public static float surve(float t) { - return t * t * (3.0f - 2.0f * t); - } - - public static float at(float x, float y, float z, float[] q) { - return x * q[0] + y * q[1] + z * q[2]; - } - - public static void hash(int x, int y, int z, float[] result) { - result[0] = hashpntf[3 * hash[hash[hash[z & 0xFF] + y & 0xFF] + x & 0xFF]]; - result[1] = hashpntf[3 * hash[hash[hash[z & 0xFF] + y & 0xFF] + x & 0xFF] + 1]; - result[2] = hashpntf[3 * hash[hash[hash[z & 0xFF] + y & 0xFF] + x & 0xFF] + 2]; - } - } - - /** - * This interface is used for distance calculation classes. Distance metrics for voronoi. e parameter only used in - * Minkovsky. - */ - interface DistanceFunction { - - /** - * This method calculates the distance for voronoi algorithms. - * @param x - * the x coordinate - * @param y - * the y coordinate - * @param z - * the z coordinate - * @param e - * this parameter used in Monkovsky (no idea what it really is ;) - * @return - */ - float execute(float x, float y, float z, float e); - } - - interface MusgraveFunction { - - float execute(MusgraveData musgraveData, float x, float y, float z); - } -} diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/generating/TextureGenerator.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/generating/TextureGenerator.java deleted file mode 100644 index e459a0dfb..000000000 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/generating/TextureGenerator.java +++ /dev/null @@ -1,128 +0,0 @@ -/* - * 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.blender.textures.generating; - -import com.jme3.scene.plugins.blender.BlenderContext; -import com.jme3.scene.plugins.blender.file.Structure; -import com.jme3.scene.plugins.blender.textures.ColorBand; -import com.jme3.scene.plugins.blender.textures.TexturePixel; -import com.jme3.texture.Image.Format; - -/** - * This class is a base class for texture generators. - * @author Marcin Roguski (Kaelthas) - */ -public abstract class TextureGenerator { - protected NoiseGenerator noiseGenerator; - protected int flag; - protected float[][] colorBand; - protected BrightnessAndContrastData bacd; - protected Format imageFormat; - - public TextureGenerator(NoiseGenerator noiseGenerator, Format imageFormat) { - this.noiseGenerator = noiseGenerator; - this.imageFormat = imageFormat; - } - - public Format getImageFormat() { - return imageFormat; - } - - public void readData(Structure tex, BlenderContext blenderContext) { - flag = ((Number) tex.getFieldValue("flag")).intValue(); - colorBand = new ColorBand(tex, blenderContext).computeValues(); - bacd = new BrightnessAndContrastData(tex); - if (colorBand != null) { - imageFormat = Format.RGBA8; - } - } - - public abstract void getPixel(TexturePixel pixel, float x, float y, float z); - - /** - * This method applies brightness and contrast for RGB textures. - */ - protected void applyBrightnessAndContrast(BrightnessAndContrastData bacd, TexturePixel texres) { - texres.red = (texres.red - 0.5f) * bacd.contrast + bacd.brightness; - if (texres.red < 0.0f) { - texres.red = 0.0f; - } - texres.green = (texres.green - 0.5f) * bacd.contrast + bacd.brightness; - if (texres.green < 0.0f) { - texres.green = 0.0f; - } - texres.blue = (texres.blue - 0.5f) * bacd.contrast + bacd.brightness; - if (texres.blue < 0.0f) { - texres.blue = 0.0f; - } - } - - /** - * This method applies brightness and contrast for Luminance textures. - * @param texres - * @param contrast - * @param brightness - */ - protected void applyBrightnessAndContrast(TexturePixel texres, float contrast, float brightness) { - texres.intensity = (texres.intensity - 0.5f) * contrast + brightness; - if (texres.intensity < 0.0f) { - texres.intensity = 0.0f; - } else if (texres.intensity > 1.0f) { - texres.intensity = 1.0f; - } - } - - /** - * This class contains brightness and contrast data. - * @author Marcin Roguski (Kaelthas) - */ - protected static class BrightnessAndContrastData { - public final float contrast; - public final float brightness; - public final float rFactor; - public final float gFactor; - public final float bFactor; - - /** - * Constructor reads the required data from the given structure. - * @param tex - * texture structure - */ - public BrightnessAndContrastData(Structure tex) { - contrast = ((Number) tex.getFieldValue("contrast")).floatValue(); - brightness = ((Number) tex.getFieldValue("bright")).floatValue() - 0.5f; - rFactor = ((Number) tex.getFieldValue("rfac")).floatValue(); - gFactor = ((Number) tex.getFieldValue("gfac")).floatValue(); - bFactor = ((Number) tex.getFieldValue("bfac")).floatValue(); - } - } -} diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/generating/TextureGeneratorBlend.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/generating/TextureGeneratorBlend.java deleted file mode 100644 index f822e5427..000000000 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/generating/TextureGeneratorBlend.java +++ /dev/null @@ -1,138 +0,0 @@ -/* - * Copyright (c) 2009-2020 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.blender.textures.generating; - -import com.jme3.math.FastMath; -import com.jme3.scene.plugins.blender.BlenderContext; -import com.jme3.scene.plugins.blender.file.Structure; -import com.jme3.scene.plugins.blender.textures.TexturePixel; -import com.jme3.texture.Image.Format; - -/** - * This class generates the 'blend' texture. - * @author Marcin Roguski (Kaelthas) - */ -public final class TextureGeneratorBlend extends TextureGenerator { - - private static final IntensityFunction INTENSITY_FUNCTION[] = new IntensityFunction[7]; - static { - INTENSITY_FUNCTION[0] = new IntensityFunction() {// Linear: stype = 0 (TEX_LIN) - @Override - public float getIntensity(float x, float y, float z) { - return (1.0f + x) * 0.5f; - } - }; - INTENSITY_FUNCTION[1] = new IntensityFunction() {// Quad: stype = 1 (TEX_QUAD) - @Override - public float getIntensity(float x, float y, float z) { - float result = (1.0f + x) * 0.5f; - return result * result; - } - }; - INTENSITY_FUNCTION[2] = new IntensityFunction() {// Ease: stype = 2 (TEX_EASE) - @Override - public float getIntensity(float x, float y, float z) { - float result = (1.0f + x) * 0.5f; - if (result <= 0.0f) { - return 0.0f; - } else if (result >= 1.0f) { - return 1.0f; - } else { - return result * result * (3.0f - 2.0f * result); - } - } - }; - INTENSITY_FUNCTION[3] = new IntensityFunction() {// Diagonal: stype = 3 (TEX_DIAG) - @Override - public float getIntensity(float x, float y, float z) { - return (2.0f + x + y) * 0.25f; - } - }; - INTENSITY_FUNCTION[4] = new IntensityFunction() {// Sphere: stype = 4 (TEX_SPHERE) - @Override - public float getIntensity(float x, float y, float z) { - float result = 1.0f - (float) Math.sqrt(x * x + y * y + z * z); - return result < 0.0f ? 0.0f : result; - } - }; - INTENSITY_FUNCTION[5] = new IntensityFunction() {// Halo: stype = 5 (TEX_HALO) - @Override - public float getIntensity(float x, float y, float z) { - float result = 1.0f - (float) Math.sqrt(x * x + y * y + z * z); - return result <= 0.0f ? 0.0f : result * result; - } - }; - INTENSITY_FUNCTION[6] = new IntensityFunction() {// Radial: stype = 6 (TEX_RAD) - @Override - public float getIntensity(float x, float y, float z) { - return (float) Math.atan2(y, x) * FastMath.INV_TWO_PI + 0.5f; - } - }; - } - - /** - * Constructor stores the given noise generator. - * @param noiseGenerator - * the noise generator - */ - public TextureGeneratorBlend(NoiseGenerator noiseGenerator) { - super(noiseGenerator, Format.Luminance8); - } - - protected int stype; - - @Override - public void readData(Structure tex, BlenderContext blenderContext) { - super.readData(tex, blenderContext); - stype = ((Number) tex.getFieldValue("stype")).intValue(); - } - - @Override - public void getPixel(TexturePixel pixel, float x, float y, float z) { - pixel.intensity = INTENSITY_FUNCTION[stype].getIntensity(x, y, z); - - if (colorBand != null) { - int colorbandIndex = (int) (pixel.intensity * 1000.0f); - pixel.red = colorBand[colorbandIndex][0]; - pixel.green = colorBand[colorbandIndex][1]; - pixel.blue = colorBand[colorbandIndex][2]; - - this.applyBrightnessAndContrast(bacd, pixel); - } else { - this.applyBrightnessAndContrast(pixel, bacd.contrast, bacd.brightness); - } - } - - private static interface IntensityFunction { - float getIntensity(float x, float y, float z); - } -} diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/generating/TextureGeneratorClouds.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/generating/TextureGeneratorClouds.java deleted file mode 100644 index 0d1cf2c54..000000000 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/generating/TextureGeneratorClouds.java +++ /dev/null @@ -1,121 +0,0 @@ -/* - * 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.blender.textures.generating; - -import com.jme3.math.FastMath; -import com.jme3.scene.plugins.blender.BlenderContext; -import com.jme3.scene.plugins.blender.file.Structure; -import com.jme3.scene.plugins.blender.textures.TexturePixel; -import com.jme3.scene.plugins.blender.textures.generating.NoiseGenerator.NoiseFunction; -import com.jme3.texture.Image.Format; - -/** - * This class generates the 'clouds' texture. - * @author Marcin Roguski (Kaelthas) - */ -public class TextureGeneratorClouds extends TextureGenerator { - // noiseType - protected static final int TEX_NOISESOFT = 0; - protected static final int TEX_NOISEPERL = 1; - - // sType - protected static final int TEX_DEFAULT = 0; - protected static final int TEX_COLOR = 1; - - protected float noisesize; - protected int noiseDepth; - protected int noiseBasis; - protected NoiseFunction noiseFunction; - protected int noiseType; - protected boolean isHard; - protected int sType; - - /** - * Constructor stores the given noise generator. - * @param noiseGenerator - * the noise generator - */ - public TextureGeneratorClouds(NoiseGenerator noiseGenerator) { - super(noiseGenerator, Format.Luminance8); - } - - @Override - public void readData(Structure tex, BlenderContext blenderContext) { - super.readData(tex, blenderContext); - noisesize = ((Number) tex.getFieldValue("noisesize")).floatValue(); - noiseDepth = ((Number) tex.getFieldValue("noisedepth")).intValue(); - noiseBasis = ((Number) tex.getFieldValue("noisebasis")).intValue(); - noiseType = ((Number) tex.getFieldValue("noisetype")).intValue(); - isHard = noiseType != TEX_NOISESOFT; - sType = ((Number) tex.getFieldValue("stype")).intValue(); - if (sType == TEX_COLOR) { - imageFormat = Format.RGBA8; - } - - noiseFunction = NoiseGenerator.noiseFunctions.get(noiseBasis); - if (noiseFunction == null) { - noiseFunction = NoiseGenerator.noiseFunctions.get(0); - noiseBasis = 0; - } - } - - @Override - public void getPixel(TexturePixel pixel, float x, float y, float z) { - if (noiseBasis == 0) { - ++x; - ++y; - ++z; - } - pixel.intensity = NoiseGenerator.NoiseFunctions.turbulence(x, y, z, noisesize, noiseDepth, noiseFunction, isHard); - if (colorBand != null) { - int colorbandIndex = (int) (pixel.intensity * 1000.0f); - pixel.red = colorBand[colorbandIndex][0]; - pixel.green = colorBand[colorbandIndex][1]; - pixel.blue = colorBand[colorbandIndex][2]; - pixel.alpha = colorBand[colorbandIndex][3]; - - this.applyBrightnessAndContrast(bacd, pixel); - } else if (sType == TEX_COLOR) { - pixel.red = pixel.intensity; - pixel.green = NoiseGenerator.NoiseFunctions.turbulence(y, x, z, noisesize, noiseDepth, noiseFunction, isHard); - pixel.blue = NoiseGenerator.NoiseFunctions.turbulence(y, z, x, noisesize, noiseDepth, noiseFunction, isHard); - - pixel.green = FastMath.clamp(pixel.green, 0.0f, 1.0f); - pixel.blue = FastMath.clamp(pixel.blue, 0.0f, 1.0f); - pixel.alpha = 1.0f; - - this.applyBrightnessAndContrast(bacd, pixel); - } else { - this.applyBrightnessAndContrast(pixel, bacd.contrast, bacd.brightness); - } - } -} diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/generating/TextureGeneratorDistnoise.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/generating/TextureGeneratorDistnoise.java deleted file mode 100644 index 232ccbeb3..000000000 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/generating/TextureGeneratorDistnoise.java +++ /dev/null @@ -1,111 +0,0 @@ -/* - * 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.blender.textures.generating; - -import com.jme3.math.FastMath; -import com.jme3.scene.plugins.blender.BlenderContext; -import com.jme3.scene.plugins.blender.file.Structure; -import com.jme3.scene.plugins.blender.textures.TexturePixel; -import com.jme3.scene.plugins.blender.textures.generating.NoiseGenerator.NoiseFunction; -import com.jme3.texture.Image.Format; - -/** - * This class generates the 'distorted noise' texture. - * @author Marcin Roguski (Kaelthas) - */ -public class TextureGeneratorDistnoise extends TextureGenerator { - protected float noisesize; - protected float distAmount; - protected int noisebasis; - protected int noisebasis2; - - /** - * Constructor stores the given noise generator. - * @param noiseGenerator - * the noise generator - */ - public TextureGeneratorDistnoise(NoiseGenerator noiseGenerator) { - super(noiseGenerator, Format.Luminance8); - } - - @Override - public void readData(Structure tex, BlenderContext blenderContext) { - super.readData(tex, blenderContext); - noisesize = ((Number) tex.getFieldValue("noisesize")).floatValue(); - distAmount = ((Number) tex.getFieldValue("dist_amount")).floatValue(); - noisebasis = ((Number) tex.getFieldValue("noisebasis")).intValue(); - noisebasis2 = ((Number) tex.getFieldValue("noisebasis2")).intValue(); - } - - @Override - public void getPixel(TexturePixel pixel, float x, float y, float z) { - pixel.intensity = this.musgraveVariableLunacrityNoise(x * 4, y * 4, z * 4, distAmount, noisebasis, noisebasis2); - pixel.intensity = FastMath.clamp(pixel.intensity, 0.0f, 1.0f); - if (colorBand != null) { - int colorbandIndex = (int) (pixel.intensity * 1000.0f); - pixel.red = colorBand[colorbandIndex][0]; - pixel.green = colorBand[colorbandIndex][1]; - pixel.blue = colorBand[colorbandIndex][2]; - - this.applyBrightnessAndContrast(bacd, pixel); - } else { - this.applyBrightnessAndContrast(pixel, bacd.contrast, bacd.brightness); - } - } - - /** - * "Variable Lacunarity Noise" A distorted variety of Perlin noise. This method is used to calculate distorted noise - * texture. - * @param x - * @param y - * @param z - * @param distortion - * @param nbas1 - * @param nbas2 - * @return - */ - private float musgraveVariableLunacrityNoise(float x, float y, float z, float distortion, int nbas1, int nbas2) { - NoiseFunction abstractNoiseFunc1 = NoiseGenerator.noiseFunctions.get(Integer.valueOf(nbas1)); - if (abstractNoiseFunc1 == null) { - abstractNoiseFunc1 = NoiseGenerator.noiseFunctions.get(Integer.valueOf(0)); - } - NoiseFunction abstractNoiseFunc2 = NoiseGenerator.noiseFunctions.get(Integer.valueOf(nbas2)); - if (abstractNoiseFunc2 == null) { - abstractNoiseFunc2 = NoiseGenerator.noiseFunctions.get(Integer.valueOf(0)); - } - // get a random vector and scale the randomization - float rx = abstractNoiseFunc1.execute(x + 13.5f, y + 13.5f, z + 13.5f) * distortion; - float ry = abstractNoiseFunc1.execute(x, y, z) * distortion; - float rz = abstractNoiseFunc1.execute(x - 13.5f, y - 13.5f, z - 13.5f) * distortion; - return abstractNoiseFunc2.executeSigned(x + rx, y + ry, z + rz); // distorted-domain noise - } -} diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/generating/TextureGeneratorFactory.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/generating/TextureGeneratorFactory.java deleted file mode 100644 index a0eda9adc..000000000 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/generating/TextureGeneratorFactory.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.jme3.scene.plugins.blender.textures.generating; - -import com.jme3.scene.plugins.blender.textures.TextureHelper; - -public class TextureGeneratorFactory { - - private NoiseGenerator noiseGenerator = new NoiseGenerator(); - - public TextureGenerator createTextureGenerator(int generatedTexture) { - switch (generatedTexture) { - case TextureHelper.TEX_BLEND: - return new TextureGeneratorBlend(noiseGenerator); - case TextureHelper.TEX_CLOUDS: - return new TextureGeneratorClouds(noiseGenerator); - case TextureHelper.TEX_DISTNOISE: - return new TextureGeneratorDistnoise(noiseGenerator); - case TextureHelper.TEX_MAGIC: - return new TextureGeneratorMagic(noiseGenerator); - case TextureHelper.TEX_MUSGRAVE: - return new TextureGeneratorMusgrave(noiseGenerator); - case TextureHelper.TEX_NOISE: - return new TextureGeneratorNoise(noiseGenerator); - case TextureHelper.TEX_STUCCI: - return new TextureGeneratorStucci(noiseGenerator); - case TextureHelper.TEX_VORONOI: - return new TextureGeneratorVoronoi(noiseGenerator); - default: - throw new IllegalStateException("Unknown generated texture type: " + generatedTexture); - } - } -} diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/generating/TextureGeneratorMagic.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/generating/TextureGeneratorMagic.java deleted file mode 100644 index ff7d07196..000000000 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/generating/TextureGeneratorMagic.java +++ /dev/null @@ -1,170 +0,0 @@ -/* - * Copyright (c) 2009-2020 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.blender.textures.generating; - -import com.jme3.math.FastMath; -import com.jme3.scene.plugins.blender.BlenderContext; -import com.jme3.scene.plugins.blender.file.Structure; -import com.jme3.scene.plugins.blender.textures.TexturePixel; -import com.jme3.texture.Image.Format; - -/** - * This class generates the 'magic' texture. - * @author Marcin Roguski (Kaelthas) - */ -public class TextureGeneratorMagic extends TextureGenerator { - private static NoiseDepthFunction[] noiseDepthFunctions = new NoiseDepthFunction[10]; - static { - noiseDepthFunctions[0] = new NoiseDepthFunction() { - @Override - public void compute(float[] xyz, float turbulence) { - xyz[1] = -(float) Math.cos(xyz[0] - xyz[1] + xyz[2]) * turbulence; - } - }; - noiseDepthFunctions[1] = new NoiseDepthFunction() { - @Override - public void compute(float[] xyz, float turbulence) { - xyz[0] = (float) Math.cos(xyz[0] - xyz[1] - xyz[2]) * turbulence; - } - }; - noiseDepthFunctions[2] = new NoiseDepthFunction() { - @Override - public void compute(float[] xyz, float turbulence) { - xyz[2] = (float) Math.sin(-xyz[0] - xyz[1] - xyz[2]) * turbulence; - } - }; - noiseDepthFunctions[3] = new NoiseDepthFunction() { - @Override - public void compute(float[] xyz, float turbulence) { - xyz[0] = -(float) Math.cos(-xyz[0] + xyz[1] - xyz[2]) * turbulence; - } - }; - noiseDepthFunctions[4] = new NoiseDepthFunction() { - @Override - public void compute(float[] xyz, float turbulence) { - xyz[1] = -(float) Math.sin(-xyz[0] + xyz[1] + xyz[2]) * turbulence; - } - }; - noiseDepthFunctions[5] = new NoiseDepthFunction() { - @Override - public void compute(float[] xyz, float turbulence) { - xyz[1] = -(float) Math.cos(-xyz[0] + xyz[1] + xyz[2]) * turbulence; - } - }; - noiseDepthFunctions[6] = new NoiseDepthFunction() { - @Override - public void compute(float[] xyz, float turbulence) { - xyz[0] = (float) Math.cos(xyz[0] + xyz[1] + xyz[2]) * turbulence; - } - }; - noiseDepthFunctions[7] = new NoiseDepthFunction() { - @Override - public void compute(float[] xyz, float turbulence) { - xyz[2] = (float) Math.sin(xyz[0] + xyz[1] - xyz[2]) * turbulence; - } - }; - noiseDepthFunctions[8] = new NoiseDepthFunction() { - @Override - public void compute(float[] xyz, float turbulence) { - xyz[0] = -(float) Math.cos(-xyz[0] - xyz[1] + xyz[2]) * turbulence; - } - }; - noiseDepthFunctions[9] = new NoiseDepthFunction() { - @Override - public void compute(float[] xyz, float turbulence) { - xyz[1] = -(float) Math.sin(xyz[0] - xyz[1] + xyz[2]) * turbulence; - } - }; - } - - protected int noisedepth; - protected float turbul; - protected float[] xyz = new float[3]; - - /** - * Constructor stores the given noise generator. - * @param noiseGenerator - * the noise generator - */ - public TextureGeneratorMagic(NoiseGenerator noiseGenerator) { - super(noiseGenerator, Format.RGBA8); - } - - @Override - public void readData(Structure tex, BlenderContext blenderContext) { - super.readData(tex, blenderContext); - noisedepth = ((Number) tex.getFieldValue("noisedepth")).intValue(); - turbul = ((Number) tex.getFieldValue("turbul")).floatValue() / 5.0f; - } - - @Override - public void getPixel(TexturePixel pixel, float x, float y, float z) { - float turb = turbul; - xyz[0] = (float) Math.sin((x + y + z) * 5.0f); - xyz[1] = (float) Math.cos((-x + y - z) * 5.0f); - xyz[2] = -(float) Math.cos((-x - y + z) * 5.0f); - - if (colorBand != null) { - pixel.intensity = FastMath.clamp(0.3333f * (xyz[0] + xyz[1] + xyz[2]), 0.0f, 1.0f); - int colorbandIndex = (int) (pixel.intensity * 1000.0f); - pixel.red = colorBand[colorbandIndex][0]; - pixel.green = colorBand[colorbandIndex][1]; - pixel.blue = colorBand[colorbandIndex][2]; - pixel.alpha = colorBand[colorbandIndex][3]; - } else { - if (noisedepth > 0) { - xyz[0] *= turb; - xyz[1] *= turb; - xyz[2] *= turb; - for (int m = 0; m < noisedepth; ++m) { - noiseDepthFunctions[m].compute(xyz, turb); - } - } - - if (turb != 0.0f) { - turb *= 2.0f; - xyz[0] /= turb; - xyz[1] /= turb; - xyz[2] /= turb; - } - pixel.red = 0.5f - xyz[0]; - pixel.green = 0.5f - xyz[1]; - pixel.blue = 0.5f - xyz[2]; - pixel.alpha = 1.0f; - } - this.applyBrightnessAndContrast(bacd, pixel); - } - - private static interface NoiseDepthFunction { - void compute(float[] xyz, float turbulence); - } -} diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/generating/TextureGeneratorMusgrave.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/generating/TextureGeneratorMusgrave.java deleted file mode 100644 index 707d4dfe0..000000000 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/generating/TextureGeneratorMusgrave.java +++ /dev/null @@ -1,124 +0,0 @@ -/* - * 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.blender.textures.generating; - -import com.jme3.scene.plugins.blender.BlenderContext; -import com.jme3.scene.plugins.blender.file.Structure; -import com.jme3.scene.plugins.blender.textures.TexturePixel; -import com.jme3.scene.plugins.blender.textures.generating.NoiseGenerator.MusgraveFunction; -import com.jme3.scene.plugins.blender.textures.generating.NoiseGenerator.NoiseFunction; -import com.jme3.texture.Image.Format; - -/** - * This class generates the 'musgrave' texture. - * @author Marcin Roguski (Kaelthas) - */ -public class TextureGeneratorMusgrave extends TextureGenerator { - protected MusgraveData musgraveData; - protected MusgraveFunction musgraveFunction; - protected int stype; - protected float noisesize; - - /** - * Constructor stores the given noise generator. - * @param noiseGenerator - * the noise generator - */ - public TextureGeneratorMusgrave(NoiseGenerator noiseGenerator) { - super(noiseGenerator, Format.Luminance8); - } - - @Override - public void readData(Structure tex, BlenderContext blenderContext) { - super.readData(tex, blenderContext); - musgraveData = new MusgraveData(tex); - stype = ((Number) tex.getFieldValue("stype")).intValue(); - noisesize = ((Number) tex.getFieldValue("noisesize")).floatValue(); - musgraveFunction = NoiseGenerator.musgraveFunctions.get(Integer.valueOf(musgraveData.stype)); - if (musgraveFunction == null) { - throw new IllegalStateException("Unknown type of musgrave texture: " + stype); - } - } - - @Override - public void getPixel(TexturePixel pixel, float x, float y, float z) { - pixel.intensity = musgraveData.outscale * musgraveFunction.execute(musgraveData, x, y, z); - if (pixel.intensity > 1) { - pixel.intensity = 1.0f; - } else if (pixel.intensity < 0) { - pixel.intensity = 0.0f; - } - - if (colorBand != null) { - int colorbandIndex = (int) (pixel.intensity * 1000.0f); - pixel.red = colorBand[colorbandIndex][0]; - pixel.green = colorBand[colorbandIndex][1]; - pixel.blue = colorBand[colorbandIndex][2]; - - this.applyBrightnessAndContrast(pixel, bacd.contrast, bacd.brightness); - pixel.alpha = colorBand[colorbandIndex][3]; - } else { - this.applyBrightnessAndContrast(bacd, pixel); - } - } - - protected static class MusgraveData { - public final int stype; - public final float outscale; - public final float h; - public final float lacunarity; - public final float octaves; - public final int noisebasis; - public final NoiseFunction noiseFunction; - public final float offset; - public final float gain; - - public MusgraveData(Structure tex) { - stype = ((Number) tex.getFieldValue("stype")).intValue(); - outscale = ((Number) tex.getFieldValue("ns_outscale")).floatValue(); - h = ((Number) tex.getFieldValue("mg_H")).floatValue(); - lacunarity = ((Number) tex.getFieldValue("mg_lacunarity")).floatValue(); - octaves = ((Number) tex.getFieldValue("mg_octaves")).floatValue(); - offset = ((Number) tex.getFieldValue("mg_offset")).floatValue(); - gain = ((Number) tex.getFieldValue("mg_gain")).floatValue(); - - int noisebasis = ((Number) tex.getFieldValue("noisebasis")).intValue(); - NoiseFunction noiseFunction = NoiseGenerator.noiseFunctions.get(noisebasis); - if (noiseFunction == null) { - noiseFunction = NoiseGenerator.noiseFunctions.get(0); - noisebasis = 0; - } - this.noisebasis = noisebasis; - this.noiseFunction = noiseFunction; - } - } -} diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/generating/TextureGeneratorNoise.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/generating/TextureGeneratorNoise.java deleted file mode 100644 index aa8b7b18d..000000000 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/generating/TextureGeneratorNoise.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * 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.blender.textures.generating; - -import com.jme3.math.FastMath; -import com.jme3.scene.plugins.blender.BlenderContext; -import com.jme3.scene.plugins.blender.file.Structure; -import com.jme3.scene.plugins.blender.textures.TexturePixel; -import com.jme3.texture.Image.Format; - -/** - * This class generates the 'noise' texture. - * @author Marcin Roguski (Kaelthas) - */ -public class TextureGeneratorNoise extends TextureGenerator { - protected int noisedepth; - - /** - * Constructor stores the given noise generator. - * @param noiseGenerator - * the noise generator - */ - public TextureGeneratorNoise(NoiseGenerator noiseGenerator) { - super(noiseGenerator, Format.Luminance8); - } - - @Override - public void readData(Structure tex, BlenderContext blenderContext) { - super.readData(tex, blenderContext); - noisedepth = ((Number) tex.getFieldValue("noisedepth")).intValue(); - } - - @Override - public void getPixel(TexturePixel pixel, float x, float y, float z) { - int random = FastMath.rand.nextInt(); - int val = random & 3; - - int loop = noisedepth; - while (loop-- != 0) { - random >>= 2; - val *= random & 3; - } - pixel.intensity = FastMath.clamp(val, 0.0f, 1.0f); - if (colorBand != null) { - int colorbandIndex = (int) (pixel.intensity * 1000.0f); - pixel.red = colorBand[colorbandIndex][0]; - pixel.green = colorBand[colorbandIndex][1]; - pixel.blue = colorBand[colorbandIndex][2]; - - this.applyBrightnessAndContrast(bacd, pixel); - pixel.alpha = colorBand[colorbandIndex][3]; - } else { - this.applyBrightnessAndContrast(pixel, bacd.contrast, bacd.brightness); - } - } -} diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/generating/TextureGeneratorStucci.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/generating/TextureGeneratorStucci.java deleted file mode 100644 index dd07ef1c1..000000000 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/generating/TextureGeneratorStucci.java +++ /dev/null @@ -1,115 +0,0 @@ -/* - * 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.blender.textures.generating; - -import com.jme3.scene.plugins.blender.BlenderContext; -import com.jme3.scene.plugins.blender.file.Structure; -import com.jme3.scene.plugins.blender.textures.TexturePixel; -import com.jme3.scene.plugins.blender.textures.generating.NoiseGenerator.NoiseFunction; -import com.jme3.texture.Image.Format; - -/** - * This class generates the 'stucci' texture. - * @author Marcin Roguski (Kaelthas) - */ -public class TextureGeneratorStucci extends TextureGenerator { - protected static final int TEX_NOISESOFT = 0; - - protected float noisesize; - protected int noisebasis; - protected NoiseFunction noiseFunction; - protected int noisetype; - protected float turbul; - protected boolean isHard; - protected int stype; - - /** - * Constructor stores the given noise generator. - * @param noiseGenerator - * the noise generator - */ - public TextureGeneratorStucci(NoiseGenerator noiseGenerator) { - super(noiseGenerator, Format.Luminance8); - } - - @Override - public void readData(Structure tex, BlenderContext blenderContext) { - super.readData(tex, blenderContext); - noisesize = ((Number) tex.getFieldValue("noisesize")).floatValue(); - - noisebasis = ((Number) tex.getFieldValue("noisebasis")).intValue(); - noiseFunction = NoiseGenerator.noiseFunctions.get(noisebasis); - if (noiseFunction == null) { - noiseFunction = NoiseGenerator.noiseFunctions.get(0); - noisebasis = 0; - } - - noisetype = ((Number) tex.getFieldValue("noisetype")).intValue(); - turbul = ((Number) tex.getFieldValue("turbul")).floatValue(); - isHard = noisetype != TEX_NOISESOFT; - stype = ((Number) tex.getFieldValue("stype")).intValue(); - if (noisesize <= 0.001f) {// the texture goes black if this value is lower than 0.001f - noisesize = 0.001f; - } - } - - @Override - public void getPixel(TexturePixel pixel, float x, float y, float z) { - if (noisebasis == 0) { - ++x; - ++y; - ++z; - } - float noiseValue = NoiseGenerator.NoiseFunctions.noise(x, y, z, noisesize, 0, noiseFunction, isHard); - float ofs = turbul / 200.0f; - if (stype != 0) { - ofs *= noiseValue * noiseValue; - } - - pixel.intensity = NoiseGenerator.NoiseFunctions.noise(x, y, z + ofs, noisesize, 0, noiseFunction, isHard); - if (colorBand != null) { - int colorbandIndex = (int) (pixel.intensity * 1000.0f); - pixel.red = colorBand[colorbandIndex][0]; - pixel.green = colorBand[colorbandIndex][1]; - pixel.blue = colorBand[colorbandIndex][2]; - pixel.alpha = colorBand[colorbandIndex][3]; - } - - if (stype == NoiseGenerator.TEX_WALLOUT) { - pixel.intensity = 1.0f - pixel.intensity; - } - if (pixel.intensity < 0.0f) { - pixel.intensity = 0.0f; - } - // no brightness and contrast needed for stucci (it doesn't affect the texture) - } -} diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/generating/TextureGeneratorVoronoi.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/generating/TextureGeneratorVoronoi.java deleted file mode 100644 index e53a6e73b..000000000 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/generating/TextureGeneratorVoronoi.java +++ /dev/null @@ -1,143 +0,0 @@ -/* - * 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.blender.textures.generating; - -import com.jme3.math.FastMath; -import com.jme3.scene.plugins.blender.BlenderContext; -import com.jme3.scene.plugins.blender.file.Structure; -import com.jme3.scene.plugins.blender.textures.TexturePixel; -import com.jme3.scene.plugins.blender.textures.generating.NoiseGenerator.DistanceFunction; -import com.jme3.scene.plugins.blender.textures.generating.NoiseGenerator.NoiseMath; -import com.jme3.texture.Image.Format; - -/** - * This class generates the 'voronoi' texture. - * @author Marcin Roguski (Kaelthas) - */ -public class TextureGeneratorVoronoi extends TextureGenerator { - protected float noisesize; - protected float outscale; - protected float mexp; - protected DistanceFunction distanceFunction; - protected int voronoiColorType; - protected float[] da = new float[4], pa = new float[12]; - protected float[] hashPoint; - protected float[] voronoiWeights; - protected float weightSum; - - /** - * Constructor stores the given noise generator. - * @param noiseGenerator - * the noise generator - */ - public TextureGeneratorVoronoi(NoiseGenerator noiseGenerator) { - super(noiseGenerator, Format.Luminance8); - } - - @Override - public void readData(Structure tex, BlenderContext blenderContext) { - super.readData(tex, blenderContext); - voronoiWeights = new float[4]; - voronoiWeights[0] = ((Number) tex.getFieldValue("vn_w1")).floatValue(); - voronoiWeights[1] = ((Number) tex.getFieldValue("vn_w2")).floatValue(); - voronoiWeights[2] = ((Number) tex.getFieldValue("vn_w3")).floatValue(); - voronoiWeights[3] = ((Number) tex.getFieldValue("vn_w4")).floatValue(); - noisesize = ((Number) tex.getFieldValue("noisesize")).floatValue(); - outscale = ((Number) tex.getFieldValue("ns_outscale")).floatValue(); - mexp = ((Number) tex.getFieldValue("vn_mexp")).floatValue(); - int distanceType = ((Number) tex.getFieldValue("vn_distm")).intValue(); - distanceFunction = NoiseGenerator.distanceFunctions.get(distanceType); - voronoiColorType = ((Number) tex.getFieldValue("vn_coltype")).intValue(); - hashPoint = voronoiColorType != 0 ? new float[3] : null; - weightSum = voronoiWeights[0] + voronoiWeights[1] + voronoiWeights[2] + voronoiWeights[3]; - if (weightSum != 0.0f) { - weightSum = outscale / weightSum; - } - if (voronoiColorType != 0 || colorBand != null) { - imageFormat = Format.RGBA8; - } - } - - @Override - public void getPixel(TexturePixel pixel, float x, float y, float z) { - // for voronoi we need to widen the range a little - NoiseGenerator.NoiseFunctions.voronoi(x * 4, y * 4, z * 4, da, pa, mexp, distanceFunction); - pixel.intensity = weightSum * FastMath.abs(voronoiWeights[0] * da[0] + voronoiWeights[1] * da[1] + voronoiWeights[2] * da[2] + voronoiWeights[3] * da[3]); - if (pixel.intensity > 1.0f) { - pixel.intensity = 1.0f; - } else if (pixel.intensity < 0.0f) { - pixel.intensity = 0.0f; - } - - if (colorBand != null) {// colorband ALWAYS goes first and covers the color (if set) - int colorbandIndex = (int) (pixel.intensity * 1000.0f); - pixel.red = colorBand[colorbandIndex][0]; - pixel.green = colorBand[colorbandIndex][1]; - pixel.blue = colorBand[colorbandIndex][2]; - pixel.alpha = colorBand[colorbandIndex][3]; - } else if (voronoiColorType != 0) { - pixel.red = pixel.green = pixel.blue = 0.0f; - pixel.alpha = 1.0f; - for (int m = 0; m < 12; m += 3) { - float weight = voronoiWeights[m / 3]; - NoiseMath.hash((int) pa[m], (int) pa[m + 1], (int) pa[m + 2], hashPoint); - pixel.red += weight * hashPoint[0]; - pixel.green += weight * hashPoint[1]; - pixel.blue += weight * hashPoint[2]; - } - if (voronoiColorType >= 2) { - float t1 = (da[1] - da[0]) * 10.0f; - if (t1 > 1.0f) { - t1 = 1.0f; - } - if (voronoiColorType == 3) { - t1 *= pixel.intensity; - } else { - t1 *= weightSum; - } - pixel.red *= t1; - pixel.green *= t1; - pixel.blue *= t1; - } else { - pixel.red *= weightSum; - pixel.green *= weightSum; - pixel.blue *= weightSum; - } - } - - if (voronoiColorType != 0 || colorBand != null) { - this.applyBrightnessAndContrast(bacd, pixel); - } else { - this.applyBrightnessAndContrast(pixel, bacd.contrast, bacd.brightness); - } - } -} diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/io/AWTPixelInputOutput.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/io/AWTPixelInputOutput.java deleted file mode 100644 index 8409f29ec..000000000 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/io/AWTPixelInputOutput.java +++ /dev/null @@ -1,160 +0,0 @@ -package com.jme3.scene.plugins.blender.textures.io; - -import com.jme3.math.FastMath; -import com.jme3.scene.plugins.blender.textures.TexturePixel; -import com.jme3.texture.Image; -import java.nio.ByteBuffer; -import jme3tools.converters.RGB565; - -/** - * Implemens read/write operations for AWT images. - * @author Marcin Roguski (Kaelthas) - */ -/* package */class AWTPixelInputOutput implements PixelInputOutput { - @Override - public void read(Image image, int layer, TexturePixel pixel, int index) { - ByteBuffer data = image.getData(layer); - switch (image.getFormat()) { - case RGBA8: - pixel.fromARGB8(data.get(index + 3), data.get(index), data.get(index + 1), data.get(index + 2)); - break; - case ARGB8: - pixel.fromARGB8(data.get(index), data.get(index + 1), data.get(index + 2), data.get(index + 3)); - break; - case ABGR8: - pixel.fromARGB8(data.get(index), data.get(index + 3), data.get(index + 2), data.get(index + 1)); - break; - case BGR8: - pixel.fromARGB8((byte) 0xFF, data.get(index + 2), data.get(index + 1), data.get(index)); - break; - case BGRA8: - pixel.fromARGB8(data.get(index + 3), data.get(index + 2), data.get(index + 1), data.get(index)); - break; - case RGB8: - pixel.fromARGB8((byte) 0xFF, data.get(index), data.get(index + 1), data.get(index + 2)); - break; - case RGB565: - pixel.fromARGB8(RGB565.RGB565_to_ARGB8(data.getShort(index))); - break; - case RGB5A1: - short rgb5a1 = data.getShort(index); - byte a = (byte) (rgb5a1 & 0x01); - int r = (rgb5a1 & 0xf800) >> 11 << 3; - int g = (rgb5a1 & 0x07c0) >> 6 << 3; - int b = (rgb5a1 & 0x001f) >> 1 << 3; - pixel.fromARGB8(a == 1 ? (byte) 255 : 0, (byte) r, (byte) g, (byte) b); - break; - case RGB16F: - case RGB16F_to_RGB111110F: - case RGB16F_to_RGB9E5: - pixel.fromARGB(1, FastMath.convertHalfToFloat(data.getShort(index)), FastMath.convertHalfToFloat(data.getShort(index + 2)), FastMath.convertHalfToFloat(data.getShort(index + 4))); - break; - case RGBA16F: - pixel.fromARGB(FastMath.convertHalfToFloat(data.getShort(index + 6)), FastMath.convertHalfToFloat(data.getShort(index)), FastMath.convertHalfToFloat(data.getShort(index + 2)), FastMath.convertHalfToFloat(data.getShort(index + 4))); - break; - case RGBA32F: - pixel.fromARGB(Float.intBitsToFloat(data.getInt(index + 12)), Float.intBitsToFloat(data.getInt(index)), Float.intBitsToFloat(data.getInt(index + 4)), Float.intBitsToFloat(data.getInt(index + 8))); - break; - case RGB111110F:// the data is stored as 32-bit unsigned int, that is why we cast the read data to long and remove MSB-bytes to get the positive value - pixel.fromARGB(1, (float) Double.longBitsToDouble((long) data.getInt(index) & 0x00000000FFFFFFFF), (float) Double.longBitsToDouble((long) data.getInt(index + 4) & 0x00000000FFFFFFFF), (float) Double.longBitsToDouble((long) data.getInt(index + 8) & 0x00000000FFFFFFFF)); - break; - case RGB9E5:// TODO: support these - throw new IllegalStateException("Not supported image type for IO operations: " + image.getFormat()); - default: - throw new IllegalStateException("Unknown image format: " + image.getFormat()); - } - } - - @Override - public void read(Image image, int layer, TexturePixel pixel, int x, int y) { - int index = (y * image.getWidth() + x) * (image.getFormat().getBitsPerPixel() >> 3); - this.read(image, layer, pixel, index); - } - - @Override - public void write(Image image, int layer, TexturePixel pixel, int index) { - ByteBuffer data = image.getData(layer); - switch (image.getFormat()) { - case RGBA8: - data.put(index, pixel.getR8()); - data.put(index + 1, pixel.getG8()); - data.put(index + 2, pixel.getB8()); - data.put(index + 3, pixel.getA8()); - break; - case ARGB8: - data.put(index, pixel.getA8()); - data.put(index + 1, pixel.getR8()); - data.put(index + 2, pixel.getG8()); - data.put(index + 3, pixel.getB8()); - break; - case ABGR8: - data.put(index, pixel.getA8()); - data.put(index + 1, pixel.getB8()); - data.put(index + 2, pixel.getG8()); - data.put(index + 3, pixel.getR8()); - break; - case BGR8: - data.put(index, pixel.getB8()); - data.put(index + 1, pixel.getG8()); - data.put(index + 2, pixel.getR8()); - break; - case BGRA8: - data.put(index, pixel.getB8()); - data.put(index + 1, pixel.getG8()); - data.put(index + 2, pixel.getR8()); - data.put(index + 3, pixel.getA8()); - break; - case RGB8: - data.put(index, pixel.getR8()); - data.put(index + 1, pixel.getG8()); - data.put(index + 2, pixel.getB8()); - break; - case RGB565: - data.putShort(RGB565.ARGB8_to_RGB565(pixel.toARGB8())); - break; - case RGB5A1: - int argb8 = pixel.toARGB8(); - short r = (short) ((argb8 & 0x00F80000) >> 8); - short g = (short) ((argb8 & 0x0000F800) >> 5); - short b = (short) ((argb8 & 0x000000F8) >> 2); - short a = (short) ((short) ((argb8 & 0xFF000000) >> 24) > 0 ? 1 : 0); - data.putShort(index, (short) (r | g | b | a)); - break; - case RGB16F: - case RGB16F_to_RGB111110F: - case RGB16F_to_RGB9E5: - data.putShort(index, FastMath.convertFloatToHalf(pixel.red)); - data.putShort(index + 2, FastMath.convertFloatToHalf(pixel.green)); - data.putShort(index + 4, FastMath.convertFloatToHalf(pixel.blue)); - break; - case RGBA16F: - data.putShort(index, FastMath.convertFloatToHalf(pixel.red)); - data.putShort(index + 2, FastMath.convertFloatToHalf(pixel.green)); - data.putShort(index + 4, FastMath.convertFloatToHalf(pixel.blue)); - data.putShort(index + 6, FastMath.convertFloatToHalf(pixel.blue)); - break; - case RGB32F: - case RGB111110F:// this data is stored as 32-bit unsigned int - data.putInt(index, Float.floatToIntBits(pixel.red)); - data.putInt(index + 2, Float.floatToIntBits(pixel.green)); - data.putInt(index + 4, Float.floatToIntBits(pixel.blue)); - break; - case RGBA32F: - data.putInt(index, Float.floatToIntBits(pixel.red)); - data.putInt(index + 2, Float.floatToIntBits(pixel.green)); - data.putInt(index + 4, Float.floatToIntBits(pixel.blue)); - data.putInt(index + 6, Float.floatToIntBits(pixel.alpha)); - break; - case RGB9E5:// TODO: support these - throw new IllegalStateException("Not supported image type for IO operations: " + image.getFormat()); - default: - throw new IllegalStateException("Unknown image format: " + image.getFormat()); - } - } - - @Override - public void write(Image image, int layer, TexturePixel pixel, int x, int y) { - int index = (y * image.getWidth() + x) * (image.getFormat().getBitsPerPixel() >> 3); - this.write(image, layer, pixel, index); - } -} diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/io/DDSPixelInputOutput.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/io/DDSPixelInputOutput.java deleted file mode 100644 index c5ffdb7f9..000000000 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/io/DDSPixelInputOutput.java +++ /dev/null @@ -1,175 +0,0 @@ -package com.jme3.scene.plugins.blender.textures.io; - -import com.jme3.math.FastMath; -import com.jme3.scene.plugins.blender.textures.TexturePixel; -import com.jme3.texture.Image; -import java.nio.ByteBuffer; -import jme3tools.converters.RGB565; - -/** - * Implemens read/write operations for DDS images. - * This class currently implements only read operation. - * @author Marcin Roguski (Kaelthas) - */ -/* package */class DDSPixelInputOutput implements PixelInputOutput { - /** - * For this class the index should be considered as a pixel index in AWT image format. - */ - @Override - public void read(Image image, int layer, TexturePixel pixel, int index) { - this.read(image, layer, pixel, index % image.getWidth(), index / image.getWidth()); - } - - @Override - public void read(Image image, int layer, TexturePixel pixel, int x, int y) { - int xTexetlIndex = x % image.getWidth() >> 2; - int yTexelIndex = y % image.getHeight() >> 2; - int xTexelCount = image.getWidth() >> 2; - int texelIndex = yTexelIndex * xTexelCount + xTexetlIndex; - - TexturePixel[] colors = new TexturePixel[] { new TexturePixel(), new TexturePixel(), new TexturePixel(), new TexturePixel() }; - int indexes = 0; - long alphaIndexes = 0; - float[] alphas = null; - ByteBuffer data = image.getData().get(layer); - - switch (image.getFormat()) { - case DXT1: // BC1 - case DXT1A: { - data.position(texelIndex * 8); - short c0 = data.getShort(); - short c1 = data.getShort(); - int col0 = RGB565.RGB565_to_ARGB8(c0); - int col1 = RGB565.RGB565_to_ARGB8(c1); - colors[0].fromARGB8(col0); - colors[1].fromARGB8(col1); - - if (col0 > col1) { - // creating color2 = 2/3color0 + 1/3color1 - colors[2].fromPixel(colors[0]); - colors[2].mult(2); - colors[2].add(colors[1]); - colors[2].divide(3); - - // creating color3 = 1/3color0 + 2/3color1; - colors[3].fromPixel(colors[1]); - colors[3].mult(2); - colors[3].add(colors[0]); - colors[3].divide(3); - } else { - // creating color2 = 1/2color0 + 1/2color1 - colors[2].fromPixel(colors[0]); - colors[2].add(colors[1]); - colors[2].mult(0.5f); - - colors[3].fromARGB8(0); - } - indexes = data.getInt();// 4-byte table with color indexes in decompressed table - break; - } - case DXT3: {// BC2 - data.position(texelIndex * 16); - long alpha = data.getLong(); - alphas = new float[16]; - for (int i = 0; i < 16; ++i) { - alphaIndexes |= i << i * 4; - byte a = (byte) ((alpha >> i * 4 & 0x0F) << 4); - alphas[i] = a >= 0 ? a / 255.0f : 1.0f - ~a / 255.0f; - } - - short c0 = data.getShort(); - short c1 = data.getShort(); - int col0 = RGB565.RGB565_to_ARGB8(c0); - int col1 = RGB565.RGB565_to_ARGB8(c1); - colors[0].fromARGB8(col0); - colors[1].fromARGB8(col1); - - // creating color2 = 2/3color0 + 1/3color1 - colors[2].fromPixel(colors[0]); - colors[2].mult(2); - colors[2].add(colors[1]); - colors[2].divide(3); - - // creating color3 = 1/3color0 + 2/3color1; - colors[3].fromPixel(colors[1]); - colors[3].mult(2); - colors[3].add(colors[0]); - colors[3].divide(3); - - indexes = data.getInt();// 4-byte table with color indexes in decompressed table - break; - } - case DXT5: {// BC3 - data.position(texelIndex * 16); - alphas = new float[8]; - alphas[0] = data.get() * 255.0f; - alphas[1] = data.get() * 255.0f; - // the casts to long must be done here because otherwise 32-bit integers would be shifetd by 32 and 40 bits which would result in improper values - alphaIndexes = (long)data.get() | (long)data.get() << 8 | (long)data.get() << 16 | (long)data.get() << 24 | (long)data.get() << 32 | (long)data.get() << 40; - if (alphas[0] > alphas[1]) {// 6 interpolated alpha values. - alphas[2] = (6 * alphas[0] + alphas[1]) / 7; - alphas[3] = (5 * alphas[0] + 2 * alphas[1]) / 7; - alphas[4] = (4 * alphas[0] + 3 * alphas[1]) / 7; - alphas[5] = (3 * alphas[0] + 4 * alphas[1]) / 7; - alphas[6] = (2 * alphas[0] + 5 * alphas[1]) / 7; - alphas[7] = (alphas[0] + 6 * alphas[1]) / 7; - } else { - alphas[2] = (4 * alphas[0] + alphas[1]) * 0.2f; - alphas[3] = (3 * alphas[0] + 2 * alphas[1]) * 0.2f; - alphas[4] = (2 * alphas[0] + 3 * alphas[1]) * 0.2f; - alphas[5] = (alphas[0] + 4 * alphas[1]) * 0.2f; - alphas[6] = 0; - alphas[7] = 1; - } - - short c0 = data.getShort(); - short c1 = data.getShort(); - int col0 = RGB565.RGB565_to_ARGB8(c0); - int col1 = RGB565.RGB565_to_ARGB8(c1); - colors[0].fromARGB8(col0); - colors[1].fromARGB8(col1); - - // creating color2 = 2/3color0 + 1/3color1 - colors[2].fromPixel(colors[0]); - colors[2].mult(2); - colors[2].add(colors[1]); - colors[2].divide(3); - - // creating color3 = 1/3color0 + 2/3color1; - colors[3].fromPixel(colors[1]); - colors[3].mult(2); - colors[3].add(colors[0]); - colors[3].divide(3); - - indexes = data.getInt();// 4-byte table with color indexes in decompressed table - break; - } - default: - throw new IllegalStateException("Unsupported decompression format."); - } - - // coordinates of the pixel in the selected texel - x = x - 4 * xTexetlIndex;// pixels are arranged from left to right - y = 3 - y - 4 * yTexelIndex;// pixels are arranged from bottom to top (that is why '3 - ...' is at the start) - - int pixelIndexInTexel = (y * 4 + x) * (int) FastMath.log(colors.length, 2); - int alphaIndexInTexel = alphas != null ? (y * 4 + x) * (int) FastMath.log(alphas.length, 2) : 0; - - // getting the pixel - int indexMask = colors.length - 1; - int colorIndex = indexes >> pixelIndexInTexel & indexMask; - float alpha = alphas != null ? alphas[(int) (alphaIndexes >> alphaIndexInTexel & 0x07)] : colors[colorIndex].alpha; - pixel.fromPixel(colors[colorIndex]); - pixel.alpha = alpha; - } - - @Override - public void write(Image image, int layer, TexturePixel pixel, int index) { - throw new UnsupportedOperationException("Cannot put the DXT pixel by index because not every index contains the pixel color!"); - } - - @Override - public void write(Image image, int layer, TexturePixel pixel, int x, int y) { - throw new UnsupportedOperationException("Writing to DDS texture pixel by pixel is not yet supported!"); - } -} diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/io/LuminancePixelInputOutput.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/io/LuminancePixelInputOutput.java deleted file mode 100644 index 0df0c4566..000000000 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/io/LuminancePixelInputOutput.java +++ /dev/null @@ -1,78 +0,0 @@ -package com.jme3.scene.plugins.blender.textures.io; - -import com.jme3.math.FastMath; -import com.jme3.scene.plugins.blender.textures.TexturePixel; -import com.jme3.texture.Image; -import java.nio.ByteBuffer; - -/** - * Implemens read/write operations for luminance images. - * - * @author Marcin Roguski (Kaelthas) - */ -/* package */class LuminancePixelInputOutput implements PixelInputOutput { - @Override - public void read(Image image, int layer, TexturePixel pixel, int index) { - ByteBuffer data = image.getData(layer); - switch (image.getFormat()) { - case Luminance8: - pixel.fromIntensity(data.get(index)); - break; - case Luminance8Alpha8: - pixel.fromIntensity(data.get(index)); - pixel.setAlpha(data.get(index + 1)); - break; - case Luminance16F: - pixel.intensity = FastMath.convertHalfToFloat(data.getShort(index)); - break; - case Luminance16FAlpha16F: - pixel.intensity = FastMath.convertHalfToFloat(data.getShort(index)); - pixel.alpha = FastMath.convertHalfToFloat(data.getShort(index + 2)); - break; - case Luminance32F: - pixel.intensity = Float.intBitsToFloat(data.getInt(index)); - break; - default: - throw new IllegalStateException("Unknown luminance format type."); - } - } - - @Override - public void read(Image image, int layer, TexturePixel pixel, int x, int y) { - int index = y * image.getWidth() + x; - this.read(image, layer, pixel, index); - } - - @Override - public void write(Image image, int layer, TexturePixel pixel, int index) { - ByteBuffer data = image.getData(layer); - data.put(index, pixel.getInt()); - switch (image.getFormat()) { - case Luminance8: - data.put(index, pixel.getInt()); - break; - case Luminance8Alpha8: - data.put(index, pixel.getInt()); - data.put(index + 1, pixel.getA8()); - break; - case Luminance16F: - data.putShort(index, FastMath.convertFloatToHalf(pixel.intensity)); - break; - case Luminance16FAlpha16F: - data.putShort(index, FastMath.convertFloatToHalf(pixel.intensity)); - data.putShort(index + 2, FastMath.convertFloatToHalf(pixel.alpha)); - break; - case Luminance32F: - data.putInt(index, Float.floatToIntBits(pixel.intensity)); - break; - default: - throw new IllegalStateException("Unknown luminance format type."); - } - } - - @Override - public void write(Image image, int layer, TexturePixel pixel, int x, int y) { - int index = y * image.getWidth() + x; - this.write(image, layer, pixel, index); - } -} diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/io/PixelIOFactory.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/io/PixelIOFactory.java deleted file mode 100644 index 47cf7090b..000000000 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/io/PixelIOFactory.java +++ /dev/null @@ -1,66 +0,0 @@ -package com.jme3.scene.plugins.blender.textures.io; - -import com.jme3.texture.Image.Format; -import java.util.HashMap; -import java.util.Map; - -/** - * This class creates a pixel IO object for the specified image format. - * - * @author Marcin Roguski (Kaelthas) - */ -public class PixelIOFactory { - private static final Map PIXEL_INPUT_OUTPUT = new HashMap(); - - /** - * This method returns pixel IO object for the specified format. - * - * @param format - * the format of the image - * @return pixel IO object - */ - public static PixelInputOutput getPixelIO(Format format) { - PixelInputOutput result = PIXEL_INPUT_OUTPUT.get(format); - if (result == null) { - switch (format) { - case ABGR8: - case RGBA8: - case BGR8: - case BGRA8: - case RGB8: - case ARGB8: - case RGB111110F: - case RGB16F: - case RGB16F_to_RGB111110F: - case RGB16F_to_RGB9E5: - case RGB32F: - case RGB565: - case RGB5A1: - case RGB9E5: - case RGBA16F: - case RGBA32F: - result = new AWTPixelInputOutput(); - break; - case Luminance8: - case Luminance16F: - case Luminance16FAlpha16F: - case Luminance32F: - case Luminance8Alpha8: - result = new LuminancePixelInputOutput(); - break; - case DXT1: - case DXT1A: - case DXT3: - case DXT5: - result = new DDSPixelInputOutput(); - break; - default: - throw new IllegalStateException("Unsupported image format for IO operations: " + format); - } - synchronized (PIXEL_INPUT_OUTPUT) { - PIXEL_INPUT_OUTPUT.put(format, result); - } - } - return result; - } -} diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/io/PixelInputOutput.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/io/PixelInputOutput.java deleted file mode 100644 index 3ff592047..000000000 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/io/PixelInputOutput.java +++ /dev/null @@ -1,65 +0,0 @@ -package com.jme3.scene.plugins.blender.textures.io; - -import com.jme3.scene.plugins.blender.textures.TexturePixel; -import com.jme3.texture.Image; - -/** - * Implemens read/write operations for images. - * - * @author Marcin Roguski (Kaelthas) - */ -public interface PixelInputOutput { - /** - * This method reads a pixel that starts at the given index. - * - * @param image - * the image we read pixel from - * @param pixel - * the pixel where the result is stored - * @param index - * the index where the pixel begins in the image data - */ - void read(Image image, int layer, TexturePixel pixel, int index); - - /** - * This method reads a pixel that is located at the given position on the - * image. - * - * @param image - * the image we read pixel from - * @param pixel - * the pixel where the result is stored - * @param x - * the X coordinate of the pixel - * @param y - * the Y coordinate of the pixel - */ - void read(Image image, int layer, TexturePixel pixel, int x, int y); - - /** - * This method writes a pixel that starts at the given index. - * - * @param image - * the image we read pixel from - * @param pixel - * the pixel where the result is stored - * @param index - * the index where the pixel begins in the image data - */ - void write(Image image, int layer, TexturePixel pixel, int index); - - /** - * This method writes a pixel that is located at the given position on the - * image. - * - * @param image - * the image we read pixel from - * @param pixel - * the pixel where the result is stored - * @param x - * the X coordinate of the pixel - * @param y - * the Y coordinate of the pixel - */ - void write(Image image, int layer, TexturePixel pixel, int x, int y); -} diff --git a/jme3-blender/src/main/resources/com/jme3/scene/plugins/blender/textures/generating/noiseconstants.dat b/jme3-blender/src/main/resources/com/jme3/scene/plugins/blender/textures/generating/noiseconstants.dat deleted file mode 100644 index 81fea0b61..000000000 Binary files a/jme3-blender/src/main/resources/com/jme3/scene/plugins/blender/textures/generating/noiseconstants.dat and /dev/null differ diff --git a/jme3-blender/testdata/src/main/resources/Blender/2.4x/BaseMesh_249.blend b/jme3-blender/testdata/src/main/resources/Blender/2.4x/BaseMesh_249.blend deleted file mode 100644 index 617711c51..000000000 Binary files a/jme3-blender/testdata/src/main/resources/Blender/2.4x/BaseMesh_249.blend and /dev/null differ diff --git a/jme3-blender/testdata/src/main/resources/Blender/2.4x/BaseScene.blend b/jme3-blender/testdata/src/main/resources/Blender/2.4x/BaseScene.blend deleted file mode 100644 index f4149af04..000000000 Binary files a/jme3-blender/testdata/src/main/resources/Blender/2.4x/BaseScene.blend and /dev/null differ diff --git a/jme3-blender/testdata/src/main/resources/Blender/2.4x/MountainValley_Track.blend b/jme3-blender/testdata/src/main/resources/Blender/2.4x/MountainValley_Track.blend deleted file mode 100644 index f2f794f14..000000000 Binary files a/jme3-blender/testdata/src/main/resources/Blender/2.4x/MountainValley_Track.blend and /dev/null differ diff --git a/jme3-blender/testdata/src/main/resources/Blender/2.4x/ObjectAnimation.blend b/jme3-blender/testdata/src/main/resources/Blender/2.4x/ObjectAnimation.blend deleted file mode 100644 index ea0404c87..000000000 Binary files a/jme3-blender/testdata/src/main/resources/Blender/2.4x/ObjectAnimation.blend and /dev/null differ diff --git a/jme3-blender/testdata/src/main/resources/Blender/2.4x/SimpleAnimation.blend b/jme3-blender/testdata/src/main/resources/Blender/2.4x/SimpleAnimation.blend deleted file mode 100644 index 8ca887d09..000000000 Binary files a/jme3-blender/testdata/src/main/resources/Blender/2.4x/SimpleAnimation.blend and /dev/null differ diff --git a/jme3-blender/testdata/src/main/resources/Blender/2.4x/Sinbad.blend b/jme3-blender/testdata/src/main/resources/Blender/2.4x/Sinbad.blend deleted file mode 100644 index 3737d0085..000000000 Binary files a/jme3-blender/testdata/src/main/resources/Blender/2.4x/Sinbad.blend and /dev/null differ diff --git a/jme3-blender/testdata/src/main/resources/Blender/2.4x/WoodCrate_lighter - (Inverted Normal Map).png b/jme3-blender/testdata/src/main/resources/Blender/2.4x/WoodCrate_lighter - (Inverted Normal Map).png deleted file mode 100644 index cfe461db2..000000000 Binary files a/jme3-blender/testdata/src/main/resources/Blender/2.4x/WoodCrate_lighter - (Inverted Normal Map).png and /dev/null differ diff --git a/jme3-blender/testdata/src/main/resources/Blender/2.4x/WoodCrate_lighter - Height Map.png b/jme3-blender/testdata/src/main/resources/Blender/2.4x/WoodCrate_lighter - Height Map.png deleted file mode 100644 index 20caf5007..000000000 Binary files a/jme3-blender/testdata/src/main/resources/Blender/2.4x/WoodCrate_lighter - Height Map.png and /dev/null differ diff --git a/jme3-blender/testdata/src/main/resources/Blender/2.4x/WoodCrate_lighter.png b/jme3-blender/testdata/src/main/resources/Blender/2.4x/WoodCrate_lighter.png deleted file mode 100644 index 29d6867ab..000000000 Binary files a/jme3-blender/testdata/src/main/resources/Blender/2.4x/WoodCrate_lighter.png and /dev/null differ diff --git a/jme3-blender/testdata/src/main/resources/Blender/2.4x/animtest.blend b/jme3-blender/testdata/src/main/resources/Blender/2.4x/animtest.blend deleted file mode 100644 index eabd593dc..000000000 Binary files a/jme3-blender/testdata/src/main/resources/Blender/2.4x/animtest.blend and /dev/null differ diff --git a/jme3-blender/testdata/src/main/resources/Blender/2.4x/constraints.blend b/jme3-blender/testdata/src/main/resources/Blender/2.4x/constraints.blend deleted file mode 100644 index 53976ad01..000000000 Binary files a/jme3-blender/testdata/src/main/resources/Blender/2.4x/constraints.blend and /dev/null differ diff --git a/jme3-blender/testdata/src/main/resources/Blender/2.4x/curves.blend b/jme3-blender/testdata/src/main/resources/Blender/2.4x/curves.blend deleted file mode 100644 index 9e9b73b1a..000000000 Binary files a/jme3-blender/testdata/src/main/resources/Blender/2.4x/curves.blend and /dev/null differ diff --git a/jme3-blender/testdata/src/main/resources/Blender/2.4x/kerrigan.blend b/jme3-blender/testdata/src/main/resources/Blender/2.4x/kerrigan.blend deleted file mode 100644 index 51b4266eb..000000000 Binary files a/jme3-blender/testdata/src/main/resources/Blender/2.4x/kerrigan.blend and /dev/null differ diff --git a/jme3-blender/testdata/src/main/resources/Blender/2.4x/kerrigan_diffuse.png b/jme3-blender/testdata/src/main/resources/Blender/2.4x/kerrigan_diffuse.png deleted file mode 100644 index 3776ea017..000000000 Binary files a/jme3-blender/testdata/src/main/resources/Blender/2.4x/kerrigan_diffuse.png and /dev/null differ diff --git a/jme3-blender/testdata/src/main/resources/Blender/2.4x/materials.blend b/jme3-blender/testdata/src/main/resources/Blender/2.4x/materials.blend deleted file mode 100644 index e38be7355..000000000 Binary files a/jme3-blender/testdata/src/main/resources/Blender/2.4x/materials.blend and /dev/null differ diff --git a/jme3-blender/testdata/src/main/resources/Blender/2.4x/modifiers.blend b/jme3-blender/testdata/src/main/resources/Blender/2.4x/modifiers.blend deleted file mode 100644 index d722d851f..000000000 Binary files a/jme3-blender/testdata/src/main/resources/Blender/2.4x/modifiers.blend and /dev/null differ diff --git a/jme3-blender/testdata/src/main/resources/Blender/2.4x/nurbs.blend b/jme3-blender/testdata/src/main/resources/Blender/2.4x/nurbs.blend deleted file mode 100644 index 84b291b73..000000000 Binary files a/jme3-blender/testdata/src/main/resources/Blender/2.4x/nurbs.blend and /dev/null differ diff --git a/jme3-blender/testdata/src/main/resources/Blender/2.4x/particles.blend b/jme3-blender/testdata/src/main/resources/Blender/2.4x/particles.blend deleted file mode 100644 index ba7ee53ee..000000000 Binary files a/jme3-blender/testdata/src/main/resources/Blender/2.4x/particles.blend and /dev/null differ diff --git a/jme3-blender/testdata/src/main/resources/Blender/2.4x/positions.blend b/jme3-blender/testdata/src/main/resources/Blender/2.4x/positions.blend deleted file mode 100644 index d22e89796..000000000 Binary files a/jme3-blender/testdata/src/main/resources/Blender/2.4x/positions.blend and /dev/null differ diff --git a/jme3-blender/testdata/src/main/resources/Blender/2.4x/sinbad_body.tga b/jme3-blender/testdata/src/main/resources/Blender/2.4x/sinbad_body.tga deleted file mode 100644 index 0074ecd4c..000000000 Binary files a/jme3-blender/testdata/src/main/resources/Blender/2.4x/sinbad_body.tga and /dev/null differ diff --git a/jme3-blender/testdata/src/main/resources/Blender/2.4x/sinbad_clothes.tga b/jme3-blender/testdata/src/main/resources/Blender/2.4x/sinbad_clothes.tga deleted file mode 100644 index 51bf211bd..000000000 Binary files a/jme3-blender/testdata/src/main/resources/Blender/2.4x/sinbad_clothes.tga and /dev/null differ diff --git a/jme3-blender/testdata/src/main/resources/Blender/2.4x/sinbad_sword.tga b/jme3-blender/testdata/src/main/resources/Blender/2.4x/sinbad_sword.tga deleted file mode 100644 index 2cabb79e8..000000000 Binary files a/jme3-blender/testdata/src/main/resources/Blender/2.4x/sinbad_sword.tga and /dev/null differ diff --git a/jme3-blender/testdata/src/main/resources/Blender/2.4x/texturedPlaneTest.blend b/jme3-blender/testdata/src/main/resources/Blender/2.4x/texturedPlaneTest.blend deleted file mode 100644 index da8128d6b..000000000 Binary files a/jme3-blender/testdata/src/main/resources/Blender/2.4x/texturedPlaneTest.blend and /dev/null differ diff --git a/jme3-blender/testdata/src/main/resources/Blender/2.4x/textures.blend b/jme3-blender/testdata/src/main/resources/Blender/2.4x/textures.blend deleted file mode 100644 index 183fc88d1..000000000 Binary files a/jme3-blender/testdata/src/main/resources/Blender/2.4x/textures.blend and /dev/null differ diff --git a/jme3-blender/testdata/src/main/resources/Blender/2.4x/textures/Concrete_Wall.PNG b/jme3-blender/testdata/src/main/resources/Blender/2.4x/textures/Concrete_Wall.PNG deleted file mode 100644 index b6713622e..000000000 Binary files a/jme3-blender/testdata/src/main/resources/Blender/2.4x/textures/Concrete_Wall.PNG and /dev/null differ diff --git a/jme3-blender/testdata/src/main/resources/Blender/2.4x/textures/Grass_256.png b/jme3-blender/testdata/src/main/resources/Blender/2.4x/textures/Grass_256.png deleted file mode 100644 index 4b6cd8466..000000000 Binary files a/jme3-blender/testdata/src/main/resources/Blender/2.4x/textures/Grass_256.png and /dev/null differ diff --git a/jme3-blender/testdata/src/main/resources/Blender/2.4x/textures/SandDesert_StartTower.png b/jme3-blender/testdata/src/main/resources/Blender/2.4x/textures/SandDesert_StartTower.png deleted file mode 100644 index 7b2e6ddd2..000000000 Binary files a/jme3-blender/testdata/src/main/resources/Blender/2.4x/textures/SandDesert_StartTower.png and /dev/null differ diff --git a/jme3-blender/testdata/src/main/resources/Blender/2.4x/textures/SkyBox-Mountain.png b/jme3-blender/testdata/src/main/resources/Blender/2.4x/textures/SkyBox-Mountain.png deleted file mode 100644 index ac0821d56..000000000 Binary files a/jme3-blender/testdata/src/main/resources/Blender/2.4x/textures/SkyBox-Mountain.png and /dev/null differ diff --git a/jme3-blender/testdata/src/main/resources/Blender/2.4x/textures/Tar_Cracked.png b/jme3-blender/testdata/src/main/resources/Blender/2.4x/textures/Tar_Cracked.png deleted file mode 100644 index 27ba25f32..000000000 Binary files a/jme3-blender/testdata/src/main/resources/Blender/2.4x/textures/Tar_Cracked.png and /dev/null differ diff --git a/jme3-blender/testdata/src/main/resources/Blender/2.4x/textures/WarningStrip.png b/jme3-blender/testdata/src/main/resources/Blender/2.4x/textures/WarningStrip.png deleted file mode 100644 index 05366a7af..000000000 Binary files a/jme3-blender/testdata/src/main/resources/Blender/2.4x/textures/WarningStrip.png and /dev/null differ diff --git a/jme3-blender/testdata/src/main/resources/Blender/2.5x/BaseMesh_256.blend b/jme3-blender/testdata/src/main/resources/Blender/2.5x/BaseMesh_256.blend deleted file mode 100644 index 2d058d196..000000000 Binary files a/jme3-blender/testdata/src/main/resources/Blender/2.5x/BaseMesh_256.blend and /dev/null differ diff --git a/jme3-blender/testdata/src/main/resources/Blender/2.5x/textures.blend b/jme3-blender/testdata/src/main/resources/Blender/2.5x/textures.blend deleted file mode 100644 index a4f743c17..000000000 Binary files a/jme3-blender/testdata/src/main/resources/Blender/2.5x/textures.blend and /dev/null differ diff --git a/jme3-blender/testdata/src/main/resources/Blender/test.conf b/jme3-blender/testdata/src/main/resources/Blender/test.conf deleted file mode 100644 index 40b630423..000000000 Binary files a/jme3-blender/testdata/src/main/resources/Blender/test.conf and /dev/null differ diff --git a/jme3-blender/testdata/src/main/resources/Models/HoverTank/tankFinalExport.blend b/jme3-blender/testdata/src/main/resources/Models/HoverTank/tankFinalExport.blend deleted file mode 100644 index ee44ffb9c..000000000 Binary files a/jme3-blender/testdata/src/main/resources/Models/HoverTank/tankFinalExport.blend and /dev/null differ diff --git a/jme3-blender/testdata/src/main/resources/Models/TangentBugs/test.blend b/jme3-blender/testdata/src/main/resources/Models/TangentBugs/test.blend deleted file mode 100644 index 51890a238..000000000 Binary files a/jme3-blender/testdata/src/main/resources/Models/TangentBugs/test.blend and /dev/null differ diff --git a/jme3-examples/build.gradle b/jme3-examples/build.gradle index ab352cb73..af9ec627f 100644 --- a/jme3-examples/build.gradle +++ b/jme3-examples/build.gradle @@ -18,7 +18,6 @@ task run(dependsOn: 'build', type:JavaExec) { } dependencies { - compile project(':jme3-blender') compile project(':jme3-core') compile project(':jme3-desktop') compile project(':jme3-effects') diff --git a/settings.gradle b/settings.gradle index ffbf7ec24..fd51b5c41 100644 --- a/settings.gradle +++ b/settings.gradle @@ -12,7 +12,6 @@ include 'jme3-terrain' // Desktop dependent java classes include 'jme3-desktop' -include 'jme3-blender' include 'jme3-lwjgl' if (JavaVersion.current().isJava8Compatible()) { include 'jme3-lwjgl3'