/** * This method retuns the bone tracks for animation for blender version 2.50 and higher. * * @param actionStructure the structure containing the tracks * @param blenderContext the blender context * @return a list of tracks for the specified animation * @throws BlenderFileException an exception is thrown when there are problems with the blend file */ private BlenderAction getTracks250(Structure actionStructure, BlenderContext blenderContext) throws BlenderFileException { LOGGER.log(Level.FINE, "Getting tracks!"); Structure groups = (Structure) actionStructure.getFieldValue("groups"); List<Structure> actionGroups = groups.evaluateListBase(); // bActionGroup BlenderAction blenderAction = new BlenderAction(actionStructure.getName(), blenderContext.getBlenderKey().getFps()); int lastFrame = 1; for (Structure actionGroup : actionGroups) { String name = actionGroup.getFieldValue("name").toString(); List<Structure> channels = ((Structure) actionGroup.getFieldValue("channels")).evaluateListBase(); BezierCurve[] bezierCurves = new BezierCurve[channels.size()]; int channelCounter = 0; for (Structure c : channels) { int type = this.getCurveType(c, blenderContext); Pointer pBezTriple = (Pointer) c.getFieldValue("bezt"); List<Structure> bezTriples = pBezTriple.fetchData(); bezierCurves[channelCounter++] = new BezierCurve(type, bezTriples, 2); } Ipo ipo = new Ipo(bezierCurves, fixUpAxis, blenderContext.getBlenderVersion()); lastFrame = Math.max(lastFrame, ipo.getLastFrame()); blenderAction.featuresTracks.put(name, ipo); } blenderAction.stopFrame = lastFrame; return blenderAction; }
/** * This method retuns the bone tracks for animation for blender version 2.49 (and probably several * lower versions too). * * @param actionStructure the structure containing the tracks * @param blenderContext the blender context * @return a list of tracks for the specified animation * @throws BlenderFileException an exception is thrown when there are problems with the blend file */ private BlenderAction getTracks249(Structure actionStructure, BlenderContext blenderContext) throws BlenderFileException { LOGGER.log(Level.FINE, "Getting tracks!"); Structure chanbase = (Structure) actionStructure.getFieldValue("chanbase"); List<Structure> actionChannels = chanbase.evaluateListBase(); // bActionChannel BlenderAction blenderAction = new BlenderAction(actionStructure.getName(), blenderContext.getBlenderKey().getFps()); int lastFrame = 1; for (Structure bActionChannel : actionChannels) { String animatedFeatureName = bActionChannel.getFieldValue("name").toString(); Pointer p = (Pointer) bActionChannel.getFieldValue("ipo"); if (!p.isNull()) { Structure ipoStructure = p.fetchData().get(0); Ipo ipo = this.fromIpoStructure(ipoStructure, blenderContext); if (ipo != null) { // this can happen when ipo with no curves appear in blender file lastFrame = Math.max(lastFrame, ipo.getLastFrame()); blenderAction.featuresTracks.put(animatedFeatureName, ipo); } } } blenderAction.stopFrame = lastFrame; return blenderAction; }
/** * This method creates an ipo object used for interpolation calculations. * * @param ipoStructure the structure with ipo definition * @param blenderContext the blender context * @return the ipo object * @throws BlenderFileException this exception is thrown when the blender file is somehow * corrupted */ public Ipo fromIpoStructure(Structure ipoStructure, BlenderContext blenderContext) throws BlenderFileException { Structure curvebase = (Structure) ipoStructure.getFieldValue("curve"); // preparing bezier curves Ipo result = null; List<Structure> curves = curvebase.evaluateListBase(); // IpoCurve if (curves.size() > 0) { BezierCurve[] bezierCurves = new BezierCurve[curves.size()]; int frame = 0; for (Structure curve : curves) { Pointer pBezTriple = (Pointer) curve.getFieldValue("bezt"); List<Structure> bezTriples = pBezTriple.fetchData(); int type = ((Number) curve.getFieldValue("adrcode")).intValue(); bezierCurves[frame++] = new BezierCurve(type, bezTriples, 2); } curves.clear(); result = new Ipo(bezierCurves, fixUpAxis, blenderContext.getBlenderVersion()); Long ipoOma = ipoStructure.getOldMemoryAddress(); blenderContext.addLoadedFeatures(ipoOma, LoadedDataType.STRUCTURE, ipoStructure); blenderContext.addLoadedFeatures(ipoOma, LoadedDataType.FEATURE, result); } return result; }
/** * 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 IpoHelper ipoHelper = blenderContext.getHelper(IpoHelper.class); Map<String, Map<String, Ipo>> constraintsIpos = new HashMap<String, Map<String, Ipo>>(); Pointer pActions = (Pointer) objectStructure.getFieldValue("action"); if (pActions.isNotNull()) { List<Structure> actions = pActions.fetchData(blenderContext.getInputStream()); for (Structure action : actions) { Structure chanbase = (Structure) action.getFieldValue("chanbase"); List<Structure> actionChannels = chanbase.evaluateListBase(blenderContext); for (Structure actionChannel : actionChannels) { Map<String, Ipo> ipos = new HashMap<String, Ipo>(); Structure constChannels = (Structure) actionChannel.getFieldValue("constraintChannels"); List<Structure> constraintChannels = constChannels.evaluateListBase(blenderContext); for (Structure constraintChannel : constraintChannels) { Pointer pIpo = (Pointer) constraintChannel.getFieldValue("ipo"); if (pIpo.isNotNull()) { String constraintName = constraintChannel.getFieldValue("name").toString(); Ipo ipo = ipoHelper.fromIpoStructure( pIpo.fetchData(blenderContext.getInputStream()).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<Structure> poseChannels = ((Structure) pPose.fetchData(blenderContext.getInputStream()).get(0).getFieldValue("chanbase")) .evaluateListBase(blenderContext); for (Structure poseChannel : poseChannels) { List<Constraint> constraintsList = new ArrayList<Constraint>(); 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<Structure> constraints = ((Structure) poseChannel.getFieldValue("constraints")).evaluateListBase(blenderContext); for (Structure constraint : constraints) { String constraintName = constraint.getFieldValue("name").toString(); Map<String, Ipo> ipoMap = constraintsIpos.get(name); Ipo ipo = ipoMap == null ? null : ipoMap.get(constraintName); if (ipo == null) { float enforce = ((Number) constraint.getFieldValue("enforce")).floatValue(); ipo = ipoHelper.fromValue(enforce); } constraintsList.add(this.createConstraint(constraint, boneOMA, ipo, blenderContext)); } blenderContext.addConstraints(boneOMA, constraintsList); } } // loading constraints connected with the object itself List<Structure> constraints = ((Structure) objectStructure.getFieldValue("constraints")).evaluateListBase(blenderContext); List<Constraint> constraintsList = new ArrayList<Constraint>(constraints.size()); for (Structure constraint : constraints) { String constraintName = constraint.getFieldValue("name").toString(); String objectName = objectStructure.getName(); Map<String, Ipo> objectConstraintsIpos = constraintsIpos.get(objectName); Ipo ipo = objectConstraintsIpos != null ? objectConstraintsIpos.get(constraintName) : null; if (ipo == null) { float enforce = ((Number) constraint.getFieldValue("enforce")).floatValue(); ipo = ipoHelper.fromValue(enforce); } constraintsList.add( this.createConstraint( constraint, objectStructure.getOldMemoryAddress(), ipo, blenderContext)); } blenderContext.addConstraints(objectStructure.getOldMemoryAddress(), constraintsList); }
/** * 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); } } }