/** * The method applies bone's current position to all of the traces of the given animations. * * @param boneContext the bone context * @param space the bone's evaluation space * @param referenceAnimData the object containing the animations */ protected void applyAnimData(BoneContext boneContext, Space space, AnimData referenceAnimData) { ConstraintHelper constraintHelper = blenderContext.getHelper(ConstraintHelper.class); Transform transform = constraintHelper.getBoneTransform(space, boneContext.getBone()); AnimData animData = blenderContext.getAnimData(boneContext.getBoneOma()); for (Animation animation : referenceAnimData.anims) { BoneTrack parentTrack = (BoneTrack) animation.getTracks()[0]; float[] times = parentTrack.getTimes(); Vector3f[] translations = new Vector3f[times.length]; Quaternion[] rotations = new Quaternion[times.length]; Vector3f[] scales = new Vector3f[times.length]; Arrays.fill(translations, transform.getTranslation()); Arrays.fill(rotations, transform.getRotation()); Arrays.fill(scales, transform.getScale()); for (Animation anim : animData.anims) { anim.addTrack( new BoneTrack( animData.skeleton.getBoneIndex(boneContext.getBone()), times, translations, rotations, scales)); } } blenderContext.setAnimData(boneContext.getBoneOma(), animData); }
/** * The method applies animations to the given node. The names of the animations should be the same * as actions names in the blender file. * * @param node the node to whom the animations will be applied * @param animationMatchMethod the way animation should be matched with node */ public void applyAnimations(Node node, AnimationMatchMethod animationMatchMethod) { List<BlenderAction> actions = this.getActions(node, animationMatchMethod); if (actions.size() > 0) { List<Animation> animations = new ArrayList<Animation>(); for (BlenderAction action : actions) { SpatialTrack[] tracks = action.toTracks(node); if (tracks != null && tracks.length > 0) { Animation spatialAnimation = new Animation(action.getName(), action.getAnimationTime()); spatialAnimation.setTracks(tracks); animations.add(spatialAnimation); blenderContext.addAnimation( (Long) node.getUserData(ObjectHelper.OMA_MARKER), spatialAnimation); } } if (animations.size() > 0) { AnimControl control = new AnimControl(); HashMap<String, Animation> anims = new HashMap<String, Animation>(animations.size()); for (int i = 0; i < animations.size(); ++i) { Animation animation = animations.get(i); anims.put(animation.getName(), animation); } control.setAnimations(anims); node.addControl(control); } } }
@Override @SuppressWarnings("unchecked") public Node apply(Node node, BlenderContext blenderContext) { if (invalid) { LOGGER.log( Level.WARNING, "Armature modifier is invalid! Cannot be applied to: {0}", node.getName()); } // if invalid, animData will be null if (animData == null) { return node; } // setting weights for bones List<Geometry> geomList = (List<Geometry>) blenderContext.getLoadedFeature(this.meshOMA, LoadedFeatureDataType.LOADED_FEATURE); for (Geometry geom : geomList) { Mesh mesh = geom.getMesh(); if (this.verticesWeights != null) { mesh.setMaxNumWeights(this.boneGroups); mesh.setBuffer(this.verticesWeights); mesh.setBuffer(this.verticesWeightsIndices); } } ArrayList<Animation> animList = animData.anims; if (animList != null && animList.size() > 0) { List<Constraint> constraints = blenderContext.getConstraints(this.armatureObjectOMA); HashMap<String, Animation> anims = new HashMap<String, Animation>(); for (int i = 0; i < animList.size(); ++i) { Animation animation = (Animation) animList.get(i).clone(); // baking constraints into animations if (constraints != null && constraints.size() > 0) { for (Constraint constraint : constraints) { Long boneOMA = constraint.getBoneOMA(); Bone bone = (Bone) blenderContext.getLoadedFeature(boneOMA, LoadedFeatureDataType.LOADED_FEATURE); int targetIndex = bone == null ? 0 : animData.skeleton.getBoneIndex( bone); // bone==null may mean the object animation constraint.affectAnimation(animation, targetIndex); } } anims.put(animation.getName(), animation); } // applying the control to the node SkeletonControl skeletonControl = new SkeletonControl(animData.skeleton); AnimControl control = new AnimControl(animData.skeleton); control.setAnimations(anims); node.addControl(control); node.addControl(skeletonControl); } return node; }
/** * The method determines if the bone has animations. * * @param boneOMA OMA of the bone * @return <b>true</b> if the bone has animations and <b>false</b> otherwise */ protected boolean hasAnimation(Long boneOMA) { AnimData animData = blenderContext.getAnimData(boneOMA); if (animData != null) { Bone bone = blenderContext.getBoneContext(boneOMA).getBone(); int boneIndex = animData.skeleton.getBoneIndex(bone); for (Animation animation : animData.anims) { for (Track track : animation.getTracks()) { if (track instanceof BoneTrack && ((BoneTrack) track).getTargetBoneIndex() == boneIndex) { return true; } } } } return false; }
private static BoneTrack getFirstTrackForBone(Animation animation, int boneIndex) { BoneTrack result = null; for (Track track : animation.getTracks()) { if (track instanceof BoneTrack) { BoneTrack boneTrack = (BoneTrack) track; if (boneIndex == boneTrack.getTargetBoneIndex()) { return boneTrack; } } } return result; }
/** * The method applies skeleton animations to the given node. * * @param node the node where the animations will be applied * @param skeleton the skeleton of the node * @param animationMatchMethod the way animation should be matched with skeleton */ public void applyAnimations( Node node, Skeleton skeleton, AnimationMatchMethod animationMatchMethod) { node.addControl(new SkeletonControl(skeleton)); blenderContext.setNodeForSkeleton(skeleton, node); List<BlenderAction> actions = this.getActions(skeleton, animationMatchMethod); if (actions.size() > 0) { List<Animation> animations = new ArrayList<Animation>(); for (BlenderAction action : actions) { BoneTrack[] tracks = action.toTracks(skeleton); if (tracks != null && tracks.length > 0) { Animation boneAnimation = new Animation(action.getName(), action.getAnimationTime()); boneAnimation.setTracks(tracks); animations.add(boneAnimation); Long animatedNodeOMA = ((Number) blenderContext.getMarkerValue(ObjectHelper.OMA_MARKER, node)).longValue(); blenderContext.addAnimation(animatedNodeOMA, boneAnimation); } } if (animations.size() > 0) { AnimControl control = new AnimControl(skeleton); HashMap<String, Animation> anims = new HashMap<String, Animation>(animations.size()); for (int i = 0; i < animations.size(); ++i) { Animation animation = animations.get(i); anims.put(animation.getName(), animation); } control.setAnimations(anims); node.addControl(control); // make sure that SkeletonControl is added AFTER the AnimControl SkeletonControl skeletonControl = node.getControl(SkeletonControl.class); if (skeletonControl != null) { node.removeControl(SkeletonControl.class); node.addControl(skeletonControl); } } } }
/** * The method loads library of a given ID from linked blender file. * * @param id the ID of the linked feature (it contains its name and blender path) * @return loaded feature or null if none was found * @throws BlenderFileException and exception is throw when problems with reading a blend file * occur */ @SuppressWarnings("unchecked") protected Object loadLibrary(Structure id) throws BlenderFileException { Pointer pLib = (Pointer) id.getFieldValue("lib"); if (pLib.isNotNull()) { String fullName = id.getFieldValue("name").toString(); // we need full name with the prefix String nameOfFeatureToLoad = id.getName(); Structure library = pLib.fetchData().get(0); String path = library.getFieldValue("filepath").toString(); if (!blenderContext.getLinkedFeatures().keySet().contains(path)) { File file = new File(path); List<String> pathsToCheck = new ArrayList<String>(); String currentPath = file.getName(); do { pathsToCheck.add(currentPath); file = file.getParentFile(); if (file != null) { currentPath = file.getName() + '/' + currentPath; } } while (file != null); Spatial loadedAsset = null; BlenderKey blenderKey = null; for (String p : pathsToCheck) { blenderKey = new BlenderKey(p); blenderKey.setLoadUnlinkedAssets(true); try { loadedAsset = blenderContext.getAssetManager().loadAsset(blenderKey); break; // break if no exception was thrown } catch (AssetNotFoundException e) { LOGGER.log(Level.FINEST, "Cannot locate linked resource at path: {0}.", p); } } if (loadedAsset != null) { Map<String, Map<String, Object>> linkedData = loadedAsset.getUserData("linkedData"); for (Entry<String, Map<String, Object>> entry : linkedData.entrySet()) { String linkedDataFilePath = "this".equals(entry.getKey()) ? path : entry.getKey(); List<Node> scenes = (List<Node>) entry.getValue().get("scenes"); for (Node scene : scenes) { blenderContext.addLinkedFeature(linkedDataFilePath, "SC" + scene.getName(), scene); } List<Node> objects = (List<Node>) entry.getValue().get("objects"); for (Node object : objects) { blenderContext.addLinkedFeature(linkedDataFilePath, "OB" + object.getName(), object); } List<TemporalMesh> meshes = (List<TemporalMesh>) entry.getValue().get("meshes"); for (TemporalMesh mesh : meshes) { blenderContext.addLinkedFeature(linkedDataFilePath, "ME" + mesh.getName(), mesh); } List<MaterialContext> materials = (List<MaterialContext>) entry.getValue().get("materials"); for (MaterialContext materialContext : materials) { blenderContext.addLinkedFeature( linkedDataFilePath, "MA" + materialContext.getName(), materialContext); } List<Texture> textures = (List<Texture>) entry.getValue().get("textures"); for (Texture texture : textures) { blenderContext.addLinkedFeature( linkedDataFilePath, "TE" + texture.getName(), texture); } List<Texture> images = (List<Texture>) entry.getValue().get("images"); for (Texture image : images) { blenderContext.addLinkedFeature(linkedDataFilePath, "IM" + image.getName(), image); } List<Animation> animations = (List<Animation>) entry.getValue().get("animations"); for (Animation animation : animations) { blenderContext.addLinkedFeature( linkedDataFilePath, "AC" + animation.getName(), animation); } List<Camera> cameras = (List<Camera>) entry.getValue().get("cameras"); for (Camera camera : cameras) { blenderContext.addLinkedFeature(linkedDataFilePath, "CA" + camera.getName(), camera); } List<Light> lights = (List<Light>) entry.getValue().get("lights"); for (Light light : lights) { blenderContext.addLinkedFeature(linkedDataFilePath, "LA" + light.getName(), light); } Spatial sky = (Spatial) entry.getValue().get("sky"); if (sky != null) { blenderContext.addLinkedFeature(linkedDataFilePath, sky.getName(), sky); } List<Filter> filters = (List<Filter>) entry.getValue().get("filters"); for (Filter filter : filters) { blenderContext.addLinkedFeature(linkedDataFilePath, filter.getName(), filter); } } } else { LOGGER.log(Level.WARNING, "No features loaded from path: {0}.", path); } } Object result = blenderContext.getLinkedFeature(path, fullName); if (result == null) { LOGGER.log( Level.WARNING, "Could NOT find asset named {0} in the library of path: {1}.", new Object[] {nameOfFeatureToLoad, path}); } else { blenderContext.addLoadedFeatures(id.getOldMemoryAddress(), LoadedDataType.STRUCTURE, id); blenderContext.addLoadedFeatures(id.getOldMemoryAddress(), LoadedDataType.FEATURE, result); } return result; } else { LOGGER.warning("Library link points to nothing!"); } return null; }
/** * This constructor reads animation data from the object structore. The stored data is the * AnimData and additional data is armature's OMA. * * @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 ArmatureModifier( Structure objectStructure, Structure modifierStructure, BlenderContext blenderContext) throws BlenderFileException { if (this.validate(modifierStructure, blenderContext)) { Pointer pArmatureObject = (Pointer) modifierStructure.getFieldValue("object"); if (pArmatureObject.isNotNull()) { ObjectHelper objectHelper = blenderContext.getHelper(ObjectHelper.class); ArmatureHelper armatureHelper = blenderContext.getHelper(ArmatureHelper.class); Structure armatureObject = pArmatureObject.fetchData(blenderContext.getInputStream()).get(0); this.armatureObjectOMA = armatureObject.getOldMemoryAddress(); // read skeleton // changing bones matrices so that they fit the current object Structure armatureStructure = ((Pointer) armatureObject.getFieldValue("data")) .fetchData(blenderContext.getInputStream()) .get(0); Structure bonebase = (Structure) armatureStructure.getFieldValue("bonebase"); List<Structure> bonesStructures = bonebase.evaluateListBase(blenderContext); for (Structure boneStructure : bonesStructures) { BoneTransformationData rootBoneTransformationData = armatureHelper.readBoneAndItsChildren(boneStructure, null, blenderContext); armatureHelper.addBoneDataRoot(rootBoneTransformationData); } Matrix4f armatureObjectMatrix = objectHelper.getTransformationMatrix(armatureObject); Matrix4f inverseMeshObjectMatrix = objectHelper.getTransformationMatrix(objectStructure).invert(); Matrix4f additionalRootBoneTransformation = inverseMeshObjectMatrix.multLocal(armatureObjectMatrix); Bone[] bones = armatureHelper.buildBonesStructure(Long.valueOf(0L), additionalRootBoneTransformation); // read mesh indexes Structure meshStructure = ((Pointer) objectStructure.getFieldValue("data")) .fetchData(blenderContext.getInputStream()) .get(0); this.meshOMA = meshStructure.getOldMemoryAddress(); this.readVerticesWeightsData(objectStructure, meshStructure, blenderContext); // read animations ArrayList<Animation> animations = new ArrayList<Animation>(); List<FileBlockHeader> actionHeaders = blenderContext.getFileBlocks(Integer.valueOf(FileBlockHeader.BLOCK_AC00)); if (actionHeaders != null) { // it may happen that the model has armature with no actions for (FileBlockHeader header : actionHeaders) { Structure actionStructure = header.getStructure(blenderContext); String actionName = actionStructure.getName(); BoneTrack[] tracks = armatureHelper.getTracks(actionStructure, blenderContext); // determining the animation time float maximumTrackLength = 0; for (BoneTrack track : tracks) { float length = track.getLength(); if (length > maximumTrackLength) { maximumTrackLength = length; } } Animation boneAnimation = new Animation(actionName, maximumTrackLength); boneAnimation.setTracks(tracks); animations.add(boneAnimation); } } animData = new AnimData(new Skeleton(bones), animations); } } }