All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.jme3.scene.plugins.blender.constraints.ConstraintHelper Maven / Gradle / Ivy

The newest version!
package com.jme3.scene.plugins.blender.constraints;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Logger;

import com.jme3.animation.Bone;
import com.jme3.animation.Skeleton;
import com.jme3.math.Matrix4f;
import com.jme3.math.Quaternion;
import com.jme3.math.Transform;
import com.jme3.math.Vector3f;
import com.jme3.scene.Spatial;
import com.jme3.scene.plugins.blender.AbstractBlenderHelper;
import com.jme3.scene.plugins.blender.BlenderContext;
import com.jme3.scene.plugins.blender.BlenderContext.LoadedDataType;
import com.jme3.scene.plugins.blender.animations.AnimationHelper;
import com.jme3.scene.plugins.blender.animations.BoneContext;
import com.jme3.scene.plugins.blender.animations.Ipo;
import com.jme3.scene.plugins.blender.file.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 com.jme3.util.TempVars;

/**
 * This class should be used for constraint calculations.
 * 
 * @author Marcin Roguski (Kaelthas)
 */
public class ConstraintHelper extends AbstractBlenderHelper {
    private static final Logger     LOGGER                      = Logger.getLogger(ConstraintHelper.class.getName());

    /**
     * Helper constructor.
     * 
     * @param blenderVersion
     *            the version read from the blend file
     * @param blenderContext
     *            the blender context
     */
    public ConstraintHelper(String blenderVersion, BlenderContext blenderContext) {
        super(blenderVersion, blenderContext);
    }

    /**
     * This method reads constraints for for the given structure. The
     * constraints are loaded only once for object/bone.
     * 
     * @param objectStructure
     *            the structure we read constraint's for
     * @param blenderContext
     *            the blender context
     * @throws BlenderFileException
     */
    public void loadConstraints(Structure objectStructure, BlenderContext blenderContext) throws BlenderFileException {
        LOGGER.fine("Loading constraints.");
        // reading influence ipos for the constraints
        AnimationHelper animationHelper = blenderContext.getHelper(AnimationHelper.class);
        Map> constraintsIpos = new HashMap>();
        Pointer pActions = (Pointer) objectStructure.getFieldValue("action");
        if (pActions.isNotNull()) {
            List actions = pActions.fetchData();
            for (Structure action : actions) {
                Structure chanbase = (Structure) action.getFieldValue("chanbase");
                List actionChannels = chanbase.evaluateListBase();
                for (Structure actionChannel : actionChannels) {
                    Map ipos = new HashMap();
                    Structure constChannels = (Structure) actionChannel.getFieldValue("constraintChannels");
                    List constraintChannels = constChannels.evaluateListBase();
                    for (Structure constraintChannel : constraintChannels) {
                        Pointer pIpo = (Pointer) constraintChannel.getFieldValue("ipo");
                        if (pIpo.isNotNull()) {
                            String constraintName = constraintChannel.getFieldValue("name").toString();
                            Ipo ipo = animationHelper.fromIpoStructure(pIpo.fetchData().get(0), blenderContext);
                            ipos.put(constraintName, ipo);
                        }
                    }
                    String actionName = actionChannel.getFieldValue("name").toString();
                    constraintsIpos.put(actionName, ipos);
                }
            }
        }

        // loading constraints connected with the object's bones
        Pointer pPose = (Pointer) objectStructure.getFieldValue("pose");
        if (pPose.isNotNull()) {
            List poseChannels = ((Structure) pPose.fetchData().get(0).getFieldValue("chanbase")).evaluateListBase();
            for (Structure poseChannel : poseChannels) {
                List constraintsList = new ArrayList();
                Long boneOMA = Long.valueOf(((Pointer) poseChannel.getFieldValue("bone")).getOldMemoryAddress());

                // the name is read directly from structure because bone might
                // not yet be loaded
                String name = blenderContext.getFileBlock(boneOMA).getStructure(blenderContext).getFieldValue("name").toString();
                List constraints = ((Structure) poseChannel.getFieldValue("constraints")).evaluateListBase();
                for (Structure constraint : constraints) {
                    String constraintName = constraint.getFieldValue("name").toString();
                    Map ipoMap = constraintsIpos.get(name);
                    Ipo ipo = ipoMap == null ? null : ipoMap.get(constraintName);
                    if (ipo == null) {
                        float enforce = ((Number) constraint.getFieldValue("enforce")).floatValue();
                        ipo = animationHelper.fromValue(enforce);
                    }
                    constraintsList.add(new BoneConstraint(constraint, boneOMA, ipo, blenderContext));
                }
                blenderContext.addConstraints(boneOMA, constraintsList);
            }
        }

        // loading constraints connected with the object itself
        List constraints = ((Structure) objectStructure.getFieldValue("constraints")).evaluateListBase();
        if (constraints != null && constraints.size() > 0) {
            Pointer pData = (Pointer) objectStructure.getFieldValue("data");
            String dataType = pData.isNotNull() ? pData.fetchData().get(0).getType() : null;
            List constraintsList = new ArrayList(constraints.size());

            for (Structure constraint : constraints) {
                String constraintName = constraint.getFieldValue("name").toString();
                String objectName = objectStructure.getName();

                Map objectConstraintsIpos = constraintsIpos.get(objectName);
                Ipo ipo = objectConstraintsIpos != null ? objectConstraintsIpos.get(constraintName) : null;
                if (ipo == null) {
                    float enforce = ((Number) constraint.getFieldValue("enforce")).floatValue();
                    ipo = animationHelper.fromValue(enforce);
                }

                constraintsList.add(this.createConstraint(dataType, constraint, objectStructure.getOldMemoryAddress(), ipo, blenderContext));
            }
            blenderContext.addConstraints(objectStructure.getOldMemoryAddress(), constraintsList);
        }
    }

