/** * Constructor. Creates the basic set of bone's data. * * @param boneStructure the bone's structure * @param parent bone's parent (null if the bone is the root bone) * @param objectToArmatureMatrix object-to-armature transformation matrix * @param bonesPoseChannels a map of pose channels for each bone OMA * @param blenderContext the blender context * @throws BlenderFileException an exception is thrown when problem with blender data reading * occurs */ private BoneContext( Structure boneStructure, BoneContext parent, Matrix4f objectToArmatureMatrix, final Map<Long, Structure> bonesPoseChannels, BlenderContext blenderContext) throws BlenderFileException { this.parent = parent; this.boneStructure = boneStructure; boneName = boneStructure.getFieldValue("name").toString(); ObjectHelper objectHelper = blenderContext.getHelper(ObjectHelper.class); armatureMatrix = objectHelper.getMatrix(boneStructure, "arm_mat", true); fixUpAxis = blenderContext.getBlenderKey().isFixUpAxis(); this.computeRestMatrix(objectToArmatureMatrix); List<Structure> childbase = ((Structure) boneStructure.getFieldValue("childbase")).evaluateListBase(blenderContext); for (Structure child : childbase) { this.children.add( new BoneContext(child, this, objectToArmatureMatrix, bonesPoseChannels, blenderContext)); } poseChannel = bonesPoseChannels.get(boneStructure.getOldMemoryAddress()); blenderContext.setBoneContext(boneStructure.getOldMemoryAddress(), this); }
/** * This method returns the type of the ipo curve. * * @param structure the structure must contain the 'rna_path' field and 'array_index' field (the * type is not important here) * @param blenderContext the blender context * @return the type of the curve */ public int getCurveType(Structure structure, BlenderContext blenderContext) { // reading rna path first BlenderInputStream bis = blenderContext.getInputStream(); int currentPosition = bis.getPosition(); Pointer pRnaPath = (Pointer) structure.getFieldValue("rna_path"); FileBlockHeader dataFileBlock = blenderContext.getFileBlock(pRnaPath.getOldMemoryAddress()); bis.setPosition(dataFileBlock.getBlockPosition()); String rnaPath = bis.readString(); bis.setPosition(currentPosition); int arrayIndex = ((Number) structure.getFieldValue("array_index")).intValue(); // determining the curve type if (rnaPath.endsWith("location")) { return Ipo.AC_LOC_X + arrayIndex; } if (rnaPath.endsWith("rotation_quaternion")) { return Ipo.AC_QUAT_W + arrayIndex; } if (rnaPath.endsWith("scale")) { return Ipo.AC_SIZE_X + arrayIndex; } if (rnaPath.endsWith("rotation") || rnaPath.endsWith("rotation_euler")) { return Ipo.OB_ROT_X + arrayIndex; } LOGGER.log(Level.WARNING, "Unknown curve rna path: {0}", rnaPath); return -1; }
protected String getConstraintClassName( Structure constraintStructure, BlenderContext blenderContext) throws BlenderFileException { Pointer pData = (Pointer) constraintStructure.getFieldValue("data"); if (pData.isNotNull()) { Structure data = pData.fetchData(blenderContext.getInputStream()).get(0); return data.getType(); } return constraintStructure.getType(); }
/** * 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; }
/** * Loads all animations that are stored in the blender file. The animations are not yet applied to * the scene features. This should be called before objects are loaded. * * @throws BlenderFileException an exception is thrown when problems with blender file reading * occur */ public void loadAnimations() throws BlenderFileException { LOGGER.info("Loading animations that will be later applied to scene features."); List<FileBlockHeader> actionHeaders = blenderContext.getFileBlocks(Integer.valueOf(FileBlockHeader.BLOCK_AC00)); if (actionHeaders != null) { for (FileBlockHeader header : actionHeaders) { Structure actionStructure = header.getStructure(blenderContext); LOGGER.log(Level.INFO, "Found animation: {0}.", actionStructure.getName()); blenderContext.addAction(this.getTracks(actionStructure, blenderContext)); } } }
/** * This method loads the properties if they are available and defined for the structure. * * @param structure the structure we read the properties from * @param blenderContext the blender context * @return loaded properties or null if they are not available * @throws BlenderFileException an exception is thrown when the blend file is somehow corrupted */ protected Properties loadProperties(Structure structure, BlenderContext blenderContext) throws BlenderFileException { Properties properties = null; Structure id = (Structure) structure.getFieldValue("ID"); if (id != null) { Pointer pProperties = (Pointer) id.getFieldValue("properties"); if (pProperties.isNotNull()) { Structure propertiesStructure = pProperties.fetchData().get(0); properties = new Properties(); properties.load(propertiesStructure, blenderContext); } } return properties; }
public ConstraintDefinition(Structure constraintData, BlenderContext blenderContext) { if (constraintData != null) { // Null constraint has no data Number flag = (Number) constraintData.getFieldValue("flag"); if (flag != null) { this.flag = flag.intValue(); } } }
/** * This method reads mesh indexes * * @param objectStructure structure of the object that has the armature modifier applied * @param meshStructure the structure of the object's mesh * @param blenderContext the blender context * @throws BlenderFileException this exception is thrown when the blend file structure is somehow * invalid or corrupted */ private void readVerticesWeightsData( Structure objectStructure, Structure meshStructure, BlenderContext blenderContext) throws BlenderFileException { ArmatureHelper armatureHelper = blenderContext.getHelper(ArmatureHelper.class); Structure defBase = (Structure) objectStructure.getFieldValue("defbase"); Map<Integer, Integer> groupToBoneIndexMap = armatureHelper.getGroupToBoneIndexMap(defBase, blenderContext); int[] bonesGroups = new int[] {0}; MeshContext meshContext = blenderContext.getMeshContext(meshStructure.getOldMemoryAddress()); VertexBuffer[] boneWeightsAndIndex = this.getBoneWeightAndIndexBuffer( meshStructure, meshContext.getVertexList().size(), bonesGroups, meshContext.getVertexReferenceMap(), groupToBoneIndexMap, blenderContext); this.verticesWeights = boneWeightsAndIndex[0]; this.verticesWeightsIndices = boneWeightsAndIndex[1]; this.boneGroups = bonesGroups[0]; }
/** This method computes the pose transform for the bone. */ @SuppressWarnings("unchecked") private void computePoseTransform() { DynamicArray<Number> loc = (DynamicArray<Number>) poseChannel.getFieldValue("loc"); DynamicArray<Number> size = (DynamicArray<Number>) poseChannel.getFieldValue("size"); DynamicArray<Number> quat = (DynamicArray<Number>) poseChannel.getFieldValue("quat"); if (fixUpAxis) { poseTransform.setTranslation( loc.get(0).floatValue(), -loc.get(2).floatValue(), loc.get(1).floatValue()); poseTransform.setRotation( new Quaternion( quat.get(1).floatValue(), quat.get(3).floatValue(), -quat.get(2).floatValue(), quat.get(0).floatValue())); poseTransform.setScale( size.get(0).floatValue(), size.get(2).floatValue(), size.get(1).floatValue()); } else { poseTransform.setTranslation( loc.get(0).floatValue(), loc.get(1).floatValue(), loc.get(2).floatValue()); poseTransform.setRotation( new Quaternion( quat.get(0).floatValue(), quat.get(1).floatValue(), quat.get(2).floatValue(), quat.get(3).floatValue())); poseTransform.setScale( size.get(0).floatValue(), size.get(1).floatValue(), size.get(2).floatValue()); } Transform localTransform = new Transform(bone.getLocalPosition(), bone.getLocalRotation()); localTransform.setScale(bone.getLocalScale()); localTransform.getTranslation().addLocal(poseTransform.getTranslation()); localTransform.getRotation().multLocal(poseTransform.getRotation()); localTransform.getScale().multLocal(poseTransform.getScale()); poseTransform.set(localTransform); }
/** * This method builds the bone. It recursively builds the bone's children. * * @param bones a list of bones where the newly created bone will be added * @param boneOMAs the map between bone and its old memory address * @param blenderContext the blender context * @return newly created bone */ public Bone buildBone(List<Bone> bones, Map<Bone, Long> boneOMAs, BlenderContext blenderContext) { Long boneOMA = boneStructure.getOldMemoryAddress(); bone = new Bone(boneName); bones.add(bone); boneOMAs.put(bone, boneOMA); blenderContext.addLoadedFeatures(boneOMA, boneName, boneStructure, bone); Matrix4f pose = this.restMatrix.clone(); ObjectHelper objectHelper = blenderContext.getHelper(ObjectHelper.class); Vector3f poseLocation = pose.toTranslationVector(); Quaternion rotation = pose.toRotationQuat(); Vector3f scale = objectHelper.getScale(pose); bone.setBindTransforms(poseLocation, rotation, scale); for (BoneContext child : children) { bone.addChild(child.buildBone(bones, boneOMAs, blenderContext)); } this.computePoseTransform(); return bone; }
/** * 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 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); } } }
/** * This method returns an array of size 2. The first element is a vertex buffer holding bone * weights for every vertex in the model. The second element is a vertex buffer holding bone * indices for vertices (the indices of bones the vertices are assigned to). * * @param meshStructure the mesh structure object * @param vertexListSize a number of vertices in the model * @param bonesGroups this is an output parameter, it should be a one-sized array; the maximum * amount of weights per vertex (up to MAXIMUM_WEIGHTS_PER_VERTEX) is stored there * @param vertexReferenceMap this reference map allows to map the original vertices read from * blender to vertices that are really in the model; one vertex may appear several times in * the result model * @param groupToBoneIndexMap this object maps the group index (to which a vertices in blender * belong) to bone index of the model * @param blenderContext the blender context * @return arrays of vertices weights and their bone indices and (as an output parameter) the * maximum amount of weights for a vertex * @throws BlenderFileException this exception is thrown when the blend file structure is somehow * invalid or corrupted */ private VertexBuffer[] getBoneWeightAndIndexBuffer( Structure meshStructure, int vertexListSize, int[] bonesGroups, Map<Integer, List<Integer>> vertexReferenceMap, Map<Integer, Integer> groupToBoneIndexMap, BlenderContext blenderContext) throws BlenderFileException { Pointer pDvert = (Pointer) meshStructure.getFieldValue("dvert"); // dvert = DeformVERTices FloatBuffer weightsFloatData = BufferUtils.createFloatBuffer(vertexListSize * MAXIMUM_WEIGHTS_PER_VERTEX); ByteBuffer indicesData = BufferUtils.createByteBuffer(vertexListSize * MAXIMUM_WEIGHTS_PER_VERTEX); if (pDvert.isNotNull()) { // assigning weights and bone indices List<Structure> dverts = pDvert.fetchData( blenderContext.getInputStream()); // dverts.size() == verticesAmount (one dvert per // vertex in blender) int vertexIndex = 0; for (Structure dvert : dverts) { int totweight = ((Number) dvert.getFieldValue("totweight")) .intValue(); // total amount of weights assignet to the vertex // (max. 4 in JME) Pointer pDW = (Pointer) dvert.getFieldValue("dw"); List<Integer> vertexIndices = vertexReferenceMap.get( Integer.valueOf(vertexIndex)); // we fetch the referenced vertices here if (totweight > 0 && pDW.isNotNull() && groupToBoneIndexMap != null) { // pDW should never be null here, but I check it just in case :) int weightIndex = 0; List<Structure> dw = pDW.fetchData(blenderContext.getInputStream()); for (Structure deformWeight : dw) { Integer boneIndex = groupToBoneIndexMap.get(((Number) deformWeight.getFieldValue("def_nr")).intValue()); // Remove this code if 4 weights limitation is removed if (weightIndex == 4) { LOGGER.log( Level.WARNING, "{0} has more than 4 weight on bone index {1}", new Object[] {meshStructure.getName(), boneIndex}); break; } if (boneIndex != null) { // null here means that we came accross group that has no bone attached // to float weight = ((Number) deformWeight.getFieldValue("weight")).floatValue(); if (weight == 0.0f) { weight = 1; boneIndex = Integer.valueOf(0); } // we apply the weight to all referenced vertices for (Integer index : vertexIndices) { weightsFloatData.put(index * MAXIMUM_WEIGHTS_PER_VERTEX + weightIndex, weight); indicesData.put( index * MAXIMUM_WEIGHTS_PER_VERTEX + weightIndex, boneIndex.byteValue()); } } ++weightIndex; } } else { for (Integer index : vertexIndices) { weightsFloatData.put(index * MAXIMUM_WEIGHTS_PER_VERTEX, 1.0f); indicesData.put(index * MAXIMUM_WEIGHTS_PER_VERTEX, (byte) 0); } } ++vertexIndex; } } else { // always bind all vertices to 0-indexed bone // this bone makes the model look normally if vertices have no bone assigned // and it is used in object animation, so if we come accross object animation // we can use the 0-indexed bone for this for (List<Integer> vertexIndexList : vertexReferenceMap.values()) { // we apply the weight to all referenced vertices for (Integer index : vertexIndexList) { weightsFloatData.put(index * MAXIMUM_WEIGHTS_PER_VERTEX, 1.0f); indicesData.put(index * MAXIMUM_WEIGHTS_PER_VERTEX, (byte) 0); } } } bonesGroups[0] = this.endBoneAssigns(vertexListSize, weightsFloatData); VertexBuffer verticesWeights = new VertexBuffer(Type.BoneWeight); verticesWeights.setupData(Usage.CpuOnly, bonesGroups[0], Format.Float, weightsFloatData); VertexBuffer verticesWeightsIndices = new VertexBuffer(Type.BoneIndex); verticesWeightsIndices.setupData( Usage.CpuOnly, bonesGroups[0], Format.UnsignedByte, indicesData); return new VertexBuffer[] {verticesWeights, verticesWeightsIndices}; }
/** @return the old memory address of the bone */ public Long getBoneOma() { return boneStructure.getOldMemoryAddress(); }
/** * 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); }
/** * 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; }