• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package com.jme3.scene.plugins.blender.constraints;
2 
3 import java.lang.reflect.InvocationTargetException;
4 import java.util.ArrayList;
5 import java.util.HashMap;
6 import java.util.List;
7 import java.util.Map;
8 import java.util.logging.Logger;
9 
10 import com.jme3.scene.plugins.blender.AbstractBlenderHelper;
11 import com.jme3.scene.plugins.blender.BlenderContext;
12 import com.jme3.scene.plugins.blender.animations.Ipo;
13 import com.jme3.scene.plugins.blender.animations.IpoHelper;
14 import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
15 import com.jme3.scene.plugins.blender.file.Pointer;
16 import com.jme3.scene.plugins.blender.file.Structure;
17 
18 /**
19  * This class should be used for constraint calculations.
20  * @author Marcin Roguski (Kaelthas)
21  */
22 public class ConstraintHelper extends AbstractBlenderHelper {
23 	private static final Logger LOGGER = Logger.getLogger(ConstraintHelper.class.getName());
24 
25 	private static final Map<String, Class<? extends Constraint>> constraintClasses = new HashMap<String, Class<? extends Constraint>>(22);
26 	static {
27 		constraintClasses.put("bActionConstraint", ConstraintAction.class);
28 		constraintClasses.put("bChildOfConstraint", ConstraintChildOf.class);
29 		constraintClasses.put("bClampToConstraint", ConstraintClampTo.class);
30 		constraintClasses.put("bDistLimitConstraint", ConstraintDistLimit.class);
31 		constraintClasses.put("bFollowPathConstraint", ConstraintFollowPath.class);
32 		constraintClasses.put("bKinematicConstraint", ConstraintInverseKinematics.class);
33 		constraintClasses.put("bLockTrackConstraint", ConstraintLockTrack.class);
34 		constraintClasses.put("bLocateLikeConstraint", ConstraintLocLike.class);
35 		constraintClasses.put("bLocLimitConstraint", ConstraintLocLimit.class);
36 		constraintClasses.put("bMinMaxConstraint", ConstraintMinMax.class);
37 		constraintClasses.put("bNullConstraint", ConstraintNull.class);
38 		constraintClasses.put("bPythonConstraint", ConstraintPython.class);
39 		constraintClasses.put("bRigidBodyJointConstraint", ConstraintRigidBodyJoint.class);
40 		constraintClasses.put("bRotateLikeConstraint", ConstraintRotLike.class);
41 		constraintClasses.put("bShrinkWrapConstraint", ConstraintShrinkWrap.class);
42 		constraintClasses.put("bSizeLikeConstraint", ConstraintSizeLike.class);
43 		constraintClasses.put("bSizeLimitConstraint", ConstraintSizeLimit.class);
44 		constraintClasses.put("bStretchToConstraint", ConstraintStretchTo.class);
45 		constraintClasses.put("bTransformConstraint", ConstraintTransform.class);
46 		constraintClasses.put("bRotLimitConstraint", ConstraintRotLimit.class);
47 		//Blender 2.50+
48 		constraintClasses.put("bSplineIKConstraint", ConstraintSplineInverseKinematic.class);
49 		constraintClasses.put("bDampTrackConstraint", ConstraintDampTrack.class);
50 		constraintClasses.put("bPivotConstraint", ConstraintDampTrack.class);
51 	}
52 
53 	/**
54 	 * Helper constructor. It's main task is to generate the affection functions. These functions are common to all
55 	 * ConstraintHelper instances. Unfortunately this constructor might grow large. If it becomes too large - I shall
56 	 * consider refactoring. The constructor parses the given blender version and stores the result. Some
57 	 * functionalities may differ in different blender versions.
58 	 * @param blenderVersion
59 	 *        the version read from the blend file
60 	 * @param fixUpAxis
61      *        a variable that indicates if the Y asxis is the UP axis or not
62 	 */
ConstraintHelper(String blenderVersion, BlenderContext blenderContext, boolean fixUpAxis)63 	public ConstraintHelper(String blenderVersion, BlenderContext blenderContext, boolean fixUpAxis) {
64 		super(blenderVersion, fixUpAxis);
65 	}
66 
67 	/**
68 	 * This method reads constraints for for the given structure. The
69 	 * constraints are loaded only once for object/bone.
70 	 *
71 	 * @param objectStructure
72 	 *            the structure we read constraint's for
73 	 * @param blenderContext
74 	 *            the blender context
75 	 * @throws BlenderFileException
76 	 */
loadConstraints(Structure objectStructure, BlenderContext blenderContext)77 	public void loadConstraints(Structure objectStructure, BlenderContext blenderContext) throws BlenderFileException {
78 		LOGGER.fine("Loading constraints.");
79 		// reading influence ipos for the constraints
80 		IpoHelper ipoHelper = blenderContext.getHelper(IpoHelper.class);
81 		Map<String, Map<String, Ipo>> constraintsIpos = new HashMap<String, Map<String, Ipo>>();
82 		Pointer pActions = (Pointer) objectStructure.getFieldValue("action");
83 		if (pActions.isNotNull()) {
84 			List<Structure> actions = pActions.fetchData(blenderContext.getInputStream());
85 			for (Structure action : actions) {
86 				Structure chanbase = (Structure) action.getFieldValue("chanbase");
87 				List<Structure> actionChannels = chanbase.evaluateListBase(blenderContext);
88 				for (Structure actionChannel : actionChannels) {
89 					Map<String, Ipo> ipos = new HashMap<String, Ipo>();
90 					Structure constChannels = (Structure) actionChannel.getFieldValue("constraintChannels");
91 					List<Structure> constraintChannels = constChannels.evaluateListBase(blenderContext);
92 					for (Structure constraintChannel : constraintChannels) {
93 						Pointer pIpo = (Pointer) constraintChannel.getFieldValue("ipo");
94 						if (pIpo.isNotNull()) {
95 							String constraintName = constraintChannel.getFieldValue("name").toString();
96 							Ipo ipo = ipoHelper.fromIpoStructure(pIpo.fetchData(blenderContext.getInputStream()).get(0), blenderContext);
97 							ipos.put(constraintName, ipo);
98 						}
99 					}
100 					String actionName = actionChannel.getFieldValue("name").toString();
101 					constraintsIpos.put(actionName, ipos);
102 				}
103 			}
104 		}
105 
106 		//loading constraints connected with the object's bones
107 		Pointer pPose = (Pointer) objectStructure.getFieldValue("pose");
108 		if (pPose.isNotNull()) {
109 			List<Structure> poseChannels = ((Structure) pPose.fetchData(blenderContext.getInputStream()).get(0).getFieldValue("chanbase")).evaluateListBase(blenderContext);
110 			for (Structure poseChannel : poseChannels) {
111 				List<Constraint> constraintsList = new ArrayList<Constraint>();
112 				Long boneOMA = Long.valueOf(((Pointer) poseChannel.getFieldValue("bone")).getOldMemoryAddress());
113 
114 				//the name is read directly from structure because bone might not yet be loaded
115 				String name = blenderContext.getFileBlock(boneOMA).getStructure(blenderContext).getFieldValue("name").toString();
116 				List<Structure> constraints = ((Structure) poseChannel.getFieldValue("constraints")).evaluateListBase(blenderContext);
117 				for (Structure constraint : constraints) {
118 					String constraintName = constraint.getFieldValue("name").toString();
119 					Map<String, Ipo> ipoMap = constraintsIpos.get(name);
120 					Ipo ipo = ipoMap==null ? null : ipoMap.get(constraintName);
121 					if (ipo == null) {
122 						float enforce = ((Number) constraint.getFieldValue("enforce")).floatValue();
123 						ipo = ipoHelper.fromValue(enforce);
124 					}
125 					constraintsList.add(this.createConstraint(constraint, boneOMA, ipo, blenderContext));
126 				}
127 				blenderContext.addConstraints(boneOMA, constraintsList);
128 			}
129 		}
130 
131 		//loading constraints connected with the object itself
132 		List<Structure> constraints = ((Structure)objectStructure.getFieldValue("constraints")).evaluateListBase(blenderContext);
133 		List<Constraint> constraintsList = new ArrayList<Constraint>(constraints.size());
134 
135 		for(Structure constraint : constraints) {
136 			String constraintName = constraint.getFieldValue("name").toString();
137 			String objectName = objectStructure.getName();
138 
139 			Map<String, Ipo> objectConstraintsIpos = constraintsIpos.get(objectName);
140 			Ipo ipo = objectConstraintsIpos!=null ? objectConstraintsIpos.get(constraintName) : null;
141 			if (ipo == null) {
142 				float enforce = ((Number) constraint.getFieldValue("enforce")).floatValue();
143 				ipo = ipoHelper.fromValue(enforce);
144 			}
145 			constraintsList.add(this.createConstraint(constraint, objectStructure.getOldMemoryAddress(), ipo, blenderContext));
146 		}
147 		blenderContext.addConstraints(objectStructure.getOldMemoryAddress(), constraintsList);
148 	}
149 
150 	/**
151 	 * This method creates the constraint instance.
152 	 *
153 	 * @param constraintStructure
154 	 *            the constraint's structure (bConstraint clss in blender 2.49).
155 	 * @param ownerOMA
156 	 *            the old memory address of the constraint's owner
157 	 * @param influenceIpo
158 	 *            the ipo curve of the influence factor
159 	 * @param blenderContext
160 	 *            the blender context
161 	 * @throws BlenderFileException
162 	 *             this exception is thrown when the blender file is somehow
163 	 *             corrupted
164 	 */
createConstraint(Structure constraintStructure, Long ownerOMA, Ipo influenceIpo, BlenderContext blenderContext)165 	protected Constraint createConstraint(Structure constraintStructure, Long ownerOMA, Ipo influenceIpo,
166 						BlenderContext blenderContext) throws BlenderFileException {
167 		String constraintClassName = this.getConstraintClassName(constraintStructure, blenderContext);
168 		Class<? extends Constraint> constraintClass = constraintClasses.get(constraintClassName);
169 		if(constraintClass != null) {
170 			try {
171 				return (Constraint) constraintClass.getDeclaredConstructors()[0].newInstance(constraintStructure, ownerOMA, influenceIpo,
172 						blenderContext);
173 			} catch (IllegalArgumentException e) {
174 				throw new BlenderFileException(e.getLocalizedMessage(), e);
175 			} catch (SecurityException e) {
176 				throw new BlenderFileException(e.getLocalizedMessage(), e);
177 			} catch (InstantiationException e) {
178 				throw new BlenderFileException(e.getLocalizedMessage(), e);
179 			} catch (IllegalAccessException e) {
180 				throw new BlenderFileException(e.getLocalizedMessage(), e);
181 			} catch (InvocationTargetException e) {
182 				throw new BlenderFileException(e.getLocalizedMessage(), e);
183 			}
184 		} else {
185 			throw new BlenderFileException("Unknown constraint type: " + constraintClassName);
186 		}
187 	}
188 
getConstraintClassName(Structure constraintStructure, BlenderContext blenderContext)189 	protected String getConstraintClassName(Structure constraintStructure, BlenderContext blenderContext) throws BlenderFileException {
190 		Pointer pData = (Pointer)constraintStructure.getFieldValue("data");
191 		if(pData.isNotNull()) {
192 			Structure data = pData.fetchData(blenderContext.getInputStream()).get(0);
193 			return data.getType();
194 
195 		}
196 		return constraintStructure.getType();
197 	}
198 
199 	@Override
shouldBeLoaded(Structure structure, BlenderContext blenderContext)200 	public boolean shouldBeLoaded(Structure structure, BlenderContext blenderContext) {
201 		return true;
202 	}
203 }
204