<commit_before>package com.jme3.scene.plugins.blender.modifiers;

import java.nio.ByteBuffer;
import java.nio.FloatBuffer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

import com.jme3.animation.AnimControl;
import com.jme3.animation.Animation;
import com.jme3.animation.Bone;
import com.jme3.animation.BoneTrack;
import com.jme3.animation.Skeleton;
import com.jme3.animation.SkeletonControl;
import com.jme3.math.Matrix4f;
import com.jme3.scene.Geometry;
import com.jme3.scene.Mesh;
import com.jme3.scene.Node;
import com.jme3.scene.VertexBuffer;
import com.jme3.scene.VertexBuffer.Format;
import com.jme3.scene.VertexBuffer.Type;
import com.jme3.scene.VertexBuffer.Usage;
import com.jme3.scene.plugins.blender.BlenderContext;
import com.jme3.scene.plugins.blender.BlenderContext.LoadedFeatureDataType;
import com.jme3.scene.plugins.blender.animations.ArmatureHelper;
import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
import com.jme3.scene.plugins.blender.file.FileBlockHeader;
import com.jme3.scene.plugins.blender.file.Pointer;
import com.jme3.scene.plugins.blender.file.Structure;
import com.jme3.scene.plugins.blender.meshes.MeshContext;
import com.jme3.scene.plugins.blender.objects.ObjectHelper;
import com.jme3.scene.plugins.ogre.AnimData;
import com.jme3.util.BufferUtils;

/**
 * This modifier allows to add bone animation to the object.
 * 
 * @author <NAME> (Kaelthas)
 */
