aboutsummaryrefslogtreecommitdiff
path: root/engine/src/blender/com/jme3/scene/plugins/blender/modifiers/MirrorModifier.java
blob: d44147666e9e597c3ffa73a5fab9db0af6b9a5a9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
package com.jme3.scene.plugins.blender.modifiers;

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.VertexBuffer.Type;
import com.jme3.scene.plugins.blender.BlenderContext;
import com.jme3.scene.plugins.blender.exceptions.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 java.nio.FloatBuffer;
import java.nio.IntBuffer;
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;

/**
 * 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());
	
	/** Parameters of the modifier. */
	private Map<String, Object> modifierData = new HashMap<String, Object>();
	
	/**
	 * 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)) {
			modifierData.put("flag", modifierStructure.getFieldValue("flag"));
			modifierData.put("tolerance", modifierStructure.getFieldValue("tolerance"));
	        Pointer pMirrorOb = (Pointer) modifierStructure.getFieldValue("mirror_ob");
	        if (pMirrorOb.isNotNull()) {
	        	modifierData.put("mirrorob", pMirrorOb);
	        }
		}
	}
	
	@Override
	public Node apply(Node node, BlenderContext blenderContext) {
		if(invalid) {
			LOGGER.log(Level.WARNING, "Mirror modifier is invalid! Cannot be applied to: {0}", node.getName());
			return node;
		}
		
        int flag = ((Number) modifierData.get("flag")).intValue();
        float[] mirrorFactor = new float[]{
            (flag & 0x08) != 0 ? -1.0f : 1.0f,
            (flag & 0x10) != 0 ? -1.0f : 1.0f,
            (flag & 0x20) != 0 ? -1.0f : 1.0f
        };
        float[] center = new float[]{0.0f, 0.0f, 0.0f};
        Pointer pObject = (Pointer) modifierData.get("mirrorob");
        if (pObject != null) {
            Structure objectStructure;
            try {
                objectStructure = pObject.fetchData(blenderContext.getInputStream()).get(0);
                ObjectHelper objectHelper = blenderContext.getHelper(ObjectHelper.class);
                Node object = (Node) objectHelper.toObject(objectStructure, blenderContext);
                if (object != null) {
                    Vector3f translation = object.getWorldTranslation();
                    center[0] = translation.x;
                    center[1] = translation.y;
                    center[2] = translation.z;
                }
            } catch (BlenderFileException e) {
                LOGGER.log(Level.SEVERE, "Cannot load mirror''s reference object. Cause: {0}", e.getLocalizedMessage());
            }
        }
        float tolerance = ((Number) modifierData.get("tolerance")).floatValue();
        boolean mirrorU = (flag & 0x01) != 0;
        boolean mirrorV = (flag & 0x02) != 0;
//		boolean mirrorVGroup = (flag & 0x20) != 0;

        List<Geometry> geometriesToAdd = new ArrayList<Geometry>();
        for (int mirrorIndex = 0; mirrorIndex < 3; ++mirrorIndex) {
            if (mirrorFactor[mirrorIndex] == -1.0f) {
                for (Spatial spatial : node.getChildren()) {
                    if (spatial instanceof Geometry) {
                        Mesh mesh = ((Geometry) spatial).getMesh();
                        Mesh clone = mesh.deepClone();

                        // getting buffers
                        FloatBuffer position = mesh.getFloatBuffer(Type.Position);
                        FloatBuffer bindPosePosition = mesh.getFloatBuffer(Type.BindPosePosition);

                        FloatBuffer clonePosition = clone.getFloatBuffer(Type.Position);
                        FloatBuffer cloneBindPosePosition = clone.getFloatBuffer(Type.BindPosePosition);
                        FloatBuffer cloneNormals = clone.getFloatBuffer(Type.Normal);
                        FloatBuffer cloneBindPoseNormals = clone.getFloatBuffer(Type.BindPoseNormal);
                        IntBuffer cloneIndexes = (IntBuffer) clone.getBuffer(Type.Index).getData();

                        // modyfying data
                        for (int i = mirrorIndex; i < clonePosition.limit(); i += 3) {
                            float value = clonePosition.get(i);
                            float d = center[mirrorIndex] - value;

                            if (Math.abs(d) <= tolerance) {
                                clonePosition.put(i, center[mirrorIndex]);
                                cloneBindPosePosition.put(i, center[mirrorIndex]);
                                position.put(i, center[mirrorIndex]);
                                bindPosePosition.put(i, center[mirrorIndex]);
                            } else {
                                clonePosition.put(i, value + 2.0f * d);
                                cloneBindPosePosition.put(i, value + 2.0f * d);
                            }
                            cloneNormals.put(i, -cloneNormals.get(i));
                            cloneBindPoseNormals.put(i, -cloneNormals.get(i));

                            //modifying clone indexes
                            int vertexIndex = (i - mirrorIndex) / 3;
                            if (vertexIndex % 3 == 0 && vertexIndex<cloneIndexes.limit()) {
                                int index = cloneIndexes.get(vertexIndex + 2);
                                cloneIndexes.put(vertexIndex + 2, cloneIndexes.get(vertexIndex + 1));
                                cloneIndexes.put(vertexIndex + 1, index);
                            }
                        }

                        if (mirrorU) {
                            FloatBuffer cloneUVs = (FloatBuffer) clone.getBuffer(Type.TexCoord).getData();
                            for (int i = 0; i < cloneUVs.limit(); i += 2) {
                                cloneUVs.put(i, 1.0f - cloneUVs.get(i));
                            }
                        }
                        if (mirrorV) {
                            FloatBuffer cloneUVs = (FloatBuffer) clone.getBuffer(Type.TexCoord).getData();
                            for (int i = 1; i < cloneUVs.limit(); i += 2) {
                                cloneUVs.put(i, 1.0f - cloneUVs.get(i));
                            }
                        }

                        Geometry geometry = new Geometry(null, clone);
                        geometry.setMaterial(((Geometry) spatial).getMaterial());
                        geometriesToAdd.add(geometry);
                    }
                }

                // adding meshes to node
                for (Geometry geometry : geometriesToAdd) {
                    node.attachChild(geometry);
                }
                geometriesToAdd.clear();
            }
        }
        return node;
	}
	
	@Override
	public String getType() {
		return Modifier.MIRROR_MODIFIER_DATA;
	}
}