159b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartapackage com.jme3.scene.plugins.blender.modifiers;
259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
359b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.math.Vector3f;
459b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.scene.Geometry;
559b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.scene.Mesh;
659b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.scene.Node;
759b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.scene.Spatial;
859b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.scene.VertexBuffer.Type;
959b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.scene.plugins.blender.BlenderContext;
1059b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
1159b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.scene.plugins.blender.file.Pointer;
1259b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.scene.plugins.blender.file.Structure;
1359b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.scene.plugins.blender.objects.ObjectHelper;
1459b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport java.nio.FloatBuffer;
1559b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport java.nio.IntBuffer;
1659b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport java.util.ArrayList;
1759b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport java.util.HashMap;
1859b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport java.util.List;
1959b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport java.util.Map;
2059b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport java.util.logging.Level;
2159b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport java.util.logging.Logger;
2259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
2359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta/**
2459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * This modifier allows to array modifier to the object.
2559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta *
2659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * @author Marcin Roguski (Kaelthas)
2759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta */
2859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta/*package*/ class MirrorModifier extends Modifier {
2959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta	private static final Logger LOGGER = Logger.getLogger(MirrorModifier.class.getName());
3059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
3159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta	/** Parameters of the modifier. */
3259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta	private Map<String, Object> modifierData = new HashMap<String, Object>();
3359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
3459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta	/**
3559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta	 * This constructor reads mirror data from the modifier structure. The
3659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta	 * stored data is a map of parameters for mirror modifier. No additional data
3759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta	 * is loaded.
3859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta	 * When the modifier is applied it is necessary to get the newly created node.
3959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta	 *
4059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta	 * @param objectStructure
4159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta	 *            the structure of the object
4259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta	 * @param modifierStructure
4359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta	 *            the structure of the modifier
4459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta	 * @param blenderContext
4559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta	 *            the blender context
4659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta	 * @throws BlenderFileException
4759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta	 *             this exception is thrown when the blender file is somehow
4859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta	 *             corrupted
4959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta	 */
5059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta	public MirrorModifier(Structure modifierStructure, BlenderContext blenderContext) {
5159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta		if(this.validate(modifierStructure, blenderContext)) {
5259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta			modifierData.put("flag", modifierStructure.getFieldValue("flag"));
5359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta			modifierData.put("tolerance", modifierStructure.getFieldValue("tolerance"));
5459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta	        Pointer pMirrorOb = (Pointer) modifierStructure.getFieldValue("mirror_ob");
5559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta	        if (pMirrorOb.isNotNull()) {
5659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta	        	modifierData.put("mirrorob", pMirrorOb);
5759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta	        }
5859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta		}
5959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta	}
6059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
6159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta	@Override
6259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta	public Node apply(Node node, BlenderContext blenderContext) {
6359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta		if(invalid) {
6459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta			LOGGER.log(Level.WARNING, "Mirror modifier is invalid! Cannot be applied to: {0}", node.getName());
6559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta			return node;
6659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta		}
6759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
6859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        int flag = ((Number) modifierData.get("flag")).intValue();
6959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        float[] mirrorFactor = new float[]{
7059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            (flag & 0x08) != 0 ? -1.0f : 1.0f,
7159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            (flag & 0x10) != 0 ? -1.0f : 1.0f,
7259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            (flag & 0x20) != 0 ? -1.0f : 1.0f
7359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        };
7459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        float[] center = new float[]{0.0f, 0.0f, 0.0f};
7559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        Pointer pObject = (Pointer) modifierData.get("mirrorob");
7659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        if (pObject != null) {
7759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            Structure objectStructure;
7859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            try {
7959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                objectStructure = pObject.fetchData(blenderContext.getInputStream()).get(0);
8059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                ObjectHelper objectHelper = blenderContext.getHelper(ObjectHelper.class);
8159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                Node object = (Node) objectHelper.toObject(objectStructure, blenderContext);
8259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                if (object != null) {
8359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    Vector3f translation = object.getWorldTranslation();
8459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    center[0] = translation.x;
8559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    center[1] = translation.y;
8659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    center[2] = translation.z;
8759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                }
8859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            } catch (BlenderFileException e) {
8959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                LOGGER.log(Level.SEVERE, "Cannot load mirror''s reference object. Cause: {0}", e.getLocalizedMessage());
9059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            }
9159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
9259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        float tolerance = ((Number) modifierData.get("tolerance")).floatValue();
9359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        boolean mirrorU = (flag & 0x01) != 0;
9459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        boolean mirrorV = (flag & 0x02) != 0;
9559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta//		boolean mirrorVGroup = (flag & 0x20) != 0;
9659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
9759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        List<Geometry> geometriesToAdd = new ArrayList<Geometry>();
9859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        for (int mirrorIndex = 0; mirrorIndex < 3; ++mirrorIndex) {
9959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            if (mirrorFactor[mirrorIndex] == -1.0f) {
10059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                for (Spatial spatial : node.getChildren()) {
10159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    if (spatial instanceof Geometry) {
10259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                        Mesh mesh = ((Geometry) spatial).getMesh();
10359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                        Mesh clone = mesh.deepClone();
10459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
10559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                        // getting buffers
10659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                        FloatBuffer position = mesh.getFloatBuffer(Type.Position);
10759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                        FloatBuffer bindPosePosition = mesh.getFloatBuffer(Type.BindPosePosition);
10859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
10959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                        FloatBuffer clonePosition = clone.getFloatBuffer(Type.Position);
11059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                        FloatBuffer cloneBindPosePosition = clone.getFloatBuffer(Type.BindPosePosition);
11159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                        FloatBuffer cloneNormals = clone.getFloatBuffer(Type.Normal);
11259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                        FloatBuffer cloneBindPoseNormals = clone.getFloatBuffer(Type.BindPoseNormal);
11359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                        IntBuffer cloneIndexes = (IntBuffer) clone.getBuffer(Type.Index).getData();
11459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
11559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                        // modyfying data
11659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                        for (int i = mirrorIndex; i < clonePosition.limit(); i += 3) {
11759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                            float value = clonePosition.get(i);
11859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                            float d = center[mirrorIndex] - value;
11959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
12059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                            if (Math.abs(d) <= tolerance) {
12159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                                clonePosition.put(i, center[mirrorIndex]);
12259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                                cloneBindPosePosition.put(i, center[mirrorIndex]);
12359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                                position.put(i, center[mirrorIndex]);
12459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                                bindPosePosition.put(i, center[mirrorIndex]);
12559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                            } else {
12659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                                clonePosition.put(i, value + 2.0f * d);
12759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                                cloneBindPosePosition.put(i, value + 2.0f * d);
12859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                            }
12959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                            cloneNormals.put(i, -cloneNormals.get(i));
13059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                            cloneBindPoseNormals.put(i, -cloneNormals.get(i));
13159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
13259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                            //modifying clone indexes
13359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                            int vertexIndex = (i - mirrorIndex) / 3;
13459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                            if (vertexIndex % 3 == 0 && vertexIndex<cloneIndexes.limit()) {
13559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                                int index = cloneIndexes.get(vertexIndex + 2);
13659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                                cloneIndexes.put(vertexIndex + 2, cloneIndexes.get(vertexIndex + 1));
13759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                                cloneIndexes.put(vertexIndex + 1, index);
13859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                            }
13959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                        }
14059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
14159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                        if (mirrorU) {
14259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                            FloatBuffer cloneUVs = (FloatBuffer) clone.getBuffer(Type.TexCoord).getData();
14359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                            for (int i = 0; i < cloneUVs.limit(); i += 2) {
14459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                                cloneUVs.put(i, 1.0f - cloneUVs.get(i));
14559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                            }
14659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                        }
14759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                        if (mirrorV) {
14859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                            FloatBuffer cloneUVs = (FloatBuffer) clone.getBuffer(Type.TexCoord).getData();
14959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                            for (int i = 1; i < cloneUVs.limit(); i += 2) {
15059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                                cloneUVs.put(i, 1.0f - cloneUVs.get(i));
15159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                            }
15259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                        }
15359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
15459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                        Geometry geometry = new Geometry(null, clone);
15559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                        geometry.setMaterial(((Geometry) spatial).getMaterial());
15659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                        geometriesToAdd.add(geometry);
15759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    }
15859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                }
15959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
16059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                // adding meshes to node
16159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                for (Geometry geometry : geometriesToAdd) {
16259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    node.attachChild(geometry);
16359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                }
16459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                geometriesToAdd.clear();
16559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            }
16659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
16759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        return node;
16859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta	}
16959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
17059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta	@Override
17159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta	public String getType() {
17259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta		return Modifier.MIRROR_MODIFIER_DATA;
17359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta	}
17459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta}
175