    /**
     * This method creates a proper constraint object depending on the object's
     * data type. Supported data types: 
  • Mesh
  • Armature
  • Camera
  • * Lamp Bone constraints are created in a different place. * * @param dataType * the type of the object's data * @param constraintStructure * the constraint structure * @param ownerOMA * the owner OMA * @param influenceIpo * the influence interpolation curve * @param blenderContext * the blender context * @return constraint object for the required type * @throws BlenderFileException * thrown when problems with blender file occurred */ private Constraint createConstraint(String dataType, Structure constraintStructure, Long ownerOMA, Ipo influenceIpo, BlenderContext blenderContext) throws BlenderFileException { if (dataType == null || "Mesh".equalsIgnoreCase(dataType) || "Camera".equalsIgnoreCase(dataType) || "Lamp".equalsIgnoreCase(dataType)) { return new SpatialConstraint(constraintStructure, ownerOMA, influenceIpo, blenderContext); } else if ("Armature".equalsIgnoreCase(dataType)) { return new SkeletonConstraint(constraintStructure, ownerOMA, influenceIpo, blenderContext); } else { throw new IllegalArgumentException("Unsupported data type for applying constraints: " + dataType); } } /** * The method bakes all available and valid constraints. * * @param blenderContext * the blender context */ public void bakeConstraints(BlenderContext blenderContext) { Set owners = new HashSet(); for (Constraint constraint : blenderContext.getAllConstraints()) { if(constraint instanceof BoneConstraint) { BoneContext boneContext = blenderContext.getBoneContext(constraint.ownerOMA); owners.add(boneContext.getArmatureObjectOMA()); } else { Spatial spatial = (Spatial) blenderContext.getLoadedFeature(constraint.ownerOMA, LoadedDataType.FEATURE); while (spatial.getParent() != null) { spatial = spatial.getParent(); } owners.add((Long)blenderContext.getMarkerValue(ObjectHelper.OMA_MARKER, spatial)); } } List simulationRootNodes = new ArrayList(owners.size()); for(Long ownerOMA : owners) { simulationRootNodes.add(new SimulationNode(ownerOMA, blenderContext)); } for (SimulationNode node : simulationRootNodes) { node.simulate(); } } /** * The method retrieves the transform from a feature in a given space. * * @param oma * the OMA of the feature (spatial or armature node) * @param subtargetName * the feature's subtarget (bone in a case of armature's node) * @param space * the space the transform is evaluated to * @return the transform of a feature in a given space */ public Transform getTransform(Long oma, String subtargetName, Space space) { Spatial feature = (Spatial) blenderContext.getLoadedFeature(oma, LoadedDataType.FEATURE); boolean isArmature = blenderContext.getMarkerValue(ObjectHelper.ARMATURE_NODE_MARKER, feature) != null; if (isArmature) { blenderContext.getSkeleton(oma).updateWorldVectors(); BoneContext targetBoneContext = blenderContext.getBoneByName(oma, subtargetName); Bone bone = targetBoneContext.getBone(); if (bone.getParent() == null && (space == Space.CONSTRAINT_SPACE_LOCAL || space == Space.CONSTRAINT_SPACE_PARLOCAL)) { space = Space.CONSTRAINT_SPACE_POSE; } TempVars tempVars = TempVars.get();// use readable names of the matrices so that the code is more clear Transform result; switch (space) { case CONSTRAINT_SPACE_WORLD: Spatial model = (Spatial) blenderContext.getLoadedFeature(targetBoneContext.getSkeletonOwnerOma(), LoadedDataType.FEATURE); Matrix4f boneModelMatrix = this.toMatrix(bone.getModelSpacePosition(), bone.getModelSpaceRotation(), bone.getModelSpaceScale(), tempVars.tempMat4); Matrix4f modelWorldMatrix = this.toMatrix(model.getWorldTransform(), tempVars.tempMat42); Matrix4f boneMatrixInWorldSpace = modelWorldMatrix.multLocal(boneModelMatrix); result = new Transform(boneMatrixInWorldSpace.toTranslationVector(), boneMatrixInWorldSpace.toRotationQuat(), boneMatrixInWorldSpace.toScaleVector()); break; case CONSTRAINT_SPACE_LOCAL: assert bone.getParent() != null : "CONSTRAINT_SPACE_LOCAL should be evaluated as CONSTRAINT_SPACE_POSE if the bone has no parent!"; result = new Transform(bone.getLocalPosition(), bone.getLocalRotation(), bone.getLocalScale()); break; case CONSTRAINT_SPACE_POSE: { Matrix4f boneWorldMatrix = this.toMatrix(this.getTransform(oma, subtargetName, Space.CONSTRAINT_SPACE_WORLD), tempVars.tempMat4); Matrix4f armatureInvertedWorldMatrix = this.toMatrix(feature.getWorldTransform(), tempVars.tempMat42).invertLocal(); Matrix4f bonePoseMatrix = armatureInvertedWorldMatrix.multLocal(boneWorldMatrix); result = new Transform(bonePoseMatrix.toTranslationVector(), bonePoseMatrix.toRotationQuat(), bonePoseMatrix.toScaleVector()); break; } case CONSTRAINT_SPACE_PARLOCAL: { Matrix4f boneWorldMatrix = this.toMatrix(this.getTransform(oma, subtargetName, Space.CONSTRAINT_SPACE_WORLD), tempVars.tempMat4); Matrix4f armatureInvertedWorldMatrix = this.toMatrix(feature.getWorldTransform(), tempVars.tempMat42).invertLocal(); Matrix4f bonePoseMatrix = armatureInvertedWorldMatrix.multLocal(boneWorldMatrix); result = new Transform(bonePoseMatrix.toTranslationVector(), bonePoseMatrix.toRotationQuat(), bonePoseMatrix.toScaleVector()); Bone parent = bone.getParent(); if(parent != null) { BoneContext parentContext = blenderContext.getBoneContext(parent); Vector3f head = parent.getModelSpacePosition(); Vector3f tail = head.add(bone.getModelSpaceRotation().mult(Vector3f.UNIT_Y.mult(parentContext.getLength()))); result.getTranslation().subtractLocal(tail); } break; } default: throw new IllegalStateException("Unknown space type: " + space); } tempVars.release(); return result; } else { switch (space) { case CONSTRAINT_SPACE_LOCAL: return feature.getLocalTransform(); case CONSTRAINT_SPACE_WORLD: return feature.getWorldTransform(); case CONSTRAINT_SPACE_PARLOCAL: case CONSTRAINT_SPACE_POSE: throw new IllegalStateException("Nodes can have only Local and World spaces applied!"); default: throw new IllegalStateException("Unknown space type: " + space); } } } /** * Applies transform to a feature (bone or spatial). Computations transform * the given transformation from the given space to the feature's local * space. * * @param oma * the OMA of the feature we apply transformation to * @param subtargetName * the name of the feature's subtarget (bone in case of armature) * @param space * the space in which the given transform is to be applied * @param transform * the transform we apply */ public void applyTransform(Long oma, String subtargetName, Space space, Transform transform) { Spatial feature = (Spatial) blenderContext.getLoadedFeature(oma, LoadedDataType.FEATURE); boolean isArmature = blenderContext.getMarkerValue(ObjectHelper.ARMATURE_NODE_MARKER, feature) != null; if (isArmature) { Skeleton skeleton = blenderContext.getSkeleton(oma); BoneContext targetBoneContext = blenderContext.getBoneByName(oma, subtargetName); Bone bone = targetBoneContext.getBone(); if (bone.getParent() == null && (space == Space.CONSTRAINT_SPACE_LOCAL || space == Space.CONSTRAINT_SPACE_PARLOCAL)) { space = Space.CONSTRAINT_SPACE_POSE; } TempVars tempVars = TempVars.get(); switch (space) { case CONSTRAINT_SPACE_LOCAL: assert bone.getParent() != null : "CONSTRAINT_SPACE_LOCAL should be evaluated as CONSTRAINT_SPACE_POSE if the bone has no parent!"; bone.setBindTransforms(transform.getTranslation(), transform.getRotation(), transform.getScale()); break; case CONSTRAINT_SPACE_WORLD: { Matrix4f boneMatrixInWorldSpace = this.toMatrix(transform, tempVars.tempMat4); Matrix4f modelWorldMatrix = this.toMatrix(this.getTransform(targetBoneContext.getSkeletonOwnerOma(), null, Space.CONSTRAINT_SPACE_WORLD), tempVars.tempMat42); Matrix4f boneMatrixInModelSpace = modelWorldMatrix.invertLocal().multLocal(boneMatrixInWorldSpace); Bone parent = bone.getParent(); if (parent != null) { Matrix4f parentMatrixInModelSpace = this.toMatrix(parent.getModelSpacePosition(), parent.getModelSpaceRotation(), parent.getModelSpaceScale(), tempVars.tempMat4); boneMatrixInModelSpace = parentMatrixInModelSpace.invertLocal().multLocal(boneMatrixInModelSpace); } bone.setBindTransforms(boneMatrixInModelSpace.toTranslationVector(), boneMatrixInModelSpace.toRotationQuat(), boneMatrixInModelSpace.toScaleVector()); break; } case CONSTRAINT_SPACE_POSE: { Matrix4f armatureWorldMatrix = this.toMatrix(feature.getWorldTransform(), tempVars.tempMat4); Matrix4f boneMatrixInWorldSpace = armatureWorldMatrix.multLocal(this.toMatrix(transform, tempVars.tempMat42)); Matrix4f invertedModelMatrix = this.toMatrix(this.getTransform(targetBoneContext.getSkeletonOwnerOma(), null, Space.CONSTRAINT_SPACE_WORLD), tempVars.tempMat42).invertLocal(); Matrix4f boneMatrixInModelSpace = invertedModelMatrix.multLocal(boneMatrixInWorldSpace); Bone parent = bone.getParent(); if (parent != null) { Matrix4f parentMatrixInModelSpace = this.toMatrix(parent.getModelSpacePosition(), parent.getModelSpaceRotation(), parent.getModelSpaceScale(), tempVars.tempMat4); boneMatrixInModelSpace = parentMatrixInModelSpace.invertLocal().multLocal(boneMatrixInModelSpace); } bone.setBindTransforms(boneMatrixInModelSpace.toTranslationVector(), boneMatrixInModelSpace.toRotationQuat(), boneMatrixInModelSpace.toScaleVector()); break; } case CONSTRAINT_SPACE_PARLOCAL: Matrix4f armatureWorldMatrix = this.toMatrix(feature.getWorldTransform(), tempVars.tempMat4); Matrix4f boneMatrixInWorldSpace = armatureWorldMatrix.multLocal(this.toMatrix(transform, tempVars.tempMat42)); Matrix4f invertedModelMatrix = this.toMatrix(this.getTransform(targetBoneContext.getSkeletonOwnerOma(), null, Space.CONSTRAINT_SPACE_WORLD), tempVars.tempMat42).invertLocal(); Matrix4f boneMatrixInModelSpace = invertedModelMatrix.multLocal(boneMatrixInWorldSpace); Bone parent = bone.getParent(); if (parent != null) { //first add the initial parent matrix to the bone's model matrix BoneContext parentContext = blenderContext.getBoneContext(parent); Matrix4f initialParentMatrixInModelSpace = parentContext.getBoneMatrixInModelSpace(); Matrix4f currentParentMatrixInModelSpace = this.toMatrix(parent.getModelSpacePosition(), parent.getModelSpaceRotation(), parent.getModelSpaceScale(), tempVars.tempMat4); //the bone will now move with its parent in model space //now we need to subtract the difference between current parent's model matrix and its initial model matrix boneMatrixInModelSpace = initialParentMatrixInModelSpace.mult(boneMatrixInModelSpace); Matrix4f diffMatrix = initialParentMatrixInModelSpace.mult(currentParentMatrixInModelSpace.invert()); boneMatrixInModelSpace.multLocal(diffMatrix); //now the bone will have its position in model space with initial parent's model matrix added } bone.setBindTransforms(boneMatrixInModelSpace.toTranslationVector(), boneMatrixInModelSpace.toRotationQuat(), boneMatrixInModelSpace.toScaleVector()); break; default: tempVars.release(); throw new IllegalStateException("Invalid space type for target object: " + space.toString()); } tempVars.release(); skeleton.updateWorldVectors(); } else { switch (space) { case CONSTRAINT_SPACE_LOCAL: feature.getLocalTransform().set(transform); break; case CONSTRAINT_SPACE_WORLD: if (feature.getParent() == null) { feature.setLocalTransform(transform); } else { Transform parentWorldTransform = feature.getParent().getWorldTransform(); TempVars tempVars = TempVars.get(); Matrix4f parentInverseMatrix = this.toMatrix(parentWorldTransform, tempVars.tempMat4).invertLocal(); Matrix4f m = this.toMatrix(transform, tempVars.tempMat42); m = m.multLocal(parentInverseMatrix); tempVars.release(); transform.setTranslation(m.toTranslationVector()); transform.setRotation(m.toRotationQuat()); transform.setScale(m.toScaleVector()); feature.setLocalTransform(transform); } break; default: throw new IllegalStateException("Invalid space type for spatial object: " + space.toString()); } } } /** * Converts given transform to the matrix. * * @param transform * the transform to be converted * @param store * the matrix where the result will be stored * @return the store matrix */ public Matrix4f toMatrix(Transform transform, Matrix4f store) { if (transform != null) { return this.toMatrix(transform.getTranslation(), transform.getRotation(), transform.getScale(), store); } store.loadIdentity(); return store; } /** * Converts given transformation parameters into the matrix. * * @param position * the position of the feature * @param rotation * the rotation of the feature * @param scale * the scale of the feature * @param store * the matrix where the result will be stored * @return the store matrix */ private Matrix4f toMatrix(Vector3f position, Quaternion rotation, Vector3f scale, Matrix4f store) { store.loadIdentity(); store.setTranslation(position); store.setRotationQuaternion(rotation); store.setScale(scale); return store; } /** * The space of target or owner transformation. * * @author Marcin Roguski (Kaelthas) */ public static enum Space { /** A transformation of the bone or spatial in the world space. */ CONSTRAINT_SPACE_WORLD, /** * For spatial it is the transformation in its parent space or in WORLD space if it has no parent. * For bone it is a transformation in its bone parent space or in armature space if it has no parent. */ CONSTRAINT_SPACE_LOCAL, /** * This space IS NOT applicable for spatials. * For bone it is a transformation in the blender's armature object space. */ CONSTRAINT_SPACE_POSE, CONSTRAINT_SPACE_PARLOCAL; /** * This method returns the enum instance when given the appropriate * value from the blend file. * * @param c * the blender's value of the space modifier * @return the scape enum instance */ public static Space valueOf(byte c) { switch (c) { case 0: return CONSTRAINT_SPACE_WORLD; case 1: return CONSTRAINT_SPACE_LOCAL; case 2: return CONSTRAINT_SPACE_POSE; case 3: return CONSTRAINT_SPACE_PARLOCAL; default: throw new IllegalArgumentException("Value: " + c + " cannot be converted to Space enum instance!"); } } } }




  • © 2015 - 2024 Weber Informatics LLC | Privacy Policy