|
@@ -1,15 +1,12 @@
|
|
package com.jme3.scene.plugins.blender.constraints;
|
|
package com.jme3.scene.plugins.blender.constraints;
|
|
|
|
|
|
import java.util.ArrayList;
|
|
import java.util.ArrayList;
|
|
-import java.util.Collections;
|
|
|
|
-import java.util.Comparator;
|
|
|
|
import java.util.HashMap;
|
|
import java.util.HashMap;
|
|
import java.util.HashSet;
|
|
import java.util.HashSet;
|
|
import java.util.List;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
import java.util.Map;
|
|
import java.util.Map.Entry;
|
|
import java.util.Map.Entry;
|
|
import java.util.Set;
|
|
import java.util.Set;
|
|
-import java.util.logging.Level;
|
|
|
|
import java.util.logging.Logger;
|
|
import java.util.logging.Logger;
|
|
|
|
|
|
import com.jme3.animation.AnimChannel;
|
|
import com.jme3.animation.AnimChannel;
|
|
@@ -43,14 +40,13 @@ import com.jme3.util.TempVars;
|
|
public class SimulationNode {
|
|
public class SimulationNode {
|
|
private static final Logger LOGGER = Logger.getLogger(SimulationNode.class.getName());
|
|
private static final Logger LOGGER = Logger.getLogger(SimulationNode.class.getName());
|
|
|
|
|
|
|
|
+ private Long featureOMA;
|
|
/** The blender context. */
|
|
/** The blender context. */
|
|
private BlenderContext blenderContext;
|
|
private BlenderContext blenderContext;
|
|
/** The name of the node (for debugging purposes). */
|
|
/** The name of the node (for debugging purposes). */
|
|
private String name;
|
|
private String name;
|
|
/** A list of children for the node (either bones or child spatials). */
|
|
/** A list of children for the node (either bones or child spatials). */
|
|
private List<SimulationNode> children = new ArrayList<SimulationNode>();
|
|
private List<SimulationNode> children = new ArrayList<SimulationNode>();
|
|
- /** A list of constraints that the current node has. */
|
|
|
|
- private List<Constraint> constraints;
|
|
|
|
/** A list of node's animations. */
|
|
/** A list of node's animations. */
|
|
private List<Animation> animations;
|
|
private List<Animation> animations;
|
|
|
|
|
|
@@ -68,7 +64,7 @@ public class SimulationNode {
|
|
private Transform spatialStartTransform;
|
|
private Transform spatialStartTransform;
|
|
/** Star transformations for bones. Needed to properly reset the bones. */
|
|
/** Star transformations for bones. Needed to properly reset the bones. */
|
|
private Map<Bone, Transform> boneStartTransforms;
|
|
private Map<Bone, Transform> boneStartTransforms;
|
|
-
|
|
|
|
|
|
+
|
|
/**
|
|
/**
|
|
* Builds the nodes tree for the given feature. The feature (bone or
|
|
* Builds the nodes tree for the given feature. The feature (bone or
|
|
* spatial) is found by its OMA. The feature must be a root bone or a root
|
|
* spatial) is found by its OMA. The feature must be a root bone or a root
|
|
@@ -94,6 +90,7 @@ public class SimulationNode {
|
|
* indicates if the feature is a root bone or root spatial or not
|
|
* indicates if the feature is a root bone or root spatial or not
|
|
*/
|
|
*/
|
|
private SimulationNode(Long featureOMA, BlenderContext blenderContext, boolean rootNode) {
|
|
private SimulationNode(Long featureOMA, BlenderContext blenderContext, boolean rootNode) {
|
|
|
|
+ this.featureOMA = featureOMA;
|
|
this.blenderContext = blenderContext;
|
|
this.blenderContext = blenderContext;
|
|
Node spatial = (Node) blenderContext.getLoadedFeature(featureOMA, LoadedDataType.FEATURE);
|
|
Node spatial = (Node) blenderContext.getLoadedFeature(featureOMA, LoadedDataType.FEATURE);
|
|
if (blenderContext.getMarkerValue(ObjectHelper.ARMATURE_NODE_MARKER, spatial) != null) {
|
|
if (blenderContext.getMarkerValue(ObjectHelper.ARMATURE_NODE_MARKER, spatial) != null) {
|
|
@@ -117,22 +114,8 @@ public class SimulationNode {
|
|
|
|
|
|
name = '>' + spatial.getName() + '<';
|
|
name = '>' + spatial.getName() + '<';
|
|
|
|
|
|
- constraints = this.findConstraints(featureOMA, blenderContext);
|
|
|
|
- if (constraints == null) {
|
|
|
|
- constraints = new ArrayList<Constraint>();
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
// add children nodes
|
|
// add children nodes
|
|
if (skeleton != null) {
|
|
if (skeleton != null) {
|
|
- // bone with index 0 is a root bone and should not be considered
|
|
|
|
- // here
|
|
|
|
- for (int i = 1; i < skeleton.getBoneCount(); ++i) {
|
|
|
|
- BoneContext boneContext = blenderContext.getBoneContext(skeleton.getBone(i));
|
|
|
|
- List<Constraint> boneConstraints = this.findConstraints(boneContext.getBoneOma(), blenderContext);
|
|
|
|
- if (boneConstraints != null) {
|
|
|
|
- constraints.addAll(boneConstraints);
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
Node node = blenderContext.getControlledNode(skeleton);
|
|
Node node = blenderContext.getControlledNode(skeleton);
|
|
Long animatedNodeOMA = ((Number) blenderContext.getMarkerValue(ObjectHelper.OMA_MARKER, node)).longValue();
|
|
Long animatedNodeOMA = ((Number) blenderContext.getMarkerValue(ObjectHelper.OMA_MARKER, node)).longValue();
|
|
animations = blenderContext.getAnimations(animatedNodeOMA);
|
|
animations = blenderContext.getAnimations(animatedNodeOMA);
|
|
@@ -144,38 +127,6 @@ public class SimulationNode {
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
-
|
|
|
|
- LOGGER.info("Removing invalid constraints.");
|
|
|
|
- List<Constraint> validConstraints = new ArrayList<Constraint>(constraints.size());
|
|
|
|
- for (Constraint constraint : constraints) {
|
|
|
|
- if (constraint.validate()) {
|
|
|
|
- validConstraints.add(constraint);
|
|
|
|
- } else {
|
|
|
|
- LOGGER.log(Level.WARNING, "Constraint {0} is invalid and will not be applied.", constraint.name);
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- constraints = validConstraints;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /**
|
|
|
|
- * Tells if the node already contains the given constraint (so that it is
|
|
|
|
- * not applied twice).
|
|
|
|
- *
|
|
|
|
- * @param constraint
|
|
|
|
- * the constraint to be checked
|
|
|
|
- * @return <b>true</b> if the constraint already is stored in the node and
|
|
|
|
- * <b>false</b> otherwise
|
|
|
|
- */
|
|
|
|
- public boolean contains(Constraint constraint) {
|
|
|
|
- boolean result = false;
|
|
|
|
- if (constraints != null && constraints.size() > 0) {
|
|
|
|
- for (Constraint c : constraints) {
|
|
|
|
- if (c.equals(constraint)) {
|
|
|
|
- return true;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- return result;
|
|
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -191,6 +142,7 @@ public class SimulationNode {
|
|
for (Entry<Bone, Transform> entry : boneStartTransforms.entrySet()) {
|
|
for (Entry<Bone, Transform> entry : boneStartTransforms.entrySet()) {
|
|
Transform t = entry.getValue();
|
|
Transform t = entry.getValue();
|
|
entry.getKey().setBindTransforms(t.getTranslation(), t.getRotation(), t.getScale());
|
|
entry.getKey().setBindTransforms(t.getTranslation(), t.getRotation(), t.getScale());
|
|
|
|
+ entry.getKey().updateModelTransforms();
|
|
}
|
|
}
|
|
skeleton.reset();
|
|
skeleton.reset();
|
|
}
|
|
}
|
|
@@ -200,7 +152,9 @@ public class SimulationNode {
|
|
* Simulates the spatial node.
|
|
* Simulates the spatial node.
|
|
*/
|
|
*/
|
|
private void simulateSpatial() {
|
|
private void simulateSpatial() {
|
|
|
|
+ List<Constraint> constraints = blenderContext.getConstraints(featureOMA);
|
|
if (constraints != null && constraints.size() > 0) {
|
|
if (constraints != null && constraints.size() > 0) {
|
|
|
|
+ LOGGER.fine("Simulating spatial.");
|
|
boolean applyStaticConstraints = true;
|
|
boolean applyStaticConstraints = true;
|
|
if (animations != null) {
|
|
if (animations != null) {
|
|
for (Animation animation : animations) {
|
|
for (Animation animation : animations) {
|
|
@@ -248,83 +202,86 @@ public class SimulationNode {
|
|
* Simulates the bone node.
|
|
* Simulates the bone node.
|
|
*/
|
|
*/
|
|
private void simulateSkeleton() {
|
|
private void simulateSkeleton() {
|
|
- if (constraints != null && constraints.size() > 0) {
|
|
|
|
- Set<Long> alteredOmas = new HashSet<Long>();
|
|
|
|
-
|
|
|
|
- if (animations != null) {
|
|
|
|
- TempVars vars = TempVars.get();
|
|
|
|
- AnimChannel animChannel = animControl.createChannel();
|
|
|
|
-
|
|
|
|
- List<Bone> bonesWithConstraints = this.collectBonesWithConstraints(skeleton);
|
|
|
|
- for (Animation animation : animations) {
|
|
|
|
- float[] animationTimeBoundaries = this.computeAnimationTimeBoundaries(animation);
|
|
|
|
- int maxFrame = (int) animationTimeBoundaries[0];
|
|
|
|
- float maxTime = animationTimeBoundaries[1];
|
|
|
|
-
|
|
|
|
- Map<Integer, VirtualTrack> tracks = new HashMap<Integer, VirtualTrack>();
|
|
|
|
- for (int frame = 0; frame < maxFrame; ++frame) {
|
|
|
|
- // this MUST be done here, otherwise setting next frame of animation will
|
|
|
|
- // lead to possible errors
|
|
|
|
- this.reset();
|
|
|
|
|
|
+ LOGGER.fine("Simulating skeleton.");
|
|
|
|
+ Set<Long> alteredOmas = new HashSet<Long>();
|
|
|
|
+
|
|
|
|
+ if (animations != null) {
|
|
|
|
+ TempVars vars = TempVars.get();
|
|
|
|
+ AnimChannel animChannel = animControl.createChannel();
|
|
|
|
+
|
|
|
|
+ // List<Bone> bonesWithConstraints = this.collectBonesWithConstraints(skeleton);
|
|
|
|
+ for (Animation animation : animations) {
|
|
|
|
+ float[] animationTimeBoundaries = this.computeAnimationTimeBoundaries(animation);
|
|
|
|
+ int maxFrame = (int) animationTimeBoundaries[0];
|
|
|
|
+ float maxTime = animationTimeBoundaries[1];
|
|
|
|
+
|
|
|
|
+ Map<Integer, VirtualTrack> tracks = new HashMap<Integer, VirtualTrack>();
|
|
|
|
+ for (int frame = 0; frame < maxFrame; ++frame) {
|
|
|
|
+ // this MUST be done here, otherwise setting next frame of animation will
|
|
|
|
+ // lead to possible errors
|
|
|
|
+ this.reset();
|
|
|
|
+
|
|
|
|
+ // first set proper time for all bones in all the tracks ...
|
|
|
|
+ for (Track track : animation.getTracks()) {
|
|
|
|
+ float time = ((BoneTrack) track).getTimes()[frame];
|
|
|
|
+ track.setTime(time, 1, animControl, animChannel, vars);
|
|
|
|
+ skeleton.updateWorldVectors();
|
|
|
|
+ }
|
|
|
|
|
|
- // first set proper time for all bones in all the tracks ...
|
|
|
|
- for (Track track : animation.getTracks()) {
|
|
|
|
- float time = ((BoneTrack) track).getTimes()[frame];
|
|
|
|
- track.setTime(time, 1, animControl, animChannel, vars);
|
|
|
|
- skeleton.updateWorldVectors();
|
|
|
|
|
|
+ // ... and then apply constraints from the root bone to the last child ...
|
|
|
|
+ Set<Long> applied = new HashSet<Long>();
|
|
|
|
+ for (Bone rootBone : skeleton.getRoots()) {
|
|
|
|
+ // ignore the 0-indexed bone
|
|
|
|
+ if (skeleton.getBoneIndex(rootBone) > 0) {
|
|
|
|
+ this.applyConstraints(rootBone, alteredOmas, applied, frame);
|
|
}
|
|
}
|
|
|
|
+ }
|
|
|
|
|
|
- // ... and then apply constraints from the root bone to the last child ...
|
|
|
|
- for (Bone rootBone : bonesWithConstraints) {
|
|
|
|
- this.applyConstraints(rootBone, alteredOmas, frame);
|
|
|
|
|
|
+ // ... add virtual tracks if neccessary, for bones that were altered but had no tracks before ...
|
|
|
|
+ for (Long boneOMA : alteredOmas) {
|
|
|
|
+ BoneContext boneContext = blenderContext.getBoneContext(boneOMA);
|
|
|
|
+ int boneIndex = skeleton.getBoneIndex(boneContext.getBone());
|
|
|
|
+ if (!tracks.containsKey(boneIndex)) {
|
|
|
|
+ tracks.put(boneIndex, new VirtualTrack(boneContext.getBone().getName(), maxFrame, maxTime));
|
|
}
|
|
}
|
|
|
|
+ }
|
|
|
|
+ alteredOmas.clear();
|
|
|
|
|
|
- // ... add virtual tracks if neccessary, for bones that were altered but had no tracks before ...
|
|
|
|
- for (Long boneOMA : alteredOmas) {
|
|
|
|
- BoneContext boneContext = blenderContext.getBoneContext(boneOMA);
|
|
|
|
- int boneIndex = skeleton.getBoneIndex(boneContext.getBone());
|
|
|
|
- if (!tracks.containsKey(boneIndex)) {
|
|
|
|
- tracks.put(boneIndex, new VirtualTrack(boneContext.getBone().getName(), maxFrame, maxTime));
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- alteredOmas.clear();
|
|
|
|
-
|
|
|
|
- // ... and fill in another frame in the result track
|
|
|
|
- for (Entry<Integer, VirtualTrack> trackEntry : tracks.entrySet()) {
|
|
|
|
- Bone bone = skeleton.getBone(trackEntry.getKey());
|
|
|
|
- Transform startTransform = boneStartTransforms.get(bone);
|
|
|
|
|
|
+ // ... and fill in another frame in the result track
|
|
|
|
+ for (Entry<Integer, VirtualTrack> trackEntry : tracks.entrySet()) {
|
|
|
|
+ Bone bone = skeleton.getBone(trackEntry.getKey());
|
|
|
|
+ Transform startTransform = boneStartTransforms.get(bone);
|
|
|
|
|
|
- // track contains differences between the frame position and bind positions of bones/spatials
|
|
|
|
- Vector3f bonePositionDifference = bone.getLocalPosition().subtract(startTransform.getTranslation());
|
|
|
|
- Quaternion boneRotationDifference = startTransform.getRotation().inverse().mult(bone.getLocalRotation()).normalizeLocal();
|
|
|
|
- Vector3f boneScaleDifference = bone.getLocalScale().divide(startTransform.getScale());
|
|
|
|
|
|
+ // track contains differences between the frame position and bind positions of bones/spatials
|
|
|
|
+ Vector3f bonePositionDifference = bone.getLocalPosition().subtract(startTransform.getTranslation());
|
|
|
|
+ Quaternion boneRotationDifference = startTransform.getRotation().inverse().mult(bone.getLocalRotation()).normalizeLocal();
|
|
|
|
+ Vector3f boneScaleDifference = bone.getLocalScale().divide(startTransform.getScale());
|
|
|
|
|
|
- trackEntry.getValue().setTransform(frame, new Transform(bonePositionDifference, boneRotationDifference, boneScaleDifference));
|
|
|
|
- }
|
|
|
|
|
|
+ trackEntry.getValue().setTransform(frame, new Transform(bonePositionDifference, boneRotationDifference, boneScaleDifference));
|
|
}
|
|
}
|
|
|
|
+ }
|
|
|
|
|
|
- for (Entry<Integer, VirtualTrack> trackEntry : tracks.entrySet()) {
|
|
|
|
- Track newTrack = trackEntry.getValue().getAsBoneTrack(trackEntry.getKey());
|
|
|
|
- if (newTrack != null) {
|
|
|
|
- boolean trackReplaced = false;
|
|
|
|
- for (Track track : animation.getTracks()) {
|
|
|
|
- if (((BoneTrack) track).getTargetBoneIndex() == trackEntry.getKey().intValue()) {
|
|
|
|
- animation.removeTrack(track);
|
|
|
|
- animation.addTrack(newTrack);
|
|
|
|
- trackReplaced = true;
|
|
|
|
- break;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- if (!trackReplaced) {
|
|
|
|
|
|
+ for (Entry<Integer, VirtualTrack> trackEntry : tracks.entrySet()) {
|
|
|
|
+ Track newTrack = trackEntry.getValue().getAsBoneTrack(trackEntry.getKey());
|
|
|
|
+ if (newTrack != null) {
|
|
|
|
+ boolean trackReplaced = false;
|
|
|
|
+ for (Track track : animation.getTracks()) {
|
|
|
|
+ if (((BoneTrack) track).getTargetBoneIndex() == trackEntry.getKey().intValue()) {
|
|
|
|
+ animation.removeTrack(track);
|
|
animation.addTrack(newTrack);
|
|
animation.addTrack(newTrack);
|
|
|
|
+ trackReplaced = true;
|
|
|
|
+ break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
+ if (!trackReplaced) {
|
|
|
|
+ animation.addTrack(newTrack);
|
|
|
|
+ }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
- vars.release();
|
|
|
|
- animControl.clearChannels();
|
|
|
|
- this.reset();
|
|
|
|
}
|
|
}
|
|
|
|
+ vars.release();
|
|
|
|
+ animControl.clearChannels();
|
|
|
|
+ this.reset();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@@ -338,16 +295,32 @@ public class SimulationNode {
|
|
* @param frame
|
|
* @param frame
|
|
* the current frame of the animation
|
|
* the current frame of the animation
|
|
*/
|
|
*/
|
|
- private void applyConstraints(Bone bone, Set<Long> alteredOmas, int frame) {
|
|
|
|
|
|
+ private void applyConstraints(Bone bone, Set<Long> alteredOmas, Set<Long> applied, int frame) {
|
|
BoneContext boneContext = blenderContext.getBoneContext(bone);
|
|
BoneContext boneContext = blenderContext.getBoneContext(bone);
|
|
- List<Constraint> constraints = this.findConstraints(boneContext.getBoneOma(), blenderContext);
|
|
|
|
- if (constraints != null && constraints.size() > 0) {
|
|
|
|
- for (Constraint constraint : constraints) {
|
|
|
|
- constraint.apply(frame);
|
|
|
|
- if (constraint.getAlteredOmas() != null) {
|
|
|
|
- alteredOmas.addAll(constraint.getAlteredOmas());
|
|
|
|
|
|
+ if(!applied.contains(boneContext.getBoneOma())) {
|
|
|
|
+ List<Constraint> constraints = this.findConstraints(boneContext.getBoneOma(), blenderContext);
|
|
|
|
+ if (constraints != null && constraints.size() > 0) {
|
|
|
|
+ // TODO: BEWARE OF INFINITE LOOPS !!!!!!!!!!!!!!!!!!!!!!!!!!
|
|
|
|
+ for (Constraint constraint : constraints) {
|
|
|
|
+ if (constraint.getTargetOMA() != null && constraint.getTargetOMA() > 0L) {
|
|
|
|
+ // first apply constraints of the target bone
|
|
|
|
+ BoneContext targetBone = blenderContext.getBoneContext(constraint.getTargetOMA());
|
|
|
|
+ this.applyConstraints(targetBone.getBone(), alteredOmas, applied, frame);
|
|
|
|
+ }
|
|
|
|
+ constraint.apply(frame);
|
|
|
|
+ if (constraint.getAlteredOmas() != null) {
|
|
|
|
+ alteredOmas.addAll(constraint.getAlteredOmas());
|
|
|
|
+ }
|
|
|
|
+ alteredOmas.add(boneContext.getBoneOma());
|
|
}
|
|
}
|
|
- alteredOmas.add(boneContext.getBoneOma());
|
|
|
|
|
|
+ }
|
|
|
|
+ applied.add(boneContext.getBoneOma());
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ List<Bone> children = bone.getChildren();
|
|
|
|
+ if (children != null && children.size() > 0) {
|
|
|
|
+ for (Bone child : bone.getChildren()) {
|
|
|
|
+ this.applyConstraints(child, alteredOmas, applied, frame);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
@@ -364,150 +337,6 @@ public class SimulationNode {
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
- /**
|
|
|
|
- * Collects the bones that will take part in constraint computations.
|
|
|
|
- * The result will not include bones whose constraints will not change them or are invalid.
|
|
|
|
- * The bones are sorted so that the constraint applying is done in the proper order.
|
|
|
|
- * @param skeleton
|
|
|
|
- * the simulated skeleton
|
|
|
|
- * @return a list of bones that will take part in constraints computations
|
|
|
|
- */
|
|
|
|
- private List<Bone> collectBonesWithConstraints(Skeleton skeleton) {
|
|
|
|
- Map<BoneContext, List<Constraint>> bonesWithConstraints = new HashMap<BoneContext, List<Constraint>>();
|
|
|
|
- for (int i = 1; i < skeleton.getBoneCount(); ++i) {// ommit the 0 - indexed root bone as it is the bone added by importer
|
|
|
|
- Bone bone = skeleton.getBone(i);
|
|
|
|
- BoneContext boneContext = blenderContext.getBoneContext(bone);
|
|
|
|
- List<Constraint> constraints = this.findConstraints(boneContext.getBoneOma(), blenderContext);
|
|
|
|
- if (constraints != null && constraints.size() > 0) {
|
|
|
|
- bonesWithConstraints.put(boneContext, constraints);
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- // first sort out constraints that are not implemented or invalid or will not affect the bone's tracks
|
|
|
|
- List<BoneContext> bonesToRemove = new ArrayList<BoneContext>(bonesWithConstraints.size());
|
|
|
|
- for (Entry<BoneContext, List<Constraint>> entry : bonesWithConstraints.entrySet()) {
|
|
|
|
- List<Constraint> validConstraints = new ArrayList<Constraint>(entry.getValue().size());
|
|
|
|
- for (Constraint constraint : entry.getValue()) {// TODO: sprawdziæ czy wprowadza jakiekolwiek zmiany
|
|
|
|
- if (constraint.isImplemented() && constraint.validate() && constraint.isTrackToBeChanged()) {
|
|
|
|
- validConstraints.add(constraint);
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- if (validConstraints.size() > 0) {
|
|
|
|
- entry.setValue(validConstraints);
|
|
|
|
- } else {
|
|
|
|
- bonesToRemove.add(entry.getKey());
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- for (BoneContext boneContext : bonesToRemove) {
|
|
|
|
- bonesWithConstraints.remove(boneContext);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- List<BoneContext> bonesConstrainedWithoutTarget = new ArrayList<BoneContext>();
|
|
|
|
- Set<Long> remainedOMAS = new HashSet<Long>();
|
|
|
|
- // later move all bones with not dependant constraints to the front
|
|
|
|
- bonesToRemove.clear();
|
|
|
|
- for (Entry<BoneContext, List<Constraint>> entry : bonesWithConstraints.entrySet()) {
|
|
|
|
- boolean hasDependantConstraints = false;
|
|
|
|
- for (Constraint constraint : entry.getValue()) {
|
|
|
|
- if (constraint.targetOMA != null) {
|
|
|
|
- hasDependantConstraints = true;
|
|
|
|
- break;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (!hasDependantConstraints) {
|
|
|
|
- bonesConstrainedWithoutTarget.add(entry.getKey());
|
|
|
|
- bonesToRemove.add(entry.getKey());
|
|
|
|
- } else {
|
|
|
|
- remainedOMAS.add(entry.getKey().getBoneOma());
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- for (BoneContext boneContext : bonesToRemove) {
|
|
|
|
- bonesWithConstraints.remove(boneContext);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- this.sortBonesByChain(bonesConstrainedWithoutTarget);
|
|
|
|
-
|
|
|
|
- // another step is to add those bones whose constraints depend only on bones already added to the result or to those
|
|
|
|
- // that are not included neither in the result nor in the remaining map
|
|
|
|
- // do this as long as bones are being moved to the result and the 'bonesWithConstraints' is not empty
|
|
|
|
- List<BoneContext> bonesConstrainedWithTarget = new ArrayList<BoneContext>();
|
|
|
|
- do {
|
|
|
|
- bonesToRemove.clear();
|
|
|
|
- for (Entry<BoneContext, List<Constraint>> entry : bonesWithConstraints.entrySet()) {
|
|
|
|
- boolean unconstrainedBone = true;
|
|
|
|
- for (Constraint constraint : entry.getValue()) {
|
|
|
|
- if (remainedOMAS.contains(constraint.getTargetOMA())) {
|
|
|
|
- unconstrainedBone = false;
|
|
|
|
- break;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- if (unconstrainedBone) {
|
|
|
|
- bonesToRemove.add(entry.getKey());
|
|
|
|
- bonesConstrainedWithTarget.add(entry.getKey());
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- for (BoneContext boneContext : bonesToRemove) {
|
|
|
|
- bonesWithConstraints.remove(boneContext);
|
|
|
|
- remainedOMAS.remove(boneContext.getBoneOma());
|
|
|
|
- }
|
|
|
|
- } while (bonesWithConstraints.size() > 0 && bonesToRemove.size() > 0);
|
|
|
|
- this.sortBonesByChain(bonesConstrainedWithoutTarget);
|
|
|
|
-
|
|
|
|
- // prepare the result
|
|
|
|
- List<Bone> result = new ArrayList<Bone>();
|
|
|
|
- for (BoneContext boneContext : bonesConstrainedWithoutTarget) {
|
|
|
|
- result.add(boneContext.getBone());
|
|
|
|
- }
|
|
|
|
- for (BoneContext boneContext : bonesConstrainedWithTarget) {
|
|
|
|
- result.add(boneContext.getBone());
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- // in the end prepare the mapping between bone OMA
|
|
|
|
- if (bonesWithConstraints.size() > 0) {
|
|
|
|
- LOGGER.warning("Some bones have loops in their constraints' definitions. The result might not be properly computed!");
|
|
|
|
- for (BoneContext boneContext : bonesWithConstraints.keySet()) {
|
|
|
|
- result.add(boneContext.getBone());
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- return result;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /**
|
|
|
|
- * The method sorts the given bones from root to top.
|
|
|
|
- * If the list contains bones from different branches then those branches will be listed
|
|
|
|
- * one after another - which means that bones will be grouped by branches they belong to.
|
|
|
|
- * @param bones
|
|
|
|
- * a list of bones
|
|
|
|
- */
|
|
|
|
- private void sortBonesByChain(List<BoneContext> bones) {
|
|
|
|
- Map<BoneContext, List<BoneContext>> branches = new HashMap<BoneContext, List<BoneContext>>();
|
|
|
|
-
|
|
|
|
- for (BoneContext bone : bones) {
|
|
|
|
- BoneContext root = bone.getRoot();
|
|
|
|
- List<BoneContext> list = branches.get(root);
|
|
|
|
- if (list == null) {
|
|
|
|
- list = new ArrayList<BoneContext>();
|
|
|
|
- branches.put(root, list);
|
|
|
|
- }
|
|
|
|
- list.add(bone);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- // sort the bones in each branch from root to leaf
|
|
|
|
- bones.clear();
|
|
|
|
- for (Entry<BoneContext, List<BoneContext>> entry : branches.entrySet()) {
|
|
|
|
- Collections.sort(entry.getValue(), new Comparator<BoneContext>() {
|
|
|
|
- @Override
|
|
|
|
- public int compare(BoneContext o1, BoneContext o2) {
|
|
|
|
- return o1.getDistanceFromRoot() - o2.getDistanceFromRoot();
|
|
|
|
- }
|
|
|
|
- });
|
|
|
|
- bones.addAll(entry.getValue());
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
/**
|
|
/**
|
|
* Computes the maximum frame and time for the animation. Different tracks
|
|
* Computes the maximum frame and time for the animation. Different tracks
|
|
* can have different lengths so here the maximum one is being found.
|
|
* can have different lengths so here the maximum one is being found.
|
|
@@ -547,11 +376,10 @@ public class SimulationNode {
|
|
List<Constraint> constraints = blenderContext.getConstraints(ownerOMA);
|
|
List<Constraint> constraints = blenderContext.getConstraints(ownerOMA);
|
|
if (constraints != null) {
|
|
if (constraints != null) {
|
|
for (Constraint constraint : constraints) {
|
|
for (Constraint constraint : constraints) {
|
|
- if (constraint.isImplemented() && constraint.validate()) {
|
|
|
|
|
|
+ if (constraint.isImplemented() && constraint.validate() && constraint.isTrackToBeChanged()) {
|
|
result.add(constraint);
|
|
result.add(constraint);
|
|
- } else {
|
|
|
|
- LOGGER.log(Level.WARNING, "Constraint named: ''{0}'' of type ''{1}'' is not implemented and will NOT be applied!", new Object[] { constraint.name, constraint.getConstraintTypeName() });
|
|
|
|
}
|
|
}
|
|
|
|
+ // TODO: add proper warnings to some map or set so that they are not logged on every frame
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return result.size() > 0 ? result : null;
|
|
return result.size() > 0 ? result : null;
|