1package com.jme3.scene.plugins.blender.constraints;
2
3import com.jme3.animation.Animation;
4import com.jme3.animation.Bone;
5import com.jme3.math.FastMath;
6import com.jme3.math.Quaternion;
7import com.jme3.math.Transform;
8import com.jme3.scene.Spatial;
9import com.jme3.scene.plugins.blender.BlenderContext;
10import com.jme3.scene.plugins.blender.animations.Ipo;
11import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
12import com.jme3.scene.plugins.blender.file.Structure;
13import com.jme3.scene.plugins.ogre.AnimData;
14
15/**
16 * This class represents 'Rot limit' constraint type in blender.
17 *
18 * @author Marcin Roguski (Kaelthas)
19 */
20/* package */class ConstraintRotLimit extends Constraint {
21	private static final int	LIMIT_XROT	= 0x01;
22	private static final int	LIMIT_YROT	= 0x02;
23	private static final int	LIMIT_ZROT	= 0x04;
24
25	protected float[][]			limits		= new float[3][2];
26	protected int				flag;
27	protected boolean			updated;
28
29	/**
30	 * This constructor creates the constraint instance.
31	 *
32	 * @param constraintStructure
33	 *            the constraint's structure (bConstraint clss in blender 2.49).
34	 * @param ownerOMA
35	 *            the old memory address of the constraint owner
36	 * @param influenceIpo
37	 *            the ipo curve of the influence factor
38	 * @param blenderContext
39	 *            the blender context
40	 * @throws BlenderFileException
41	 *             this exception is thrown when the blender file is somehow
42	 *             corrupted
43	 */
44	public ConstraintRotLimit(Structure constraintStructure, Long ownerOMA, Ipo influenceIpo, BlenderContext blenderContext) throws BlenderFileException {
45		super(constraintStructure, ownerOMA, influenceIpo, blenderContext);
46
47		flag = ((Number) data.getFieldValue("flag")).intValue();
48		if (blenderContext.getBlenderKey().isFixUpAxis() && owner.spatial != null) {
49			limits[0][0] = ((Number) data.getFieldValue("xmin")).floatValue();
50			limits[0][1] = ((Number) data.getFieldValue("xmax")).floatValue();
51			limits[2][0] = -((Number) data.getFieldValue("ymin")).floatValue();
52			limits[2][1] = -((Number) data.getFieldValue("ymax")).floatValue();
53			limits[1][0] = ((Number) data.getFieldValue("zmin")).floatValue();
54			limits[1][1] = ((Number) data.getFieldValue("zmax")).floatValue();
55
56			// swapping Y and X limits flag in the bitwise flag
57			int limitY = flag & LIMIT_YROT;
58			int limitZ = flag & LIMIT_ZROT;
59			flag &= LIMIT_XROT;// clear the other flags to swap them
60			flag |= limitY << 1;
61			flag |= limitZ >> 1;
62		} else {
63			limits[0][0] = ((Number) data.getFieldValue("xmin")).floatValue();
64			limits[0][1] = ((Number) data.getFieldValue("xmax")).floatValue();
65			limits[1][0] = ((Number) data.getFieldValue("ymin")).floatValue();
66			limits[1][1] = ((Number) data.getFieldValue("ymax")).floatValue();
67			limits[2][0] = ((Number) data.getFieldValue("zmin")).floatValue();
68			limits[2][1] = ((Number) data.getFieldValue("zmax")).floatValue();
69		}
70
71		// until blender 2.49 the rotations values were stored in degrees
72		if (blenderContext.getBlenderVersion() <= 249) {
73			for (int i = 0; i < limits.length; ++i) {
74				limits[i][0] *= FastMath.DEG_TO_RAD;
75				limits[i][1] *= FastMath.DEG_TO_RAD;
76			}
77		}
78	}
79
80	@Override
81	protected void bakeConstraint() {
82		this.update();
83		Object owner = this.owner.getObject();
84		AnimData animData = blenderContext.getAnimData(this.owner.getOma());
85		if (animData != null) {
86			for (Animation animation : animData.anims) {
87				BlenderTrack track = this.getTrack(owner, animData.skeleton, animation);
88				Quaternion[] rotations = track.getRotations();
89				float[] angles = new float[3];
90				int maxFrames = rotations.length;
91				for (int frame = 0; frame < maxFrames; ++frame) {
92					rotations[frame].toAngles(angles);
93					this.rotLimit(angles, ipo.calculateValue(frame));
94					rotations[frame].fromAngles(angles);
95				}
96				track.setKeyframes(track.getTimes(), track.getTranslations(), rotations, track.getScales());
97			}
98		}
99
100		if (owner instanceof Spatial) {
101			Transform ownerTransform = this.owner.getTransform();
102			float[] angles = ownerTransform.getRotation().toAngles(null);
103			this.rotLimit(angles, ipo.calculateValue(0));
104			ownerTransform.getRotation().fromAngles(angles);
105			this.owner.applyTransform(ownerTransform);
106		}
107	}
108
109	/**
110	 * This method computes new constrained angles.
111	 *
112	 * @param angles
113	 *            angles to be altered
114	 * @param influence
115	 *            the alteration influence
116	 */
117	private void rotLimit(float[] angles, float influence) {
118		if ((flag & LIMIT_XROT) != 0) {
119			float difference = 0.0f;
120			if (angles[0] < limits[0][0]) {
121				difference = (angles[0] - limits[0][0]) * influence;
122			} else if (angles[0] > limits[0][1]) {
123				difference = (angles[0] - limits[0][1]) * influence;
124			}
125			angles[0] -= difference;
126		}
127		if ((flag & LIMIT_YROT) != 0) {
128			float difference = 0.0f;
129			if (angles[1] < limits[1][0]) {
130				difference = (angles[1] - limits[1][0]) * influence;
131			} else if (angles[1] > limits[1][1]) {
132				difference = (angles[1] - limits[1][1]) * influence;
133			}
134			angles[1] -= difference;
135		}
136		if ((flag & LIMIT_ZROT) != 0) {
137			float difference = 0.0f;
138			if (angles[2] < limits[2][0]) {
139				difference = (angles[2] - limits[2][0]) * influence;
140			} else if (angles[2] > limits[2][1]) {
141				difference = (angles[2] - limits[2][1]) * influence;
142			}
143			angles[2] -= difference;
144		}
145	}
146
147	/**
148	 * This method is called before baking (performes its operations only once).
149	 * It is important to update the state of the limits and owner/target before
150	 * baking the constraint.
151	 */
152	private void update() {
153		if (!updated) {
154			updated = true;
155			if (owner != null) {
156				owner.update();
157			}
158			if (target != null) {
159				target.update();
160			}
161			if (this.owner.getObject() instanceof Bone) {// for bones we need to
162															// change the sign
163															// of the limits
164				for (int i = 0; i < limits.length; ++i) {
165					limits[i][0] *= -1;
166					limits[i][1] *= -1;
167				}
168			}
169
170			// sorting the limits (lower is always first)
171			for (int i = 0; i < limits.length; ++i) {
172				if (limits[i][0] > limits[i][1]) {
173					float temp = limits[i][0];
174					limits[i][0] = limits[i][1];
175					limits[i][1] = temp;
176				}
177			}
178		}
179	}
180}
181