/* package */class ArmatureModifier extends Modifier {
	private static final Logger	LOGGER						= Logger.getLogger(ArmatureModifier.class.getName());
	private static final int	MAXIMUM_WEIGHTS_PER_VERTEX	= 4;
	// @Marcin it was an Ogre limitation, but as long as we use a MaxNumWeight
	// variable in mesh,
	// i guess this limitation has no sense for the blender loader...so i guess
	// it's up to you. You'll have to deternine the max weight according to the
	// provided blend file
	// I added a check to avoid crash when loading a model that has more than 4
	// weight per vertex on line 258
	// If you decide to remove this limitation, remove this code.
	// Rémy

	private Skeleton skeleton;
	private Structure objectStructure;
	private Structure meshStructure;
	
	/** Loaded animation data. */
	private AnimData			animData;
	/** Old memory address of the mesh that will have the skeleton applied. */
	private Long				meshOMA;

	/**
	 * 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 {
		Structure meshStructure = ((Pointer) objectStructure.getFieldValue("data")).fetchData(blenderContext.getInputStream()).get(0);
		Pointer pDvert = (Pointer) meshStructure.getFieldValue("dvert");// dvert = DeformVERTices

		// if pDvert==null then there are not vertex groups and no need to load
		// skeleton (untill bone envelopes are supported)
		if (this.validate(modifierStructure, blenderContext) && pDvert.isNotNull()) {
			Pointer pArmatureObject = (Pointer) modifierStructure.getFieldValue("object");
			if (pArmatureObject.isNotNull()) {
				ArmatureHelper armatureHelper = blenderContext.getHelper(ArmatureHelper.class);

				Structure armatureObject = pArmatureObject.fetchData(blenderContext.getInputStream()).get(0);

				// load skeleton
				Structure armatureStructure = ((Pointer) armatureObject.getFieldValue("data")).fetchData(blenderContext.getInputStream()).get(0);

				Structure pose = ((Pointer) armatureObject.getFieldValue("pose")).fetchData(blenderContext.getInputStream()).get(0);
				List<Structure> chanbase = ((Structure) pose.getFieldValue("chanbase")).evaluateListBase(blenderContext);

				Map<Long, Structure> bonesPoseChannels = new HashMap<Long, Structure>(chanbase.size());
				for (Structure poseChannel : chanbase) {
					Pointer pBone = (Pointer) poseChannel.getFieldValue("bone");
					bonesPoseChannels.put(pBone.getOldMemoryAddress(), poseChannel);
				}

				ObjectHelper objectHelper = blenderContext.getHelper(ObjectHelper.class);
				Matrix4f armatureObjectMatrix = objectHelper.getMatrix(armatureObject, "obmat", true);
				Matrix4f inverseMeshObjectMatrix = objectHelper.getMatrix(objectStructure, "obmat", true).invertLocal();
				Matrix4f objectToArmatureTransformation = armatureObjectMatrix.multLocal(inverseMeshObjectMatrix);

				List<Structure> bonebase = ((Structure) armatureStructure.getFieldValue("bonebase")).evaluateListBase(blenderContext);
				List<Bone> bonesList = new ArrayList<Bone>();
				for (int i = 0; i < bonebase.size(); ++i) {
					armatureHelper.buildBones(bonebase.get(i), null, bonesList, objectToArmatureTransformation, bonesPoseChannels, blenderContext);
				}
				bonesList.add(0, new Bone(""));
				Bone[] bones = bonesList.toArray(new Bone[bonesList.size()]);
				skeleton = new Skeleton(bones);
				this.objectStructure = objectStructure;
				this.meshStructure = meshStructure;
				
				// read mesh indexes
				this.meshOMA = meshStructure.getOldMemoryAddress();

				// 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, skeleton, blenderContext);
						if(tracks != null && tracks.length > 0) {
							// 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);
						}
					}
				}
				//fetching action defined in object
				Pointer pAction = (Pointer) objectStructure.getFieldValue("action");
				if (pAction.isNotNull()) {
					Structure actionStructure = pAction.fetchData(blenderContext.getInputStream()).get(0);
					String actionName = actionStructure.getName();

					BoneTrack[] tracks = armatureHelper.getTracks(actionStructure, skeleton, blenderContext);
					if(tracks != null && tracks.length > 0) {
						// 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(skeleton, animations);

				// store the animation data for each bone
				for (Bone bone : bones) {
					Long boneOma = armatureHelper.getBoneOMA(bone);
					if (boneOma != null) {
						blenderContext.setAnimData(boneOma, animData);
					}
				}
			}
		}
	}

	@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(meshOMA, LoadedFeatureDataType.LOADED_FEATURE);
		MeshContext meshContext = blenderContext.getMeshContext(meshOMA);
		int[] bonesGroups = new int[] { 0 };
		for (Geometry geom : geomList) {
			int materialIndex = meshContext.getMaterialIndex(geom);
			Mesh mesh = geom.getMesh();
			<commit_msg>Fixing a bug where applied armeture has no skeleton attached.<commit_after>package com.jme3.scene.plugins.blender.modifiers;

import java.nio.ByteBuffer;
import java.nio.FloatBuffer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

import com.jme3.animation.AnimControl;
import com.jme3.animation.Animation;
import com.jme3.animation.Bone;
import com.jme3.animation.BoneTrack;
import com.jme3.animation.Skeleton;
import com.jme3.animation.SkeletonControl;
import com.jme3.math.Matrix4f;
import com.jme3.scene.Geometry;
import com.jme3.scene.Mesh;
import com.jme3.scene.Node;
import com.jme3.scene.VertexBuffer;
import com.jme3.scene.VertexBuffer.Format;
import com.jme3.scene.VertexBuffer.Type;
import com.jme3.scene.VertexBuffer.Usage;
import com.jme3.scene.plugins.blender.BlenderContext;
import com.jme3.scene.plugins.blender.BlenderContext.LoadedFeatureDataType;
import com.jme3.scene.plugins.blender.animations.ArmatureHelper;
import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
import com.jme3.scene.plugins.blender.file.FileBlockHeader;
import com.jme3.scene.plugins.blender.file.Pointer;
import com.jme3.scene.plugins.blender.file.Structure;
import com.jme3.scene.plugins.blender.meshes.MeshContext;
import com.jme3.scene.plugins.blender.objects.ObjectHelper;
import com.jme3.scene.plugins.ogre.AnimData;
import com.jme3.util.BufferUtils;

/**
 * This modifier allows to add bone animation to the object.
 * 
 * @author <NAME> (Kaelthas)
 */
/* package */class ArmatureModifier extends Modifier {
	private static final Logger	LOGGER						= Logger.getLogger(ArmatureModifier.class.getName());
	private static final int	MAXIMUM_WEIGHTS_PER_VERTEX	= 4;
	// @Marcin it was an Ogre limitation, but as long as we use a MaxNumWeight
	// variable in mesh,
	// i guess this limitation has no sense for the blender loader...so i guess
	// it's up to you. You'll have to deternine the max weight according to the
	// provided blend file
	// I added a check to avoid crash when loading a model that has more than 4
	// weight per vertex on line 258
	// If you decide to remove this limitation, remove this code.
	// Rémy

	private Skeleton skeleton;
	private Structure objectStructure;
	private Structure meshStructure;
	
	/** Loaded animation data. */
	private AnimData			animData;
	/** Old memory address of the mesh that will have the skeleton applied. */
	private Long				meshOMA;

	/**
	 * 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 {
		Structure meshStructure = ((Pointer) objectStructure.getFieldValue("data")).fetchData(blenderContext.getInputStream()).get(0);
		Pointer pDvert = (Pointer) meshStructure.getFieldValue("dvert");// dvert = DeformVERTices

		// if pDvert==null then there are not vertex groups and no need to load
		// skeleton (untill bone envelopes are supported)
		if (this.validate(modifierStructure, blenderContext) && pDvert.isNotNull()) {
			Pointer pArmatureObject = (Pointer) modifierStructure.getFieldValue("object");
			if (pArmatureObject.isNotNull()) {
				ArmatureHelper armatureHelper = blenderContext.getHelper(ArmatureHelper.class);

				Structure armatureObject = pArmatureObject.fetchData(blenderContext.getInputStream()).get(0);

				// load skeleton
				Structure armatureStructure = ((Pointer) armatureObject.getFieldValue("data")).fetchData(blenderContext.getInputStream()).get(0);

				Structure pose = ((Pointer) armatureObject.getFieldValue("pose")).fetchData(blenderContext.getInputStream()).get(0);
				List<Structure> chanbase = ((Structure) pose.getFieldValue("chanbase")).evaluateListBase(blenderContext);

				Map<Long, Structure> bonesPoseChannels = new HashMap<Long, Structure>(chanbase.size());
				for (Structure poseChannel : chanbase) {
					Pointer pBone = (Pointer) poseChannel.getFieldValue("bone");
					bonesPoseChannels.put(pBone.getOldMemoryAddress(), poseChannel);
				}

				ObjectHelper objectHelper = blenderContext.getHelper(ObjectHelper.class);
				Matrix4f armatureObjectMatrix = objectHelper.getMatrix(armatureObject, "obmat", true);
				Matrix4f inverseMeshObjectMatrix = objectHelper.getMatrix(objectStructure, "obmat", true).invertLocal();
				Matrix4f objectToArmatureTransformation = armatureObjectMatrix.multLocal(inverseMeshObjectMatrix);

				List<Structure> bonebase = ((Structure) armatureStructure.getFieldValue("bonebase")).evaluateListBase(blenderContext);
				List<Bone> bonesList = new ArrayList<Bone>();
				for (int i = 0; i < bonebase.size(); ++i) {
					armatureHelper.buildBones(bonebase.get(i), null, bonesList, objectToArmatureTransformation, bonesPoseChannels, blenderContext);
				}
				bonesList.add(0, new Bone(""));
				Bone[] bones = bonesList.toArray(new Bone[bonesList.size()]);
				skeleton = new Skeleton(bones);
				this.objectStructure = objectStructure;
				this.meshStructure = meshStructure;
				
				// read mesh indexes
				this.meshOMA = meshStructure.getOldMemoryAddress();

				// 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, skeleton, blenderContext);
						if(tracks != null && tracks.length > 0) {
							// 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);
						}
					}
				}
				//fetching action defined in object
				Pointer pAction = (Pointer) objectStructure.getFieldValue("action");
				if (pAction.isNotNull()) {
					Structure actionStructure = pAction.fetchData(blenderContext.getInputStream()).get(0);
					String actionName = actionStructure.getName();

					BoneTrack[] tracks = armatureHelper.getTracks(actionStructure, skeleton, blenderContext);
					if(tracks != null && tracks.length > 0) {
						// 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(skeleton, animations);

				// store the animation data for each bone
				for (Bone bone : bones) {
					Long boneOma = armatureHelper.getBoneOMA(bone);
					if (boneOma != null) {
						blenderContext.setAnimData(boneOma, animData);
					}
				}
			}
		}
	}

	@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 || skeleton == null) {
			return node;
		}

		// setting weights for bones
		List<Geometry> geomList = (List<Geometry>) blenderContext.getLoadedFeature(meshOMA, LoadedFeatureDataType.LOADED_FEATURE);
		MeshContext meshContext = blenderContext.getMeshContext(meshOMA);
		int[] bonesGroups = new int[] { 0 };
		for (Geometry geom : geomList) {
			int materialIndex = meshContext.getMaterialIndex(geom);
			Mesh mesh = geom.getMesh();
			
{'fraction_non_alphanumeric': 0.10201081843838193, 'fraction_numerical': 0.005879586077140169, 'mean_word_length': 4.168337891218474, 'pattern_counts': {'":': 0, '<': 29, '<?xml version=': 0, '>': 35, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3024135', 'n_tokens_mistral': 5749, 'n_tokens_neox': 4797, 'n_words': 1219}