Merge branch 'master' of https://github.com/jMonkeyEngine/jmonkeyengine.git
This commit is contained in:
commit
09b72fc388
@ -7,6 +7,7 @@ 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.Stack;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import com.jme3.animation.AnimChannel;
|
import com.jme3.animation.AnimChannel;
|
||||||
@ -38,9 +39,9 @@ import com.jme3.util.TempVars;
|
|||||||
* @author Marcin Roguski (Kaelthas)
|
* @author Marcin Roguski (Kaelthas)
|
||||||
*/
|
*/
|
||||||
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;
|
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). */
|
||||||
@ -51,11 +52,11 @@ public class SimulationNode {
|
|||||||
private List<Animation> animations;
|
private List<Animation> animations;
|
||||||
|
|
||||||
/** The nodes spatial (if null then the boneContext should be set). */
|
/** The nodes spatial (if null then the boneContext should be set). */
|
||||||
private Spatial spatial;
|
private Spatial spatial;
|
||||||
/** The skeleton of the bone (not null if the node simulated the bone). */
|
/** The skeleton of the bone (not null if the node simulated the bone). */
|
||||||
private Skeleton skeleton;
|
private Skeleton skeleton;
|
||||||
/** Animation controller for the node's feature. */
|
/** Animation controller for the node's feature. */
|
||||||
private AnimControl animControl;
|
private AnimControl animControl;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The star transform of a spatial. Needed to properly reset the spatial to
|
* The star transform of a spatial. Needed to properly reset the spatial to
|
||||||
@ -209,7 +210,6 @@ public class SimulationNode {
|
|||||||
TempVars vars = TempVars.get();
|
TempVars vars = TempVars.get();
|
||||||
AnimChannel animChannel = animControl.createChannel();
|
AnimChannel animChannel = animControl.createChannel();
|
||||||
|
|
||||||
// List<Bone> bonesWithConstraints = this.collectBonesWithConstraints(skeleton);
|
|
||||||
for (Animation animation : animations) {
|
for (Animation animation : animations) {
|
||||||
float[] animationTimeBoundaries = this.computeAnimationTimeBoundaries(animation);
|
float[] animationTimeBoundaries = this.computeAnimationTimeBoundaries(animation);
|
||||||
int maxFrame = (int) animationTimeBoundaries[0];
|
int maxFrame = (int) animationTimeBoundaries[0];
|
||||||
@ -233,7 +233,7 @@ public class SimulationNode {
|
|||||||
for (Bone rootBone : skeleton.getRoots()) {
|
for (Bone rootBone : skeleton.getRoots()) {
|
||||||
// ignore the 0-indexed bone
|
// ignore the 0-indexed bone
|
||||||
if (skeleton.getBoneIndex(rootBone) > 0) {
|
if (skeleton.getBoneIndex(rootBone) > 0) {
|
||||||
this.applyConstraints(rootBone, alteredOmas, applied, frame);
|
this.applyConstraints(rootBone, alteredOmas, applied, frame, new Stack<Bone>());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -294,34 +294,39 @@ public class SimulationNode {
|
|||||||
* the set of OMAS of the altered bones (is populated if necessary)
|
* the set of OMAS of the altered bones (is populated if necessary)
|
||||||
* @param frame
|
* @param frame
|
||||||
* the current frame of the animation
|
* the current frame of the animation
|
||||||
|
* @param bonesStack
|
||||||
|
* the stack of bones used to avoid infinite loops while applying constraints
|
||||||
*/
|
*/
|
||||||
private void applyConstraints(Bone bone, Set<Long> alteredOmas, Set<Long> applied, int frame) {
|
private void applyConstraints(Bone bone, Set<Long> alteredOmas, Set<Long> applied, int frame, Stack<Bone> bonesStack) {
|
||||||
BoneContext boneContext = blenderContext.getBoneContext(bone);
|
if (!bonesStack.contains(bone)) {
|
||||||
if(!applied.contains(boneContext.getBoneOma())) {
|
bonesStack.push(bone);
|
||||||
List<Constraint> constraints = this.findConstraints(boneContext.getBoneOma(), blenderContext);
|
BoneContext boneContext = blenderContext.getBoneContext(bone);
|
||||||
if (constraints != null && constraints.size() > 0) {
|
if (!applied.contains(boneContext.getBoneOma())) {
|
||||||
// TODO: BEWARE OF INFINITE LOOPS !!!!!!!!!!!!!!!!!!!!!!!!!!
|
List<Constraint> constraints = this.findConstraints(boneContext.getBoneOma(), blenderContext);
|
||||||
for (Constraint constraint : constraints) {
|
if (constraints != null && constraints.size() > 0) {
|
||||||
if (constraint.getTargetOMA() != null && constraint.getTargetOMA() > 0L) {
|
for (Constraint constraint : constraints) {
|
||||||
// first apply constraints of the target bone
|
if (constraint.getTargetOMA() != null && constraint.getTargetOMA() > 0L) {
|
||||||
BoneContext targetBone = blenderContext.getBoneContext(constraint.getTargetOMA());
|
// first apply constraints of the target bone
|
||||||
this.applyConstraints(targetBone.getBone(), alteredOmas, applied, frame);
|
BoneContext targetBone = blenderContext.getBoneContext(constraint.getTargetOMA());
|
||||||
|
this.applyConstraints(targetBone.getBone(), alteredOmas, applied, frame, bonesStack);
|
||||||
|
}
|
||||||
|
constraint.apply(frame);
|
||||||
|
if (constraint.getAlteredOmas() != null) {
|
||||||
|
alteredOmas.addAll(constraint.getAlteredOmas());
|
||||||
|
}
|
||||||
|
alteredOmas.add(boneContext.getBoneOma());
|
||||||
}
|
}
|
||||||
constraint.apply(frame);
|
}
|
||||||
if (constraint.getAlteredOmas() != null) {
|
applied.add(boneContext.getBoneOma());
|
||||||
alteredOmas.addAll(constraint.getAlteredOmas());
|
}
|
||||||
}
|
|
||||||
alteredOmas.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, bonesStack);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
applied.add(boneContext.getBoneOma());
|
bonesStack.pop();
|
||||||
}
|
|
||||||
|
|
||||||
List<Bone> children = bone.getChildren();
|
|
||||||
if (children != null && children.size() > 0) {
|
|
||||||
for (Bone child : bone.getChildren()) {
|
|
||||||
this.applyConstraints(child, alteredOmas, applied, frame);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
package com.jme3.scene.plugins.blender.constraints.definitions;
|
package com.jme3.scene.plugins.blender.constraints.definitions;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import org.ejml.simple.SimpleMatrix;
|
import org.ejml.simple.SimpleMatrix;
|
||||||
|
|
||||||
@ -19,18 +19,26 @@ import com.jme3.scene.plugins.blender.math.DTransform;
|
|||||||
import com.jme3.scene.plugins.blender.math.Matrix;
|
import com.jme3.scene.plugins.blender.math.Matrix;
|
||||||
import com.jme3.scene.plugins.blender.math.Vector3d;
|
import com.jme3.scene.plugins.blender.math.Vector3d;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A definiotion of a Inverse Kinematics constraint. This implementation uses Jacobian pseudoinverse algorithm.
|
||||||
|
*
|
||||||
|
* @author Marcin Roguski (Kaelthas)
|
||||||
|
*/
|
||||||
public class ConstraintDefinitionIK extends ConstraintDefinition {
|
public class ConstraintDefinitionIK extends ConstraintDefinition {
|
||||||
private static final float MIN_DISTANCE = 0.001f;
|
private static final float MIN_DISTANCE = 0.001f;
|
||||||
private static final int FLAG_USE_TAIL = 0x01;
|
private static final float MIN_ANGLE_CHANGE = 0.001f;
|
||||||
private static final int FLAG_POSITION = 0x20;
|
private static final int FLAG_USE_TAIL = 0x01;
|
||||||
|
private static final int FLAG_POSITION = 0x20;
|
||||||
|
|
||||||
private BonesChain bones;
|
private BonesChain bones;
|
||||||
/** The number of affected bones. Zero means that all parent bones of the current bone should take part in baking. */
|
/** The number of affected bones. Zero means that all parent bones of the current bone should take part in baking. */
|
||||||
private int bonesAffected;
|
private int bonesAffected;
|
||||||
/** Indicates if the tail of the bone should be used or not. */
|
/** Indicates if the tail of the bone should be used or not. */
|
||||||
private boolean useTail;
|
private boolean useTail;
|
||||||
/** The amount of iterations of the algorithm. */
|
/** The amount of iterations of the algorithm. */
|
||||||
private int iterations;
|
private int iterations;
|
||||||
|
/** The count of bones' chain. */
|
||||||
|
private int bonesCount = -1;
|
||||||
|
|
||||||
public ConstraintDefinitionIK(Structure constraintData, Long ownerOMA, BlenderContext blenderContext) {
|
public ConstraintDefinitionIK(Structure constraintData, Long ownerOMA, BlenderContext blenderContext) {
|
||||||
super(constraintData, ownerOMA, blenderContext);
|
super(constraintData, ownerOMA, blenderContext);
|
||||||
@ -47,85 +55,104 @@ public class ConstraintDefinitionIK extends ConstraintDefinition {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
/**
|
||||||
public Set<Long> getAlteredOmas() {
|
* Below are the variables that only need to be allocated once for IK constraint instance.
|
||||||
return bones.alteredOmas;
|
*/
|
||||||
}
|
/** Temporal quaternion. */
|
||||||
|
private DQuaternion tempDQuaternion = new DQuaternion();
|
||||||
|
/** Temporal matrix column. */
|
||||||
|
private Vector3d col = new Vector3d();
|
||||||
|
/** Effector's position change. */
|
||||||
|
private Matrix deltaP = new Matrix(3, 1);
|
||||||
|
/** The current target position. */
|
||||||
|
private Vector3d target = new Vector3d();
|
||||||
|
/** Rotation vectors for each joint (allocated when we know the size of a bones' chain. */
|
||||||
|
private Vector3d[] rotationVectors;
|
||||||
|
/** The Jacobian matrix. Allocated when the bones' chain size is known. */
|
||||||
|
private Matrix J;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void bake(Space ownerSpace, Space targetSpace, Transform targetTransform, float influence) {
|
public void bake(Space ownerSpace, Space targetSpace, Transform targetTransform, float influence) {
|
||||||
if (influence == 0 || !trackToBeChanged || targetTransform == null) {
|
if (influence == 0 || !trackToBeChanged || targetTransform == null || bonesCount == 0) {
|
||||||
return;// no need to do anything
|
return;// no need to do anything
|
||||||
}
|
}
|
||||||
|
|
||||||
DQuaternion q = new DQuaternion();
|
if (bones == null) {
|
||||||
Vector3d t = new Vector3d(targetTransform.getTranslation());
|
bones = new BonesChain((Bone) this.getOwner(), useTail, bonesAffected, alteredOmas, blenderContext);
|
||||||
bones = new BonesChain((Bone) this.getOwner(), useTail, bonesAffected, blenderContext);
|
}
|
||||||
if (bones.size() == 0) {
|
if (bones.size() == 0) {
|
||||||
|
bonesCount = 0;
|
||||||
return;// no need to do anything
|
return;// no need to do anything
|
||||||
}
|
}
|
||||||
double distanceFromTarget = Double.MAX_VALUE;
|
double distanceFromTarget = Double.MAX_VALUE;
|
||||||
|
target.set(targetTransform.getTranslation().x, targetTransform.getTranslation().y, targetTransform.getTranslation().z);
|
||||||
|
|
||||||
|
if (bonesCount < 0) {
|
||||||
|
bonesCount = bones.size();
|
||||||
|
rotationVectors = new Vector3d[bonesCount];
|
||||||
|
for (int i = 0; i < bonesCount; ++i) {
|
||||||
|
rotationVectors[i] = new Vector3d();
|
||||||
|
}
|
||||||
|
J = new Matrix(3, bonesCount);
|
||||||
|
}
|
||||||
|
|
||||||
Vector3d target = new Vector3d(targetTransform.getTranslation());
|
|
||||||
Vector3d[] rotationVectors = new Vector3d[bones.size()];
|
|
||||||
BoneContext topBone = bones.get(0);
|
BoneContext topBone = bones.get(0);
|
||||||
for (int i = 1; i <= iterations; ++i) {
|
for (int i = 0; i < iterations; ++i) {
|
||||||
DTransform topBoneTransform = bones.getWorldTransform(topBone);
|
DTransform topBoneTransform = bones.getWorldTransform(topBone);
|
||||||
Vector3d e = topBoneTransform.getTranslation().add(topBoneTransform.getRotation().mult(Vector3d.UNIT_Y).multLocal(topBone.getLength()));// effector
|
Vector3d e = topBoneTransform.getTranslation().add(topBoneTransform.getRotation().mult(Vector3d.UNIT_Y).multLocal(topBone.getLength()));// effector
|
||||||
distanceFromTarget = e.distance(t);
|
distanceFromTarget = e.distance(target);
|
||||||
if (distanceFromTarget <= MIN_DISTANCE) {
|
if (distanceFromTarget <= MIN_DISTANCE) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
Matrix deltaP = new Matrix(3, 1);
|
deltaP.setColumn(0, 0, target.x - e.x, target.y - e.y, target.z - e.z);
|
||||||
deltaP.setColumn(target.subtract(e), 0);
|
|
||||||
|
|
||||||
Matrix J = new Matrix(3, bones.size());
|
|
||||||
int column = 0;
|
int column = 0;
|
||||||
for (BoneContext boneContext : bones) {
|
for (BoneContext boneContext : bones) {
|
||||||
DTransform boneWorldTransform = bones.getWorldTransform(boneContext);
|
DTransform boneWorldTransform = bones.getWorldTransform(boneContext);
|
||||||
Vector3d j = boneWorldTransform.getTranslation(); // current join position
|
Vector3d j = boneWorldTransform.getTranslation(); // current join position
|
||||||
Vector3d vectorFromJointToEffector = e.subtract(j);
|
Vector3d vectorFromJointToEffector = e.subtract(j);
|
||||||
rotationVectors[column] = vectorFromJointToEffector.cross(target.subtract(j)).normalize();
|
vectorFromJointToEffector.cross(target.subtract(j), rotationVectors[column]).normalizeLocal();
|
||||||
Vector3d col = rotationVectors[column].cross(vectorFromJointToEffector);
|
rotationVectors[column].cross(vectorFromJointToEffector, col);
|
||||||
J.setColumn(col, column++);
|
J.setColumn(col, column++);
|
||||||
}
|
}
|
||||||
Matrix J_1 = J.pseudoinverse();
|
Matrix J_1 = J.pseudoinverse();
|
||||||
|
|
||||||
SimpleMatrix deltaThetas = J_1.mult(deltaP);
|
SimpleMatrix deltaThetas = J_1.mult(deltaP);
|
||||||
|
if (deltaThetas.elementMaxAbs() < MIN_ANGLE_CHANGE) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
for (int j = 0; j < deltaThetas.numRows(); ++j) {
|
for (int j = 0; j < deltaThetas.numRows(); ++j) {
|
||||||
double angle = deltaThetas.get(j, 0);
|
double angle = deltaThetas.get(j, 0);
|
||||||
Vector3d rotationVector = rotationVectors[j];
|
Vector3d rotationVector = rotationVectors[j];
|
||||||
|
|
||||||
q.fromAngleAxis(angle, rotationVector);
|
tempDQuaternion.fromAngleAxis(angle, rotationVector);
|
||||||
BoneContext boneContext = bones.get(j);
|
BoneContext boneContext = bones.get(j);
|
||||||
Bone bone = boneContext.getBone();
|
Bone bone = boneContext.getBone();
|
||||||
if (bone.equals(this.getOwner())) {
|
if (bone.equals(this.getOwner())) {
|
||||||
if (boneContext.isLockX()) {
|
if (boneContext.isLockX()) {
|
||||||
q.set(0, q.getY(), q.getZ(), q.getW());
|
tempDQuaternion.set(0, tempDQuaternion.getY(), tempDQuaternion.getZ(), tempDQuaternion.getW());
|
||||||
}
|
}
|
||||||
if (boneContext.isLockY()) {
|
if (boneContext.isLockY()) {
|
||||||
q.set(q.getX(), 0, q.getZ(), q.getW());
|
tempDQuaternion.set(tempDQuaternion.getX(), 0, tempDQuaternion.getZ(), tempDQuaternion.getW());
|
||||||
}
|
}
|
||||||
if (boneContext.isLockZ()) {
|
if (boneContext.isLockZ()) {
|
||||||
q.set(q.getX(), q.getY(), 0, q.getW());
|
tempDQuaternion.set(tempDQuaternion.getX(), tempDQuaternion.getY(), 0, tempDQuaternion.getW());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DTransform boneTransform = bones.getWorldTransform(boneContext);
|
DTransform boneTransform = bones.getWorldTransform(boneContext);
|
||||||
boneTransform.getRotation().set(q.mult(boneTransform.getRotation()));
|
boneTransform.getRotation().set(tempDQuaternion.mult(boneTransform.getRotation()));
|
||||||
bones.setWorldTransform(boneContext, boneTransform);
|
bones.setWorldTransform(boneContext, boneTransform);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// applying the results
|
// applying the results
|
||||||
for (int i = bones.size() - 1; i >= 0; --i) {
|
for (int i = bonesCount - 1; i >= 0; --i) {
|
||||||
BoneContext boneContext = bones.get(i);
|
BoneContext boneContext = bones.get(i);
|
||||||
DTransform transform = bones.getWorldTransform(boneContext);
|
DTransform transform = bones.getWorldTransform(boneContext);
|
||||||
constraintHelper.applyTransform(boneContext.getArmatureObjectOMA(), boneContext.getBone().getName(), Space.CONSTRAINT_SPACE_WORLD, transform.toTransform());
|
constraintHelper.applyTransform(boneContext.getArmatureObjectOMA(), boneContext.getBone().getName(), Space.CONSTRAINT_SPACE_WORLD, transform.toTransform());
|
||||||
}
|
}
|
||||||
bones.reset();
|
bones = null;// need to reload them again
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -133,47 +160,39 @@ public class ConstraintDefinitionIK extends ConstraintDefinition {
|
|||||||
return "Inverse kinematics";
|
return "Inverse kinematics";
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isTrackToBeChanged() {
|
|
||||||
if (trackToBeChanged) {
|
|
||||||
// need to check the bone structure too (when constructor was called not all of the bones might have been loaded yet)
|
|
||||||
// that is why it is also checked here
|
|
||||||
bones = new BonesChain((Bone) this.getOwner(), useTail, bonesAffected, blenderContext);
|
|
||||||
trackToBeChanged = bones.size() > 0;
|
|
||||||
}
|
|
||||||
return trackToBeChanged;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isTargetRequired() {
|
public boolean isTargetRequired() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loaded bones' chain. This class allows to operate on transform matrices that use double precision in computations.
|
||||||
|
* Only the final result is being transformed to single precision numbers.
|
||||||
|
*
|
||||||
|
* @author Marcin Roguski (Kaelthas)
|
||||||
|
*/
|
||||||
private static class BonesChain extends ArrayList<BoneContext> {
|
private static class BonesChain extends ArrayList<BoneContext> {
|
||||||
private static final long serialVersionUID = -1850524345643600718L;
|
private static final long serialVersionUID = -1850524345643600718L;
|
||||||
|
|
||||||
private Set<Long> alteredOmas = new HashSet<Long>();
|
private List<Matrix> bonesMatrices = new ArrayList<Matrix>();
|
||||||
private List<Matrix> originalBonesMatrices = new ArrayList<Matrix>();
|
|
||||||
private List<Matrix> bonesMatrices = new ArrayList<Matrix>();
|
|
||||||
|
|
||||||
public BonesChain(Bone bone, boolean useTail, int bonesAffected, BlenderContext blenderContext) {
|
public BonesChain(Bone bone, boolean useTail, int bonesAffected, Collection<Long> alteredOmas, BlenderContext blenderContext) {
|
||||||
if (bone != null) {
|
if (bone != null) {
|
||||||
ConstraintHelper constraintHelper = blenderContext.getHelper(ConstraintHelper.class);
|
ConstraintHelper constraintHelper = blenderContext.getHelper(ConstraintHelper.class);
|
||||||
if (!useTail) {
|
if (!useTail) {
|
||||||
bone = bone.getParent();
|
bone = bone.getParent();
|
||||||
}
|
}
|
||||||
while (bone != null && this.size() < bonesAffected) {
|
while (bone != null && (bonesAffected <= 0 || this.size() < bonesAffected)) {
|
||||||
BoneContext boneContext = blenderContext.getBoneContext(bone);
|
BoneContext boneContext = blenderContext.getBoneContext(bone);
|
||||||
this.add(boneContext);
|
this.add(boneContext);
|
||||||
alteredOmas.add(boneContext.getBoneOma());
|
alteredOmas.add(boneContext.getBoneOma());
|
||||||
|
|
||||||
Space space = this.size() < bonesAffected ? Space.CONSTRAINT_SPACE_LOCAL : Space.CONSTRAINT_SPACE_WORLD;
|
Space space = this.size() < bonesAffected ? Space.CONSTRAINT_SPACE_LOCAL : Space.CONSTRAINT_SPACE_WORLD;
|
||||||
Transform transform = constraintHelper.getTransform(boneContext.getArmatureObjectOMA(), boneContext.getBone().getName(), space);
|
Transform transform = constraintHelper.getTransform(boneContext.getArmatureObjectOMA(), boneContext.getBone().getName(), space);
|
||||||
originalBonesMatrices.add(new DTransform(transform).toMatrix());
|
bonesMatrices.add(new DTransform(transform).toMatrix());
|
||||||
|
|
||||||
bone = bone.getParent();
|
bone = bone.getParent();
|
||||||
}
|
}
|
||||||
this.reset();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -204,12 +223,5 @@ public class ConstraintDefinitionIK extends ConstraintDefinition {
|
|||||||
result = result.mult(bonesMatrices.get(index));
|
result = result.mult(bonesMatrices.get(index));
|
||||||
return new Matrix(result);
|
return new Matrix(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void reset() {
|
|
||||||
bonesMatrices.clear();
|
|
||||||
for (Matrix m : originalBonesMatrices) {
|
|
||||||
bonesMatrices.add(new Matrix(m));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -51,7 +51,7 @@ public class Matrix extends SimpleMatrix {
|
|||||||
|
|
||||||
int N = Math.min(this.numRows(),this.numCols());
|
int N = Math.min(this.numRows(),this.numCols());
|
||||||
double maxSingular = 0;
|
double maxSingular = 0;
|
||||||
for( int i = 0; i < N; i++ ) {
|
for( int i = 0; i < N; ++i ) {
|
||||||
if( S.get(i, i) > maxSingular ) {
|
if( S.get(i, i) > maxSingular ) {
|
||||||
maxSingular = S.get(i, i);
|
maxSingular = S.get(i, i);
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user