Home | History | Annotate | Download | only in constraints
      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 	 */
     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 	 */
     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 	 */
    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 
    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
    200 	public boolean shouldBeLoaded(Structure structure, BlenderContext blenderContext) {
    201 		return true;
    202 	}
    203 }
    204