Several changes to constraints (but may not working well yet).
Fix to Y-up axis issue when parent was present bu not loaded (in different layer for example). git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@7913 75d07b2b-3a1a-0410-a2c5-0572b91ccdca
This commit is contained in:
parent
f182b5d91b
commit
99746c72ec
@ -267,9 +267,9 @@ public class ArmatureHelper extends AbstractBlenderHelper {
|
||||
}
|
||||
|
||||
/**
|
||||
* This method returns bone transformation data for the bone of a given name.
|
||||
* @param boneName
|
||||
* the name of the bone
|
||||
* This method returns bone transformation data for the bone of a given index.
|
||||
* @param index
|
||||
* the index of the bone
|
||||
* @return bone's transformation data
|
||||
*/
|
||||
public BoneTransformationData getBoneTransformationDataRoot(int index) {
|
||||
@ -368,4 +368,9 @@ public class ArmatureHelper extends AbstractBlenderHelper {
|
||||
bonesMap.clear();
|
||||
boneDataRoots.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldBeLoaded(Structure structure, DataRepository dataRepository) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -3,10 +3,12 @@ package com.jme3.scene.plugins.blender.helpers.v249;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import com.jme3.asset.BlenderKey.FeaturesToLoad;
|
||||
import com.jme3.renderer.Camera;
|
||||
import com.jme3.scene.plugins.blender.data.Structure;
|
||||
import com.jme3.scene.plugins.blender.exception.BlenderFileException;
|
||||
import com.jme3.scene.plugins.blender.utils.AbstractBlenderHelper;
|
||||
import com.jme3.scene.plugins.blender.utils.DataRepository;
|
||||
|
||||
/**
|
||||
* A class that is used in light calculations.
|
||||
@ -55,4 +57,9 @@ public class CameraHelper extends AbstractBlenderHelper {
|
||||
result.setFrustumPerspective(angle, aspect, clipsta, clipend);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldBeLoaded(Structure structure, DataRepository dataRepository) {
|
||||
return (dataRepository.getBlenderKey().getFeaturesToLoad() & FeaturesToLoad.CAMERAS) != 0;
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ package com.jme3.scene.plugins.blender.helpers.v249;
|
||||
|
||||
import java.nio.FloatBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@ -13,6 +14,7 @@ import com.jme3.animation.BoneTrack;
|
||||
import com.jme3.animation.Skeleton;
|
||||
import com.jme3.math.FastMath;
|
||||
import com.jme3.math.Quaternion;
|
||||
import com.jme3.math.Transform;
|
||||
import com.jme3.math.Vector3f;
|
||||
import com.jme3.scene.Geometry;
|
||||
import com.jme3.scene.Mesh;
|
||||
@ -22,6 +24,7 @@ import com.jme3.scene.VertexBuffer.Type;
|
||||
import com.jme3.scene.plugins.blender.data.Structure;
|
||||
import com.jme3.scene.plugins.blender.exception.BlenderFileException;
|
||||
import com.jme3.scene.plugins.blender.structures.AbstractInfluenceFunction;
|
||||
import com.jme3.scene.plugins.blender.structures.CalculationBone;
|
||||
import com.jme3.scene.plugins.blender.structures.Constraint;
|
||||
import com.jme3.scene.plugins.blender.structures.Constraint.Space;
|
||||
import com.jme3.scene.plugins.blender.structures.ConstraintType;
|
||||
@ -146,70 +149,89 @@ public class ConstraintHelper extends AbstractBlenderHelper {
|
||||
|
||||
@Override
|
||||
public void affectAnimation(Skeleton skeleton, BoneAnimation boneAnimation, Constraint constraint) {
|
||||
Structure constraintStructure = constraint.getData();
|
||||
this.validateConstraintType(constraintStructure);
|
||||
/*Long boneOMA = constraint.getBoneOMA();
|
||||
//IK solver is only attached to bones
|
||||
Bone ownerBone = (Bone)dataRepository.getLoadedFeature(boneOMA, LoadedFeatureDataType.LOADED_FEATURE);
|
||||
try {
|
||||
Structure constraintStructure = constraint.getData();
|
||||
this.validateConstraintType(constraintStructure);
|
||||
Long boneOMA = constraint.getBoneOMA();
|
||||
// IK solver is only attached to bones
|
||||
Bone ownerBone = (Bone) dataRepository.getLoadedFeature(boneOMA, LoadedFeatureDataType.LOADED_FEATURE);
|
||||
|
||||
//get the target point
|
||||
Object targetObject = this.getTarget(constraint, LoadedFeatureDataType.LOADED_FEATURE);
|
||||
Vector3f pt = null;//Point Target
|
||||
if(targetObject instanceof Bone) {
|
||||
pt = ((Bone)targetObject).getModelSpacePosition();
|
||||
} else if(targetObject instanceof Node) {
|
||||
pt = ((Node)targetObject).getWorldTranslation();
|
||||
} else if(targetObject instanceof Skeleton) {
|
||||
Structure armatureNodeStructure = (Structure)this.getTarget(constraint, LoadedFeatureDataType.LOADED_STRUCTURE);
|
||||
ObjectHelper objectHelper = dataRepository.getHelper(ObjectHelper.class);
|
||||
Transform transform = objectHelper.getTransformation(armatureNodeStructure);
|
||||
pt = transform.getTranslation();
|
||||
} else {
|
||||
throw new IllegalStateException("Unknown target object type! Should be Node, Bone or Skeleton and there is: " + targetObject.getClass().getName());
|
||||
}
|
||||
//preparing data
|
||||
int maxIterations = ((Number)constraintStructure.getFieldValue("iterations")).intValue();
|
||||
CalculationBone[] bones = this.getBonesToCalculate(ownerBone, skeleton, boneAnimation);
|
||||
for(int i=0;i<bones.length;++i) {
|
||||
System.out.println(Arrays.toString(bones[i].track.getTranslations()));
|
||||
System.out.println(Arrays.toString(bones[i].track.getRotations()));
|
||||
System.out.println("===============================");
|
||||
}
|
||||
Quaternion rotation = new Quaternion();
|
||||
int maxFrames = bones[0].track.getTimes().length;//all tracks should have the same amount of frames
|
||||
// get the target point
|
||||
Object targetObject = this.getTarget(constraint, LoadedFeatureDataType.LOADED_FEATURE);
|
||||
Vector3f pt = null;// Point Target
|
||||
if (targetObject instanceof Bone) {
|
||||
pt = ((Bone) targetObject).getModelSpacePosition();
|
||||
} else if (targetObject instanceof Node) {
|
||||
pt = ((Node) targetObject).getWorldTranslation();
|
||||
} else if (targetObject instanceof Skeleton) {
|
||||
Structure armatureNodeStructure = (Structure) this.getTarget(constraint, LoadedFeatureDataType.LOADED_STRUCTURE);
|
||||
ObjectHelper objectHelper = dataRepository.getHelper(ObjectHelper.class);
|
||||
Transform transform = objectHelper.getTransformation(armatureNodeStructure, dataRepository);
|
||||
pt = transform.getTranslation();
|
||||
} else {
|
||||
throw new IllegalStateException(
|
||||
"Unknown target object type! Should be Node, Bone or Skeleton and there is: "
|
||||
+ targetObject.getClass().getName());
|
||||
}
|
||||
|
||||
//fetching the owner's bone track
|
||||
// BoneTrack ownerBoneTrack = null;
|
||||
// int boneIndex = skeleton.getBoneIndex(ownerBone);
|
||||
// for (int i = 0; i < boneAnimation.getTracks().length; ++i) {
|
||||
// if (boneAnimation.getTracks()[i].getTargetBoneIndex() == boneIndex) {
|
||||
// ownerBoneTrack = boneAnimation.getTracks()[i];
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
// int ownerBoneFramesCount = ownerBoneTrack==null ? 0 : ownerBoneTrack.getTimes().length;
|
||||
|
||||
// preparing data
|
||||
int maxIterations = ((Number) constraintStructure.getFieldValue("iterations")).intValue();
|
||||
CalculationBone[] bones = this.getBonesToCalculate(ownerBone, skeleton, boneAnimation);
|
||||
// for (int i = 0; i < bones.length; ++i) {
|
||||
// System.out.println(Arrays.toString(bones[i].track.getTranslations()));
|
||||
// System.out.println(Arrays.toString(bones[i].track.getRotations()));
|
||||
// System.out.println("===============================");
|
||||
// }
|
||||
Quaternion rotation = new Quaternion();
|
||||
//all tracks should have the same amount of frames
|
||||
int framesCount = bones[0].getBoneFramesCount();
|
||||
assert framesCount >=1;
|
||||
for (int frame = 0; frame < framesCount; ++frame) {
|
||||
float error = IK_SOLVER_ERROR;
|
||||
int iteration = 0;
|
||||
while (error >= IK_SOLVER_ERROR && iteration <= maxIterations) {
|
||||
// rotating the bones
|
||||
for (int i = 0; i < bones.length - 1; ++i) {
|
||||
Vector3f pe = bones[i].getEndPoint();
|
||||
Vector3f pc = bones[i + 1].getWorldTranslation().clone();
|
||||
|
||||
for(int frame = 0; frame < maxFrames; ++frame) {
|
||||
float error = IK_SOLVER_ERROR;
|
||||
int iteration = 0;
|
||||
while(error >= IK_SOLVER_ERROR && iteration <= maxIterations) {
|
||||
//rotating the bones
|
||||
for(int i = 0; i < bones.length - 1; ++i) {
|
||||
Vector3f pe = bones[i].getEndPoint();
|
||||
Vector3f pc = bones[i + 1].getWorldTranslation().clone();
|
||||
Vector3f peSUBpc = pe.subtract(pc).normalizeLocal();
|
||||
Vector3f ptSUBpc = pt.subtract(pc).normalizeLocal();
|
||||
|
||||
Vector3f peSUBpc = pe.subtract(pc).normalizeLocal();
|
||||
Vector3f ptSUBpc = pt.subtract(pc).normalizeLocal();
|
||||
float theta = FastMath.acos(peSUBpc.dot(ptSUBpc));
|
||||
Vector3f direction = peSUBpc.cross(ptSUBpc).normalizeLocal();
|
||||
bones[i].rotate(rotation.fromAngleAxis(theta, direction), frame);
|
||||
}
|
||||
error = pt.subtract(bones[0].getEndPoint()).length();
|
||||
++iteration;
|
||||
}
|
||||
System.out.println("error = " + error + " iterations = " + iteration);
|
||||
}
|
||||
|
||||
float theta = FastMath.acos(peSUBpc.dot(ptSUBpc));
|
||||
Vector3f direction = peSUBpc.cross(ptSUBpc).normalizeLocal();
|
||||
bones[i].rotate(rotation.fromAngleAxis(theta, direction), frame);
|
||||
}
|
||||
error = pt.subtract(bones[0].getEndPoint()).length();
|
||||
++iteration;
|
||||
}
|
||||
System.out.println("error = " + error + " iterations = " + iteration);
|
||||
}
|
||||
for (CalculationBone bone : bones) {
|
||||
bone.applyCalculatedTracks();
|
||||
}
|
||||
|
||||
for(CalculationBone bone : bones) {
|
||||
bone.applyCalculatedTracks();
|
||||
}
|
||||
|
||||
System.out.println("&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&");
|
||||
for(int i=0;i<bones.length;++i) {
|
||||
System.out.println(Arrays.toString(bones[i].track.getTranslations()));
|
||||
System.out.println(Arrays.toString(bones[i].track.getRotations()));
|
||||
System.out.println("===============================");
|
||||
}*/
|
||||
// System.out.println("&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&");
|
||||
// for (int i = 0; i < bones.length; ++i) {
|
||||
// System.out.println(Arrays.toString(bones[i].track.getTranslations()));
|
||||
// System.out.println(Arrays.toString(bones[i].track.getRotations()));
|
||||
// System.out.println("===============================");
|
||||
// }
|
||||
} catch(BlenderFileException e) {
|
||||
LOGGER.severe(e.getLocalizedMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -226,13 +248,14 @@ public class ConstraintHelper extends AbstractBlenderHelper {
|
||||
List<CalculationBone> bonesList = new ArrayList<CalculationBone>();
|
||||
Bone currentBone = bone;
|
||||
do {
|
||||
int boneIndex = skeleton.getBoneIndex(currentBone);
|
||||
for (int i = 0; i < boneAnimation.getTracks().length; ++i) {
|
||||
if (boneAnimation.getTracks()[i].getTargetBoneIndex() == boneIndex) {
|
||||
bonesList.add(new CalculationBone(currentBone, boneAnimation.getTracks()[i]));
|
||||
break;
|
||||
}
|
||||
}
|
||||
bonesList.add(new CalculationBone(currentBone, 1));
|
||||
// int boneIndex = skeleton.getBoneIndex(currentBone);
|
||||
// for (int i = 0; i < boneAnimation.getTracks().length; ++i) {
|
||||
// if (boneAnimation.getTracks()[i].getTargetBoneIndex() == boneIndex) {
|
||||
// bonesList.add(new CalculationBone(currentBone, boneAnimation.getTracks()[i]));
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
currentBone = currentBone.getParent();
|
||||
} while (currentBone != null);
|
||||
//attaching children
|
||||
@ -725,101 +748,9 @@ public class ConstraintHelper extends AbstractBlenderHelper {
|
||||
public void clearState() {
|
||||
constraints.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* The purpose of this class is to imitate bone's movement when calculating inverse kinematics.
|
||||
* @author Marcin Roguski
|
||||
*/
|
||||
private static class CalculationBone extends Node {
|
||||
|
||||
/** The name of the bone. Only to be used in toString method. */
|
||||
private String boneName;
|
||||
/** The bone's tracks. Will be altered at the end of calculation process. */
|
||||
private BoneTrack track;
|
||||
/** The starting position of the bone. */
|
||||
private Vector3f startTranslation;
|
||||
/** The starting rotation of the bone. */
|
||||
private Quaternion startRotation;
|
||||
/** The starting scale of the bone. */
|
||||
private Vector3f startScale;
|
||||
private Vector3f[] translations;
|
||||
private Quaternion[] rotations;
|
||||
private Vector3f[] scales;
|
||||
|
||||
/**
|
||||
* Constructor. Stores the track, starting transformation and sets the transformation to the starting positions.
|
||||
* @param bone
|
||||
* the bone this class will imitate
|
||||
* @param track
|
||||
* the bone's tracks
|
||||
*/
|
||||
public CalculationBone(Bone bone, BoneTrack track) {
|
||||
this.boneName = bone.getName();
|
||||
this.track = track;
|
||||
this.startRotation = bone.getModelSpaceRotation().clone();
|
||||
this.startTranslation = bone.getModelSpacePosition().clone();
|
||||
this.startScale = bone.getModelSpaceScale().clone();
|
||||
this.translations = track.getTranslations();
|
||||
this.rotations = track.getRotations();
|
||||
this.scales = track.getScales();
|
||||
this.reset();
|
||||
}
|
||||
|
||||
/**
|
||||
* This method returns the end point of the bone. If the bone has parent it is calculated from the start point
|
||||
* of parent to the start point of this bone. If the bone doesn't have a parent the end location is considered
|
||||
* to be 1 point up along Y axis (scale is applied if set to != 1.0);
|
||||
* @return the end point of this bone
|
||||
*/
|
||||
//TODO: set to Z axis if user defined it this way
|
||||
public Vector3f getEndPoint() {
|
||||
if (this.getParent() == null) {
|
||||
return new Vector3f(0, this.getLocalScale().y, 0);
|
||||
} else {
|
||||
Node parent = this.getParent();
|
||||
return parent.getWorldTranslation().subtract(this.getWorldTranslation()).multLocal(this.getWorldScale());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method resets the calculation bone to the starting position.
|
||||
*/
|
||||
public void reset() {
|
||||
this.setLocalTranslation(startTranslation);
|
||||
this.setLocalRotation(startRotation);
|
||||
this.setLocalScale(startScale);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int attachChild(Spatial child) {
|
||||
if (this.getChildren() != null && this.getChildren().size() > 1) {
|
||||
throw new IllegalStateException(this.getClass().getName() + " class instance can only have one child!");
|
||||
}
|
||||
return super.attachChild(child);
|
||||
}
|
||||
|
||||
public Spatial rotate(Quaternion rot, int frame) {
|
||||
Spatial spatial = super.rotate(rot);
|
||||
this.updateWorldTransforms();
|
||||
if (this.getChildren() != null && this.getChildren().size() > 0) {
|
||||
CalculationBone child = (CalculationBone) this.getChild(0);
|
||||
child.updateWorldTransforms();
|
||||
}
|
||||
rotations[frame].set(this.getLocalRotation());
|
||||
translations[frame].set(this.getLocalTranslation());
|
||||
if (scales != null) {
|
||||
scales[frame].set(this.getLocalScale());
|
||||
}
|
||||
return spatial;
|
||||
}
|
||||
|
||||
public void applyCalculatedTracks() {
|
||||
track.setKeyframes(track.getTimes(), translations, rotations);//TODO:scales
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return boneName + ": " + this.getLocalRotation() + " " + this.getLocalTranslation();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldBeLoaded(Structure structure, DataRepository dataRepository) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -599,4 +599,9 @@ public class CurvesHelper extends AbstractBlenderHelper {
|
||||
return new Vector3f(locArray.get(0).floatValue(), locArray.get(2).floatValue(), locArray.get(1).floatValue());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldBeLoaded(Structure structure, DataRepository dataRepository) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -71,6 +71,11 @@ public class IpoHelper extends AbstractBlenderHelper {
|
||||
return new ConstIpo(constValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldBeLoaded(Structure structure, DataRepository dataRepository) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ipo constant curve. This is a curve with only one value and no specified type. This type of ipo cannot be used to
|
||||
* calculate tracks. It should only be used to calculate single value for a given frame.
|
||||
|
@ -34,6 +34,7 @@ package com.jme3.scene.plugins.blender.helpers.v249;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import com.jme3.asset.BlenderKey.FeaturesToLoad;
|
||||
import com.jme3.light.DirectionalLight;
|
||||
import com.jme3.light.Light;
|
||||
import com.jme3.light.PointLight;
|
||||
@ -97,4 +98,9 @@ public class LightHelper extends AbstractBlenderHelper {
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldBeLoaded(Structure structure, DataRepository dataRepository) {
|
||||
return (dataRepository.getBlenderKey().getFeaturesToLoad() & FeaturesToLoad.LIGHTS) != 0;
|
||||
}
|
||||
}
|
||||
|
@ -685,6 +685,11 @@ public class MaterialHelper extends AbstractBlenderHelper {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldBeLoaded(Structure structure, DataRepository dataRepository) {
|
||||
return (dataRepository.getBlenderKey().getFeaturesToLoad() & FeaturesToLoad.MATERIALS) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* An interface used in calculating alpha mask during particles' texture calculations.
|
||||
* @author Marcin Roguski (Kaelthas)
|
||||
|
@ -605,4 +605,9 @@ public class MeshHelper extends AbstractBlenderHelper {
|
||||
// mesh.setMaxNumWeights(maxWeightsPerVert);
|
||||
return maxWeightsPerVert;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldBeLoaded(Structure structure, DataRepository dataRepository) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -352,7 +352,7 @@ public class ModifierHelper extends AbstractBlenderHelper {
|
||||
|
||||
//preparing the object's bone
|
||||
ObjectHelper objectHelper = dataRepository.getHelper(ObjectHelper.class);
|
||||
Transform t = objectHelper.getTransformation(objectStructure);
|
||||
Transform t = objectHelper.getTransformation(objectStructure, dataRepository);
|
||||
Bone bone = new Bone(null);
|
||||
bone.setBindTransforms(t.getTranslation(), t.getRotation(), t.getScale());
|
||||
|
||||
@ -545,7 +545,7 @@ public class ModifierHelper extends AbstractBlenderHelper {
|
||||
ObjectHelper objectHelper = dataRepository.getHelper(ObjectHelper.class);
|
||||
try {// we take the structure in case the object was not yet loaded
|
||||
Structure offsetStructure = offsetObjectBlock.getStructure(dataRepository);
|
||||
Vector3f translation = objectHelper.getTransformation(offsetStructure).getTranslation();
|
||||
Vector3f translation = objectHelper.getTransformation(offsetStructure, dataRepository).getTranslation();
|
||||
objectOffset[0] = translation.x;
|
||||
objectOffset[1] = translation.y;
|
||||
objectOffset[2] = translation.z;
|
||||
@ -754,4 +754,9 @@ public class ModifierHelper extends AbstractBlenderHelper {
|
||||
}
|
||||
return new Skeleton(bones.toArray(new Bone[bones.size()]));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldBeLoaded(Structure structure, DataRepository dataRepository) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -1529,6 +1529,11 @@ public class NoiseHelper extends AbstractBlenderHelper {
|
||||
return rx * q[0] + ry * q[1] + rz * q[2];
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldBeLoaded(Structure structure, DataRepository dataRepository) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* This interface is used for distance calculation classes. Distance metrics for voronoi. e parameter only used in
|
||||
|
@ -31,10 +31,13 @@
|
||||
*/
|
||||
package com.jme3.scene.plugins.blender.helpers.v249;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import com.jme3.asset.BlenderKey.FeaturesToLoad;
|
||||
import com.jme3.light.DirectionalLight;
|
||||
import com.jme3.light.Light;
|
||||
import com.jme3.light.PointLight;
|
||||
@ -77,6 +80,16 @@ public class ObjectHelper extends AbstractBlenderHelper {
|
||||
protected static final int OBJECT_TYPE_LATTICE = 22;
|
||||
protected static final int OBJECT_TYPE_ARMATURE = 25;
|
||||
|
||||
/**
|
||||
* This collection contains types of features that are not visible to the user.
|
||||
* These will be loaded regardless the layer they reside in.
|
||||
*/
|
||||
protected static final Collection<Integer> invisibleTypes = new ArrayList<Integer>();
|
||||
static {
|
||||
invisibleTypes.add(OBJECT_TYPE_EMPTY);
|
||||
invisibleTypes.add(OBJECT_TYPE_ARMATURE);
|
||||
}
|
||||
|
||||
/** This variable indicates if the Y asxis is the UP axis or not. */
|
||||
protected boolean fixUpAxis;
|
||||
/** Quaternion used to rotate data when Y is up axis. */
|
||||
@ -149,7 +162,7 @@ public class ObjectHelper extends AbstractBlenderHelper {
|
||||
parent = this.toObject(parentStructure, dataRepository);
|
||||
}
|
||||
|
||||
Transform t = objectHelper.getTransformation(objectStructure);
|
||||
Transform t = objectHelper.getTransformation(objectStructure, dataRepository);
|
||||
|
||||
try {
|
||||
switch(type) {
|
||||
@ -268,15 +281,23 @@ public class ObjectHelper extends AbstractBlenderHelper {
|
||||
* @return objects transformation relative to its parent
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public Transform getTransformation(Structure objectStructure) {
|
||||
public Transform getTransformation(Structure objectStructure, DataRepository dataRepository) {
|
||||
//these are transformations in global space
|
||||
DynamicArray<Number> loc = (DynamicArray<Number>)objectStructure.getFieldValue("loc");
|
||||
DynamicArray<Number> size = (DynamicArray<Number>)objectStructure.getFieldValue("size");
|
||||
DynamicArray<Number> rot = (DynamicArray<Number>)objectStructure.getFieldValue("rot");
|
||||
|
||||
//load parent inverse matrix
|
||||
Pointer parent = (Pointer) objectStructure.getFieldValue("parent");
|
||||
Matrix4f parentInv = parent.isNull() ? Matrix4f.IDENTITY : this.getMatrix(objectStructure, "parentinv");
|
||||
Pointer pParent = (Pointer) objectStructure.getFieldValue("parent");
|
||||
Structure parent = null;
|
||||
if(pParent.isNotNull()) {
|
||||
try {
|
||||
parent = pParent.fetchData(dataRepository.getInputStream()).get(0);
|
||||
} catch (BlenderFileException e) {
|
||||
LOGGER.log(Level.WARNING, "Cannot fetch parent for object! Reason: {0}", e.getLocalizedMessage());
|
||||
}
|
||||
}
|
||||
Matrix4f parentInv = pParent.isNull() ? Matrix4f.IDENTITY : this.getMatrix(objectStructure, "parentinv");
|
||||
|
||||
//create the global matrix (without the scale)
|
||||
Matrix4f globalMatrix = new Matrix4f();
|
||||
@ -296,7 +317,7 @@ public class ObjectHelper extends AbstractBlenderHelper {
|
||||
size.get(2).floatValue() * scaleZ);
|
||||
|
||||
//the root object is transformed if the Y axis is UP
|
||||
if(parent.isNull() && fixUpAxis) {
|
||||
if(fixUpAxis && (pParent.isNull() || (parent!=null && !this.shouldBeLoaded(parent, dataRepository)))) {
|
||||
float y = translation.y;
|
||||
translation.y = translation.z;
|
||||
translation.z = -y;
|
||||
@ -343,4 +364,11 @@ public class ObjectHelper extends AbstractBlenderHelper {
|
||||
public void clearState() {
|
||||
fixUpAxis = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldBeLoaded(Structure structure, DataRepository dataRepository) {
|
||||
int lay = ((Number) structure.getFieldValue("lay")).intValue();
|
||||
return ((lay & dataRepository.getBlenderKey().getLayersToLoad()) != 0
|
||||
&& (dataRepository.getBlenderKey().getFeaturesToLoad() & FeaturesToLoad.OBJECTS) != 0);
|
||||
}
|
||||
}
|
||||
|
@ -187,4 +187,9 @@ public class ParticlesHelper extends AbstractBlenderHelper {
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldBeLoaded(Structure structure, DataRepository dataRepository) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -42,6 +42,7 @@ import java.util.logging.Logger;
|
||||
|
||||
import com.jme3.asset.AssetNotFoundException;
|
||||
import com.jme3.asset.TextureKey;
|
||||
import com.jme3.asset.BlenderKey.FeaturesToLoad;
|
||||
import com.jme3.math.FastMath;
|
||||
import com.jme3.scene.plugins.blender.data.FileBlockHeader;
|
||||
import com.jme3.scene.plugins.blender.data.Structure;
|
||||
@ -1779,6 +1780,11 @@ public class TextureHelper extends AbstractBlenderHelper {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldBeLoaded(Structure structure, DataRepository dataRepository) {
|
||||
return (dataRepository.getBlenderKey().getFeaturesToLoad() & FeaturesToLoad.TEXTURES) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Image types that can be loaded. AWT: png, jpg, jped or bmp TGA: tga DDS: DirectX image files
|
||||
|
@ -139,20 +139,21 @@ public abstract class AbstractInfluenceFunction {
|
||||
* @throws BlenderFileException this exception is thrown if the blend file is somehow corrupted
|
||||
*/
|
||||
protected Object getTarget(Constraint constraint, LoadedFeatureDataType loadedFeatureDataType) throws BlenderFileException {
|
||||
//TODO: load the feature through objectHelper, this way we are certain the object loads and has
|
||||
//load the feature through objectHelper, this way we are certain the object loads and has
|
||||
//his own constraints applied to traces
|
||||
ObjectHelper objectHelper = dataRepository.getHelper(ObjectHelper.class);
|
||||
//subtarget goes first
|
||||
//always load the target first
|
||||
Long targetOMA = ((Pointer) constraint.getData().getFieldValue("tar")).getOldMemoryAddress();
|
||||
Structure objectStructure = dataRepository.getFileBlock(targetOMA).getStructure(dataRepository);
|
||||
Object result = objectHelper.toObject(objectStructure, dataRepository);
|
||||
|
||||
//subtarget should be loaded alogn with target
|
||||
Object subtarget = constraint.getData().getFieldValue("subtarget");
|
||||
String subtargetName = subtarget==null ? null : subtarget.toString();
|
||||
if (subtargetName!=null && subtargetName.length() > 0) {
|
||||
//TODO: what if it is not yet loaded ???
|
||||
return dataRepository.getLoadedFeature(subtargetName, loadedFeatureDataType);
|
||||
result = dataRepository.getLoadedFeature(subtargetName, loadedFeatureDataType);
|
||||
}
|
||||
//and now use the target
|
||||
Long targetOMA = ((Pointer) constraint.getData().getFieldValue("target")).getOldMemoryAddress();
|
||||
Structure objectStructure = dataRepository.getFileBlock(targetOMA).getStructure(dataRepository);
|
||||
return objectHelper.toObject(objectStructure, dataRepository);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -0,0 +1,129 @@
|
||||
package com.jme3.scene.plugins.blender.structures;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import com.jme3.animation.Bone;
|
||||
import com.jme3.animation.BoneTrack;
|
||||
import com.jme3.math.Quaternion;
|
||||
import com.jme3.math.Vector3f;
|
||||
import com.jme3.scene.Node;
|
||||
import com.jme3.scene.Spatial;
|
||||
|
||||
/**
|
||||
* The purpose of this class is to imitate bone's movement when calculating inverse kinematics.
|
||||
* @author Marcin Roguski
|
||||
*/
|
||||
public class CalculationBone extends Node {
|
||||
private Bone bone;
|
||||
/** The bone's tracks. Will be altered at the end of calculation process. */
|
||||
private BoneTrack track;
|
||||
/** The starting position of the bone. */
|
||||
private Vector3f startTranslation;
|
||||
/** The starting rotation of the bone. */
|
||||
private Quaternion startRotation;
|
||||
/** The starting scale of the bone. */
|
||||
private Vector3f startScale;
|
||||
private Vector3f[] translations;
|
||||
private Quaternion[] rotations;
|
||||
private Vector3f[] scales;
|
||||
|
||||
public CalculationBone(Bone bone, int boneFramesCount) {
|
||||
this.bone = bone;
|
||||
this.startRotation = bone.getModelSpaceRotation().clone();
|
||||
this.startTranslation = bone.getModelSpacePosition().clone();
|
||||
this.startScale = bone.getModelSpaceScale().clone();
|
||||
this.reset();
|
||||
if(boneFramesCount > 0) {
|
||||
this.translations = new Vector3f[boneFramesCount];
|
||||
this.rotations = new Quaternion[boneFramesCount];
|
||||
this.scales = new Vector3f[boneFramesCount];
|
||||
|
||||
Arrays.fill(this.translations, 0, boneFramesCount, this.startTranslation);
|
||||
Arrays.fill(this.rotations, 0, boneFramesCount, this.startRotation);
|
||||
Arrays.fill(this.scales, 0, boneFramesCount, this.startScale);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor. Stores the track, starting transformation and sets the transformation to the starting positions.
|
||||
* @param bone
|
||||
* the bone this class will imitate
|
||||
* @param track
|
||||
* the bone's tracks
|
||||
*/
|
||||
public CalculationBone(Bone bone, BoneTrack track) {
|
||||
this(bone, 0);
|
||||
this.track = track;
|
||||
this.translations = track.getTranslations();
|
||||
this.rotations = track.getRotations();
|
||||
this.scales = track.getScales();
|
||||
}
|
||||
|
||||
public int getBoneFramesCount() {
|
||||
return this.translations==null ? 0 : this.translations.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method returns the end point of the bone. If the bone has parent it is calculated from the start point
|
||||
* of parent to the start point of this bone. If the bone doesn't have a parent the end location is considered
|
||||
* to be 1 point up along Y axis (scale is applied if set to != 1.0);
|
||||
* @return the end point of this bone
|
||||
*/
|
||||
//TODO: set to Z axis if user defined it this way
|
||||
public Vector3f getEndPoint() {
|
||||
if (this.getParent() == null) {
|
||||
return new Vector3f(0, this.getLocalScale().y, 0);
|
||||
} else {
|
||||
Node parent = this.getParent();
|
||||
return parent.getWorldTranslation().subtract(this.getWorldTranslation()).multLocal(this.getWorldScale());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method resets the calculation bone to the starting position.
|
||||
*/
|
||||
public void reset() {
|
||||
this.setLocalTranslation(startTranslation);
|
||||
this.setLocalRotation(startRotation);
|
||||
this.setLocalScale(startScale);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int attachChild(Spatial child) {
|
||||
if (this.getChildren() != null && this.getChildren().size() > 1) {
|
||||
throw new IllegalStateException(this.getClass().getName() + " class instance can only have one child!");
|
||||
}
|
||||
return super.attachChild(child);
|
||||
}
|
||||
|
||||
public Spatial rotate(Quaternion rot, int frame) {
|
||||
Spatial spatial = super.rotate(rot);
|
||||
this.updateWorldTransforms();
|
||||
if (this.getChildren() != null && this.getChildren().size() > 0) {
|
||||
CalculationBone child = (CalculationBone) this.getChild(0);
|
||||
child.updateWorldTransforms();
|
||||
}
|
||||
rotations[frame].set(this.getLocalRotation());
|
||||
translations[frame].set(this.getLocalTranslation());
|
||||
if (scales != null) {
|
||||
scales[frame].set(this.getLocalScale());
|
||||
}
|
||||
return spatial;
|
||||
}
|
||||
|
||||
public void applyCalculatedTracks() {
|
||||
if(track != null) {
|
||||
track.setKeyframes(track.getTimes(), translations, rotations);//TODO:scales
|
||||
} else {
|
||||
bone.setUserControl(true);
|
||||
bone.setUserTransforms(translations[0], rotations[0], scales[0]);
|
||||
bone.setUserControl(false);
|
||||
bone.updateWorldVectors();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return bone.getName() + ": " + this.getLocalRotation() + " " + this.getLocalTranslation();
|
||||
}
|
||||
}
|
@ -127,4 +127,15 @@ public abstract class AbstractBlenderHelper {
|
||||
}
|
||||
return properties;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method analyzes the given structure and the data contained within
|
||||
* data repository and decides if the feature should be loaded.
|
||||
* @param structure
|
||||
* structure to be analyzed
|
||||
* @param dataRepository
|
||||
* the data repository
|
||||
* @return <b>true</b> if the feature should be loaded and false otherwise
|
||||
*/
|
||||
public abstract boolean shouldBeLoaded(Structure structure, DataRepository dataRepository);
|
||||
}
|
||||
|
@ -32,7 +32,6 @@
|
||||
package com.jme3.scene.plugins.blender.utils;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import com.jme3.asset.BlenderKey.FeaturesToLoad;
|
||||
import com.jme3.asset.BlenderKey.WorldData;
|
||||
@ -57,7 +56,6 @@ import com.jme3.scene.plugins.blender.helpers.ObjectHelper;
|
||||
*/
|
||||
public class JmeConverter implements BlenderConverter<Node, Camera, Light, Object, List<Geometry>, Material> {
|
||||
|
||||
private static final Logger LOGGER = Logger.getLogger(JmeConverter.class.getName());
|
||||
private final DataRepository dataRepository;
|
||||
|
||||
/**
|
||||
@ -104,46 +102,47 @@ public class JmeConverter implements BlenderConverter<Node, Camera, Light, Objec
|
||||
|
||||
@Override
|
||||
public Camera toCamera(Structure structure) throws BlenderFileException {
|
||||
if ((dataRepository.getBlenderKey().getFeaturesToLoad() & FeaturesToLoad.CAMERAS) == 0) {
|
||||
return null;
|
||||
CameraHelper cameraHelper = dataRepository.getHelper(CameraHelper.class);
|
||||
if (cameraHelper.shouldBeLoaded(structure, dataRepository)) {
|
||||
return cameraHelper.toCamera(structure);
|
||||
}
|
||||
CameraHelper cameraHelper = dataRepository.getHelper(CameraHelper.class);
|
||||
return cameraHelper.toCamera(structure);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Light toLight(Structure structure) throws BlenderFileException {
|
||||
if ((dataRepository.getBlenderKey().getFeaturesToLoad() & FeaturesToLoad.LIGHTS) == 0) {
|
||||
return null;
|
||||
LightHelper lightHelper = dataRepository.getHelper(LightHelper.class);
|
||||
if (lightHelper.shouldBeLoaded(structure, dataRepository)) {
|
||||
return lightHelper.toLight(structure, dataRepository);
|
||||
}
|
||||
LightHelper lightHelper = dataRepository.getHelper(LightHelper.class);
|
||||
return lightHelper.toLight(structure, dataRepository);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object toObject(Structure structure) throws BlenderFileException {
|
||||
int lay = ((Number) structure.getFieldValue("lay")).intValue();
|
||||
if ((lay & dataRepository.getBlenderKey().getLayersToLoad()) == 0
|
||||
|| (dataRepository.getBlenderKey().getFeaturesToLoad() & FeaturesToLoad.OBJECTS) == 0) {
|
||||
return null;
|
||||
}
|
||||
ObjectHelper objectHelper = dataRepository.getHelper(ObjectHelper.class);
|
||||
return objectHelper.toObject(structure, dataRepository);
|
||||
if(objectHelper.shouldBeLoaded(structure, dataRepository)) {
|
||||
return objectHelper.toObject(structure, dataRepository);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Geometry> toMesh(Structure structure) throws BlenderFileException {
|
||||
MeshHelper meshHelper = dataRepository.getHelper(MeshHelper.class);
|
||||
return meshHelper.toMesh(structure, dataRepository);
|
||||
if(meshHelper.shouldBeLoaded(structure, dataRepository)) {
|
||||
return meshHelper.toMesh(structure, dataRepository);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Material toMaterial(Structure structure) throws BlenderFileException {
|
||||
if ((dataRepository.getBlenderKey().getFeaturesToLoad() & FeaturesToLoad.MATERIALS) == 0) {
|
||||
return null;
|
||||
MaterialHelper materialHelper = dataRepository.getHelper(MaterialHelper.class);
|
||||
if (materialHelper.shouldBeLoaded(structure, dataRepository)) {
|
||||
return materialHelper.toMaterial(structure, dataRepository);
|
||||
}
|
||||
MaterialHelper materialHelper = dataRepository.getHelper(MaterialHelper.class);
|
||||
return materialHelper.toMaterial(structure, dataRepository);
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
Loading…
x
Reference in New Issue
Block a user